我有一个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中生成和绘制视频的快速方法是什么?