教學課程:1-Pass Windows 媒體編碼

編碼是指將數位媒體從一種格式轉換成另一種格式的程式。 例如,將 MP3 音訊轉換成Windows媒體音訊格式,如進階系統格式 (ASF) 規格所定義。

注意 ASF 規格會定義輸出檔 (.wma 或 .wmv) 的容器類型,該檔案可以包含任何格式的媒體資料、壓縮或未壓縮。 例如,.wma 檔案之類的 ASF 容器可以包含 MP3 格式的媒體資料。 編碼程式會轉換檔案內所含資料的實際格式。

本教學課程示範如何使用管線層 ASF 元件,將非 DRM 保護) 輸入來源的清除 (內容編碼為Windows媒體內容,並將資料寫入新的 ASF 檔案 (.wm*) 。 這些元件將用來建置部分編碼拓撲,此拓撲將由媒體會話的實例控制。

在本教學課程中,您將建立主控台應用程式,以接受輸入和輸出檔案名,並將編碼模式當做引數。 輸入檔案可以是壓縮或未壓縮的媒體格式。 有效的編碼模式是 「CBR」 (常數位元速率) 或 「VBR」 (變數位元速率) 。 應用程式會建立媒體來源來代表輸入檔案名所指定的來源,以及 ASF 檔案接收,以將原始程式檔的編碼內容封存到 ASF 檔案中。 為了簡化實作案例,輸出檔案只會有一個音訊串流和一個視訊串流。 應用程式會插入音訊資料流程格式轉換的 Windows Media Audio 9.1 Professional 編解碼器,並針對視訊串流Windows媒體視訊 9 編解碼器。

針對常數位元速率編碼,在編碼會話開始之前,編碼器必須知道它必須達到的目標位元速率。 在本教學課程中,針對 「CBR」 模式,應用程式會使用在媒體類型交涉期間從編碼器擷取的第一個輸出媒體類型作為目標位元速率的可用位元速率。 針對變數位元速率編碼,本教學課程會藉由設定品質等級來示範使用變數位元速率的編碼。 音訊串流會以品質層級 98 編碼,而視訊串流則以品質層級 95 編碼。

下列程式摘要說明使用 1 階段編碼模式,在 ASF 容器中編碼Windows媒體內容的步驟。

  1. 使用來源解析程式為指定的 建立媒體來源。
  2. 列舉媒體來源中的資料流程。
  3. 建立 ASF 媒體接收,並根據需要編碼之媒體來源中的資料流程來新增串流接收。
  4. 使用必要的編碼屬性設定媒體接收。
  5. 為輸出檔案中的資料流程建立Windows媒體編碼器。
  6. 使用媒體接收上設定的屬性來設定編碼器。
  7. 建置部分編碼拓撲。
  8. 具現化媒體會話,並在媒體會話上設定拓撲。
  9. 藉由控制媒體會話,並從媒體會話取得所有相關事件,以啟動編碼會話。
  10. 針對 VBR 編碼,請從編碼器取得編碼屬性值,並在媒體接收上設定它們。
  11. 關閉並關閉編碼會話。

本教學課程包含下列各節:

先決條件

本教學課程假設您已句備下列條件:

設定Project

  1. 在您的原始程式檔中包含下列標頭:

    #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>
    
    
  2. 連結至下列程式庫檔案:

    // 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")
    
    
  3. 宣告 SafeRelease 函式。

    template <class T> void SafeRelease(T **ppT)
    {
        if (*ppT)
        {
            (*ppT)->Release();
            *ppT = NULL;
        }
    }
    
  4. 宣告ENCODING_MODE列舉,以定義 CBR 和 VBR 編碼類型。

    // Encoding mode
    typedef enum ENCODING_MODE {
      NONE  = 0x00000000,
      CBR   = 0x00000001,
      VBR   = 0x00000002,
    } ;
    
    
  5. 為視訊資料流程定義緩衝區視窗的常數。

    // Video buffer window
    const INT32 VIDEO_WINDOW_MSEC = 3000;
    
    

建立媒體來源

建立輸入來源的媒體來源。 媒體基礎提供各種媒體格式的內建媒體來源:MP3、MP4/3GP、AVI/WAVE。 如需媒體基礎所支援之格式的詳細資訊,請參閱 Media Foundation 中支援的媒體格式

若要建立媒體來源,請使用 來源解析程式。 此物件會分析為來源檔案指定的 URL,並建立適當的媒體來源。

進行下列呼叫:

下列程式碼範例示範建立指定檔案之媒體來源的 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 Profile 物件中設定,而且編碼和全域屬性是在 ASF ContentInfo 物件所管理的屬性存放區中設定。

如需 ASF 檔案接收的概觀,請參閱 ASF 媒體接收

建立 ASF 設定檔物件

若要讓 ASF 檔案接收將編碼的媒體資料寫入 ASF 檔案中,接收必須知道資料流程數目,以及要為其建立資料流程接收的資料流程類型。 在本教學課程中,您將從媒體來源擷取該資訊,並建立對應的輸出資料流程。 將您的輸出資料流程限制為一個音訊串流和一個視訊串流。 針對來源中的每個選取資料流程,建立目標資料流程並將它新增至設定檔。

若要實作此步驟,您需要下列物件。

  • ASF 設定檔
  • 媒體來源的簡報描述項
  • 媒體來源中所選資料流程的資料流程描述項。
  • 所選資料流程的媒體類型。

下列步驟說明建立 ASF 設定檔和目標資料流程的程式。

建立 ASF 設定檔

  1. 呼叫 MFCreateASFProfile 以建立空的設定檔物件。

  2. 呼叫 IMFMediaSource::CreatePresentationDescriptor ,為本教學課程的一節中所述的步驟所建立的媒體來源建立簡報描述項。

  3. 呼叫 IMFPresentationDescriptor::GetStreamDescriptorCount 以取得媒體來源中的資料流程數目。

  4. 針對媒體來源中的每個資料流程呼叫 IMFPresentationDescriptor::GetStreamDescriptorByIndex ,取得資料流程的資料流程描述元。

  5. 呼叫 IMFStreamDescriptor::GetMediaTypeHandler ,後面接著 IMFMediaTypeHandler::GetMediaTypeByIndex ,並取得資料流程的第一個媒體類型。

    注意 若要避免複雜的呼叫,假設每個資料流程只有一個媒體類型存在,並選取資料流程的第一個媒體類型。 對於複雜的資料流程,您必須從媒體類型處理常式列舉每個媒體類型,然後選擇您想要編碼的媒體類型。

  6. 呼叫IIMFMediaType::GetMajorType 以取得資料流程的主要類型,以判斷資料流程是否包含音訊或視訊。

  7. 視資料流程的主要類型而定,建立目標媒體類型。 這些媒體類型會保存編碼器在編碼會話期間將使用的格式資訊。 本教學課程的下列各節說明建立目標媒體類型的程式。

    • 建立壓縮的音訊媒體類型
    • 建立壓縮的視訊媒體類型
  8. 根據目標媒體類型建立資料流程、根據應用程式的需求設定資料流程,並將資料流程新增至設定檔。 如需詳細資訊,請參閱 將資料流程資訊新增至 ASF 檔案接收

    1. 呼叫 IMFASFProfile::CreateStream ,並傳遞目標媒體類型以建立輸出資料流程。 方法會擷取資料流程物件的 IMFASFStreamConfig 介面。
    2. 設定資料流程。
    3. 使用 ASF ContentInfo 物件新增資料流程層級編碼屬性。 For more information about this step, see the "Create the ASF ContentInfo Object" section in this tutorial.
    4. 呼叫 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媒體音訊編碼器相容、具現化編碼器 MFT、設定編碼屬性,以及呼叫IMFTransform::GetOutputAvailableType來取得媒體類型。 藉由迴圈查看所有可用的類型、取得每個媒體類型的屬性,以及選取最接近您需求的類型,以取得所需的輸出類型。 在本教學課程中,從編碼器支援的輸出媒體類型清單中取得第一個可用的類型。

注意針對 Windows 7,Media Foundation 提供新的函式MFTranscodeGetAudioOutputAvailableTypes,以擷取相容的音訊類型清單。 此函式只會取得 CBR 編碼的媒體類型。

完整的音訊類型必須設定下列屬性:

下列程式碼範例會從 Windows 媒體音訊編碼器取得相容的類型,以建立壓縮的音訊類型。 The implementation for SetEncodingProperties is shown in the "Create the ASF ContentInfo Object" section of this tutorial.

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

建立壓縮的視訊媒體類型

如果您想要在輸出檔案中包含視訊串流,請建立完整編碼的視訊類型。 完整的媒體類型必須包含所需的位元速率和編解碼器私人資料。

有兩種方式可以建立完整的視訊媒體類型。

  • 建立空的媒體類型,並從來源視訊類型複製媒體類型屬性,並使用 GUID 常數MFVideoFormat_WMV3覆寫 MF_MT_SUBTYPE 屬性。

    完整的視訊類型必須設定下列屬性:

    • 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媒體視訊編碼器取得相容的媒體類型。 這個方法會傳回部分類型。 新增下列資訊,以確定您將部分類型轉換成完整類型:

    因為 IWMCodecPrivateData::GetPrivateData 會先檢查位元速率再傳回編解碼器私人資料,所以請務必先設定位元速率,再取得編解碼器私人資料。

    下列程式碼範例會從 Windows Media Video 編碼器取得相容的類型,以建立壓縮的視訊類型。 它也會建立未壓縮的視訊類型,並將其設定為編碼器的輸入。 這會在 Helper 函式 CreateUncompressedVideoType 中實作。 GetOutputTypeFromWMVEncoder 藉由新增編解碼器私用資料,將傳回的部分類型轉換成完整類型。 The implementation for SetEncodingProperties is shown in the "Create the ASF ContentInfo Object" section of this tutorial.

    //-------------------------------------------------------------------
    //  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 物件是 WMContainer 層級元件,主要是設計用來儲存 ASF 標頭物件資訊。 ASF 檔案接收會實作 ContentInfo 物件,以便將資訊 (儲存在屬性存放區中,) 用來寫入編碼檔案的 ASF 標頭物件。 若要這樣做,您必須先在 ContentInfo 物件上建立及設定下列一組資訊,才能開始編碼會話。

  1. 呼叫 MFCreateASFContentInfo 以建立空的 ContentInfo 物件。

    下列程式碼範例會建立空的 ContentInfo 物件。

        // Create an empty ContentInfo object
        hr = MFCreateASFContentInfo(&pContentInfo);
        if (FAILED(hr))
        {
            goto done;
        }
    
    
  2. 呼叫 IMFASFContentInfo::GetEncodingConfigurationPropertyStore 以取得檔案接收的資料流程層級屬性存放區。 在此呼叫中,您必須在建立 ASF 設定檔時傳遞為數據流指派的資料流程號碼。

  3. 在檔案接收的資料流程屬性存放區中設定資料流程層級 編碼屬性 。 如需詳細資訊,請參閱 在檔案接收中設定屬性中的

    下列程式碼範例會在檔案接收的屬性存放區中設定資料流程層級屬性。

            //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;
    }
    
  4. 呼叫 IMFASFContentInfo::GetEncodingConfigurationPropertyStore 以取得檔案接收的全域屬性存放區。 在此呼叫中,您需要在第一個參數中傳遞 0。 如需詳細資訊,請參閱 在檔案接收中設定屬性中的

  5. MFPKEY_ASFMEDIASINK_AUTOADJUST_BITRATE 設定為VARIANT_TRUE,以確保正確調整 ASF 多工器中的流失值區值。 For information about this property, see "Multiplexer Initialization and Leaky Bucket Settings" in Creating the Multiplexer Object.

    下列程式碼範例會在檔案接收的屬性存放區中設定資料流程層級屬性。

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

    注意

    您可以在檔案接收的全域層級設定其他屬性。 For more information, see "Configuring the ContentInfo Object with Encoder Settings" in Setting Properties in the ContentInfo Object.

     

