Udostępnij za pośrednictwem


Tworzenie topologii odtwarzania

W tym temacie opisano sposób tworzenia topologii na potrzeby odtwarzania audio lub wideo. W przypadku odtwarzania podstawowego można utworzyć topologię częściową, w której węzły źródłowe są połączone bezpośrednio z węzłami wyjściowymi. Nie trzeba wstawiać żadnych węzłów dla przekształceń pośrednich, takich jak dekodatory lub konwertery kolorów. Sesja medialna użyje ładowarki topologii, aby rozwiązać topologię, a ładowarka topologii wstawi wymagane przekształcenia.

Tworzenie topologii

Poniżej przedstawiono ogólne kroki tworzenia topologii częściowego odtwarzania ze źródła multimediów:

  1. Utwórz źródło multimediów. W większości przypadków użyjesz narzędzia rozpoznawania źródła do utworzenia źródła multimediów. Aby uzyskać więcej informacji, zobacz Source Resolver.
  2. Pobierz opis prezentacji źródła multimediów.
  3. Utwórz pustą topologię.
  4. Użyj deskryptora prezentacji, aby wyliczyć deskryptory strumienia. Dla każdego deskryptora strumienia:
    1. Pobierz główny typ multimediów strumienia, taki jak audio lub wideo.
    2. Sprawdź, czy strumień jest aktualnie wybrany. (Opcjonalnie możesz zaznaczyć lub odznaczyć strumień w zależności od typu nośnika).
    3. Jeśli strumień zostanie wybrany, utwórz obiekt aktywacji dla odbiornika multimediów na podstawie typu nośnika.
    4. Dodaj węzeł źródłowy dla strumienia i węzeł wyjściowy dla odbiornika multimediów.
    5. Połącz węzeł źródłowy z węzłem wyjściowym.

Aby ułatwić wykonanie tego procesu, przykładowy kod w tym temacie jest zorganizowany w kilka funkcji. Funkcja najwyższego poziomu nosi nazwę CreatePlaybackTopology. Przyjmuje trzy parametry:

Funkcja zwraca wskaźnik do topologii częściowego odtwarzania w parametrze 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;
}

Ta funkcja wykonuje następujące czynności:

  1. Wywołaj MFCreateTopology, aby utworzyć topologię. Początkowo topologia nie zawiera żadnych węzłów.
  2. Aby uzyskać liczbę strumieni w prezentacji, wywołaj IMFPresentationDescriptor::GetStreamDescriptorCount.
  3. Dla każdego strumienia wywołaj funkcję AddBranchToPartialTopology zdefiniowaną przez aplikację do gałęzi w topologii. Ta funkcja jest wyświetlana w następnej sekcji.

Łączenie strumieni z ujściami multimediów

Dla każdego wybranego strumienia dodaj węzeł źródłowy i węzeł wyjściowy, a następnie połącz dwa węzły. Węzeł źródłowy reprezentuje strumień. Węzeł wyjściowy reprezentuje Ulepszony moduł renderowania wideo (EVR) lub moduł renderowania audio strumieniowego (SAR).

Funkcja AddBranchToPartialTopology pokazana w następnym przykładzie przyjmuje następujące parametry:

  • Wskaźnik do interfejsu IMFTopology topologii.
  • Wskaźnik do interfejsu IMFMediaSource z źródła multimediów.
  • Wskaźnik do interfejsu IMFPresentationDescriptor deskryptora prezentacji.
  • Indeks strumienia oparty na zerach.
  • Uchwyt do okna wideo. Ten uchwyt jest używany tylko dla strumienia wideo.
//  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;
}

