教程: 1-通过 Windows 媒体编码

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

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

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

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

对于常量比特率编码,在编码会话开始之前,编码器必须知道其必须达到的目标比特率。 在本教程中,对于 "CBR" 模式,应用程序将使用比特率,在媒体类型协商期间从编码器检索到的比特率为目标比特率。 对于可变比特率编码,本教程演示如何通过设置质量级别来使用可变比特率编码。 音频流在质量级别为98,视频流编码为95的质量级别。

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

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

本教程包含以下部分:

先决条件

本教程的假设条件如下:

设置 Project

  1. 在源文件中包含以下标头:

    #include <new>
    #include <mfidl.h>            // Media Foundation interfaces
    #include <mfapi.h>            // Media Foundation platform APIs
    #include <mferror.h>        // Media Foundation error codes
    #include <wmcontainer.h>    // ASF-specific components
    #include <wmcodecdsp.h>        // Windows Media DSP interfaces
    #include <Dmo.h>            // DMO objects
    #include <uuids.h>            // Definition for FORMAT_VideoInfo
    #include <propvarutil.h>
    
    
  2. 链接到以下库文件:

    // The required link libraries 
    #pragma comment(lib, "mfplat")
    #pragma comment(lib, "mf")
    #pragma comment(lib, "mfuuid")
    #pragma comment(lib, "msdmo")
    #pragma comment(lib, "strmiids")
    #pragma comment(lib, "propsys")
    
    
  3. 声明 SafeRelease 函数。

    template <class T> void SafeRelease(T **ppT)
    {
        if (*ppT)
        {
            (*ppT)->Release();
            *ppT = NULL;
        }
    }
    
  4. 声明编码 _ 模式枚举,以定义 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、3GP/、AVI/波纹。 有关媒体基础支持的格式的信息,请参阅 媒体基础支持的媒体格式

若要创建媒体源,请使用 源解析程序。 此对象将分析为源文件指定的 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 文件接收器创建激活对象。 文件接收器需要以下信息才能创建所需的流接收器。

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

流信息在 ASF 配置文件对象中进行配置,并且编码和全局属性设置在由 ASF ContentInfo 对象管理的属性存储中。

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

创建 ASF 配置文件对象

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

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

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

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

创建 ASF 配置文件

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

  2. 调用 THISMEDIASource::CreatePresentationDescriptor, 为在本教程的"创建媒体源"部分所述的步骤中创建的媒体源创建演示描述符。

  3. 调用"开始""数据"" 描述符::GetStreamDescriptorCount", 获取媒体源中的流数。

  4. 媒体源中每个 流调用"10000001",获取流的流描述符。

  5. 调用 以下代码::GETMediaTypeHandler, 后跟 "开始""MEDIATypeHandler::GetMediaTypeByIndex", 并获取流的第一个媒体类型。

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

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

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

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

    1. 调用 "OASFProfile::CreateStream" 并传递目标媒体类型以创建输出流。 方法检索流对象的"市/县""流配置"接口。
    2. 配置流。
    3. 使用 ASF ContentInfo 对象添加流级别编码属性。 有关此步骤的信息,请参阅本教程中的"创建 ASF ContentInfo 对象"部分。
    4. 调用 "OASFProfile::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 Media 音频编码器兼容,请实例化编码器 MFT,设置编码属性,然后通过调用 "格式::GetOutputAvailableType"获取媒体类型。 通过循环访问所有可用类型、获取每种媒体类型的属性并选择最符合要求的类型,获取所需的输出类型。 在本教程中,从编码器支持的输出媒体类型列表中获取第一个可用类型。

注意 对于 Windows 7,媒体基础提供了一个新函数 MFTranscodeGetAudioOutputAvailableTypes,用于检索兼容音频类型的列表。 此函数仅获取 CBR 编码的媒体类型。

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

下面的代码示例通过从媒体音频编码器获取兼容类型来创建Windows音频类型。 本教程的"创建 ASF ContentInfo 对象"部分显示了 SetEncodingProperties 的实现。

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

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

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

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

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

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

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

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

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

    }

    // Get the encoder's property store

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

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

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

创建压缩视频媒体类型

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

有两种方法可以创建完整的视频媒体类型。

  • 创建一个空媒体类型,然后从源视频类型复制媒体类型属性,然后使用 GUID 常量 MFVideoFormat WMV3 覆盖 MF _ MT _ SUBTYPE _ 属性。

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

    • MF _ MT _ 主要 _ 类型
    • MF _ MT _ 子类型
    • MF _ MT _ 帧 _ 速率
    • MF _ MT _ 帧 _ 大小
    • MF _ MT _ 交错 _ 模式
    • MF _ MT _ 像素 _ 纵 _ 横比
    • MF _ MT _ AVG _ 比特率
    • MF _ MT _ 用户 _ 数据

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

    //-------------------------------------------------------------------
    //  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;
    }
    
    
  • 通过设置编码属性,然后调用 "WINDOWS::GetOutputAvailableType",从媒体视频编码器获取兼容的媒体类型。 此方法返回分部类型。 通过添加以下信息,确保将分部类型转换为完整类型:

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

    下面的代码示例通过从媒体视频编码器获取兼容类型来创建Windows类型。 它还创建未压缩的视频类型,并设置它作为编码器的输入。 这是在帮助程序函数 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. 调用 UFASFContentInfo::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. 调用 UFASFContentInfo::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 对象,请通过调用文件接收器上的 "FPMediaSink::QueryInterface", 获取 对FPASFContentInfo接口的引用。 然后,必须按本部分中所述配置 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创建空拓扑对象。

为媒体源创建源拓扑节点

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

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

  • 指向在本教程的"创建媒体源"部分中所述的步骤中创建的媒体源的指针。
  • 指向媒体源的演示描述符的指针。 可以通过调用"媒体源 ::CreatePresentationDescriptor"获取对媒体源的"开始"接口的引用。
  • 指向在本教程的"创建 ASF 配置文件对象"部分中所述的步骤中创建目标流的媒体源中每个流的流描述符的指针。

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

下面的代码示例通过添加源节点和所需的转换节点来创建部分拓扑。 此代码调用帮助程序函数 AddSourceNode 和 AddTransformOutputNodes。 本教程稍后将介绍这些函数。

//-------------------------------------------------------------------
//  BuildPartialTopology
//  Create a partial encoding topology by adding the source and the sink.
//
//  pSource:  A pointer to the media source to enumerate the source streams.
//  pSinkActivate: A pointer to the activation object for ASF file sink.
//  ppTopology:  Receives a pointer to the topology.
//-------------------------------------------------------------------

HRESULT BuildPartialTopology(
       IMFMediaSource *pSource, 
       IMFActivate* pSinkActivate,
       IMFTopology** ppTopology)
{
    if (!pSource || !pSinkActivate)
    {
        return E_INVALIDARG;
    }
    if (!ppTopology)
    {
        return E_POINTER;
    }

    HRESULT hr = S_OK;

    IMFPresentationDescriptor* pPD = NULL;
    IMFStreamDescriptor *pStreamDesc = NULL;
    IMFMediaTypeHandler* pMediaTypeHandler = NULL;
    IMFMediaType* pSrcType = NULL;

    IMFTopology* pTopology = NULL;
    IMFTopologyNode* pSrcNode = NULL;
    IMFTopologyNode* pEncoderNode = NULL;
    IMFTopologyNode* pOutputNode = NULL;


    DWORD cElems = 0;
    DWORD dwSrcStream = 0;
    DWORD StreamID = 0;
    GUID guidMajor = GUID_NULL;
    BOOL fSelected = FALSE;


    //Create the topology that represents the encoding pipeline
    hr = MFCreateTopology (&pTopology);
    if (FAILED(hr))
    {
        goto done;
    }

        
    hr = pSource->CreatePresentationDescriptor(&pPD);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pPD->GetStreamDescriptorCount(&dwSrcStream);
    if (FAILED(hr))
    {
        goto done;
    }

    for (DWORD iStream = 0; iStream < dwSrcStream; iStream++)
    {
        hr = pPD->GetStreamDescriptorByIndex(
            iStream, &fSelected, &pStreamDesc);
        if (FAILED(hr))
        {
            goto done;
        }

        if (!fSelected)
        {
            continue;
        }

        hr = AddSourceNode(pTopology, pSource, pPD, pStreamDesc, &pSrcNode);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pStreamDesc->GetMediaTypeHandler (&pMediaTypeHandler);
        if (FAILED(hr))
        {
            goto done;
        }
        
        hr = pStreamDesc->GetStreamIdentifier(&StreamID);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pMediaTypeHandler->GetMediaTypeByIndex(0, &pSrcType);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pSrcType->GetMajorType(&guidMajor);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = AddTransformOutputNodes(pTopology, pSinkActivate, pSrcType, &pEncoderNode);
        if (FAILED(hr))
        {
            goto done;
        }

        //now we have the transform node, connect it to the source node
        hr = pSrcNode->ConnectOutput(0, pEncoderNode, 0);
        if (FAILED(hr))
        {
            goto done;
        }
                

        SafeRelease(&pStreamDesc);
        SafeRelease(&pMediaTypeHandler);
        SafeRelease(&pSrcType);
        SafeRelease(&pEncoderNode);
        SafeRelease(&pOutputNode);
        guidMajor = GUID_NULL;
    }

    *ppTopology = pTopology;
   (*ppTopology)->AddRef();


    wprintf_s(L"Partial Topology Built.\n");

done:
    SafeRelease(&pStreamDesc);
    SafeRelease(&pMediaTypeHandler);
    SafeRelease(&pSrcType);
    SafeRelease(&pEncoderNode);
    SafeRelease(&pOutputNode);
    SafeRelease(&pTopology);

    return hr;
}

下面的代码示例创建源拓扑节点,并将该节点添加到编码拓扑。 它采用指向以前拓扑对象的指针、用于枚举源流的媒体源、媒体源的演示描述符以及媒体源的流描述符。 调用方接收指向源拓扑节点的指针。

// Add a source node to a topology.
HRESULT AddSourceNode(
    IMFTopology *pTopology,           // Topology.
    IMFMediaSource *pSource,          // Media source.
    IMFPresentationDescriptor *pPD,   // Presentation descriptor.
    IMFStreamDescriptor *pSD,         // Stream descriptor.
    IMFTopologyNode **ppNode)         // Receives the node pointer.
{
    IMFTopologyNode *pNode = NULL;

    // Create the node.
    HRESULT hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &pNode);
    if (FAILED(hr))
    {
        goto done;
    }

    // Set the attributes.
    hr = pNode->SetUnknown(MF_TOPONODE_SOURCE, pSource);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pNode->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, pPD);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pNode->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, pSD);
    if (FAILED(hr))
    {
        goto done;
    }
    
    // Add the node to the topology.
    hr = pTopology->AddNode(pNode);
    if (FAILED(hr))
    {
        goto done;
    }

    // Return the pointer to the caller.
    *ppNode = pNode;
    (*ppNode)->AddRef();

done:
    SafeRelease(&pNode);
    return hr;
}

实例化所需的编码器并创建转换节点

该媒体基础管道不会自动为必须编码Windows流插入所需的媒体编码器。 应用程序必须手动添加编码器。 为此,请枚举在本教程的"创建 ASF 配置文件对象"部分中所述的步骤中创建的 ASF 配置文件中的流。 对于源中的每个流和配置文件中的相应流,实例化所需的编码器。 对于此步骤,需要指向在本教程的"创建 ASF 文件接收器"部分中所述的步骤中创建的文件接收器的激活对象的指针。

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

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

  1. 获取对接收器 ContentInfo 对象的引用,方法为在文件接收器上调用 "激活",然后通过调用 QueryInterface 从文件接收器中查询 "市/县:激活对象"。。

  2. 通过调用 "OASFContentInfo::GetProfile"获取与 ContentInfo 对象关联的 ASF 配置文件。

  3. 枚举配置文件中的流。 为此,需要流计数以及每个流的 "WILLASFStreamConfig" 接口的引用。

    调用以下方法:

  4. 对于每个流,从 ContentInfo 对象获取主要类型和流的编码属性。

    调用以下方法:

  5. 根据流、音频或视频的类型,通过调用 MFCreateWMAEncoderActivateMFCreateWMVEncoderActivate实例化编码器的激活对象。

    若要调用这些函数,需要以下引用:

  6. 更新音频流的泄漏存储桶参数。

    MFCreateWMAEncoderActivate在媒体音频编解码器的基础编码器 MFT 上Windows类型。 设置输出媒体类型后,编码器从输出媒体类型获取平均比特率,计算缓冲区窗口比特率,并设置将在编码会话期间使用的泄漏桶值。 可以通过查询编码器或设置自定义值来更新文件接收器中的这些值。 若要更新值,需要以下一组信息:

    创建 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. 调用 "开始"::SetTopology, 在会话上设置编码拓扑。 如果调用完成,媒体会话将评估拓扑节点并插入其他转换对象,例如将指定的压缩源转换为未压缩样本的解码器,以作为编码器的输入进行馈送。

  3. 调用 "开始""媒体会话::GetEvent", 请求媒体会话所引发事件。

    在事件循环中,将启动和关闭编码会话,具体取决于媒体会话所引发事件。 当设置了 MF TOPOSTATUS READY 标志时,将引发 MESessionTopologyStatus事件的媒体会话会导致该媒体会话的调用结果为 :MFMediaSession::SetTopology _ _ 调用。 所有事件都是异步生成的,应用程序可以同步或异步检索这些事件。 由于本教程中的应用程序是控制台应用程序,阻塞用户界面线程不是一个问题,因此我们将同步从媒体会话获取事件。

    若要以异步方式获取事件,应用程序必须实现 "开始"接口。 有关详细信息和此接口的示例实现,请参阅 How to Play Media Files with 媒体基础。

