QueryAccept (アップストリーム)
この機構では、入力ピンはアップストリームの対応するピンにフォーマットの変更を提示できる。ダウンストリーム フィルタは、アップストリーム フィルタが次の IMemAllocator::GetBuffer 呼び出しで取得するサンプルにメディア タイプをアタッチする必要がある。しかしそれには、ダウンストリーム フィルタが接続用のカスタム アロケータを提供する必要がある。このアロケータは、ダウンストリーム フィルタが次のサンプルにメディア タイプを設定するときに使えるプライベート メソッドを実装する必要がある。
次の手順が実行される。
- ダウンストリーム フィルタは、ピン接続がフィルタのカスタム アロケータを使うかどうかを確認する。アップストリーム フィルタがアロケータを所有している場合、ダウンストリーム フィルタはフォーマットを変更できない。
- ダウンストリーム フィルタは、アップストリーム出力ピンの IPin::QueryAccept を呼び出す (図の手順 A を参照すること)。
- QueryAccept が S_OK を返すと、ダウンストリーム フィルタはメディア タイプを設定するためにアロケータのプライベート メソッドを呼び出す。このプライベート メソッド内で、アロケータは次に利用可能なサンプルの IMediaSample::SetMediaType を呼び出す (B)。
- アップストリーム フィルタは GetBuffer を呼び出して新しいサンプルを取得し (C)、IMediaSample::GetMediaType を呼び出してメディア タイプを取得する (D)。
- アップストリーム フィルタがサンプルを送信するとき、メディア タイプはそのサンプルにアタッチしたままにする。そうすると、ダウンストリーム フィルタはメディア タイプが変わったことを確認できる (E)。
アップストリーム フィルタがフォーマットの変更を受け入れる場合、元のメディア タイプにも切り替えられなければならない。
この種のフォーマット変更の主な例として、DirectShow ビデオ レンダラがある。
- 元のビデオ レンダラ フィルタはストリーミング中に RGB タイプと YUV タイプの間で切り替えられる。フィルタは接続するとき、現在の表示設定に一致する RGB フォーマットを必要とする。このことで、必要な場合は GDI に戻れることが保証される。ストリーミングが始まった後、DirectDraw が利用できる場合、ビデオ レンダラは YUV タイプへのフォーマット変更を要求する。後で、何らかの理由で DirectDraw サーフェイスを失った場合、RGB に戻すことがある。
- ビデオ レンダラより新しい Video Mixing Renderer (VMR) フィルタは、YUV タイプを含め、グラフィック ハードウェアでサポートされている任意のフォーマットで接続する。しかし、グラフィック ハードウェアは、パフォーマンスを最適化するため、基になる DirectDraw サーフェイスのストライドを変更することがある。VMR フィルタは QueryAccept を使って新しいストライドを報告する。新しいストライドは BITMAPINFOHEADER 構造体の biWidth メンバに指定される。VIDEOINFOHEADER 構造体または VIDEOINFOHEADER2 構造体の転送元矩形と転送先矩形は、ビデオをデコードする領域を識別する。
実装上の注意
アップストリーム フォーマットの変更を "要求する" 必要があるフィルタを作成する可能性は低い。これは、主にビデオ レンダラの機能であるためである。しかし、ビデオ変換フィルタまたはビデオ デコーダを作成する場合、フィルタはビデオ レンダラからの要求に正しく応答する必要がある。
ビデオ レンダラとデコーダの間に置く変換インプレイス フィルタでは、すべての QueryAccept 呼び出しをアップストリーム方向に渡す必要がある。新しいフォーマット情報が到着したら、保存すること。
コピー変換フィルタ (つまり、非変換インプレイス フィルタ) は、次のいずれか 1 つの動作を実行する必要がある。
- フォーマット変更をアップストリームに渡し、新しいフォーマット情報が到着したら保存する。フィルタは、アップストリーム サンプルにフォーマットをアタッチできるように、カスタム アロケータを使う必要がある。
- フィルタ内のフォーマット変換を実行する。この処理はフォーマット変換をアップストリームに渡すより簡単なことが多い。ただし、デコーダ フィルタを正しいフォーマットにデコードさせるより効率が悪いことがある。
- 最終的な手段として、フォーマット変更を拒否する。(詳細については、DirectShow 基底クラス ライブラリの CTransInPlaceOutputPin::CheckMediaType メソッドのソース コードを参照すること。)しかし、フォーマット変更を拒否すると、ビデオ レンダラが最も効率的なフォーマットを使えなくなるため、パフォーマンスが低下することがある。
次の疑似コードは、YUV と RGB の出力タイプ間を切り替えられるコピー変換フィルタ (CTransformFilter から派生) をどのように実装するかを示している。この例で、フィルタはフォーマット変更をアップストリームに渡すのではなく、変換自体を行うことを想定している。
HRESULT CMyTransform::CheckInputType(const CMediaType *pmt)
{
if (pmt はサポートしている YUV タイプである) {
return S_OK;
}
else {
return VFW_E_TYPE_NOT_ACCEPTED;
}
}
HRESULT CMyTransform::CheckTransform(
const CMediaType *mtIn, const CMediaType *mtOut)
{
if (mtOut はサポートしている YUV タイプまたは RGB タイプである)
{
if ((mtIn に mtOut と同じビデオ ディメンジョンがある) &&
(mtIn から mtOut への変換をサポートしている))
{
return S_OK;
}
}
// それ以外の場合。
return VFW_E_TYPE_NOT_ACCEPTED;
}
// GetMediaType: 優先出力タイプを返す。
HRESULT CMyTransform::GetMediaType(int iPosition, CMediaType *pMediaType)
{
if (iPosition < 0) {
return E_INVALIDARG;
}
switch (iPosition)
{
case 0:
入力タイプ (YUV) を pMediaType にコピーする。
return S_OK;
case 1:
入力タイプに一致する RGB タイプを作成する。
return S_OK;
default:
return VFW_S_NO_MORE_ITEMS;
}
}
// SetMediaType: CTransformFilter からオーバーライドする。
HRESULT CMyTransform::SetMediaType(
PIN_DIRECTION direction, const CMediaType *pmt)
{
// この情報をキャプチャする ...
if (direction == PINDIR_OUTPUT)
{
m_bYuv = (pmt->subtype == MEDIASUBTYPE_UYVY);
}
return S_OK;
}
HRESULT CMyTransform::Transform(
IMediaSample *pSource, IMediaSample *pDest)
{
// ダウンストリームからのフォーマット変更を検索する。
CMediaType *pMT = NULL;
HRESULT hr = pDest->GetMediaType((AM_MEDIA_TYPE**)&pMT);
if (hr == S_OK)
{
hr = m_pOutput->CheckMediaType(pMT);
if(FAILED(hr))
{
DeleteMediaType(pMT);
return E_FAIL;
}
// 新しいタイプについて自身の出力ピンに通知する。
m_pOutput->SetMediaType(pMT);
DeleteMediaType(pMT);
}
// バッファを処理する。
if (m_bYuv) {
return ProcessFrameYUV(pSource, pDest);
}
else {
return ProcessFrameRGB(pSource, pDest);
}
}