您的位置:首页 > 其它

视频抓图(有硬编码解码)一获得解码后数据

2015-07-01 14:43 555 查看

一、解决方案

抓图就是保存视频帧,对于有硬编解码的视频,我们需要在编码前,或者解码后,获得视频帧。考虑到准确定位到要抓取的画面的帧,在接收流上做,是一个不错的选择。因为硬编解码的存在,好多事情都变得不那么简单了,比如GetCurrentImageFilter方法肯定不行了,在解码filter后连接一个抓图filter也不能达到预期效果,测试发现这个filter虽然连接上了,但是却工作不了,因为硬编码最后数据放到了显存中,硬解码filter跟最终的EVR(EnhancedVideo Renderer)是经过了特定协商的,才成功把视频帧传下去了。如果中间插入一个filter,硬解码数据就不能成功传过来,所以就工作不了了。那么解决方案是什么了?一个比较好的也是较简单的就是在解码filter上添加一个接口,再解码完成后,保存视频帧,这完全不影响它正常传给EVR。那么我们面对的问题,就主要有以下几个:获得到显存中数据;将帧格式转为rgb格式;将rgb数据生成一个bmp图片。

二、获取数据

对于STDMETHODIMP GetPointer(BYTE ** ppBuffer);方法,大家是不是格外亲切熟悉了,是的,一般通过这个方法就能获得sample内存里的数据。但是硬解码后,再用这个方法会发现得不到任何数据,它确实得不到,也不可能得到,因为此时数据没有在内存中,所以我们要自己想办法得到数据,通过看硬解码filter的allocator发现EVR是通过MR_BUFFER_SERVICE获得surface的,注意这个surface是IDirect3DSurface9,然后调用它的LockRect方法,就获得数据了。

1.IMFGetService

<strong>HRESULT CDXVA2Sample::QueryInterface(REFIID riid, void **ppv)
{
CheckPointer(ppv, E_POINTER);

if (riid == __uuidof(IMFGetService))
{
return GetInterface((IUnknown*)static_cast<IMFGetService*>(this), ppv);
}

return CMFXSample::QueryInterface(riid, ppv);
}</strong>

2.从sample上获得获得service

IMFGetService    *pGetService;
hr = pOutSample->QueryInterface(__uuidof(IMFGetService), (void**)&pGetService);  //这个pOutSample就是要最后要往下deliver的

3.获得IDirect3DSurface9

hr = pGetService->GetService(MR_BUFFER_SERVICE, __uuidof(IDirect3DSurface9), (void**)&pD3DSurface);

4.现在需要从pD3DSurface中获得数据,用到IDirect3DSurface的LockRect方法。

mfxStatus LockFrame(IDirect3DSurface9 *surface, mfxFrameData *ptr)
{
IDirect3DSurface9 *pSurface = surface;
if (pSurface == 0)
return MFX_ERR_INVALID_HANDLE;

if (ptr == 0)
return MFX_ERR_LOCK_MEMORY;

D3DSURFACE_DESC desc;
HRESULT hr = pSurface->GetDesc(&desc);
if (FAILED(hr))
return MFX_ERR_LOCK_MEMORY;

if (desc.Format != D3DFMT_NV12 &&
desc.Format != D3DFMT_YV12 &&
desc.Format != D3DFMT_YUY2 &&
desc.Format != D3DFMT_R8G8B8 &&
desc.Format != D3DFMT_A8R8G8B8 &&
desc.Format != D3DFMT_P8)
return MFX_ERR_LOCK_MEMORY;

D3DLOCKED_RECT locked;

hr = pSurface->LockRect(&locked, 0, D3DLOCK_NOSYSLOCK);
if (FAILED(hr))
return MFX_ERR_LOCK_MEMORY;

switch ((DWORD)desc.Format)
{
case D3DFMT_NV12:
ptr->Pitch = (mfxU16)locked.Pitch;
ptr->Y = (mfxU8 *)locked.pBits;
ptr->U = (mfxU8 *)locked.pBits + desc.Height * locked.Pitch;
ptr->V = ptr->U + 1;
break;
case D3DFMT_YV12:
ptr->Pitch = (mfxU16)locked.Pitch;
ptr->Y = (mfxU8 *)locked.pBits;
ptr->V = ptr->Y + desc.Height * locked.Pitch;
ptr->U = ptr->V + (desc.Height * locked.Pitch) / 4;
break;
case D3DFMT_YUY2:
ptr->Pitch = (mfxU16)locked.Pitch;
ptr->Y = (mfxU8 *)locked.pBits;
ptr->U = ptr->Y + 1;
ptr->V = ptr->Y + 3;
break;
case D3DFMT_R8G8B8:
ptr->Pitch = (mfxU16)locked.Pitch;
ptr->B = (mfxU8 *)locked.pBits;
ptr->G = ptr->B + 1;
ptr->R = ptr->B + 2;
break;
case D3DFMT_A8R8G8B8:
ptr->Pitch = (mfxU16)locked.Pitch;
ptr->B = (mfxU8 *)locked.pBits;
ptr->G = ptr->B + 1;
ptr->R = ptr->B + 2;
ptr->A = ptr->B + 3;
break;
case D3DFMT_P8:
ptr->Pitch = (mfxU16)locked.Pitch;
ptr->Y = (mfxU8 *)locked.pBits;
ptr->U = 0;
ptr->V = 0;
break;
}
return MFX_ERR_NONE;
}
<pre class="cpp" name="code" style="font-size: 14.039999961853px; font-weight: bold;">//数据就放到ptr中去了,然后对数据进行处理,最后unlock掉
mfxStatus CDecVideoFilter::UnlockFrame(IDirect3DSurface9 *surface, mfxFrameData *ptr){IDirect3DSurface9 *pSurface =surface;if (pSurface == 0)return MFX_ERR_INVALID_HANDLE;pSurface->UnlockRect();if (NULL != ptr){ptr->Pitch = 0;ptr->Y     = 0;ptr->U     = 0;ptr->V     = 0;}return MFX_ERR_NONE;}
三、至此已经获取到数据了,注意对数据进行处理后,要调用unlockframe方法。


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: