DirectShow播放器(LAVFilter + EVR)开发例子
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)开发例子相关推荐
- 搭建rtmp直播流服务之4:videojs和ckPlayer开源播放器二次开发(播放rtmp、hls直播流及普通视频)...
前面几章讲解了使用 nginx-rtmp搭建直播流媒体服务器; ffmpeg推流到nginx-rtmp服务器; java通过命令行调用ffmpeg实现推流服务; 从数据源获取,到使用ffmpeg推流, ...
- android音乐播放器ppt,基于Android音乐播放器设计与开发.ppt
基于Android音乐播放器设计与开发 毕业设计 基于Android的音乐播放器 设计与开发 设计概述 研究背景 研究意义 研究目标 系统演示 研究背景 随着Android智能手机的普及,用户越来越能 ...
- 播放器色觉辅助功能开发,助力提升色觉障碍用户的视频观看体验
本文同时发布于团队博客:https://blog.csdn.net/avlabs/article/details/80470370 本文包含以下内容 1.简单介绍人眼彩色视觉的基本原理 2.介绍如何利 ...
- 提升色觉障碍用户的视频观看体验——播放器色觉辅助功能开发
本文包含以下内容 1.简单介绍人眼彩色视觉的基本原理 2.介绍如何利用算法模拟色觉障碍用户所看到的画面 3.基于色觉障碍模拟算法,介绍几种色觉障碍辅助方法,这些方法可以帮助色觉障碍用户更好地分辨本来难 ...
- linux mplayer_移植mplayer播放器到EK200开发板的步骤浅析
mplayer是一款开源多媒体播放器,以gnu通用公共许可证发布 此款软件可在各主流操作系统使用,一方面它资源占用率低:另一方面,无论是音频还是视频方面,它支持的格式相当全面,能支持播放大部分的音频. ...
- linux mplayer_移植mplayer播放器到MY-IMX6-EK200开发板的步骤浅析
mplayer是一款开源多媒体播放器,以gnu通用公共许可证发布. 此款软件可在各主流操作系统使用,一方面它资源占用率低:另一方面,无论是音频还是视频方面,它支持的格式相当全面,能支持播放大部分的音频 ...
- Linux播放器MPlayer嵌入式开发系统中的应用
摘要:本文论述了MPlayer在嵌入式开发系统中的应用,说明了如何在嵌入式开发板的平台上实现MPlayer的配置.编译.安装及调试过程. 关键词:嵌入式系统:MPlayer:MEncoder 引言 嵌 ...
- python开发音乐播放器教程_python开发简易版在线音乐播放器示例代码
在线音乐播放器,使用python的Tkinter库做了一个界面,感觉这个库使用起来还是挺方便的,音乐的数据来自网易云音乐的一个接口,通过urllib.urlopen模块打开网址,使用Json模块进行数 ...
- 首度揭秘:这款GitHub 3400星的播放器是如何开发出来的
在字节跳动,有这样一支技术团队: 他们开源的项目在GitHub摘得3400多颗星,除了字节跳动,不少其他互联网公司也在用他们的产品: 他们经历过不少极限操作,<囧妈>2020年春节期间网络 ...
最新文章
- 技术图文:如何利用C#爬取CSDN的博客文章?
- luogu 1471
- 在哪里能收到python实例代码-用python做一个搜索引擎(Pylucene)的实例代码
- 信息安全与网络安全,你分清了吗?
- SQL server连接到服务器,无法连接到 (local)。在与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误。未找到或无法访问服务器。请验证实例名称是否正确并且 SQL Se
- 【论文解读】CVPR 2021 当之无愧的最佳论文奖:GIRAFFE,一种可控图像合成方法...
- 监控最佳实践--redis及业务接口
- 不要在viewWillDisappear:方法中移除通知
- HDU 3094 树上删边 NIM变形
- 【BZOJ4516】生成魔咒,后缀数组+Splay
- linux学习笔记整理
- java数字转换成大写字母
- 宏碁电脑重装win10系统教程,宏碁怎么重装系统win10
- 【股票融资融券模拟交易系统】开发与设计
- ipv4或ipv6没有访问权限的解决办法
- QPSK数字调制系统在AWGN信道下的BER仿真
- python中quadratic_python 练习题:定义一个函数quadratic(a, b, c),接收3个参数,返回一元二次方程ax^2+bx+c=0的两个解...
- c++贪吃蛇源代码 完整版
- 关于绿色建筑的发展促进,从技术厂家角度我们这么看!
- 微博封禁117个百万大V账号!