LAVFilter是一套著名的DirectShow插件,包括Demux,Video Decoder,AudioDecoder,播放文件所需要的几个重要插件都包含进去了,并且支持播放的视音频格式非常广泛,FFmpeg支持的它几乎都支持(因为它底层是调用FFmpeg)。LAVFilter是我们开发Directshow播放器必不可少的插件,值的一提的是它既支持软解又支持硬解,功能非常强大。在Vista以上系统硬解的时候渲染器必须要用EVR,很多关于directshow播放器的例子都是解码器连接VMR进行播放的,连接EVR的很少介绍,这篇文章就给大家讲一下怎么用LAV + EVR来播放视频。

假如我们要播放一个视频文件,那么用Directshow需要构建一个Filter链路图,以下就是播放一个文件的链路图例子:

上图连接的渲染器是Video Renderer(VMR),但是如果要切换到硬解模式,则连接的渲染器要改成EVR(enhanced video renderer)。

我写了一个类,封装了构建和运行DirectShow FilterGraph的一些流程,下面是类的声明:


// CHDVideoPlayGraphclass CHDVideoPlayGraph : public CWnd
{DECLARE_DYNAMIC(CHDVideoPlayGraph)public:CHDVideoPlayGraph();virtual ~CHDVideoPlayGraph();void SetNum(int num) { m_Num = num; }HRESULT   BuildGraph(LPCTSTR lpszSrcFile);void      StopCapture();BOOL      GetVideoSize(CSize & size);PLAYSTATE GetState() { return m_psCurrent; }BOOL      GetVideoStatis(DWORD & dwBitrate, UINT & dwFps);void      UnIntializeVideo();HRESULT   InitializeVideo(HWND hWnd);HRESULT  SetupVideoWindow(HWND hVideoWnd);void      ResizeVideoWindow();DWORD     GetCurrentBitrate(); void      SetVideoMediatype(AM_MEDIA_TYPE * pMt);void      SetAudioMediatype(AM_MEDIA_TYPE * pmt);void      SetCaptureCallback(VideoCaptureCB captureCB) { m_CaptureCB = captureCB; }void      CheckRgb24Buffer();void      OnRecvVideo(int nNum,PBYTE pBuffer,long BufferLen); //被回调函数调用BOOL      HasAudioStream() { return m_bHasAudio; }void      SetAudioStream(BOOL bEnable) { m_bHasAudio = bEnable; }//void      SetVideoRecvPort(int port);LRESULT   OnRestartPlaying(WPARAM wParam, LPARAM lParam);void      SetHarewareDecode(BOOL bFlag);void      SetVMRMode(int nVMR);int       GetFrameRate();
protected:DECLARE_MESSAGE_MAP()afx_msg HRESULT OnGraphNotify(WPARAM wp, LPARAM lp);afx_msg void OnPaint();void      DisplayMesg(TCHAR* szFormat, ...);LRESULT   ClearInterfaces(WPARAM wp, LPARAM lp);void      CloseInterfaces();HRESULT   AddGraphToRot(IUnknown* pUnkGraph, DWORD* pdwRegister);void      RemoveGraphFromRot(DWORD pdwRegister);HRESULT   HandleGraphEvent();HRESULT   GetInterfaces();HRESULT   SetSyncClock();void      GetSyncClock();HRESULT  RenderFilter(IBaseFilter * pFilter);private:UINT chFullScreen, chAlwaysOnTop;UINT            m_Num;CBrush          m_emptyBrush;DWORD           m_dwGraphRegister;HWND            m_hApp;IVideoWindow*   m_pVW; IMediaControl*  m_pMC;IMediaEventEx*  m_pME;IGraphBuilder*  m_pGraph;ICaptureGraphBuilder2 *m_pCapture;VideoCaptureCB   m_CaptureCB;IBaseFilter   * m_pRenderer;IBaseFilter   * m_pNullFilter;IBaseFilter    * m_pVideoDec;IBaseFilter    * m_pAudioDec;PLAYSTATE          m_psCurrent; BOOL               m_bHasAudio;AM_MEDIA_TYPE      m_AudioMediaType;AM_MEDIA_TYPE      m_VideoMediaType;ULONG              m_nWidth, m_nHeight;PBYTE              m_pRgb24;TCHAR              m_szFile[256];BOOL               m_bHarewareDecode; //1--硬解码, 0--软解码INT                m_nVMRRenderer; ///1--VMR7, 2--VMR9,3--EVRDWORD              m_dwStartTick;
};

我们首先要在代码中定义要引用到的几个Filter的CLSID,包括LAV和EVR的:

DEFINE_GUID(CLSID_EnhancedVideoRenderer,0xFA10746C, 0x9B63, 0x4B6C, 0xBC, 0x49, 0xFC, 0x30, 0x0E, 0xA5, 0xF2, 0x56);DEFINE_GUID(CLSID_LAVSplitter,0x171252A0, 0x8820, 0x4AFE, 0x9D, 0xF8, 0x5C, 0x92, 0xB2, 0xD6, 0x6B, 0x04);DEFINE_GUID(CLSID_LAVSource, 0xB98D13E7, 0x55DB, 0x4385, 0xA3, 0x3D, 0x09, 0xFD, 0x1B, 0xA2, 0x63, 0x38);DEFINE_GUID(CLSID_LAVVideoDec,0xEE30215D, 0x164F, 0x4A92, 0xA4, 0xEB, 0x9D, 0x4C, 0x13, 0x39, 0x0F, 0x9F);DEFINE_GUID(CLSID_LAVAudioDec,0xE8E73B6B, 0x4CB3, 0x44A4, 0xBE, 0x99, 0x4F, 0x7B, 0xCB, 0x96, 0xE4, 0x91);

为了支持软解和硬解,并且能连接不同的渲染器Renderer,我在类声明里定义了两个变量:一个是解码模式,另外一个是连接的渲染器类型。方便对不同的模式进行切换。

    BOOL               m_bHarewareDecode; //1--硬解码, 0--软解码INT                m_nVMRRenderer; 0--GDI, 1--VMR7, 2--VMR9

接着,我们看一下怎么构建DirectShow链接图:


