재생 토폴로지 만들기

이 항목에서는 오디오 또는 비디오 재생을 위한 토폴로지를 만드는 방법을 설명합니다. 기본 재생의 경우 원본 노드가 출력 노드에 직접 연결되는 부분 토폴로지를 만들 수 있습니다. 디코더 또는 색 변환기와 같은 중간 변환에 대한 노드를 삽입할 필요가 없습니다. 미디어 세션은 토폴로지 로더를 사용하여 토폴로지를 resolve 토폴로지 로더는 필요한 변환을 삽입합니다.

토폴로지 만들기

미디어 원본에서 부분 재생 토폴로지를 만들기 위한 전체 단계는 다음과 같습니다.

  1. 미디어 원본을 만듭니다. 대부분의 경우 원본 확인자를 사용하여 미디어 원본을 만듭니다. 자세한 내용은 Source Resolver를 참조하세요.
  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 인터페이스에 대한 포인터입니다.
  • 스트림의 인덱스(0부터 시작)입니다.
  • 비디오 창에 대한 핸들입니다. 이 핸들은 비디오 스트림에만 사용됩니다.
//  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 을 호출하여 두 노드를 연결합니다. 애플리케이션은 노드를 연결하여 업스트림 노드가 다운스트림 노드에 데이터를 전달해야 임을 나타냅니다. 원본 노드에는 하나의 출력이 있고 출력 노드에는 하나의 입력이 있으므로 두 스트림 인덱스는 모두 0입니다.

고급 애플리케이션은 원본의 기본 구성을 사용하는 대신 스트림을 선택하거나 선택 취소할 수 있습니다. 원본에는 여러 스트림이 있을 수 있으며, 그 중 어느 스트림도 기본적으로 선택될 수 있습니다. 미디어 원본의 프레젠테이션 설명자에는 기본 스트림 선택 집합이 있습니다. 단일 오디오 스트림 및 비디오 스트림이 있는 간단한 비디오 파일에서 미디어 원본은 일반적으로 기본적으로 두 스트림을 모두 선택합니다. 그러나 파일에는 다른 언어에 대한 여러 오디오 스트림 또는 서로 다른 비트 전송률로 인코딩된 여러 비디오 스트림이 있을 수 있습니다. 이 경우 일부 스트림은 기본적으로 선택되지 않습니다. 애플리케이션은 프레젠테이션 설명자에서 IMFPresentationDescriptor::SelectStreamIMFPresentationDescriptor::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. 스트림 유형이 오디오인 경우 함수는 MFCreateAudioRendererActivate 를 호출하여 오디오 렌더러 활성화 개체를 만듭니다. 스트림 유형이 비디오인 경우 함수는 MFCreateVideoRendererActivate 를 호출하여 비디오 렌더러 활성화 개체를 만듭니다. 이러한 두 함수는 모두 IMFActivate 인터페이스에 대한 포인터를 반환합니다. 이 포인터는 이전에 표시된 것처럼 싱크의 출력 노드를 초기화하는 데 사용됩니다.

다른 스트림 형식의 경우 이 예제에서는 오류 코드를 반환합니다. 또는 스트림을 선택 취소하기만 하면 됩니다.

다음 단계

한 번에 하나의 미디어 파일을 재생하려면 IMFMediaSession::SetTopology를 호출하여 미디어 세션에서 토폴로지를 큐에 대기합니다. 미디어 세션은 토폴로지 로더를 사용하여 토폴로지를 resolve. 전체 예제는 Media Foundation을 사용하여 미디어 파일을 재생하는 방법을 참조하세요.

보호되지 않는 미디어 파일을 재생하는 방법

미디어 세션

토폴로지