次の方法で共有


チュートリアル: WMContainer オブジェクトを使用した WMA ファイルの作成

このチュートリアルでは、圧縮されていないオーディオ ファイル (.wav) からメディア コンテンツを抽出し、ASF 形式で圧縮することで、新しいオーディオ ファイル (.wma) を作成する方法について説明します。 変換に使用されるエンコード モードは 、定数ビット レート エンコード (CBR) です。 このモードでは、エンコード セッションの前に、エンコーダーで実現する必要があるターゲット ビット レートがアプリケーションによって指定されます。

このチュートリアルでは、入力ファイル名と出力ファイル名を引数として受け取るコンソール アプリケーションを作成します。 アプリケーションは、このチュートリアルで提供されている wave ファイル解析アプリケーションから圧縮されていないメディア サンプルを取得します。 これらのサンプルは、Windows Media Audio 9 形式に変換するためにエンコーダーに送信されます。 エンコーダーは CBR エンコード用に構成され、メディア タイプ ネゴシエーション中に使用可能な最初のビット レートをターゲット ビット レートとして使用します。 エンコードされたサンプルは、ASF データ形式でパケット化のためにマルチプレクサに送信されます。 これらのパケットは、ASF データ オブジェクトを表すバイト ストリームに書き込まれます。 データ セクションの準備ができたら、ASF オーディオ ファイルを作成し、すべてのヘッダー情報を統合し、ASF データ オブジェクトのバイト ストリームを追加する新しい ASF ヘッダー オブジェクトを書き込みます。

このチュートリアルには、次のセクションが含まれています。

前提条件

このチュートリアルでは、次のことを前提としています。

  • ASF オブジェクトを操作するために Media Foundation によって提供される ASF ファイルの構造とコンポーネントに精通しています。 これらのコンポーネントには、ContentInfo、splitter、マルチプレクサー、プロファイル オブジェクトが含まれます。 詳細については、「 WMContainer ASF コンポーネント」を参照してください。
  • Windows Media エンコーダー、およびさまざまなエンコードの種類 (特に CBR) に精通しています。 詳細については、「 Windows Media Encoders 」を参照してください。
  • メディア バッファーとバイト ストリームについてよく知っています。具体的には、バイト ストリームを使用したファイル操作、およびメディア バッファーの内容をバイト ストリームに書き込みます。

用語

このチュートリアルでは、次の用語を使用します。

  • ソース メディアの種類: メディア型オブジェクトは、入力ファイルの内容を記述する IMFMediaType インターフェイスを公開します。
  • オーディオ プロファイル: プロファイル オブジェクトは、出力ファイルのオーディオ ストリームのみを含む IMFASFProfile インターフェイスを公開します。
  • ストリーム サンプル: メディア サンプルは、 IMFSample インターフェイスを公開し、圧縮状態のエンコーダーから取得した入力ファイルのメディア データを表します。
  • ContentInfo オブジェクト: ASF ContentInfo オブジェクトは、出力ファイルの ASF ヘッダー オブジェクトを表す IMFASFContentInfo インターフェイスを公開します。
  • データ バイト ストリーム: バイト ストリーム オブジェクトは、出力ファイルの ASF データ オブジェクト部分全体を表す IMFByteStream インターフェイスを公開します。
  • データ パケット: メディア サンプルは、ASF マルチプレクサーによって生成された IMFSample インターフェイスを公開します。は、データ バイト ストリームに書き込まれる ASF データ パケットを表します。
  • 出力バイト ストリーム: バイト ストリーム オブジェクトは、出力ファイルの内容を含む IMFByteStream インターフェイスを公開します。

1. プロジェクトを設定する

  1. ソース ファイルに次のヘッダーを含めます。

    #include <new>
    #include <stdio.h>       // Standard I/O
    #include <windows.h>     // Windows headers
    #include <mfapi.h>       // Media Foundation platform
    #include <wmcontainer.h> // ASF interfaces
    #include <mferror.h>     // Media Foundation error codes
    
  2. 次のライブラリ ファイルにリンクします。

    • mfplat.lib
    • mf.lib
    • mfuuid.lib
  3. SafeRelease 関数を宣言します。

  4. プロジェクトに CWmaEncoder クラスを含めます。 このクラスの完全なソース コードについては、「 Encoder Example Code」を参照してください。