您將使用設定的 ASF ContentInfo,為 ASF 檔案接收建立啟用物件, (下一節) 中所述。

如果您要建立跨進程檔案接收 (MFCreateASFMediaSinkActivate) ,也就是使用啟用物件,您可以使用設定的 ContentInfo 物件來具現化下一節所述的 ASF 媒體接收 () 。 如果您要建立同進程檔案接收 (MFCreateASFMediaSink) ,而不是如步驟 1 所述建立空的 ContentInfo 物件,請在檔案接收上呼叫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媒體編碼器和 ASF 檔案接收,以建置部分編碼拓撲。 新增拓撲節點之後,您將連接來源、轉換和接收節點。 新增拓撲節點之前,您必須呼叫 MFCreateTopology來建立空的拓撲物件。

建立媒體來源的來源拓撲節點

在此步驟中,您將建立媒體來源的來源拓撲節點。

若要建立此節點,您需要下列參考:

如需建立來源節點和程式碼範例的詳細資訊,請參閱 建立來源節點

下列程式碼範例會藉由新增來源節點和必要的轉換節點來建立部分拓撲。 此程式碼會呼叫 Helper 函式 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媒體編碼器。 應用程式必須手動新增編碼器。 To do so, enumerate the streams in the ASF profile that you created in the step described in the "Create the ASF Profile Object" section of this tutorial. 針對來源中的每個資料流程和設定檔中的對應資料流程,具現化所需的編碼器。 For this step, you need a pointer to the activation object for the file sink that you created in the step described in the "Create the ASF File Sink" section of this tutorial.

如需透過啟用物件建立編碼器的概觀,請參閱 使用編碼器的啟用物件

下列程式描述具現化所需編碼器所需的步驟。

  1. 藉由在檔案接收啟動上呼叫IMFActivate::ActivateObject,然後藉由呼叫QueryInterface從檔案接收查詢IMFASFContentInfo,以取得接收 ContentInfo 物件的參考。

  2. 呼叫 IMFASFContentInfo::GetProfile,以取得與 ContentInfo 物件相關聯的 ASF 設定檔。

  3. 列舉設定檔中的資料流程。 為此,您將需要資料流程計數和每個資料流程 之 IMFASFStreamConfig 介面的參考。

    呼叫下列方法:

  4. 針對每個資料流程,會從 ContentInfo 物件取得主要類型和資料流程的編碼屬性。

    呼叫下列方法:

  5. 根據資料流程、音訊或視訊的類型,呼叫 MFCreateWMAEncoderActivateMFCreateWMVEncoderActivate來具現化編碼器的啟用物件。

    若要呼叫這些函式,您需要下列參考:

  6. 更新音訊資料流程的流失值區參數。

    MFCreateWMAEncoderActivate會在Windows媒體音訊編解碼器的基礎編碼器 MFT 上設定輸出類型。 設定輸出媒體類型之後,編碼器會從輸出媒體類型取得平均位元速率、計算緩衝區視窗 rage 位速率,以及設定編碼會話期間將使用的流失值區值。 您可以藉由查詢編碼器或設定自訂值,在檔案接收中更新這些值。 若要更新值,您需要下列一組資訊:

    建立 DWORD 的陣列,並在音訊資料流程接收 的 MFPKEY_ASFSTREAMSINK_CORRECTED_LEAKYBUCKET 屬性中設定值。 如果您未提供更新的值,媒體會話會適當地設定它們。

    如需詳細資訊,請參閱 流失貯體緩衝區模型

在步驟 5 中建立的啟用物件必須新增至拓撲作為轉換拓撲節點。 For more information and code example, see "Creating a Transform Node from an Activation Object" in Creating Transform Nodes.

下列程式碼範例會建立並新增必要的編碼器啟動。 它會取得先前建立之拓撲物件的指標、檔案接收的啟用物件和來來源資料流的媒體類型。 它也會呼叫 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 檔案接收的輸出拓撲節點。

若要建立此節點,您需要下列參考:

  • A pointer to the activation object that you created in the step described in the "Create the ASF File Sink" section of this tutorial.
  • 用來識別新增至檔案接收之資料流程接收的資料流程編號。 資料流程編號符合串流建立期間所設定的資料流程識別。

For more information about creating output nodes and code example, see "Creating an Output Node from an Activation Object" in Creating Output Nodes.

如果您未針對檔案接收使用啟用物件,則必須列舉 ASF 檔案接收中的資料流程接收,並將每個資料流程接收設定為拓撲中的輸出節點。 如需資料流程接收列舉的相關資訊,請參閱 將資料流程資訊新增至 ASF 檔案接收中的

下列程式碼範例會建立接收節點,並將其新增至編碼拓撲。 它會取得先前建立之拓撲物件的指標、檔案接收的啟用物件和資料流程的識別碼。 呼叫端會收到來源拓撲節點的指標。

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

連線來源、轉換和接收節點

在此步驟中,您會將來源節點連接到參考編碼的轉換節點,這是在本教學課程的一節中所述的步驟中所建立的編碼。 轉換節點將會連接到包含檔案接收啟用物件的輸出節點。

處理編碼會話

在步驟中,您將執行下列步驟:

  1. 呼叫 MFCreateMediaSession 以建立編碼會話。

  2. 呼叫 IMFMediaSession::SetTopology 以在會話上設定編碼拓撲。 如果呼叫完成,媒體會話會評估拓撲節點,並插入其他轉換物件,例如將指定的壓縮來源轉換成未壓縮樣本的解碼器,以作為編碼器的輸入來饋送。

  3. 呼叫 IMFMediaSession::GetEvent 以要求媒體會話所引發的事件。

    在事件迴圈中,您將根據媒體會話所引發的事件來啟動和關閉編碼會話。 IMFMediaSession::SetTopology呼叫會導致媒體會話引發MESessionTopologyStatus事件並設定MF_TOPOSTATUS_READY旗標。 所有事件都會以非同步方式產生,而且應用程式可以同步或非同步擷取這些事件。 由於本教學課程中的應用程式是主控台應用程式,且封鎖使用者介面執行緒並不相關,因此我們會以同步方式從媒體會話取得事件。

    若要以非同步方式取得事件,您的應用程式必須實作 IMFAsyncCallback 介面。 如需此介面的詳細資訊和範例實作,請參閱 如何使用 Media Foundation 播放媒體檔案中的。

在取得媒體會話事件的事件迴圈中,等候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 檔案接收上的後置編碼屬性值

  1. 呼叫 IMFTopology::GetOutputNodeCollection 以從編碼拓撲取得輸出節點集合。
  2. 針對每個節點,呼叫 IMFTopologyNode::GetObject,以取得節點中資料流程接收的指標。 在IMFTopologyNode::GetObject所傳回的 IUnknown指標上查詢IMFStreamSink介面。
  3. 針對每個資料流程接收,呼叫 IMFTopologyNode::GetInput來取得下游節點 (編碼器) 。
  4. 查詢節點,以從編碼器節點取得 IMFTransform 指標。
  5. 查詢 IPropertyStore 指標的編碼器,以從編碼器取得編碼屬性存放區。
  6. 查詢 IPropertyStore 指標的資料流程接收,以取得資料流程接收的屬性存放區。
  7. 呼叫 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

下列程式碼範例顯示主控台應用程式的主要功能。

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

測試輸出檔案

下列清單描述用於測試編碼檔案的檢查清單。 您可以在檔案屬性對話方塊中核取這些值,方法是以滑鼠右鍵按一下編碼的檔案,然後從操作功能表中選取 [ 屬性 ]。

  • 編碼檔案的路徑是正確的。
  • 檔案的大小超過零 KB,播放持續時間符合來源檔案的持續時間。
  • 針對視訊資料流程,請檢查畫面寬度和高度、畫面播放速率。 These values should match the values that you specified in the ASF profile that you created in the step described in the section "Create the ASF Profile Object".
  • 針對音訊資料流程,位元速率必須接近您在目標媒體類型上指定的值。
  • 在 Windows 媒體播放機 中開啟檔案,並檢查編碼品質。
  • 在 ASFViewer 中開啟 ASF 檔案,以查看 ASF 檔案的結構。 您可以從此 Microsoft 網站下載此工具。

常見的錯誤碼和偵錯提示

下列清單描述您可能會收到的常見錯誤碼和偵錯提示。

  • IMFSourceResolver::CreateObjectFromURL 的呼叫會停止應用程式。

    請確定您已呼叫 MFStartup來初始化 Media Foundation 平臺。 此函式會在內部設定啟動非同步作業的所有方法所使用的非同步平臺,例如 IMFSourceResolver::CreateObjectFromURL

  • IMFSourceResolver::CreateObjectFromURL 會傳回 HRESULT 0x80070002:「系統找不到指定的檔案。

    請確定使用者在第一個引數中指定的輸入檔案名存在。

  • HRESULT 0x80070020「進程無法存取檔案,因為它正由另一個進程使用。 "

    請確定系統中其他資源目前未使用輸入和輸出檔案。

  • 呼叫 IMFTransform 方法會傳回MF_E_INVALIDMEDIATYPE。

    請確定下列條件成立:

    • 您指定的輸入類型或輸出類型與編碼器支援的媒體類型相容。
    • 您指定的媒體類型已完成。 若要讓媒體類型完成,請參閱本教學課程的和一節中的必要屬性。
    • 請確定您已在新增編解碼器私人資料的部分媒體類型上設定目標位元速率。
  • 媒體會話會傳回事件狀態中的MF_E_UNSUPPORTED_D3D_TYPE。

    當來源的媒體類型指出Windows媒體視訊編碼器不支援的混合交錯模式時,就會傳回此錯誤。 如果您的壓縮視訊媒體類型設定為使用漸進模式,管線必須使用反交錯轉換。 因為管線找不到這個錯誤碼) 所指出的相符 (,所以您必須手動在解碼器和編碼器節點之間插入反交錯器 (轉碼視訊處理器) 。

  • 媒體會話會傳回事件狀態中的E_INVALIDARG。

    當來源的媒體類型屬性與Windows媒體編碼器上設定的輸出媒體類型屬性不相容時,就會傳回此錯誤。

  • IWMCodecPrivateData::GetPrivateData 會傳回 HRESULT 0x80040203「嘗試評估查詢字串時發生語法錯誤」

    請確定已在編碼器 MFT 上設定輸入類型。

管線層 ASF 元件