视频播放 via DirectShow

DirectShow 简介

DirectShow(有时缩写为 DS 或 DShow),开发代号 Quartz,是微软在 ActiveMovie 和 Video for Windows 的基础上推出的新一代基于 COM 的流媒体处理的开发包,与 DirectX 开发包一起发布。DShow 使用一种叫 Filter Graph 的模型来管理整个数据流的处理过程,有了 DShow,我们可以很方便地从支持 WDM 驱动模型的采集卡上捕获数据,并且进行相应的后期处理乃至存储到文件中。这样使在多媒体数据库管理系统(MDBMS)中多媒体数据的存取变得更加方便。它广泛地支持各种媒体格式,包括 asf、mpeg、avi、dv、mp3、wav 等,为多媒体流的捕捉和回放提供了强有力的支持。

DirectShow 播放视频

MSDN 上有基于本文所用代码的类似文章,全英文的,有兴趣的请走 这边

播放流程

dshow v play

播放代码

以下是整个 DShow 播放过程的概要代码,略去各个函数的具体实现和资源释放:

hr = CoInitialize(NULL);

MainWindow *pWin = new MainWindow();
hr = pWin->Create(hInstance);
hr = pWin->Show(nCmdShow);

m_pPlayer = new DShowPlayer(m_hwnd);

// Set the event notification window.
hr = m_pPlayer->SetEventWindow(m_hwnd, WM_GRAPH_EVENT);
hr = m_pPlayer->OpenFile(szFileName);

m_pPlayer->Play();
m_pPlayer->Pause();
m_pPlayer->SetPosition(ONE_MSEC * pInfo->position); // Seek
m_pPlayer->Stop();

delete m_pPlayer;
delete pWin;
CoUninitialize();

DShowPlayer::OpenFile 函数

打开媒体文件,创建并连接 filter graph。

HRESULT DShowPlayer::OpenFile(const WCHAR* sFileName)
{
    HRESULT hr = S_OK;
    IBaseFilter *pSource = NULL;
    // Create a new filter graph. (This also closes the old one, if any.)
    hr = InitializeGraph();
    
    // Add the source filter to the graph.
    hr = m_pGraph->AddSourceFilter(sFileName, NULL, &pSource);
    
    // Try to render the streams.
    hr = RenderStreams(pSource);
    
    // Get the seeking capabilities.
    hr = m_pSeek->GetCapabilities(&m_seekCaps);
    
    // Set the volume.
    hr = UpdateVolume();
    
    // Update our state.
    m_state = STATE_STOPPED;
    
    SAFE_RELEASE(pSource);
    return hr;
}

DShowPlayer::InitializeGraph 函数

创建 filter graph,并获得相应的控制接口。

HRESULT DShowPlayer::InitializeGraph()
{
    HRESULT hr = S_OK;
    TearDownGraph();

    // Create the Filter Graph Manager.
    hr = CoCreateInstance( CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, 
                           IID_IGraphBuilder, (void**)&m_pGraph );

    // Query for graph interfaces.
    hr = m_pGraph->QueryInterface(IID_IMediaControl, (void**)&m_pControl);

    hr = m_pGraph->QueryInterface(IID_IMediaEventEx, (void**)&m_pEvent);

    hr = m_pGraph->QueryInterface(IID_IMediaSeeking, (void**)&m_pSeek);

    hr = m_pGraph->QueryInterface(IID_IBasicAudio, (void**)&m_pAudio);

    // Set up event notification.
    hr = m_pEvent->SetNotifyWindow((OAHWND)m_hwndEvent, m_EventMsg, NULL);
    return hr;
}

DShowPlayer::RenderStreams 函数

连接各个 filter 和 render。

HRESULT DShowPlayer::RenderStreams(IBaseFilter *pSource)
{
    BOOL bRenderedAnyPin = FALSE;
    IFilterGraph2 *pGraph2 = NULL;
    IEnumPins *pEnum = NULL;
    IBaseFilter *pAudioRenderer = NULL;
    
    hr = m_pGraph->QueryInterface(IID_IFilterGraph2, (void**)&pGraph2);
    RETURN_IF_FAILED(hr);
    
    hr = CreateVideoRenderer();
    RETURN_IF_FAILED(hr);
    
    hr = AddFilterByCLSID(m_pGraph, CLSID_DSoundRender, &pAudioRenderer, L"Audio Renderer");
    RETURN_IF_FAILED(hr);
    
    hr = pSource->EnumPins(&pEnum);
    IPin *pPin = NULL;
    while (S_OK == pEnum->Next(1, &pPin, NULL)) {
        HRESULT hr2 = pGraph2->RenderEx(pPin, AM_RENDEREX_RENDERTOEXISTINGRENDERERS, NULL);
        pPin->Release();
        if (SUCCEEDED(hr2))
            bRenderedAnyPin = TRUE;
    }
    
    // Remove un-used renderers.
    hr = m_pVideo->FinalizeGraph(m_pGraph);
    
    BOOL bAudRemoved = FALSE;
    hr = RemoveUnconnectedRenderer(m_pGraph, pAudioRenderer, &bAudRemoved);
    m_bAudioStream = !bAudRemoved;
    
    if (!bRenderedAnyPin)
        hr = VFW_E_CANNOT_RENDER;
    return hr;
}
DShowPlayer::CreateVideoRenderer 函数

