チュートリアル: WMContainer オブジェクトを使用した ASF ストリームのコピー

ASF ファイルを作成する方法の 1 つは、既存のファイルから ASF ストリームをコピーすることです。 これを行うには、ソース ファイルからメディア データを取得し、出力ファイルに書き込むことができます。 ソース ファイルが ASF ファイルの場合は、圧縮解除と再圧縮を行わずにストリーム サンプルをコピーできます。

このチュートリアルでは、ASF オーディオ ビデオ ファイル (.wmv) から最初のオーディオ ストリームを抽出し、それを新しい ASF オーディオ ファイル (.wma) にコピーすることで、このシナリオを示します。 このチュートリアルでは、入力ファイル名と出力ファイル名を引数として受け取るコンソール アプリケーションを作成します。 アプリケーションは ASF スプリッターを使用して入力ストリーム サンプルを解析し、ASF マルチプレクサーに送信してオーディオ ストリームの ASF データ パケットを書き込みます。

このチュートリアルで実行する手順は次のとおりです。

前提条件

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

  • ASF オブジェクトを操作するために Media Foundation によって提供される ASF ファイルの構造とコンポーネントに精通しています。 これらのコンポーネントには、ContentInfo、splitter、マルチプレクサー、プロファイル オブジェクトが含まれます。 詳細については、「 WMContainer ASF コンポーネント」を参照してください。
  • 既存のファイルの ASF ヘッダー オブジェクトと ASF データ パケットを解析し、スプリッターを使用して圧縮ストリーム サンプルを生成するプロセスについて理解しています。 詳細については、「 チュートリアル: ASF ファイルの読み取り」を参照してください。
  • メディア バッファーとバイト ストリームについてよく知っています。具体的には、バイト ストリームを使用したファイル操作と、メディア バッファーの内容をバイト ストリームに書き込む操作です。 ( 2 を参照してください。ヘルパー関数を宣言します。)

用語

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

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

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

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

#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

次のライブラリ ファイルにリンクします。

  • mfplat.lib
  • mf.lib
  • mfuuid.lib

SafeRelease 関数を宣言します。

template <class T> void SafeRelease(T **ppT)
{
    if (*ppT)
    {
        (*ppT)->Release();
        *ppT = NULL;
    }
}

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

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

関数は AllocReadFromByteStream バイト ストリームからデータを読み取り、データを保持する新しいメディア バッファーを割り当てます。 詳細については、「 IMFByteStream::Read」を参照してください。

//-------------------------------------------------------------------
// AllocReadFromByteStream
//
// Reads data from a byte stream and returns a media buffer that
// contains the data.
//-------------------------------------------------------------------

HRESULT AllocReadFromByteStream(
    IMFByteStream *pStream,         // Pointer to the byte stream.
    DWORD cbToRead,                 // Number of bytes to read.
    IMFMediaBuffer **ppBuffer       // Receives a pointer to the media buffer. 
    )
{
    HRESULT hr = S_OK;
    BYTE *pData = NULL;
    DWORD cbRead = 0;   // Actual amount of data read.

    IMFMediaBuffer *pBuffer = NULL;

    // Create the media buffer. 
    // This function allocates the memory for the buffer.
    hr = MFCreateMemoryBuffer(cbToRead, &pBuffer);

    // Get a pointer to the memory buffer.
    if (SUCCEEDED(hr))
    {
        hr = pBuffer->Lock(&pData, NULL, NULL);
    }

    // Read the data from the byte stream.
    if (SUCCEEDED(hr))
    {
        hr = pStream->Read(pData, cbToRead, &cbRead);
    }

    // Update the size of the valid data in the buffer.
    if (SUCCEEDED(hr))
    {
        hr = pBuffer->SetCurrentLength(cbRead);
    }

    // Return the pointer to the caller.
    if (SUCCEEDED(hr))
    {
        *ppBuffer = pBuffer;
        (*ppBuffer)->AddRef();
    }

    if (pData)
    {
        pBuffer->Unlock();
    }
    SafeRelease(&pBuffer);
    return hr;
}

関数は WriteBufferToByteStream 、メディア バッファーからバイト ストリームにデータを書き込みます。 詳細については、「 IMFByteStream::Write」を参照してください。

//-------------------------------------------------------------------
// 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;
}

関数は AppendToByteStream 、1 つのバイト ストリームの内容を別のバイト ストリームに追加します。

//-------------------------------------------------------------------
// 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;
}

3. 入力 ASF ファイルを開く

MFCreateFile 関数を呼び出して、入力ファイルを開きます。 メソッドは、ファイルの内容を含むバイト ストリーム オブジェクトへのポインターを返します。 ファイル名は、アプリケーションのコマンド ライン引数を使用してユーザーによって指定されます。

次のコード例では、ファイル名を取得し、ファイルの読み取りに使用できるバイト ストリーム オブジェクトへのポインターを返します。

        // Open the file.
        hr = MFCreateFile(MF_ACCESSMODE_READ, MF_OPENMODE_FAIL_IF_NOT_EXIST, 
            MF_FILEFLAGS_NONE, pszFileName, &pStream);

4. 入力ファイルのオブジェクトを初期化する

次に、ソース ContentInfo オブジェクトとストリーム サンプルを生成するためのスプリッターを作成して初期化します。

手順 2 で作成したこのソース バイト ストリームは、ASF ヘッダー オブジェクトを解析し、ソース ContentInfo オブジェクトを設定するために使用されます。 このオブジェクトは、入力ファイル内のオーディオ ストリームの ASF データ パケットの解析を容易にするために、スプリッターを初期化するために使用されます。 また、入力ファイル内の ASF データ オブジェクトの長さと、ファイルの先頭からの最初の ASF データ パケットへのオフセットも取得します。 これらの属性は、オーディオ ストリームサンプルを生成するためにスプリッターによって使用されます。

入力ファイルの ASF コンポーネントを作成して初期化するには、

  1. MFCreateASFContentInfo を呼び出して ContentInfo オブジェクトを作成します。 この関数は、 IMFASFContentInfo インターフェイスへのポインターを返します。
  2. IMFASFContentInfo::P arseHeader を呼び出して、ASF ヘッダーを解析します。 この手順の詳細については、「 既存のファイルの ASF ヘッダー オブジェクトの読み取り」を参照してください。
  3. MFCreateASFSplitter を呼び出して、ASF スプリッター オブジェクトを作成します。 この関数は、 IMFASFSplitter インターフェイスへのポインターを返します。
  4. IMFASFSplitter::Initialize を呼び出し、IMFASFContentInfoポインターを渡します。 この手順の詳細については、「 ASF Splitter オブジェクトの作成」を参照してください。
  5. IMFASFContentInfo::GeneratePresentationDescriptor を呼び出して、ASF ファイルのプレゼンテーション記述子を取得します。
  6. プレゼンテーション記述子から MF_PD_ASF_DATA_START_OFFSET 属性の値を取得します。 この値は、ファイルの先頭からのバイト オフセットとして、ファイル内の ASF データ オブジェクトの場所です。
  7. プレゼンテーション記述子から MF_PD_ASF_DATA_LENGTH 属性の値を取得します。 この値は、ASF データ オブジェクトの合計サイズ (バイト単位) です。 詳細については、「 ASF ヘッダー オブジェクトからの情報の取得」を参照してください。

次のコード例は、すべての手順を統合する関数を示しています。 この関数は、ソース バイト ストリームへのポインターを受け取り、ソース ContentInfo オブジェクトとスプリッターへのポインターを返します。 また、ASF データ オブジェクトの長さとオフセットを受け取ります。

