溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何解決WPF下YUV播放D3D的問題

發(fā)布時間:2021-07-20 13:56:48 來源:億速云 閱讀:253 作者:小新 欄目:開發(fā)技術(shù)

小編給大家分享一下如何解決WPF下YUV播放D3D的問題,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

在視頻媒體播放,監(jiān)控系統(tǒng)的構(gòu)建當(dāng)中,經(jīng)常會涉及到Y(jié)UV數(shù)據(jù)的顯示問題。一般的播放控件以及SDK都是通過使用Window句柄,利用DirectDraw直接在窗口上渲染。但是,如果用戶界面是使用WPF開發(fā)的時候,通常只能通過WinFormHost在WPF界面中嵌入WinForm來完成。但這么做會遇到AeroSpace的問題,即winform的控件永遠(yuǎn)浮在WPF的最上層,任何WPF元素都會被蓋住,同時縮放和拖動的時候都會造成很差的用戶體驗。原因是由于WPF和Winform使用了不同的渲染技術(shù)。

要在WPF中完美的支持YUV數(shù)據(jù)的顯示,通常的解決方式是使用先把YUV數(shù)據(jù)轉(zhuǎn)換成WPF可以支持的RGB數(shù)據(jù),然后利用類似于WriteableBitmap的控件,把他展現(xiàn)在WPF上。這么做的主要問題是在做RGB轉(zhuǎn)換的時候,需要消耗大量的CPU, 效率比較低。一種優(yōu)化方式是使用FFMPEG里的SwScale或者Intel的IPP庫,這些庫經(jīng)過了一定的優(yōu)化,可以有限度的使用硬件加速。下面為一個使用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中顯示。一個參考就是VMR9在WPF中的應(yīng)用。VMR9是微軟提供的DirectShow的Render。經(jīng)過仔細(xì)參考了WpfMediaTookit中VMR9相關(guān)的代碼后,其核心的思想就是在初始化DirectShow構(gòu)建VMR9渲染器時,讓其輸出一個D3D9Surface,D3DImage將使用該Surface作為BackBuffer。當(dāng)有新的視頻幀在該Surface渲染完成后,VMR9將發(fā)送一個事件通知。收到通知后,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時,注冊新視頻幀渲染完成事件 
 allocator.NewAllocatorFrame += new Action(allocator_NewAllocatorFrame); 
 // 注冊接收新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上完美顯示。但很多時候,DirectShow不能解決所有問題。例如在做交互式視頻優(yōu)化處理或是視頻疊加的時候, 采用固定濾鏡流水線的DirectShow很難滿足要求。有的時候還是需要便捷的直接渲染的方式。

由VMR9的例子我們可以看出,產(chǎn)生出一個D3D9Surface并在上面渲染是其中的關(guān)鍵。那么剩下的問題就是如何把YUV數(shù)據(jù)渲染到D3D9Surface。

D3D沒有直接支持YUV圖像格式。因此需要我們想辦法讓D3D能夠渲染YUV數(shù)據(jù)。在用C#改寫的過程當(dāng)中,我突然發(fā)現(xiàn)D3D已經(jīng)提供了更簡單的方法幫助我們實現(xiàn)YUV到RGB顏色空間的轉(zhuǎn)換,而且是通過顯卡硬件直接支持。效率相當(dāng)?shù)母?。主要原理就是借助D3DDevice的StrentchRectangle方法。

public void StretchRectangle( 
 Surface sourceSurface, 
 Rectangle sourceRectangle, 
 Surface destSurface, 
 Rectangle destRectangle, 
 TextureFilter filter 
);

StrentchRectangle方法的主要功能是將一個Surface上的某個區(qū)域的內(nèi)容拷貝到另一個Surface的指定區(qū)域中。在Copy的過程當(dāng)中,只要是顯卡直接支持的格式,如YV12,YUY2等等, 都會自動的進行D3D PixelFormat的轉(zhuǎn)換!因此,我們只需要創(chuàng)建一個指定好PixelFormat的D3D OffscreenPlainSurface, 把原始數(shù)據(jù)填充進去,調(diào)用StrentchRectangle向目標(biāo)Surface拷貝,我們就得到了想要的Surface。剩下的事情就交給D3DImage了。下面是例子代碼的核心部分

public void Render(IntPtr imgBuffer) 
{ 
 lock (this.renderLock) 
 { 
  // 將圖像數(shù)據(jù)填充進offscreen surface 
  this.FillBuffer(imgBuffer); 
  // 調(diào)用StrentchRectangle把原始圖像數(shù)據(jù)Copy到TextureSurface中         
  this.StretchSurface(); 
  // 執(zhí)行渲染操作 
  this.CreateScene(); 
 } 
 // 通知D3DImage刷新圖像 
 this.InvalidateImage(); 
}

以上是“如何解決WPF下YUV播放D3D的問題”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI