Date

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 を取得する。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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 に設定すれば完了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
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 を実行すれば エンコード処理が始まる。

https://github.com/mahorigahama/mf_fasttranscode


Comments

comments powered by Disqus