Compartilhar via


Tutorial: Codificação do Windows Media 1-Pass

A codificação refere-se ao processo de conversão de mídia digital de um formato para outro. Por exemplo, converter áudio MP3 no formato de áudio do Windows Media, conforme definido pela especificação ASF (Advanced Systems Format).

Observação A especificação ASF define um tipo de contêiner para o arquivo de saída (.wma ou .wmv) que pode conter dados de mídia em qualquer formato, compactados ou descompactados. Por exemplo, um contêiner ASF, como um arquivo .wma, pode conter dados de mídia no formato MP3. O processo de codificação converte o formato real dos dados contidos no arquivo.

Este tutorial demonstra a codificação de fonte de entrada de conteúdo claro (não protegida por DRM) no conteúdo do Windows Media e a gravação dos dados em um novo arquivo ASF (.wm*) usando componentes ASF da camada de pipeline. Esses componentes serão usados para criar uma topologia de codificação parcial, que será controlada por uma instância da Sessão de Mídia.

Neste tutorial, você criará um aplicativo de console que usa os nomes de arquivos de entrada e saída e o modo de codificação como argumentos. O arquivo de entrada pode estar em um formato de mídia compactado ou descompactado. Os modos de codificação válidos são "CBR" (taxa de bits constante) ou "VBR" (taxa de bits variável). O aplicativo cria uma fonte de mídia para representar a fonte especificada pelo nome de arquivo de entrada e o coletor de arquivos ASF para arquivar o conteúdo codificado do arquivo de origem em um arquivo ASF. Para manter o cenário simples de implementar, o arquivo de saída terá apenas um fluxo de áudio e um fluxo de vídeo. O aplicativo insere o codec Windows Media Audio 9.1 Professional para a conversão de formato de fluxo de áudio e o codec Windows Media Video 9 para o fluxo de vídeo.

Para codificação de taxa de bits constante, antes do início da sessão de codificação, o codificador deve saber a taxa de bits de destino que ele deve atingir. Neste tutorial, para o modo "CBR", o aplicativo usa a taxa de bits disponível com o primeiro tipo de mídia de saída que é recuperado do codificador durante a negociação de tipo de mídia como a taxa de bits de destino. Para codificação de taxa de bits variável, este tutorial demonstra a codificação com taxa de bits variável definindo um nível de qualidade. Os fluxos de áudio são codificados no nível de qualidade de 98 e os fluxos de vídeo no nível de qualidade de 95.

O procedimento a seguir resume as etapas para codificar conteúdo do Windows Media em um contêiner ASF usando um modo de codificação de 1 passagem.

  1. Crie uma fonte de mídia para o especificado usando o resolvedor de origem.
  2. Enumere os fluxos na fonte de mídia.
  3. Crie o coletor de mídia ASF e adicione coletores de fluxo dependendo dos fluxos na fonte de mídia que precisam ser codificados.
  4. Configure o coletor de mídia com as propriedades de codificação necessárias.
  5. Crie os codificadores do Windows Media para os fluxos no arquivo de saída.
  6. Configure os codificadores com as propriedades definidas no coletor de mídia.
  7. Crie uma topologia de codificação parcial.
  8. Instancie a Sessão de Mídia e defina a topologia na Sessão de Mídia.
  9. Inicie a sessão de codificação controlando a Sessão de Mídia e obtendo todos os eventos relevantes da Sessão de Mídia.
  10. Para codificação VBR, obtenha os valores de propriedade de codificação do codificador e defina-os no coletor de mídia.
  11. Feche e desligue a sessão de codificação.

Este tutorial contém as seguintes seções:

Pré-requisitos

Este tutorial pressupõe o seguinte:

  • Você está familiarizado com a estrutura de arquivos ASF, os componentes ASF da camada de pipeline fornecidos pelo Media Foundation para trabalhar com objetos ASF. Esses componentes incluem os seguintes objetos:

  • Você está familiarizado com os codificadores do Windows Media e os vários tipos de codificação, particularmente Codificação de taxa de bits constante e Codificação de taxa de bits variável baseada em qualidade.

  • Você está familiarizado com as operações MFT do codificador. Especificamente, criando uma instância do codificador e definindo tipos de entrada e saída no codificador.

  • Você está familiarizado com objetos de topologia e como criar uma topologia de codificação. Para obter mais informações sobre topologias e nós de topologia, consulte Criando topologias.

  • Você está familiarizado com o modelo de evento do Media Session e as operações de controle de fluxo. Para obter informações, consulte Eventos de sessão de mídia.

Configurar o Projeto

  1. Inclua os seguintes cabeçalhos no arquivo de origem:

    #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. Link para os seguintes arquivos de biblioteca:

    // 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. Declare a função SafeRelease .

    template <class T> void SafeRelease(T **ppT)
    {
        if (*ppT)
        {
            (*ppT)->Release();
            *ppT = NULL;
        }
    }
    
  4. Declare a enumeração ENCODING_MODE para definir os tipos de codificação CBR e VBR.

    // Encoding mode
    typedef enum ENCODING_MODE {
      NONE  = 0x00000000,
      CBR   = 0x00000001,
      VBR   = 0x00000002,
    } ;
    
    
  5. Defina uma constante para a janela de buffer para um fluxo de vídeo.

    // Video buffer window
    const INT32 VIDEO_WINDOW_MSEC = 3000;
    
    

Criar a fonte de mídia

Crie uma fonte de mídia para a fonte de entrada. Media Foundation fornece built-in fontes de mídia para vários formatos de mídia: MP3, MP4/3GP, AVI / WAVE. Para obter informações sobre os formatos suportados pelo Media Foundation, consulte Formatos de mídia suportados no Media Foundation.

Para criar a fonte de mídia, use o Resolvedor de código-fonte. Esse objeto analisa a URL que foi especificada para o arquivo de origem e cria a fonte de mídia apropriada.

Faça as seguintes chamadas:

O exemplo de código a seguir mostra uma função CreateMediaSource que cria uma fonte de mídia para o arquivo especificado.

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

Criar o coletor de arquivos ASF

Crie uma instância do coletor de arquivos ASF que arquivará dados de mídia codificados em um arquivo ASF no final da sessão de codificação.

Neste tutorial, você criará um objeto de ativação para o coletor de arquivos ASF. O coletor de arquivos precisa das seguintes informações para criar os coletores de fluxo necessários.

  • Os fluxos a serem codificados e gravados no arquivo final.
  • As propriedades de codificação usadas para codificar o conteúdo de mídia, como tipo de codificação, número de passos de codificação e as propriedades relacionadas.
  • As propriedades globais do arquivo que indicam ao coletor de mídia se ele deve ajustar os parâmetros do bucket com vazamento (taxa de bits e tamanho do buffer) automaticamente.

As informações de fluxo são configuradas no objeto Perfil ASF e as propriedades globais e de codificação são definidas em um repositório de propriedades gerenciado pelo Objeto ASF ContentInfo.

Para obter uma visão geral do coletor de arquivos ASF, consulte Coletores de mídia ASF.

Criar o objeto de perfil ASF

Para que o coletor de arquivos ASF grave dados de mídia codificados em um arquivo ASF, o coletor precisa saber o número de fluxos e o tipo de fluxos para os quais criar coletores de fluxo. Neste tutorial, você extrairá essas informações da fonte de mídia e criará fluxos de saída correspondentes. Restrinja seus fluxos de saída a um fluxo de áudio e um fluxo de vídeo. Para cada fluxo selecionado na origem, crie um fluxo de destino e adicione-o ao perfil.

Para implementar esta etapa, você precisa dos seguintes objetos.

  • Perfil ASF
  • Descritor de apresentação para a fonte de mídia
  • Descritores de fluxo para os fluxos selecionados na fonte de mídia.
  • Tipos de mídia para os fluxos selecionados.

As etapas a seguir descrevem o processo de criação do perfil ASF e dos fluxos de destino.

Para criar o perfil ASF

  1. Chame MFCreateASFProfile para criar um objeto de perfil vazio.

  2. Chame IMFMediaSource::CreatePresentationDescriptor para criar o descritor de apresentação para a fonte de mídia criada na etapa descrita na seção "Criar a fonte de mídia" deste tutorial.

  3. Chame IMFPresentationDescriptor::GetStreamDescriptorCount para obter o número de fluxos na fonte de mídia.

  4. Chame IMFPresentationDescriptor::GetStreamDescriptorByIndex para cada fluxo na fonte de mídia, obtenha o descritor de fluxo do fluxo.

  5. Chame IMFStreamDescriptor::GetMediaTypeHandler seguido por IMFMediaTypeHandler::GetMediaTypeByIndex e obtenha o primeiro tipo de mídia para o fluxo.

    Observação Para evitar chamadas complexas, suponha que exista apenas um tipo de mídia por fluxo e selecione o primeiro tipo de mídia do fluxo. Para fluxos complexos, você precisa enumerar cada tipo de mídia do manipulador de tipo de mídia e escolher o tipo de mídia que deseja codificar.

  6. Chame IIMFMediaType::GetMajorType para obter o tipo principal do fluxo para determinar se o fluxo contém áudio ou vídeo.

  7. Dependendo do tipo principal do fluxo, crie tipos de mídia de destino. Esses tipos de mídia conterão informações de formato que o codificador usará durante a sessão de codificação. As seções a seguir deste tutorial descrevem o processo de criação dos tipos de mídia de destino.

    • Criar um tipo de mídia de áudio compactado
    • Criar um tipo de mídia de vídeo compactado
  8. Crie um fluxo com base no tipo de mídia de destino, configure o fluxo de acordo com os requisitos do aplicativo e adicione o fluxo ao perfil. Para obter mais informações, consulte Adicionando informações de fluxo ao coletor de arquivos ASF.

    1. Chame IMFASFProfile::CreateStream e passe o tipo de mídia de destino para criar o fluxo de saída. O método recupera a interface IMFASFStreamConfig do objeto de fluxo.
    2. Configure o fluxo.
      • Chame IMFASFStreamConfig::SetStreamNumber para atribuir um número ao fluxo. É uma configuração obrigatória.
      • Opcionalmente, configure os parâmetros de bucket com vazamento, a extensão de carga útil, a exclusão mútua em cada fluxo chamando os métodos IMFASFStreamConfig e os atributos de configuração de fluxo relevantes.
    3. Adicione as propriedades de codificação de nível de fluxo usando o objeto ASF ContentInfo. Para obter mais informações sobre essa etapa, consulte a seção "Criar o objeto ASF ContentInfo" neste tutorial.
    4. Chame IMFASFProfile::SetStream para adicionar o fluxo ao perfil.

    O exemplo de código a seguir cria um fluxo de áudio de saída.

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

    O exemplo de código a seguir cria um fluxo de vídeo de saída.

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