Funkcja wykonuje następujące czynności:

  1. Wywołuje IMFPresentationDescriptor::GetStreamDescriptorByIndex i przekazuje indeks strumienia. Ta metoda zwraca wskaźnik do deskryptora strumienia dla tego strumienia wraz z wartością logiczną wskazującą, czy strumień jest wybrany.
  2. Jeśli strumień nie jest zaznaczony, funkcja kończy działanie i zwraca S_OK, ponieważ aplikacja nie musi tworzyć gałęzi topologii dla strumienia, chyba że została wybrana.
  3. W przypadku wybrania strumienia funkcja kończy gałąź topologii w następujący sposób:
    1. Tworzy obiekt aktywacji dla ujścia, wywołując funkcję CreateMediaSinkActivate zdefiniowaną przez aplikację. Ta funkcja jest wyświetlana w następnej sekcji.
    2. Dodaje węzeł źródłowy do topologii. Kod tego kroku jest wyświetlany w temacie Tworzenie węzłów źródłowych.
    3. Dodaje węzeł wyjściowy do topologii. Kod tego kroku jest wyświetlany w temacie Tworzenie węzłów wyjściowych.
    4. Łączy dwa węzły, wywołując IMFTopologyNode::ConnectOutput w węźle źródłowym. Łącząc węzły, aplikacja wskazuje, że węzeł nadrzędny powinien dostarczać dane do węzła podrzędnego. Węzeł źródłowy ma jedno dane wyjściowe, a węzeł wyjściowy ma jedno dane wejściowe, więc oba indeksy strumienia są zerowe.

Bardziej zaawansowane aplikacje mogą wybierać lub odznaczać strumienie, zamiast korzystać z domyślnej konfiguracji źródła. Źródło może mieć wiele strumieni, a dowolna z nich może być domyślnie wybrana. Deskryptor prezentacji źródła multimediów ma domyślny zestaw wyborów strumienia. W prostym pliku wideo z jednym strumieniem audio i strumieniem wideo źródło multimediów zwykle wybiera oba strumienie domyślnie. Jednak plik może mieć wiele strumieni audio dla różnych języków lub wiele strumieni wideo zakodowanych w różnych szybkościach transmisji bitów. W takim przypadku niektóre strumienie zostaną domyślnie niezaznaczone. Aplikacja może zmienić wybór, wywołując IMFPresentationDescriptor::SelectStream i IMFPresentationDescriptor::DeselectStream na deskryptorze prezentacji.

Tworzenie odbiornika multimediów

Następna funkcja tworzy obiekt aktywacji dla ujścia multimediów EVR lub 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;
}

Ta funkcja wykonuje następujące czynności:

  1. Wywołuje IMFStreamDescriptor::GetMediaTypeHandler w deskryptorze strumienia. Ta metoda zwraca wskaźnik interfejsu IMFMediaTypeHandler.

  2. Wywołuje metodę IMFMediaTypeHandler::GetMajorType. Ta metoda zwraca identyfikator GUID typu głównego dla strumienia.

  3. Jeśli typ strumienia to dźwięk, funkcja wywołuje MFCreateAudioRendererActivate w celu utworzenia obiektu aktywacji modułu renderowania audio. Jeśli typ strumienia to wideo, funkcja wywołuje MFCreateVideoRendererActivate w celu utworzenia obiektu aktywacji modułu renderowania wideo. Obie te funkcje zwracają wskaźnik do interfejsu IMFActivate. Ten wskaźnik służy do inicjowania węzła wyjściowego odbiornika, jak pokazano wcześniej.

W przypadku dowolnego innego typu strumienia ten przykład zwraca kod błędu. Alternatywnie możesz po prostu usunąć zaznaczenie strumienia.

Następne kroki

Aby odtworzyć jeden plik multimedialny naraz, ustaw topologię w kolejce na sesji multimedialnej, wywołując IMFMediaSession::SetTopology. Sesja multimedialna użyje loadera topologii, aby rozwiązać topologię. Aby zapoznać się z kompletnym przykładem, zobacz Jak odtwarzać pliki multimedialne za pomocą Media Foundation.

Jak odtwarzać niechronione pliki multimedialne

sesja multimediów

Topologie