基本ストリームに demux を使う
MPEG-2 demux が PES ペイロードを送信するとき、メディア サンプルの集合で ES バイト ストリームを送信する。デフォルトのサンプル サイズは 8 KB である。demux は各 PES 境界で新しいメディア サンプルを開始するが、1 つの PES ペイロードを複数のサンプルに分割することがある。たとえば、PES ペイロードが 20 KB である場合、2 つの 8 KB のサンプルと 1 つの 4 KB のサンプルとして送信される。demux はバイト ストリームのコンテンツを調べない。シーケンス ヘッダーを解析し、フォーマット変更を調べるのはデコーダの役目である。
demux フィルタの出力ピンはデコーダに接続するとき、ピンの作成時に指定されたメディア タイプを提供する。demux は ES バイト ストリームを調べないため、メディア タイプは検証されない。理論上、MPEG-2 デコーダはデータのタイプを示すために設定されたメジャー タイプおよびサブタイプと接続できることになっている。その後、デコーダはメディア サンプル内で到着するシーケンス ヘッダーを調べる。しかし実際には、メディア タイプが完全なフォーマット ブロックを持たない限り接続しないデコーダが多い。
たとえば、PID 0x31 に MPEG-2 メイン プロファイル ビデオが含まれているとする。この場合、少なくとも以下の手順が必要である。
まず、MPEG-2 ビデオのメディア タイプを作成する。今のところ、フォーマット ブロックは考慮に入れない。
AM_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE);
mt.majortype = MEDIATYPE_Video ;
mt.subtype = MEDIASUBTYPE_MPEG2_VIDEO;
// フォーマット ブロックを作成することがある (ここでは省略)。
次に、demux で出力ピンを作成する。
// demux フィルタで IMpeg2Demultiplexer を問い合わせる。
IMpeg2Demultiplexer *pDemux;
hr = pFilter->QueryInterface(IID_IMpeg2Demultiplexer, (void**)&pDemux);
if (SUCCEEDED(hr))
{
// 新しい出力ピンを作成する。
IPin *pPin0;
hr = pDemux->CreateOutputPin(&mt, L"Video Pin", &pPin0);
if (SUCCEEDED(hr))
{
....
}
}
次に、新しいピンで IMPEG2PIDMap インターフェイスを問い合わせ、MapPID を呼び出す。PID 番号 0x30、および PES ペイロードに対する MEDIA_ELEMENTARY_STREAM を指定する。
// ピンで IMPEG2PIDMap を問い合わせる。その処理により、demux が
// トランスポート ストリームを運ぶように暗黙のうちに設定される。
IMPEG2PIDMap *pPidMap;
hr = pPin0->QueryInterface(IID_IMPEG2PIDMap, (void**)&pPidMap);
if (SUCCEEDED(hr))
{
// ピン 0 に PID 0x31 を代入する。タイプを "PES ペイロード" に設定する。
ULONG Pid = 0x031;
hr = pPidMap->MapPID(1, &Pid, MEDIA_ELEMENTARY_STREAM);
pPidMap->Release();
}
最後に、処理が終了したとき、すべてのインターフェイスを解放する。
pPin0->Release();
pDemux->Release();
次に示すのは、フォーマット ブロックを含め、メディア タイプを設定する、前より完全な例である。この例でも、MPEG-2 メイン プロファイル ビデオを仮定している。ストリーム コンテンツにより、詳細は異なる。
// バイト配列が最初のシーケンス ヘッダーを保持するように設定する。この処理が
// 必要かどうかはデコーダによって異なる。
BYTE SeqHdr[] = { ... }; // シーケンス ヘッダーが入る (省略)。
AM_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
mt.majortype = MEDIATYPE_Video ;
mt.subtype = MEDIASUBTYPE_MPEG2_VIDEO;
mt.formattype = FORMAT_MPEG2Video;
// シーケンス ヘッダーのスペースを含め、フォーマット ブロックを代入する。
mt.cbFormat = sizeof(MPEG2VIDEOINFO) + sizeof(SeqHdr);
mt.pbFormat = (BYTE*)CoTaskMemAlloc(mt.cbFormat);
if (mt.pbFormat == NULL)
{
// メモリ不足。エラー コードを返す。
}
ZeroMemory(mt.pbFormat, mt.cbFormat);
// バッファ ポインタを MPEG2VIDEOINFO 構造体にキャストする。
MPEG2VIDEOINFO *pMVIH = (MPEG2VIDEOINFO*)mt.pbFormat;
RECT rcSrc = {0, 480, 0, 720}; // 転送元矩形。
pMVIH->hdr.rcSource = rcSrc;
pMVIH->hdr.dwBitRate = 4000000; // ビット レート。
pMVIH->hdr.AvgTimePerFrame = 333667; // 29.97 fps。
pMVIH->hdr.dwPictAspectRatioX = 4; // 4:3 アスペクト比。
pMVIH->hdr.dwPictAspectRatioY = 3;
// BITMAPINFOHEADER 情報。
pMVIH->hdr.bmiHeader.biSize = 40;
pMVIH->hdr.bmiHeader.biWidth = 720;
pMVIH->hdr.bmiHeader.biHeight = 480;
pMVIH->dwLevel = AM_MPEG2Profile_Main; // MPEG-2 プロファイル。
pMVIH->dwProfile = AM_MPEG2Level_Main; // MPEG-2 レベル。
// シーケンス ヘッダーをフォーマット ブロックにコピーする。
pMVIH->cbSequenceHeader = sizeof(SeqHdr); // シーケンス ヘッダーのサイズ。
memcpy(pMVIH->dwSequenceHeader, SeqHdr, sizeof(SeqHdr));
参照