在用于获取媒体会话事件的事件循环中,等待 在完成并解析拓扑时引发 MESessionTopologyStatus事件。 收到 MESessionTopologyStatus 事件后,通过调用 "开始""媒体""会话::启动"来启动编码会话。 当所有编码操作完成时,媒体会话将生成 MEEndOfPresentation 事件。 此事件必须针对 VBR 编码进行处理,本教程的下一节"更新 VBR 编码的文件接收器上的编码属性"将对此进行讨论。

媒体会话生成 ASF 标头对象,在编码会话完成时完成文件,然后引发 MESessionClosed 事件。 必须通过对媒体会话执行适当的关闭操作来处理此事件。 若要开始关闭操作,请调用 "开始""媒体""ession::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. 调用 "C""拓扑::GetOutputNodeCollection", 从编码拓扑获取输出节点集合。
  2. 对于每个节点,请通过调用 "C1"获取指向节点中的流接收器的指针,该指针调用 "C""节点::GetObject"。 查询由 "IUnknown"指针返回的"IUnknown"接口 的"C""节点::GetObject"。
  3. 对于每个流接收器,通过调用 (:getInput) 获取编码器的下游节点。
  4. 查询节点,从 编码器节点获取"市 /省/市/市/省"指针。
  5. 查询 IPropertyStore 指针的编码器,从编码器获取编码属性存储。
  6. 查询 IPropertyStore 指针的流接收器,获取流接收器的属性存储。
  7. 调用 IPropertyStore::GetValue, 从编码器的属性存储获取所需的属性值,然后通过调用 IPropertyStore::SetValue 将它们复制到流接收器的属性存储。

下表显示了必须在视频流的流接收器上设置的 post 编码属性值。

编码类型 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 配置文件对象"部分中所述的步骤中创建的 ASF 配置文件中指定的值。
  • 对于音频流,比特率必须接近在目标媒体类型上指定的值。
  • 打开 Windows Media Player 并检查编码的质量。
  • 在 ASFViewer 中打开 ASF 文件以查看 ASF 文件的结构。 可从此 Microsoft 网站下载 此工具

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

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

  • 调用 "EMCSourceResolver::CreateObjectFromURL" 会停止应用程序。

    请确保已通过调用 MFStartup媒体基础平台。 此函数设置异步平台,该平台由启动异步操作的所有方法使用,如 在内部启动异步操作的方法,例如:WEBSOURCEResolver::CreateObjectFromURL。

  • 对于"系统找不到指定的文件",0X80070002 SOURCEResolver::CreateObjectFromURL 返回 HRESULT。

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

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

    请确保输入和输出文件当前未由系统中另一个资源使用。

  • 调用 "MFTransform" 方法会返回 MF _ E _ INVALIDMEDIATYPE。

    确保满足以下条件:

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

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

  • 媒体会话在事件状态中返回 E _ INVALIDARG。

    如果源的媒体类型属性与源媒体编码器上设置的输出媒体类型属性不兼容,则Windows此错误。

  • IWMCodecPrivateData::GetPrivateData 返回 HRESULT 0x80040203"尝试计算查询字符串时出现语法错误"

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

管道层 ASF 组件