WPF下YUV播放的D3D解決方案
在視頻媒體播放,監(jiān)控系統(tǒng)的構(gòu)建當(dāng)中,經(jīng)常會(huì)涉及到Y(jié)UV數(shù)據(jù)的顯示問題。一般的播放控件以及SDK都是通過使用Window句柄,利用DirectDraw直接在窗口上渲染。但是,如果用戶界面是使用WPF開發(fā)的時(shí)候,通常只能通過WinFormHost在WPF界面中嵌入WinForm來完成。但這么做會(huì)遇到AeroSpace的問題,即winform的控件永遠(yuǎn)浮在WPF的最上層,任何WPF元素都會(huì)被蓋住,同時(shí)縮放和拖動(dòng)的時(shí)候都會(huì)造成很差的用戶體驗(yàn)。原因是由于WPF和Winform使用了不同的渲染技術(shù)。
要在WPF中完美的支持YUV數(shù)據(jù)的顯示,通常的解決方式是使用先把YUV數(shù)據(jù)轉(zhuǎn)換成WPF可以支持的RGB數(shù)據(jù),然后利用類似于WriteableBitmap的控件,把他展現(xiàn)在WPF上。這么做的主要問題是在做RGB轉(zhuǎn)換的時(shí)候,需要消耗大量的CPU, 效率比較低。一種優(yōu)化方式是使用FFMPEG里的SwScale或者Intel的IPP庫(kù),這些庫(kù)經(jīng)過了一定的優(yōu)化,可以有限度的使用硬件加速。下面為一個(gè)使用WritableBitmap的例子。
WriteableBitmap imageSource = new WriteableBitmap(videoWidth, videoHeight, DPI_X, DPI_Y, System.Windows.Media.PixelFormats.Bgr32, null); ... int rgbSize = width * height * 4; // bgr32 IntPtr rgbPtr = Marshal.AllocHGlobal(rgbSize); YV12ToRgb(yv12Ptr, rgbPtr, width, height); // 更新圖像 imageSource.Lock(); Interop.Memcpy(this.imageSource.BackBuffer, rgbPtr, rgbSize); imageSource.AddDirtyRect(this.imageSourceRect); imageSource.Unlock(); Marshal.FreeHGlobal(rgbPtr);
另一種解決方法是使用D3DImage作為WPF與顯卡的橋梁。我們可以借助D3DImage,直接將D3D渲染過后的部分送到WPF中顯示。一個(gè)參考就是VMR9在WPF中的應(yīng)用。VMR9是微軟提供的DirectShow的Render。經(jīng)過仔細(xì)參考了WpfMediaTookit中VMR9相關(guān)的代碼后,其核心的思想就是在初始化DirectShow構(gòu)建VMR9渲染器時(shí),讓其輸出一個(gè)D3D9Surface,D3DImage將使用該Surface作為BackBuffer。當(dāng)有新的視頻幀在該Surface渲染完成后,VMR9將發(fā)送一個(gè)事件通知。收到通知后,D3DImage刷新一下BackBuffer即可。下面代碼展現(xiàn)了核心思想部分。
private VideoMixingRenderer9 CreateRenderer() {
var result = new VideoMixingRenderer9();
var cfg = result as IVMRFilterConfig9;
cfg.SetNumberOfStreams(1);
cfg.SetRenderingMode(VMR9Mode.Renderless);
var notify = result as IVMRSurfaceAllocatorNotify9;
var allocator = new Vmr9Allocator();
notify.AdviseSurfaceAllocator(m_userId, allocator);
allocator.AdviseNotify(notify);
// 在構(gòu)建VMR9 Render時(shí),注冊(cè)新視頻幀渲染完成事件
allocator.NewAllocatorFrame += new Action(allocator_NewAllocatorFrame);
// 注冊(cè)接收新D3DSurface被創(chuàng)建的事件
allocator.NewAllocatorSurface += new NewAllocatorSurfaceDelegate(allocator_NewAllocatorSurface);
return result;
}
void allocator_NewAllocatorSurface(object sender, IntPtr pSurface)
{
// 為了方便理解,只保留核心部分。省略改寫了其他部分
...
// 將pSurface設(shè)置為D3DImage的BackBuffer
this.m_d3dImage.Lock();
this.m_d3dImage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, pSurface);
this.m_d3dImage.Unlock();
...
}
void allocator_NewAllocatorFrame()
{
...
// 重繪
this.m_d3dImage.Lock();
this.m_d3dImage.AddDirtyRect(new Int32Rect(0, /* Left */
0, /* Top */
this.m_d3dImage.PixelWidth, /* Width */
this.m_d3dImage.PixelHeight /* Height */));
this.m_d3dImage.Unlock();
...
}
由此,只要是使用DirectShow的視頻播放就可以借助VMR9在WPF上完美顯示。但很多時(shí)候,DirectShow不能解決所有問題。例如在做交互式視頻優(yōu)化處理或是視頻疊加的時(shí)候, 采用固定濾鏡流水線的DirectShow很難滿足要求。有的時(shí)候還是需要便捷的直接渲染的方式。
由VMR9的例子我們可以看出,產(chǎn)生出一個(gè)D3D9Surface并在上面渲染是其中的關(guān)鍵。那么剩下的問題就是如何把YUV數(shù)據(jù)渲染到D3D9Surface。
D3D沒有直接支持YUV圖像格式。因此需要我們想辦法讓D3D能夠渲染YUV數(shù)據(jù)。在用C#改寫的過程當(dāng)中,我突然發(fā)現(xiàn)D3D已經(jīng)提供了更簡(jiǎn)單的方法幫助我們實(shí)現(xiàn)YUV到RGB顏色空間的轉(zhuǎn)換,而且是通過顯卡硬件直接支持。效率相當(dāng)?shù)母摺V饕砭褪墙柚鶧3DDevice的StrentchRectangle方法。
public void StretchRectangle( Surface sourceSurface, Rectangle sourceRectangle, Surface destSurface, Rectangle destRectangle, TextureFilter filter );
StrentchRectangle方法的主要功能是將一個(gè)Surface上的某個(gè)區(qū)域的內(nèi)容拷貝到另一個(gè)Surface的指定區(qū)域中。在Copy的過程當(dāng)中,只要是顯卡直接支持的格式,如YV12,YUY2等等, 都會(huì)自動(dòng)的進(jìn)行D3D PixelFormat的轉(zhuǎn)換!因此,我們只需要?jiǎng)?chuàng)建一個(gè)指定好PixelFormat的D3D OffscreenPlainSurface, 把原始數(shù)據(jù)填充進(jìn)去,調(diào)用StrentchRectangle向目標(biāo)Surface拷貝,我們就得到了想要的Surface。剩下的事情就交給D3DImage了。下面是例子代碼的核心部分
public void Render(IntPtr imgBuffer)
{
lock (this.renderLock)
{
// 將圖像數(shù)據(jù)填充進(jìn)offscreen surface
this.FillBuffer(imgBuffer);
// 調(diào)用StrentchRectangle把原始圖像數(shù)據(jù)Copy到TextureSurface中
this.StretchSurface();
// 執(zhí)行渲染操作
this.CreateScene();
}
// 通知D3DImage刷新圖像
this.InvalidateImage();
}
以上所述是小編給大家介紹的WPF下YUV播放的D3D解決方案,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
.NET內(nèi)存泄漏分析Windbg項(xiàng)目實(shí)例
這篇文章介紹了.NET內(nèi)存泄漏分析Windbg項(xiàng)目實(shí)例,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-12-12
ASP.NET MVC懶加載如何逐步加載數(shù)據(jù)庫(kù)信息
在ASP.NET MVC中實(shí)現(xiàn)數(shù)據(jù)庫(kù)的逐步加載可通過懶加載技術(shù)完成,首先,在EntityFramework中配置數(shù)據(jù)庫(kù)上下文,使用對(duì)應(yīng)的實(shí)體類映射數(shù)據(jù)庫(kù)表,本文給大家介紹ASP.NET MVC懶加載如何逐步加載數(shù)據(jù)庫(kù)信息,感興趣的朋友跟隨小編一起看看吧2024-10-10
服務(wù)器讀取EXCEL不安裝OFFICE如何實(shí)現(xiàn)
用asp.net做了一簡(jiǎn)單的游戲管理后臺(tái),涉及到了上傳Excel導(dǎo)入數(shù)據(jù)的功能,在本地開發(fā)實(shí)現(xiàn)都好好的,可已上傳的服務(wù)器上就悲劇了,下面有個(gè)不錯(cuò)的解決方法,大家可以參考下2014-03-03
解析WPF綁定層次結(jié)構(gòu)數(shù)據(jù)的應(yīng)用詳解
本文講述WPF中單層次數(shù)據(jù)和多層次數(shù)據(jù)的綁定方法,主要闡述數(shù)據(jù)綁定的顯示層面,其中涉及了ListBox和Treeview控件。并說明它們之間的差異2013-05-05
ASP.NET JSON字符串與實(shí)體類的互轉(zhuǎn)換示例代碼
本篇文章主要是對(duì)ASP.NET JSON字符串與實(shí)體類的互轉(zhuǎn)換的示例代碼進(jìn)行了介紹,需要的朋友可以過來參考下,希望對(duì)大家有所幫助2014-01-01
MVC使用Controller代替Filter完成登錄驗(yàn)證(Session校驗(yàn))學(xué)習(xí)筆記5
這篇文章主要介紹了MVC使用Controller代替Filter完成登錄驗(yàn)證即Session校驗(yàn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09
asp.net DZ論壇中根據(jù)IP地址取得所在地的代碼
從dz .net版發(fā)現(xiàn)的這個(gè)不錯(cuò)的函數(shù),大家以后就可以方便調(diào)用了2008-10-10
用Fine Uploader+ASP.NET MVC實(shí)現(xiàn)ajax文件上傳[代碼示例]
Fine Uploader(http://fineuploader.com/)是一個(gè)實(shí)現(xiàn) ajax 上傳文件的 Javascript 組件2013-01-01
.Net極限生產(chǎn)力之分表分庫(kù)全自動(dòng)化Migrations?Code-First
這篇文章主要介紹了.Net極限生產(chǎn)力之分表分庫(kù)全自動(dòng)化Migrations?Code-First,輕量級(jí)針對(duì)分表分庫(kù)讀寫分離的解決方案,具有零依賴、零學(xué)習(xí)成本、零業(yè)務(wù)代碼入侵適配2022-07-07

