Bagikan melalui


Membuat Topologi Pemutaran

Topik ini menjelaskan cara membuat topologi untuk pemutaran audio atau video. Untuk pemutaran dasar, Anda dapat membuat topologi parsial, di mana simpul sumber terhubung langsung ke simpul output. Anda tidak perlu menyisipkan simpul apa pun untuk transformasi perantara, seperti dekode atau pengonversi warna. Sesi Media akan menggunakan pemuat topologi untuk menyelesaikan topologi, dan pemuat topologi akan menyisipkan transformasi yang diperlukan.

Membuat Topologi

Berikut adalah langkah-langkah keseluruhan untuk membuat topologi pemutaran parsial dari sumber media:

  1. Buat sumber media. Dalam kebanyakan kasus, Anda akan menggunakan pemecah masalah sumber untuk membuat sumber media. Untuk informasi selengkapnya, lihat Pemecah Masalah Sumber.
  2. Dapatkan deskriptor presentasi sumber media.
  3. Buat topologi kosong.
  4. Gunakan deskriptor presentasi untuk menghitung deskriptor aliran. Untuk setiap deskriptor aliran:
    1. Dapatkan jenis media utama streaming, seperti audio atau video.
    2. Periksa apakah aliran saat ini dipilih. (Secara opsional, Anda dapat memilih atau membatalkan pilihan aliran, berdasarkan jenis media.)
    3. Jika aliran dipilih, buat objek aktivasi untuk sink media, berdasarkan jenis media aliran.
    4. Tambahkan simpul sumber untuk aliran dan simpul output untuk sink media.
    5. Sambungkan simpul sumber ke simpul output.

Untuk membuat proses ini lebih mudah diikuti, contoh kode dalam topik ini diatur ke dalam beberapa fungsi. Fungsi tingkat atas diberi nama CreatePlaybackTopology. Dibutuhkan tiga parameter:

Fungsi mengembalikan penunjuk ke topologi pemutaran parsial dalam parameter 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;
}

Fungsi ini melakukan langkah-langkah berikut:

  1. Panggil MFCreateTopology untuk membuat topologi. Awalnya, topologi tidak berisi simpul apa pun.
  2. Panggil IMFPresentationDescriptor::GetStreamDescriptorCount untuk mendapatkan jumlah aliran dalam presentasi.
  3. Untuk setiap aliran, panggil fungsi yang ditentukan AddBranchToPartialTopology aplikasi ke cabang dalam topologi. Fungsi ini ditampilkan di bagian berikutnya.

Menyambungkan Aliran ke Sink Media

Untuk setiap aliran yang dipilih, tambahkan simpul sumber dan simpul output, lalu sambungkan dua simpul. Simpul sumber mewakili aliran. Simpul output mewakili Enhanced Video Renderer (EVR) atau Streaming Audio Renderer (SAR).

Fungsi AddBranchToPartialTopology , yang ditunjukkan dalam contoh berikutnya, mengambil parameter berikut:

  • Penunjuk ke antarmuka IMFTopology topologi.
  • Penunjuk ke antarmuka IMFMediaSource dari sumber media.
  • Penunjuk ke antarmuka IMFPresentationDescriptor dari deskriptor presentasi.
  • Indeks aliran berbasis nol.
  • Handel ke jendela video. Handel ini hanya digunakan untuk streaming video.
//  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;
}

Fungsi melakukan hal berikut:

  1. Memanggil IMFPresentationDescriptor::GetStreamDescriptorByIndex dan meneruskan indeks aliran. Metode ini mengembalikan penunjuk ke deskriptor aliran untuk aliran tersebut, bersama dengan nilai Boolean yang menunjukkan apakah aliran dipilih.
  2. Jika aliran tidak dipilih, fungsi keluar dan mengembalikan S_OK, karena aplikasi tidak perlu membuat cabang topologi untuk aliran kecuali dipilih.
  3. Jika aliran dipilih, fungsi menyelesaikan cabang topologi sebagai berikut:
    1. Membuat objek aktivasi untuk sink, dengan memanggil fungsi CreateMediaSinkActivate yang ditentukan aplikasi. Fungsi ini ditampilkan di bagian berikutnya.
    2. Menambahkan simpul sumber ke topologi. Kode untuk langkah ini ditampilkan dalam topik Membuat Simpul Sumber.
    3. Menambahkan simpul output ke topologi. Kode untuk langkah ini ditampilkan dalam topik Membuat Simpul Output.
    4. Menyambungkan dua simpul dengan memanggil IMFTopologyNode::ConnectOutput pada simpul sumber. Dengan menghubungkan simpul, aplikasi menunjukkan bahwa simpul upstream harus mengirimkan data ke simpul hilir. Simpul sumber memiliki satu output, dan simpul output memiliki satu input, sehingga kedua indeks aliran adalah nol.

Aplikasi yang lebih canggih dapat memilih atau membatalkan pilihan aliran, alih-alih menggunakan konfigurasi default sumber. Sumber dapat memiliki beberapa aliran, dan salah satunya mungkin dipilih secara default. Deskriptor presentasi sumber media memiliki sekumpulan pilihan streaming default. Dalam file video sederhana dengan aliran audio tunggal dan aliran video, sumber media biasanya akan memilih kedua aliran secara default. Namun, file mungkin memiliki beberapa aliran audio untuk bahasa yang berbeda, atau beberapa aliran video yang dikodekan pada laju bit yang berbeda. Dalam hal ini, beberapa aliran tidak akan dipilih secara default. Aplikasi dapat mengubah pilihan dengan memanggil IMFPresentationDescriptor::SelectStream dan IMFPresentationDescriptor::D eselectStream pada deskriptor presentasi.

Membuat Sink Media

Fungsi berikutnya membuat objek aktivasi untuk sink media EVR atau 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;
}

Fungsi ini melakukan langkah-langkah berikut:

  1. Memanggil IMFStreamDescriptor::GetMediaTypeHandler pada deskriptor aliran. Metode ini mengembalikan penunjuk antarmuka IMFMediaTypeHandler .

  2. Memanggil IMFMediaTypeHandler::GetMajorType. Metode ini mengembalikan GUID jenis utama untuk aliran.

  3. Jika jenis aliran adalah audio, fungsi memanggil MFCreateAudioRendererActivate untuk membuat objek aktivasi perender audio. Jika jenis stream adalah video, fungsi memanggil MFCreateVideoRendererActivate untuk membuat objek aktivasi perender video. Kedua fungsi ini mengembalikan penunjuk ke antarmuka IMFActivate . Pointer ini digunakan untuk menginisialisasi simpul output untuk sink, seperti yang ditunjukkan sebelumnya.

Untuk jenis aliran lainnya, contoh ini mengembalikan kode kesalahan. Atau, Anda hanya dapat membatalkan pilihan aliran.

Langkah berikutnya

Untuk memutar satu file media pada satu waktu, antrekan topologi pada Sesi Media dengan memanggil IMFMediaSession::SetTopology. Sesi Media akan menggunakan pemuat topologi untuk menyelesaikan topologi. Untuk contoh lengkapnya, lihat Cara Memutar File Media dengan Media Foundation.

Cara Memainkan File Media yang Tidak Terlindungi

Sesi Media

Topologi