未壓縮的視訊緩衝區

本文說明如何使用包含未壓縮視訊畫面的媒體緩衝區。 根據喜好設定,有下列選項可供使用。 並非所有媒體緩衝區都支援每個選項。

  1. 使用基礎 Direct3D 介面。 (僅適用于儲存在 Direct3D surfaces.) 中的視訊畫面
  2. 使用 IMF2DBuffer 介面。
  3. 使用 IMFMediaBuffer 介面。

使用基礎 Direct3D Surface

視訊框架可能會儲存在 Direct3D 介面內。 如果是,您可以在媒體緩衝區物件上呼叫 IMFGetService::GetServiceMFGetService ,以取得介面的指標。 使用服務識別碼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 介面,但效率可能較低。

  1. 在媒體緩衝區上呼叫 QueryInterface 以取得 IMF2DBuffer 介面。
  2. 呼叫 IMF2DBuffer::Lock2D 以存取緩衝區的記憶體。 這個方法會傳回圖元上列第一個位元組的指標。 它也會傳回影像步幅,這是從圖元列開始到下一個資料列開頭的位元組數。 緩衝區可能包含每個圖元資料列之後的填補位元組,因此步幅可能會比以位元組為單位的影像寬度寬。 如果影像在記憶體中由下而下導向,則分步也可能是負數。 如需詳細資訊,請參閱 Image Stride
  3. 只有在您需要存取記憶體時,才將緩衝區保持鎖定。 呼叫 IMF2DBuffer::Unlock2D來解除鎖定緩衝區。

並非所有媒體緩衝區都會實作 IMF2DBuffer 介面。 如果媒體緩衝區未實作此介面 (,則緩衝區物件會在步驟 1) 中傳回E_NOINTERFACE,您必須使用接下來所述的 IMFMediaBuffer 介面介面。

使用 IMFMediaBuffer 介面

如果媒體緩衝區未公開 IMF2DBuffer 介面,請使用 IMFMediaBuffer 介面。 此介面的一般語意描述于 使用媒體緩衝區主題中。

  1. 在媒體緩衝區上呼叫 QueryInterface 以取得 IMFMediaBuffer 介面。
  2. 呼叫 IMFMediaBuffer::Lock 以存取緩衝區記憶體。 這個方法會傳回緩衝區記憶體的指標。 使用 Lock 方法時,步進一律是有問題的視訊格式最小步幅,而且沒有額外的填補位元組。
  3. 只有在您需要存取記憶體時,才將緩衝區保持鎖定。 呼叫 IMFMediaBuffer::Unlock來解除鎖定緩衝區。

如果緩衝區公開IMF2DBuffer,您應該一律避免呼叫IMFMediaBuffer::Lock,因為Lock方法可以將緩衝區物件強制進入連續記憶體區塊,然後再返回一次。 另一方面,如果緩衝區不支援 IMF2DBuffer則 IMFMediaBuffer::Lock 可能不會產生複本。

計算媒體類型中的最小步幅,如下所示:

下列程式碼示範如何取得最常見視訊格式的預設步幅。

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 方法,它會呼叫 Lock2DLock ,並傳回圖元第一列開頭的指標。 (此行為符合 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;
};

Image Stride

媒體緩衝區