1

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

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

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

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

RenderFrames 循环,直到 MyVideo 说通过更改其布尔“我们正在渲染”属性中的任何一个来停止,并且它从 Bitmap -> BitmapImage -> Image.Source 进行转换,然后使用 MainWindow Dispatcher 将 VideoContentControl.Content 设置为图像.

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

我的问题是:我可以尝试哪些其他方法将位图帧从“sharedVideoRenderer”传输到 VideoContentControl,并使用新图像不断更新它,而不冻结 GUI?

任何帮助,将不胜感激。

相关代码:

视频渲染器.cs:

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

我的视频.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 线程,但这可能会给你一个开始,你可能想要四处移动。

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

于 2012-07-29T18:15:59.243 回答