2. ヘルパー関数を宣言する

このチュートリアルでは、次のヘルパー関数を使用して、バイト ストリームの読み取りと書き込みを行います。

  • AppendToByteStream: 1 つのバイト ストリームの内容を別のバイト ストリームに追加します。
  • WriteBufferToByteStream: メディア バッファーからバイト ストリームにデータを書き込みます。

詳細については、「 IMFByteStream::Write」を参照してください。 次のコードは、これらのヘルパー関数を示しています。

//-------------------------------------------------------------------
// AppendToByteStream
//
// Reads the contents of pSrc and writes them to pDest.
//-------------------------------------------------------------------

HRESULT AppendToByteStream(IMFByteStream *pSrc, IMFByteStream *pDest)
{
    HRESULT hr = S_OK;

    const DWORD READ_SIZE = 1024;

    BYTE buffer[READ_SIZE];

    while (1)
    {
        ULONG cbRead;

        hr = pSrc->Read(buffer, READ_SIZE, &cbRead);

        if (FAILED(hr)) { break; }

        if (cbRead == 0)
        {
            break;
        }

        hr = pDest->Write(buffer, cbRead, &cbRead);

        if (FAILED(hr)) { break; }
    }

    return hr;
}
//-------------------------------------------------------------------
// WriteBufferToByteStream
//
// Writes data from a media buffer to a byte stream.
//-------------------------------------------------------------------

HRESULT WriteBufferToByteStream(
    IMFByteStream *pStream,   // Pointer to the byte stream.
    IMFMediaBuffer *pBuffer,  // Pointer to the media buffer.
    DWORD *pcbWritten         // Receives the number of bytes written.
    )
{
    HRESULT hr = S_OK;
    DWORD cbData = 0;
    DWORD cbWritten = 0;
    BYTE *pMem = NULL;

    hr = pBuffer->Lock(&pMem, NULL, &cbData);

    if (SUCCEEDED(hr))
    {
        hr = pStream->Write(pMem, cbData, &cbWritten);
    }

    if (SUCCEEDED(hr))
    {
        if (pcbWritten)
        {
            *pcbWritten = cbWritten;
        }
    }

    if (pMem)
    {
        pBuffer->Unlock();
    }
    return hr;
}

3. オーディオ ファイルを開く

このチュートリアルでは、アプリケーションでエンコード用に圧縮されていないオーディオを生成することを前提としています。 そのため、このチュートリアルでは、次の 2 つの関数が宣言されています。

HRESULT OpenAudioFile(PCWSTR pszURL, IMFMediaType **ppAudioFormat);
HRESULT GetNextAudioSample(BOOL *pbEOS, IMFSample **ppSample);

これらの関数の実装は、リーダーに任されています。

  • 関数はOpenAudioFile、pszURL で指定されたメディア ファイルを開き、オーディオ ストリームを記述するメディアの種類へのポインターを返す必要があります。
  • 関数は GetNextAudioSample 、 によって OpenAudioFile開かれたファイルから圧縮されていない PCM オーディオを読み取る必要があります。 ファイルの末尾に達すると、 pbEOS は値 TRUE を受け取ります。 それ以外の場合、 ppSample はオーディオ バッファーを含むメディア サンプルを受け取ります。

4. エンコーダーを構成する

次に、エンコーダーを作成し、CBR でエンコードされたストリーム サンプルを生成するように構成し、入力と出力メディアの種類をネゴシエートします。

Media Foundation では、エンコーダー ( IMFTransform インターフェイスを公開) は Media Foundation Transforms (MFT) として実装されます。

このチュートリアルでは、MFT のラッパーを CWmaEncoder 提供する クラスにエンコーダーを実装します。 このクラスの完全なソース コードについては、「 Encoder Example Code」を参照してください。

注意

