チュートリアル: シングル パス Windows Media エンコード
エンコードとは、デジタル メディアをある形式から別の形式に変換するプロセスを指します。 たとえば、MP3 オーディオを、Advanced Systems Format (ASF) 仕様によって定義される Windows Media Audio 形式に変換します。
注意: ASF 仕様では、出力ファイル (.wma または .wmv) のコンテナーの種類を定義します。このコンテナーの種類には、圧縮、非圧縮を問わず、あらゆる形式のメディア データを含めることができます。 たとえば、.wma ファイルなどの ASF コンテナーには、MP3 形式のメディア データを含めることができます。 エンコード プロセスは、ファイルに含まれるデータの実際の形式を変換します。
このチュートリアルでは、パイプライン レイヤー ASF コンポーネントを使用して、クリア コンテンツ (DRM で保護されていない) の入力ソースを Windows Media コンテンツにエンコードし、新しい ASF ファイル (.wm*) にデータを書き込む方法について説明します。 これらのコンポーネントは、メディア セッションのインスタンスによって制御される部分エンコード トポロジを構築するために使用されます。
このチュートリアルでは、入力ファイル名と出力ファイル名、およびエンコード モードを引数として受け取るコンソール アプリケーションを作成します。 入力ファイルは、圧縮または非圧縮のメディア形式にすることができます。 有効なエンコード モードは、"CBR" (定数ビット レート) または "VBR" (可変ビット レート) です。 アプリケーションは、入力ファイル名で指定されたソースを表すメディア ソースと、ソース ファイルのエンコードされた内容を ASF ファイルにアーカイブする ASF ファイル シンクを作成します。 シナリオを簡単に実装できるように、出力ファイルには、1 つのオーディオ ストリームと 1 つのビデオ ストリームのみが含まれます。 アプリケーションは、オーディオ ストリーム形式変換用の Windows Media Audio 9.1 Professional コーデックと、ビデオ ストリーム用の Windows Media Video 9 コーデックを挿入します。
定数ビット レート エンコードの場合、エンコード セッションが開始される前に、エンコーダーは、実現する必要があるターゲットのビット レートを認識している必要があります。 このチュートリアルでは、"CBR" モードの場合、アプリケーションは、メディア タイプ ネゴシエーション中にエンコーダーから取得された最初の出力メディアの種類で使用可能なビット レートを、ターゲット ビット レートとして使用します。 可変ビット レート エンコードの場合、このチュートリアルでは、品質レベルを設定して可変ビット レートでエンコードする方法について説明します。 オーディオ ストリームは品質レベル 98 でエンコードされ、ビデオ ストリームは品質レベル 95 でエンコードされます。
以下は、シングル パス エンコード モードを使用して ASF コンテナー内の Windows Media コンテンツをエンコードする手順をまとめたものです。
- ソース リゾルバーを使用して、指定されたメディア ソースを作成します。
- メディア ソース内のストリームを列挙します。
- ASF メディア シンクを作成し、エンコードする必要があるメディア ソース内のストリームに応じてストリーム シンクを追加します。
- 必要なエンコード プロパティを使用してメディア シンクを構成します。
- 出力ファイル内のストリーム用の Windows Media エンコーダーを作成します。
- メディア シンクに設定されているプロパティを使用してエンコーダーを構成します。
- 部分エンコード トポロジを構築します。
- メディア セッションをインスタンス化し、メディア セッションでトポロジを設定します。
- メディア セッションを制御し、関連するすべてのイベントをメディア セッションから取得して、エンコード セッションを開始します。
- VBR エンコードの場合は、エンコーダーからエンコード プロパティの値を取得し、メディア シンクに設定します。
- エンコード セッションを閉じてシャットダウンします。
このチュートリアルは次のセクションで構成されています。
- 前提条件
- プロジェクトをセットアップする
- メディア ソースを作成する
- ASF ファイル シンクを作成する
- 部分エンコード トポロジを構築する
- エンコード セッションを処理する
- ファイル シンクのエンコード プロパティを更新する
- main を実装する
- 出力ファイルをテストする
- 一般的なエラー コードとデバッグのヒント
- 関連トピック
前提条件
このチュートリアルでは、次のことを前提としています。
ASF オブジェクトを操作するためにメディア ファンデーションによって提供されるパイプライン レイヤー ASF コンポーネントである、ASF ファイル構造について熟知している。 これらのコンポーネントには次のオブジェクトが含まれます。
-
注意: トランスレート (形式を変更せずに、高いビット レート ファイルを低いビット レート ファイルに変換する) を実行する場合は、ASF メディア ソースを使用します。
-
Windows Media エンコーダーと、さまざまなエンコードの種類 (特に定数ビット レート エンコードと品質ベースの可変ビット レート エンコード) について熟知している。
エンコーダー MFT の操作に熟知している。 具体的には、エンコーダーのインスタンスを作成し、エンコーダーに入力と出力の種類を設定します。
トポロジ オブジェクトと、エンコード トポロジを構築する方法について熟知している。 トポロジとトポロジ ノードの詳細については、「トポロジの作成」を参照してください。
メディア セッションのイベント モデルとフロー制御演算について熟知している。 詳細については、「メディア セッション イベント」を参照してください。
プロジェクトをセットアップする
ソース ファイルに次のヘッダーを含めます。
#include <new> #include <mfidl.h> // Media Foundation interfaces #include <mfapi.h> // Media Foundation platform APIs #include <mferror.h> // Media Foundation error codes #include <wmcontainer.h> // ASF-specific components #include <wmcodecdsp.h> // Windows Media DSP interfaces #include <Dmo.h> // DMO objects #include <uuids.h> // Definition for FORMAT_VideoInfo #include <propvarutil.h>
次のライブラリ ファイルにリンクします。
// The required link libraries #pragma comment(lib, "mfplat") #pragma comment(lib, "mf") #pragma comment(lib, "mfuuid") #pragma comment(lib, "msdmo") #pragma comment(lib, "strmiids") #pragma comment(lib, "propsys")
SafeRelease 関数を宣言します。
template <class T> void SafeRelease(T **ppT) { if (*ppT) { (*ppT)->Release(); *ppT = NULL; } }
ENCODING_MODE 列挙型を宣言して、CBR および VBR エンコードの種類を定義します。
// Encoding mode typedef enum ENCODING_MODE { NONE = 0x00000000, CBR = 0x00000001, VBR = 0x00000002, } ;
ビデオ ストリームのバッファー ウィンドウの定数を定義します。
// Video buffer window const INT32 VIDEO_WINDOW_MSEC = 3000;
メディア ソースを作成する
入力ソースのメディア ソースを作成します。 メディア ファンデーションには、MP3、MP4/3GP、AVI/WAVE など、さまざまなメディア形式用の組み込みメディア ソースが用意されています。 メディア ファンデーションでサポートされている形式の詳細については、「メディア ファンデーションでサポートされるメディア形式」を参照してください。
メディア ソースを作成するには、ソース リゾルバーを使用します。 このオブジェクトは、ソース ファイルに指定された URL を分析し、適切なメディア ソースを作成します。
次の呼び出しを行います。
IMFSourceResolver::CreateObjectFromURL
これらの呼び出しの詳細については、「ソース リゾルバーの使用」を参照してください。
入力ファイルが ASF 形式で、形式を変更せずに別のビット レート ファイルに変換する場合は、ソース ソルバーによって ASF メディア ソースのインスタンスが作成されます。
次のコード例は、指定したファイルのメディア ソースを作成する関数、CreateMediaSource を示しています。
// Create a media source from a URL.
HRESULT CreateMediaSource(PCWSTR sURL, IMFMediaSource **ppSource)
{
MF_OBJECT_TYPE ObjectType = MF_OBJECT_INVALID;
IMFSourceResolver* pSourceResolver = NULL;
IUnknown* pSource = NULL;
// Create the source resolver.
HRESULT hr = MFCreateSourceResolver(&pSourceResolver);
if (FAILED(hr))
{
goto done;
}
// Use the source resolver to create the media source.
// Note: For simplicity this sample uses the synchronous method to create
// the media source. However, creating a media source can take a noticeable
// amount of time, especially for a network source. For a more responsive
// UI, use the asynchronous BeginCreateObjectFromURL method.
hr = pSourceResolver->CreateObjectFromURL(
sURL, // URL of the source.
MF_RESOLUTION_MEDIASOURCE, // Create a source object.
NULL, // Optional property store.
&ObjectType, // Receives the created object type.
&pSource // Receives a pointer to the media source.
);
if (FAILED(hr))
{
goto done;
}
// Get the IMFMediaSource interface from the media source.
hr = pSource->QueryInterface(IID_PPV_ARGS(ppSource));
done:
SafeRelease(&pSourceResolver);
SafeRelease(&pSource);
return hr;
}
ASF ファイル シンクを作成する
エンコード されたメディア データをエンコード セッションの終了時に ASF ファイルにアーカイブする、ASF ファイル シンクのインスタンスを作成します。
このチュートリアルでは、ASF ファイル シンクのアクティブ化オブジェクトを作成します。 必要なストリーム シンクを作成するには、ファイル シンクに次の情報が必要になります。
- 最終ファイルでエンコードされ書き込まれるストリーム。
- メディア コンテンツのエンコードに使用されるエンコード プロパティ (エンコードの種類、エンコード パスの数、関連プロパティなど)。
- 漏れバケット パラメーター (ビット レートとバッファー サイズ) を自動的に調整する必要があるかどうかをメディア シンクに示すグローバル ファイルのプロパティ。
ストリーム情報は ASF プロファイル オブジェクトで構成され、エンコード プロパティとグローバル プロパティは、ASF ContentInfo オブジェクトによって管理されるプロパティ ストアで設定されます。
ASF ファイル シンクの概要については、「ASF メディア シンク」を参照してください。
ASF プロファイル オブジェクトを作成する
ASF ファイル シンクがエンコードされたメディア データを ASF ファイルに書き込むには、ストリーム シンクを作成するストリームの数とストリームの種類をシンクが認識している必要があります。 このチュートリアルでは、メディア ソースからその情報を抽出し、対応する出力ストリームを作成します。 出力ストリームは、1 つのオーディオ ストリームと 1 つのビデオ ストリームに制限します。 ソースで選んだストリームごとに、ターゲット ストリームを作成し、プロファイルに追加します。
この手順を実装するには、次のオブジェクトが必要になります。
- ASF プロファイル
- メディア ソースのプレゼンテーション記述子
- メディア ソースで選択したストリームのストリーム記述子
- 選択したストリームのメディアの種類
次の手順では、ASF プロファイルとターゲット ストリームを作成するプロセスについて説明します。
ASF プロファイルを作成するには
MFCreateASFProfile を呼び出して、空のプロファイル オブジェクトを作成します。
IMFMediaSource::CreatePresentationDescriptor を呼び出して、このチュートリアルの「メディア ソースを作成する」セクションで説明されている手順で作成したメディア ソースのプレゼンテーション記述子を作成します。
IMFPresentationDescriptor::GetStreamDescriptorCount を呼び出して、メディア ソース内のストリームの数を取得します。
メディア ソース内の各ストリームに対して IMFPresentationDescriptor::GetStreamDescriptorByIndex を呼び出して、ストリームのストリーム記述子を取得します。
IMFStreamDescriptor::GetMediaTypeHandler の後に IMFMediaTypeHandler::GetMediaTypeByIndex を呼び出して、ストリームの最初のメディアの種類を取得します。
注意: 複雑な呼び出しを避けるため、ストリームごとに 1 つのメディアの種類のみが存在すると想定し、ストリームの最初のメディアの種類を選びます。 複雑なストリームの場合は、メディアの種類ハンドラーから各メディアの種類を列挙し、エンコードするメディアの種類を選ぶ必要があります。
IIMFMediaType::GetMajorType を呼び出してストリームのメジャー型を取得し、ストリームにオーディオとビデオのどちらが含まれているかを判断します。
ストリームのメジャー型に応じて、ターゲット メディアの種類を作成します。 これらのメディアの種類は、エンコード セッション中にエンコーダーが使用する形式情報を保持します。 このチュートリアルの以降のセクションでは、ターゲット メディアの種類を作成するプロセスについて説明します。
- 圧縮オーディオ メディアの種類を作成する
- 圧縮ビデオ メディアの種類を作成する
ターゲット メディアの種類に基づいてストリームを作成し、アプリケーションの要件に従ってストリームを構成し、ストリームをプロファイルに追加します。 詳細については、「ASF ファイル シンクへのストリーム情報の追加」を参照してください。
- IMFASFProfile::CreateStream を呼び出して、ターゲット メディアの種類を渡して出力ストリームを作成します。 このメソッドは、ストリーム オブジェクトの IMFASFStreamConfig インターフェイスを取得します。
- ストリームを構成します。
- IMFASFStreamConfig::SetStreamNumber を呼び出して、ストリームに番号を割り当てます。 この設定は必須です。
- 必要に応じて、IMFASFStreamConfig メソッドと関連するストリーム構成属性を呼び出して、各ストリームで漏れバケット パラメーター、ペイロード拡張、相互排他を構成します。
- ASF ContentInfo オブジェクトを使用して、ストリーム レベルのエンコード プロパティを追加します。 この手順の詳細については、このチュートリアルの「ASF ContentInfo オブジェクトを作成する」セクションを参照してください。
- IMFASFProfile::SetStream を呼び出して、ストリームをプロファイルに追加します。
次のコード例では、出力オーディオ ストリームを作成します。
//------------------------------------------------------------------- // CreateAudioStream // Create an audio stream and add it to the profile. // // pProfile: A pointer to the ASF profile. // wStreamNumber: Stream number to assign for the new stream. //------------------------------------------------------------------- HRESULT CreateAudioStream(IMFASFProfile* pProfile, WORD wStreamNumber) { if (!pProfile) { return E_INVALIDARG; } if (wStreamNumber < 1 || wStreamNumber > 127 ) { return MF_E_INVALIDSTREAMNUMBER; } IMFMediaType* pAudioType = NULL; IMFASFStreamConfig* pAudioStream = NULL; //Create an output type from the encoder HRESULT hr = GetOutputTypeFromWMAEncoder(&pAudioType); if (FAILED(hr)) { goto done; } //Create a new stream with the audio type hr = pProfile->CreateStream(pAudioType, &pAudioStream); if (FAILED(hr)) { goto done; } //Set stream number hr = pAudioStream->SetStreamNumber(wStreamNumber); if (FAILED(hr)) { goto done; } //Add the stream to the profile hr = pProfile->SetStream(pAudioStream); if (FAILED(hr)) { goto done; } wprintf_s(L"Audio Stream created. Stream Number: %d.\n", wStreamNumber); done: SafeRelease(&pAudioStream); SafeRelease(&pAudioType); return hr; }
次のコード例では、出力ビデオ ストリームを作成します。
//------------------------------------------------------------------- // CreateVideoStream // Create an video stream and add it to the profile. // // pProfile: A pointer to the ASF profile. // wStreamNumber: Stream number to assign for the new stream. // pType: A pointer to the source's video media type. //------------------------------------------------------------------- HRESULT CreateVideoStream(IMFASFProfile* pProfile, WORD wStreamNumber, IMFMediaType* pType) { if (!pProfile) { return E_INVALIDARG; } if (wStreamNumber < 1 || wStreamNumber > 127 ) { return MF_E_INVALIDSTREAMNUMBER; } HRESULT hr = S_OK; IMFMediaType* pVideoType = NULL; IMFASFStreamConfig* pVideoStream = NULL; UINT32 dwBitRate = 0; //Create a new video type from the source type hr = CreateCompressedVideoType(pType, &pVideoType); if (FAILED(hr)) { goto done; } //Create a new stream with the video type hr = pProfile->CreateStream(pVideoType, &pVideoStream); if (FAILED(hr)) { goto done; } //Set a valid stream number hr = pVideoStream->SetStreamNumber(wStreamNumber); if (FAILED(hr)) { goto done; } //Add the stream to the profile hr = pProfile->SetStream(pVideoStream); if (FAILED(hr)) { goto done; } wprintf_s(L"Video Stream created. Stream Number: %d .\n", wStreamNumber); done: SafeRelease(&pVideoStream); SafeRelease(&pVideoType); return hr; }
次のコード例では、ソース内のストリームに応じて出力ストリームを作成します。
//For each stream in the source, add target stream information in the profile
for (DWORD iStream = 0; iStream < dwSrcStream; iStream++)
{
hr = pPD->GetStreamDescriptorByIndex(
iStream, &fSelected, &pStreamDesc);
if (FAILED(hr))
{
goto done;
}
if (!fSelected)
{
continue;
}
//Get the media type handler
hr = pStreamDesc->GetMediaTypeHandler(&pHandler);
if (FAILED(hr))
{
goto done;
}
//Get the first media type
hr = pHandler->GetMediaTypeByIndex(0, &pMediaType);
if (FAILED(hr))
{
goto done;
}
//Get the major type
hr = pMediaType->GetMajorType(&guidMajor);
if (FAILED(hr))
{
goto done;
}
// If this is a video stream, create the target video stream
if (guidMajor == MFMediaType_Video)
{
//The stream level encoding properties will be set in this call
hr = CreateVideoStream(pProfile, wStreamID, pMediaType);
if (FAILED(hr))
{
goto done;
}
}
// If this is an audio stream, create the target audio stream
if (guidMajor == MFMediaType_Audio)
{
//The stream level encoding properties will be set in this call
hr = CreateAudioStream(pProfile, wStreamID);
if (FAILED(hr))
{
goto done;
}
}
//Get stream's encoding property
hr = pContentInfo->GetEncodingConfigurationPropertyStore(wStreamID, &pContentInfoProps);
if (FAILED(hr))
{
goto done;
}
//Set the stream-level encoding properties
hr = SetEncodingProperties(guidMajor, pContentInfoProps);
if (FAILED(hr))
{
goto done;
}
SafeRelease(&pMediaType);
SafeRelease(&pStreamDesc);
SafeRelease(&pContentInfoProps);
wStreamID++;
}
圧縮オーディオ メディアの種類を作成する
出力ファイルにオーディオ ストリームを含める場合は、必要な属性を設定してエンコードされた型の特性を指定して、オーディオの種類を作成します。 オーディオの種類が Windows Media オーディオ エンコーダーと互換性があることを確認するには、エンコーダー MFT をインスタンス化し、エンコード プロパティを設定し、IMFTransform::GetOutputAvailableType を呼び出してメディアの種類を取得します。 使用可能なすべての種類をループ処理し、各メディアの種類の属性を取得し、要件に最も近い種類を選択して、必要な出力の種類を取得します。 このチュートリアルでは、エンコーダーでサポートされている出力メディアの種類の一覧から、使用可能な最初の種類を取得します。
注意: Windows 7 の場合、メディア ファンデーションには、互換性のあるオーディオの種類の一覧を取得する新しい関数、MFTranscodeGetAudioOutputAvailableTypes が用意されています。 この関数は、CBR エンコードのメディアの種類のみを取得します。
完全なオーディオの種類には、次の属性が設定されている必要があります。
- MF_MT_MAJOR_TYPE
- MF_MT_SUBTYPE
- MF_MT_AUDIO_NUM_CHANNELS
- MF_MT_AUDIO_SAMPLES_PER_SECOND
- MF_MT_AUDIO_BLOCK_ALIGNMENT
- MF_MT_AUDIO_AVG_BYTES_PER_SECOND
- MF_MT_AUDIO_BITS_PER_SAMPLE
次のコード例では、Windows Media オーディオ エンコーダーから互換性のある種類を取得して、圧縮オーディオの種類を作成します。 SetEncodingProperties の実装については、このチュートリアルの「ASF ContentInfo オブジェクトを作成する」セクションを参照してください。
//-------------------------------------------------------------------
// GetOutputTypeFromWMAEncoder
// Gets a compatible output type from the Windows Media audio encoder.
//
// ppAudioType: Receives a pointer to the media type.
//-------------------------------------------------------------------
HRESULT GetOutputTypeFromWMAEncoder(IMFMediaType** ppAudioType)
{
if (!ppAudioType)
{
return E_POINTER;
}
IMFTransform* pMFT = NULL;
IMFMediaType* pType = NULL;
IPropertyStore* pProps = NULL;
//We need to find a suitable output media type
//We need to create the encoder to get the available output types
//and discard the instance.
CLSID *pMFTCLSIDs = NULL; // Pointer to an array of CLISDs.
UINT32 cCLSID = 0; // Size of the array.
MFT_REGISTER_TYPE_INFO tinfo;
tinfo.guidMajorType = MFMediaType_Audio;
tinfo.guidSubtype = MFAudioFormat_WMAudioV9;
// Look for an encoder.
HRESULT hr = MFTEnum(
MFT_CATEGORY_AUDIO_ENCODER,
0, // Reserved
NULL, // Input type to match. None.
&tinfo, // WMV encoded type.
NULL, // Attributes to match. (None.)
&pMFTCLSIDs, // Receives a pointer to an array of CLSIDs.
&cCLSID // Receives the size of the array.
);
if (FAILED(hr))
{
goto done;
}
// MFTEnum can return zero matches.
if (cCLSID == 0)
{
hr = MF_E_TOPO_CODEC_NOT_FOUND;
goto done;
}
else
{
// Create the MFT decoder
hr = CoCreateInstance(pMFTCLSIDs[0], NULL,
CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pMFT));
if (FAILED(hr))
{
goto done;
}
}
// Get the encoder's property store
hr = pMFT->QueryInterface(IID_PPV_ARGS(&pProps));
if (FAILED(hr))
{
goto done;
}
//Set encoding properties
hr = SetEncodingProperties(MFMediaType_Audio, pProps);
if (FAILED(hr))
{
goto done;
}
//Get the first output type
//You can loop through the available output types to choose
//the one that meets your target requirements
hr = pMFT->GetOutputAvailableType(0, 0, &pType);
if (FAILED(hr))
{
goto done;
}
//Return to the caller
*ppAudioType = pType;
(*ppAudioType)->AddRef();
done:
SafeRelease(&pProps);
SafeRelease(&pType);
SafeRelease(&pMFT);
CoTaskMemFree(pMFTCLSIDs);
return hr;
}
圧縮ビデオ メディアの種類を作成する
出力ファイルにビデオ ストリームを含める場合は、完全にエンコードされたビデオの種類を作成します。 完全なメディアの種類には、必要なビット レートとコーデック プライベート データを含める必要があります。
完全なビデオ メディア の種類を作成するには、2 通りの方法があります。
空のメディアの種類を作成し、ソース ビデオの種類からメディアの種類の属性をコピーし、MF_MT_SUBTYPE 属性を GUID 定数 MFVideoFormat_WMV3 で上書きします。
完全なビデオの種類には、次の属性が設定されている必要があります。
- MF_MT_MAJOR_TYPE
- MF_MT_SUBTYPE
- MF_MT_FRAME_RATE
- MF_MT_FRAME_SIZE
- MF_MT_INTERLACE_MODE
- MF_MT_PIXEL_ASPECT_RATIO
- MF_MT_AVG_BITRATE
- MF_MT_USER_DATA
次のコード例では、ソースのビデオの種類から圧縮されたビデオの種類を作成します。
//------------------------------------------------------------------- // CreateCompressedVideoType // Creates an output type from source's video media type. // // pType: A pointer to the source's video media type. // ppVideoType: Receives a pointer to the media type. //------------------------------------------------------------------- HRESULT CreateCompressedVideoType( IMFMediaType* pType, IMFMediaType** ppVideoType) { if (!pType) { return E_INVALIDARG; } if (!ppVideoType) { return E_POINTER; } HRESULT hr = S_OK; MFRatio fps = { 0 }; MFRatio par = { 0 }; UINT32 width = 0, height = 0; UINT32 interlace = MFVideoInterlace_Unknown; UINT32 fSamplesIndependent = 0; UINT32 cBitrate = 0; IMFMediaType* pMediaType = NULL; GUID guidMajor = GUID_NULL; hr = pType->GetMajorType(&guidMajor); if (FAILED(hr)) { goto done; } if (guidMajor != MFMediaType_Video ) { hr = MF_E_INVALID_FORMAT; goto done; } hr = MFCreateMediaType(&pMediaType); if (FAILED(hr)) { goto done; } hr = pType->CopyAllItems(pMediaType); if (FAILED(hr)) { goto done; } //Fill the missing attributes //1. Frame rate hr = MFGetAttributeRatio(pMediaType, MF_MT_FRAME_RATE, (UINT32*)&fps.Numerator, (UINT32*)&fps.Denominator); if (hr == MF_E_ATTRIBUTENOTFOUND) { fps.Numerator = 30000; fps.Denominator = 1001; hr = MFSetAttributeRatio(pMediaType, MF_MT_FRAME_RATE, fps.Numerator, fps.Denominator); if (FAILED(hr)) { goto done; } } ////2. Frame size hr = MFGetAttributeSize(pMediaType, MF_MT_FRAME_SIZE, &width, &height); if (hr == MF_E_ATTRIBUTENOTFOUND) { width = 1280; height = 720; hr = MFSetAttributeSize(pMediaType, MF_MT_FRAME_SIZE, width, height); if (FAILED(hr)) { goto done; } } ////3. Interlace mode if (FAILED(pMediaType->GetUINT32(MF_MT_INTERLACE_MODE, &interlace))) { hr = pMediaType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive); if (FAILED(hr)) { goto done; } } ////4. Pixel aspect Ratio hr = MFGetAttributeRatio(pMediaType, MF_MT_PIXEL_ASPECT_RATIO, (UINT32*)&par.Numerator, (UINT32*)&par.Denominator); if (hr == MF_E_ATTRIBUTENOTFOUND) { par.Numerator = par.Denominator = 1; hr = MFSetAttributeRatio(pMediaType, MF_MT_PIXEL_ASPECT_RATIO, (UINT32)par.Numerator, (UINT32)par.Denominator); if (FAILED(hr)) { goto done; } } //6. bit rate if (FAILED(pMediaType->GetUINT32(MF_MT_AVG_BITRATE, &cBitrate))) { hr = pMediaType->SetUINT32(MF_MT_AVG_BITRATE, 1000000); if (FAILED(hr)) { goto done; } } hr = pMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_WMV3); if (FAILED(hr)) { goto done; } // Return the pointer to the caller. *ppVideoType = pMediaType; (*ppVideoType)->AddRef(); done: SafeRelease(&pMediaType); return hr; }
エンコード プロパティを設定し、IMFTransform::GetOutputAvailableType を呼び出して、Windows Media ビデオ エンコーダーから互換性のあるメディアの種類を取得します。 このメソッドは、部分型を返します。 次の情報を追加して、部分型を完全な型に変換します。
- MF_MT_AVG_BITRATE 属性でビデオ ビット レートを設定します。
- MF_MT_USER_DATA 属性を設定して、コーデック プライベート データを追加します。 手順の詳細については、「WMV エンコーダーの構成」の「プライベート コーデック データ」を参照してください。
IWMCodecPrivateData::GetPrivateData はコーデック プライベート データを返す前にビット レートをチェックするため、コーデック プライベート データを取得する前にビット レートを設定してください。
次のコード例では、Windows Media ビデオ エンコーダーから互換性のある種類を取得して、圧縮ビデオの種類を作成します。 また、圧縮されていないビデオの種類も作成し、エンコーダーの入力として設定します。 これは、ヘルパー関数の CreateUncompressedVideoType で実装されます。 GetOutputTypeFromWMVEncoder は、コーデック プライベート データを追加することによって、返された部分型を完全な型に変換します。 SetEncodingProperties の実装については、このチュートリアルの「ASF ContentInfo オブジェクトを作成する」セクションを参照してください。
//------------------------------------------------------------------- // GetOutputTypeFromWMVEncoder // Gets a compatible output type from the Windows Media video encoder. // // pType: A pointer to the source video stream's media type. // ppVideoType: Receives a pointer to the media type. //------------------------------------------------------------------- HRESULT GetOutputTypeFromWMVEncoder(IMFMediaType* pType, IMFMediaType** ppVideoType) { if (!ppVideoType) { return E_POINTER; } IMFTransform* pMFT = NULL; IPropertyStore* pProps = NULL; IMFMediaType* pInputType = NULL; IMFMediaType* pOutputType = NULL; UINT32 cBitrate = 0; //We need to find a suitable output media type //We need to create the encoder to get the available output types //and discard the instance. CLSID *pMFTCLSIDs = NULL; // Pointer to an array of CLISDs. UINT32 cCLSID = 0; // Size of the array. MFT_REGISTER_TYPE_INFO tinfo; tinfo.guidMajorType = MFMediaType_Video; tinfo.guidSubtype = MFVideoFormat_WMV3; // Look for an encoder. HRESULT hr = MFTEnum( MFT_CATEGORY_VIDEO_ENCODER, 0, // Reserved NULL, // Input type to match. None. &tinfo, // WMV encoded type. NULL, // Attributes to match. (None.) &pMFTCLSIDs, // Receives a pointer to an array of CLSIDs. &cCLSID // Receives the size of the array. ); if (FAILED(hr)) { goto done; } // MFTEnum can return zero matches. if (cCLSID == 0) { hr = MF_E_TOPO_CODEC_NOT_FOUND; goto done; } else { //Create the MFT decoder hr = CoCreateInstance(pMFTCLSIDs[0], NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pMFT)); if (FAILED(hr)) { goto done; } } //Get the video encoder property store hr = pMFT->QueryInterface(IID_PPV_ARGS(&pProps)); if (FAILED(hr)) { goto done; } //Set encoding properties hr = SetEncodingProperties(MFMediaType_Video, pProps); if (FAILED(hr)) { goto done; } hr = CreateUncompressedVideoType(pType, &pInputType); if (FAILED(hr)) { goto done; } hr = pMFT->SetInputType(0, pInputType, 0); //Get the first output type //You can loop through the available output types to choose //the one that meets your target requirements hr = pMFT->GetOutputAvailableType(0, 0, &pOutputType); if (FAILED(hr)) { goto done; } hr = pType->GetUINT32(MF_MT_AVG_BITRATE, &cBitrate); if (FAILED(hr)) { goto done; } //Now set the bit rate hr = pOutputType->SetUINT32(MF_MT_AVG_BITRATE, cBitrate); if (FAILED(hr)) { goto done; } hr = AddPrivateData(pMFT, pOutputType); if (FAILED(hr)) { goto done; } //Return to the caller *ppVideoType = pOutputType; (*ppVideoType)->AddRef(); done: SafeRelease(&pProps); SafeRelease(&pOutputType); SafeRelease(&pInputType); SafeRelease(&pMFT); CoTaskMemFree(pMFTCLSIDs); return hr; }
次のコード例では、圧縮されていないビデオの種類を作成します。
//------------------------------------------------------------------- // CreateUncompressedVideoType // Creates an uncompressed video type. // // pType: A pointer to the source's video media type. // ppVideoType: Receives a pointer to the media type. //------------------------------------------------------------------- HRESULT CreateUncompressedVideoType(IMFMediaType* pType, IMFMediaType** ppMediaType) { if (!pType) { return E_INVALIDARG; } if (!ppMediaType) { return E_POINTER; } MFRatio fps = { 0 }; MFRatio par = { 0 }; UINT32 width = 0, height = 0; UINT32 interlace = MFVideoInterlace_Unknown; UINT32 cBitrate = 0; IMFMediaType* pMediaType = NULL; GUID guidMajor = GUID_NULL; HRESULT hr = pType->GetMajorType(&guidMajor); if (FAILED(hr)) { goto done; } if (guidMajor != MFMediaType_Video ) { hr = MF_E_INVALID_FORMAT; goto done; } hr = MFGetAttributeRatio(pType, MF_MT_FRAME_RATE, (UINT32*)&fps.Numerator, (UINT32*)&fps.Denominator); if (hr == MF_E_ATTRIBUTENOTFOUND) { fps.Numerator = 30000; fps.Denominator = 1001; } hr = MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &width, &height); if (hr == MF_E_ATTRIBUTENOTFOUND) { width = 1280; height = 720; } interlace = MFGetAttributeUINT32(pType, MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive); hr = MFGetAttributeRatio(pType, MF_MT_PIXEL_ASPECT_RATIO, (UINT32*)&par.Numerator, (UINT32*)&par.Denominator); if (FAILED(hr)) { par.Numerator = par.Denominator = 1; } cBitrate = MFGetAttributeUINT32(pType, MF_MT_AVG_BITRATE, 1000000); hr = MFCreateMediaType(&pMediaType); if (FAILED(hr)) { goto done; } hr = pMediaType->SetGUID(MF_MT_MAJOR_TYPE, guidMajor); if (FAILED(hr)) { goto done; } hr = pMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32); if (FAILED(hr)) { goto done; } hr = MFSetAttributeRatio(pMediaType, MF_MT_FRAME_RATE, fps.Numerator, fps.Denominator); if (FAILED(hr)) { goto done; } hr = MFSetAttributeSize(pMediaType, MF_MT_FRAME_SIZE, width, height); if (FAILED(hr)) { goto done; } hr = pMediaType->SetUINT32(MF_MT_INTERLACE_MODE, 2); if (FAILED(hr)) { goto done; } hr = pMediaType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE); if (FAILED(hr)) { goto done; } hr = pMediaType->SetUINT32(MF_MT_AVG_BITRATE, cBitrate); if (FAILED(hr)) { goto done; } // Return the pointer to the caller. *ppMediaType = pMediaType; (*ppMediaType)->AddRef(); done: SafeRelease(&pMediaType); return hr; }
次のコード例では、指定したビデオ メディアの種類にコーデック プライベート データを追加します。
// // AddPrivateData // Appends the private codec data to a media type. // // pMFT: The video encoder // pTypeOut: A video type from the encoder's type list. // // The function modifies pTypeOut by adding the codec data. // HRESULT AddPrivateData(IMFTransform *pMFT, IMFMediaType *pTypeOut) { HRESULT hr = S_OK; ULONG cbData = 0; BYTE *pData = NULL; IWMCodecPrivateData *pPrivData = NULL; DMO_MEDIA_TYPE mtOut = { 0 }; // Convert the type to a DMO type. hr = MFInitAMMediaTypeFromMFMediaType( pTypeOut, FORMAT_VideoInfo, (AM_MEDIA_TYPE*)&mtOut ); if (SUCCEEDED(hr)) { hr = pMFT->QueryInterface(IID_PPV_ARGS(&pPrivData)); } if (SUCCEEDED(hr)) { hr = pPrivData->SetPartialOutputType(&mtOut); } // // Get the private codec data // // First get the buffer size. if (SUCCEEDED(hr)) { hr = pPrivData->GetPrivateData(NULL, &cbData); } if (SUCCEEDED(hr)) { pData = new BYTE[cbData]; if (pData == NULL) { hr = E_OUTOFMEMORY; } } // Now get the data. if (SUCCEEDED(hr)) { hr = pPrivData->GetPrivateData(pData, &cbData); } // Add the data to the media type. if (SUCCEEDED(hr)) { hr = pTypeOut->SetBlob(MF_MT_USER_DATA, pData, cbData); } delete [] pData; MoFreeMediaType(&mtOut); SafeRelease(&pPrivData); return hr; }
ASF ContentInfo オブジェクトを作成する
ASF ContentInfo オブジェクトは、主に ASF ヘッダー オブジェクト情報を格納するように設計された WMContainer レベルのコンポーネントです。 ASF ファイル シンクは、エンコードされたファイルの ASF ヘッダー オブジェクトの書き込みに使用される情報を (プロパティ ストアに) 格納するために ContentInfo オブジェクトを実装します。 そのためには、エンコード セッションを開始する前に、ContentInfo オブジェクトに関する次の一連の情報を作成して構成する必要があります。
MFCreateASFContentInfo を呼び出して、空の ContentInfo オブジェクトを作成します。
次のコード例では、空の ContentInfo オブジェクトを作成します。
// Create an empty ContentInfo object hr = MFCreateASFContentInfo(&pContentInfo); if (FAILED(hr)) { goto done; }
IMFASFContentInfo::GetEncodingConfigurationPropertyStore を呼び出して、ファイル シンクのストリーム レベルのプロパティ ストアを取得します。 この呼び出しでは、ASF プロファイルの作成時にストリームに割り当てたストリーム番号を渡す必要があります。
ファイル シンクのストリーム プロパティ ストアで、ストリーム レベルのエンコード プロパティを設定します。 詳細については、「ファイル シンクのプロパティの設定」の「ストリーム エンコード プロパティ」を参照してください。
次のコード例では、ファイル シンクのプロパティ ストアでストリーム レベルのプロパティを設定します。
//Get stream's encoding property hr = pContentInfo->GetEncodingConfigurationPropertyStore(wStreamID, &pContentInfoProps); if (FAILED(hr)) { goto done; } //Set the stream-level encoding properties hr = SetEncodingProperties(guidMajor, pContentInfoProps); if (FAILED(hr)) { goto done; }
次のコード例では、SetEncodingProperties の実装を示しています。 この関数は、CBR および VBR のストリーム レベルのエンコード プロパティを設定します。
//------------------------------------------------------------------- // SetEncodingProperties // Create a media source from a URL. // // guidMT: Major type of the stream, audio or video // pProps: A pointer to the property store in which // to set the required encoding properties. //------------------------------------------------------------------- HRESULT SetEncodingProperties (const GUID guidMT, IPropertyStore* pProps) { if (!pProps) { return E_INVALIDARG; } if (EncodingMode == NONE) { return MF_E_NOT_INITIALIZED; } HRESULT hr = S_OK; PROPVARIANT var; switch (EncodingMode) { case CBR: // Set VBR to false. hr = InitPropVariantFromBoolean(FALSE, &var); if (FAILED(hr)) { goto done; } hr = pProps->SetValue(MFPKEY_VBRENABLED, var); if (FAILED(hr)) { goto done; } // Set the video buffer window. if (guidMT == MFMediaType_Video) { hr = InitPropVariantFromInt32(VIDEO_WINDOW_MSEC, &var); if (FAILED(hr)) { goto done; } hr = pProps->SetValue(MFPKEY_VIDEOWINDOW, var); if (FAILED(hr)) { goto done; } } break; case VBR: //Set VBR to true. hr = InitPropVariantFromBoolean(TRUE, &var); if (FAILED(hr)) { goto done; } hr = pProps->SetValue(MFPKEY_VBRENABLED, var); if (FAILED(hr)) { goto done; } // Number of encoding passes is 1. hr = InitPropVariantFromInt32(1, &var); if (FAILED(hr)) { goto done; } hr = pProps->SetValue(MFPKEY_PASSESUSED, var); if (FAILED(hr)) { goto done; } // Set the quality level. if (guidMT == MFMediaType_Audio) { hr = InitPropVariantFromUInt32(98, &var); if (FAILED(hr)) { goto done; } hr = pProps->SetValue(MFPKEY_DESIRED_VBRQUALITY, var); if (FAILED(hr)) { goto done; } } else if (guidMT == MFMediaType_Video) { hr = InitPropVariantFromUInt32(95, &var); if (FAILED(hr)) { goto done; } hr = pProps->SetValue(MFPKEY_VBRQUALITY, var); if (FAILED(hr)) { goto done; } } break; default: hr = E_UNEXPECTED; break; } done: PropVariantClear(&var); return hr; }
IMFASFContentInfo::GetEncodingConfigurationPropertyStore を呼び出して、ファイル シンクのグローバル プロパティ ストアを取得します。 この呼び出しでは、最初のパラメーターに 0 を渡す必要があります。 詳細については、「ファイル シンクのプロパティの設定」の「グローバル ファイル シンクのプロパティ」を参照してください。
ASF マルチプレクサーの漏れバケット値が適切に調整されるように、MFPKEY_ASFMEDIASINK_AUTOADJUST_BITRATE を VARIANT_TRUE に設定します。 このプロパティの詳細については、「マルチプレクサー オブジェクトの作成」の「マルチプレクサーの初期化と漏れバケット設定」を参照してください。
次のコード例では、ファイル シンクのプロパティ ストアでストリーム レベルのプロパティを設定します。
//Now we need to set global properties that will be set on the media sink hr = pContentInfo->GetEncodingConfigurationPropertyStore(0, &pContentInfoProps); if (FAILED(hr)) { goto done; } //Auto adjust Bitrate var.vt = VT_BOOL; var.boolVal = VARIANT_TRUE; hr = pContentInfoProps->SetValue(MFPKEY_ASFMEDIASINK_AUTOADJUST_BITRATE, var); //Initialize with the profile hr = pContentInfo->SetProfile(pProfile); if (FAILED(hr)) { goto done; }
Note
この他にも、ファイル シンクのグローバル レベルで設定できるプロパティがあります。 詳細については、「ContentInfo オブジェクトのプロパティの設定」の「エンコーダー設定を使用した ContentInfo オブジェクトの構成」を参照してください。
構成された ASF ContentInfo を使用して、ASF ファイル シンクのアクティブ化オブジェクトを作成します (次のセクションで説明します)。
アウトプロセス ファイル シンク (MFCreateASFMediaSinkActivate) を作成する場合は、アクティブ化オブジェクトを使用して、構成された ContentInfo オブジェクトを使用して ASF メディア シンクをインスタンス化できます (次のセクションで説明します)。 手順 1 で説明したとおり、空の ContentInfo オブジェクトを作成するのではなく、インプロセス ファイル シンク (MFCreateASFMediaSink) を作成する場合は、ファイル シンクで IMFMediaSink::QueryInterface を呼び出して、IMFASFContentInfo インターフェイスへの参照を取得します。 次に、このセクションの説明に従って ContentInfo オブジェクトを構成する必要があります。
ASF ファイル シンクを作成する
チュートリアルのこの手順では、前の手順で作成した構成済みの ASF ContentInfo を使用して、MFCreateASFMediaSinkActivate 関数を呼び出して ASF ファイル シンクのアクティブ化オブジェクトを作成します。 詳細については、「ASFファイル シンクの作成」を参照してください。
次のコード例では、ファイル シンクのアクティブ化オブジェクトを作成します。
//Create the activation object for the file sink
hr = MFCreateASFMediaSinkActivate(sURL, pContentInfo, &pActivate);
if (FAILED(hr))
{
goto done;
}
部分エンコード トポロジを構築する
次に、メディア ソース、必要な Windows Media エンコーダー、ASF ファイル シンクのトポロジ ノードを作成して、部分エンコード トポロジを構築します。 トポロジ ノードを追加した後、ソース、変換、シンク ノードを接続します。 トポロジ ノードを追加する前に、MFCreateTopology を呼び出して空のトポロジ オブジェクトを作成する必要があります。
メディア ソースのソース トポロジ ノードを作成する
この手順では、メディア ソースのソース トポロジ ノードを作成します。
このノードを作成するには、次の参照が必要です。
- このチュートリアルの「メディア ソースを作成する」セクションで説明されている手順で作成したメディア ソースへのポインター。
- メディア ソースのプレゼンテーション記述子へのポインター。 IMFMediaSource::CreatePresentationDescriptor を呼び出すことで、メディア ソースの IMFPresentationDescriptor インターフェイスへの参照を取得できます。
- このチュートリアルの「ASF プロファイル オブジェクトを作成する」セクションで説明されている手順でターゲット ストリームを作成したメディア ソース内の各ストリームのストリーム記述子へのポインター。
ソース ノードの作成とコード例の詳細については、「ソース ノードの作成」を参照してください。
次のコード例では、ソース ノードと必要な変換ノードを追加して、部分トポロジを作成します。 このコードは、ヘルパー関数の AddSourceNode と AddTransformOutputNodes を呼び出します。 これらの関数については、このチュートリアル内で後述しています。
//-------------------------------------------------------------------
// BuildPartialTopology
// Create a partial encoding topology by adding the source and the sink.
//
// pSource: A pointer to the media source to enumerate the source streams.
// pSinkActivate: A pointer to the activation object for ASF file sink.
// ppTopology: Receives a pointer to the topology.
//-------------------------------------------------------------------
HRESULT BuildPartialTopology(
IMFMediaSource *pSource,
IMFActivate* pSinkActivate,
IMFTopology** ppTopology)
{
if (!pSource || !pSinkActivate)
{
return E_INVALIDARG;
}
if (!ppTopology)
{
return E_POINTER;
}
HRESULT hr = S_OK;
IMFPresentationDescriptor* pPD = NULL;
IMFStreamDescriptor *pStreamDesc = NULL;
IMFMediaTypeHandler* pMediaTypeHandler = NULL;
IMFMediaType* pSrcType = NULL;
IMFTopology* pTopology = NULL;
IMFTopologyNode* pSrcNode = NULL;
IMFTopologyNode* pEncoderNode = NULL;
IMFTopologyNode* pOutputNode = NULL;
DWORD cElems = 0;
DWORD dwSrcStream = 0;
DWORD StreamID = 0;
GUID guidMajor = GUID_NULL;
BOOL fSelected = FALSE;
//Create the topology that represents the encoding pipeline
hr = MFCreateTopology (&pTopology);
if (FAILED(hr))
{
goto done;
}
hr = pSource->CreatePresentationDescriptor(&pPD);
if (FAILED(hr))
{
goto done;
}
hr = pPD->GetStreamDescriptorCount(&dwSrcStream);
if (FAILED(hr))
{
goto done;
}
for (DWORD iStream = 0; iStream < dwSrcStream; iStream++)
{
hr = pPD->GetStreamDescriptorByIndex(
iStream, &fSelected, &pStreamDesc);
if (FAILED(hr))
{
goto done;
}
if (!fSelected)
{
continue;
}
hr = AddSourceNode(pTopology, pSource, pPD, pStreamDesc, &pSrcNode);
if (FAILED(hr))
{
goto done;
}
hr = pStreamDesc->GetMediaTypeHandler (&pMediaTypeHandler);
if (FAILED(hr))
{
goto done;
}
hr = pStreamDesc->GetStreamIdentifier(&StreamID);
if (FAILED(hr))
{
goto done;
}
hr = pMediaTypeHandler->GetMediaTypeByIndex(0, &pSrcType);
if (FAILED(hr))
{
goto done;
}
hr = pSrcType->GetMajorType(&guidMajor);
if (FAILED(hr))
{
goto done;
}
hr = AddTransformOutputNodes(pTopology, pSinkActivate, pSrcType, &pEncoderNode);
if (FAILED(hr))
{
goto done;
}
//now we have the transform node, connect it to the source node
hr = pSrcNode->ConnectOutput(0, pEncoderNode, 0);
if (FAILED(hr))
{
goto done;
}
SafeRelease(&pStreamDesc);
SafeRelease(&pMediaTypeHandler);
SafeRelease(&pSrcType);
SafeRelease(&pEncoderNode);
SafeRelease(&pOutputNode);
guidMajor = GUID_NULL;
}
*ppTopology = pTopology;
(*ppTopology)->AddRef();
wprintf_s(L"Partial Topology Built.\n");
done:
SafeRelease(&pStreamDesc);
SafeRelease(&pMediaTypeHandler);
SafeRelease(&pSrcType);
SafeRelease(&pEncoderNode);
SafeRelease(&pOutputNode);
SafeRelease(&pTopology);
return hr;
}
次のコード例では、ソース トポロジ ノードを作成し、エンコード トポロジに追加します。 これまでに作成したトポロジ オブジェクト、ソース ストリームを列挙するメディア ソース、メディア ソースのプレゼンテーション記述子、およびメディア ソースのストリーム記述子へのポインターを受け取ります。 呼び出し元は、ソース トポロジ ノードへのポインターを受け取ります。
// Add a source node to a topology.
HRESULT AddSourceNode(
IMFTopology *pTopology, // Topology.
IMFMediaSource *pSource, // Media source.
IMFPresentationDescriptor *pPD, // Presentation descriptor.
IMFStreamDescriptor *pSD, // Stream descriptor.
IMFTopologyNode **ppNode) // Receives the node pointer.
{
IMFTopologyNode *pNode = NULL;
// Create the node.
HRESULT hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &pNode);
if (FAILED(hr))
{
goto done;
}
// Set the attributes.
hr = pNode->SetUnknown(MF_TOPONODE_SOURCE, pSource);
if (FAILED(hr))
{
goto done;
}
hr = pNode->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, pPD);
if (FAILED(hr))
{
goto done;
}
hr = pNode->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, pSD);
if (FAILED(hr))
{
goto done;
}
// Add the node to the topology.
hr = pTopology->AddNode(pNode);
if (FAILED(hr))
{
goto done;
}
// Return the pointer to the caller.
*ppNode = pNode;
(*ppNode)->AddRef();
done:
SafeRelease(&pNode);
return hr;
}
必要なエンコーダーをインスタンス化し、変換ノードを作成する
メディア ファンデーション パイプラインは、エンコードする必要があるストリームに必要な Windows Media エンコーダーを自動的に挿入しません。 アプリケーションはエンコーダーを手動で追加する必要があります。 これを行うには、このチュートリアルの「ASF プロファイル オブジェクトを作成する」セクションで説明されている手順で作成した ASF プロファイル内のストリームを列挙します。 ソース内のストリームとプロファイル内の対応するストリームごとに、必要なエンコーダーをインスタンス化します。 この手順では、このチュートリアルの「ASF ファイル シンクを作成する」セクションで説明されている手順で作成したファイル シンクのアクティブ化オブジェクトへのポインターが必要になります。
アクティブ化オブジェクトを使用したエンコーダーの作成の概要については、「エンコーダーのアクティブ化オブジェクトの使用」を参照してください。
次の手順では、必要なエンコーダーをインスタンス化するために必要な手順について説明します。
ファイル シンク アクティブ化で IMFActivate::ActivateObject を呼び出し、QueryInterface を呼び出してファイル シンクから IMFASFContentInfo のクエリを実行することで、シンクの ContentInfo オブジェクトへの参照を取得します。
IMFASFContentInfo::GetProfile を呼び出して、ContentInfo オブジェクトに関連付けられている ASF プロファイルを取得します。
プロファイル内のストリームを列挙します。 そのためには、ストリーム数と、各ストリームの IMFASFStreamConfig インターフェイスへの参照が必要です。
次のメソッドを呼び出します。
各ストリームについて、メジャー型とストリームのエンコード プロパティを ContentInfo オブジェクトから取得します。
次のメソッドを呼び出します。
IMFASFContentInfo::GetEncodingConfigurationPropertyStore
この呼び出しには、IMFASFProfile::GetStream 呼び出しによって取得されたストリーム番号が必要です。
ストリーム、オーディオ、またはビデオの種類に応じて、MFCreateWMAEncoderActivate または MFCreateWMVEncoderActivate を呼び出して、エンコーダーのアクティブ化オブジェクトをインスタンス化します。
これらの関数を呼び出すには、次の参照が必要です。
- 前の手順で IMFASFStreamConfig::GetMediaType によって 取得されたストリームのメディアの種類へのポインター。
- IMFASFContentInfo::GetEncodingConfigurationPropertyStore によって取得されたストリームのエンコード プロパティ ストアへのポインター。 ポインターをプロパティ ストアへ渡すことで、ファイル シンクに設定されたストリーム プロパティがエンコーダー MFT にコピーされます。
オーディオ ストリームの漏れバケット パラメーターを更新します。
MFCreateWMAEncoderActivate は、Windows Media オーディオ コーデックの基になるエンコーダー MFT に出力の種類を設定します。 出力メディアの種類が設定されると、エンコーダーは出力メディアの種類から平均ビット レートを取得し、バッファー ウィンドウを計算し、エンコード セッション中に使用される漏れバケット値を設定します。 エンコーダーに対してクエリを実行するか、カスタム値を設定することで、ファイル シンク内のこれらの値を更新できます。 値を更新するには、次の一連の情報が必要になります。
- 平均ビット レート: メディア タイプ ネゴシエーション中に選択された出力メディアの種類で設定された MF_MT_AUDIO_AVG_BYTES_PER_SECOND 属性から、平均ビット レートを取得します。
- バッファー ウィンドウ: IWMCodecLeakyBucket::GetBufferSizeBits を呼び出して取得します。
- 初期バッファー サイズ: 0 に設定します。
DWORD の配列を作成し、オーディオ ストリーム シンクの MFPKEY_ASFSTREAMSINK_CORRECTED_LEAKYBUCKET プロパティにその値を設定します。 更新された値を指定しない場合、メディア セッションによって適切に設定されます。
詳細については、「漏れバケット バッファー モデル」を参照してください。
手順 5 で作成したアクティブ化オブジェクトは、変換トポロジ ノードとしてトポロジに追加する必要があります。 詳細とコード例については、「変換ノードの作成」の「アクティブ化オブジェクトからの変換ノードの作成」を参照してください。
次のコード例では、必要なエンコーダーのアクティブ化を作成して追加します。 これまでに作成したトポロジ オブジェクト、ファイル シンクのアクティブ化オブジェクト、およびソース ストリームのメディアの種類へのポインターを受け取ります。 また、エンコード トポロジにシンク ノードを作成して追加する AddOutputNode (次のコード例を参照) も呼び出します。 呼び出し元は、ソース トポロジ ノードへのポインターを受け取ります。
//-------------------------------------------------------------------
// AddTransformOutputNodes
// Creates and adds the sink node to the encoding topology.
// Creates and adds the required encoder activates.
// pTopology: A pointer to the topology.
// pSinkActivate: A pointer to the file sink's activation object.
// pSourceType: A pointer to the source stream's media type.
// ppNode: Receives a pointer to the topology node.
//-------------------------------------------------------------------
HRESULT AddTransformOutputNodes(
IMFTopology* pTopology,
IMFActivate* pSinkActivate,
IMFMediaType* pSourceType,
IMFTopologyNode **ppNode // Receives the node pointer.
)
{
if (!pTopology || !pSinkActivate || !pSourceType)
{
return E_INVALIDARG;
}
IMFTopologyNode* pEncNode = NULL;
IMFTopologyNode* pOutputNode = NULL;
IMFASFContentInfo* pContentInfo = NULL;
IMFASFProfile* pProfile = NULL;
IMFASFStreamConfig* pStream = NULL;
IMFMediaType* pMediaType = NULL;
IPropertyStore* pProps = NULL;
IMFActivate *pEncoderActivate = NULL;
IMFMediaSink *pSink = NULL;
GUID guidMT = GUID_NULL;
GUID guidMajor = GUID_NULL;
DWORD cStreams = 0;
WORD wStreamNumber = 0;
HRESULT hr = S_OK;
hr = pSourceType->GetMajorType(&guidMajor);
if (FAILED(hr))
{
goto done;
}
// Create the node.
hr = MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &pEncNode);
if (FAILED(hr))
{
goto done;
}
//Activate the sink
hr = pSinkActivate->ActivateObject(__uuidof(IMFMediaSink), (void**)&pSink);
if (FAILED(hr))
{
goto done;
}
//find the media type in the sink
//Get content info from the sink
hr = pSink->QueryInterface(__uuidof(IMFASFContentInfo), (void**)&pContentInfo);
if (FAILED(hr))
{
goto done;
}
hr = pContentInfo->GetProfile(&pProfile);
if (FAILED(hr))
{
goto done;
}
hr = pProfile->GetStreamCount(&cStreams);
if (FAILED(hr))
{
goto done;
}
for(DWORD index = 0; index < cStreams ; index++)
{
hr = pProfile->GetStream(index, &wStreamNumber, &pStream);
if (FAILED(hr))
{
goto done;
}
hr = pStream->GetMediaType(&pMediaType);
if (FAILED(hr))
{
goto done;
}
hr = pMediaType->GetMajorType(&guidMT);
if (FAILED(hr))
{
goto done;
}
if (guidMT!=guidMajor)
{
SafeRelease(&pStream);
SafeRelease(&pMediaType);
guidMT = GUID_NULL;
continue;
}
//We need to activate the encoder
hr = pContentInfo->GetEncodingConfigurationPropertyStore(wStreamNumber, &pProps);
if (FAILED(hr))
{
goto done;
}
if (guidMT == MFMediaType_Audio)
{
hr = MFCreateWMAEncoderActivate(pMediaType, pProps, &pEncoderActivate);
if (FAILED(hr))
{
goto done;
}
wprintf_s(L"Audio Encoder created. Stream Number: %d .\n", wStreamNumber);
break;
}
if (guidMT == MFMediaType_Video)
{
hr = MFCreateWMVEncoderActivate(pMediaType, pProps, &pEncoderActivate);
if (FAILED(hr))
{
goto done;
}
wprintf_s(L"Video Encoder created. Stream Number: %d .\n", wStreamNumber);
break;
}
}
// Set the object pointer.
hr = pEncNode->SetObject(pEncoderActivate);
if (FAILED(hr))
{
goto done;
}
// Add the node to the topology.
hr = pTopology->AddNode(pEncNode);
if (FAILED(hr))
{
goto done;
}
//Add the output node to this node.
hr = AddOutputNode(pTopology, pSinkActivate, wStreamNumber, &pOutputNode);
if (FAILED(hr))
{
goto done;
}
//now we have the output node, connect it to the transform node
hr = pEncNode->ConnectOutput(0, pOutputNode, 0);
if (FAILED(hr))
{
goto done;
}
// Return the pointer to the caller.
*ppNode = pEncNode;
(*ppNode)->AddRef();
done:
SafeRelease(&pEncNode);
SafeRelease(&pOutputNode);
SafeRelease(&pEncoderActivate);
SafeRelease(&pMediaType);
SafeRelease(&pProps);
SafeRelease(&pStream);
SafeRelease(&pProfile);
SafeRelease(&pContentInfo);
SafeRelease(&pSink);
return hr;
}
ファイル シンクの出力トポロジ ノードを作成する
この手順では、ASF ファイル シンクの出力トポロジ ノードを作成します。
このノードを作成するには、次の参照が必要です。
- このチュートリアルの「ASF ファイル シンクを作成する」セクションで説明されている手順で作成したアクティブ化オブジェクトへのポインター。
- ファイル シンクに追加されたストリーム シンクを識別するストリーム番号。 ストリーム番号は、ストリームの作成時に設定されたストリームの ID と一致します。
出力ノードの作成とコード例の詳細については、「出力ノードの作成」の「アクティブ化オブジェクトからの出力ノードの作成」を参照してください。
ファイル シンクにアクティブ化オブジェクトを使用していない場合は、ASF ファイル シンク内のストリーム シンクを列挙し、各ストリーム シンクをトポロジの出力ノードとして設定する必要があります。 ストリーム シンクの列挙については、「ASF ファイル シンクへのストリーム情報の追加」の「ストリーム シンクの列挙」を参照してください。
次のコード例では、シンク ノードを作成し、エンコード トポロジに追加します。 これまでに作成したトポロジ オブジェクト、ファイル シンクのアクティブ化オブジェクト、およびストリームの ID 番号へのポインターを受け取ります。 呼び出し元は、ソース トポロジ ノードへのポインターを受け取ります。
// Add an output node to a topology.
HRESULT AddOutputNode(
IMFTopology *pTopology, // Topology.
IMFActivate *pActivate, // Media sink activation object.
DWORD dwId, // Identifier of the stream sink.
IMFTopologyNode **ppNode) // Receives the node pointer.
{
IMFTopologyNode *pNode = NULL;
// Create the node.
HRESULT hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &pNode);
if (FAILED(hr))
{
goto done;
}
// Set the object pointer.
hr = pNode->SetObject(pActivate);
if (FAILED(hr))
{
goto done;
}
// Set the stream sink ID attribute.
hr = pNode->SetUINT32(MF_TOPONODE_STREAMID, dwId);
if (FAILED(hr))
{
goto done;
}
hr = pNode->SetUINT32(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE);
if (FAILED(hr))
{
goto done;
}
// Add the node to the topology.
hr = pTopology->AddNode(pNode);
if (FAILED(hr))
{
goto done;
}
// Return the pointer to the caller.
*ppNode = pNode;
(*ppNode)->AddRef();
done:
SafeRelease(&pNode);
return hr;
}
次のコード例では、特定のメディア シンクのストリーム シンクを列挙します。
//-------------------------------------------------------------------
// EnumerateStreamSinks
// Enumerates the stream sinks within the specified media sink.
//
// pSink: A pointer to the media sink.
//-------------------------------------------------------------------
HRESULT EnumerateStreamSinks (IMFMediaSink* pSink)
{
if (!pSink)
{
return E_INVALIDARG;
}
IMFStreamSink* pStreamSink = NULL;
DWORD cStreamSinks = 0;
HRESULT hr = pSink->GetStreamSinkCount(&cStreamSinks);
if (FAILED(hr))
{
goto done;
}
for(DWORD index = 0; index < cStreamSinks; index++)
{
hr = pSink->GetStreamSinkByIndex (index, &pStreamSink);
if (FAILED(hr))
{
goto done;
}
//Use the stream sink
//Not shown.
}
done:
SafeRelease(&pStreamSink);
return hr;
}
ソース ノード、変換ノード、シンク ノードを接続する
この手順では、このチュートリアルの「必要なエンコーダーをインスタンス化し、変換ノードを作成する」セクションで説明されている手順で作成したエンコード アクティブ化を参照する変換ノードに、ソース ノードを接続します。 変換ノードは、ファイル シンクのアクティブ化オブジェクトを含む出力ノードに接続されます。
エンコード セッションを処理する
この手順では、以下の手順を実行します。
MFCreateMediaSession を呼び出して、エンコード セッションを作成します。
IMFMediaSession::SetTopology を呼び出して、セッションでエンコード トポロジを設定します。 呼び出しが完了すると、メディア セッションはトポロジ ノードを評価し、エンコーダーへの入力としてフィードするために、指定された圧縮ソースを圧縮されていないサンプルに変換するデコーダーなどの追加の変換オブジェクトを挿入します。
IMFMediaSession::GetEvent を呼び出して、メディア セッションによって発生したイベントを要求します。
イベント ループでは、メディア セッションによって発生したイベントに応じて、エンコード セッションを開始および終了します。 IMFMediaSession::SetTopology 呼び出しは、MF_TOPOSTATUS_READY フラグが設定された MESessionTopologyStatus イベントを発生させるメディア セッションになります。 すべてのイベントは非同期的に生成され、アプリケーションはこれらのイベントを同期的または非同期的に取得できます。 このチュートリアルのアプリケーションはコンソール アプリケーションであり、ユーザー インターフェイス スレッドをブロックすることは問題ではないため、メディア セッションからイベントを同期的に取得します。
イベントを非同期的に取得するには、アプリケーションで IMFAsyncCallback インターフェイスを実装する必要があります。 このインターフェイスの実装例と詳細については、「メディア ファンデーションでメディア ファイルを再生する方法」の「セッション イベントを処理する」を参照してください。
メディア セッション イベントを取得するためのイベント ループで、IMFMediaSession::SetTopology が完了しトポロジが解決されたときに発生する、MESessionTopologyStatus イベントを待ちます。 MESessionTopologyStatus イベントを取得したら、IMFMediaSession::Start を呼び出してエンコード セッションを開始します。 メディア セッションでは、すべてのエンコード操作が完了すると、MEEndOfPresentation イベントが生成されます。 このイベントは VBR エンコードに対して処理される必要があり、このチュートリアルの次のセクション「VBR エンコード用のファイル シンクのエンコード プロパティを更新する」で説明します。
メディア セッションは ASF ヘッダー オブジェクトを生成し、エンコード セッションが完了したときにファイルを最終処理し、MESessionClosed イベントを発生させます。 このイベントは、メディア セッションで適切なシャットダウン操作を実行して処理する必要があります。 シャットダウン操作を開始するには、IMFMediaSession::Shutdown を呼び出します。 エンコード セッションを閉じた後は、同じメディア セッション インスタンスで別のトポロジをエンコード用に設定することはできません。 別のファイルをエンコードするには、現在のメディア セッションを閉じて解放し、新しく作成されたメディア セッションで新しいトポロジを設定する必要があります。 次のコード例では、メディア セッションを作成し、エンコード トポロジを設定し、メディア セッション イベントを処理します。
次のコード例では、メディア セッションを作成し、エンコード トポロジを設定し、メディア セッションからのイベントを処理してエンコード セッションを制御します。
//-------------------------------------------------------------------
// Encode
// Controls the encoding session and handles events from the media session.
//
// pTopology: A pointer to the encoding topology.
//-------------------------------------------------------------------
HRESULT Encode(IMFTopology *pTopology)
{
if (!pTopology)
{
return E_INVALIDARG;
}
IMFMediaSession *pSession = NULL;
IMFMediaEvent* pEvent = NULL;
IMFTopology* pFullTopology = NULL;
IUnknown* pTopoUnk = NULL;
MediaEventType meType = MEUnknown; // Event type
HRESULT hr = S_OK;
HRESULT hrStatus = S_OK; // Event status
MF_TOPOSTATUS TopoStatus = MF_TOPOSTATUS_INVALID; // Used with MESessionTopologyStatus event.
hr = MFCreateMediaSession(NULL, &pSession);
if (FAILED(hr))
{
goto done;
}
hr = pSession->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE, pTopology);
if (FAILED(hr))
{
goto done;
}
//Get media session events synchronously
while (1)
{
hr = pSession->GetEvent(0, &pEvent);
if (FAILED(hr))
{
goto done;
}
hr = pEvent->GetType(&meType);
if (FAILED(hr))
{
goto done;
}
hr = pEvent->GetStatus(&hrStatus);
if (FAILED(hr))
{
goto done;
}
if (FAILED(hrStatus))
{
hr = hrStatus;
goto done;
}
switch(meType)
{
case MESessionTopologyStatus:
{
// Get the status code.
MF_TOPOSTATUS status = (MF_TOPOSTATUS)MFGetAttributeUINT32(
pEvent, MF_EVENT_TOPOLOGY_STATUS, MF_TOPOSTATUS_INVALID);
if (status == MF_TOPOSTATUS_READY)
{
PROPVARIANT var;
PropVariantInit(&var);
wprintf_s(L"Topology resolved and set on the media session.\n");
hr = pSession->Start(NULL, &var);
if (FAILED(hr))
{
goto done;
}
}
if (status == MF_TOPOSTATUS_STARTED_SOURCE)
{
wprintf_s(L"Encoding started.\n");
break;
}
if (status == MF_TOPOSTATUS_ENDED)
{
wprintf_s(L"Encoding complete.\n");
hr = pSession->Close();
if (FAILED(hr))
{
goto done;
}
break;
}
}
break;
case MESessionEnded:
wprintf_s(L"Encoding complete.\n");
hr = pSession->Close();
if (FAILED(hr))
{
goto done;
}
break;
case MEEndOfPresentation:
{
if (EncodingMode == VBR)
{
hr = pSession->GetFullTopology(MFSESSION_GETFULLTOPOLOGY_CURRENT, 0, &pFullTopology);
if (FAILED(hr))
{
goto done;
}
hr = PostEncodingUpdate(pFullTopology);
if (FAILED(hr))
{
goto done;
}
wprintf_s(L"Updated sinks for VBR. \n");
}
}
break;
case MESessionClosed:
wprintf_s(L"Encoding session closed.\n");
hr = pSession->Shutdown();
goto done;
}
if (FAILED(hr))
{
goto done;
}
SafeRelease(&pEvent);
}
done:
SafeRelease(&pEvent);
SafeRelease(&pSession);
SafeRelease(&pFullTopology);
SafeRelease(&pTopoUnk);
return hr;
}
ファイル シンクのエンコード プロパティを更新する
エンコード ビット レートや正確な漏れバケット値などの特定のエンコード プロパティは、エンコードが完了するまで認識されません (特に VBR エンコードの場合)。 正しい値を取得するには、アプリケーションは、エンコード セッションが完了したことを示す MEEndOfPresentation イベントを待つ必要があります。 漏れバケット値は、ASF ヘッダー オブジェクトが正確な値を反映できるように、シンクで値を更新する必要があります。
次の手順では、エンコード トポロジ内のノードを走査してファイル シンク ノードを取得し、必要な漏れバケットのプロパティを設定するために必要な手順について説明します。
ASF ファイル シンクのエンコード後のプロパティ値を更新するには
- IMFTopology::GetOutputNodeCollection を呼び出して、エンコード トポロジから出力ノード コレクションを取得します。
- ノードごとに、IMFTopologyNode::GetObject を呼び出してノード内のストリーム シンクへのポインターを取得します。 IMFTopologyNode::GetObject によって返される IUnknown ポインターの IMFStreamSink インターフェイスのクエリを実行します。
- ストリーム シンクごとに、IMFTopologyNode::GetInput を呼び出してダウンストリーム ノード (エンコーダー) を取得します。
- ノードにクエリを実行して、エンコーダー ノードから IMFTransform ポインターを取得します。
- エンコーダーに IPropertyStore ポインターのクエリを実行して、エンコーダーからエンコード プロパティ ストアを取得します。
- ストリーム シンクに IPropertyStore ポインターのクエリを実行して、ストリーム シンクのプロパティ ストアを取得します。
- IPropertyStore::GetValue を呼び出してエンコーダーのプロパティ ストアから必要なプロパティ値を取得し、IPropertyStore::SetValue を呼び出してストリーム シンクのプロパティ ストアにコピーします。
次の表は、ビデオ ストリームのストリーム シンクで設定する必要があるエンコード後のプロパティ値を示しています。
エンコードの種類 | プロパティ名 (GetValue) | プロパティ名 (SetValue) |
---|---|---|
定数ビット レート エンコード | MFPKEY_BAVG MFPKEY_RAVG |
MFPKEY_STAT_BAVG MFPKEY_STAT_RAVG |
品質ベースの可変ビット レート エンコード | MFPKEY_BAVG MFPKEY_RAVG MFPKEY_BMAX MFPKEY_RMAX |
MFPKEY_STAT_BAVG MFPKEY_STAT_RAVG MFPKEY_STAT_BMAX MFPKEY_STAT_RMAX |
次のコード例では、エンコード後のプロパティ値を設定します。
//-------------------------------------------------------------------
// PostEncodingUpdate
// Updates the file sink with encoding properties set on the encoder
// during the encoding session.
//1. Get the output nodes
//2. For each node, get the downstream node
//3. For the downstream node, get the MFT
//4. Get the property store
//5. Get the required values
//6. Set them on the stream sink
//
// pTopology: A pointer to the full topology retrieved from the media session.
//-------------------------------------------------------------------
HRESULT PostEncodingUpdate(IMFTopology *pTopology)
{
if (!pTopology)
{
return E_INVALIDARG;
}
HRESULT hr = S_OK;
IMFCollection* pOutputColl = NULL;
IUnknown* pNodeUnk = NULL;
IMFMediaType* pType = NULL;
IMFTopologyNode* pNode = NULL;
IUnknown* pSinkUnk = NULL;
IMFStreamSink* pStreamSink = NULL;
IMFTopologyNode* pEncoderNode = NULL;
IUnknown* pEncoderUnk = NULL;
IMFTransform* pEncoder = NULL;
IPropertyStore* pStreamSinkProps = NULL;
IPropertyStore* pEncoderProps = NULL;
GUID guidMajorType = GUID_NULL;
PROPVARIANT var;
PropVariantInit( &var );
DWORD cElements = 0;
hr = pTopology->GetOutputNodeCollection( &pOutputColl);
if (FAILED(hr))
{
goto done;
}
hr = pOutputColl->GetElementCount(&cElements);
if (FAILED(hr))
{
goto done;
}
for(DWORD index = 0; index < cElements; index++)
{
hr = pOutputColl->GetElement(index, &pNodeUnk);
if (FAILED(hr))
{
goto done;
}
hr = pNodeUnk->QueryInterface(IID_IMFTopologyNode, (void**)&pNode);
if (FAILED(hr))
{
goto done;
}
hr = pNode->GetInputPrefType(0, &pType);
if (FAILED(hr))
{
goto done;
}
hr = pType->GetMajorType( &guidMajorType );
if (FAILED(hr))
{
goto done;
}
hr = pNode->GetObject(&pSinkUnk);
if (FAILED(hr))
{
goto done;
}
hr = pSinkUnk->QueryInterface(IID_IMFStreamSink, (void**)&pStreamSink);
if (FAILED(hr))
{
goto done;
}
hr = pNode->GetInput( 0, &pEncoderNode, NULL );
if (FAILED(hr))
{
goto done;
}
hr = pEncoderNode->GetObject(&pEncoderUnk);
if (FAILED(hr))
{
goto done;
}
hr = pEncoderUnk->QueryInterface(IID_IMFTransform, (void**)&pEncoder);
if (FAILED(hr))
{
goto done;
}
hr = pStreamSink->QueryInterface(IID_IPropertyStore, (void**)&pStreamSinkProps);
if (FAILED(hr))
{
goto done;
}
hr = pEncoder->QueryInterface(IID_IPropertyStore, (void**)&pEncoderProps);
if (FAILED(hr))
{
goto done;
}
if( guidMajorType == MFMediaType_Video )
{
hr = pEncoderProps->GetValue( MFPKEY_BAVG, &var );
if (FAILED(hr))
{
goto done;
}
hr = pStreamSinkProps->SetValue( MFPKEY_STAT_BAVG, var );
if (FAILED(hr))
{
goto done;
}
PropVariantClear( &var );
hr = pEncoderProps->GetValue( MFPKEY_RAVG, &var );
if (FAILED(hr))
{
goto done;
}
hr = pStreamSinkProps->SetValue( MFPKEY_STAT_RAVG, var);
if (FAILED(hr))
{
goto done;
}
PropVariantClear( &var );
hr = pEncoderProps->GetValue( MFPKEY_BMAX, &var );
if (FAILED(hr))
{
goto done;
}
hr = pStreamSinkProps->SetValue( MFPKEY_STAT_BMAX, var);
if (FAILED(hr))
{
goto done;
}
PropVariantClear( &var );
hr = pEncoderProps->GetValue( MFPKEY_RMAX, &var );
if (FAILED(hr))
{
goto done;
}
hr = pStreamSinkProps->SetValue( MFPKEY_STAT_RMAX, var);
if (FAILED(hr))
{
goto done;
}
}
else if( guidMajorType == MFMediaType_Audio )
{
hr = pEncoderProps->GetValue( MFPKEY_STAT_BAVG, &var );
if (FAILED(hr))
{
goto done;
}
hr = pStreamSinkProps->SetValue( MFPKEY_STAT_BAVG, var );
if (FAILED(hr))
{
goto done;
}
PropVariantClear( &var );
hr = pEncoderProps->GetValue( MFPKEY_STAT_RAVG, &var );
if (FAILED(hr))
{
goto done;
}
hr = pStreamSinkProps->SetValue( MFPKEY_STAT_RAVG, var );
if (FAILED(hr))
{
goto done;
}
PropVariantClear( &var );
hr = pEncoderProps->GetValue( MFPKEY_STAT_BMAX, &var);
if (FAILED(hr))
{
goto done;
}
hr = pStreamSinkProps->SetValue( MFPKEY_STAT_BMAX, var);
if (FAILED(hr))
{
goto done;
}
PropVariantClear( &var );
hr = pEncoderProps->GetValue( MFPKEY_STAT_RMAX, &var );
if (FAILED(hr))
{
goto done;
}
hr = pStreamSinkProps->SetValue( MFPKEY_STAT_RMAX, var );
if (FAILED(hr))
{
goto done;
}
PropVariantClear( &var );
hr = pEncoderProps->GetValue( MFPKEY_WMAENC_AVGBYTESPERSEC, &var );
if (FAILED(hr))
{
goto done;
}
hr = pStreamSinkProps->SetValue( MFPKEY_WMAENC_AVGBYTESPERSEC, var );
if (FAILED(hr))
{
goto done;
}
}
PropVariantClear( &var );
}
done:
SafeRelease (&pOutputColl);
SafeRelease (&pNodeUnk);
SafeRelease (&pType);
SafeRelease (&pNode);
SafeRelease (&pSinkUnk);
SafeRelease (&pStreamSink);
SafeRelease (&pEncoderNode);
SafeRelease (&pEncoderUnk);
SafeRelease (&pEncoder);
SafeRelease (&pStreamSinkProps);
SafeRelease (&pEncoderProps);
return hr;
}
main を実装する
次のコード例は、コンソール アプリケーションの main 関数を示します。
int wmain(int argc, wchar_t* argv[])
{
HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
if (argc != 4)
{
wprintf_s(L"Usage: %s inputaudio.mp3, %s output.wm*, %Encoding Type: CBR, VBR\n");
return 0;
}
HRESULT hr = S_OK;
IMFMediaSource* pSource = NULL;
IMFTopology* pTopology = NULL;
IMFActivate* pFileSinkActivate = NULL;
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (FAILED(hr))
{
goto done;
}
//Set the requested encoding mode
if (wcscmp(argv[3], L"CBR")==0)
{
EncodingMode = CBR;
}
else if (wcscmp(argv[3], L"VBR")==0)
{
EncodingMode = VBR;
}
else
{
EncodingMode = CBR;
}
// Start up Media Foundation platform.
hr = MFStartup(MF_VERSION);
if (FAILED(hr))
{
goto done;
}
//Create the media source
hr = CreateMediaSource(argv[1], &pSource);
if (FAILED(hr))
{
goto done;
}
//Create the file sink activate
hr = CreateMediaSink(argv[2], pSource, &pFileSinkActivate);
if (FAILED(hr))
{
goto done;
}
//Build the encoding topology.
hr = BuildPartialTopology(pSource, pFileSinkActivate, &pTopology);
if (FAILED(hr))
{
goto done;
}
//Instantiate the media session and start encoding
hr = Encode(pTopology);
if (FAILED(hr))
{
goto done;
}
done:
// Clean up.
SafeRelease(&pSource);
SafeRelease(&pTopology);
SafeRelease(&pFileSinkActivate);
MFShutdown();
CoUninitialize();
if (FAILED(hr))
{
wprintf_s(L"Could not create the output file due to 0x%X\n", hr);
}
return 0;
}
出力ファイルをテストする
次の一覧は、エンコードされたファイルをテストするためのチェック項目になります。 これらの値は、エンコードされたファイルを右クリックし、コンテキスト メニューから [プロパティ] を選ぶことで表示できる、ファイルのプロパティ ダイアログ ボックスで確認できます。
- エンコードされたファイルのパスが正しいことを確認します。
- ファイルのサイズは 0 KB より大きく、再生時間はソース ファイルの長さと一致することを確認します。
- ビデオ ストリームの場合は、フレームの幅と高さ、フレーム レートをチェックします。 これらの値は、「ASF プロファイル オブジェクトを作成する」セクションで説明されている手順で作成した ASF プロファイルで指定した値と一致する必要があります。
- オーディオ ストリームの場合は、ビット レートはターゲット メディアの種類で指定した値に近い値である必要があります。
- Windows Media Player でファイルを開き、エンコードの品質をチェックします。
- ASFViewer で ASF ファイルを開き、ASF ファイルの構造を確認します。 このツールは、この Microsoft Web サイトからダウンロードできます。
一般的なエラー コードとデバッグのヒント
次の一覧は、受け取る可能性がある一般的なエラー コードとデバッグのヒントについて説明になります。
IMFSourceResolver::CreateObjectFromURL を呼び出すと、アプリケーションがストールします。
メディア ファンデーション プラットフォームが MFStartup を呼び出して初期化されていることを確認します。 この関数は、IMFSourceResolver::CreateObjectFromURL など、非同期操作を内部で開始するすべてのメソッドで使用される非同期プラットフォームを設定します。
IMFSourceResolver::CreateObjectFromURL が HRESULT 0x80070002 "指定されたファイルが見つかりません" を返します。
最初の引数でユーザーが指定した入力ファイル名が存在することを確認します。
HRESULT 0x80070020 "ファイルは別のプロセスで使用されているため、このプロセスからアクセスすることはできません。 "
入力ファイルと出力ファイルが、システム内の別のリソースによって現在使用されていないことを確認します。
IMFTransform メソッドの呼び出しが MF_E_INVALIDMEDIATYPE を返します。
次の条件が満たされていることを確認してください。
- 指定した入力の種類または出力の種類は、エンコーダーがサポートするメディアの種類と互換性があります。
- 指定したメディアの種類は完全です。 メディアの種類を完全にするには、このチュートリアルの「圧縮オーディオ メディアの種類を作成する」および「圧縮ビデオ メディアの種類を作成する」セクションで必要な属性を参照してください。
- コーデックのプライベート データを追加する部分的なメディアの種類でターゲット ビット レートが設定されていることを確認します。
メディア セッションが、イベントの状態で MF_E_UNSUPPORTED_D3D_TYPE を返します。
このエラーは、ソースのメディアの種類が、Windows Media ビデオ エンコーダーでサポートされていない混合インターレース モードを示している場合に返されます。 圧縮ビデオ メディアの種類がプログレッシブ モードを使用するように設定されている場合は、パイプラインでインターレース解除変換を使用する必要があります。 また、このエラー コードが示すように一致するものがパイプラインで見つからないため、インターレース解除 (ビデオ プロセッサのトランスコード) をデコーダー ノードとエンコーダー ノードの間に手動で挿入する必要があります。
メディア セッションが、イベントの状態で E_INVALIDARG を返します。
このエラーは、ソースのメディアの種類の属性が、Windows Media エンコーダーに設定されている出力メディアの種類の属性と互換性がない場合に返されます。
IWMCodecPrivateData::GetPrivateData が、HRESULT 0x80040203 "クエリ文字列の評価中に構文エラーが発生しました" を返します。
エンコーダー MFT で入力の種類が設定されていることを確認します。
関連トピック