Поделиться через


Учебник по одноэтапной кодировке Windows Media

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

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

Необходимые условия

В этом руководстве предполагается следующее:

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

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

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

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

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

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

    // Video buffer window
    const INT32 VIDEO_WINDOW_MSEC = 3000;
    
    

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

Создайте источник мультимедиа для источника входных данных. 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. Приемник файлов должен иметь следующие сведения, чтобы создать необходимые приемники потоков.

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

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

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

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

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

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

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

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

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

  1. Вызовите MFCreateASFProfile для создания пустого объекта профиля.

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

  3. Вызовите IMFPresentationDescriptor::GetStreamDescriptorCount, чтобы получить количество потоков в медиаресурсе.

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

  5. Вызовите IMFStreamDescriptor::GetMediaTypeHandler, следуемый IMFMediaTypeHandler::GetMediaTypeByIndex, чтобы получить первый тип мультимедиа для потока.

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

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

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

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

    1. Вызовите IMFASFProfile::CreateStream и передайте целевой тип носителя, чтобы создать выходной поток. Метод извлекает интерфейс IMFASFStreamConfig объекта потока.
    2. Настройте поток.
      • Вызовите IMFASFStreamConfig::SetStreamNumber, чтобы назначить номер потоку. Этот параметр является обязательным.
      • При необходимости настройте параметры утечки контейнера, расширение полезных данных, взаимное исключение для каждого потока путем вызова методов МВФASFStreamConfig и соответствующих атрибутов конфигурации потока.
    3. Добавьте свойства кодирования уровня потока с помощью объекта ASF ContentInfo. Дополнительные сведения об этом шаге см. в разделе "Создание объекта ASF ContentInfo" в этом руководстве.
    4. Вызовите IMFASFProfile::SetStream, чтобы добавить поток в профиль.

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

    //-------------------------------------------------------------------
    //  CreateAudioStream
    //  Create an audio stream and add it to the profile.
    //
    //  pProfile: A pointer to the ASF profile.
    //  wStreamNumber: Stream number to assign for the new stream.
    //-------------------------------------------------------------------
    
    HRESULT CreateAudioStream(IMFASFProfile* pProfile, WORD wStreamNumber)
    {
        if (!pProfile)
        {
            return E_INVALIDARG;
        }
        if (wStreamNumber < 1 || wStreamNumber > 127 )
        {
            return MF_E_INVALIDSTREAMNUMBER;
        }
    
        IMFMediaType* pAudioType = NULL;
        IMFASFStreamConfig* pAudioStream = NULL;
    
        //Create an output type from the encoder
        HRESULT hr = GetOutputTypeFromWMAEncoder(&pAudioType);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Create a new stream with the audio type
        hr = pProfile->CreateStream(pAudioType, &pAudioStream);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Set stream number
        hr = pAudioStream->SetStreamNumber(wStreamNumber);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Add the stream to the profile
        hr = pProfile->SetStream(pAudioStream);
        if (FAILED(hr))
        {
            goto done;
        }
    
        wprintf_s(L"Audio Stream created. Stream Number: %d.\n", wStreamNumber);
    
    done:
        SafeRelease(&pAudioStream);
        SafeRelease(&pAudioType);
        return hr;
    }
    

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

    //-------------------------------------------------------------------
    //  CreateVideoStream
    //  Create an video stream and add it to the profile.
    //
    //  pProfile: A pointer to the ASF profile.
    //  wStreamNumber: Stream number to assign for the new stream.
    //    pType: A pointer to the source's video media type.
    //-------------------------------------------------------------------
    
    HRESULT CreateVideoStream(IMFASFProfile* pProfile, WORD wStreamNumber, IMFMediaType* pType)
    {
        if (!pProfile)
        {
            return E_INVALIDARG;
        }
        if (wStreamNumber < 1 || wStreamNumber > 127 )
        {
            return MF_E_INVALIDSTREAMNUMBER;
        }
    
        HRESULT hr = S_OK;
    
    
        IMFMediaType* pVideoType = NULL;
        IMFASFStreamConfig* pVideoStream = NULL;
    
        UINT32 dwBitRate = 0;
    
        //Create a new video type from the source type
        hr = CreateCompressedVideoType(pType, &pVideoType);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Create a new stream with the video type
        hr = pProfile->CreateStream(pVideoType, &pVideoStream);
        if (FAILED(hr))
        {
            goto done;
        }
    
    
        //Set a valid stream number
        hr = pVideoStream->SetStreamNumber(wStreamNumber);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Add the stream to the profile
        hr = pProfile->SetStream(pVideoStream);
        if (FAILED(hr))
        {
            goto done;
        }
    
        wprintf_s(L"Video Stream created. Stream Number: %d .\n", wStreamNumber);
    
    done:
    
        SafeRelease(&pVideoStream);
        SafeRelease(&pVideoType);
    
        return hr;
    }
    

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

    //For each stream in the source, add target stream information in the profile
    for (DWORD iStream = 0; iStream < dwSrcStream; iStream++)
    {
        hr = pPD->GetStreamDescriptorByIndex(
            iStream, &fSelected, &pStreamDesc);
        if (FAILED(hr))
        {
            goto done;
        }

        if (!fSelected)
        {
            continue;
        }

        //Get the media type handler
        hr = pStreamDesc->GetMediaTypeHandler(&pHandler);
        if (FAILED(hr))
        {
            goto done;
        }

        //Get the first media type 
        hr = pHandler->GetMediaTypeByIndex(0, &pMediaType);
        if (FAILED(hr))
        {
            goto done;
        }

        //Get the major type
        hr = pMediaType->GetMajorType(&guidMajor);
        if (FAILED(hr))
        {
            goto done;
        }

        // If this is a video stream, create the target video stream
        if (guidMajor == MFMediaType_Video)
        {
            //The stream level encoding properties will be set in this call
            hr = CreateVideoStream(pProfile, wStreamID, pMediaType);

            if (FAILED(hr))
            {
                goto done;
            }
        }
        
        // If this is an audio stream, create the target audio stream
        if (guidMajor == MFMediaType_Audio)
        {
            //The stream level encoding properties will be set in this call
            hr = CreateAudioStream(pProfile, wStreamID);

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

        //Get stream's encoding property
        hr = pContentInfo->GetEncodingConfigurationPropertyStore(wStreamID, &pContentInfoProps);
        if (FAILED(hr))
        {
            goto done;
        }

        //Set the stream-level encoding properties
        hr = SetEncodingProperties(guidMajor, pContentInfoProps);       
        if (FAILED(hr))
        {
            goto done;
        }


        SafeRelease(&pMediaType);
        SafeRelease(&pStreamDesc);
        SafeRelease(&pContentInfoProps);
        wStreamID++;
    }

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

Если вы хотите включить аудиопоток в выходной файл, создайте тип звука, указав характеристики закодированного типа, задав необходимые атрибуты. Чтобы убедиться, что тип звука совместим с аудиокодировщиком Windows Media, создайте экземпляр MFT кодировщика, задайте свойства кодирования и получите тип носителя, вызвав IMFTransform::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, задав свойства кодирования, а затем вызовите IMFTransform::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. Вызовите IMFASFContentInfo::GetEncodingConfigurationPropertyStore, чтобы получить хранилище свойств для уровня потока файлового приемника. В этом вызове необходимо передать номер потока данных, который вы назначили для этого потока при создании профиля ASF.

  3. Задайте свойства кодирования на уровне потока в хранилище свойств потока приемника файлов. Дополнительные сведения см. в разделе "Параметры кодирования потока" в Настройка свойств приемника файлов.

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

            //Get stream's encoding property
            hr = pContentInfo->GetEncodingConfigurationPropertyStore(wStreamID, &pContentInfoProps);
            if (FAILED(hr))
            {
                goto done;
            }
    
            //Set the stream-level encoding properties
            hr = SetEncodingProperties(guidMajor, pContentInfoProps);       
            if (FAILED(hr))
            {
                goto done;
            }
    
    
    

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

    //-------------------------------------------------------------------
    //  SetEncodingProperties
    //  Create a media source from a URL.
    //
    //  guidMT:  Major type of the stream, audio or video
    //  pProps:  A pointer to the property store in which 
    //           to set the required encoding properties.
    //-------------------------------------------------------------------
    
    HRESULT SetEncodingProperties (const GUID guidMT, IPropertyStore* pProps)
    {
        if (!pProps)
        {
            return E_INVALIDARG;
        }
    
        if (EncodingMode == NONE)
        {
            return MF_E_NOT_INITIALIZED;
        }
    
        HRESULT hr = S_OK;
    
        PROPVARIANT var;
    
        switch (EncodingMode)
        {
            case CBR:
                // Set VBR to false.
                hr = InitPropVariantFromBoolean(FALSE, &var);
                if (FAILED(hr))
                {
                    goto done;
                }
    
                hr = pProps->SetValue(MFPKEY_VBRENABLED, var);
                if (FAILED(hr))
                {
                    goto done;
                }
    
                // Set the video buffer window.
                if (guidMT == MFMediaType_Video)
                {
                    hr = InitPropVariantFromInt32(VIDEO_WINDOW_MSEC, &var);
                    if (FAILED(hr))
                    {
                        goto done;
                    }
    
                    hr = pProps->SetValue(MFPKEY_VIDEOWINDOW, var);    
                    if (FAILED(hr))
                    {
                        goto done;
                    }
                }
                break;
    
            case VBR:
                //Set VBR to true.
                hr = InitPropVariantFromBoolean(TRUE, &var);
                if (FAILED(hr))
                {
                    goto done;
                }
    
                hr = pProps->SetValue(MFPKEY_VBRENABLED, var);
                if (FAILED(hr))
                {
                    goto done;
                }
    
                // Number of encoding passes is 1.
    
                hr = InitPropVariantFromInt32(1, &var);
                if (FAILED(hr))
                {
                    goto done;
                }
    
                hr = pProps->SetValue(MFPKEY_PASSESUSED, var);
                if (FAILED(hr))
                {
                    goto done;
                }
    
                // Set the quality level.
    
                if (guidMT == MFMediaType_Audio)
                {
                    hr = InitPropVariantFromUInt32(98, &var);
                    if (FAILED(hr))
                    {
                        goto done;
                    }
    
                    hr = pProps->SetValue(MFPKEY_DESIRED_VBRQUALITY, var);    
                    if (FAILED(hr))
                    {
                        goto done;
                    }
                }
                else if (guidMT == MFMediaType_Video)
                {
                    hr = InitPropVariantFromUInt32(95, &var);
                    if (FAILED(hr))
                    {
                        goto done;
                    }
    
                    hr = pProps->SetValue(MFPKEY_VBRQUALITY, var);    
                    if (FAILED(hr))
                    {
                        goto done;
                    }
                }
                break;
    
            default:
                hr = E_UNEXPECTED;
                break;
        }    
    
    done:
        PropVariantClear(&var);
        return hr;
    }
    
  4. Вызовите IMFASFContentInfo::GetEncodingConfigurationPropertyStore, чтобы получить глобальное хранилище свойств приемника файлов. В этом вызове необходимо передать 0 в первом параметре. Для получения дополнительной информации см. раздел "Свойства глобального приемника файлов" в "Настройка свойств в приемнике файлов".

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

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

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

    Заметка

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

     

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

Если вы создаете приемник файлов вне процесса (MFCreateASFMediaSinkActivate), то есть с помощью объекта активации, можно использовать настроенный объект ContentInfo для создания экземпляра приемника мультимедиа ASF (описано в следующем разделе). Если вы создаете внутренний приемник файлов (MFCreateASFMediaSink), вместо создания пустого объекта ContentInfo, как описано на шаге 1, получите ссылку на интерфейс IMFASFContentInfo через вызов IMFMediaSink::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.

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

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

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

  • Указатель на источник мультимедиа, созданный на шаге, описанном в разделе "Создание источника мультимедиа" этого руководства.
  • Указатель на дескриптор презентации для источника мультимедиа. Вы можете получить ссылку на интерфейс IMFPresentationDescriptor мультимедиа-источника, вызвав IMFMediaSource::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 для активации файлового приемника, а затем запросите объект IMFASFContentInfo из файлового приемника, вызвав QueryInterface.

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

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

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

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

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

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

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

    • Указатель на тип мультимедиа потока, полученный IMFASFStreamConfig::GetMediaType на предыдущем шаге.
    • Указатель на хранилище свойств кодирования потока, полученное IMFASFContentInfo::GetEncodingConfigurationPropertyStore. Передавая указатель в хранилище свойств, свойства потоков, заданные в приемнике файлов, копируются в MFT кодировщика.
  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. Вызовите IMFMediaSession::SetTopology, чтобы задать топологию кодирования в сеансе. Если вызов завершится, сеанс мультимедиа оценивает узлы топологии и вставляет дополнительные объекты преобразования, такие как декодатор, который преобразует указанный сжатый источник в несжатые образцы, подаваемые в кодировщик в качестве входных данных.

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

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

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

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

Сеанс мультимедиа создает объект заголовка ASF и завершает файл после завершения сеанса кодирования, а затем вызывает событие MESessionClosed. Это событие должно обрабатываться путем выполнения соответствующих операций завершения работы в сеансе мультимедиа. Чтобы начать операции завершения работы, вызовите IMFMediaSession::Shutdown. После закрытия сеанса кодирования нельзя задать другую топологию для кодирования в одном экземпляре сеанса мультимедиа. Чтобы закодировать другой файл, текущий сеанс мультимедиа должен быть закрыт и выпущен, а новая топология должна быть задана в только что созданном сеансе мультимедиа. Следующий пример кода создает сеанс мультимедиа, задает топологию кодирования и обрабатывает события сеанса мультимедиа.

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

//-------------------------------------------------------------------
//  Encode
//  Controls the encoding session and handles events from the media session.
//
//  pTopology:  A pointer to the encoding topology.
//-------------------------------------------------------------------

HRESULT Encode(IMFTopology *pTopology)
{
    if (!pTopology)
    {
        return E_INVALIDARG;
    }
    
    IMFMediaSession *pSession = NULL;
    IMFMediaEvent* pEvent = NULL;
    IMFTopology* pFullTopology = NULL;
    IUnknown* pTopoUnk = NULL;


    MediaEventType meType = MEUnknown;  // Event type

    HRESULT hr = S_OK;
    HRESULT hrStatus = S_OK;            // Event status
                
    MF_TOPOSTATUS TopoStatus = MF_TOPOSTATUS_INVALID; // Used with MESessionTopologyStatus event.    
        

    hr = MFCreateMediaSession(NULL, &pSession);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pSession->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE, pTopology);
    if (FAILED(hr))
    {
        goto done;
    }

    //Get media session events synchronously
    while (1)
    {
        hr = pSession->GetEvent(0, &pEvent);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pEvent->GetType(&meType);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pEvent->GetStatus(&hrStatus);
        if (FAILED(hr))
        {
            goto done;
        }
        if (FAILED(hrStatus))
        {
            hr = hrStatus;
            goto done;
        }

       switch(meType)
        {
            case MESessionTopologyStatus:
                {
                    // Get the status code.
                    MF_TOPOSTATUS status = (MF_TOPOSTATUS)MFGetAttributeUINT32(
                                             pEvent, MF_EVENT_TOPOLOGY_STATUS, MF_TOPOSTATUS_INVALID);

                    if (status == MF_TOPOSTATUS_READY)
                    {
                        PROPVARIANT var;
                        PropVariantInit(&var);
                        wprintf_s(L"Topology resolved and set on the media session.\n");

                        hr = pSession->Start(NULL, &var);
                        if (FAILED(hr))
                        {
                            goto done;
                        }

                    }
                    if (status == MF_TOPOSTATUS_STARTED_SOURCE)
                    {
                        wprintf_s(L"Encoding started.\n");
                        break;
                    }
                    if (status == MF_TOPOSTATUS_ENDED)
                    {
                        wprintf_s(L"Encoding complete.\n");
                        hr = pSession->Close();
                        if (FAILED(hr))
                        {
                            goto done;
                        }

                        break;
                    }
                }
                break;


            case MESessionEnded:
                wprintf_s(L"Encoding complete.\n");
                hr = pSession->Close();
                if (FAILED(hr))
                {
                    goto done;
                }
                break;

            case MEEndOfPresentation:
                {
                    if (EncodingMode == VBR)
                    {
                        hr = pSession->GetFullTopology(MFSESSION_GETFULLTOPOLOGY_CURRENT, 0, &pFullTopology);
                        if (FAILED(hr))
                        {
                            goto done;
                        }
                        hr = PostEncodingUpdate(pFullTopology);
                        if (FAILED(hr))
                        {
                            goto done;
                        }
                        wprintf_s(L"Updated sinks for VBR. \n");
                    }
                }
                break;

            case MESessionClosed:
                wprintf_s(L"Encoding session closed.\n");

                hr = pSession->Shutdown();
                goto done;
        }
        if (FAILED(hr))
        {
            goto done;
        }

        SafeRelease(&pEvent);

    }
done:
    SafeRelease(&pEvent);
    SafeRelease(&pSession);
    SafeRelease(&pFullTopology);
    SafeRelease(&pTopoUnk);
    return hr;
}

Обновление свойств кодирования в приемнике файлов

Некоторые свойства кодирования, такие как скорость кодирования и точные значения сегментов утечки, не известны до завершения кодирования, особенно для кодирования VBR. Чтобы получить правильные значения, приложение должно ожидать события MEEndOfPresentation, указывающее, что сеанс кодирования завершен. Значения в модели управления потоком "leaky bucket" должны быть обновлены в приемнике данных, чтобы объект заголовка ASF отображал верные значения.

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

Обновить значения свойств пост-кодировки на приемнике файлов ASF

  1. Вызовите IMFTopology::GetOutputNodeCollection, чтобы получить коллекцию выходных узлов из топологии кодирования.
  2. Для каждого узла получите указатель на потоковый приемник в узле, вызвав IMFTopologyNode::GetObject. Выполнить запрос интерфейса IMFStreamSink на указателе IUnknown, который возвращает IMFTopologyNode::GetObject.
  3. Для каждого приемника потоков получите подчиненный узел (кодировщик), вызвав IMFTopologyNode::GetInput.
  4. Выполните запрос к узлу, чтобы получить указатель IMFTransform из узла кодировщика.
  5. Запросите кодировщик для указателя IPropertyStore, чтобы получить хранилище свойств кодировки от кодировщика.
  6. Запрос приемника потока для указателя IPropertyStore, чтобы получить хранилище свойств приемника потока.
  7. Вызовите IPropertyStore::GetValue, чтобы получить необходимые значения свойств из хранилища свойств кодировщика и скопировать их в хранилище свойств приемника потоков путем вызова IPropertyStore::SetValue.

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

Тип кодирования Имя свойства (GetValue) Имя свойства (SetValue)
кодирование с постоянной битовой скоростью MFPKEY_BAVG
MFPKEY_RAVG
MFPKEY_STAT_BAVG
MFPKEY_STAT_RAVG
Quality-Based кодирование с переменной скоростью передачи данных MFPKEY_BAVG
MFPKEY_RAVG
MFPKEY_BMAX
MFPKEY_RMAX
MFPKEY_STAT_BAVG
MFPKEY_STAT_RAVG
MFPKEY_STAT_BMAX
MFPKEY_STAT_RMAX

 

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

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

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

    HRESULT hr = S_OK;

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

    GUID guidMajorType = GUID_NULL;

    PROPVARIANT var;
    PropVariantInit( &var );

    DWORD cElements = 0;

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


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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return hr;
   
}

Реализовать «main»

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

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

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


    HRESULT             hr = S_OK;

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

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

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

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

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

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

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

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


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

    MFShutdown();
    CoUninitialize();

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

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

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

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

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

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

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

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

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

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

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

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

  • Вызовы к методам IMFTransform возвращают MF_E_INVALIDMEDIATYPE.

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

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

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

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

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

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

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

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