必要に応じて、エンコードの種類を CBR として指定できます。 既定では、エンコーダーは CBR エンコードを使用するように構成されています。 詳細については、「 定数ビット レート エンコード」を参照してください。 エンコード の種類に応じて追加のプロパティを設定できます。エンコード モードに固有のプロパティの詳細については、「 品質ベースの可変ビット レート エンコード」、「 制約のない変数ビット レート エンコード」、「 ピーク制限付き変数ビット レート エンコード」を参照してください。

 

    CWmaEncoder* pEncoder = NULL; //Pointer to the Encoder object.

    hr = OpenAudioFile(sInputFileName, &pInputType);
    if (FAILED(hr))
    {
        goto done;
    }

    // Initialize the WMA encoder wrapper.

    pEncoder = new (std::nothrow) CWmaEncoder();
    if (pEncoder == NULL)
    {
        hr = E_OUTOFMEMORY;
        goto done;
    }

    hr = pEncoder->Initialize();
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pEncoder->SetEncodingType(EncodeMode_CBR);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pEncoder->SetInputType(pInputType);
    if (FAILED(hr))
    {
        goto done;
    }

5. ASF ContentInfo オブジェクトを作成します。

ASF ContentInfo オブジェクトには、出力ファイルのさまざまなヘッダー オブジェクトに関する情報が含まれています。

まず、オーディオ ストリームの ASF プロファイルを作成します。

  1. 空の ASF プロファイル オブジェクトを作成するには、 MFCreateASFProfile を呼び出します。 ASF プロファイルは、 IMFASFProfile インターフェイスを 公開します。 詳細については、「 ASF ストリームの作成と構成」を参照してください。
  2. オブジェクトからエンコードされたオーディオ形式を CWmaEncoder 取得します。
  3. IMFASFProfile::CreateStream を呼び出して、ASF プロファイルの新しいストリームを作成します。 ストリーム形式を表す IMFMediaType インターフェイスへのポインターを渡します。
  4. IMFASFStreamConfig::SetStreamNumber を呼び出して、ストリーム識別子を割り当てます。
  5. ストリーム オブジェクトに MF_ASFSTREAMCONFIG_LEAKYBUCKET1 属性を設定して、"漏れバケット" パラメーターを設定します。
  6. IMFASFProfile::SetStream を呼び出して、新しいストリームをプロファイルに追加します。

次に、次のように ASF ContentInfo オブジェクトを作成します。

  1. 空の ContentInfo オブジェクトを作成するには、MFCreateASFContentInfo を呼び出します。
  2. IMFASFContentInfo::SetProfile を呼び出して ASF プロファイルを設定します。

これらの手順を示すコードは次のようになります。

HRESULT CreateASFContentInfo(
    CWmaEncoder* pEncoder,
    IMFASFContentInfo** ppContentInfo
    )
{
    HRESULT hr = S_OK;
    
    IMFASFProfile* pProfile = NULL;
    IMFMediaType* pMediaType = NULL;
    IMFASFStreamConfig* pStream = NULL;
    IMFASFContentInfo* pContentInfo = NULL;

    // Create the ASF profile object.

    hr = MFCreateASFProfile(&pProfile); 
    if (FAILED(hr))
    {
        goto done;
    }

    // Create a stream description for the encoded audio.

    hr = pEncoder->GetOutputType(&pMediaType); 
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pProfile->CreateStream(pMediaType, &pStream); 
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pStream->SetStreamNumber(DEFAULT_STREAM_NUMBER); 
    if (FAILED(hr))
    {
        goto done;
    }

    // Set "leaky bucket" values.

    LeakyBucket bucket;

    hr = pEncoder->GetLeakyBucket1(&bucket);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pStream->SetBlob(
        MF_ASFSTREAMCONFIG_LEAKYBUCKET1, 
        (UINT8*)&bucket, 
        sizeof(bucket)
        );

    if (FAILED(hr))
    {
        goto done;
    }

    //Add the stream to the profile

    hr = pProfile->SetStream(pStream);
    if (FAILED(hr))
    {
        goto done;
    }

    // Create the ASF ContentInfo object.

    hr = MFCreateASFContentInfo(&pContentInfo); 
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pContentInfo->SetProfile(pProfile); 
    if (FAILED(hr))
    {
        goto done;
    }

    // Return the pointer to the caller.

    *ppContentInfo = pContentInfo;
    (*ppContentInfo)->AddRef();

done:
    SafeRelease(&pProfile);
    SafeRelease(&pStream);
    SafeRelease(&pMediaType);
    SafeRelease(&pContentInfo);
    return hr;
}

