圧縮されていないビデオ バッファー
この記事では、圧縮されていないビデオ フレームを含むメディア バッファーを操作する方法について説明します。 優先順に、次のオプションを使用できます。 すべてのメディア バッファーで各オプションがサポートされているわけではありません。
- 基になる Direct3D サーフェスを使用します。 (Direct3D サーフェスに格納されているビデオ フレームにのみ適用されます)。
- IMF2DBuffer インターフェイスを使用します。
- 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 インターフェイスは引き続き使用できますが、効率が低下する可能性があります。
- メディア バッファーで QueryInterface を呼び出して、 IMF2DBuffer インターフェイスを 取得します。
- IMF2DBuffer::Lock2D を呼び出して、バッファーのメモリにアクセスします。 このメソッドは、ピクセルの先頭行の最初のバイトへのポインターを返します。 また、イメージストライドも返されます。これは、ピクセルの行の先頭から次の行の先頭までのバイト数です。 バッファーには、ピクセルの各行の後に埋め込みバイトが含まれている可能性があるため、ストライドはイメージの幅 (バイト単位) よりも広くなることがあります。 ストライドは、画像がメモリ内のボトムアップ方向の場合にも負の値になる可能性があります。 詳細については、「 イメージストライド」を参照してください。
- メモリにアクセスする必要がある場合にのみ、バッファーをロックしたままにしておきます。 IMF2DBuffer::Unlock2D を呼び出してバッファーのロックを解除します。
すべてのメディア バッファーが IMF2DBuffer インターフェイスを実装しているわけではありません。 メディア バッファーがこのインターフェイスを実装していない場合 (つまり、buffer オブジェクトは手順 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 をサポートしているかどうかが事前にわかっている場合があります (たとえば、バッファーを作成した場合)。 それ以外の場合は、2 つのバッファー インターフェイスのいずれかを使用する準備が必要です。
次の例は、これらの詳細の一部を非表示にするヘルパー クラスを示しています。 このクラスには、 Lock2D または Lock を呼び出し、ピクセルの最初の行の先頭へのポインターを返す LockBuffer メソッドがあります。 (この動作は 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;
};
関連トピック