//-------------------------------------------------------------------
// CreateSourceParsers
//
// Creates the ASF splitter and the ASF Content Info object for the 
// source file.
// 
// This function also calulates the offset and length of the ASF 
// Data Object.
//-------------------------------------------------------------------

HRESULT CreateSourceParsers(
    IMFByteStream *pSourceStream,
    IMFASFContentInfo **ppSourceContentInfo,
    IMFASFSplitter **ppSplitter,
    UINT64 *pcbDataOffset,
    UINT64 *pcbDataLength
    )
{
    const DWORD MIN_ASF_HEADER_SIZE = 30;

    IMFMediaBuffer *pBuffer = NULL;
    IMFPresentationDescriptor *pPD = NULL;
    IMFASFContentInfo *pSourceContentInfo = NULL;
    IMFASFSplitter *pSplitter = NULL;

    QWORD cbHeader = 0;

    /*------- Parse the ASF header. -------*/

    // Create the ASF ContentInfo object.
    HRESULT hr = MFCreateASFContentInfo(&pSourceContentInfo);
    
    // Read the first 30 bytes to find the total header size.
    if (SUCCEEDED(hr))
    {
        hr = AllocReadFromByteStream(
            pSourceStream, 
            MIN_ASF_HEADER_SIZE, 
            &pBuffer
            );
    }

    // Get the header size.
    if (SUCCEEDED(hr))
    {
        hr = pSourceContentInfo->GetHeaderSize(pBuffer, &cbHeader);
    }

    // Release the buffer; we will reuse it.
    SafeRelease(&pBuffer);
    
    // Read the entire header into a buffer.
    if (SUCCEEDED(hr))
    {
        hr = pSourceStream->SetCurrentPosition(0);
    }

    if (SUCCEEDED(hr))
    {
        hr = AllocReadFromByteStream(
            pSourceStream, 
            (DWORD)cbHeader, 
            &pBuffer
            );
    }

    // Parse the buffer and populate the header object.
    if (SUCCEEDED(hr))
    {
        hr = pSourceContentInfo->ParseHeader(pBuffer, 0);
    }

    /*------- Initialize the ASF splitter. -------*/

    // Create the splitter.
    if (SUCCEEDED(hr))
    {
        hr = MFCreateASFSplitter(&pSplitter);
    }
    
    // initialize the splitter with the ContentInfo object.
    if (SUCCEEDED(hr))
    {
        hr = pSplitter->Initialize(pSourceContentInfo);
    }


    /*------- Get the offset and size of the ASF Data Object. -------*/

    // Generate the presentation descriptor.
    if (SUCCEEDED(hr))
    {
        hr =  pSourceContentInfo->GeneratePresentationDescriptor(&pPD);
    }

    // Get the offset to the start of the Data Object.
    if (SUCCEEDED(hr))
    {
        hr = pPD->GetUINT64(MF_PD_ASF_DATA_START_OFFSET, pcbDataOffset);
    }

    // Get the length of the Data Object.
    if (SUCCEEDED(hr))
    {
        hr = pPD->GetUINT64(MF_PD_ASF_DATA_LENGTH, pcbDataLength);
    }

    // Return the pointers to the caller.
    if (SUCCEEDED(hr))
    {
        *ppSourceContentInfo = pSourceContentInfo;
        (*ppSourceContentInfo)->AddRef();

        *ppSplitter = pSplitter;
        (*ppSplitter)->AddRef();

    }

    SafeRelease(&pPD);
    SafeRelease(&pBuffer);
    SafeRelease(&pSourceContentInfo);
    SafeRelease(&pSplitter);

    return S_OK;
}

5. オーディオ プロファイルを作成する

次に、ソース ContentInfo オブジェクトから取得して、入力ファイルのプロファイル オブジェクトを作成します。 次に、入力ファイルのオーディオ ストリームのみが含まれるようにプロファイルを構成します。 これを行うには、ストリームを列挙し、プロファイルからオーディオ以外のストリームを削除します。 オーディオ プロファイル オブジェクトは、このチュートリアルの後半で、出力 ContentInfo オブジェクトを初期化するために使用します。

