1

Image コントロールを継承する VideoRenderer クラス、VideoRenderer インスタンスを持つ MyVideo クラス、および MainWindow コード ビハインドの RenderFrames メソッドがあります。

これらのクラスの関連セクション、RenderFrames メソッド、および MainWindow 分離コード コンストラクターの一部を以下に添付しました。

VideoRenderer は外部ビデオを受け取り、ビデオ フレームを Bitmap オブジェクトとして生成し、「bitmap」フィールドに格納します。各プロセスが終了したら、ビットマップのクローンをフィールド「bitmapCopy」に保存します。

MyVideo は、VideoRenderer インスタンス「sharedVideoRenderer」が、MainWindow RenderFrames メソッドへのフレームの送受信を開始および停止するタイミングを制御し、ThreadPool.QueueUserWorkItem を使用して VideoRenderer インスタンスをオブジェクト パラメータとして渡します。

RenderFrames は、MyVideo が bool の「レンダリング中」プロパティのいずれかを変更して停止するように指示するまでループし、Bitmap -> BitmapImage -> Image.Source から変換を行い、MainWindow Dispatcher を使用して VideoContentControl.Content を画像として設定します。 .

すべてが機能し、ビデオはレンダリングされますが、操作全体を MainWindow スレッドにディスパッチし、絶えずループするとスレッドが拘束されるため、GUI コントロールは基本的にフリーズし、他のボタンやその他の機能は機能しません。

私の質問は次のとおりです。ビットマップ フレームを「sharedVideoRenderer」から VideoContentControl に転送し、GUI をフリーズせずに新しい画像で更新し続けるには、他にどのような方法がありますか?

どんな助けでも大歓迎です。

関連コード:

VideoRenderer.cs:

internal void DrawBitmap()
{
    lock (bitmapLock)
    {
        bitmapCopy = (Bitmap)bitmap.Clone();
    }
}

MyVideo.cs:

public static void RenderVideoPreview()
{
    sharedVideoRenderer.VideoObject = videoPreview;

    sharedVideoRenderer.Start();

    videoPreviewIsRendering = true;

    ThreadPool.QueueUserWorkItem((Application.Current.MainWindow as MainWindow).RenderFrames, sharedVideoRenderer);
    }

MainWindow.xaml.cs:

Dispatcher mainWindowDispatcher;

public MainWindow()
{
    InitializeComponent();

    mainWindowDispatcher = this.Dispatcher;
...

public void RenderFrames(object videoRenderer)
{
    while (MyVideo.VideoPreviewIsRendering || MyVideo.LiveSessionParticipantVideoIsRendering)
    {
        mainWindowDispatcher.Invoke(new Action(() =>
        {
            try
            {
                System.Drawing.Bitmap bitmap;

                bitmap = (videoRenderer as VideoRenderer).BitmapCopy;

                using (MemoryStream memory = new MemoryStream())
                {
                    BitmapImage bitmapImage = new BitmapImage();
                    ImageSource imageSource;
                    Image image;
                    image = new Image();

                    bitmap.Save(memory, System.Drawing.Imaging.ImageFormat.Bmp);

                    memory.Position = 0;

                    bitmapImage.BeginInit();
                    bitmapImage.StreamSource = memory;
                    bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
                    bitmapImage.EndInit();

                    imageSource = bitmapImage;

                    image.Source = imageSource;

                    VideoContentControl.Content = image;

                    memory.Close();
                }
            }
            catch (Exception)
            {

            }
        }));
    }

    mainWindowDispatcher.Invoke(new Action(() =>
        {
            VideoContentControl.ClearValue(ContentProperty);

            VideoContentControl.InvalidateVisual();
        }));
}
4

1 に答える 1

1
    ThreadPool.QueueUserWorkItem((Application.Current.MainWindow as MainWindow).RenderFrames, sharedVideoRenderer);

////    

public void RenderFrames(object videoRenderer)
{
    while (MyVideo.VideoPreviewIsRendering || MyVideo.LiveSessionParticipantVideoIsRendering)
    {
        mainWindowDispatcher.Invoke(new Action(() =>

これらの行は、私が推測する痛みを引き起こしています。

Renderframes メソッドはスレッドプール スレッドで呼び出されますが、このメソッドが行う主な処理は、制御を UI スレッドに戻し、要求/ブロックすることです。

より小さなスコープを試してみることができます:

while (MyVideo.VideoPreviewIsRendering || MyVideo.LiveSessionParticipantVideoIsRendering)
{

        try
        {
            System.Drawing.Bitmap bitmap;

            bitmap = (videoRenderer as VideoRenderer).BitmapCopy;

            using (MemoryStream memory = new MemoryStream())
            {
                BitmapImage bitmapImage = new BitmapImage();
                ImageSource imageSource;
                Image image;
                image = new Image();

                bitmap.Save(memory, System.Drawing.Imaging.ImageFormat.Bmp);

                memory.Position = 0;

                bitmapImage.BeginInit();
                bitmapImage.StreamSource = memory;
                bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
                bitmapImage.EndInit();

                imageSource = bitmapImage;

                mainWindowDispatcher.Invoke(new Action(() =>
                {
                    image.Source = imageSource;

                    VideoContentControl.Content = image;

                    memory.Close();
                }
            }
        }
        catch (Exception)
        {

        }
    }));
}

何が UI スレッドに属しているのかよくわからないので、スコープを変更したことが少なすぎたり多すぎたりしたかどうかはよくわかりませんが、これがきっかけとなり、物事を動かしたくなるかもしれません。

使用できる/使用すべきもう 1 つのトリックは、作成する UI 要素の数を最小限に抑えることです。現在のコードでは、フレームごとに Image 要素を作成しています。代わりに、単一の WriteableBitmap を作成し、Image の ImageSource をそのビットマップに向けて、単純に画像データをそれにブリットすることができます。参照: WPF でビデオを生成および描画するための高速な方法は何ですか?

于 2012-07-29T18:15:59.243 に答える