教程:1-Pass Windows 媒体编码

编码是指将数字媒体从一种格式转换为另一种格式的过程。 例如,将 MP3 音频转换为由高级系统格式 (ASF) 规范定义的Windows媒体音频格式。

注意 ASF 规范为输出文件 (.wma 或 .wmv) 定义容器类型,该容器类型可以包含任何格式的媒体数据(压缩或未压缩)。 例如,ASF 容器(如 .wma 文件)可以包含 MP3 格式的媒体数据。 编码过程转换文件中包含的数据的实际格式。

本教程演示如何使用管道层 ASF 组件将非 DRM 保护) 输入源的明文 (内容编码为Windows媒体内容,并将数据写入新的 ASF 文件 (.wm*) 。 这些组件将用于生成部分编码拓扑,该拓扑将由媒体会话的实例控制。

在本教程中,你将创建一个控制台应用程序,该应用程序采用输入和输出文件名,并将编码模式作为参数。 输入文件可以采用压缩或未压缩的媒体格式。 有效的编码模式是“CBR” (常量比特率) 或“VBR” (可变比特率) 。 应用程序创建一个媒体源来表示输入文件名指定的源,而 ASF 文件接收器用于将源文件的编码内容存档到 ASF 文件中。 为了使方案易于实现,输出文件将只有一个音频流和一个视频流。 应用程序为音频流格式转换插入Windows媒体音频 9.1 Professional编解码器,并为视频流插入 Windows媒体视频 9 编解码器。

对于常量比特率编码,在编码会话开始之前,编码器必须知道它必须实现的目标比特率。 在本教程中,对于“CBR”模式,应用程序使用在媒体类型协商期间从编码器检索的第一个输出媒体类型的比特率作为目标比特率。 对于可变比特率编码,本教程通过设置质量级别演示了使用可变比特率的编码。 音频流在 98 的质量级别编码,视频流的质量级别为 95。

以下过程总结了使用 1 传递编码模式对 ASF 容器中的媒体内容进行编码Windows的步骤。

  1. 使用源解析程序为指定的媒体源创建媒体源。
  2. 枚举媒体源中的流。
  3. 创建 ASF 媒体接收器并添加流接收器,具体取决于需要编码的媒体源中的流。
  4. 使用所需的编码属性配置媒体接收器。
  5. 为输出文件中的流创建Windows媒体编码器。
  6. 使用媒体接收器上设置的属性配置编码器。
  7. 生成部分编码拓扑。
  8. 实例化媒体会话并在媒体会话上设置拓扑。
  9. 通过控制媒体会话并从媒体会话获取所有相关事件来"开始"菜单编码会话。
  10. 对于 VBR 编码,请从编码器获取编码属性值,并在媒体接收器上设置它们。
  11. 关闭并关闭编码会话。

本教程包含以下部分:

先决条件

本教程的假设条件如下:

设置项目

  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;
    
    

创建媒体源

为输入源创建媒体源。 Media Foundation 为各种媒体格式提供内置媒体源:MP3、MP4/3GP、AVI/WAVE。 有关 Media Foundation 支持的格式的信息,请参阅 Media Foundation 中支持的媒体格式

若要创建媒体源,请使用 源解析程序。 此对象分析为源文件指定的 URL,并创建相应的媒体源。

进行以下调用:

下面的代码示例演示一个函数 CreateMediaSource,用于为指定文件创建媒体源。

//  Create a media source from a URL.
HRESULT CreateMediaSource(PCWSTR sURL, IMFMediaSource **ppSource)
{
    MF_OBJECT_TYPE ObjectType = MF_OBJECT_INVALID;

    IMFSourceResolver* pSourceResolver = NULL;
    IUnknown* pSource = NULL;

    // Create the source resolver.
    HRESULT hr = MFCreateSourceResolver(&pSourceResolver);
    if (FAILED(hr))
    {
        goto done;
    }

    // Use the source resolver to create the media source.

    // Note: For simplicity this sample uses the synchronous method to create 
    // the media source. However, creating a media source can take a noticeable
    // amount of time, especially for a network source. For a more responsive 
    // UI, use the asynchronous BeginCreateObjectFromURL method.

    hr = pSourceResolver->CreateObjectFromURL(
        sURL,                       // URL of the source.
        MF_RESOLUTION_MEDIASOURCE,  // Create a source object.
        NULL,                       // Optional property store.
        &ObjectType,        // Receives the created object type. 
        &pSource            // Receives a pointer to the media source.
        );
    if (FAILED(hr))
    {
        goto done;
    }

    // Get the IMFMediaSource interface from the media source.
    hr = pSource->QueryInterface(IID_PPV_ARGS(ppSource));

done:
    SafeRelease(&pSourceResolver);
    SafeRelease(&pSource);
    return hr;
}

创建 ASF 文件接收器

创建 ASF 文件接收器的实例,该接收器将在编码会话结束时将编码的媒体数据存档到 ASF 文件。

在本教程中,你将为 ASF 文件接收器创建一个激活对象。 文件接收器需要以下信息才能创建所需的流接收器。

  • 要编码和写入最终文件中的流。
  • 用于对媒体内容进行编码的编码属性,例如编码类型、编码传递数和相关属性。
  • 指示媒体接收器是否应自动调整泄漏存储桶参数 (比特率和缓冲区) 大小的全局文件属性。

流信息在 ASF Profile 对象中配置,编码和全局属性在 ASF ContentInfo 对象管理的属性存储中设置。

有关 ASF 文件接收器的概述,请参阅 ASF 媒体接收器

创建 ASF 配置文件对象

若要使 ASF 文件接收器将编码的媒体数据写入 ASF 文件,接收器需要知道流的数量和要为其创建流接收器的流类型。 在本教程中,你将从媒体源中提取该信息并创建相应的输出流。 将输出流限制为一个音频流和一个视频流。 对于源中的每个选定流,请创建一个目标流并将其添加到配置文件。

若要实现此步骤,需要以下对象。

  • ASF 配置文件
  • 媒体源的演示文稿描述符
  • 媒体源中所选流的流描述符。
  • 所选流的媒体类型。

以下步骤介绍了创建 ASF 配置文件和目标流的过程。

创建 ASF 配置文件

  1. 调用 MFCreateASFProfile 以创建空配置文件对象。

  2. 调用 IMFMediaSource::CreatePresentationDescriptor ,为本教程的“创建媒体源”部分中所述的步骤中创建的媒体源创建演示文稿描述符。

  3. 调用 IMFPresentationDescriptor::GetStreamDescriptorCount 以获取媒体源中的流数。

  4. 为媒体源中的每个流调用 IMFPresentationDescriptor::GetStreamDescriptorByIndex ,获取流的流描述符。

  5. 调用 IMFStreamDescriptor::GetMediaTypeHandler ,后跟 IMFMediaTypeHandler::GetMediaTypeByIndex 并获取流的第一个媒体类型。

    注意 为了避免复杂的调用,假设每个流只存在一个媒体类型,并选择流的第一个媒体类型。 对于复杂流,需要从媒体类型处理程序枚举每个媒体类型,然后选择要编码的媒体类型。

  6. 调用 IIMFMediaType::GetMajorType 以获取流的主要类型,以确定流是否包含音频或视频。

  7. 根据流的主要类型,创建目标媒体类型。 这些媒体类型将保存编码器在编码会话期间将使用的格式信息。 本教程的以下部分介绍了创建目标媒体类型的过程。

    • 创建压缩的音频媒体类型
    • 创建压缩的视频媒体类型
  8. 根据目标媒体类型创建流,根据应用程序的要求配置流,并将流添加到配置文件。 有关详细信息,请参阅 将流信息添加到 ASF 文件接收器

    1. 调用 IMFASFProfile::CreateStream 并传递目标媒体类型以创建输出流。 该方法检索流对象的 IMFASFStreamConfig 接口。
    2. 配置流。
    3. 使用 ASF ContentInfo 对象添加流级别编码属性。 有关此步骤的详细信息,请参阅本教程中的“创建 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,Media Foundation 提供了一个新函数 MFTranscodeGetAudioOutputAvailableTypes,用于检索兼容音频类型的列表。 此函数仅获取 CBR 编码的媒体类型。

完整的音频类型必须设置以下属性:

下面的代码示例通过从 Windows Media Audio 编码器获取兼容类型来创建压缩的音频类型。 SetEncodingProperties 的实现显示在本教程的“创建 ASF ContentInfo 对象”部分中。

//-------------------------------------------------------------------
//  GetOutputTypeFromWMAEncoder
//  Gets a compatible output type from the Windows Media audio encoder.
//
//  ppAudioType: Receives a pointer to the media type.
//-------------------------------------------------------------------

HRESULT GetOutputTypeFromWMAEncoder(IMFMediaType** ppAudioType)
{
    if (!ppAudioType)
    {
        return E_POINTER;
    }

    IMFTransform* pMFT = NULL;
    IMFMediaType* pType = NULL;
    IPropertyStore* pProps = NULL;

    //We need to find a suitable output media type
    //We need to create the encoder to get the available output types
    //and discard the instance.

    CLSID *pMFTCLSIDs = NULL;   // Pointer to an array of CLISDs. 
    UINT32 cCLSID = 0;            // Size of the array.

    MFT_REGISTER_TYPE_INFO tinfo;
    
    tinfo.guidMajorType = MFMediaType_Audio;
    tinfo.guidSubtype = MFAudioFormat_WMAudioV9;

    // Look for an encoder.
    HRESULT hr = MFTEnum(
        MFT_CATEGORY_AUDIO_ENCODER,
        0,                  // Reserved
        NULL,                // Input type to match. None.
        &tinfo,             // WMV encoded type.
        NULL,               // Attributes to match. (None.)
        &pMFTCLSIDs,        // Receives a pointer to an array of CLSIDs.
        &cCLSID             // Receives the size of the array.
        );
    if (FAILED(hr))
    {
        goto done;
    }

    // MFTEnum can return zero matches.
    if (cCLSID == 0)
    {
        hr = MF_E_TOPO_CODEC_NOT_FOUND;
        goto done;
    }
    else
    {
        // Create the MFT decoder
        hr = CoCreateInstance(pMFTCLSIDs[0], NULL,
            CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pMFT));

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

    }

    // Get the encoder's property store

    hr = pMFT->QueryInterface(IID_PPV_ARGS(&pProps));
    if (FAILED(hr))
    {
        goto done;
    }
    
    //Set encoding properties
    hr = SetEncodingProperties(MFMediaType_Audio, pProps);
    if (FAILED(hr))
    {
        goto done;
    }

    //Get the first output type
    //You can loop through the available output types to choose 
    //the one that meets your target requirements
    hr = pMFT->GetOutputAvailableType(0, 0, &pType);
    if (FAILED(hr))
    {
        goto done;
    }

    //Return to the caller
    *ppAudioType = pType;
    (*ppAudioType)->AddRef();
    
done:
    SafeRelease(&pProps);
    SafeRelease(&pType);
    SafeRelease(&pMFT);
    CoTaskMemFree(pMFTCLSIDs);
    return hr;
}