6. ASF マルチプレクサーを作成する

ASF マルチプレクサーは ASF データ パケットを生成します。

  1. MFCreateASFMultiplexer を呼び出して、ASF マルチプレクサーを作成します。
  2. IMFASFMultiplexer::Initialize を呼び出して、マルチプレクサーを初期化します。 前のセクションで作成した ASF コンテンツ情報オブジェクトへのポインターを渡します。
  3. IMFASFMultiplexer::SetFlags を呼び出して、MFASF_MULTIPLEXER_AUTOADJUST_BITRATE フラグを設定します。 この設定を使用すると、マルチプレクサは、多重化されるストリームの特性に合わせて ASF コンテンツのビット レートを自動的に調整します。
HRESULT CreateASFMux( 
    IMFASFContentInfo* pContentInfo,
    IMFASFMultiplexer** ppMultiplexer
    )
{
    HRESULT hr = S_OK;
    
    IMFMediaType* pMediaType = NULL;
    IMFASFMultiplexer *pMultiplexer = NULL;

    // Create and initialize the ASF Multiplexer object.

    hr = MFCreateASFMultiplexer(&pMultiplexer);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pMultiplexer->Initialize(pContentInfo);
    if (FAILED(hr))
    {
        goto done;
    }

    // Enable automatic bit-rate adjustment.

    hr = pMultiplexer->SetFlags(MFASF_MULTIPLEXER_AUTOADJUST_BITRATE);
    if (FAILED(hr))
    {
        goto done;
    }

    *ppMultiplexer = pMultiplexer;
    (*ppMultiplexer)->AddRef();

done:
    SafeRelease(&pMultiplexer);
    return hr;
}

7. 新しい ASF データ パケットを生成する

次に、新しいファイルの ASF データ パケットを生成します。 これらのデータ パケットは、新しいファイルの最終的な ASF データ オブジェクトを構成します。 新しい ASF データ パケットを生成するには:

  1. MFCreateTempFile を呼び出して、ASF データ パケットを保持する一時バイト ストリームを作成します。
  2. アプリケーション定義 GetNextAudioSample 関数を呼び出して、エンコーダーの非圧縮オーディオ データを取得します。
  3. 圧縮されていないオーディオをエンコーダーに渡して圧縮します。 詳細については、「エンコーダーでのデータの処理」を参照してください。
  4. IMFASFMultiplexer::P rocessSample を呼び出して、圧縮されたオーディオ サンプルをパケット化のために ASF マルチプレクサーに送信します。
  5. マルチプレクサーから ASF パケットを取得し、一時バイト ストリームに書き込みます。 詳細については、「 新しい ASF データ パケットの生成」を参照してください。
  6. ソース ストリームの最後に達したら、エンコーダーをドレインし、エンコーダーから残りの圧縮サンプルをプルします。 MFT のドレインの詳細については、「 基本的な MFT 処理モデル」を参照してください。
  7. すべてのサンプルがマルチプレクサーに送信されたら、 IMFASFMultiplexer::Flush を呼び出し、残りの ASF パケットをマルチプレクサーからプルします。
  8. IMFASFMultiplexer::End を呼び出します。

次のコードでは、ASF データ パケットが生成されます。 関数は、ASF データ オブジェクトを含むバイト ストリームへのポインターを返します。