关于 EVR,VMR7 和 VMR9 的区别请看 MSDN

HRESULT DShowPlayer::CreateVideoRenderer()
{
    HRESULT hr = E_FAIL;
    enum { Try_EVR, Try_VMR9, Try_VMR7 };
    
    for (DWORD i = 0; i <= Try_VMR7; i++) {
        switch (i) {
        case Try_EVR:
            m_pVideo = new EVR();
            break;
            
        case Try_VMR9:
            m_pVideo = new VMR9();
            break;
            
        case Try_VMR7:
            m_pVideo = new VMR7();
            break;
        }
        
        hr = m_pVideo->AddToGraph(m_pGraph, m_hwndVideo);
        if (SUCCEEDED(hr))
            break;
        SAFE_DELETE(m_pVideo);
    }
    
    return hr;
}

下面选择 EVR 继续。

EVR::AddToGraph 函数

把 EVR 加到 filter graph 中并初始化。

HRESULT EVR::AddToGraph(IGraphBuilder *pGraph, HWND hwnd)
{
    HRESULT hr = S_OK;
    IBaseFilter *pEVR = NULL;
    
    hr = AddFilterByCLSID(pGraph, CLSID_EnhancedVideoRenderer, &pEVR, L"EVR");
    RETURN_IF_FAILED(hr);
    
    hr = InitializeEVR(pEVR, hwnd, &m_pVideoDisplay);
    RETURN_IF_FAILED(hr);
    
    m_pEVR = pEVR;
    m_pEVR->AddRef();
    SAFE_RELEASE(pEVR);
    return hr;
}
EVR::InitializeEVR 函数

绑定 EVR 和 窗口句柄,设置视频显示宽高比为原始比例(通常会使得视频窗口边上部分涂黑)。

HRESULT InitializeEVR( IBaseFilter *pEVR, HWND hwnd, IMFVideoDisplayControl** ppDisplayControl ) 
{ 
    HRESULT hr = S_OK;
    IMFGetService *pService = NULL;
    IMFVideoDisplayControl *pDisplay = NULL;
    
    hr = pEVR->QueryInterface(__uuidof(IMFGetService), (void**)&pService); 
    hr = pService->GetService(MR_VIDEO_RENDER_SERVICE, __uuidof(IMFVideoDisplayControl), (void**)&pDisplay);
    hr = pDisplay->SetVideoWindow(hwnd);
    
    // Preserve aspect ratio by letter-boxing
    hr = pDisplay->SetAspectRatioMode(MFVideoARMode_PreservePicture);
    *ppDisplayControl = pDisplay;
    (*ppDisplayControl)->AddRef();
    
    SAFE_RELEASE(pService);
    SAFE_RELEASE(pDisplay);
    return hr; 
}

DShowPlayer::Play & Stop 函数

顾名思义。

HRESULT DShowPlayer::Play()
{
    if (m_state != STATE_PAUSED && m_state != STATE_STOPPED)
        return VFW_E_WRONG_STATE;

    HRESULT hr = m_pControl->Run();
    if (SUCCEEDED(hr))
        m_state = STATE_RUNNING;

    return hr;
}

HRESULT DShowPlayer::Stop()
{
    if (m_state != STATE_RUNNING && m_state != STATE_PAUSED)
        return VFW_E_WRONG_STATE;

    HRESULT hr = m_pControl->Stop();
    if (SUCCEEDED(hr))
        m_state = STATE_STOPPED;

    return hr;
}

DShowPlayer::SetPosition 函数

即 seek 功能。

HRESULT DShowPlayer::SetPosition(REFERENCE_TIME pos)
{
    if (m_pControl == NULL || m_pSeek == NULL)
        return E_UNEXPECTED;

    HRESULT hr = S_OK;

    hr = m_pSeek->SetPositions(&pos, AM_SEEKING_AbsolutePositioning,
        NULL, AM_SEEKING_NoPositioning);

    if (SUCCEEDED(hr)) {
        // If playback is stopped, we need to put the graph into the paused
        // state to update the video renderer with the new frame, and then stop 
        // the graph again. The IMediaControl::StopWhenReady does this.
        if (m_state == STATE_STOPPED)
            hr = m_pControl->StopWhenReady();
    }

    return hr;
}

DShow 播放视频的 Filter Graph

以下是播放一个 WMV 文件生成的 Filter Graph,包含五个模块:Source Filter, Audio Decoder, Audio Render, Video Decoder 和 Video Render 。
在这里插入图片描述

其他框架下的播放

请参考对应的文章。

Blueware
EOF

  • 0
    点赞
  • 0
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:精致技术 设计师:CSDN官方博客 返回首页
评论

打赏作者

CBlueware

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值