Windows 7 で強化された機能、 Fast Transcode を使って WAV から MPEG-4 audio にエンコードする処理を実装してみよう。今回作成したサンプルアプリケーションのクラス図を示す。
Fast Transcode はあるフォーマットから別のフォーマットへ変換する機能
そもそも「Fast Transcode」とは何かというと、あるフォーマットから別のフォーマットへ変換する Media Foundation の一つの機能です。DirectShow や Media Session と Topology を用いてもフォーマット変換はできるが、入出力ファイルに合わせてデコーダ、エンコーダ、メディアタイプの設定、Topology あるいは フィルタグラフの構築…と煩雑な手続きを踏まなければならなかった。Fast Transcode を用いるとメディアタイプを設定すれば必要なデコーダ、エンコーダを自動的に選択し、Toplogy 構築まで行ってくれる。またハードウェアエンコーダやデコーダがあれば、それも利用できる。
Media Session の初期化と Media Source の作成
Fast Transcode は Media Session を完全に置き換えるものではなく、Topology 構築のヘルパーオブジェクトとして働く。はじめに Media Session の初期化と Media Source を作成する。それから Media Source から Presentation Descriptor, Stream Descriptor を取得する。
LRESULT CMyWindow::OnCreate(UINT, WPARAM, LPARAM, BOOL&)
{
ATLASSERT(m_MediaSession == NULL);
CHResult hr;
try {
CComPtr<IMFTopology> topology;
hr = MFCreateMediaSession(NULL, &m_MediaSession);
hr = m_MediaSession->BeginGetEvent(this, NULL);
CreateMediaSrc();
CComPtr<IMFPresentationDescriptor> pres_desc;
CComPtr<IMFStreamDescriptor> stream_desc;
DWORD stream_count;
BOOL selected;
hr = m_Source->CreatePresentationDescriptor(&pres_desc);
hr = pres_desc->GetStreamDescriptorCount(&stream_count);
ATLASSERT(stream_count == 1);
hr = pres_desc->GetStreamDescriptorByIndex(0, &selected, &stream_desc);
ATLASSERT(selected == TRUE);
ConfigureOutput(stream_desc, topology);
hr = m_MediaSession->SetTopology(0, topology);
}
catch (CAtlException &) {
DestroyWindow();
}
return 0;
}
HRESULT CMyWindow::CreateMediaSrc() {
CHResult hr;
MF_OBJECT_TYPE object_type;
CComPtr<IMFSourceResolver> src_resolver;
hr = MFCreateSourceResolver(&src_resolver);
hr = src_resolver->CreateObjectFromURL(
INPUT_FILE_NAME,
MF_RESOLUTION_MEDIASOURCE,
NULL,
&object_type,
(IUnknown**)&m_Source);
ATLASSERT(object_type == MF_OBJECT_MEDIASOURCE);
return hr;
}
Fast Transcode を使って Topology を構築する
作成した Media Source を入力とし、 Fast Transcode を使って Topology の残りの部分を構築する。
まず、Media Source のメディアタイプを確認する。今回は WAV ファイルを扱うので WAVEFORMATEX 構造体で取得する。ここで取得したメディアタイプは、エンコード結果が入力ファイルと同じ周波数、チャンネル数にするために必要となる。
次に MFCreateTranscodeProfile を呼んで Transcode Profile オブジェクトを作成する。このオブジェクトが Fast Transcode の肝となる部分である。MFTranscodeGetAudioOutputAvailableTypes を呼んで利用可能な出力オーディオタイプを取得する。その中に、AAC 圧縮、かつ、先ほど取得したメディアタイプと同じ周波数、チャンネル数のものがあるかどうか調べる。もしあれば、それを出力メディアタイプとする。出力メディアタイプが決まったら、コンテナ形式を設定する。コンテナは属性オブジェクトを渡すことにより設定する。ここでは MPEG-4 Audio にしたいので MF_TRANSCODE_CONTAINERTYPE 属性の値が MFTranscodeContainerType_MPEG4 となる属性オブジェクトを作成し、Transcode Profile に IMFTranscodeProfile::SetContainerAttributes で渡す。
最後に MFCreateTranscodeTopology に Media Source と Transcode Profile を渡すと Topology を自動的に構築してくれるので、それを Media Session に設定すれば完了。
HRESULT CMyWindow::ConfigureOutput(CComPtr<IMFStreamDescriptor> stream_desc,
CComPtr<IMFTopology> &topology) {
CHResult hr;
CComPtr<IMFMediaType> in_mfmt;
CComPtr<IMFMediaTypeHandler> mt_handler;
GUID major_type_guid = GUID_NULL;
GUID subtype;
DWORD mt_count;
hr = stream_desc->GetMediaTypeHandler(&mt_handler);
hr = mt_handler->GetMajorType(&major_type_guid);
ATLASSERT(major_type_guid == MFMediaType_Audio);
hr = mt_handler->GetMediaTypeCount(&mt_count);
ATLASSERT(mt_count == 1);
hr = mt_handler->GetMediaTypeByIndex(0, &in_mfmt);
hr = in_mfmt->GetGUID(MF_MT_SUBTYPE, &subtype);
WAVEFORMATEX *in_wfx;
UINT32 wfx_size;
MFCreateWaveFormatExFromMFMediaType(in_mfmt, &in_wfx, &wfx_size);
const WORD in_ch = in_wfx->nChannels;
const WORD in_freq = in_wfx->wBitsPerSample;
ATLASSERT(in_wfx->wFormatTag == WAVE_FORMAT_PCM);
ATLTRACE(_T("Input Media Type\n"));
TraceWavFormatEx(in_wfx);
CoTaskMemFree(in_wfx);
CComPtr<IMFTranscodeProfile> x_prof;
CComPtr<IMFCollection> available_types;
DWORD dwFlags = MFT_ENUM_FLAG_ALL & (~MFT_ENUM_FLAG_FIELDOFUSE);
hr = MFCreateTranscodeProfile(&x_prof);
hr = MFTranscodeGetAudioOutputAvailableTypes(
MFAudioFormat_AAC,
dwFlags | MFT_ENUM_FLAG_SORTANDFILTER,
NULL,
&available_types);
hr = available_types->GetElementCount(&mt_count);
CComPtr<IMFAttributes> attr;
CComPtr<IMFAttributes> attr_container;
for (DWORD index = 0;index < mt_count;index++) {
CComQIPtr<IMFMediaType> out_mfmt;
hr = available_types->GetElement(index, (IUnknown**)&out_mfmt);
WAVEFORMATEX *out_wfx;
hr = MFCreateWaveFormatExFromMFMediaType(out_mfmt, &out_wfx, &wfx_size);
const WORD out_ch = out_wfx->nChannels;
const WORD out_freq = out_wfx->wBitsPerSample;
TraceWavFormatEx(out_wfx);
CoTaskMemFree(out_wfx);
if (out_ch == in_ch && out_freq == out_freq) {
hr = MFCreateAttributes(&attr, 0);
hr = out_mfmt->CopyAllItems(attr);
break;
}
}
hr = x_prof->SetAudioAttributes(attr);
hr = MFCreateAttributes(&attr_container, 1);
hr = attr_container->SetGUID(
MF_TRANSCODE_CONTAINERTYPE,
MFTranscodeContainerType_MPEG4);
hr = x_prof->SetContainerAttributes(attr_container);
hr = MFCreateTranscodeTopology(
m_Source,
OUTPUT_FILE_NAME,
x_prof,
&topology);
return hr;
}
Topology を実行すれば エンコード処理が始まる。
