FFmpeg DXVA2解碼獲得的數據使用surface來承載的,surface限制不少,若是能用紋理來渲染的話,那咱們就能夠充分開發D3D,好比能夠用座標變換來實現電子放大的功能,還能夠用座標變換來實現視頻圖像任意角度的旋轉等功能。而對於我來講,最重要的是紋理渲染可使得解碼後的數據可以用像素着色器來作簡單的視頻圖像處理,若是是用的是D3D11,對於更爲複雜的視頻圖像處理算法也是有望能夠用Compute Shader實現,以便充分利用顯卡來加速和釋放CPU。html
DXVA2解碼數據用紋理渲染的方法其實就是D3D中的渲染到紋理,只是有幾個參數須要注意一下。算法
紋理設置與常規的紋理使用流程同樣。windows
static bool setup_texture(IDirect3DDevice9* Device, int Width, int Height,D3DFORMAT format) { if (!Device) { return false ; } HRESULT hr = 0; hr = Device->CreateVertexBuffer( 4 * sizeof(Dxva2TexVertex), D3DUSAGE_WRITEONLY, Dxva2TexVertex::FVF, D3DPOOL_MANAGED, &QuadVB, 0); Dxva2TexVertex* v = 0; QuadVB->Lock(0, 0, (void**)&v, 0); v[0] = Dxva2TexVertex(-20.0f, 20.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f); v[1] = Dxva2TexVertex( 20.0f, 20.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f); v[2] = Dxva2TexVertex( 20.0f, -20.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f); v[3] = Dxva2TexVertex(-20.0f, -20.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f); QuadVB->Unlock(); D3DXMATRIX P; D3DXMatrixPerspectiveFovLH(&P, D3DX_PI * 0.5f, 1.0f, 1.0f, //近裁減面到座標原點的距離 1000.0f //遠裁減面到原點的距離 ); Device->SetTransform(D3DTS_PROJECTION, &P); Device->SetRenderState(D3DRS_LIGHTING, false); D3DXVECTOR3 position( 0.0f, 0.0f, -20.0f); D3DXVECTOR3 target(0.0f, 0.0f, 0.0f); D3DXVECTOR3 up(0.0f, 1.0f, 0.0f); D3DXMATRIX V; D3DXMatrixLookAtLH(&V, &position, &target, &up);//計算取景變換矩陣 Device->SetTransform(D3DTS_VIEW, &V);//取景變換 hr = Device->CreateTexture ( Width, Height, 1, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &g_SurfaceTexture, NULL ) ; if (FAILED(hr)) return false; g_SurfaceTexture->GetSurfaceLevel(0, &g_OffScreenSurface); return true; }
其中須要注意其中的如下代碼:緩存
hr = Device->CreateTexture ( Width, Height, 1, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &g_SurfaceTexture, NULL ) ; if (FAILED(hr)) return false; g_SurfaceTexture->GetSurfaceLevel(0, &g_OffScreenSurface);
CreateTexture的第四個參數注意設置爲D3DUSAGE_RENDERTARGET,第五個參數format與設置D3D時的參數中的 d3dpp.BackBufferFormat = d3ddm.Format; 保持一致,詳見工程源碼。GetSurfaceLevel可以拿到具體某個level的mipmap的surface,我獲取的是g_SurfaceTexture在level爲0的surface,即g_OffScreenSurface。函數
渲染過程是先把DXVA2解碼的數據先渲染到紋理,而後經過紋理來顯示數據的。oop
static int dxva2_retrieve_data(AVCodecContext *s, AVFrame *frame) { LPDIRECT3DSURFACE9 surface = (LPDIRECT3DSURFACE9)frame->data[3]; InputStream *ist = (InputStream *)s->opaque; DXVA2Context *ctx = (DXVA2Context *)ist->hwaccel_ctx; HRESULT hr ; int ret = 0 ; EnterCriticalSection(&cs); if (ctx->d3d9device && g_OffScreenSurface) { ctx->d3d9device->SetRenderTarget(0, g_OffScreenSurface); ctx->d3d9device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(200, 200, 200), 1.0f, 0); ctx->d3d9device->BeginScene(); ctx->d3d9device->SetTexture(0, NULL); GetClientRect(d3dpp.hDeviceWindow, &m_rtViewport); ctx->d3d9device->StretchRect(surface, NULL, g_OffScreenSurface, NULL, D3DTEXF_LINEAR); ctx->d3d9device->EndScene(); ctx->d3d9device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &m_pBackBuffer); ctx->d3d9device->SetRenderTarget(0, m_pBackBuffer); ctx->d3d9device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0); ctx->d3d9device->BeginScene(); ctx->d3d9device->SetTexture(0, g_SurfaceTexture); ctx->d3d9device->SetFVF(Dxva2TexVertex::FVF); ctx->d3d9device->SetStreamSource(0, QuadVB, 0, sizeof(Dxva2TexVertex)); ctx->d3d9device->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2); ctx->d3d9device->EndScene(); hr = ctx->d3d9device->Present(NULL, NULL, NULL, NULL); if (FAILED(hr)) { if (ctx->d3d9device->TestCooperativeLevel() == D3DERR_DEVICENOTRESET) { printf("Failed to Present !") ; ret = -1 ; } } else { ret = 0 ; } } LeaveCriticalSection(&cs); return ret; }
能夠看到代碼中有兩組spa
ctx->d3d9device->BeginScene();
····
ctx->d3d9device->EndScene();
第一組經過 ctx->d3d9device->SetRenderTarget(0, g_OffScreenSurface); 把渲染目標設置爲紋理的surface,把DXVA2解碼獲得的數據渲染到前面準備好的紋理的surface中;第二組則把渲染目標設爲後臺緩存,直接把紋理渲染出來便可。作這一層折騰的緣由在於StretchRect函數的一個限制,.net
大意就是若是源或者目的surface是個紋理surface,就須要查看驅動是否支持,而3d
詳見https://msdn.microsoft.com/en-us/library/windows/desktop/bb174471(v=vs.85).aspxcode
因此我在前面強調建立紋理的第四個參數注意設置爲D3DUSAGE_RENDERTARGET。起初我也是由於這個參數設錯了,一直無法成功,後來忽然想到RT texture多是指設爲D3DUSAGE_RENDERTARGET的texture,RT多是RENDERTARGET的縮寫,而後才成功的。Off-screen plain指離屏表面,我對D3D的一些概念不是特別清楚,不知道承載DXVA2解碼數據的surface是否是離屏表面,但我試了許多方法,只有這樣才最後成功。若是這一塊的理解有問題,歡迎拍磚指教。
對於FFmpeg DXVA2硬解有疑問的,能夠參考http://www.cnblogs.com/betterwgo/p/6125507.html。對於D3D有疑問的,請自行上網查詢,我懂的也很少,能夠相互交流一下。
完整工程源碼:http://download.csdn.net/download/qq_33892166/9742467
運行工程的時候注意修改代碼中視頻文件的路徑。