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

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

Создание топологии

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

  1. Создайте источник мультимедиа. В большинстве случаев для создания источника мультимедиа используется сопоставитель источника. Дополнительные сведения см. в разделе Сопоставитель источника.
  2. Получение дескриптора презентации источника мультимедиа.
  3. Создайте пустую топологию.
  4. Используйте дескриптор презентации для перечисления дескрипторов потока. Для каждого дескриптора потока:
    1. Получение основного типа мультимедиа потока, например аудио или видео.
    2. Проверьте, выбран ли поток в данный момент. (При необходимости можно выбрать или отменить выбор потока в зависимости от типа носителя.)
    3. Если выбран поток, создайте объект активации для приемника мультимедиа на основе типа мультимедиа потока.
    4. Добавьте исходный узел для потока и выходной узел для приемника мультимедиа.
    5. Подключите исходный узел к узлу вывода.

Чтобы упростить выполнение этого процесса, пример кода в этом разделе состоит из нескольких функций. Функция верхнего уровня называется CreatePlaybackTopology. Он принимает три параметра:

  • Указатель на интерфейс IMFMediaSource источника мультимедиа.
  • Указатель на интерфейс IMFPresentationDescriptor дескриптора представления. Получите этот указатель, вызвав IMFMediaSource::CreatePresentationDescriptor. Для источников с несколькими презентациями дескрипторы презентаций для последующих презентаций доставляются в событии MENewPresentation .
  • Дескриптор окна приложения. Если источник содержит видеопоток, видео будет отображаться в этом окне.

Функция возвращает указатель на топологию частичного воспроизведения в параметре ppTopology .