オーディオ プロファイルを作成するには

  1. IMFASFContentInfo::GetProfile を呼び出して、ソース ContentInfo オブジェクトから入力ファイルのプロファイル オブジェクトを取得します。 メソッドは、入力ファイル内のすべてのストリームを含むプロファイル オブジェクトへのポインターを返します。 詳細については、「 ASF プロファイルの作成」を参照してください。
  2. プロファイルから相互除外オブジェクトを削除します。 この手順は、オーディオ以外のストリームがプロファイルから削除されるために必要です。これにより、相互除外オブジェクトが無効になる可能性があります。
  3. 次のように、プロファイルからすべての非オーディオ ストリームを削除します。
  4. 最初のオーディオ ストリームのストリーム番号を格納します。 これは、ストリーム サンプルを生成するためにスプリッターで選択されます。 ストリーム番号が 0 の場合、呼び出し元はオーディオ ストリーム ファイルがなかったと見なすことができます。

次のコードでは、これらの手順を実行します。

//-------------------------------------------------------------------
// GetAudioProfile
//
// Gets the ASF profile from the source file and removes any video
// streams from the profile.
//-------------------------------------------------------------------

HRESULT GetAudioProfile(
    IMFASFContentInfo *pSourceContentInfo, 
    IMFASFProfile **ppAudioProfile, 
    WORD *pwSelectStreamNumber
    )
{
    IMFASFStreamConfig *pStream = NULL;
    IMFASFProfile *pProfile = NULL;

    DWORD dwTotalStreams = 0;
    WORD  wStreamNumber = 0; 
    GUID guidMajorType = GUID_NULL;
    
    // Get the profile object from the source ContentInfo object.
    HRESULT hr = pSourceContentInfo->GetProfile(&pProfile);

    // Remove mutexes from the profile
    if (SUCCEEDED(hr))
    {
        hr = RemoveMutexes(pProfile);
    }

    // Get the total number of streams on the profile.
    if (SUCCEEDED(hr))
    {
        hr = pProfile->GetStreamCount(&dwTotalStreams);
    }

    // Enumerate the streams and remove the non-audio streams.
    if (SUCCEEDED(hr))
    {
        for (DWORD index = 0; index < dwTotalStreams; )
        {
            hr = pProfile->GetStream(index, &wStreamNumber, &pStream);

            if (FAILED(hr)) { break; }

            hr = pStream->GetStreamType(&guidMajorType);

            SafeRelease(&pStream);

            if (FAILED(hr)) { break; }

            if (guidMajorType != MFMediaType_Audio)
            {
                hr = pProfile->RemoveStream(wStreamNumber);
    
                if (FAILED(hr)) { break; }

                index = 0;
                dwTotalStreams--;
            }
            else
            {
                // Store the first audio stream number. 
                // This will be selected on the splitter.

                if (*pwSelectStreamNumber == 0)
                {
                    *pwSelectStreamNumber = wStreamNumber;
                }

                index++;
            }
        }
    }

    if (SUCCEEDED(hr))
    {
        *ppAudioProfile = pProfile;
        (*ppAudioProfile)->AddRef();
    }

    SafeRelease(&pStream);
    SafeRelease(&pProfile);

    return S_OK;
}

この関数は RemoveMutexes 、プロファイルから相互除外オブジェクトを削除します。

