教學課程:1-Pass Windows Media Encoding

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

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

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

在本教學課程中,您將建立採用輸入和輸出檔名的控制台應用程式,並將編碼模式當做自變數。 輸入檔案可以採用壓縮或未壓縮的媒體格式。 有效的編碼模式為 “CBR” (常數比特率) 或 “VBR” (可變比特率)。 應用程式會建立媒體來源來代表輸入檔名所指定的來源,而 ASF 檔案接收會將來源檔案的編碼內容封存到 ASF 檔案中。 為了讓案例簡單實作,輸出檔案只會有一個音訊串流和一個視訊串流。 應用程式會針對音訊串流格式轉換插入 Windows Media Audio 9.1 Professional 編解碼器,並插入視訊數據流的 Windows Media Video 9 編解碼器。

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

下列程式摘要說明使用 1 次編碼模式在 ASF 容器中編碼 Windows Media 內容的步驟。

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

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

必要條件

本教學課程假設下列專案:

設定專案

  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. 宣告 保管庫 Release 函式。

    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。 如需媒體基礎所支援之格式的資訊,請參閱 媒體基礎中支援的媒體格式。

若要建立媒體來源,請使用 來源解析程式。 此物件會分析為來源檔案指定的 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 物件新增數據流層級編碼屬性。 如需此步驟的詳細資訊,請參閱本教學課程中的<建立 ASF ContentInfo 物件>一節。
    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,媒體基礎提供新的函 式 MFTranscodeGetAudioOutputAvailableTypes ,可擷取相容音頻類型的清單。 此函式只會取得 CBR 編碼的媒體類型。

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

下列程式代碼範例會從 Windows 媒體音訊編碼器取得相容的類型,以建立壓縮的音訊類型。 本教學課程的<建立 ASF ContentInfo 物件>一節會顯示 SetEncodingProperties 的實作。

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

    • 在 MF_MT_AVG_BITRATE 屬性中設定視訊比特率。
    • 藉由設定 MF_MT_USER_DATA 屬性來新增編解碼器私用數據。 如需詳細指示,請參閱設定WMV編碼器中的

    因為 IWMCodecPrivateData::GetPrivateData 會先檢查比特率,再傳回編解碼器私用數據,請務必先設定比特率,再取得編解碼器私用數據。

    下列程式代碼範例會從 Windows Media Video 編碼器取得相容的類型,以建立壓縮的視訊類型。 它也會建立未壓縮的視訊類型,並將其設定為編碼器的輸入。 這會在 Helper 函式 CreateUncompressedVideoType 中實作。 GetOutputTypeFromWMVEncoder 藉由新增編解碼器私用數據,將傳回的部分類型轉換成完整類型。 本教學課程的<建立 ASF ContentInfo 物件>一節會顯示 SetEncodingProperties 的實作。

    //-------------------------------------------------------------------
    //  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 多任務器中的流失值區值。 如需此屬性的相關信息,請參閱建立 Multiplexer 物件中的<多任務器初始化和流失貯體 設定>。

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

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

    注意

    您也可以在檔案接收的全域層級設定其他屬性。 如需詳細資訊,請參閱在 ContentInfo 物件中設定屬性中的<使用編碼器設定 ContentInfo 物件 設定>。

     

您將使用設定的 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;
}

具現化必要的編碼器並建立轉換節點

Media Foundation 管線不會針對它必須編碼的數據流自動插入必要的 Windows Media 編碼器。 應用程式必須手動新增編碼器。 若要這樣做,請列舉您在本教學課程的<建立 ASF 配置檔物件>一節中所述的步驟中所建立的 ASF 配置檔中的數據流。 針對來源中的每個數據流和配置檔中的對應數據流,具現化所需的編碼器。 針對此步驟,您需要在本教學課程的<建立 ASF 檔案接收>一節中所述的步驟中所建立之檔案接收的啟用物件指標。

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

下列程序說明具現化必要編碼器所需的步驟。

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

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

  3. 列舉配置檔中的數據流。 為此,您將需要數據流計數和每個數據流的 IMFASFStreamConfig 介面參考。

    呼叫下列方法:

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

    呼叫下列方法:

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

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

    • 上一個步驟中 IMFASFStreamConfig::GetMediaType擷取之數據流媒體類型的指標。
    • IMFASFContentInfo::GetEncodingConfigurationPropertyStore 所擷取之數據流編碼屬性存放區的指標。 藉由將指標傳遞至屬性存放區,就會在編碼器 MFT 上複製檔案接收中設定的數據流屬性。
  6. 更新音訊數據流的流失貯體參數。

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

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

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

在步驟 5 中建立的啟用對象必須新增至拓撲作為轉換拓撲節點。 如需詳細資訊和程式代碼範例,請參閱建立轉換節點中的 <從啟用物件建立轉換節點>。

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

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

  • 您在本教學課程的<建立 ASF 檔案接收>一節中所述的步驟中建立之啟用物件的指標。
  • 用來識別新增至檔案接收之數據流接收的數據流編號。 數據流編號符合數據流建立期間所設定的數據流標識碼。

如需建立輸出節點和程式碼範例的詳細資訊,請參閱建立輸出節點中的 <從啟用物件建立輸出節點>。

如果您未針對檔案接收使用啟用物件,則必須列舉 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 介面。 如需此介面的詳細資訊和範例實作,請參閱如何使用媒體基礎播放媒體檔案中的

在取得媒體會話事件的事件迴圈中,等候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,播放持續時間符合來源檔案的持續時間。
  • 針對視訊串流,請檢查畫面寬度和高度、幀速率。 這些值應該符合您在 ASF 配置檔中指定的值,而您在<建立 ASF 配置檔物件>一節中所述的步驟中建立的值。
  • 針對音訊數據流,比特率必須接近您在目標媒體類型上指定的值。
  • 在 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 Media Video 編碼器不支援的混合交錯模式時,就會傳回此錯誤。 如果您的壓縮視訊媒體類型設定為使用漸進模式,管線必須使用反交錯轉換。 因為管線找不到相符專案(以這個錯誤碼表示),您必須手動在譯碼器和編碼器節點之間插入反交錯器(轉碼視訊處理器)。

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

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

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

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

管線層 ASF 元件