HRESULT CHDVideoPlayGraph::BuildGraph(LPCTSTR lpszSrcFile)
{HRESULT hr;USES_CONVERSION;CFileFind find;if(find.FindFile(lpszSrcFile) == FALSE){AfxMessageBox("Can not Find File");return E_FAIL;}_tcscpy(m_szFile, lpszSrcFile);// Get DirectShow interfaceshr = GetInterfaces();if (FAILED(hr)){DisplayMesg(TEXT("Failed to get video interfaces!  hr=0x%x"), hr);return hr;}BOOL bIsWin7 = FALSE;TCHAR sRC[64] = {0};OSVERSIONINFO versionInfo;versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);if(::GetVersionEx(&versionInfo)){switch(versionInfo.dwPlatformId){case VER_PLATFORM_WIN32_WINDOWS:if(versionInfo.dwMinorVersion >= 0 && versionInfo.dwMinorVersion <= 9 ){_tcscpy( sRC, _T("95"));}else if(versionInfo.dwMinorVersion >= 10 && versionInfo.dwMinorVersion <= 89 ){_tcscpy(sRC, _T("98"));}else if(versionInfo.dwMinorVersion == 90 ){_tcscpy(sRC, _T("Me"));}break;case VER_PLATFORM_WIN32_NT:if(versionInfo.dwMajorVersion == 4){}else if(versionInfo.dwMajorVersion == 5){switch(versionInfo.dwMinorVersion){case 0:_tcscpy(sRC, _T("2000"));break;case 1:_tcscpy(sRC, _T("XP"));break;case 2:_tcscpy(sRC, _T("2003"));break;}//switch}// else if(versionInfo.dwMajorVersion == 5)else {_tcscpy(sRC, _T("Windows7"));bIsWin7 = TRUE;}break;default:break;}  //switch}else{OutputDebugString("GetVersionEx Errro!\n");}char szSysInfo[100] = {0};sprintf(szSysInfo, "OSType is: %s \n", sRC);OutputDebugString(szSysInfo);CComPtr<IBaseFilter> pSrcBaseFilter;hr = ::AddFilterByCLSID(m_pGraph, CLSID_LAVSource, L"LAVSource Filter", &pSrcBaseFilter);if (FAILED(hr)){MessageBox("Add LAVSource Filter  Failed", "Error");return hr;}IFileSourceFilter * pFileSource = NULL;hr = pSrcBaseFilter->QueryInterface(IID_IFileSourceFilter, (void**)&pFileSource);if(pFileSource){hr = pFileSource->Load(A2W(m_szFile), NULL);pFileSource->Release();}if(FAILED(hr)){MessageBox("Load File Failed", "Error");return hr;}//Add Video Decode filterhr = ::AddFilterByCLSID(m_pGraph, CLSID_LAVVideoDec, L"LAVVideo  Decoder", &m_pVideoDec);if (FAILED(hr)){return hr;   }ILAVVideoSettings  * pLAVSetting = NULL;hr = m_pVideoDec->QueryInterface(__uuidof(ILAVVideoSettings), (void**)&pLAVSetting);if(SUCCEEDED(hr)){pLAVSetting->SetHWAccel(m_bHarewareDecode ? HWAccel_DXVA2Native : HWAccel_None);TRACE("SetHWAccel HW: %d \n", m_bHarewareDecode);pLAVSetting->Release();}if(bIsWin7){CComPtr<IMFVideoDisplayControl> m_spDisplayCtrl;  CComPtr<IMFGetService> pGetService;  //Add Enhanced Video Renderer hr = ::AddFilterByCLSID(m_pGraph,  CLSID_EnhancedVideoRenderer,  L"Enhanced Video Renderer ", &m_pRenderer);if(FAILED(hr)){return hr;}hr = m_pRenderer->QueryInterface((__uuidof(IMFGetService)),(VOID ** )&pGetService);  if(FAILED(hr)){return hr;}hr = pGetService->GetService(MR_VIDEO_RENDER_SERVICE,__uuidof(IMFVideoDisplayControl), (void **)&m_spDisplayCtrl);  RECT Rect;  ::GetClientRect (m_hApp, &Rect);  m_spDisplayCtrl->SetVideoWindow (m_hApp);  m_spDisplayCtrl->SetAspectRatioMode(MFVideoARMode_None);  m_spDisplayCtrl->SetVideoPosition(NULL, &Rect);  }else{//Add Video Renderer if(m_nVMRRenderer == 0){hr = ::AddFilterByCLSID(m_pGraph, CLSID_VideoRenderer, L"Video Renderer ", &m_pRenderer);if(FAILED(hr)){return hr;}}else if(m_nVMRRenderer == 1){hr = ::AddFilterByCLSID(m_pGraph, CLSID_VideoMixingRenderer, L"Video Renderer ", &m_pRenderer);if(FAILED(hr)){return hr;}}else if(m_nVMRRenderer == 2){hr = ::AddFilterByCLSID(m_pGraph, CLSID_VideoMixingRenderer9, L"Video Renderer ", &m_pRenderer);if(FAILED(hr)){return hr;}}hr = m_pGraph->QueryInterface(IID_IVideoWindow, (LPVOID *) &m_pVW);if (FAILED(hr))return hr;}hr = RenderFilter(pSrcBaseFilter);if (FAILED(hr)){OutputDebugString(_T("RenderFilter Failed. \n"));return hr;}hr = SetupVideoWindow(m_hWnd);//SetSyncClock();IPin * pRenderInputPin = NULL;pRenderInputPin = GetInPin(m_pRenderer, 0);if(pRenderInputPin == NULL)return E_FAIL;AM_MEDIA_TYPE  amType;AM_MEDIA_TYPE * pmt = &amType;hr = pRenderInputPin->ConnectionMediaType(pmt);if(SUCCEEDED(hr)){if(pmt->formattype == FORMAT_VideoInfo){VIDEOINFOHEADER * pVidHdr = (VIDEOINFOHEADER*) pmt->pbFormat;m_nWidth   = pVidHdr->bmiHeader.biWidth;m_nHeight  = pVidHdr->bmiHeader.biHeight;}else if(pmt->formattype == FORMAT_VideoInfo2){VIDEOINFOHEADER2 * pVidHdr = (VIDEOINFOHEADER2*) pmt->pbFormat;m_nWidth   = pVidHdr->bmiHeader.biWidth;m_nHeight  = pVidHdr->bmiHeader.biHeight;}else if (pmt->formattype == FORMAT_MPEGVideo){MPEG1VIDEOINFO *pVidHdr = (MPEG1VIDEOINFO *) pmt->pbFormat;m_nWidth  = pVidHdr->hdr.bmiHeader.biWidth;m_nHeight = pVidHdr->hdr.bmiHeader.biHeight;} else if (pmt->formattype  == FORMAT_MPEG2Video){MPEG2VIDEOINFO *pVidHdr = (MPEG2VIDEOINFO *) pmt->pbFormat;m_nWidth = pVidHdr->hdr.bmiHeader.biWidth;m_nHeight = pVidHdr->hdr.bmiHeader.biHeight;} else{}FreeMediaType(amType);}#ifdef REGISTER_FILTERGRAPH// Add our graph to the running object table, which will allow// the GraphEdit application to "spy" on our graphhr = AddGraphToRot(m_pGraph, &m_dwGraphRegister);if (FAILED(hr)){DisplayMesg(TEXT("Failed to register filter graph with ROT!  hr=0x%x"), hr);m_dwGraphRegister = 0;}
#endif// Start previewing video datahr = m_pMC->Run();if (FAILED(hr)){DisplayMesg(TEXT("Couldn't run the graph!  hr=0x%x"), hr);return hr;}// Remember current statem_psCurrent = RUNNING;m_dwStartTick = GetTickCount();return S_OK;
}

