圧縮されていないビデオ バッファー

この記事では、圧縮されていないビデオ フレームを含むメディア バッファーを操作する方法について説明します。 優先順に、次のオプションを使用できます。 すべてのメディア バッファーで各オプションがサポートされているわけではありません。

  1. 基になる Direct3D サーフェスを使用します。 (Direct3D サーフェスに格納されているビデオ フレームにのみ適用されます)。
  2. IMF2DBuffer インターフェイスを使用します。
  3. IMFMediaBuffer インターフェイスを使用します。

基になる Direct3D サーフェスを使用する

ビデオ フレームは 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 インターフェイスは引き続き使用できますが、効率が低い場合があります。

  1. メディア バッファーで QueryInterface を呼び出して、 IMF2DBuffer インターフェイスを 取得します。
  2. IMF2DBuffer::Lock2D を呼び出して、バッファーのメモリにアクセスします。 このメソッドは、ピクセルの先頭行の最初のバイトへのポインターを返します。 また、イメージストライドも返されます。これは、ピクセル行の先頭から次の行の先頭までのバイト数です。 バッファーには、ピクセルの各行の後に埋め込みバイトが含まれる可能性があるため、ストライドはイメージの幅 (バイト単位) よりも広くなる可能性があります。 ストライドは、イメージがメモリ内のボトムアップ方向の場合にも負の値になる可能性があります。 詳細については、「 イメージストライド」を参照してください。
  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 ではコピーが発生しない可能性があります。

メディアの種類から最小ストライドを次のように計算します。

  • 最小ストライドは 、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 をサポートしているかどうかを事前に把握している場合があります (たとえば、バッファーを作成した場合)。 それ以外の場合は、2 つのバッファー インターフェイスのいずれかを使用するように準備する必要があります。

次の例は、これらの詳細の一部を非表示にするヘルパー クラスを示しています。 このクラスには 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;
};

Image Stride

メディア バッファー