カスタム メディア ソースの作成

このトピックでは、Microsoft Media Foundation でカスタム メディア ソースを実装する方法について説明します。 次のセクションが含まれます。

プレゼンテーション記述子の作成

IMFMediaSource::CreatePresentationDescriptor メソッドは、ソースのプレゼンテーション記述子のコピーを返します。 プレゼンテーション記述子を作成するには、ソース コンテンツ内のストリームの数と、各ストリームの使用可能な形式を把握している必要があります。 ストリームごとに、次のようにストリーム記述子を作成します。

  1. メディアの種類の配列を作成します。 配列内の各メディアの種類は、ストリームに使用できる形式を表します。 メディアタイプの作成の詳細については、「メディア タイプ」を参照してください。
  2. MFCreateStreamDescriptor を呼び出して、ストリーム記述子を作成します。 メディアの種類の配列を渡します。 関数は IMFStreamDescriptor ポインターを 返します。
  3. IMFStreamDescriptor::GetMediaTypeHandler を呼び出して、ストリーム記述子のメディア型ハンドラーを取得します。
  4. IMFMediaTypeHandler::SetCurrentMediaType を呼び出して、既定のストリーム形式を設定します。 手順 1 で作成したメディアの種類のいずれかを使用します。 一般に、 形式は最高の品質で使用する必要があります。
  5. 必要に応じて、ストリーム記述子に属性を設定します。 ストリーム記述子に適用される属性の一覧については、「 ストリーム記述子属性」を参照してください。

次に、プレゼンテーション記述子を作成します。

  1. MFCreatePresentationDescriptor を呼び出し、ストリーム記述子の配列を渡します。 関数は IMFPresentationDescriptor ポインターを 返します。
  2. IMFPresentationDescriptor::SelectStream を呼び出して既定のストリーム選択を選択し、1 つ以上のストリームを選択します。 既定の構成では、少なくとも 1 つのストリームを選択する必要があります。
  3. 必要に応じて、プレゼンテーション記述子に属性を設定します。 ストリーム記述子に適用される属性の一覧については、「 プレゼンテーション記述子の属性」を参照してください。

プレゼンテーション記述子は、起動時に作成するか、ソースデータを十分に解析して内容を決定した後に作成する必要があります。 CreatePresentationDescriptor メソッドは、プレゼンテーション記述子のコピーを返す必要があります。 コピーを作成するには、 IMFPresentationDescriptor::Clone を呼び出します。 コピーを返す場合、クライアントは属性やストリームの選択など、元のプレゼンテーション記述子の状態を変更できなくなります。 ただし、 Clone では簡易コピーが作成されるため、クライアントは基になるストリーム記述子を変更できる可能性があることに注意してください。

メディア ソースの開始

IMFMediaSource::Start メソッドは、メディア ソースを開始するか、新しい位置にシークします。 Start を呼び出すと、前の状態が一時停止または実行中で、新しい開始時刻が指定されたときにシークが発生します。 それ以外の場合は、 Start メソッドによって 開始が発生します。 開始操作が完了したら、次のイベントを送信します。

  1. 新しいストリームごとに MENewStream イベントを送信します。つまり、以前に選択解除され、選択されている各ストリームです。 イベント データはストリームへのポインターです。
  2. 以前に選択され、選択されている各ストリームに対して MEUpdatedStream イベントを送信します。 イベント データはストリームへのポインターです。 (選択されていないストリームに対してイベントを送信しないでください。)
  3. ソースがシークしている場合は、 MESourceSeeked イベントを送信します。 それ以外の場合は、 MESourceStarted イベントを 送信します。 イベント データは、 Start メソッドで指定された開始時刻です。 MESourceStarted イベントの場合、開始時刻がVT_EMPTYの場合は、イベントに MF_EVENT_SOURCE_ACTUAL_START 属性を設定します。 属性値は実際の開始時刻です。
  4. ストリームごとに、ソースがシークしている場合は、 MEStreamSeeked イベントを送信します。 それ以外の場合は、 MEStreamStarted イベントを 送信します。 イベント データは開始時刻です。 (メディア ソースは、ストリームの IMFMediaEventGenerator::QueueEvent メソッドを呼び出すことによって、ストリーム上のイベントをキューに登録できます)。

ストリームの選択が解除されたら、ストリームをシャットダウンします。 ストリームは、その時点でそれ以上のイベントをキューに入れてはなりません。

Start メソッドの時刻形式は、pguidTimeFormat パラメーターで指定します。 GUID_NULLで示される標準時刻形式は、100 ナノ秒単位です。 メディア ソースは、この時刻形式をサポートする必要があります。

