简体   繁体   English

最快的方式解码和同时显示许多H264视频C#

[英]Fastest way to Decode and Display many H264 Videos Simultaneously C#

As you might surmise from the question title, we are required to decode and display multiple (eg, eight) H.264 encoded videos at the same time (and keep them all time synchronized, but that's another question for another time). 正如您可能从问题标题中推测的那样,我们需要同时解码和显示多个(例如,8个)H.264编码视频(并保持它们所有时间同步,但这是另一个问题的另一个问题)。 The videos are usually at at 25 FPS with a resolution of 640x480. 视频通常为25 FPS,分辨率为640x480。

I'm going to provide a bit of background before I get to the crux of the problem. 在我解决问题的关键之前,我将提供一些背景知识。

The feature needs to be baked into a fairly large C# 3.5 (WinForms) application. 该功能需要融入相当大的C#3.5(WinForms)应用程序。 The videos will occupy rectangles in the application - the managed code needs to be able to specify where each video is drawn as well as it's size. 视频将占用应用程序中的矩形 - 托管代码需要能够指定每个视频的绘制位置以及大小。

We get the H264 packets in C# and fire them into a native H264 decoder to get YUV12 image data. 我们在C#中获取H264数据包并将其激发到本机H264解码器以获取YUV12图像数据。

An early attempt consisted of converting the YUV12 images to RGB24 and BitBlt'ing them to a HWND passed into the native code from C#. 早期的尝试包括将YUV12图像转换为RGB24,将BitBlt将它们转换为从C#传递到本机代码的HWND。 While functional, all BitBlt'ing had to happen on the UI thread which caused it to bog down when displaying more than a couple videos (on a 2.6 GHZ core 2 duo). 功能上,所有BitBlt都必须在UI线程上发生,这导致它在显示多个视频时陷入困境(在2.6 GHZ核心2 duo上)。

The current attempt spins up one-thread-per-cpu-core on startup and load balances the decoding/displaying of videos across these threads. 当前尝试在启动时旋转每个cpu-core的一个线程并负载平衡跨这些线程的视频解码/显示。 The performance of this is mind-blasting (I find watching task manager much more interesting than the videos being displayed). 这种表现是令人兴奋的(我发现观看任务管理器比显示的视频更有趣)。 UI-wise, it leaves a lot to be desired. UI方面,它留下了很多不足之处。

The millisecond we started drawing to an HWND created on the UI thread (eg, a panel docked in a WinForms control) from a non-UI thread, we started getting all sorts of funky behavior due to the un-thread-safeness of WinForms. 我们从非UI线程开始绘制在UI线程(例如,停靠在WinForms控件中的面板)上创建的HWND毫秒,由于WinForms的非线程安全性,我们开始获得各种各样的时髦行为。 This led us to create the HWND's in native code and draw to those, with C# providing the rectangles they should be drawn to in screen coordinates. 这导致我们用本机代码创建HWND并绘制到那些,用C#提供它们应该在屏幕坐标中绘制的矩形。

Gah! 尔加! CanOfWorms.Open(). CanOfWorms.Open()。

Problem: When the C# application receives focus, it jumps to the front of the Z-Order and hides the video windows. 问题:当C#应用程序获得焦点时,它会跳转到Z-Order的前面并隐藏视频窗口。 Solution: Place the video windows Always On Top. 解决方案:将视频窗口始终放在顶部。

Problem: When the user switches to another application, the video windows are still on top. 问题:当用户切换到另一个应用程序时,视频窗口仍然位于顶部。 Solution: Detect activation and deactivation of the C# application and show/hide the video windows accordingly. 解决方案:检测C#应用程序的激活和停用,并相应地显示/隐藏视频窗口。

Problem: User says, "I want my videos playing on one monitor while I edit a Word document in the other!" 问题:用户说:“我希望我在另一台显示器上编辑Word文档时在一台显示器上播放视频!” Solution: Tell user to shut up and that Word sucks anyways. 解决方案:告诉用户关闭并且Word无论如何都很糟糕。

Problem: I get fired. 问题:我被解雇了。

etc. etc. 等等

I guess the crux of the problem is that we have HWND's created on a non-UI thread and we want to 'simulate' those being embedded in the C# application. 我想问题的关键在于我们在非UI线程上创建了HWND,并且我们想要“模拟”嵌入在C#应用程序中的那些。

Any thoughts/suggestions? 有什么想法/建议? Am I completely out to lunch here? 我完全在这里吃午饭吗?

I'm more than open to taking a completely different approach if one exists (This project required a lot of learning - winning the lottery would have a greater likelihood than me having picked the best approach at every step along the road). 如果存在一个完全不同的方法,我就会采取完全不同的方法(这个项目需要大量的学习 - 赢得彩票的可能性比我在路上的每一步选择最佳方法的可能性更大)。

Forget about BitBlt-ing and do this: 忘记BitBlt-ing并执行此操作:

  • for each window you want your video to be played, create one DirectShow graph and attach the renderer of the graph to that window 对于您希望播放视频的每个窗口,创建一个DirectShow图形并将图形的渲染器附加到该窗口
  • before renderer in the graph put the samplegrabber filter. 在图表中的渲染器之前放入samplegrabber过滤器。 It will allow you to have callback in which you'll be able to fill the buffer 它将允许您进行回调,您可以在其中填充缓冲区
  • instead of blitting, decode to the buffer provided in samplegrabber. 而不是blitting,解码到samplegrabber中提供的缓冲区。

In addition, I guess that you'll be able to put raw YUV12 into the buffer, as VMRenderer is able to display them directly. 另外,我猜你可以将原始YUV12放入缓冲区,因为VMRenderer能够直接显示它们。

Use DirectShowNet library. 使用DirectShowNet库。

EDIT: 编辑:

And yes, BTW, if the videos are on the same 'canvas', you can use same technique with renderer and create only one large window, then shift decoded video rectangles 'by hand' and put them into the framebuffers buffer. 是的,顺便说一下,如果视频在同一个“画布”上,你可以使用与渲染器相同的技术并只创建一个大窗口,然后“手动”移动解码的视频矩形并将它们放入帧缓冲区缓冲区。

YET ANOTHER EDIT: 再编辑:

BitBlts are ALWAYS serialized, ie they can't run in parallel. BitBlts总是被序列化,即它们不能并行运行。

The millisecond we started drawing to an HWND created on the UI thread (eg, a panel docked in a WinForms control) from a non-UI thread, we started getting all sorts of funky behavior due to the un-thread-safeness of WinForms. 我们从非UI线程开始绘制在UI线程(例如,停靠在WinForms控件中的面板)上创建的HWND毫秒,由于WinForms的非线程安全性,我们开始获得各种各样的时髦行为。 This led us to create the HWND's in native code and draw to those, with C# providing the rectangles they should be drawn to in screen coordinates. 这导致我们用本机代码创建HWND并绘制到那些,用C#提供它们应该在屏幕坐标中绘制的矩形。

What kind of funky behavior? 什么样的时髦行为? If you mean flickering or drawing delay, have you tried to lock() the panel or any other class for thread/drawing synchronisation? 如果你的意思是闪烁或绘制延迟,你是否试图锁定()面板或任何其他类进行线程/绘图同步? Again: Whats the exact problem when you send the data to the decoder, receive a image, convert it and then draw it with an OnPaint handler. 再次:当您将数据发送到解码器,接收图像,转换它然后使用OnPaint处理程序绘制它时,确切的问题是什么。 (Setup a different thread that loops at 25fps, call panel1.Invalidate() then) (设置一个以25fps循环的不同线程,调用panel1.Invalidate()然后)

I guess the crux of the problem is that we have HWND's created on a non-UI thread and we want to 'simulate' those being embedded in the C# application. 我想问题的关键在于我们在非UI线程上创建了HWND,并且我们想要“模拟”嵌入在C#应用程序中的那些。

Don't do that. 不要那样做。 Try to draw the received data in your c# application. 尝试在c#应用程序中绘制接收的数据。 In general, I wouldn't reccomend mixing native code and c#. 一般来说,我不会建议混合本机代码和c#。 Having the h264 decoder in native code is the only exception here. 在本机代码中使用h264解码器是唯一的例外。

Use your threads to decode the video packets (as you already do) then have one thread that loops and calls Invalidate(as said above). 使用你的线程解码视频数据包(正如你已经做的那样),然后让一个线程循环并调用Invalidate(如上所述)。 Then have an OnPaint handler for each panel you are displaying a video in. In this handler get the most recent video picture and draw it (e.Graphics). 然后为要显示视频的每个面板都有一个OnPaint处理程序。在此处理程序中获取最新的视频图片并绘制它(e.Graphics)。

I hope this helps, but also need more information about the problem... 我希望这有所帮助,但也需要有关该问题的更多信息......

I like the DirectShow answer posted earlier, but I wanted to include an additional option that might be easier for you to implement, based on this excerpt from your question: 我喜欢之前发布的DirectShow答案,但我希望根据您的问题摘录中包含一个可能更容易实现的附加选项:

While functional, all BitBlt'ing had to happen on the UI thread which caused it to bog down when displaying more than a couple videos 功能上,所有BitBlt都必须在UI线程上发生,导致它在显示多个视频时陷入困境

My idea is to start from that code, and use the Async CTP for Visual Studio 2010 that is currently available and includes a go-live license. 我的想法是从该代码开始,并使用当前可用的 Visual Studio 2010的Async CTP并包含上线许可证。 From there it should be a relatively simple to modify this existing code to be more responsive: just add await and async keywords in a few places and the rest of the code should be largely unchanged. 从那里开始修改现有代码应该是一个相对简单的响应:只需在几个地方添加await和async关键字,其余的代码应该基本保持不变。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM