共用方式為


教學課程:1-Pass Windows Media Encoding

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

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

若要建立媒體來源,請使用 Source Resolver。 此物件會分析為來源檔案指定的 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來取得媒體類型。 藉由迴圈查看所有可用的類型、取得每個媒體類型的屬性,以及選取最接近您需求的類型,以取得必要的輸出類型。 在本教學課程中,從編碼器支援的輸出媒體類型清單中取得第一個可用的類型。

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

    因為 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 物件中的「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 內容資訊來創建 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 Media 編碼器和 ASF 檔案接收器建立拓撲節點,以構建部分編碼拓撲。 新增拓撲節點之後,您將連接來源、轉換和接收節點。 新增拓撲節點之前,您必須呼叫 MFCreateTopology來建立空的拓撲物件。

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

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

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

  • 您在本教學課程的「建立媒體來源」一節中所述步驟中建立的媒體來源的指標性連結。
  • 媒體來源之簡報描述元的指標。 您可以呼叫 IMFMediaSource::CreatePresentationDescriptor ,以取得媒體來源的 IMFPresentationDescriptor 介面的參考。
  • 在本教學課程的「建立 ASF 配置檔物件」一節中,針對您已建立目標數據流的媒體來源中的每個數據流,提供指向其數據流描述的指標。

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

下列程式代碼範例會藉由新增來源節點和必要的轉換節點來建立部分拓撲。 此程式代碼會呼叫 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 來取得槽的 ContentInfo 對象的參考,然後藉由呼叫 QueryInterface從檔案槽查詢 IMFASFContentInfo

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

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

    呼叫下列方法:

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

    呼叫下列方法:

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

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

  6. 更新音頻流的漏桶參數。

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

    建立一個包含 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 介面。 如需此介面的詳細資訊和範例實作,請參閱 "如何使用 Media Foundation 播放媒體檔案" 中的「處理會話事件」。

在取得媒體會話事件的事件迴圈中,等候 MESessionTopologyStatus 事件,該事件會在 IMFMediaSession::SetTopology 完成並解析拓撲時引發。 取得 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
Quality-Based 變數比特率編碼 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 Media Player 中開啟檔案,並檢查編碼品質。
  • 在 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 組件