Nicht komprimierte Videopuffer

In diesem Artikel wird beschrieben, wie Sie mit Medienpuffern arbeiten, die nicht komprimierte Videoframes enthalten. In der Reihenfolge der Präferenz sind die folgenden Optionen verfügbar. Nicht jeder Medienpuffer unterstützt jede Option.

  1. Verwenden Sie die zugrunde liegende Direct3D-Oberfläche. (Gilt nur für Videoframes, die auf Direct3D-Oberflächen gespeichert sind.)
  2. Verwenden Sie die IMF2DBuffer-Schnittstelle .
  3. Verwenden Sie die IMFMediaBuffer-Schnittstelle .

Verwenden der zugrunde liegenden Direct3D-Oberfläche

Der Videoframe kann in einer Direct3D-Oberfläche gespeichert werden. Wenn ja, können Sie einen Zeiger auf die Oberfläche abrufen, indem Sie IMFGetService::GetService oder MFGetService für das Medienpufferobjekt aufrufen. Verwenden Sie den Dienstbezeichner MR_BUFFER_SERVICE. Dieser Ansatz wird empfohlen, wenn die Komponente, die auf den Videoframe zugreift, Direct3D verwendet. Beispielsweise sollte ein Videodecoder, der die DirectX-Videobeschleunigung unterstützt, diesen Ansatz verwenden.

Der folgende Code zeigt, wie Sie den IDirect3DSurface9-Zeiger aus einem Medienpuffer abrufen.

IDirect3DSurface9 *pSurface = NULL;

hr = MFGetService(
    pBuffer, 
    MR_BUFFER_SERVICE,
    __uuidof(IDirect3DSurface9), 
    (void**)&pSurface
    );

if (SUCCEEDED(hr))
{
    // Call IDirect3DSurface9 methods.
}

Die folgenden Objekte unterstützen den MR_BUFFER_SERVICE-Dienst :

Verwenden der IMF2DBuffer-Schnittstelle

Wenn der Videoframe nicht auf einer Direct3D-Oberfläche gespeichert ist oder die Komponente nicht für die Verwendung von Direct3D konzipiert ist, besteht die nächste empfohlene Möglichkeit für den Zugriff auf den Videoframe darin, den Puffer für die IMF2DBuffer-Schnittstelle abzufragen . Diese Schnittstelle wurde speziell für Bilddaten entwickelt. Um einen Zeiger auf diese Schnittstelle zu erhalten, rufen Sie QueryInterface für den Medienpuffer auf. Nicht alle Medienpufferobjekte machen diese Schnittstelle verfügbar. Wenn jedoch ein Medienpuffer die IMF2DBuffer-Schnittstelle verfügbar macht, sollten Sie diese Schnittstelle verwenden, um möglichst auf die Daten zuzugreifen, anstatt IMFMediaBuffer zu verwenden. Sie können die IMFMediaBuffer-Schnittstelle weiterhin verwenden, aber sie ist möglicherweise weniger effizient.

  1. Rufen Sie QueryInterface für den Medienpuffer auf, um die IMF2DBuffer-Schnittstelle abzurufen.
  2. Rufen Sie IMF2DBuffer::Lock2D auf, um auf den Speicher für den Puffer zuzugreifen. Diese Methode gibt einen Zeiger auf das erste Byte der obersten Pixelzeile zurück. Außerdem wird der Bildschritt zurückgegeben, d. h. die Anzahl der Bytes vom Anfang einer Zeile mit Pixeln bis zum Anfang der nächsten Zeile. Der Puffer enthält möglicherweise Auffüllungsbytes nach jeder Zeile von Pixeln, sodass der Schritt größer als die Bildbreite in Bytes sein kann. Stride kann auch negativ sein, wenn das Bild im Arbeitsspeicher von unten nach oben ausgerichtet ist. Weitere Informationen finden Sie unter Bildstride.
  3. Lassen Sie den Puffer nur gesperrt, während Sie auf den Arbeitsspeicher zugreifen müssen. Entsperren Sie den Puffer, indem Sie IMF2DBuffer::Unlock2D aufrufen.

Nicht jeder Medienpuffer implementiert die IMF2DBuffer-Schnittstelle . Wenn der Medienpuffer diese Schnittstelle nicht implementiert (d. h., das Pufferobjekt gibt in Schritt 1 E_NOINTERFACE zurück), müssen Sie die IMFMediaBuffer-Schnittstellenschnittstelle verwenden, die als Nächstes beschrieben wird.

Verwenden der IMFMediaBuffer-Schnittstelle

Wenn ein Medienpuffer die IMF2DBuffer-Schnittstelle nicht verfügbar macht, verwenden Sie die IMFMediaBuffer-Schnittstelle . Die allgemeine Semantik dieser Schnittstelle wird im Thema Arbeiten mit Medienpuffern beschrieben.

  1. Rufen Sie QueryInterface für den Medienpuffer auf, um die IMFMediaBuffer-Schnittstelle abzurufen.
  2. Rufen Sie IMFMediaBuffer::Lock auf, um auf den Pufferspeicher zuzugreifen. Diese Methode gibt einen Zeiger auf den Pufferspeicher zurück. Wenn die Lock-Methode verwendet wird, ist stride immer der Mindestschritt für das betreffende Videoformat, ohne zusätzliche Auffüllungsbytes.
  3. Lassen Sie den Puffer nur gesperrt, während Sie auf den Arbeitsspeicher zugreifen müssen. Entsperren Sie den Puffer, indem Sie IMFMediaBuffer::Unlock aufrufen.

Sie sollten den Aufruf von IMFMediaBuffer::Lock immer vermeiden, wenn der Puffer IMF2DBuffer verfügbar macht, da die Lock-Methode das Pufferobjekt in den Videoframe in einen zusammenhängenden Speicherblock und dann wieder zurückzwingen kann. Wenn der Puffer dagegen IMF2DBuffer nicht unterstützt, führt IMFMediaBuffer::Lock wahrscheinlich nicht zu einer Kopie.

Berechnen Sie die mindestschritte aus dem Medientyp wie folgt:

Der folgende Code zeigt, wie Sie die Standardschritte für die gängigsten Videoformate abrufen.

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;
}

Je nach Anwendung wissen Sie möglicherweise im Voraus, ob ein bestimmter Medienpuffer IMF2DBuffer unterstützt (z. B. wenn Sie den Puffer erstellt haben). Andernfalls müssen Sie bereit sein, eine der beiden Pufferschnittstellen zu verwenden.

Das folgende Beispiel zeigt eine Hilfsklasse, die einige dieser Details ausblendet. Diese Klasse verfügt über eine LockBuffer-Methode, die entweder Lock2D oder Lock aufruft und einen Zeiger auf den Anfang der ersten Pixelzeile zurückgibt. (Dieses Verhalten entspricht der Lock2D-Methode .) Die LockBuffer-Methode verwendet den Standardschritt als Eingabeparameter und gibt den tatsächlichen Stride als Ausgabeparameter zurück.

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;
};

Bildstride

Medienpuffer