未压缩的视频缓冲区
本文介绍如何使用包含未压缩视频帧的媒体缓冲区。 根据首选项顺序,可以使用以下选项。 并非每个媒体缓冲区都支持每个选项。
- 使用基础 Direct3D 图面。 (仅适用于 Direct3D surfaces.) 中存储的视频帧
- 使用 IMF2DBuffer 接口。
- 使用 IMFMediaBuffer 接口。
使用基础 Direct3D Surface
视频帧可能存储在 Direct3D 图面中。 如果是这样,可以通过在媒体缓冲区对象上调用 IMFGetService::GetService 或 MFGetService 来获取指向图面的指针。 使用服务标识符MR_BUFFER_SERVICE。 当访问视频帧的组件设计为使用 Direct3D 时,建议使用此方法。 例如,支持 DirectX 视频加速的视频解码器应使用此方法。
以下代码演示如何从媒体缓冲区获取 IDirect3DSurface9 指针。
IDirect3DSurface9 *pSurface = NULL;
hr = MFGetService(
pBuffer,
MR_BUFFER_SERVICE,
__uuidof(IDirect3DSurface9),
(void**)&pSurface
);
if (SUCCEEDED(hr))
{
// Call IDirect3DSurface9 methods.
}
以下对象支持 MR_BUFFER_SERVICE 服务:
使用 IMF2DBuffer 接口
如果视频帧未存储在 Direct3D 图面中,或者组件未设计为使用 Direct3D,则访问视频帧的下一种方法是查询 IMF2DBuffer 接口的缓冲区。 此接口专为图像数据设计。 若要获取指向此接口的指针,请在媒体缓冲区上调用 QueryInterface 。 并非所有媒体缓冲区对象都公开此接口。 但是,如果媒体缓冲区确实公开 IMF2DBuffer 接口,则应使用该接口访问数据(如果可能)而不是使用 IMFMediaBuffer。 你仍然可以使用 IMFMediaBuffer 接口,但效率可能更低。
- 在媒体缓冲区上调用 QueryInterface 以获取 IMF2DBuffer 接口。
- 调用 IMF2DBuffer::Lock2D 以访问缓冲区的内存。 此方法返回指向上行像素的第一个字节的指针。 它还返回图像步幅,即从一行像素开始到下一行开始的字节数。 缓冲区可能包含每行像素之后的填充字节,因此步幅可能比图像宽度宽(以字节为单位)。 如果图像在内存中面向自下而上,则步幅也可能为负值。 有关详细信息,请参阅 图像步幅。
- 仅当需要访问内存时,才将缓冲区锁定。 通过调用 IMF2DBuffer::Unlock2D 解锁缓冲区。
并非每个媒体缓冲区都实现 IMF2DBuffer 接口。 如果媒体缓冲区未 (实现此接口,则缓冲区对象在步骤 1) 中返回E_NOINTERFACE,则必须使用下一步所述的 IMFMediaBuffer 接口接口。
使用 IMFMediaBuffer 接口
如果媒体缓冲区不公开 IMF2DBuffer 接口,请使用 IMFMediaBuffer 接口。 此接口的一般语义在主题 “使用媒体缓冲区”中介绍。
- 在媒体缓冲区上调用 QueryInterface 以获取 IMFMediaBuffer 接口。
- 调用 IMFMediaBuffer::Lock 以访问缓冲区内存。 此方法返回指向缓冲区内存的指针。 使用 Lock 方法时,步幅始终是有问题的视频格式的最低步幅,没有额外的填充字节。
- 仅当需要访问内存时,才将缓冲区锁定。 通过调用 IMFMediaBuffer::Unlock 解锁缓冲区。
如果缓冲区公开 IMF2DBuffer,应始终避免调用 IMFMediaBuffer::Lock,因为 Lock 方法可以将缓冲区对象强制进入连续内存块,然后重新返回。 另一方面,如果缓冲区不支持 IMF2DBuffer, 则 IMFMediaBuffer::Lock 可能不会生成副本。
按如下所示计算媒体类型的最小步幅:
- 最小步幅可能存储在 MF_MT_DEFAULT_STRIDE 属性中。
- 如果未设置 MF_MT_DEFAULT_STRIDE 属性,请调用 MFGetStrideForBitmapInfoHeader 函数来计算最常见视频格式的步幅。
- 如果 MFGetStrideForBitmapInfoHeader 函数无法识别格式,则必须根据格式的定义计算步幅。 在这种情况下,没有一般规则;需要了解格式定义的详细信息。
以下代码演示如何获取最常见的视频格式的默认步幅。
HRESULT GetDefaultStride(IMFMediaType *pType, LONG *plStride)
{
LONG lStride = 0;
// Try to get the default stride from the media type.
HRESULT hr = pType->GetUINT32(MF_MT_DEFAULT_STRIDE, (UINT32*)&lStride);
if (FAILED(hr))
{
// Attribute not set. Try to calculate the default stride.
GUID subtype = GUID_NULL;
UINT32 width = 0;
UINT32 height = 0;
// Get the subtype and the image size.
hr = pType->GetGUID(MF_MT_SUBTYPE, &subtype);
if (FAILED(hr))
{
goto done;
}
hr = MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &width, &height);
if (FAILED(hr))
{
goto done;
}
hr = MFGetStrideForBitmapInfoHeader(subtype.Data1, width, &lStride);
if (FAILED(hr))
{
goto done;
}
// Set the attribute for later reference.
(void)pType->SetUINT32(MF_MT_DEFAULT_STRIDE, UINT32(lStride));
}
if (SUCCEEDED(hr))
{
*plStride = lStride;
}
done:
return hr;
}
根据应用程序,你可能事先知道给定媒体缓冲区是否支持 IMF2DBuffer ((例如,如果创建了缓冲区) )。 否则,必须准备好使用这两个缓冲区接口之一。
以下示例显示了隐藏其中一些详细信息的帮助程序类。 此类具有一个 LockBuffer 方法,该方法调用 Lock2D 或 Lock ,并返回指向第一行像素开头的指针。 (此行为与 Lock2D 方法匹配。) LockBuffer 方法采用默认步幅作为输入参数,并将实际步幅作为输出参数返回。
class CBufferLock
{
public:
CBufferLock(IMFMediaBuffer *pBuffer) : m_p2DBuffer(NULL), m_bLocked(FALSE)
{
m_pBuffer = pBuffer;
m_pBuffer->AddRef();
m_pBuffer->QueryInterface(IID_IMF2DBuffer, (void**)&m_p2DBuffer);
}
~CBufferLock()
{
UnlockBuffer();
SafeRelease(&m_pBuffer);
SafeRelease(&m_p2DBuffer);
}
HRESULT LockBuffer(
LONG lDefaultStride, // Minimum stride (with no padding).
DWORD dwHeightInPixels, // Height of the image, in pixels.
BYTE **ppbScanLine0, // Receives a pointer to the start of scan line 0.
LONG *plStride // Receives the actual stride.
)
{
HRESULT hr = S_OK;
// Use the 2-D version if available.
if (m_p2DBuffer)
{
hr = m_p2DBuffer->Lock2D(ppbScanLine0, plStride);
}
else
{
// Use non-2D version.
BYTE *pData = NULL;
hr = m_pBuffer->Lock(&pData, NULL, NULL);
if (SUCCEEDED(hr))
{
*plStride = lDefaultStride;
if (lDefaultStride < 0)
{
// Bottom-up orientation. Return a pointer to the start of the
// last row *in memory* which is the top row of the image.
*ppbScanLine0 = pData + abs(lDefaultStride) * (dwHeightInPixels - 1);
}
else
{
// Top-down orientation. Return a pointer to the start of the
// buffer.
*ppbScanLine0 = pData;
}
}
}
m_bLocked = (SUCCEEDED(hr));
return hr;
}
void UnlockBuffer()
{
if (m_bLocked)
{
if (m_p2DBuffer)
{
(void)m_p2DBuffer->Unlock2D();
}
else
{
(void)m_pBuffer->Unlock();
}
m_bLocked = FALSE;
}
}
private:
IMFMediaBuffer *m_pBuffer;
IMF2DBuffer *m_p2DBuffer;
BOOL m_bLocked;
};
相关主题