Руководство по кодировке Windows Media 1-Pass

Кодировка относится к процессу преобразования цифровых носителей из одного формата в другой. Например, преобразование звука MP3 в формат звука Windows Media в соответствии со спецификацией Advanced Systems Format (ASF).

Примечание. Спецификация ASF определяет тип контейнера для выходного файла (.wma или .wmv), который может содержать данные мультимедиа в любом формате, сжатом или несжатом. Например, контейнер ASF, например файл .wma, может содержать данные мультимедиа в формате MP3. Процесс кодирования преобразует фактический формат данных, содержащихся в файле.

В этом руководстве показано, как кодирование чистого содержимого (без защиты DRM) в содержимое Windows Media и запись данных в новом файле ASF (WM*) с помощью компонентов ASF уровня конвейера. Эти компоненты будут использоваться для создания частичной топологии кодирования, которая будет контролироваться экземпляром сеанса мультимедиа.

В этом руководстве вы создадите консольное приложение, которое принимает входные и выходные имена файлов, а также режим кодирования в качестве аргументов. Входной файл может находиться в сжатом или несжатом формате мультимедиа. Допустимые режимы кодирования : CBR (постоянная скорость бита) или VBR (переменная скорость передачи). Приложение создает источник мультимедиа для представления источника, указанного входным именем файла, и приемник файлов ASF для архивирования закодированного содержимого исходного файла в ASF-файл. Чтобы обеспечить простую реализацию сценария, выходной файл будет иметь только один аудиопоток и один видеопоток. Приложение вставляет кодек Windows Media Audio 9.1 Professional для преобразования формата аудиопотока и кодека Windows Media Video 9 для видеопотока.

Для кодирования скорости константной скорости перед началом сеанса кодирования кодировщик должен знать целевую скорость битовой скорости, которую он должен достичь. В этом руководстве для режима CBR приложение использует скорость бита, доступную с первым типом выходных носителей, полученным из кодировщика во время согласования типов мультимедиа в качестве целевой скорости. Для кодирования скорости переменной скорости в этом руководстве демонстрируется кодировка с переменной скоростью битов, задав уровень качества. Аудиопотоки кодируются на уровне качества 98 и видеопотоков на уровне качества 95.

В следующей процедуре приведены инструкции по кодированию содержимого Windows Media в контейнере ASF с помощью режима кодирования с 1-проходным.

  1. Создайте источник мультимедиа для указанного с помощью средства разрешения источника.
  2. Перечисляйте потоки в источнике мультимедиа.
  3. Создайте приемник мультимедиа ASF и добавьте приемники потоков в зависимости от потоков в источнике мультимедиа, которые необходимо закодировать.
  4. Настройте приемник мультимедиа с необходимыми свойствами кодирования.
  5. Создайте кодировщики Windows Media для потоков в выходном файле.
  6. Настройте кодировщики со свойствами, заданными в приемнике мультимедиа.
  7. Создание частичной топологии кодирования.
  8. Создайте экземпляр сеанса мультимедиа и задайте топологию в сеансе мультимедиа.
  9. Запустите сеанс кодирования, управляя сеансом мультимедиа и получая все соответствующие события из сеанса мультимедиа.
  10. Для кодирования VBR получите значения свойства кодирования из кодировщика и задайте их в приемнике мультимедиа.
  11. Закройте и завершите работу сеанса кодирования.

В этом руководстве содержатся следующие разделы:

Необходимые компоненты

В этом учебнике предполагается следующее:

Настройка проекта

  1. Включите следующие заголовки в исходный файл:

    #include <new>
    #include <mfidl.h>            // Media Foundation interfaces
    #include <mfapi.h>            // Media Foundation platform APIs
    #include <mferror.h>        // Media Foundation error codes
    #include <wmcontainer.h>    // ASF-specific components
    #include <wmcodecdsp.h>        // Windows Media DSP interfaces
    #include <Dmo.h>            // DMO objects
    #include <uuids.h>            // Definition for FORMAT_VideoInfo
    #include <propvarutil.h>
    
    
  2. Ссылка на следующие файлы библиотеки:

    // The required link libraries 
    #pragma comment(lib, "mfplat")
    #pragma comment(lib, "mf")
    #pragma comment(lib, "mfuuid")
    #pragma comment(lib, "msdmo")
    #pragma comment(lib, "strmiids")
    #pragma comment(lib, "propsys")
    
    
  3. Объявите функцию Сейф Release.

    template <class T> void SafeRelease(T **ppT)
    {
        if (*ppT)
        {
            (*ppT)->Release();
            *ppT = NULL;
        }
    }
    
  4. Объявите перечисление ENCODING_MODE для определения типов кодировки CBR и VBR.

    // Encoding mode
    typedef enum ENCODING_MODE {
      NONE  = 0x00000000,
      CBR   = 0x00000001,
      VBR   = 0x00000002,
    } ;
    
    
  5. Определите константу для буферного окна для видеопотока.

    // Video buffer window
    const INT32 VIDEO_WINDOW_MSEC = 3000;
    
    

Создание источника мультимедиа

Создайте источник мультимедиа для источника входных данных. Media Foundation предоставляет встроенные источники мультимедиа для различных форматов мультимедиа: MP3, MP4/3GP, AVI/WAVE. Сведения о форматах, поддерживаемых Media Foundation, см. в разделе "Поддерживаемые форматы мультимедиа" в Media Foundation.

Чтобы создать источник мультимедиа, используйте сопоставитель источника. Этот объект анализирует URL-адрес, указанный для исходного файла, и создает соответствующий источник мультимедиа.

Выполните следующие вызовы:

В следующем примере кода показана функция CreateMediaSource, которая создает источник мультимедиа для указанного файла.

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

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

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

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

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

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

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

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

Создание приемника файлов ASF

Создайте экземпляр приемника файлов ASF, который будет архивировать закодированные данные мультимедиа в ФАЙЛ ASF в конце сеанса кодирования.

В этом руководстве вы создадите объект активации для приемника файлов ASF. Приемник файлов должен иметь следующие сведения, чтобы создать необходимые приемники потоков.

  • Потоки, которые необходимо закодировать и записать в окончательный файл.
  • Свойства кодирования, используемые для кодирования содержимого мультимедиа, такие как тип кодирования, количество проходов кодирования и связанные свойства.
  • Глобальные свойства файлов, указывающие приемнику мультимедиа, следует ли автоматически настраивать параметры утечки контейнера (скорость и размер буфера).

Сведения о потоке настраиваются в объекте профиля ASF, а кодировка и глобальные свойства задаются в хранилище свойств, управляемом объектом ASF ContentInfo.

Общие сведения о приемнике файлов ASF см. в статье ASF Media Sinks.

Создание объекта профиля ASF

Для приемника ФАЙЛОВ ASF для записи закодированных данных мультимедиа в ASF-файл приемник должен знать количество потоков и тип потоков, для которых создаются приемники потоков. В этом руководстве вы извлеките эти сведения из источника мультимедиа и создадите соответствующие выходные потоки. Ограничьте выходные потоки одним звуковым потоком и одним видеопотоком. Для каждого выбранного потока в источнике создайте целевой поток и добавьте его в профиль.

Чтобы реализовать этот шаг, вам потребуются следующие объекты.

  • Профиль ASF
  • Дескриптор презентации для источника мультимедиа
  • Дескрипторы потоков для выбранных потоков в источнике мультимедиа.
  • Типы мультимедиа для выбранных потоков.

Ниже описан процесс создания профиля ASF и целевых потоков.

Создание профиля ASF

  1. Вызовите MFCreateASFProfile , чтобы создать пустой объект профиля.

  2. Вызовите МВФMediaSource::CreatePresentationDescriptor, чтобы создать дескриптор презентации для источника мультимедиа, созданного на шаге, описанном в разделе "Создание источника мультимедиа" этого руководства.

  3. ВызовИТЕ МВФPresentationDescriptor::GetStreamDescriptorCount , чтобы получить количество потоков в источнике мультимедиа.

  4. Вызов МВФPresentationDescriptor::GetStreamDescriptorByIndex для каждого потока в источнике мультимедиа, получите дескриптор потока.

  5. Вызов МВФStreamDescriptor::GetMediaTypeHandler, а затем МВФMediaTypeHandler::GetMediaTypeByIndex и получение первого типа мультимедиа для потока.

    Обратите внимание , что избежать сложных вызовов, предположим, что существует только один тип мультимедиа на поток и выберите первый тип носителя потока. Для сложных потоков необходимо перечислить каждый тип мультимедиа из обработчика типов мультимедиа и выбрать тип носителя, который требуется закодировать.

  6. Вызовите IIMFMediaType::GetMajorType , чтобы получить основной тип потока, чтобы определить, содержит ли поток звук или видео.

  7. В зависимости от основного типа потока создайте целевые типы носителей. Эти типы носителей будут содержать сведения о формате, которые кодировщик будет использовать во время сеанса кодирования. В следующих разделах этого руководства описывается процесс создания целевых типов мультимедиа.

    • Создание типа сжатого аудиомедийного носителя
    • Создание типа сжатого видеомедийного носителя
  8. Создайте поток на основе целевого типа носителя, настройте поток в соответствии с требованиями приложения и добавьте поток в профиль. Дополнительные сведения см. в разделе "Добавление сведений о потоке" в приемник ФАЙЛОВ ASF.

    1. ВызовИТЕ МВФASFProfile::CreateStream и передайте целевой тип носителя, чтобы создать выходной поток. Метод извлекает интерфейс IMFASFStreamConfig объекта потока.
    2. Настройте поток.
      • Вызовите МВФASFStreamConfig::SetStreamNumber , чтобы назначить номер потоку. Этот параметр является обязательным.
      • При необходимости настройте параметры утечки контейнера, расширение полезных данных, взаимное исключение для каждого потока путем вызова методов IMFASFStreamConfig и соответствующих атрибутов конфигурации потока.
    3. Добавьте свойства кодирования уровня потока с помощью объекта ASF ContentInfo. Дополнительные сведения об этом шаге см. в разделе "Создание объекта ASF ContentInfo" в этом руководстве.
    4. Вызовите МВФASFProfile::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 кодировщика, задайте свойства кодирования и получите тип мультимедиа, вызвав МВФTransform::GetOutputAvailableType. Получите необходимый тип вывода, прокрутив все доступные типы, получив атрибуты каждого типа мультимедиа и выбрав тип, ближайший к вашим требованиям. В этом руководстве получите первый доступный тип из списка типов выходных носителей, поддерживаемых кодировщиком.

Примечание для Windows 7 Media Foundation предоставляет новую функцию MFTranscodeGetAudioOutputAvailableTypes, которая получает список совместимых типов звука. Эта функция получает только типы носителей для кодирования CBR.

Полный тип звука должен иметь следующие атрибуты:

В следующем примере кода создается сжатый тип звука, получая совместимый тип из кодировщика звука Windows Media. Реализация setEncodingProperties показана в разделе "Создание объекта ASF ContentInfo" этого руководства.

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

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

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

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

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

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

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

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

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

    }

    // Get the encoder's property store

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

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

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

Создание типа сжатого видеомедийного носителя

Если вы хотите включить видеопоток в выходной файл, создайте полный тип видео в кодировке. Полный тип носителя должен включать требуемый скорость передачи и частные данные кодека.

Существует два способа создания полного типа видеофайла.

  • Создайте пустой тип носителя и скопируйте атрибуты типа мультимедиа из исходного типа видео и перезаписать атрибут MF_MT_SUBTYPE с помощью константы GUID MFVideoFormat_WMV3.

    Полный тип видео должен иметь следующие атрибуты:

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

    В следующем примере кода создается сжатый тип видео из исходного типа видео.

    //-------------------------------------------------------------------
    //  CreateCompressedVideoType
    //  Creates an output type from source's video media type.
    //
    //    pType: A pointer to the source's video media type.
    //  ppVideoType: Receives a pointer to the media type.
    //-------------------------------------------------------------------
    
    HRESULT CreateCompressedVideoType(
            IMFMediaType* pType, 
            IMFMediaType** ppVideoType)
    {
        if (!pType)
        {
            return E_INVALIDARG;
        }
        if (!ppVideoType)
        {
            return E_POINTER;
        }
    
        HRESULT hr = S_OK;
        MFRatio fps = { 0 };
        MFRatio par = { 0 };
        UINT32 width = 0, height = 0;
        UINT32 interlace = MFVideoInterlace_Unknown;
        UINT32 fSamplesIndependent = 0;
        UINT32 cBitrate = 0;
    
        IMFMediaType* pMediaType = NULL;
    
        GUID guidMajor = GUID_NULL;
    
        hr = pType->GetMajorType(&guidMajor);
        if (FAILED(hr))
        {
            goto done;
        }
    
        if (guidMajor != MFMediaType_Video )
        {
            hr = MF_E_INVALID_FORMAT;
            goto done;
        }
    
        hr = MFCreateMediaType(&pMediaType);
        if (FAILED(hr))
        {
            goto done;
        }
    
        hr = pType->CopyAllItems(pMediaType);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Fill the missing attributes
        //1. Frame rate
        hr = MFGetAttributeRatio(pMediaType, MF_MT_FRAME_RATE, (UINT32*)&fps.Numerator, (UINT32*)&fps.Denominator);
        if (hr == MF_E_ATTRIBUTENOTFOUND)
        {
            fps.Numerator = 30000;
            fps.Denominator = 1001;
    
            hr = MFSetAttributeRatio(pMediaType, MF_MT_FRAME_RATE, fps.Numerator, fps.Denominator);
            if (FAILED(hr))
            {
                goto done;
            }
        }
    
        ////2. Frame size
        hr = MFGetAttributeSize(pMediaType, MF_MT_FRAME_SIZE, &width, &height);
        if (hr == MF_E_ATTRIBUTENOTFOUND)
        {
            width = 1280;
            height = 720;
    
            hr = MFSetAttributeSize(pMediaType, MF_MT_FRAME_SIZE, width, height);
            if (FAILED(hr))
            {
                goto done;
            }
        }
    
        ////3. Interlace mode
    
        if (FAILED(pMediaType->GetUINT32(MF_MT_INTERLACE_MODE, &interlace)))
        {
            hr = pMediaType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
            if (FAILED(hr))
            {
                goto done;
            }
    
        }
    
        ////4. Pixel aspect Ratio
        hr = MFGetAttributeRatio(pMediaType, MF_MT_PIXEL_ASPECT_RATIO, (UINT32*)&par.Numerator, (UINT32*)&par.Denominator);
        if (hr == MF_E_ATTRIBUTENOTFOUND)
        {
            par.Numerator = par.Denominator = 1;
            hr = MFSetAttributeRatio(pMediaType, MF_MT_PIXEL_ASPECT_RATIO, (UINT32)par.Numerator, (UINT32)par.Denominator);
            if (FAILED(hr))
            {
                goto done;
            }
    
        }
    
        //6. bit rate
        if (FAILED(pMediaType->GetUINT32(MF_MT_AVG_BITRATE, &cBitrate)))
        {
            hr = pMediaType->SetUINT32(MF_MT_AVG_BITRATE, 1000000);
            if (FAILED(hr))
            {
                goto done;
            }
    
        }
    
        hr = pMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_WMV3);
        if (FAILED(hr))
        {
            goto done;
        }
    
        // Return the pointer to the caller.
        *ppVideoType = pMediaType;
        (*ppVideoType)->AddRef();
    
    
    done:
        SafeRelease(&pMediaType);
        return hr;
    }
    
    
  • Получите совместимый тип мультимедиа из видеокодировщика Windows Media, задав свойства кодирования и вызвав МВФTransform::GetOutputAvailableType. Этот метод возвращает частичный тип. Убедитесь, что частичный тип преобразуется в полный тип, добавив следующие сведения:

    Так как IWMCodecPrivateData::GetPrivateData проверка скорость передачи данных перед возвратом частных данных кодека, убедитесь, что перед получением частных данных кодека задана скорость передачи данных.

    В следующем примере кода создается сжатый тип видео, получая совместимый тип из кодировщика видео Windows Media. Он также создает несжатый тип видео и задает его в качестве входных данных кодировщика. Это реализовано в вспомогательной функции CreateUncompressedVideoType. GetOutputTypeFromWMVEncoder преобразует возвращенный частичный тип в полный тип путем добавления частных данных кодека. Реализация setEncodingProperties показана в разделе "Создание объекта ASF ContentInfo" этого руководства.

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

    В следующем примере кода создается несжатый тип видео.

    //-------------------------------------------------------------------
    //  CreateUncompressedVideoType
    //  Creates an uncompressed video type.
    //
    //    pType: A pointer to the source's video media type.
    //  ppVideoType: Receives a pointer to the media type.
    //-------------------------------------------------------------------
    
    HRESULT CreateUncompressedVideoType(IMFMediaType* pType, IMFMediaType** ppMediaType)
    {
        if (!pType)
        {
            return E_INVALIDARG;
        }
        if (!ppMediaType)
        {
            return E_POINTER;
        }
    
        MFRatio fps = { 0 };
        MFRatio par = { 0 };
        UINT32 width = 0, height = 0;
        UINT32 interlace = MFVideoInterlace_Unknown;
        UINT32 cBitrate = 0;
    
        IMFMediaType* pMediaType = NULL;
    
        GUID guidMajor = GUID_NULL;
    
        HRESULT hr = pType->GetMajorType(&guidMajor);
        if (FAILED(hr))
        {
            goto done;
        }
    
        if (guidMajor != MFMediaType_Video )
        {
            hr = MF_E_INVALID_FORMAT;
            goto done;
        }
    
        hr = MFGetAttributeRatio(pType, MF_MT_FRAME_RATE, (UINT32*)&fps.Numerator, (UINT32*)&fps.Denominator);
        if (hr == MF_E_ATTRIBUTENOTFOUND)
        {
            fps.Numerator = 30000;
            fps.Denominator = 1001;
    
        }
        hr = MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &width, &height);
        if (hr == MF_E_ATTRIBUTENOTFOUND)
        {
            width = 1280;
            height = 720;
    
        }
    
        interlace = MFGetAttributeUINT32(pType, MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
    
        hr = MFGetAttributeRatio(pType, MF_MT_PIXEL_ASPECT_RATIO, (UINT32*)&par.Numerator, (UINT32*)&par.Denominator);
        if (FAILED(hr))
        {
            par.Numerator = par.Denominator = 1;
        }
    
        cBitrate = MFGetAttributeUINT32(pType, MF_MT_AVG_BITRATE, 1000000);
    
        hr = MFCreateMediaType(&pMediaType);
        if (FAILED(hr))
        {
            goto done;
        }
        hr = pMediaType->SetGUID(MF_MT_MAJOR_TYPE, guidMajor);
        if (FAILED(hr))
        {
            goto done;
        }
        hr = pMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32);
        if (FAILED(hr))
        {
            goto done;
        }
        hr = MFSetAttributeRatio(pMediaType, MF_MT_FRAME_RATE, fps.Numerator, fps.Denominator);
        if (FAILED(hr))
        {
            goto done;
        }
        hr = MFSetAttributeSize(pMediaType, MF_MT_FRAME_SIZE, width, height);
        if (FAILED(hr))
        {
            goto done;
        }
        hr = pMediaType->SetUINT32(MF_MT_INTERLACE_MODE, 2);
        if (FAILED(hr))
        {
            goto done;
        }
    
        hr = pMediaType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
        if (FAILED(hr))
        {
            goto done;
        }
    
        hr = pMediaType->SetUINT32(MF_MT_AVG_BITRATE, cBitrate);
        if (FAILED(hr))
        {
            goto done;
        }
    
        // Return the pointer to the caller.
        *ppMediaType = pMediaType;
        (*ppMediaType)->AddRef();
    
    
    done:
        SafeRelease(&pMediaType);
        return hr;
    }
    

    В следующем примере кода к указанному типу видеомедийного носителя добавляется частные данные кодека.

    //
    // AddPrivateData
    // Appends the private codec data to a media type.
    //
    // pMFT: The video encoder
    // pTypeOut: A video type from the encoder's type list.
    //
    // The function modifies pTypeOut by adding the codec data.
    //
    
    HRESULT AddPrivateData(IMFTransform *pMFT, IMFMediaType *pTypeOut)
    {
        HRESULT hr = S_OK;
        ULONG cbData = 0;
        BYTE *pData = NULL;
    
        IWMCodecPrivateData *pPrivData = NULL;
    
        DMO_MEDIA_TYPE mtOut = { 0 };
    
        // Convert the type to a DMO type.
        hr = MFInitAMMediaTypeFromMFMediaType(
            pTypeOut, 
            FORMAT_VideoInfo, 
            (AM_MEDIA_TYPE*)&mtOut
            );
    
        if (SUCCEEDED(hr))
        {
            hr = pMFT->QueryInterface(IID_PPV_ARGS(&pPrivData));
        }
    
        if (SUCCEEDED(hr))
        {
            hr = pPrivData->SetPartialOutputType(&mtOut);
        }
    
        //
        // Get the private codec data
        //
    
        // First get the buffer size.
        if (SUCCEEDED(hr))
        {
            hr = pPrivData->GetPrivateData(NULL, &cbData);
        }
    
        if (SUCCEEDED(hr))
        {
            pData = new BYTE[cbData];
    
            if (pData == NULL)
            {
                hr = E_OUTOFMEMORY;
            }
        }
    
        // Now get the data.
        if (SUCCEEDED(hr))
        {
            hr = pPrivData->GetPrivateData(pData, &cbData);
        }
    
        // Add the data to the media type.
        if (SUCCEEDED(hr))
        {
            hr = pTypeOut->SetBlob(MF_MT_USER_DATA, pData, cbData);
        }
    
        delete [] pData;
        MoFreeMediaType(&mtOut);
        SafeRelease(&pPrivData);
        return hr;
    }
    

Создание объекта ASF ContentInfo

Объект ASF ContentInfo — это компонент уровня WMContainer, предназначенный в основном для хранения сведений об объекте заголовка ASF. Приемник файлов ASF реализует объект ContentInfo для хранения сведений (в хранилище свойств), который будет использоваться для записи объекта заголовка ASF в кодированном файле. Для этого необходимо создать и настроить следующий набор сведений о объекте ContentInfo перед началом сеанса кодирования.

  1. Вызовите MFCreateASFContentInfo , чтобы создать пустой объект ContentInfo.

    В следующем примере кода создается пустой объект ContentInfo.

        // Create an empty ContentInfo object
        hr = MFCreateASFContentInfo(&pContentInfo);
        if (FAILED(hr))
        {
            goto done;
        }
    
    
  2. Вызовите МВФASFContentInfo::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. Вызовите МВФASFContentInfo::GetEncodingConfigurationPropertyStore , чтобы получить глобальное хранилище свойств приемника файлов. В этом вызове необходимо передать 0 в первом параметре. Дополнительные сведения см. в разделе "Свойства глобального приемника файлов" в разделе "Параметры свойств" в приемнике файлов.

  5. Задайте для MFPKEY_ASFMEDIASINK_AUTOADJUST_BITRATE значение VARIANT_TRUE, чтобы убедиться, что значения утечки контейнеров в мультиплексере ASF корректируются должным образом. Сведения об этом свойстве см. в разделе "Инициализация и утечка контейнеров Параметры" в разделе "Создание объекта Multiplexer".

    В следующем примере кода задаются свойства уровня потока в хранилище свойств приемника файлов.

        //Now we need to set global properties that will be set on the media sink
        hr = pContentInfo->GetEncodingConfigurationPropertyStore(0, &pContentInfoProps);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Auto adjust Bitrate
        var.vt = VT_BOOL;
        var.boolVal = VARIANT_TRUE;
    
        hr = pContentInfoProps->SetValue(MFPKEY_ASFMEDIASINK_AUTOADJUST_BITRATE, var);
    
        //Initialize with the profile
        hr = pContentInfo->SetProfile(pProfile);
        if (FAILED(hr))
        {
            goto done;
        }
    

    Примечание.

    Существуют другие свойства, которые можно задать на глобальном уровне для приемника файлов. Дополнительные сведения см. в разделе "Настройка объекта ContentInfo с помощью кодировщика Параметры" статьи "Настройка свойств в объекте ContentInfo".

     

Вы будете использовать настроенный ASF ContentInfo для создания объекта активации для приемника файлов ASF (описано в следующем разделе).

Если вы создаете приемник файлов вне процесса (MFCreateASFMediaSinkActivate), то есть с помощью объекта активации, можно использовать настроенный объект ContentInfo для создания экземпляра приемника мультимедиа ASF (описанного в следующем разделе). Если вы создаете приемник файлов в процессе (MFCreateASFMediaSink), вместо создания пустого объекта ContentInfo, как описано на шаге 1, получите ссылку на интерфейс МВФASFContentInfo, вызвав МВФMediaSink::QueryInterface в приемнике файлов. Затем необходимо настроить объект ContentInfo, как описано в этом разделе.

Создание приемника файлов ASF

На этом шаге руководства вы будете использовать настроенный ASF ContentInfo, созданный на предыдущем шаге, для создания объекта активации для приемника файлов ASF путем вызова функции MFCreateASFMediaSinkActivate. Дополнительные сведения см. в разделе "Создание приемника файлов ASF".

В следующем примере кода создается объект активации для приемника файлов.

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

Создание топологии частичной кодировки

Затем вы создадите частичную топологию кодирования, создав узлы топологии для источника мультимедиа, необходимые кодировщики Windows Media и приемник ФАЙЛОВ ASF. После добавления узлов топологии вы подключите исходные, преобразования и узлы приемника. Перед добавлением узлов топологии необходимо создать пустой объект топологии, вызвав MFCreateTopology.

Создание узла исходной топологии для источника мультимедиа

На этом шаге вы создадите узел исходной топологии для источника мультимедиа.

Чтобы создать этот узел, вам потребуются следующие ссылки:

  • Указатель на источник мультимедиа, созданный на шаге, описанном в разделе "Создание источника мультимедиа" этого руководства.
  • Указатель на дескриптор презентации для источника мультимедиа. Вы можете получить ссылку на интерфейс МВФPresentationDescriptor источника мультимедиа, вызвав МВФMediaSource::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;
}

Создание экземпляров обязательных кодировщиков и создание узлов преобразования

Конвейер Media Foundation не автоматически вставляет необходимые кодировщики Windows Media для потоков, которые он должен кодировать. Приложение должно вручную добавить кодировщики. Для этого перечислите потоки в профиле ASF, созданном на шаге, описанном в разделе "Создание объекта профиля ASF" этого руководства. Для каждого потока в источнике и соответствующем потоке в профиле создайте экземпляр необходимых кодировщиков. Для этого шага вам потребуется указатель на объект активации для приемника файлов, созданного на шаге, описанном в разделе "Создание приемника файлов ASF" этого руководства.

Общие сведения о создании кодировщиков с помощью объектов активации см. в разделе "Использование объектов активации кодировщика".

В следующей процедуре описаны шаги, необходимые для создания экземпляров необходимых кодировщиков.

  1. Получите ссылку на объект ContentInfo приемника путем вызова IMFActivate::ActivateObject в приемнике файлов активации, а затем запроса для МВФASFContentInfo из приемника файлов путем вызова QueryInterface.

  2. Получите профиль ASF, связанный с объектом ContentInfo, вызвав МВФASFContentInfo::GetProfile.

  3. Перечисляйте потоки в профиле. Для этого потребуется количество потоков и ссылка на интерфейс МВФASFStreamConfig каждого потока.

    Вызовите следующие методы:

  4. Для каждого потока получают основной тип и свойства кодирования потока из объекта ContentInfo.

    Вызовите следующие методы:

  5. В зависимости от типа потока, звука или видео создайте экземпляр объекта активации для кодировщика путем вызова MFCreateWMAEncoderActivate или MFCreateWMVEncoderActivate.

    Чтобы вызвать эти функции, вам потребуются следующие ссылки:

  6. Обновите параметр утечки контейнера для звукового потока.

    MFCreateWMAEncoderActivate задает тип вывода для базового кодировщика MFT для аудиокодека Windows Media. После установки типа выходного носителя кодировщик получает среднюю скорость передачи данных из выходного типа носителя, вычисляет скорость битового окна буфера и задает значения утечки контейнеров, которые будут использоваться во время сеанса кодирования. Эти значения можно обновить в приемнике файлов, запрашивая кодировщик или устанавливая пользовательские значения. Чтобы обновить значения, вам потребуется следующий набор сведений:

    • Средняя скорость битов: получение средней скорости из атрибута MF_MT_AUDIO_AVG_BYTES_PER_SECOND, заданного в типе выходного носителя, выбранном во время согласования типов мультимедиа.
    • Окно буфера: его можно получить, вызвав IWMCodecLeakyBucket::GetBufferSizeBits.
    • Начальный размер буфера: задайте значение 0.

    Создайте массив 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. ВызовИТЕ МВФMediaSession::SetTopology , чтобы задать топологию кодирования в сеансе. Если вызов завершится, сеанс мультимедиа вычисляет узлы топологии и вставляет дополнительные объекты преобразования, такие как декодатор, который преобразует указанный сжатый источник в несжатые образцы для канала в качестве входных данных в кодировщик.

  3. Вызов МВФMediaSession::GetEvent , чтобы запросить события, поднятые сеансом мультимедиа.

    В цикле событий вы запустите и закройте сеанс кодирования в зависимости от событий, вызванных сеансом мультимедиа. Вызов МВФMediaSession::SetTopology приводит к возникновению события MESessionTopologyStatus с набором флагов MF_TOPOSTATUS_READY. Все события создаются асинхронно, и приложение может получать эти события синхронно или асинхронно. Так как приложение в этом руководстве является консольным приложением и блокировкой потоков пользовательского интерфейса не является проблемой, мы будем получать события из сеанса мультимедиа синхронно.

    Чтобы получить события асинхронно, приложение должно реализовать интерфейс МВФAsyncCallback. Дополнительные сведения и пример реализации этого интерфейса см. в разделе "Обработка событий сеансов" в разделе "Воспроизведение файлов мультимедиа с помощью Media Foundation".

В цикле событий для получения событий сеанса мультимедиа дождитесь события MESessionTopologyStatus, которое возникает при завершении МВФMediaSession::SetTopology, и топология разрешается. После получения события MESessionTopologyStatus запустите сеанс кодирования, вызвав МВФMediaSession::Start. Сеанс мультимедиа создает событие MEEndOfPresentation при завершении всех операций кодирования. Это событие должно обрабатываться для кодирования VBR и рассматривается в следующем разделе "Обновление свойств кодирования в приемнике файлов для кодирования VBR" этого руководства.

Сеанс мультимедиа создает объект заголовка ASF и завершает файл после завершения сеанса кодирования, а затем вызывает событие MESessionClosed . Это событие должно обрабатываться путем выполнения соответствующих операций завершения работы в сеансе мультимедиа. Чтобы начать операции завершения работы, вызовите МВФMediaSession::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. Вызовите МВФTopology::GetOutputNodeCollection , чтобы получить коллекцию выходных узлов из топологии кодирования.
  2. Для каждого узла получите указатель на приемник потоков в узле, вызвав МВФTopologyNode::GetObject. Запрос интерфейса IMFStreamSink на указателе IUnknown, возвращенном МВФTopologyNode::GetObject.
  3. Для каждого приемника потоков получите подчиненный узел (кодировщик), вызвав МВФTopologyNode::GetInput.
  4. Запросите узел, чтобы получить указатель IMFTransform из узла кодировщика.
  5. Запросите кодировщик для указателя IPropertyStore , чтобы получить хранилище свойств кодирования из кодировщика.
  6. Запросите приемник потока для указателя IPropertyStore , чтобы получить хранилище свойств приемника потока.
  7. Вызовите IPropertyStore::GetValue, чтобы получить необходимые значения свойств из хранилища свойств кодировщика и скопировать их в хранилище свойств приемника потоков путем вызова IPropertyStore::SetValue.

В следующей таблице показаны значения свойств записи кодирования, которые должны быть заданы в приемнике потоков для видеопотока.

Тип кодирования Имя свойства (GetValue) Имя свойства (SetValue)
Кодировка скорости константной скорости MFPKEY_BAVG
MFPKEY_RAVG
MFPKEY_STAT_BAVG
MFPKEY_STAT_RAVG
Кодировка скорости скорости на основе качества MFPKEY_BAVG
MFPKEY_RAVG
MFPKEY_BMAX
MFPKEY_RMAX
MFPKEY_STAT_BAVG
MFPKEY_STAT_RAVG
MFPKEY_STAT_BMAX
MFPKEY_STAT_RMAX

 

В следующем примере кода задаются значения свойств после кодирования.

//-------------------------------------------------------------------
//  PostEncodingUpdate
//  Updates the file sink with encoding properties set on the encoder
//  during the encoding session.
    //1. Get the output nodes
    //2. For each node, get the downstream node
    //3. For the downstream node, get the MFT
    //4. Get the property store
    //5. Get the required values
    //6. Set them on the stream sink
//
//  pTopology: A pointer to the full topology retrieved from the media session.
//-------------------------------------------------------------------

HRESULT PostEncodingUpdate(IMFTopology *pTopology)
{
    if (!pTopology)
    {
        return E_INVALIDARG;
    }

    HRESULT hr = S_OK;

    IMFCollection* pOutputColl = NULL;
    IUnknown* pNodeUnk = NULL;
    IMFMediaType* pType = NULL;
    IMFTopologyNode* pNode = NULL;
    IUnknown* pSinkUnk = NULL;
    IMFStreamSink* pStreamSink = NULL;
    IMFTopologyNode* pEncoderNode = NULL;
    IUnknown* pEncoderUnk = NULL;
    IMFTransform* pEncoder = NULL;
    IPropertyStore* pStreamSinkProps = NULL;
    IPropertyStore* pEncoderProps = NULL;

    GUID guidMajorType = GUID_NULL;

    PROPVARIANT var;
    PropVariantInit( &var );

    DWORD cElements = 0;

    hr = pTopology->GetOutputNodeCollection( &pOutputColl);
    if (FAILED(hr))
    {
        goto done;
    }


    hr = pOutputColl->GetElementCount(&cElements);
    if (FAILED(hr))
    {
        goto done;
    }


    for(DWORD index = 0; index < cElements; index++)
    {
        hr = pOutputColl->GetElement(index, &pNodeUnk);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pNodeUnk->QueryInterface(IID_IMFTopologyNode, (void**)&pNode);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pNode->GetInputPrefType(0, &pType);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pType->GetMajorType( &guidMajorType );
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pNode->GetObject(&pSinkUnk);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pSinkUnk->QueryInterface(IID_IMFStreamSink, (void**)&pStreamSink);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pNode->GetInput( 0, &pEncoderNode, NULL );
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pEncoderNode->GetObject(&pEncoderUnk);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pEncoderUnk->QueryInterface(IID_IMFTransform, (void**)&pEncoder);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pStreamSink->QueryInterface(IID_IPropertyStore, (void**)&pStreamSinkProps);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pEncoder->QueryInterface(IID_IPropertyStore, (void**)&pEncoderProps);
        if (FAILED(hr))
        {
            goto done;
        }

        if( guidMajorType == MFMediaType_Video )
        {            
            hr = pEncoderProps->GetValue( MFPKEY_BAVG, &var );
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_STAT_BAVG, var );
            if (FAILED(hr))
            {
                goto done;
            }

            PropVariantClear( &var );
            hr = pEncoderProps->GetValue( MFPKEY_RAVG, &var );
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_STAT_RAVG, var);
            if (FAILED(hr))
            {
                goto done;
            }

            PropVariantClear( &var );
            hr = pEncoderProps->GetValue( MFPKEY_BMAX, &var );
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_STAT_BMAX, var);
            if (FAILED(hr))
            {
                goto done;
            }

            PropVariantClear( &var );
            hr = pEncoderProps->GetValue( MFPKEY_RMAX, &var );
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_STAT_RMAX, var);
            if (FAILED(hr))
            {
                goto done;
            }
        }
        else if( guidMajorType == MFMediaType_Audio )
        {      
            hr = pEncoderProps->GetValue( MFPKEY_STAT_BAVG, &var );
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_STAT_BAVG, var );
            if (FAILED(hr))
            {
                goto done;
            }
            
            PropVariantClear( &var );
            hr = pEncoderProps->GetValue( MFPKEY_STAT_RAVG, &var );
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_STAT_RAVG, var );
            if (FAILED(hr))
            {
                goto done;
            }
            
            PropVariantClear( &var );
            hr = pEncoderProps->GetValue( MFPKEY_STAT_BMAX, &var);
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_STAT_BMAX, var);
            if (FAILED(hr))
            {
                goto done;
            }

            PropVariantClear( &var );
            hr = pEncoderProps->GetValue( MFPKEY_STAT_RMAX, &var );
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_STAT_RMAX, var );         
            if (FAILED(hr))
            {
                goto done;
            }

            PropVariantClear( &var );
            hr = pEncoderProps->GetValue( MFPKEY_WMAENC_AVGBYTESPERSEC, &var );
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_WMAENC_AVGBYTESPERSEC, var );         
            if (FAILED(hr))
            {
                goto done;
            }
        } 
        PropVariantClear( &var ); 
   }
done:
    SafeRelease (&pOutputColl);
    SafeRelease (&pNodeUnk);
    SafeRelease (&pType);
    SafeRelease (&pNode);
    SafeRelease (&pSinkUnk);
    SafeRelease (&pStreamSink);
    SafeRelease (&pEncoderNode);
    SafeRelease (&pEncoderUnk);
    SafeRelease (&pEncoder);
    SafeRelease (&pStreamSinkProps);
    SafeRelease (&pEncoderProps);

    return hr;
   
}

Реализация main

В следующем примере кода показана основная функция консольного приложения.

int wmain(int argc, wchar_t* argv[])
{
    HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);

    if (argc != 4)
    {
        wprintf_s(L"Usage: %s inputaudio.mp3, %s output.wm*, %Encoding Type: CBR, VBR\n");
        return 0;
    }


    HRESULT             hr = S_OK;

    IMFMediaSource* pSource = NULL;
    IMFTopology* pTopology = NULL;
    IMFActivate* pFileSinkActivate = NULL;

    hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
    if (FAILED(hr))
    {
        goto done;
    }

    //Set the requested encoding mode
    if (wcscmp(argv[3], L"CBR")==0)
    {
        EncodingMode = CBR;
    }
    else if (wcscmp(argv[3], L"VBR")==0)
    {
        EncodingMode = VBR;
    }
    else
    {
        EncodingMode = CBR;
    }

    // Start up Media Foundation platform.
    hr = MFStartup(MF_VERSION);
    if (FAILED(hr))
    {
        goto done;
    }

    //Create the media source
    hr = CreateMediaSource(argv[1], &pSource);
    if (FAILED(hr))
    {
        goto done;
    }

    //Create the file sink activate
    hr = CreateMediaSink(argv[2], pSource, &pFileSinkActivate);
    if (FAILED(hr))
    {
        goto done;
    }

    //Build the encoding topology.
    hr = BuildPartialTopology(pSource, pFileSinkActivate, &pTopology);
    if (FAILED(hr))
    {
        goto done;
    }

    //Instantiate the media session and start encoding
    hr = Encode(pTopology);
    if (FAILED(hr))
    {
        goto done;
    }


done:
    // Clean up.
    SafeRelease(&pSource);
    SafeRelease(&pTopology);
    SafeRelease(&pFileSinkActivate);

    MFShutdown();
    CoUninitialize();

    if (FAILED(hr))
    {
        wprintf_s(L"Could not create the output file due to 0x%X\n", hr);
    }
    return 0;
}

Тестирование выходного файла

В следующем списке описывается список проверка для тестирования закодированного файла. Эти значения можно проверка в диалоговом окне свойств файла, которое можно отобразить, щелкнув правой кнопкой мыши закодированный файл и выбрав "Свойства" в контекстном меню.

  • Путь к закодированному файлу является точным.
  • Размер файла превышает нулевая КБ, а длительность воспроизведения соответствует длительности исходного файла.
  • Для видеопотока проверка ширину и высоту кадра, частоту кадров. Эти значения должны соответствовать значениям, указанным в профиле ASF, созданном на шаге, описанном в разделе "Создание объекта профиля ASF".
  • Для аудиопотока скорость бита должна быть близка к значению, указанному в целевом типе мультимедиа.
  • Откройте файл в Медиаплеер Windows и проверка качество кодирования.
  • Откройте ASF-файл в ASFViewer, чтобы просмотреть структуру ФАЙЛА ASF. Это средство можно скачать с этого веб-сайта Майкрософт.

Распространенные коды ошибок и Советы отладки

В следующем списке описаны распространенные коды ошибок, которые могут получать и советы по отладке.

  • Вызов МВФSourceResolver::CreateObjectFromURL останавливает приложение.

    Убедитесь, что вы инициализировали платформу Media Foundation, вызвав MFStartup. Эта функция настраивает асинхронную платформу, которая используется всеми методами, которые запускают асинхронные операции, такие как МВФSourceResolver::CreateObjectFromURL, внутренне.

  • IMFSourceResolver::CreateObjectFromURL возвращает HRESULT 0x80070002 "Система не может найти указанный файл.

    Убедитесь, что имя входного файла, указанное пользователем в первом аргументе.

  • HRESULT 0x80070020 "Процесс не может получить доступ к файлу, так как он используется другим процессом. "

    Убедитесь, что входные и выходные файлы в настоящее время не используются другим ресурсом в системе.

  • Вызовы методов МВФTransform возвращают MF_E_INVALIDMEDIATYPE.

    Убедитесь, что выполняются следующие условия:

    • Тип ввода или указанный тип вывода совместим с типами носителей, поддерживаемыми кодировщиком.
    • Указанные типы носителей завершены. Чтобы типы носителей были завершены, см. необходимые атрибуты в разделах "Создание сжатого типа аудиомедийного носителя" и "Создание сжатого типа видеотрансляции" этого руководства.
    • Убедитесь, что вы установили целевую скорость бита для частичного типа носителя, в который добавляется частные данные кодека.
  • Сеанс мультимедиа возвращает MF_E_UNSUPPORTED_D3D_TYPE в состоянии события.

    Эта ошибка возвращается, когда тип носителя источника указывает смешанный режим переплета, который не поддерживается кодировщиком видео Windows Media. Если для сжатого типа видеофайлов задан прогрессивный режим, конвейер должен использовать преобразовывание. Так как конвейер не может найти совпадение (указанное в этом коде ошибки), необходимо вручную вставить де-interlacer (обработчик видеокодировки) между декодером и узлами кодировщика.

  • Сеанс мультимедиа возвращает E_INVALIDARG в состоянии события.

    Эта ошибка возвращается, когда атрибуты типа носителя источника несовместимы с атрибутами выходного типа носителя, заданного в кодировщике Windows Media.

  • IWMCodecPrivateData::GetPrivateData возвращает HRESULT 0x80040203 "Произошла синтаксическая ошибка, пытающаяся оценить строку запроса".

    Убедитесь, что входной тип задан в кодировщике MFT.

Компоненты ASF уровня конвейера