创建压缩的视频媒体类型

如果要在输出文件中包括视频流,请创建完全编码的视频类型。 完整的媒体类型必须包括所需的比特率和编解码器专用数据。

可通过两种方式创建完整的视频媒体类型。

  • 创建空媒体类型,并从源视频类型复制媒体类型属性,并使用 GUID 常量MFVideoFormat_WMV3覆盖 MF_MT_SUBTYPE 属性。

    完整的视频类型必须设置以下属性:

    • MF_MT_MAJOR_TYPE
    • MF_MT_SUBTYPE
    • MF_MT_FRAME_RATE
    • MF_MT_FRAME_SIZE
    • MF_MT_INTERLACE_MODE
    • MF_MT_PIXEL_ASPECT_RATIO
    • MF_MT_AVG_BITRATE
    • MF_MT_USER_DATA

    下面的代码示例从源的视频类型创建压缩的视频类型。

    //-------------------------------------------------------------------
    //  CreateCompressedVideoType
    //  Creates an output type from source's video media type.
    //
    //    pType: A pointer to the source's video media type.
    //  ppVideoType: Receives a pointer to the media type.
    //-------------------------------------------------------------------
    
    HRESULT CreateCompressedVideoType(
            IMFMediaType* pType, 
            IMFMediaType** ppVideoType)
    {
        if (!pType)
        {
            return E_INVALIDARG;
        }
        if (!ppVideoType)
        {
            return E_POINTER;
        }
    
        HRESULT hr = S_OK;
        MFRatio fps = { 0 };
        MFRatio par = { 0 };
        UINT32 width = 0, height = 0;
        UINT32 interlace = MFVideoInterlace_Unknown;
        UINT32 fSamplesIndependent = 0;
        UINT32 cBitrate = 0;
    
        IMFMediaType* pMediaType = NULL;
    
        GUID guidMajor = GUID_NULL;
    
        hr = pType->GetMajorType(&guidMajor);
        if (FAILED(hr))
        {
            goto done;
        }
    
        if (guidMajor != MFMediaType_Video )
        {
            hr = MF_E_INVALID_FORMAT;
            goto done;
        }
    
        hr = MFCreateMediaType(&pMediaType);
        if (FAILED(hr))
        {
            goto done;
        }
    
        hr = pType->CopyAllItems(pMediaType);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Fill the missing attributes
        //1. Frame rate
        hr = MFGetAttributeRatio(pMediaType, MF_MT_FRAME_RATE, (UINT32*)&fps.Numerator, (UINT32*)&fps.Denominator);
        if (hr == MF_E_ATTRIBUTENOTFOUND)
        {
            fps.Numerator = 30000;
            fps.Denominator = 1001;
    
            hr = MFSetAttributeRatio(pMediaType, MF_MT_FRAME_RATE, fps.Numerator, fps.Denominator);
            if (FAILED(hr))
            {
                goto done;
            }
        }
    
        ////2. Frame size
        hr = MFGetAttributeSize(pMediaType, MF_MT_FRAME_SIZE, &width, &height);
        if (hr == MF_E_ATTRIBUTENOTFOUND)
        {
            width = 1280;
            height = 720;
    
            hr = MFSetAttributeSize(pMediaType, MF_MT_FRAME_SIZE, width, height);
            if (FAILED(hr))
            {
                goto done;
            }
        }
    
        ////3. Interlace mode
    
        if (FAILED(pMediaType->GetUINT32(MF_MT_INTERLACE_MODE, &interlace)))
        {
            hr = pMediaType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
            if (FAILED(hr))
            {
                goto done;
            }
    
        }
    
        ////4. Pixel aspect Ratio
        hr = MFGetAttributeRatio(pMediaType, MF_MT_PIXEL_ASPECT_RATIO, (UINT32*)&par.Numerator, (UINT32*)&par.Denominator);
        if (hr == MF_E_ATTRIBUTENOTFOUND)
        {
            par.Numerator = par.Denominator = 1;
            hr = MFSetAttributeRatio(pMediaType, MF_MT_PIXEL_ASPECT_RATIO, (UINT32)par.Numerator, (UINT32)par.Denominator);
            if (FAILED(hr))
            {
                goto done;
            }
    
        }
    
        //6. bit rate
        if (FAILED(pMediaType->GetUINT32(MF_MT_AVG_BITRATE, &cBitrate)))
        {
            hr = pMediaType->SetUINT32(MF_MT_AVG_BITRATE, 1000000);
            if (FAILED(hr))
            {
                goto done;
            }
    
        }
    
        hr = pMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_WMV3);
        if (FAILED(hr))
        {
            goto done;
        }
    
        // Return the pointer to the caller.
        *ppVideoType = pMediaType;
        (*ppVideoType)->AddRef();
    
    
    done:
        SafeRelease(&pMediaType);
        return hr;
    }
    
    
  • 通过设置编码属性,然后调用 IMFTransform::GetOutputAvailableType,从Windows媒体视频编码器获取兼容的媒体类型。 此方法返回分部类型。 通过添加以下信息,确保将分部类型转换为完整类型:

    由于 IWMCodecPrivateData::GetPrivateData 在返回编解码器专用数据之前检查比特率,因此请确保在获取编解码器专用数据之前设置比特率。

    下面的代码示例通过从 Windows Media Video 编码器获取兼容类型来创建压缩的视频类型。 它还会创建未压缩的视频类型,并将其设置为编码器的输入。 这是在 Helper 函数 CreateUncompressedVideoType 中实现的。 GetOutputTypeFromWMVEncoder 通过添加编解码器专用数据将返回的部分类型转换为完整类型。 SetEncodingProperties 的实现显示在本教程的“创建 ASF ContentInfo 对象”部分中。

    //-------------------------------------------------------------------
    //  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 多路复用器中的泄漏桶值。 有关此属性的信息,请参阅创建多路复用器对象中的“多路复用器初始化和泄漏存储桶设置”。

    下面的代码示例在文件接收器的属性存储中设置流级别属性。

        //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 创建一个空拓扑对象。

