다음을 통해 공유


3단계: 필터 그래프 빌드

[이 페이지와 연결된 기능인 DirectShow는 레거시 기능입니다. MediaPlayer, IMFMediaEngine 및 Media Foundation의 오디오/비디오 캡처로 대체되었습니다. 이러한 기능은 Windows 10 및 Windows 11 최적화되었습니다. 가능한 경우 새 코드가 DirectShow 대신 Media Foundation에서 MediaPlayer, IMFMediaEngine오디오/비디오 캡처를 사용하는 것이 좋습니다. 가능한 경우 레거시 API를 사용하는 기존 코드를 다시 작성하여 새 API를 사용하도록 제안합니다.]

이 항목은 DirectShow의 오디오/비디오 재생 자습서의 3단계입니다. 전체 코드는 DirectShow 재생 예제 항목에 나와 있습니다.

다음 단계는 미디어 파일을 재생하는 필터 그래프를 빌드하는 것입니다.

미디어 파일 열기

메서드는 DShowPlayer::OpenFile 재생을 위해 미디어 파일을 엽니다. 이 메서드는 다음을 수행 합니다.

  1. 새(빈) 필터 그래프를 만듭니다.
  2. IGraphBuilder::AddSourceFilter를 호출하여 지정된 파일에 대한 원본 필터를 추가합니다.
  3. 원본 필터에서 스트림을 렌더링합니다.
HRESULT DShowPlayer::OpenFile(PCWSTR pszFileName)
{
    IBaseFilter *pSource = NULL;

    // Create a new filter graph. (This also closes the old one, if any.)
    HRESULT hr = InitializeGraph();
    if (FAILED(hr))
    {
        goto done;
    }
    
    // Add the source filter to the graph.
    hr = m_pGraph->AddSourceFilter(pszFileName, NULL, &pSource);
    if (FAILED(hr))
    {
        goto done;
    }

    // Try to render the streams.
    hr = RenderStreams(pSource);

done:
    if (FAILED(hr))
    {
        TearDownGraph();
    }
    SafeRelease(&pSource);
    return hr;
}

Filter Graph Manager 만들기

메서드는 DShowPlayer::InitializeGraph 새 필터 그래프를 만듭니다. 이 메서드는 다음을 수행 합니다.

  1. CoCreateInstance를 호출하여 필터 그래프 관리자의 새 instance 만듭니다.
  2. IMediaControlIMediaEventEx 인터페이스에 대한 필터 그래프 관리자를 쿼리합니다.
  3. IMediaEventEx::SetNotifyWindow를 호출하여 이벤트 알림을 설정합니다. 자세한 내용은 DirectShow의 이벤트 알림을 참조하세요.
HRESULT DShowPlayer::InitializeGraph()
{
    TearDownGraph();

    // Create the Filter Graph Manager.
    HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, 
        CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_pGraph));
    if (FAILED(hr))
    {
        goto done;
    }

    hr = m_pGraph->QueryInterface(IID_PPV_ARGS(&m_pControl));
    if (FAILED(hr))
    {
        goto done;
    }

    hr = m_pGraph->QueryInterface(IID_PPV_ARGS(&m_pEvent));
    if (FAILED(hr))
    {
        goto done;
    }

    // Set up event notification.
    hr = m_pEvent->SetNotifyWindow((OAHWND)m_hwnd, WM_GRAPH_EVENT, NULL);
    if (FAILED(hr))
    {
        goto done;
    }

    m_state = STATE_STOPPED;

done:
    return hr;
}

스트림 렌더링

다음 단계는 원본 필터를 하나 이상의 렌더러 필터에 연결하는 것입니다.

메서드는 DShowPlayer::RenderStreams 다음 단계를 수행합니다.

  1. IFilterGraph2 인터페이스에 대한 Filter Graph Manager를 쿼리합니다.
  2. 필터 그래프에 비디오 렌더러 필터를 추가합니다.
  3. 오디오 재생을 지원하기 위해 DirectSound 렌더러 필터 를 필터 그래프에 추가합니다. 필터 그래프에 필터를 추가하는 방법에 대한 자세한 내용은 CLSID로 필터 추가를 참조하세요.
  4. 원본 필터의 출력 핀을 열거합니다. 핀 열거에 대한 자세한 내용은 핀 열거를 참조하세요.
  5. 각 핀에 대해 IFilterGraph2::RenderEx 메서드를 호출합니다. 이 메서드는 필요한 경우 중간 필터(예: 디코더)를 추가하여 출력 핀을 렌더러 필터에 연결합니다.
  6. 를 호출 CVideoRenderer::FinalizeGraph 하여 비디오 렌더러 초기화를 완료합니다.
  7. 해당 필터가 다른 필터에 연결되지 않은 경우 DirectSound 렌더러 필터를 제거합니다. 원본 파일에 오디오 스트림이 없는 경우 이 문제가 발생할 수 있습니다.