O exemplo de código a seguir cria fluxos de saída dependendo dos fluxos na origem.

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

Criar um tipo de mídia de áudio compactado

Se você quiser incluir um fluxo de áudio no arquivo de saída, crie um tipo de áudio especificando as características do tipo codificado definindo os atributos necessários. Para certificar-se de que o tipo de áudio é compatível com o codificador de áudio do Windows Media, instancie o codificador MFT, defina as propriedades de codificação e obtenha um tipo de mídia chamando IMFTransform::GetOutputAvailableType. Obtenha o tipo de saída necessário fazendo um loop por todos os tipos disponíveis, obtendo os atributos de cada tipo de mídia e selecionando o tipo mais próximo de suas necessidades. Neste tutorial, obtenha o primeiro tipo disponível na lista de tipos de mídia de saída suportados pelo codificador.

Observação Para o Windows 7, o Media Foundation fornece uma nova função, MFTranscodeGetAudioOutputAvailableTypes que recupera uma lista dos tipos de áudio compatíveis. Essa função só obtém tipos de mídia para codificação CBR.

Um tipo de áudio completo deve ter os seguintes atributos definidos:

O exemplo de código a seguir cria um tipo de áudio compactado obtendo um tipo compatível do codificador de áudio do Windows Media. A implementação para SetEncodingProperties é mostrada na seção "Criar o objeto ASF ContentInfo" deste tutorial.

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

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

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

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

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

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

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

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

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

    }

    // Get the encoder's property store

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

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

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

Criar um tipo de mídia de vídeo compactado

Se você quiser incluir um fluxo de vídeo no arquivo de saída, crie um tipo de vídeo codificado completamente. O tipo de mídia completo deve incluir a taxa de bits desejada e os dados privados do codec.