上面的BuildGraph函数,首先调用GetInterfaces接口获取Directshow组件的一些接口:

HRESULT CHDVideoPlayGraph::GetInterfaces()
{HRESULT hr;if(m_pGraph == NULL){// Create the filter graphhr = CoCreateInstance (CLSID_FilterGraph, NULL, CLSCTX_INPROC,IID_IGraphBuilder, (void **) &m_pGraph);if (FAILED(hr)){TRACE("CoCreateInstance() failed \n");return hr;}}elsereturn E_FAIL ;//hr = m_pGraph->QueryInterface(IID_IVideoWindow, (LPVOID *) &m_pVW); //使用VMR时需要获取该接口进行视频窗口控制,但EVR不需要获取该接口//   if (FAILED(hr))//       return hr;// Obtain interfaces for media control and Video Windowhr = m_pGraph->QueryInterface(IID_IMediaControl,(LPVOID *) &m_pMC);if (FAILED(hr))return hr;hr = m_pGraph->QueryInterface(IID_IMediaEvent, (LPVOID *) &m_pME);if (FAILED(hr))return hr;// Set the window handle used to process graph eventshr = m_pME->SetNotifyWindow((OAHWND)m_hApp, WM_GRAPHNOTIFY, 0);//m_pME->CancelDefaultHandling(EC_DISPLAY_CHANGED);return hr;
}

然后,依次把LAV Source Filter,LAV Video Decode Filter加进去。代码中会根据系统版本决定优先插入哪个Video Renderer,如果是Win7以上,则插入EVR;其他系统版本则根据m_nVMRRenderer的类型值来选择对应的Renderer(VMR7,VMR9)。

接着,调用RenderFilter自动将上面的Filter连接起来(可能还会加入其他Filter)。


HRESULT CHDVideoPlayGraph:: RenderFilter(IBaseFilter * pFilter)
{CComPtr< IEnumPins > pEnum;if (!pFilter)return E_POINTER;HRESULT hr = pFilter->EnumPins(&pEnum);if(FAILED(hr)) return hr;ULONG ulFound;IPin *pPin;hr = E_FAIL;int nOutputPinCount = 0;int nRenderedCount = 0;while(S_OK == pEnum->Next(1, &pPin, &ulFound)){PIN_DIRECTION pindir = (PIN_DIRECTION)3;pPin->QueryDirection(&pindir);if(pindir == PINDIR_OUTPUT){IPin * pPin2 = NULL;pPin->ConnectedTo(&pPin2);if(pPin2){//pPin->Disconnect(); //先断开连接pPin2->Release();pPin2 = NULL;}else{hr = m_pGraph->Render(pPin); //自动连接Output Pin的下一级链路if(SUCCEEDED(hr))nRenderedCount++;}nOutputPinCount++;} pPin->Release();} return nRenderedCount > 0 ? (nOutputPinCount == nRenderedCount ? S_OK : S_FALSE) : E_FAIL;
}

例子代码链接:https://download.csdn.net/download/zhoubotong2012/11874281

DirectShow播放器(LAVFilter + EVR)开发例子相关推荐

  1. 搭建rtmp直播流服务之4:videojs和ckPlayer开源播放器二次开发(播放rtmp、hls直播流及普通视频)...

    前面几章讲解了使用 nginx-rtmp搭建直播流媒体服务器; ffmpeg推流到nginx-rtmp服务器; java通过命令行调用ffmpeg实现推流服务; 从数据源获取,到使用ffmpeg推流, ...

  2. android音乐播放器ppt,基于Android音乐播放器设计与开发.ppt

    基于Android音乐播放器设计与开发 毕业设计 基于Android的音乐播放器 设计与开发 设计概述 研究背景 研究意义 研究目标 系统演示 研究背景 随着Android智能手机的普及,用户越来越能 ...

  3. 播放器色觉辅助功能开发,助力提升色觉障碍用户的视频观看体验

    本文同时发布于团队博客:https://blog.csdn.net/avlabs/article/details/80470370 本文包含以下内容 1.简单介绍人眼彩色视觉的基本原理 2.介绍如何利 ...

  4. 提升色觉障碍用户的视频观看体验——播放器色觉辅助功能开发

    本文包含以下内容 1.简单介绍人眼彩色视觉的基本原理 2.介绍如何利用算法模拟色觉障碍用户所看到的画面 3.基于色觉障碍模拟算法,介绍几种色觉障碍辅助方法,这些方法可以帮助色觉障碍用户更好地分辨本来难 ...

  5. linux mplayer_移植mplayer播放器到EK200开发板的步骤浅析

    mplayer是一款开源多媒体播放器,以gnu通用公共许可证发布 此款软件可在各主流操作系统使用,一方面它资源占用率低:另一方面,无论是音频还是视频方面,它支持的格式相当全面,能支持播放大部分的音频. ...

  6. linux mplayer_移植mplayer播放器到MY-IMX6-EK200开发板的步骤浅析

    mplayer是一款开源多媒体播放器,以gnu通用公共许可证发布. 此款软件可在各主流操作系统使用,一方面它资源占用率低:另一方面,无论是音频还是视频方面,它支持的格式相当全面,能支持播放大部分的音频 ...

  7. Linux播放器MPlayer嵌入式开发系统中的应用

    摘要:本文论述了MPlayer在嵌入式开发系统中的应用,说明了如何在嵌入式开发板的平台上实现MPlayer的配置.编译.安装及调试过程. 关键词:嵌入式系统:MPlayer:MEncoder 引言 嵌 ...

  8. python开发音乐播放器教程_python开发简易版在线音乐播放器示例代码

    在线音乐播放器,使用python的Tkinter库做了一个界面,感觉这个库使用起来还是挺方便的,音乐的数据来自网易云音乐的一个接口,通过urllib.urlopen模块打开网址,使用Json模块进行数 ...

  9. 首度揭秘:这款GitHub 3400星的播放器是如何开发出来的

    在字节跳动,有这样一支技术团队: 他们开源的项目在GitHub摘得3400多颗星,除了字节跳动,不少其他互联网公司也在用他们的产品: 他们经历过不少极限操作,<囧妈>2020年春节期间网络 ...

最新文章

  1. 技术图文:如何利用C#爬取CSDN的博客文章?
  2. luogu 1471
  3. 在哪里能收到python实例代码-用python做一个搜索引擎(Pylucene)的实例代码
  4. 信息安全与网络安全,你分清了吗?
  5. SQL server连接到服务器,无法连接到 (local)。在与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误。未找到或无法访问服务器。请验证实例名称是否正确并且 SQL Se
  6. 【论文解读】CVPR 2021 当之无愧的最佳论文奖:GIRAFFE,一种可控图像合成方法...
  7. 监控最佳实践--redis及业务接口
  8. 不要在viewWillDisappear:方法中移除通知
  9. HDU 3094 树上删边 NIM变形
  10. 【BZOJ4516】生成魔咒,后缀数组+Splay
  11. linux学习笔记整理
  12. java数字转换成大写字母
  13. 宏碁电脑重装win10系统教程,宏碁怎么重装系统win10
  14. 【股票融资融券模拟交易系统】开发与设计
  15. ipv4或ipv6没有访问权限的解决办法
  16. QPSK数字调制系统在AWGN信道下的BER仿真
  17. python中quadratic_python 练习题:定义一个函数quadratic(a, b, c),接收3个参数,返回一元二次方程ax^2+bx+c=0的两个解...
  18. c++贪吃蛇源代码 完整版
  19. 关于绿色建筑的发展促进,从技术厂家角度我们这么看!
  20. 微博封禁117个百万大V账号!

热门文章

  1. 基于nacos的分布式服务治理
  2. eBay携手走秀再攻中国市场
  3. python日历小程序_微信小程序日历效果
  4. 英式英语 vs 美式英语
  5. 百家号大鱼号头条号企鹅号的自媒体视频从哪里来?
  6. matlab GUI 数据存储,回放,显示波形 (以脉搏波和袖带压为例)
  7. LEETCODE-刷题个人笔记 Python(1-400)
  8. [每日一题] 45. 微信红包(模拟、map)
  9. 清远城市品牌将携五大百亿农业产业区域公用品牌齐亮相
  10. 【LINUX】本地安装宝塔+花生壳端口映射搭建网站