HRESULT EncodeData(
    CWmaEncoder* pEncoder, 
    IMFASFContentInfo* pContentInfo,
    IMFASFMultiplexer* pMux, 
    IMFByteStream** ppDataStream) 
{
    HRESULT hr = S_OK;

    IMFByteStream* pStream = NULL;
    IMFSample* pInputSample = NULL;
    IMFSample* pWmaSample = NULL;

    BOOL bEOF = FALSE;

   // Create a temporary file to hold the data stream.
   hr = MFCreateTempFile(
        MF_ACCESSMODE_READWRITE, 
        MF_OPENMODE_DELETE_IF_EXIST,
        MF_FILEFLAGS_NONE,
        &pStream
        );

   if (FAILED(hr))
   {
       goto done;
   }

    BOOL bNeedInput = TRUE;

    while (TRUE)
    {
        if (bNeedInput)
        {
            hr = GetNextAudioSample(&bEOF, &pInputSample);
            if (FAILED(hr))
            {
                goto done;
            }

            if (bEOF)
            {
                // Reached the end of the input file.
                break;
            }

            // Encode the uncompressed audio sample.
            hr = pEncoder->ProcessInput(pInputSample);
            if (FAILED(hr))
            {
                goto done;
            }

            bNeedInput = FALSE;
        }

        if (bNeedInput == FALSE)
        {
            // Get data from the encoder.

            hr = pEncoder->ProcessOutput(&pWmaSample);
            if (FAILED(hr))
            {
                goto done;
            }

            // pWmaSample can be NULL if the encoder needs more input.

            if (pWmaSample)
            {
                hr = pMux->ProcessSample(DEFAULT_STREAM_NUMBER, pWmaSample, 0);
                if (FAILED(hr))
                {
                    goto done;
                }

                //Collect the data packets and write them to a stream
                hr = GenerateASFDataPackets(pMux, pStream);
                if (FAILED(hr))
                {
                    goto done;
                }
            }
            else
            {
                bNeedInput = TRUE;
            }
        }
        
        SafeRelease(&pInputSample);
        SafeRelease(&pWmaSample);
    }

    // Drain the MFT and pull any remaining samples from the encoder.

    hr = pEncoder->Drain();
    if (FAILED(hr))
    {
        goto done;
    }

    while (TRUE)
    {
        hr = pEncoder->ProcessOutput(&pWmaSample);
        if (FAILED(hr))
        {
            goto done;
        }

        if (pWmaSample == NULL)
        {
            break;
        }

        hr = pMux->ProcessSample(DEFAULT_STREAM_NUMBER, pWmaSample, 0);
        if (FAILED(hr))
        {
            goto done;
        }

        //Collect the data packets and write them to a stream
        hr = GenerateASFDataPackets(pMux, pStream);
        if (FAILED(hr))
        {
            goto done;
        }

        SafeRelease(&pWmaSample);
    }

    // Flush the mux and get any pending ASF data packets.
    hr = pMux->Flush();
    if (FAILED(hr))
    {
        goto done;
    }

    hr = GenerateASFDataPackets(pMux, pStream);
    if (FAILED(hr))
    {
        goto done;
    }
    
    // Update the ContentInfo object
    hr = pMux->End(pContentInfo);
    if (FAILED(hr))
    {
        goto done;
    }

    //Return stream to the caller that contains the ASF encoded data.
    *ppDataStream = pStream;
    (*ppDataStream)->AddRef();

done:
    SafeRelease(&pStream);
    SafeRelease(&pInputSample);
    SafeRelease(&pWmaSample);
    return hr;
}

関数の GenerateASFDataPackets コードについては、 トピック「新しい ASF データ パケットの生成」を参照してください。

8. ASF ファイルを書き込む

次に、 IMFASFContentInfo::GenerateHeader を呼び出して、ASF ヘッダーをメディア バッファーに書き込みます。 このメソッドは、ContentInfo オブジェクトに格納されているデータを ASF ヘッダー オブジェクト形式のバイナリ データに変換します。 詳細については、「 新しい ASF ヘッダー オブジェクトの生成」を参照してください。

新しい ASF ヘッダー オブジェクトが生成されたら、出力ファイルのバイト ストリームを作成します。 まず、Header オブジェクトを出力バイト ストリームに書き込みます。 データ バイト ストリームに含まれる Data オブジェクトを使用して、Header オブジェクトに従います。

