如何在不冻结GUI的情况下使用视频帧不断更新内容控制



我有一个VideoRenderer类,它继承了一个Image控件,一个MyVideo类,它有一个VideoRenderer实例,以及后面MainWindow代码中的RenderFrames方法

我已经附上了这些类的相关部分,以及RenderFrames方法和下面构造函数后面的一些MainWindow代码。

VideoRenderer接收外部视频并生成视频帧作为位图对象,存储在"位图"字段中。在每个完成的过程之后,我将位图的克隆存储在字段"bitmapCopy"中。

MyVideo使用ThreadPool.QueueUserWorkItem.控制VideoRenderer实例"sharedVideoRenderer"何时启动和停止接收帧并将帧发送到MainWindow RenderFrames方法,将VideoRenderer实例作为对象参数传递

RenderFrames循环,直到MyVideo通过更改其布尔"我们正在渲染吗"属性表示停止,然后从Bitmap->BitmapImage->Image.Source进行转换,然后使用MainWindow Dispatcher将VideoContentControl.Content设置为图像。

一切都正常,视频也被渲染,但GUI控件基本上被冻结了,其他按钮和东西都不起作用,因为将整个操作分派到MainWindow线程上并不断循环会占用线程。

我的问题是:还有什么其他方法可以尝试将位图帧从"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);
    }

主窗口.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();
        }));
}
    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线程,但这可能会给你一个开始,你可能想四处移动。

你可以/应该使用的另一个技巧是尽量减少你正在制作的UI元素的数量。在当前代码中,每帧都要创建一个Image元素。相反,你可以制作一个WriteableBitmap,将图像的ImageSource指向它,然后简单地将图片数据闪电式传输到它。请参阅:在WPF中生成和绘制视频的快速方法是什么?

最新更新