DirectShow框架简介

DirectShow框架是多媒体播放框架上一个非常经典的框架,现在已经十多年了,在Windows平台上依然无法替代,非常值得去学习研究。个人觉得从设计模式的角度上看,directshow框架的灵活性、复用性、可维护性、可拓展性这些方面做得非常不错,也是它经久不衰历久弥新的一个原因,现在的很多第三方的decoder和filter都基于directshow框架开发,可以很灵活的移植到directshow视频框架中,例如视骏开发的HEVC/H.265解码器,都可以直接挂载在directshow框架中进行视频解码。


图形化理解DirectShow

推荐一款工具GraphStudio,了解DirectShow框架必备工具,软件截图如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vywGjHIE-1626603489412)(http://of6x0sb2r.bkt.clouddn.com/graphstudio.png-WaterMark)]
我们点击Graph可以插入我们在电脑系统中注册的Filter Render,默认情况下,我们将播放的视频加到GraphStudio中,会自动生成directshow的整个播放流程,然后就可以播放视频了。一般的播放效果流程如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ONNktbzr-1626603489414)(http://of6x0sb2r.bkt.clouddn.com/direcshow%E6%B5%81%E7%A8%8B%E5%9B%BE.png-WaterMark)]

GraphStudio会自动采用系统默认的一套FilterRender,如果安装了K-Lite Codec Pack,就可以修改系统默认的这一套,如下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DTTu8ava-1626603489415)(http://of6x0sb2r.bkt.clouddn.com/K_Lite_kit.png-WaterMark)]
我们想测试我们自己的FilterRender,都可以自定义插入,下面就以DirectShow中植入视骏的HEVC解码器为例子,了解DirecShow的整个播放流程,如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MavdR37W-1626603489417)(http://of6x0sb2r.bkt.clouddn.com/HEVCDecoder.png-WaterMark)]

DirectShow播放HEVC视频

可以参考雷老师关于DirectShow的介绍,
地址:http://blog.csdn.net/leixiaohua1020/article/details/42372419
播放的流程如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3lWHsY1N-1626603489418)(http://of6x0sb2r.bkt.clouddn.com/directshowplay.png-WaterMark)]

整个播放我们可以抽象出三个步骤:

Created with Raphaël 2.3.0注册Filter和Render链接Filter和Render执行播放RenderFile
  • 注册:播放HEVC视频需要的Strongene Mpeg-4 Demultiplexor,Lentoid HEVC Decoder,Video Renderer
  • 链接:相当于上面GraphStudio图中的链接箭头
  • 播放:将视频导入播放链路中,开始播放

注册Filter和Render

解码器属性

首先获取到这些Filter的Object nameCLSIDFilenameFilePath
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qhTGvbDM-1626603489418)(http://of6x0sb2r.bkt.clouddn.com/filterInfo.png-WaterMark)]
如果已经将filter注册进Windows的系统中,就只需要用到Object Name,为了避免重新注册导致冲突;

//Name:File Source (Async.) {E436EBB5-524F-11CE-9F53-0020AF0BA770}
/**
Windows自带File Source (Async.),dll路径:C:\Windows\SysWOW64\quartz.dll
CLSID  =
*///MP4 Splitter {61F47056-E400-43D3-AF1E-AB7DFFD4C4AD} Name: Strongene Mpeg-4 Demultiplexor
static const GUID CLSID_MP4Splitter = {0x61f47056,0xe400,0x43d3,{0xaf,0x1e,0xab,0x7d,0xff,0xd4,0xc4,0xad}};//h265-HEVC Decoder {658C5E1C-58E1-43CA-9D10-13735D465576} Name:Lentoid HEVC Decoder
static const GUID CLSID_HEVCDecoder = {0x658C5E1C,0x58E1,0x43CA,{0x9D,0x10,0x13,0x73,0x5D,0x46,0x55,0x76}};//Name:Video Mixing Renderer {B87BEB7B-8D29-423F-AE4D-6582C10175AC}
/**
Windows自带VideoRenderer,dll路径:C:\Windows\SysWOW64\quartz.dll
*/

声明DirectShow播放需要的类和变量

IGraphBuilder * mGraph;//创建一个Filter Graph Manager组件
IMediaControl * mMediaControl;//提供控制过滤器图表中多媒体数据流的方法,包括运行、暂停和停止
IMediaEventEx * mEvent;//继承自IMediaEvent接口,处理过滤器图表的事件
IBasicVideo *   mBasicVideo;//用于设置视频特性,如视频显示的目的区域和源区域
IBasicAudio *   mBasicAudio;//用于控制音频流的音量和平衡
IVideoWindow  * mVideoWindow;//定义一个视频窗口的控制对象
IMediaSeeking * mSeeking;//提供搜索数据流位置和设置播放速率的方法IBaseFilter *m_pSourceFilter = NULL;
IBaseFilter *m_pSplitter = NULL;
IBaseFilter *m_pSplitter = NULL;
IBaseFilter *m_pVideoHEVCDecoder = NULL;
IBaseFilter *m_pVideoRender = NULL; DWORD mObjectTableEntry = 0;

注册Filter:

  • 加载解码器并获取DllGetClassObject指针,loadFilter()
bool loadFilter(LPCSTR chAx)
{//加载Filter所在的dll文件m_hInst = ::LoadLibrary(chAx);if (NULL == m_hInst){return false;} //获取DllGetClassObject函数指针m_pDllGetClassObject = ( DLL_GET_CLASS_OBJECT )::GetProcAddress(m_hInst, "DllGetClassObject");if (NULL == m_pDllGetClassObject){return false;}return true;
}

  • 动态创建解码器对象,createFilter()
bool createFilter(GUID clId, IBaseFilter** pBaseFilter)
{if (NULL == m_pDllGetClassObject){return false;}//获取类工厂接口IClassFactory *p_IClassFactory = NULL;if (FAILED( m_pDllGetClassObject(clId, IID_IClassFactory, (void **)&p_IClassFactory) )){return false;}//创建与类厂相关联的COM对象(Filter),并获取其IBaseFilter接口p_IClassFactory->CreateInstance(NULL, IID_IBaseFilter, (void **)pBaseFilter);p_IClassFactory->Release();p_IClassFactory = NULL;if ((NULL == pBaseFilter) || (NULL == *pBaseFilter)){return false;}return true;
}

  • 申明一个方法函数InitRegister(),
bool InitRegister()
{mGraph=NULL;mMediaControl=NULL;mEvent=NULL;mBasicVideo=NULL;mBasicAudio=NULL;mVideoWindow=NULL;mSeeking=NULL;if(!mGraph){//建立 filter graph managerHRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,IID_IGraphBuilder, (void **)&mGraph);if(mGraph != NULL){LPCSTR  splitterDll, HEVCDecoderDll;LPCWSTR splitterName, HEVCDecoderName;GUID  splitterGuid, HEVCDecoderGuid;//Source Filterhr = CoCreateInstance(CLSID_AsyncReader, NULL, CLSCTX_INPROC,IID_IBaseFilter, (void **)&m_pSourceFilter);if(hr == S_OK){mGraph->AddFilter(m_pSourceFilter, L"File Source Filter");}else{cout << "load source filter failed" << endl;return false;}//Splitter splitterDll = ".\\Codec\\mp4demux.dll";splitterName = L"Strongene Mpeg-4 Demultiplexor";splitterGuid = CLSID_SMp4Demultiplexor;//HEVCDecoderHEVCDecoderDll = ".\\Codec\\hevcdecfltr.dll";HEVCDecoderName = L"Lentoid HEVC Decoder";HEVCDecoderGuid = CLSID_HEVCDecoder;//Splitterif (loadFilter(splitterDll)){if (createFilter(splitterGuid, &m_pSplitter)){mGraph->AddFilter(m_pSplitter, splitterName);}else{cout << "add spliter failed" << endl;return false;}}else{cout << "load splitter faild" << endl;return false;}//HEVC Decoderif (loadFilter(HEVCDecoderDll)){if (createFilter(HEVCDecoderGuid, &m_pVideoHEVCDecoder)){mGraph->AddFilter(m_pVideoHEVCDecoder, HEVCDecoderName);}else{cout << "add decoder failed" << endlreturn false;}}else{cout << "load decoder failed!" << endl;return false;}//Video Rendererhr = CoCreateInstance(CLSID_VideoMixingRenderer9, NULL, CLSCTX_INPROC, IID_IBaseFilter, (void **)&m_pVideoRender);if(hr == S_OK){mGraph->AddFilter(m_pVideoRender, L"Video Mixing Renderer 9");}else{cout << "load video renderer failed" << endl;return false;}}}
}

链接Filter和Renderer

  • 查找空闲的filter的接口
HRESULT GetUnconnectedPin( IBaseFilter *pFilter, PIN_DIRECTION PinDir, IPin **ppPin, int iNum )
{*ppPin = NULL;IEnumPins* pEnum = NULL;IPin* pPin = NULL;int nIndex = 0;HRESULT hr = pFilter->EnumPins(&pEnum);if (FAILED(hr)){return hr;}while(pEnum->Next(1, &pPin, NULL) == S_OK){PIN_DIRECTION ThisPinDir;pPin->QueryDirection(&ThisPinDir);if (ThisPinDir == PinDir)  //如果out/in类型符合{nIndex ++;IPin *pTmp = NULL;hr = pPin->ConnectedTo(&pTmp);  //如果已经被连接if (SUCCEEDED(hr))  {pTmp->Release();}else  {if(nIndex == iNum)  //若端口号与传入端口号同{pEnum->Release();*ppPin = pPin;pPin->Release();return S_OK;}}}pPin->Release();}pEnum->Release();cout << "GetUnconnectedPin ----> Next is null" << endl;return false;
}
  • 查找空闲的splitter的空闲指针接口
HRESULT GetUnconnectedMajortypePin( IBaseFilter *pFilter, PIN_DIRECTION PinDir, IPin **ppPin, GUID myMajortype)
{*ppPin = NULL;IEnumPins* pEnum = NULL;IPin* pPin = NULL;int nIndex = 0;IEnumMediaTypes *pType;  AM_MEDIA_TYPE  *pMType;  HRESULT hr = pFilter->EnumPins(&pEnum);if (FAILED(hr)){return hr;}while(pEnum->Next(1, &pPin, NULL) == S_OK){PIN_DIRECTION ThisPinDir;pPin->QueryDirection(&ThisPinDir);if (ThisPinDir == PinDir)  //如果out/in类型符合{nIndex ++;IPin *pTmp = NULL;hr = pPin->ConnectedTo(&pTmp);if (SUCCEEDED(hr))    //如果已经被连接{pTmp->Release();}else  {pPin->EnumMediaTypes(&pType);pType->Next(1, &pMType, 0);if(myMajortype == pMType->majortype)  //类型匹配{pEnum->Release();pType->Release();*ppPin = pPin;pPin->Release();return S_OK;}pType->Release();}}pPin->Release();}pEnum->Release();cout << "GetUnconnectedMajortypePin failed" << endl;return false;
}
  • DirectShow提供自动搜寻Filter,我们只需要将他们链接起来
bool LinkFilter()
{//定义pin对象IPin *ppinOut = NULL;IPin *ppinIn = NULL;IPin* Pin;//sourceOut -> Splitterif(m_pSourceFilter != NULL && m_pSplitter != NULL){hr = GetUnconnectedPin(m_pSourceFilter, PINDIR_OUTPUT, &Pin, 1); //查找Filter空闲的pinif (SUCCEEDED(hr)){ppinOut = Pin;          }hr = GetUnconnectedPin(m_pSplitter, PINDIR_INPUT, &Pin, 1);if (SUCCEEDED(hr)){ppinIn = Pin;          }if (FAILED(mGraph->Connect(ppinOut, ppinIn))){cout << "link source filter and spliter failed!" <<endl;return false;}}//Spliter -> hevcdecoder//解码HEVCif (m_pSplitter != NULL && m_pVideoHEVCDecoder != NULL){hr = GetUnconnectedMajortypePin(m_pSplitter, PINDIR_OUTPUT, &Pin, MEDIATYPE_Video);//查找splitter空闲的video outpinif (SUCCEEDED(hr)){ppinOut = Pin;}hr = GetUnconnectedPin(m_pVideoHEVCDecoder, PINDIR_INPUT, &Pin, 1);if (SUCCEEDED(hr)){ppinIn = Pin;}if (FAILED(mGraph->Connect(ppinOut, ppinIn))){return false;cout << "link splitter and decoder failed!" << endl;}}//hevcdecoder -> videorenderif (m_pVideoDecoder != NULL && m_pVideoRender != NULL){hr = GetUnconnectedPin(m_pVideoHEVCDecoder, PINDIR_OUTPUT, &Pin, 1); //查找Filter空闲的pinif (SUCCEEDED(hr)){ppinOut = Pin;}hr = GetUnconnectedPin(m_pVideoRender, PINDIR_INPUT, &Pin, 1);if (SUCCEEDED(hr)){ppinIn = Pin;}if (FAILED(mGraph->Connect(ppinOut, ppinIn))){return false;cout<<"link decoder and video renderer failed!"<<endl;}}return true;
}

播放HEVC视频

bool RenderFile(const *char MediaUri)
{if (mGraph){HRESULT hr;WCHAR    szFilePath[MAX_PATH];//该函数映射一个字符串到一个宽字符的字符串MultiByteToWideChar(CP_ACP, 0, MediaUri, -1, szFilePath, MAX_PATH);//source filter与视频文件关联IFileSourceFilter* pFSFilter = NULL; //pFSFilter仅需要局部变量,用完释放m_pSourceFilter->QueryInterface(&pFSFilter);pFSFilter->Load(szFilePath, NULL);   //FilePath;pFSFilter->Release();pFSFilter = NULL;if(!LinkFilter()){cout << "link filter failed" << endl;}}else{cout << "render file failded" << endl;return false;}
}

DirectShow播放视频流程相关推荐

  1. Gstreamer之gst_element_set_state (pipeline, GST_STATE_PLAYING)播放视频流程(二十一)

    1.GStreamer是什么? 众所周知,Microsoft's Windows和Apple's MacOS对多媒体设备.多媒体创作.播放和实时处理等方面都有很好的支持,而Linux对多媒体应用一直略 ...

  2. c++ DirectShow播放任意格式的视频

    利用opencv只能处理.avi的视频,opencv之前的版本之前试过好像是只能处理.avi未压缩版本的视频,未压缩过的视频相当大,一个文件大概是几十个G.(这个不确定,因为之前用的压缩过的.avi的 ...

  3. android 行车记录仪分析,基于Android架构行车记录仪的异常掉电可播放视频方法与流程...

    本发明涉及摄录像视频技术领域,特别涉及一种基于Android架构行车记录仪的异常掉电可播放视频方法. 背景技术: 随着车联网概念的兴起和技术的积累,越来越多的智能设备被接入到车辆上.行车记录仪作为非常 ...

  4. 音视频流程 - 语音/短视频 :录制(编码)和播放(解码)

    -- 录制小视频: Camera -> YUV帧序列 -> YUV帧处理(镜像,缩放,旋转) -> 编码器  -> H264数据 大体上就是从摄像头输出的YUV帧经过预处理之后 ...

  5. DirectShow 播放mp4编码 视频

    转载 自 : http://blog.csdn.net/wuanshi5/article/details/50392111 最主要的开发资料还是在 : http://blog.csdn.net/sha ...

  6. FFmpeg播放视频文件流程

    一.FFmpeg解码播放主要流程,如图1-1所示: 使用播放一个媒体文件时,通常需要经过以下几个步骤: 图1-1    FFmpeg 播放视频文件流程 1.解封装(Demuxing):就是将输入的封装 ...

  7. 理解了DirectShow播放原理

    http://blog.sina.com.cn/s/blog_4137eb6101009oha.html 在这个"BT"的时代我们为什么"BT",相信绝大多数人 ...

  8. DirectShow播放器(LAVFilter + EVR)开发例子

    LAVFilter是一套著名的DirectShow插件,包括Demux,Video Decoder,AudioDecoder,播放文件所需要的几个重要插件都包含进去了,并且支持播放的视音频格式非常广泛 ...

  9. 【FFmpeg】ffplay 播放视频命令 ( 播放 | 暂停 | 停止 | 音量控制 | 进度控制 | 音频流 / 视频流 / 字幕流 / 节目切换 )

    FFmpeg 系列文章目录 [FFmpeg]Windows 搭建 FFmpeg 命令行运行环境 [FFmpeg]FFmpeg 相关术语简介 ( 容器 | 媒体流 | 数据帧 | 数据包 | 编解码器 ...

  10. 如何通过 Siri 播放视频?且看优酷技术接入实践

    作者 | 阿里文娱高级无线开发工程师 子荀 责编 | 屠敏 随着 iOS 13 的正式发布,SiriKit 开始支持音视频内容播放.例如,用户说"Hey Siri,使用优酷播放<乡村爱 ...

最新文章

  1. jdk的安装和环境的配置
  2. POST中文乱码解决方案
  3. C++ Opengl纹理混合源码
  4. Butterknife 的简单使用 和 配合 Butterknife的插件 Zelezny
  5. mysql 日期_Mysql数据库常见函数(下)——日期时间函数
  6. SpringAOP的Aspectj方式*
  7. 2018--20179215--《文献管理与信息分析》第三讲 英文数据库资源的发展趋势和利用...
  8. java编写一个程序_计算已知长和宽的长方形的周长,请教一下大佬们,我们java留了一个作业,编写程序,定义一个接口Comput,声明计算周长和面积的方法...
  9. access 报表中序号自动_Access中自动编号的字段ID如何让它重新从初始值1开始编号...
  10. 使用wordpress分页函数paginate_links
  11. 没有5年测开经验,还真说不清Python生成器、迭代器、装饰器
  12. 修改mysql_MySQL UPDATE:修改数据(更新数据)
  13. 16进制颜色转换为UIColor
  14. 线段树辅助——扫描线法计算矩形周长并(轮廓线)
  15. 后端如何接收对象类型的数据_javascript基本数据类型赋值和对象引用的内存情况分析...
  16. c语言课程设计--打飞碟源代码,c语言课程设计_打飞碟提高篇.doc
  17. ①ESP8266-wifi模块使用方法
  18. php支付宝 缺少签名参数,【支付宝】退款接口 报 “缺少签名参数”
  19. 广义表的存储结构及其基本运算
  20. 3分钟搭建一个网站?腾讯云Serverless开发体验

热门文章

  1. 傅立叶变换、拉普拉斯变换、Z变换的联系
  2. 南邮转专业计算机科学与技术,南京邮电大学本科生转专业管理办法(2020年10月9日修订)...
  3. 人力资源管理系统HRMS 天下三分 煮酒论英雄
  4. Flink catalog简单使用
  5. 树莓派4B引脚定义及运行实例
  6. 载波与载波频率,中心频率的解释
  7. 计算机科学导论2000字报告,《计算机科学导论》(节选)翻译报告
  8. 远程数据传输使用的几个软件
  9. 进化算法之粒子群算法介绍附代码——PSO
  10. “华为电气—艾默生”系——成就A股最多上市公司的创业群体