如何在不冻结 GUI 的情况下使用视频帧不断更新 contentcontrol?
Posted
技术标签:
【中文标题】如何在不冻结 GUI 的情况下使用视频帧不断更新 contentcontrol?【英文标题】:How can I constantly update contentcontrol with video frames without freezing the GUI? 【发布时间】:2012-07-27 13:34:31 【问题描述】:我有一个 VideoRenderer 类,它继承了一个 Image 控件,一个 MyVideo 类,它有一个 VideoRenderer 实例,以及 MainWindow 代码隐藏中的一个 RenderFrames 方法
我在下面附上了这些类的相关部分,以及 RenderFrames 方法和一些 MainWindow 代码隐藏构造函数。
VideoRenderer 接收外部视频并将视频帧生成为位图对象,存储在“位图”字段中。在每个完成的过程之后,我将位图的副本存储在“bitmapCopy”字段中。
MyVideo 控制 VideoRenderer 实例“sharedVideoRenderer”何时开始和停止接收和发送帧到 MainWindow RenderFrames 方法,使用 ThreadPool.QueueUserWorkItem 将 VideoRenderer 实例作为对象参数传递。
RenderFrames 循环直到 MyVideo 说通过更改它的任一布尔“我们正在渲染”属性来停止,它从 Bitmap -> BitmapImage -> Image.Source 进行转换,然后将 VideoContentControl.Content 设置为图像,使用MainWindow 调度程序。
一切正常,视频渲染,但 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);
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();
));
【问题讨论】:
我认为您需要利用 GPU 来确保这项工作可靠。 【参考方案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 指向它,然后简单地将图片数据传送到它。见:What is a fast way to generate and draw video in WPF?
【讨论】:
以上是关于如何在不冻结 GUI 的情况下使用视频帧不断更新 contentcontrol?的主要内容,如果未能解决你的问题,请参考以下文章
如何在不冻结 gui 的情况下运行 QProcess 的同步链?
如何在不冻结 GUI 的情况下让 AudioQueue 播放?
如何在不冻结 UI 的情况下使用 QProcess 循环的输出更新 UI?