Há duas maneiras de criar um tipo de mídia de vídeo completo.

  • Crie um tipo de mídia vazio e copie os atributos de tipo de mídia do tipo de vídeo de origem e substitua o atributo MF_MT_SUBTYPE pela constante GUID MFVideoFormat_WMV3.

    Um tipo de vídeo completo deve ter os seguintes atributos definidos:

    • 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

    O exemplo de código a seguir cria um tipo de vídeo compactado do tipo de vídeo da origem.

    //-------------------------------------------------------------------
    //  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;
    }
    
    
  • Obtenha um tipo de mídia compatível do codificador de vídeo do Windows Media definindo as propriedades de codificação e chamando IMFTransform::GetOutputAvailableType. Esse método retorna o tipo parcial. Certifique-se de converter o tipo parcial em um tipo completo adicionando as seguintes informações:

    Como IWMCodecPrivateData::GetPrivateData verifica a taxa de bits antes de retornar os dados privados do codec, certifique-se de definir a taxa de bits antes de obter os dados privados do codec.

    O exemplo de código a seguir cria um tipo de vídeo compactado obtendo um tipo compatível do codificador de vídeo do Windows Media. Ele também cria um tipo de vídeo descompactado e o define como entrada do codificador. Isso é implementado na função auxiliar CreateUncompressedVideoType. GetOutputTypeFromWMVEncoder converte o tipo parcial retornado em um tipo completo adicionando dados privados do codec. A implementação para SetEncodingProperties é mostrada na seção "Criar o objeto ASF ContentInfo" deste tutorial.

    //-------------------------------------------------------------------
    //  GetOutputTypeFromWMVEncoder
    //  Gets a compatible output type from the Windows Media video encoder.
    //
    //  pType: A pointer to the source video stream's media type.
    //  ppVideoType: Receives a pointer to the media type.
    //-------------------------------------------------------------------
    
    HRESULT GetOutputTypeFromWMVEncoder(IMFMediaType* pType, IMFMediaType** ppVideoType)
    {
        if (!ppVideoType)
        {
            return E_POINTER;
        }
    
        IMFTransform* pMFT = NULL;
        IPropertyStore* pProps = NULL;
        IMFMediaType* pInputType = NULL;
        IMFMediaType* pOutputType = NULL;
    
        UINT32 cBitrate = 0;
    
        //We need to find a suitable output media type
        //We need to create the encoder to get the available output types
        //and discard the instance.
    
        CLSID *pMFTCLSIDs = NULL;   // Pointer to an array of CLISDs. 
        UINT32 cCLSID = 0;          // Size of the array.
    
        MFT_REGISTER_TYPE_INFO tinfo;
    
        tinfo.guidMajorType = MFMediaType_Video;
        tinfo.guidSubtype = MFVideoFormat_WMV3;
    
        // Look for an encoder.
        HRESULT hr = MFTEnum(
            MFT_CATEGORY_VIDEO_ENCODER,
            0,                  // Reserved
            NULL,               // Input type to match. None.
            &tinfo,             // WMV encoded type.
            NULL,               // Attributes to match. (None.)
            &pMFTCLSIDs,        // Receives a pointer to an array of CLSIDs.
            &cCLSID             // Receives the size of the array.
            );
        if (FAILED(hr))
        {
            goto done;
        }
    
        // MFTEnum can return zero matches.
        if (cCLSID == 0)
        {
            hr = MF_E_TOPO_CODEC_NOT_FOUND;
            goto done;
        }
        else
        {
            //Create the MFT decoder
            hr = CoCreateInstance(pMFTCLSIDs[0], NULL,
                CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pMFT));
            if (FAILED(hr))
            {
                goto done;
            }
    
        }
    
        //Get the video encoder property store
        hr = pMFT->QueryInterface(IID_PPV_ARGS(&pProps));
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Set encoding properties
        hr = SetEncodingProperties(MFMediaType_Video, pProps);
        if (FAILED(hr))
        {
            goto done;
        }
    
        hr = CreateUncompressedVideoType(pType, &pInputType);
        if (FAILED(hr))
        {
            goto done;
        }
    
        hr = pMFT->SetInputType(0, pInputType, 0);
    
        //Get the first output type
        //You can loop through the available output types to choose 
        //the one that meets your target requirements
        hr =  pMFT->GetOutputAvailableType(0, 0, &pOutputType);
        if (FAILED(hr))
        {
            goto done;
        }
    
        hr = pType->GetUINT32(MF_MT_AVG_BITRATE, &cBitrate);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Now set the bit rate
        hr = pOutputType->SetUINT32(MF_MT_AVG_BITRATE, cBitrate);
        if (FAILED(hr))
        {
            goto done;
        }
    
        hr = AddPrivateData(pMFT, pOutputType);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Return to the caller
        *ppVideoType = pOutputType;
        (*ppVideoType)->AddRef();
    
    done:
        SafeRelease(&pProps);
        SafeRelease(&pOutputType);
        SafeRelease(&pInputType);
        SafeRelease(&pMFT);
        CoTaskMemFree(pMFTCLSIDs);
        return hr;
    }
    

    O exemplo de código a seguir cria um tipo de vídeo descompactado.

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

    O exemplo de código a seguir adiciona dados particulares de codec ao tipo de mídia de vídeo especificado.

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

Criar o objeto ContentInfo do ASF

O objeto ASF ContentInfo é um componente de nível WMContainer projetado principalmente para armazenar informações de objeto de cabeçalho ASF. O coletor de arquivos ASF, implementa o objeto ContentInfo para armazenar informações (em um repositório de propriedades) que serão usadas para gravar o objeto de cabeçalho ASF do arquivo codificado. Para fazer isso, você deve criar e configurar o seguinte conjunto de informações no objeto ContentInfo antes de iniciar a sessão de codificação.

  1. Chame MFCreateASFContentInfo para criar um objeto ContentInfo vazio.

    O exemplo de código a seguir cria um objeto ContentInfo vazio.

        // Create an empty ContentInfo object
        hr = MFCreateASFContentInfo(&pContentInfo);
        if (FAILED(hr))
        {
            goto done;
        }
    
    
  2. Chame IMFASFContentInfo::GetEncodingConfigurationPropertyStore para obter o armazenamento de propriedades no nível de fluxo do coletor de arquivos. Nessa chamada, você precisa passar o número de fluxo atribuído para o fluxo ao criar o perfil ASF.

  3. Defina as Propriedades de codificação em nível de fluxo no repositório de propriedades de fluxo do coletor de arquivos. Para obter mais informações, consulte "Propriedades de codificação de fluxo" em Definindo propriedades no coletor de arquivos.

    O exemplo de código a seguir define as propriedades de nível de fluxo no repositório de propriedades do coletor de arquivos.

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

    O exemplo de código a seguir mostra a implementação para SetEncodingProperties. Essa função define as propriedades de codificação de nível de fluxo para CBR e 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. Chame IMFASFContentInfo::GetEncodingConfigurationPropertyStore para obter o repositório de propriedades global do coletor de arquivos. Nesta chamada, você precisa passar 0 no primeiro parâmetro. Para obter mais informações, consulte "Propriedades globais do coletor de arquivos" em Definindo propriedades no coletor de arquivos.

  5. Defina o MFPKEY_ASFMEDIASINK_AUTOADJUST_BITRATE como VARIANT_TRUE para garantir que os valores do bucket com vazamento no multiplexador ASF sejam ajustados corretamente. Para obter informações sobre essa propriedade, consulte "Multiplexer Initialization and Leaky Bucket Settings" em Criando o objeto Multiplexador.

    O exemplo de código a seguir define as propriedades de nível de fluxo no repositório de propriedades do coletor de arquivos.

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

    Observação

    Há outras propriedades que você pode definir no nível global para o coletor de arquivos. Para obter mais informações, consulte "Configurando o objeto ContentInfo com configurações do codificador" em Definindo propriedades no objeto ContentInfo.

     