//  Create a playback topology from a media source.
HRESULT CreatePlaybackTopology(
    IMFMediaSource *pSource,          // Media source.
    IMFPresentationDescriptor *pPD,   // Presentation descriptor.
    HWND hVideoWnd,                   // Video window.
    IMFTopology **ppTopology)         // Receives a pointer to the topology.
{
    IMFTopology *pTopology = NULL;
    DWORD cSourceStreams = 0;

    // Create a new topology.
    HRESULT hr = MFCreateTopology(&pTopology);
    if (FAILED(hr))
    {
        goto done;
    }




    // Get the number of streams in the media source.
    hr = pPD->GetStreamDescriptorCount(&cSourceStreams);
    if (FAILED(hr))
    {
        goto done;
    }

    // For each stream, create the topology nodes and add them to the topology.
    for (DWORD i = 0; i < cSourceStreams; i++)
    {
        hr = AddBranchToPartialTopology(pTopology, pSource, pPD, i, hVideoWnd);
        if (FAILED(hr))
        {
            goto done;
        }
    }

    // Return the IMFTopology pointer to the caller.
    *ppTopology = pTopology;
    (*ppTopology)->AddRef();

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

Эта функция выполняет следующие действия:

  1. Вызовите MFCreateTopology , чтобы создать топологию. Изначально топология не содержит узлов.
  2. Вызовите IMFPresentationDescriptor::GetStreamDescriptorCount , чтобы получить количество потоков в презентации.
  3. Для каждого потока вызовите определяемую AddBranchToPartialTopology приложением функцию в ветвь в топологии. Эта функция показана в следующем разделе.

Подключение потоков к приемникам мультимедиа

Для каждого выбранного потока добавьте исходный и выходной узел, а затем подключите два узла. Исходный узел представляет поток. Выходной узел представляет либо расширенный отрисовщик видео (EVR), либо отрисовщик потокового звука (SAR).

Функция AddBranchToPartialTopology , показанная в следующем примере, принимает следующие параметры:

  • Указатель на интерфейс IMFTopology топологии.
  • Указатель на интерфейс IMFMediaSource источника мультимедиа.
  • Указатель на интерфейс IMFPresentationDescriptor дескриптора представления.
  • Отсчитываемый от нуля индекс потока.
  • Дескриптор окна видео. Этот дескриптор используется только для видеопотока.
//  Add a topology branch for one stream.
//
//  For each stream, this function does the following:
//
//    1. Creates a source node associated with the stream. 
//    2. Creates an output node for the renderer. 
//    3. Connects the two nodes.
//
//  The media session will add any decoders that are needed.

HRESULT AddBranchToPartialTopology(
    IMFTopology *pTopology,         // Topology.
    IMFMediaSource *pSource,        // Media source.
    IMFPresentationDescriptor *pPD, // Presentation descriptor.
    DWORD iStream,                  // Stream index.
    HWND hVideoWnd)                 // Window for video playback.
{
    IMFStreamDescriptor *pSD = NULL;
    IMFActivate         *pSinkActivate = NULL;
    IMFTopologyNode     *pSourceNode = NULL;
    IMFTopologyNode     *pOutputNode = NULL;

    BOOL fSelected = FALSE;

    HRESULT hr = pPD->GetStreamDescriptorByIndex(iStream, &fSelected, &pSD);
    if (FAILED(hr))
    {
        goto done;
    }

    if (fSelected)
    {
        // Create the media sink activation object.
        hr = CreateMediaSinkActivate(pSD, hVideoWnd, &pSinkActivate);
        if (FAILED(hr))
        {
            goto done;
        }

        // Add a source node for this stream.
        hr = AddSourceNode(pTopology, pSource, pPD, pSD, &pSourceNode);
        if (FAILED(hr))
        {
            goto done;
        }

        // Create the output node for the renderer.
        hr = AddOutputNode(pTopology, pSinkActivate, 0, &pOutputNode);
        if (FAILED(hr))
        {
            goto done;
        }

        // Connect the source node to the output node.
        hr = pSourceNode->ConnectOutput(0, pOutputNode, 0);
    }
    // else: If not selected, don't add the branch. 

done:
    SafeRelease(&pSD);
    SafeRelease(&pSinkActivate);
    SafeRelease(&pSourceNode);
    SafeRelease(&pOutputNode);
    return hr;
}

Функция выполняет следующие действия:

  1. Вызывает IMFPresentationDescriptor::GetStreamDescriptorByIndex и передает индекс потока. Этот метод возвращает указатель на дескриптор потока для этого потока, а также логическое значение, указывающее, выбран ли поток.
  2. Если поток не выбран, функция завершает работу и возвращает S_OK, так как приложению не нужно создавать ветвь топологии для потока, если она не выбрана.
  3. Если выбран поток, функция завершает ветвь топологии следующим образом:
    1. Создает объект активации для приемника путем вызова определяемой приложением функции CreateMediaSinkActivate. Эта функция показана в следующем разделе.
    2. Добавляет исходный узел в топологию. Код для этого шага показан в разделе Создание исходных узлов.
    3. Добавляет выходной узел в топологию. Код для этого шага показан в разделе Создание выходных узлов.
    4. Соединяет два узла путем вызова IMFTopologyNode::ConnectOutput на исходном узле. Подключая узлы, приложение указывает, что вышестоящий узел должен доставлять данные на подчиненный узел. Исходный узел имеет один выход, а выходной узел — один вход, поэтому оба индекса потока равны нулю.

Более сложные приложения могут выбирать или отменять выбор потоков вместо использования конфигурации источника по умолчанию. Источник может содержать несколько потоков, и любой из них может быть выбран по умолчанию. Дескриптор презентации источника мультимедиа имеет набор выбранных потоков по умолчанию. В простом видеофайле с одним аудиопотоком и видеопотоком источник мультимедиа обычно выбирает оба потока по умолчанию. Однако файл может содержать несколько аудиопотоков для разных языков или несколько видеопотоков, закодированных с разной скоростью. В этом случае некоторые потоки будут отменены по умолчанию. Приложение может изменить выбор, вызвав IMFPresentationDescriptor::SelectStream и IMFPresentationDescriptor::D eselectStream в дескрипторе презентации.

Создание приемника мультимедиа

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

//  Create an activation object for a renderer, based on the stream media type.

HRESULT CreateMediaSinkActivate(
    IMFStreamDescriptor *pSourceSD,     // Pointer to the stream descriptor.
    HWND hVideoWindow,                  // Handle to the video clipping window.
    IMFActivate **ppActivate
)
{
    IMFMediaTypeHandler *pHandler = NULL;
    IMFActivate *pActivate = NULL;

    // Get the media type handler for the stream.
    HRESULT hr = pSourceSD->GetMediaTypeHandler(&pHandler);
    if (FAILED(hr))
    {
        goto done;
    }

    // Get the major media type.
    GUID guidMajorType;
    hr = pHandler->GetMajorType(&guidMajorType);
    if (FAILED(hr))
    {
        goto done;
    }
 
    // Create an IMFActivate object for the renderer, based on the media type.
    if (MFMediaType_Audio == guidMajorType)
    {
        // Create the audio renderer.
        hr = MFCreateAudioRendererActivate(&pActivate);
    }
    else if (MFMediaType_Video == guidMajorType)
    {
        // Create the video renderer.
        hr = MFCreateVideoRendererActivate(hVideoWindow, &pActivate);
    }
    else
    {
        // Unknown stream type. 
        hr = E_FAIL;
        // Optionally, you could deselect this stream instead of failing.
    }
    if (FAILED(hr))
    {
        goto done;
    }
 
    // Return IMFActivate pointer to caller.
    *ppActivate = pActivate;
    (*ppActivate)->AddRef();

done:
    SafeRelease(&pHandler);
    SafeRelease(&pActivate);
    return hr;
}

Эта функция выполняет следующие действия:

  1. Вызывает IMFStreamDescriptor::GetMediaTypeHandler для дескриптора потока. Этот метод возвращает указатель интерфейса IMFMediaTypeHandler .

  2. Вызывает IMFMediaTypeHandler::GetMajorType. Этот метод возвращает GUID основного типа для потока.

  3. Если тип потока — audio, функция вызывает MFCreateAudioRendererActivate для создания объекта активации отрисовщика звука. Если тип потока — video, функция вызывает MFCreateVideoRendererActivate для создания объекта активации отрисовщика видео. Обе эти функции возвращают указатель на интерфейс IMFActivate . Этот указатель используется для инициализации выходного узла для приемника, как показано выше.

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

Next Steps

Чтобы воспроизвести один файл мультимедиа за раз, поставить топологию в очередь сеанса мультимедиа, вызвав IMFMediaSession::SetTopology. Сеанс мультимедиа будет использовать загрузчик топологии для разрешения топологии. Полный пример см. в статье Воспроизведение файлов мультимедиа с помощью Media Foundation.

Воспроизведение незащищенных файлов мультимедиа

Сеанс мультимедиа

Топологии