HRESULT WriteASFFile( 
     IMFASFContentInfo *pContentInfo, 
     IMFByteStream *pDataStream,
     PCWSTR pszFile
     )
{
    HRESULT hr = S_OK;
    
    IMFMediaBuffer* pHeaderBuffer = NULL;
    IMFByteStream* pWmaStream = NULL;

    DWORD cbHeaderSize = 0;
    DWORD cbWritten = 0;

    //Create output file
    hr = MFCreateFile(MF_ACCESSMODE_WRITE, MF_OPENMODE_DELETE_IF_EXIST,
        MF_FILEFLAGS_NONE, pszFile, &pWmaStream);

    if (FAILED(hr))
    {
        goto done;
    }


    // Get the size of the ASF Header Object.
    hr = pContentInfo->GenerateHeader (NULL, &cbHeaderSize);
    if (FAILED(hr))
    {
        goto done;
    }

    // Create a media buffer.
    hr = MFCreateMemoryBuffer(cbHeaderSize, &pHeaderBuffer);
    if (FAILED(hr))
    {
        goto done;
    }

    // Populate the media buffer with the ASF Header Object.
    hr = pContentInfo->GenerateHeader(pHeaderBuffer, &cbHeaderSize);
    if (FAILED(hr))
    {
        goto done;
    }

    // Write the ASF header to the output file.
    hr = WriteBufferToByteStream(pWmaStream, pHeaderBuffer, &cbWritten);
    if (FAILED(hr))
    {
        goto done;
    }

    // Append the data stream to the file.

    hr = pDataStream->SetCurrentPosition(0);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = AppendToByteStream(pDataStream, pWmaStream);

done:
    SafeRelease(&pHeaderBuffer);
    SafeRelease(&pWmaStream);
    return hr;
}

9. Entry-Point関数を定義する

これで、前の手順をまとめて完全なアプリケーションに配置できます。 Media Foundation オブジェクトのいずれかを使用する前に、 MFStartup を呼び出して Media Foundation プラットフォームを初期化します。 完了したら、 MFShutdown を呼び出します。 詳細については、「 Media Foundation の初期化」を参照してください。

次のコードは、コンソール アプリケーション全体を示しています。 コマンド ライン引数は、変換するファイルの名前と新しいオーディオ ファイルの名前を指定します。

int wmain(int argc, WCHAR* argv[])
{
    HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);

    if (argc != 3)
    {
        wprintf_s(L"Usage: %s input.wmv, %s output.wma");
        return 0;
    }

    const WCHAR* sInputFileName = argv[1];    // Source file name
    const WCHAR* sOutputFileName = argv[2];  // Output file name
    
    IMFMediaType* pInputType = NULL;
    IMFASFContentInfo* pContentInfo = NULL;
    IMFASFMultiplexer* pMux = NULL;
    IMFByteStream* pDataStream = NULL;

    CWmaEncoder* pEncoder = NULL; //Pointer to the Encoder object.

    HRESULT hr = CoInitializeEx(
        NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);

    if (FAILED(hr))
    {
        goto done;
    }

    hr = MFStartup(MF_VERSION);
    if (FAILED(hr))
    {
        goto done;
    }

    CWmaEncoder* pEncoder = NULL; //Pointer to the Encoder object.

    hr = OpenAudioFile(sInputFileName, &pInputType);
    if (FAILED(hr))
    {
        goto done;
    }

    // Initialize the WMA encoder wrapper.

    pEncoder = new (std::nothrow) CWmaEncoder();
    if (pEncoder == NULL)
    {
        hr = E_OUTOFMEMORY;
        goto done;
    }

    hr = pEncoder->Initialize();
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pEncoder->SetEncodingType(EncodeMode_CBR);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pEncoder->SetInputType(pInputType);
    if (FAILED(hr))
    {
        goto done;
    }

    // Create the WMContainer objects.
    hr = CreateASFContentInfo(pEncoder, &pContentInfo);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = CreateASFMux(pContentInfo, &pMux);
    if (FAILED(hr))
    {
        goto done;
    }

    // Convert uncompressed data to ASF format.
    hr = EncodeData(pEncoder, pContentInfo, pMux, &pDataStream);
    if (FAILED(hr))
    {
        goto done;
    }

    // Write the ASF objects to the output file.
    hr = WriteASFFile(pContentInfo, pDataStream, sOutputFileName);

done:
    SafeRelease(&pInputType);
    SafeRelease(&pContentInfo);
    SafeRelease(&pMux);
    SafeRelease(&pDataStream);
    delete pEncoder;

    MFShutdown();
    CoUninitialize();

    if (FAILED(hr))
    {
        wprintf_s(L"Error: 0x%X\n", hr);
    }

    return 0;
} 

WMContainer ASF コンポーネント

Media Foundation での ASF サポート