Você usará o ASF ContentInfo configurado para criar um objeto de ativação para o coletor de arquivos ASF (descrito na próxima seção).

Se você estiver criando um coletor de arquivos fora do processo (MFCreateASFMediaSinkActivate), ou seja, usando um objeto de ativação, poderá usar o objeto ContentInfo configurado para instanciar o coletor de mídia ASF (descrito na próxima seção). Se você estiver criando um coletor de arquivos em processo (MFCreateASFMediaSink), em vez de criar o objeto ContentInfo vazio conforme descrito na etapa 1, obtenha uma referência à interface IMFASFContentInfo chamando IMFMediaSink::QueryInterface no coletor de arquivos. Em seguida, você deve configurar o objeto ContentInfo conforme descrito nesta seção.

Criar o coletor de arquivos ASF

Nesta etapa do tutorial, você usará o ASF ContentInfo configurado, que você criou na etapa anterior, para criar um objeto de ativação para o coletor de arquivos ASF chamando a função MFCreateASFMediaSinkActivate. Para obter mais informações, consulte Criando o coletor de arquivos ASF.

O exemplo de código a seguir cria o objeto de ativação para o coletor de arquivos.

    //Create the activation object for the  file sink
    hr = MFCreateASFMediaSinkActivate(sURL, pContentInfo, &pActivate);
    if (FAILED(hr))
    {
        goto done;
    }

Criar a topologia de codificação parcial

Em seguida, você criará uma topologia de codificação parcial criando nós de topologia para a fonte de mídia, os codificadores necessários do Windows Media e o coletor de arquivos ASF. Depois de adicionar os nós de topologia, você conectará os nós de origem, transformação e coletor. Antes de adicionar nós de topologia, você deve criar um objeto de topologia vazio chamando MFCreateTopology.

Criar o nó de topologia de origem para a fonte de mídia

Nesta etapa, você criará o nó da topologia de origem para a fonte de mídia.

Para criar esse nó, você precisa das seguintes referências:

  • Um ponteiro para a fonte de mídia que você criou na etapa descrita na seção "Criar a fonte de mídia" deste tutorial.
  • Um ponteiro para o descritor de apresentação para a fonte de mídia. Você pode obter uma referência à interface IMFPresentationDescriptor da fonte de mídia chamando IMFMediaSource::CreatePresentationDescriptor.
  • Um ponteiro para o descritor de fluxo para cada fluxo na fonte de mídia para a qual você criou um fluxo de destino na etapa descrita na seção "Criar o objeto de perfil ASF" deste tutorial.

Para obter mais informações sobre como criar nós de origem e exemplo de código, consulte Criando nós de código-fonte.

O exemplo de código a seguir cria uma topologia parcial adicionando o nó de origem e os nós de transformação necessários. Esse código chama as funções auxiliares AddSourceNode e AddTransformOutputNodes. Essas funções serão incluídas posteriormente neste tutorial.

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

O exemplo de código a seguir cria e adiciona o nó da topologia de origem à topologia de codificação. Ele usa ponteiros para um objeto de topologia anterior, a fonte de mídia para enumerar os fluxos de origem, o descritor de apresentação da fonte de mídia e o descritor de fluxo da fonte de mídia. O chamador recebe um ponteiro para o nó da topologia de origem.

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

Instanciar os codificadores necessários e criar os nós de transformação

O pipeline do Media Foundation não insere automaticamente os codificadores necessários do Windows Media para os fluxos que ele deve codificar. O aplicativo deve adicionar os codificadores manualmente. Para fazer isso, enumere os fluxos no perfil ASF que você criou na etapa descrita na seção "Criar o objeto de perfil ASF" deste tutorial. Para cada fluxo na origem e o fluxo correspondente no perfil, instancie os codificadores necessários. Para esta etapa, você precisa de um ponteiro para o objeto de ativação para o coletor de arquivos criado na etapa descrita na seção "Criar o coletor de arquivos ASF" deste tutorial.

Para obter uma visão geral sobre como criar codificadores por meio de objetos de ativação, consulte Usando objetos de ativação de um codificador.

O procedimento a seguir descreve as etapas necessárias para instanciar os codificadores necessários.

  1. Obtenha uma referência ao objeto ContentInfo do coletor chamando IMFActivate::ActivateObject no coletor de arquivos activate e, em seguida, consultando IMFASFContentInfo no coletor de arquivos chamando QueryInterface.

  2. Obtenha o perfil ASF associado ao objeto ContentInfo chamando IMFASFContentInfo::GetProfile.

  3. Enumere os fluxos no perfil. Para isso, você precisará da contagem de fluxo e uma referência à interface IMFASFStreamConfig de cada fluxo.

    Chame os seguintes métodos:

  4. Para cada fluxo, obtenha o tipo principal e as propriedades de codificação do fluxo do objeto ContentInfo.

    Chame os seguintes métodos:

  5. Dependendo do tipo de fluxo, áudio ou vídeo, instancie o objeto de ativação para o codificador chamando MFCreateWMAEncoderActivate ou MFCreateWMVEncoderActivate.

    Para chamar essas funções, você precisa das seguintes referências:

  6. Atualize o parâmetro de bucket com vazamento para o fluxo de áudio.

    MFCreateWMAEncoderActivate define o tipo de saída no codificador subjacente MFT para o codec de áudio do Windows Media. Depois que o tipo de mídia de saída é definido, o codificador obtém a taxa de bits média do tipo de mídia de saída, calcula a taxa de bits de raiva da janela do buffer e define os valores de bucket com vazamento que serão usados durante a sessão de codificação. Você pode atualizar esses valores no coletor de arquivos consultando o codificador ou definindo valores personalizados. Para atualizar valores, você precisa do seguinte conjunto de informações:

    Crie uma matriz de DWORDs e defina o valor na propriedade MFPKEY_ASFSTREAMSINK_CORRECTED_LEAKYBUCKET do coletor de fluxo de áudio. Se você não fornecer os valores atualizados, a Sessão de mídia os definirá adequadamente.

    Para obter mais informações, consulte O modelo de buffer de bucket com vazamento.

Os objetos de ativação criados na etapa 5 devem ser adicionados à topologia como nós de topologia de transformação. Para obter mais informações e exemplo de código, consulte "Criando um nó de transformação a partir de um objeto de ativação" em Criando nós de transformação.

O exemplo de código a seguir cria e adiciona o codificador necessário ativa. Ele leva ponteiros para um objeto de topologia criado anteriormente, o objeto de ativação do coletor de arquivos e o tipo de mídia do fluxo de origem. Ele também chama AddOutputNode (consulte o próximo exemplo de código) que cria e adiciona o nó do coletor à topologia de codificação. Ele O chamador recebe um ponteiro para o nó de topologia de origem.

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

Criar os nós de topologia de saída para o coletor de arquivos

Nesta etapa, você criará o nó de topologia de saída para o coletor de arquivos ASF.

Para criar esse nó, você precisa das seguintes referências:

  • Um ponteiro para o objeto de ativação que você criou na etapa descrita na seção "Criar o coletor de arquivos ASF" deste tutorial.
  • Os números de fluxo para identificar os coletores de fluxo adicionados ao coletor de arquivos. Os números de fluxo correspondem à identificação do fluxo que foi definida durante a criação do fluxo.

Para obter mais informações sobre como criar nós de saída e exemplo de código, consulte "Criando um nó de saída a partir de um objeto de ativação" em Criando nós de saída.

Se você não estiver usando o objeto de ativação para o coletor de arquivos, deverá enumerar os coletores de fluxo no coletor de arquivos ASF e definir cada coletor de fluxo como o nó de saída na topologia. Para obter informações sobre a enumeração do coletor de fluxo, consulte "Enumerando coletores de fluxo" em Adicionando informações de fluxo ao coletor de arquivos ASF.

O exemplo de código a seguir cria e adiciona o nó coletor à topologia de codificação. Ele leva ponteiros para um objeto de topologia criado anteriormente, o objeto de ativação do coletor de arquivos e o número de identificação do fluxo. Ele O chamador recebe um ponteiro para o nó de topologia de origem.

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

O exemplo de código a seguir enumera os coletores de fluxo para um determinado coletor de mídia.

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

Conectar os nós de origem, transformação e coletor

Nesta etapa, você conectará o nó de origem ao nó de transformação que faz referência às ativações de codificação criadas na etapa descrita na seção "Instanciar os codificadores necessários e criar os nós de transformação" deste tutorial. O nó de transformação será conectado ao nó de saída que contém o objeto de ativação para o coletor de arquivos.

Manipulando a sessão de codificação

Na etapa, você executará as seguintes etapas:

  1. Chame MFCreateMediaSession para criar uma sessão de codificação.

  2. Chame IMFMediaSession::SetTopology para definir a topologia de codificação na sessão. Se a chamada for concluída, a Sessão de Mídia avaliará os nós de topologia e insere objetos de transformação adicionais, como um decodificador que converte a fonte compactada especificada em amostras descompactadas para alimentar como entrada para o codificador.

  3. Chame IMFMediaSession::GetEvent para solicitar os eventos gerados pela Sessão de Mídia.

    No loop de eventos, você iniciará e fechará a sessão de codificação, dependendo dos eventos gerados pela Sessão de Mídia. A chamada IMFMediaSession::SetTopology resulta em Sessão de Mídia gerando o evento MESessionTopologyStatus com o sinalizador MF_TOPOSTATUS_READY definido. Todos os eventos são gerados de forma assíncrona e um aplicativo pode recuperá-los de forma síncrona ou assíncrona. Como o aplicativo neste tutorial é um aplicativo de console e bloquear threads de interface do usuário não é uma preocupação, obteremos os eventos da Sessão de Mídia de forma síncrona.

    Para obter os eventos de forma assíncrona, seu aplicativo deve implementar a interface IMFAsyncCallback. Para obter mais informações e um exemplo de implementação dessa interface, consulte "Manipulando eventos de sessão" em Como reproduzir arquivos de mídia com o Media Foundation.

No loop de eventos para obter eventos de Sessão de Mídia, aguarde o evento MESessionTopologyStatus que é gerado quando o IMFMediaSession::SetTopology é concluído e a topologia é resolvida. Ao obter o evento MESessionTopologyStatus, inicie a sessão de codificação chamando IMFMediaSession::Start. A sessão de mídia gera o evento MEEndOfPresentation quando todas as operações de codificação são concluídas. Esse evento deve ser manipulado para codificação VBR e é discutido na próxima seção "Atualizar propriedades de codificação no coletor de arquivos para codificação VBR" deste tutorial.

A sessão de mídia gera o objeto de cabeçalho ASF e finaliza o arquivo quando a sessão de codificação é concluída e, em seguida, gera o evento MESessionClosed . Esse evento deve ser manipulado executando operações de desligamento adequadas na sessão de mídia. Para iniciar as operações de desligamento, chame IMFMediaSession::Shutdown. Depois que a sessão de codificação for fechada, você não poderá definir outra topologia para codificação na mesma instância de Sessão de Mídia. Para codificar outro arquivo, a Sessão de Mídia atual deve ser fechada e liberada e a nova topologia deve ser definida em uma Sessão de Mídia recém-criada. O exemplo de código a seguir cria a sessão de mídia, define a topologia de codificação e manipula os eventos de sessão de mídia.

O exemplo de código a seguir cria a Sessão de Mídia, define a topologia de codificação e controla a sessão de codificação manipulando eventos da Sessão de Mídia.

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

Atualizar propriedades de codificação no coletor de arquivos

Certas propriedades de codificação, como a taxa de bits de codificação e os valores precisos de bucket com vazamento, não são conhecidas até que a codificação seja concluída, especialmente para codificação VBR. Para obter os valores corretos, o aplicativo deve aguardar o evento MEEndOfPresentation que indica que a sessão de codificação está concluída. Os valores do bucket com vazamento devem ser atualizados no coletor para que o objeto de cabeçalho ASF possa refletir os valores precisos.

O procedimento a seguir descreve as etapas necessárias para percorrer os nós na topologia de codificação para obter o nó do coletor de arquivos e definir as propriedades de bucket com vazamento necessárias.

Para atualizar os valores de propriedade de pós-codificação no coletor de arquivos ASF

  1. Chame IMFTopology::GetOutputNodeCollection para obter a coleção de nós de saída da topologia de codificação.
  2. Para cada nó, obtenha um ponteiro para o coletor de fluxo no nó chamando IMFTopologyNode::GetObject. Consulta para a interface IMFStreamSink no ponteiro IUnknown retornado por IMFTopologyNode::GetObject.
  3. Para cada coletor de fluxo, obtenha o nó downstream (codificador) chamando IMFTopologyNode::GetInput.
  4. Consulte o nó para obter o ponteiro IMFTransform do nó codificador.
  5. Consulte o codificador para o ponteiro IPropertyStore para obter o armazenamento de propriedades de codificação do codificador.
  6. Consulte o coletor de fluxo para o ponteiro IPropertyStore para obter o armazenamento de propriedades do coletor de fluxo.
  7. Chame IPropertyStore::GetValue para obter os valores de propriedade necessários do repositório de propriedades do codificador e copie-os para o armazenamento de propriedades do coletor de fluxo chamando IPropertyStore::SetValue.