求めて

シーク時に、要求された開始位置が正確なサンプル境界に収まっていない可能性があります。 また、圧縮コンテンツの場合、開始位置がキー フレーム間に収まる可能性があります。 ストリームは、要求された開始位置で圧縮されていないサンプルを生成するために必要な最も早い時点からサンプルを配信する必要があります。 ビデオの場合は、前のキー フレームから開始することを意味します。 パイプラインはデコーダーから余分なフレームを削除し、要求された時間に再生が開始されるようにします。

ソース イベントで指定された開始時刻 (MESourceStartedMESourceSeekedMEStreamStartedMEStreamSeeked) は、実際の開始位置に関係なく、要求された開始時刻 ( Start メソッドで指定された値) です。

たとえば、ビデオ ストリームの最初の数フレームに次の特性があるとします。

サンプル 1 2 3 4
Time 33 ミリ秒 66 ミリ秒 100 ミリ秒 133 ミリ秒
キー フレーム? はい いいえ いいえ はい

 

Start メソッドが 100 ミリ秒の値で呼び出された場合、ソースは、この時点より前の最初のキー フレームであるフレーム 1 からビデオを出力する必要があります。 start イベントは、イベント データに 100 ミリ秒を示します。

メディア ソースの一時停止

IMFMediaSource::P ause メソッドは、メディア ソースを一時停止します。

ソースが一時停止している間、ストリームは新しいサンプルを作成してキューに格納できますが、ストリームはサンプルを配信しません。 この規則の例外をいくつか次に示します。

  • ライブ ソースでは、一時停止中にデータを削除する必要があります。
  • ソースがネットワークからデータを取得すると、サーバーが一時停止する可能性があります。

ソースの一時停止中にクライアントが IMFMediaStream::RequestSample を呼び出すと、ソースが再び開始されるまで要求もキューに入れられます。 要求は削除しないでください。

一時停止は、開始状態からのみ許可されます。 それ以外の場合、 PauseMF_E_INVALID_STATE_TRANSITIONを返す必要があります。

ソース データの生成

Media Foundation では プル モデルが使用されます。つまり、ストリームはパイプラインからの要求に応じてサンプルを生成して配信します。 ストリームは、メディア ソースが実行中でストリームが選択されている場合にサンプルを配信できます。 ストリームは、クライアントが新しいサンプルを要求した場合にのみデータを配信します。

要求例

クライアントは、 IMFMediaStream::RequestSample を呼び出して新しいサンプルを要求します。 操作のシーケンスを次に示します。

  1. クライアントは IMFMediaStream::RequestSample を呼び出します。 引数は、クライアントが要求の追跡に使用する省略可能な トークン オブジェクトへのポインターです。 クライアントはトークンを実装します。 トークンは IUnknown インターフェイスを公開する必要があります。 クライアントは、トークンの代わりに NULL ポインターを渡すこともできます。

  2. クライアントがトークンを指定した場合、メディア ストリームはトークンに 対して AddRef を 呼び出し、トークンを先入れ先出しキューに配置します。 メソッドは を返し、残りの手順は非同期的に実行されます。

  3. 使用可能なデータが増えると、メディア ストリームによって新しいサンプルが作成されます。 (この手順については、次のセクションで詳しく説明します)。

  4. メディア ストリームは、キューから最初のトークンをプルします。

  5. トークンが NULL でない場合、メディア ストリームはメディア サンプルの MFSampleExtension_Token 属性を設定します。 属性の値は、トークンへのポインターです。

  6. メディア ストリームは MEMediaSample イベントを送信します。 イベント データは、サンプルの IMFSample インターフェイスへのポインターです。

  7. クライアントがトークンを指定した場合、メディア ストリームはトークン オブジェクトに対して Release を呼び出します。

メディア ストリームがクライアントの RequestSample 要求を満たすことができない場合、キューからトークンをプルし、トークンに対して Release を 呼び出しますが、 MEMediaSample イベントは送信しません。

クライアントはトークンを使用して、要求の状態を追跡できます。 クライアントは MEMediaSample イベントを受信すると、サンプルからトークンを取得し、元の要求と照合できます。 クライアントはトークンを使用して、メディア ソースが要求を削除したかどうかを検出することもできます。 トークンの参照カウントが 0 になり、メディア ストリームが MEMediaSample イベントを送信しない場合は、要求が削除されたことを意味します。

ここで示す手順では、 RequestSample メソッドが非同期操作として実装されていることを前提としています。 メソッドが同期的な場合は、要求トークンをキューに配置する必要はありません。 ただし、データの生成にかなりの時間がかかる場合は、たとえばソースがバイト ストリームからデータを読み取る場合など、非同期アプローチをお勧めします。