为媒体源创建源拓扑节点

在此步骤中,将为媒体源创建源拓扑节点。

若要创建此节点,需要以下引用:

有关创建源节点和代码示例的详细信息,请参阅 创建源节点

下面的代码示例通过添加源节点和所需的转换节点来创建部分拓扑。 此代码调用帮助程序函数 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媒体编码器。 应用程序必须手动添加编码器。 为此,请枚举在本教程的“创建 ASF 配置文件对象”部分中所述的步骤中创建的 ASF 配置文件中的流。 对于源中的每个流和配置文件中的相应流,请实例化所需的编码器。 对于此步骤,需要指向在本教程的“创建 ASF 文件接收器”部分中所述的步骤中创建的文件接收器的激活对象的指针。

有关通过激活对象创建编码器的概述,请参阅 使用编码器的激活对象

以下过程描述了实例化所需编码器所需的步骤。

  1. 通过在文件接收器激活时调用 IMFActivate::ActivateObject,然后通过调用 QueryInterface 从文件接收器查询 IMFASFContentInfo,获取对接收器的 ContentInfo 对象的引用。

  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 调用会导致媒体会话使用MF_TOPOSTATUS_READY标志集引发 MESessionTopologyStatus 事件。 所有事件都是异步生成的,应用程序可以同步或异步检索这些事件。 由于本教程中的应用程序是控制台应用程序,阻止用户界面线程并不关心,因此我们将同步从媒体会话获取事件。

    若要异步获取事件,应用程序必须实现 IMFAsyncCallback 接口。 有关此接口的详细信息和示例实现,请参阅 How to Play Media Files with Media Foundation 中的“处理会话事件”。