A tabela a seguir mostra os valores de propriedade de pós-codificação que devem ser definidos no coletor de fluxo para o fluxo de vídeo.

Tipo de codificação Nome da propriedade (GetValue) Nome da propriedade (SetValue)
Codificação de taxa de bits constante MFPKEY_BAVG
MFPKEY_RAVG
MFPKEY_STAT_BAVG
MFPKEY_STAT_RAVG
Codificação de taxa de bits variável baseada em qualidade MFPKEY_BAVG
MFPKEY_RAVG
MFPKEY_BMAX
MFPKEY_RMAX
MFPKEY_STAT_BAVG
MFPKEY_STAT_RAVG
MFPKEY_STAT_BMAX
MFPKEY_STAT_RMAX

 

O exemplo de código a seguir define os valores de propriedade pós-codificação.

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

Implementar Principal

O exemplo de código a seguir mostra a função principal do aplicativo de console.

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

Testar o arquivo de saída

A lista a seguir descreve uma lista de verificação para testar o arquivo codificado. Esses valores podem ser verificados na caixa de diálogo de propriedades do arquivo que você pode exibir clicando com o botão direito do mouse no arquivo codificado e selecionando Propriedades no menu de contexto.

  • O caminho do arquivo codificado é preciso.
  • O tamanho do arquivo é superior a zero KB e a duração da reprodução corresponde à duração do arquivo de origem.
  • Para o fluxo de vídeo, verifique a largura e altura do quadro, a taxa de quadros. Esses valores devem corresponder aos valores que você especificou no perfil ASF que você criou na etapa descrita na seção "Criar o objeto de perfil ASF".
  • Para o fluxo de áudio, a taxa de bits deve ser próxima ao valor especificado no tipo de mídia de destino.
  • Abra o arquivo no Windows Media Player e verifique a qualidade da codificação.
  • Abra o arquivo ASF no ASFViewer para ver a estrutura de um arquivo ASF. Esta ferramenta pode ser baixada deste site da Microsoft.

Códigos de erro comuns e dicas de depuração

A lista a seguir descreve os códigos de erro comuns que você pode receber e as dicas de depuração.

  • A chamada para IMFSourceResolver::CreateObjectFromURL paralisa o aplicativo.

    Certifique-se de que você inicializou a plataforma Media Foundation chamando MFStartup. Essa função configura a plataforma assíncrona que é usada por todos os métodos que iniciam operações assíncronas, como IMFSourceResolver::CreateObjectFromURL, internamente.

  • IMFSourceResolver::CreateObjectFromURL retorna HRESULT 0x80070002 "O sistema não pode localizar o arquivo especificado.

    Verifique se o nome do arquivo de entrada especificado pelo usuário no primeiro argumento existe.

  • HRESULT 0x80070020 "O processo não pode acessar o arquivo porque ele está sendo usado por outro processo. "

    Certifique-se de que os arquivos de entrada e saída não estão atualmente em uso por outro recurso no sistema.

  • As chamadas para os métodos IMFTransform retornam MF_E_INVALIDMEDIATYPE.

    Certifique-se de que as seguintes condições são verdadeiras:

    • O tipo de entrada ou o tipo de saída que você especificou é compatível com os tipos de mídia que o codificador suporta.
    • Os tipos de mídia especificados estão completos. Para que os tipos de mídia sejam concluídos, consulte os atributos necessários nas seções "Criar um tipo de mídia de áudio compactado" e "Criar um tipo de mídia de vídeo compactado" deste tutorial.
    • Certifique-se de ter definido a taxa de bits de destino no tipo de mídia parcial ao qual você está adicionando os dados privados do codec.
  • A Sessão de Mídia retorna MF_E_UNSUPPORTED_D3D_TYPE no status do evento.

    Esse erro é retornado quando o tipo de mídia da origem indica um modo de entrelaçamento misto que não é suportado pelo codificador de vídeo do Windows Media. Se o tipo de mídia de vídeo compactado estiver definido para usar o modo progressivo, o pipeline deverá usar uma transformação de desentrelaçamento. Como o pipeline não consegue localizar uma correspondência (indicada por esse código de erro), você deve inserir um desentrelaçador (processador de vídeo transcodificado) entre os nós do decodificador e do codificador manualmente.

  • A Sessão de Mídia retorna E_INVALIDARG no status do evento.

    Esse erro é retornado quando os atributos de tipo de mídia da origem não são compatíveis com os atributos do tipo de mídia de saída definido no codificador do Windows Media.

  • IWMCodecPrivateData::GetPrivateData retorna HRESULT 0x80040203 "Ocorreu um erro de sintaxe ao tentar avaliar uma cadeia de caracteres de consulta"

    Certifique-se de que o tipo de entrada esteja definido no codificador MFT.

Componentes ASF da camada de pipeline