ストリームは、 RequestSample の呼び出しの間に蓄積されるすべてのデータをバッファリングする役割を担います。

メディア ストリームがストリームの末尾に到達すると、最後のサンプルの後に MEEndOfStream イベントが送信されます。 ストリームが終了するたびに、メディア ソースは MEEndOfPresentation イベントを 送信します。 メディア ストリームが MEEndOfStream イベントを送信すると、 RequestSample メソッドは、ソース 再起動されるまでMF_E_END_OF_STREAMを返します。

サンプルの割り当て

保留中のサンプル要求を満たす準備ができたストリームは、新しいサンプルを作成し、1 つ以上のメディア バッファーをサンプルに追加します。 メディア バッファーの作成の詳細については、「メディア バッファー」を参照してください。

ストリームでは、タイム スタンプと期間 (既知の場合) を設定する必要があります。 タイム スタンプは、ソースを基準にしています。 ほとんどの場合、コンテンツの開始は 0 のタイム スタンプに対応します。 たとえば、ソースがメディア ファイルから読み取る場合、ファイルの先頭のタイム スタンプは 0 になります。

サンプルのタイム スタンプは、必ずしもプレゼンテーション時間と等しいとは限りません。 メディア セッションは、ソース時間からプレゼンテーション時間に変換されます。 圧縮データの場合、ストリームは開始時刻の前に最も近いキー フレームから開始するデータを生成する必要があります。 これにより、デコーダーは要求された開始時刻に表示されるフレームを配信できます。 (それ以外の場合、デコーダーは次のキー フレームまで待機する必要があります)。

再生速度が 1.0 より速いか遅い場合は、パイプラインによってプレゼンテーション クロックのレートが調整されます。 ソースでは、サンプルのタイム スタンプは調整されません。

ソースでは、属性を設定することで、サンプルに関する追加情報を設定できます。 サンプル属性の一覧については、「 サンプル属性」を参照してください。

ストリームのギャップ

ストリームにかなりの長さのギャップが含まれている場合は、ストリームが MEStreamTick イベントを送信することをお勧めします。 このイベントは、サンプルが見つからないことをクライアントに通知します。 イベント データは、不足しているサンプルのタイム スタンプです (100 ナノ秒単位 (VT_I8)。 このイベントにより、ダウンストリーム コンポーネントが到着しないサンプルを待機するのを防ぐことができます。 ストリームは、ストリーム内のギャップをまたぐのに必要な数の MEStreamTick イベントを送信できます。

メディア ソースのシャットダウン

メディア ソースを使用してクライアントが完了すると、 IMFMediaSource::Shutdown が呼び出されます。 このメソッド内では、メディア ソースは循環参照数を中断する必要があります。 通常、メディア ソースとメディア ストリームの間には循環参照があります。

イベント キューを使用して IMFMediaEventGenerator を実装する場合は、イベント キューで IMFMediaEventQueue::Shutdown を呼び出します。 このメソッドは、イベント キューをシャットダウンし、現在イベントを待機している呼び出し元に通知します。

シャットダウン後、ソースのすべてのメソッドは、IUnknown メソッドを除き、MF_E_SHUTDOWNを返します。

ライブ ソース

Windows 7 以降、Media Foundation はオーディオおよびビデオ キャプチャ デバイスを自動的にサポートします。 ビデオの場合、デバイスは、ビデオ キャプチャ カテゴリにカーネル ストリーミング (KS) ミニドライバーを提供する必要があります。 Media Foundation では、PnP パスを使用してデバイスを列挙します。 オーディオの場合、Media Foundation は Windows マルチメディア デバイス (MMDevice) API を使用してオーディオ エンドポイント デバイスを列挙します。 デバイスがこれらの条件を満たしている場合は、カスタム メディア ソースを実装する必要はありません。

ただし、他の種類のデバイスや他のライブ データ ソース用のカスタム メディア ソースを実装する場合があります。 ライブ ソースと他のメディア ソースの違いはごくわずかです。

  • IMFMediaSource::GetCharacteristics メソッドで、MFMEDIASOURCE_IS_LIVE フラグを返します。
  • 最初のサンプルには、タイム スタンプが 0 である必要があります。
  • イベントとストリーミング状態は、一時停止状態を除き、メディア ソースと同じように処理されます。
  • 一時停止中は、サンプルをキューに登録しないでください。 一時停止中に生成されたデータを削除します。
  • ライブ ソースでは、通常、シーク、リバース プレイ、レート制御はサポートされていません。

メディア ソース

チュートリアル: カスタム メディア ソースの作成