未壓縮的視訊緩衝區
本文說明如何使用包含未壓縮視訊畫面的媒體緩衝區。 根據喜好設定,有下列選項可供使用。 並非所有媒體緩衝區都支援每個選項。
- 使用基礎 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 以存取緩衝區的記憶體。 這個方法會傳回圖元上列第一個位元組的指標。 它也會傳回影像步幅,這是從圖元列開始到下一個資料列開頭的位元組數。 緩衝區可能包含每個圖元資料列之後的填補位元組,因此步幅可能會比以位元組為單位的影像寬度寬。 如果影像在記憶體中由下而下導向,則分步也可能是負數。 如需詳細資訊,請參閱 Image Stride。
- 只有在您需要存取記憶體時,才將緩衝區保持鎖定。 呼叫 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;
};
相關主題