HRESULT RemoveMutexes(IMFASFProfile *pProfile)
{
    DWORD cMutex = 0;
    HRESULT hr = pProfile->GetMutualExclusionCount(&cMutex);

    if (SUCCEEDED(hr))
    {
        for (DWORD i = 0; i < cMutex; i++)
        {
            hr = pProfile->RemoveMutualExclusion(0);

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

    return hr;
}

6. 出力ファイルのオブジェクトを初期化する

次に、出力ファイルのデータ パケットを生成するための出力 ContentInfo オブジェクトとマルチプレクサーを作成します。

手順 4 で作成したオーディオ プロファイルを使用して、出力 ContentInfo オブジェクトを設定します。 このオブジェクトには、グローバル ファイル属性やストリーム プロパティなどの情報が含まれます。 出力 ContentInfo オブジェクトは、出力ファイルのデータ パケットを生成するマルチプレクサーを初期化するために使用されます。 データ パケットが生成された後、新しい値を反映するように ContentInfo オブジェクトを更新する必要があります。

出力ファイルの ASF コンポーネントを作成して初期化するには

  1. MFCreateASFContentInfo を呼び出して空の ContentInfo オブジェクトを作成し、IMFASFContentInfo::SetProfile を呼び出して手順 3 で作成したオーディオ プロファイルの情報を設定します。 詳細については、「 新しい ASF ファイルの ContentInfo オブジェクトの初期化」を参照してください。
  2. 出力 ContentInfo オブジェクトを使用して、マルチプレクサー オブジェクトを作成して初期化します。 詳細については、「 マルチプレクサー オブジェクトの作成」を参照してください。

次のコード例は、手順を統合する関数を示しています。 この関数は、プロファイル オブジェクトへのポインターを受け取り、出力 ContentInfo オブジェクトとマルチプレクサーへのポインターを返します。

//-------------------------------------------------------------------
// CreateOutputGenerators
//
// Creates the ASF mux and the ASF Content Info object for the 
// output file.
//-------------------------------------------------------------------

HRESULT CreateOutputGenerators(
    IMFASFProfile *pProfile, 
    IMFASFContentInfo **ppContentInfo, 
    IMFASFMultiplexer **ppMux
    )
{
    IMFASFContentInfo *pContentInfo = NULL;
    IMFASFMultiplexer *pMux = NULL;

    // Use the ASF profile to create the ContentInfo object.
    HRESULT hr = MFCreateASFContentInfo(&pContentInfo);

    if (SUCCEEDED(hr))
    {
        hr = pContentInfo->SetProfile(pProfile);
    }

    // Create the ASF Multiplexer object.
    if (SUCCEEDED(hr))
    {
        hr = MFCreateASFMultiplexer(&pMux);
    }
    
    // Initialize it using the new ContentInfo object.
    if (SUCCEEDED(hr))
    {
        hr = pMux->Initialize(pContentInfo);
    }

    // Return the pointers to the caller.
    if (SUCCEEDED(hr))
    {
        *ppContentInfo = pContentInfo;
        (*ppContentInfo)->AddRef();

        *ppMux = pMux;
        (*ppMux)->AddRef();
    }

    SafeRelease(&pContentInfo);
    SafeRelease(&pMux);

    return hr;
}

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

次に、スプリッターを使用してソース バイト ストリームからオーディオ ストリーム サンプルを生成し、それらをマルチプレクサーに送信して ASF データ パケットを作成します。 これらのデータ パケットは、新しいファイルの最終的な ASF データ オブジェクトを構成します。

オーディオ ストリームのサンプルを生成するには

  1. IMFASFSplitter::SelectStreams を呼び出して、スプリッターの最初のオーディオ ストリームを選択します。
  2. ソース バイト ストリームからメディア バッファーにメディア データの固定サイズ ブロックを読み取ります。
  3. pdwStatusFlags パラメーターで ASF_STATUSFLAGS_INCOMPLETE フラグを受け取る限り、ループ内で IMFASFSplitter::GetNextSample を呼び出して、ストリーム サンプルをスプリッターからメディア サンプルとして収集します。 詳細については、「既存の ASF データ オブジェクトからのストリーム サンプルの生成」の「 ASF データ パケットのサンプルの生成」を参照してください。
  4. メディア サンプルごとに、 IMFASFMultiplexer::P rocessSample を呼び出して、メディア サンプルをマルチプレクサーに送信します。 マルチプレクサは、ASF データ オブジェクトのデータ パケットを生成します。
  5. マルチプレクサーによって生成されたデータ パケットをデータ バイト ストリームに書き込みます。
  6. すべてのデータ パケットが生成されたら、 IMFASFMultiplexer::End を呼び出して、出力 ContentInfo オブジェクトを ASF データ パケット生成中に収集された情報で更新します。

次のコード例では、ASF スプリッターからストリーム サンプルを生成し、マルチプレクサーに送信します。 マルチプレクサは ASF データ パケットを生成し、ストリームに書き込みます。

//-------------------------------------------------------------------
// GenerateASFDataObject
// 
// Creates a byte stream that contains the ASF Data Object for the
// output file.
//-------------------------------------------------------------------

HRESULT GenerateASFDataObject(
    IMFByteStream *pSourceStream, 
    IMFASFSplitter *pSplitter, 
    IMFASFMultiplexer *pMux, 
    UINT64   cbDataOffset,
    UINT64   cbDataLength,
    IMFByteStream **ppDataStream
    )
{
    IMFMediaBuffer *pBuffer = NULL;
    IMFByteStream *pDataStream = NULL;
    
    const DWORD READ_SIZE = 1024 * 4;

    // Flush the splitter to remove any pending samples.
    HRESULT hr = pSplitter->Flush();

    if (SUCCEEDED(hr))
    {
        hr = MFCreateTempFile(
            MF_ACCESSMODE_READWRITE, 
            MF_OPENMODE_DELETE_IF_EXIST,
            MF_FILEFLAGS_NONE,
            &pDataStream
            );
    }

    if (SUCCEEDED(hr))
    {
        hr = pSourceStream->SetCurrentPosition(cbDataOffset);
    }

    if (SUCCEEDED(hr))
    {
        while (cbDataLength > 0)
        {
            DWORD cbRead = min(READ_SIZE, (DWORD)cbDataLength);

            hr = AllocReadFromByteStream(
                pSourceStream, 
                cbRead, 
                &pBuffer
                );

            if (FAILED(hr)) 
            { 
                break; 
            }

            cbDataLength -= cbRead;

            // Push data on the splitter.
            hr =  pSplitter->ParseData(pBuffer, 0, 0);

            if (FAILED(hr)) 
            { 
                break; 
            }

            // Get ASF packets from the splitter and feed them to the mux.
            hr = GetPacketsFromSplitter(pSplitter, pMux, pDataStream);

            if (FAILED(hr)) 
            { 
                break; 
            }

            SafeRelease(&pBuffer);
        }
    }

    // Flush the mux and generate any remaining samples.
    if (SUCCEEDED(hr))
    {
        hr = pMux->Flush();
    }

    if (SUCCEEDED(hr))
    {
        hr = GenerateASFDataPackets(pMux, pDataStream);
    }

     // Return the pointer to the caller.
    if (SUCCEEDED(hr))
    {
        *ppDataStream = pDataStream;
        (*ppDataStream)->AddRef();
    }

    SafeRelease(&pBuffer);
    SafeRelease(&pDataStream);
    return hr;
}

ASF スプリッターからパケットを取得するために、前のコードは関数を呼び出します。次に GetPacketsFromSplitter 示します。

//-------------------------------------------------------------------
// GetPacketsFromSplitter
//
// Gets samples from the ASF splitter.
//
// This function is called after calling IMFASFSplitter::ParseData.
//-------------------------------------------------------------------

HRESULT GetPacketsFromSplitter(
    IMFASFSplitter *pSplitter,
    IMFASFMultiplexer *pMux,
    IMFByteStream *pDataStream
    )
{
    HRESULT hr = S_OK;
    DWORD   dwStatus = ASF_STATUSFLAGS_INCOMPLETE;
    WORD    wStreamNum = 0;

    IMFSample *pSample = NULL;

    while (dwStatus & ASF_STATUSFLAGS_INCOMPLETE) 
    {
        hr = pSplitter->GetNextSample(&dwStatus, &wStreamNum, &pSample);

        if (FAILED(hr))
        {
            break;
        }

        if (pSample)
        {
            //Send to the multiplexer to convert it into ASF format
            hr = pMux->ProcessSample(wStreamNum, pSample, 0);

            if (FAILED(hr)) 
            { 
                break; 
            }

            hr = GenerateASFDataPackets(pMux, pDataStream);

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

        SafeRelease(&pSample);
    }

    SafeRelease(&pSample);
    return hr;
}

関数は GenerateDataPackets 、マルチプレクサーからデータ パケットを取得します。 詳細については、「 ASF データ パケットの取得」を参照してください。

//-------------------------------------------------------------------
// GenerateASFDataPackets
// 
// Gets data packets from the mux. This function is called after 
// calling IMFASFMultiplexer::ProcessSample. 
//-------------------------------------------------------------------

HRESULT GenerateASFDataPackets( 
    IMFASFMultiplexer *pMux, 
    IMFByteStream *pDataStream
    )
{
    HRESULT hr = S_OK;

    IMFSample *pOutputSample = NULL;
    IMFMediaBuffer *pDataPacketBuffer = NULL;

    DWORD dwMuxStatus = ASF_STATUSFLAGS_INCOMPLETE;

    while (dwMuxStatus & ASF_STATUSFLAGS_INCOMPLETE)
    {
        hr = pMux->GetNextPacket(&dwMuxStatus, &pOutputSample);

        if (FAILED(hr))
        {
            break;
        }

        if (pOutputSample)
        {
            //Convert to contiguous buffer
            hr = pOutputSample->ConvertToContiguousBuffer(&pDataPacketBuffer);
            
            if (FAILED(hr))
            {
                break;
            }

            //Write buffer to byte stream
            hr = WriteBufferToByteStream(pDataStream, pDataPacketBuffer, NULL);

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

        SafeRelease(&pDataPacketBuffer);
        SafeRelease(&pOutputSample);
    }

    SafeRelease(&pOutputSample);
    SafeRelease(&pDataPacketBuffer);
    return hr;
}

8. 新しいファイルに ASF オブジェクトを書き込む

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

新しい ASF ヘッダー オブジェクトが生成されたら、最初にヘルパー関数 WriteBufferToByteStream を呼び出して、手順 2 で作成した出力バイト ストリームに Header オブジェクトを書き込んで、出力ファイルを書き込みます。 データ バイト ストリームに含まれる Data オブジェクトを使用して、Header オブジェクトに従います。 コード例は、データ バイト ストリームの内容を出力バイト ストリームに転送する関数を示しています。

//-------------------------------------------------------------------
// WriteASFFile
//
// Writes the complete ASF file.
//-------------------------------------------------------------------

HRESULT WriteASFFile( 
    IMFASFContentInfo *pContentInfo, // ASF Content Info for the output file.
    IMFByteStream *pDataStream,      // Data stream.
    PCWSTR pszFile                   // Output file name.
    )
{
    
    IMFMediaBuffer *pHeaderBuffer = NULL;
    IMFByteStream *pWmaStream = NULL;

    DWORD cbHeaderSize = 0;
    DWORD cbWritten = 0;

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

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

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

    // Populate the media buffer with the ASF Header Object.
    if (SUCCEEDED(hr))
    {
        hr = pContentInfo->GenerateHeader(pHeaderBuffer, &cbHeaderSize);
    }
 
    // Write the header contents to the byte stream for the output file.
    if (SUCCEEDED(hr))
    {
        hr = WriteBufferToByteStream(pWmaStream, pHeaderBuffer, &cbWritten);
    }

    if (SUCCEEDED(hr))
    {
        hr = pDataStream->SetCurrentPosition(0);
    }

    // Append the data stream to the file.

    if (SUCCEEDED(hr))
    {
        hr = AppendToByteStream(pDataStream, pWmaStream);
    }

    SafeRelease(&pHeaderBuffer);
    SafeRelease(&pWmaStream);

    return hr;
}

9 Entry-Point関数を記述する

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

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

int wmain(int argc, WCHAR* argv[])
{
    if (argc != 3)
    {
        wprintf_s(L"Usage: %s input.wmv, %s output.wma\n");
        return 0;
    }

    HRESULT hr = MFStartup(MF_VERSION);

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

    PCWSTR pszInputFile = argv[1];      
    PCWSTR pszOutputFile = argv[2];     
    
    IMFByteStream      *pSourceStream = NULL;       
    IMFASFContentInfo  *pSourceContentInfo = NULL;  
    IMFASFProfile      *pAudioProfile = NULL;       
    IMFASFContentInfo  *pOutputContentInfo = NULL;  
    IMFByteStream      *pDataStream = NULL;         
    IMFASFSplitter     *pSplitter = NULL;           
    IMFASFMultiplexer  *pMux = NULL;                

    UINT64  cbDataOffset = 0;           
    UINT64  cbDataLength = 0;           
    WORD    wSelectStreamNumber = 0;    

    // Open the input file.

    hr = OpenFile(pszInputFile, &pSourceStream);

    // Initialize the objects that will parse the source file.

    if (SUCCEEDED(hr))
    {
        hr = CreateSourceParsers(
            pSourceStream, 
            &pSourceContentInfo,    // ASF Header for the source file.
            &pSplitter,             // Generates audio samples.
            &cbDataOffset,          // Offset to the first data packet.
            &cbDataLength           // Length of the ASF Data Object.
            );
    }

    // Create a profile object for the audio streams in the source file.

    if (SUCCEEDED(hr))
    {
        hr = GetAudioProfile(
            pSourceContentInfo, 
            &pAudioProfile,         // ASF profile for the audio stream.
            &wSelectStreamNumber    // Stream number of the first audio stream.
            );
    }

    // Initialize the objects that will generate the output data.

    if (SUCCEEDED(hr))
    {
        hr = CreateOutputGenerators(
            pAudioProfile, 
            &pOutputContentInfo,    // ASF Header for the output file.
            &pMux                   // Generates ASF data packets.
            );
    }

    // Set up the splitter to generate samples for the first
    // audio stream in the source media.

    if (SUCCEEDED(hr))
    {
        hr = pSplitter->SelectStreams(&wSelectStreamNumber, 1);
    }
    
    // Generate ASF Data Packets and store them in a byte stream.

    if (SUCCEEDED(hr))
    {
        hr = GenerateASFDataObject(
               pSourceStream, 
               pSplitter, 
               pMux, 
               cbDataOffset, 
               cbDataLength, 
               &pDataStream    // Byte stream for the ASF data packets.    
               );
    }

    // Update the header with new information if any.

    if (SUCCEEDED(hr))
    {
        hr = pMux->End(pOutputContentInfo);
    }

    //Write the ASF objects to the output file
    if (SUCCEEDED(hr))
    {
        hr = WriteASFFile(pOutputContentInfo, pDataStream, pszOutputFile);
    }

    // Clean up.
    SafeRelease(&pMux);
    SafeRelease(&pSplitter);
    SafeRelease(&pDataStream);
    SafeRelease(&pOutputContentInfo);
    SafeRelease(&pAudioProfile);
    SafeRelease(&pSourceContentInfo);
    SafeRelease(&pSourceStream);

    MFShutdown();

    if (FAILED(hr))
    {
        wprintf_s(L"Could not create the audio file: 0x%X\n", hr);
    }

    return 0;
}

WMContainer ASF コンポーネント

Media Foundation での ASF サポート