在获取媒体会话事件的事件循环中,等待 IMFMediaSession::SetTopology 完成并解析拓扑时引发的 MESessionTopologyStatus 事件。 获取 MESessionTopologyStatus 事件后,通过调用 IMFMediaSession::"开始"菜单 启动编码会话。 媒体会话在完成所有编码操作时生成 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

下面的代码示例显示了控制台应用程序的 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 配置文件对象”一节中所述的步骤中指定的值匹配。
  • 对于音频流,比特率必须接近目标媒体类型上指定的值。
  • 在Windows 媒体播放器中打开文件并检查编码的质量。
  • 在 ASFViewer 中打开 ASF 文件以查看 ASF 文件的结构。 可以从此 Microsoft 网站下载此工具。

常见错误代码和调试使用技巧

以下列表描述了可能收到的常见错误代码和调试提示。

  • IMFSourceResolver::CreateObjectFromURL 的调用会停止应用程序。

    确保已通过调用 MFStartup 初始化 Media Foundation 平台。 此函数设置在内部启动异步操作的所有方法使用的异步平台,例如 IMFSourceResolver::CreateObjectFromURL

  • IMFSourceResolver::CreateObjectFromURL 返回 HRESULT 0x80070002“系统找不到指定的文件。

    确保第一个参数中用户指定的输入文件名存在。

  • HRESULT 0x80070020“进程无法访问文件,因为它正在由另一个进程使用。 "

    确保系统中其他资源当前未使用输入和输出文件。

  • IMFTransform 方法的调用返回MF_E_INVALIDMEDIATYPE。

    请确保满足以下条件:

    • 指定的输入类型或输出类型与编码器支持的媒体类型兼容。
    • 指定的媒体类型已完成。 若要使媒体类型完成,请参阅本教程的“创建压缩音频媒体类型”和“创建压缩视频媒体类型”部分中所需的属性。
    • 请确保已在要向其添加编解码器专用数据的部分媒体类型上设置目标比特率。
  • 媒体会话在事件状态中返回MF_E_UNSUPPORTED_D3D_TYPE。

    当源的媒体类型指示Windows媒体视频编码器不支持的混合交错模式时,将返回此错误。 如果压缩的视频媒体类型设置为使用渐进模式,则管道必须使用反交错转换。 由于管道找不到此错误代码) 指示的匹配 (,因此必须在解码器和编码器节点之间手动插入反交错器 (转码视频处理器) 。

  • 媒体会话在事件状态中返回E_INVALIDARG。

    当源的媒体类型属性与 Windows Media 编码器上设置的输出媒体类型属性不兼容时,将返回此错误。

  • IWMCodecPrivateData::GetPrivateData 返回 HRESULT 0x80040203“尝试评估查询字符串的语法错误”

    请确保在编码器 MFT 上设置输入类型。

管道层 ASF 组件