// Render the streams from a source filter. 

HRESULT DShowPlayer::RenderStreams(IBaseFilter *pSource)
{
    BOOL bRenderedAnyPin = FALSE;

    IFilterGraph2 *pGraph2 = NULL;
    IEnumPins *pEnum = NULL;
    IBaseFilter *pAudioRenderer = NULL;
    HRESULT hr = m_pGraph->QueryInterface(IID_PPV_ARGS(&pGraph2));
    if (FAILED(hr))
    {
        goto done;
    }

    // Add the video renderer to the graph
    hr = CreateVideoRenderer();
    if (FAILED(hr))
    {
        goto done;
    }

    // Add the DSound Renderer to the graph.
    hr = AddFilterByCLSID(m_pGraph, CLSID_DSoundRender, 
        &pAudioRenderer, L"Audio Renderer");
    if (FAILED(hr))
    {
        goto done;
    }

    // Enumerate the pins on the source filter.
    hr = pSource->EnumPins(&pEnum);
    if (FAILED(hr))
    {
        goto done;
    }

    // Loop through all the pins
    IPin *pPin;
    while (S_OK == pEnum->Next(1, &pPin, NULL))
    {           
        // Try to render this pin. 
        // It's OK if we fail some pins, if at least one pin renders.
        HRESULT hr2 = pGraph2->RenderEx(pPin, AM_RENDEREX_RENDERTOEXISTINGRENDERERS, NULL);

        pPin->Release();
        if (SUCCEEDED(hr2))
        {
            bRenderedAnyPin = TRUE;
        }
    }

    hr = m_pVideo->FinalizeGraph(m_pGraph);
    if (FAILED(hr))
    {
        goto done;
    }

    // Remove the audio renderer, if not used.
    BOOL bRemoved;
    hr = RemoveUnconnectedRenderer(m_pGraph, pAudioRenderer, &bRemoved);

done:
    SafeRelease(&pEnum);
    SafeRelease(&pAudioRenderer);
    SafeRelease(&pGraph2);

    // If we succeeded to this point, make sure we rendered at least one 
    // stream.
    if (SUCCEEDED(hr))
    {
        if (!bRenderedAnyPin)
        {
            hr = VFW_E_CANNOT_RENDER;
        }
    }
    return hr;
}

다음은 이전 예제에서 사용되는 함수에 대한 RemoveUnconnectedRenderer 코드입니다.

HRESULT RemoveUnconnectedRenderer(IGraphBuilder *pGraph, IBaseFilter *pRenderer, BOOL *pbRemoved)
{
    IPin *pPin = NULL;

    *pbRemoved = FALSE;

    // Look for a connected input pin on the renderer.

    HRESULT hr = FindConnectedPin(pRenderer, PINDIR_INPUT, &pPin);
    SafeRelease(&pPin);

    // If this function succeeds, the renderer is connected, so we don't remove it.
    // If it fails, it means the renderer is not connected to anything, so
    // we remove it.

    if (FAILED(hr))
    {
        hr = pGraph->RemoveFilter(pRenderer);
        *pbRemoved = TRUE;
    }

    return hr;
}

필터 그래프 해제

애플리케이션이 종료되면 다음 코드와 같이 필터 그래프를 해제해야 합니다.

void DShowPlayer::TearDownGraph()
{
    // Stop sending event messages
    if (m_pEvent)
    {
        m_pEvent->SetNotifyWindow((OAHWND)NULL, NULL, NULL);
    }

    SafeRelease(&m_pGraph);
    SafeRelease(&m_pControl);
    SafeRelease(&m_pEvent);

    delete m_pVideo;
    m_pVideo = NULL;

    m_state = STATE_NO_GRAPH;
}

다음: 4단계: Video Renderer 추가

DirectShow의 오디오/비디오 재생

DirectShow 재생 예제

필터 그래프 빌드

일반 Graph-Building 기술