videocapture_directshow是什么

videocapture_directshow是什么DirectShow框架简介DirectShow框架是多媒体播放框架上一个非常经典的框架,现在已经十多年了,在Windows平台上依然无法替代,非常值得去学习研究。个人觉得从设计模式的角度上看,directshow框架的灵活性、复用性、可维护性、可拓展性这些方面做得非常不错,也是它经久不衰历久弥新的一个原因,现在的很多第三方的decoder和filter都基于directshow框架开发,可以很灵活

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

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 manager
        HRESULT 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 Filter
            hr = 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;
            
            //HEVCDecoder
            HEVCDecoderDll = ".\\Codec\\hevcdecfltr.dll";
            HEVCDecoderName = L"Lentoid HEVC Decoder";
            HEVCDecoderGuid = CLSID_HEVCDecoder;

             //Splitter
            if (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 Decoder
            if (loadFilter(HEVCDecoderDll))
            {
            	if (createFilter(HEVCDecoderGuid, &m_pVideoHEVCDecoder))
            	{
            		mGraph->AddFilter(m_pVideoHEVCDecoder, HEVCDecoderName);
            	}
            	else
            	{
            	    cout << "add decoder failed" << endl
            		return false;
            	}
            }
            else
            {
                cout << "load decoder failed!" << endl;
            	return false;
            }
            
    		//Video Renderer
			hr = 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 -> Splitter
	if(m_pSourceFilter != NULL && m_pSplitter != NULL)
	{
		hr = GetUnconnectedPin(m_pSourceFilter, PINDIR_OUTPUT, &Pin, 1); //查找Filter空闲的pin
		if (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//解码HEVC
	if (m_pSplitter != NULL && m_pVideoHEVCDecoder != NULL)
	{
		hr = GetUnconnectedMajortypePin(m_pSplitter, PINDIR_OUTPUT, &Pin, MEDIATYPE_Video);//查找splitter空闲的video outpin
		if (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 -> videorender
	if (m_pVideoDecoder != NULL && m_pVideoRender != NULL)
	{
		hr = GetUnconnectedPin(m_pVideoHEVCDecoder, PINDIR_OUTPUT, &Pin, 1); //查找Filter空闲的pin
		if (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;
	}
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/183405.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • NR 5G 网络切片[通俗易懂]

    NR 5G 网络切片[通俗易懂]5G网络切片网络切片是在5G引入的新概念之一,关于网络切片首先从5G的前辈3G和4G说起,从3G时代开始,手机上网就靠数据业务流量,但网络资源有限,不可能保证所有业务都能全速进行,总得捡重要的首先保障。最简单的方式就是对业务进行分类,给予不同优先级的业务不同的资源,不同的服务质量,这就是QoS(QualityofService)的来源。3G网络,是无线互联网的开端,通过对所有用户的各种类型…

    2022年9月1日
    5
  • Cloudsim学习笔记——基本知识

    Cloudsim学习笔记——基本知识Cloudsim澳大利亚墨尔本学校的网格实验室和Gridbus项目推出,是在离散事件模拟包SimJava上开发的函数库,继承了GridSim的编程模型,特点:支持大型云计算的基础设施的建模和仿真; 一个自足的支持数据中心、服务代理人、调度和分配策略的平台独特功能:提供虚拟化引擎,旨在数据中心节点上帮助建立和管理多重的、独立的、协调的虚拟化服务; 在对虚拟化服务分配处理核心时能够在时…

    2022年10月13日
    3
  • java基本数据类型总结

    java基本数据类型总结详解一、八种基本数据类型常识1.1、基本常识表对于上图有以下几点需要注意:java八种基本数据类型分为四类八种,四类分别为整型、浮点型、布尔型、字符型;八种分别为byte、short、int、long、float、double、boolean、char;java八种基本数据类型的字节数:分别为1、2、4、8个字节;1字节(byte、boolean)、2字节(short、char)、4字节(int、float)、8字节(long、double);整数的默认类型为int,浮点数的默认类型为do

    2022年7月7日
    21
  • miRNA几大常用的数据库

    miRNA几大常用的数据库

    2022年2月24日
    48
  • 几款强大的网页生成工具[通俗易懂]

    几款强大的网页生成工具[通俗易懂]Carrd这款软件是免费的,可以帮助初学者制作一个简单、响应快捷的网页。链接:https://carrd.co/2.TemplateStash这款软件几乎集合了所有类型网页的模板,你可以通过关键词检索来找到你想要的主题风格。链接:http://www.templatestash.com/3.Bubble这款软件的优势在于使用者不用掌握任何编程技巧就可以制作网页,它独特的…

    2022年5月11日
    49
  • 因果图方法_因果图法符号

    因果图方法_因果图法符号因果图方法  一.方法简介  1.定义:是一种利用图解法分析输入的各种组合情况,从而设计测试用例的方法,它适合于检查程序输入条件的各种组合情况。  2.因果图法产生的背景:  等价类划分法和边界值分析方法都是着重考虑输入条件,但没有考虑输入条件的各种组合、输入条件之间的相互制约关系。这样虽然各种输入条件可能出错的情况已经测试到了,但多个输入条件组合起来可能出错的情况却被忽视了。

    2022年8月14日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号