最简单的基于DirectShow的示例:视频播放器自定义版

news/2025/1/11 5:20:55/

=====================================================

最简单的基于DirectShow的示例文章列表:

最简单的基于DirectShow的示例:视频播放器

最简单的基于DirectShow的示例:视频播放器图形界面版

最简单的基于DirectShow的示例:视频播放器自定义版

最简单的基于DirectShow的示例:获取Filter信息

=====================================================


本文记录一个简单的基于DirectShow的自定义的视频播放器。这里所说的“自定义播放器”,实际上指的是自己在Filter Graph中手动逐个添加Filter,并且连接这些Filter的后运行的播放器。这么做相对于使用RenderFile()这种“智能”创建Filter Graph的方法来说要复杂不少,但是可以让我们更加了解DirectShow的体系。


流程图

最简单的基于DirectShow的自定义的视频播放器的流程如下图所示。


该流程图中包含如下变量:
IGraphBuilder *pGraph:继承自IFilterGraph,用于构建Filter Graph。
IMediaControl *pControl:提供和播放控制有关的一些接口。
IMediaEvent   *pEvent:用来处理Filter Graph发出的事件。
IBaseFilter *pF_source:源Filter。
IFileSourceFilter* pFileSource:源Filter的暴露的接口,用于设置输入文件的路径。
IBaseFilter *pF_demuxer:解复用Filter。
IBaseFilter *pF_decoder:解码Filter。
IBaseFilter *pF_render:渲染Filter。
IPin *pOut:输出Pin。
IPin *pIn:输入Pin。
IPin **pPin:内部变量Pin。
该流程图大体上可以分成以下步骤:
(1)       初始化DirectShow
包括以下几个步骤:
a)       CoInitialize():初始化COM运行环境。
b)       CoCreateInstance(…,pGraph):用指定的类标识符创建一个Com对象。在这里创建IGraphBuilder。
c)       pGraph->QueryInterface(…,pControl):通过QueryInterface()查询某个组件是否支持某个特定的接口。在这里查询IMediaControl接口。
d)       pGraph->QueryInterface(…,pEvent):同上。在这里查询IMediaEvent接口。
(2)       添加Source Filter
包括以下几个步骤:
a)       CoCreateInstance(…,pF_source):创建Source Filter。
b)       pGraph->AddFilter(pF_source,…):将Source Filter加入Filter Graph。
c)       pF_source->QueryInterface(…,pFileSource):查找Source Filter的IFileSourceFilter接口。
d)       pFileSource->Load(L"xxx.mpg",pF_source):调用IFileSourceFilter的Load()方法加载视频文件。
(3)       添加Demuxer Filter
包括以下几个步骤:
a)       CoCreateInstance(…,pF_demuxer):创建Demuxer Filter。
b)       pGraph->AddFilter(pF_demuxer,…):将Demuxer Filter加入Filter Graph。
(4)       添加Decoder Filter
包括以下几个步骤:
a)       CoCreateInstance(…,pF_decoder):创建Decoder Filter。
b)       pGraph->AddFilter(pF_decoder,…):将Decoder Filter加入Filter Graph。
(5)       添加Render Filter
包括以下几个步骤:
a)       CoCreateInstance(…,pF_render):创建Render Filter。
b)       pGraph->AddFilter(pF_render,…):将Render Filter加入Filter Graph。
(6)       连接Source Filter和Demuxer Filter
调用了一个函数connect_filters()用于连接2个Filter。
connect_filters()的执行步骤如下:
a)       调用get_unconnected_pin()从源Filter中选择一个没有链接的输出Pin。
b)       调用get_unconnected_pin()从目的Filter中选择一个没有链接的输入Pin。
c)       连接这两个Pin
get_unconnected_pin()的执行步骤如下:
a)       枚举Filter上的Pin。
b)       遍历这些Pin,查找符合输出方向(通过IPin的QueryDirection()方法),而且没有在使用的Pin(通过IPin的ConnectedTo()方法)。
(7)       连接Demuxer Filter和Decoder Filter
过程同上。
(8)       连接Decoder Filter和Render Filter
过程同上。
(9)       开始播放
包括以下步骤:
pControl->Run():开始运行Filter Graph中的所有Filter。
pEvent->WaitForCompletion():等待Filter Graph处理完所有数据。
 

上述步骤可以理解为在GraphEdit软件中分别按照步骤添加以下控件。其中(1)、(2)、(3)、(4)为先添加的4个Filter,(5)、(6)、(7)为Filter之间的连接线。



源代码

/*** 最简单的基于DirectShow的视频播放器(Custom)* Simplest DirectShow Player (Custom)** 雷霄骅 Lei Xiaohua* leixiaohua1020@126.com* 中国传媒大学/数字电视技术* Communication University of China / Digital TV Technology* http://blog.csdn.net/leixiaohua1020** 本程序是一个简单的基于DirectShow的视频播放器。该播放器通过逐个添加* 滤镜并连接这些滤镜实现了视频的播放。适合初学者学习DirectShow。* * This software is a simple video player based on DirectShow.* It Add DirectShow Filter Manually and Link the Pins of these filters* to play videos.Suitable for the beginner of DirectShow.*/#include "stdafx.h"
#include <dshow.h>
//'1':Add filters manually
//'0':Add filters automatically
#define ADD_MANUAL 1//Find unconnect pins
HRESULT get_unconnected_pin(IBaseFilter *pFilter, // Pointer to the filter.PIN_DIRECTION PinDir, // Direction of the pin to find.IPin **ppPin) // Receives a pointer to the pin.
{*ppPin = 0;IEnumPins *pEnum = 0;IPin *pPin = 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){IPin *pTmp = 0;hr = pPin->ConnectedTo(&pTmp);if (SUCCEEDED(hr)) // Already connected, not the pin we want.{pTmp->Release();}else // Unconnected, the pin we want.{pEnum->Release();*ppPin = pPin;return S_OK;}}pPin->Release();}pEnum->Release();// Did not find a matching pin.return E_FAIL;
}//Connect 2 filters
HRESULT connect_filters(IGraphBuilder *pGraph, IBaseFilter *pSrc, IBaseFilter *pDest)
{if ((pGraph == NULL) || (pSrc == NULL) || (pDest == NULL)){return E_POINTER;}//Find Output pin in source filterIPin *pOut = 0;HRESULT hr = NULL;hr=get_unconnected_pin(pSrc, PINDIR_OUTPUT, &pOut);if (FAILED(hr)){return hr;}//Find Input pin in destination filterIPin *pIn = 0;hr = get_unconnected_pin(pDest, PINDIR_INPUT, &pIn);if (FAILED(hr)){return hr;}//Connnect themhr = pGraph->Connect(pOut, pIn);pIn->Release();pOut->Release();return hr;
}int _tmain(int argc, _TCHAR* argv[])
{IGraphBuilder *pGraph = NULL;IMediaControl *pControl = NULL;IMediaEvent   *pEvent = NULL; // Init COMHRESULT hr = CoInitialize(NULL);if (FAILED(hr)){printf("Error - Can't init COM.");return -1;}// Create FilterGraphhr=CoCreateInstance(CLSID_FilterGraph, NULL,CLSCTX_INPROC_SERVER,IID_IGraphBuilder, (void **)&pGraph);if (FAILED(hr)){printf("Error - Can't create Filter Graph.");return -1;}// Query Interfacehr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);//1. Add Filters=======================//SourceIBaseFilter *pF_source = 0;hr = CoCreateInstance(CLSID_AsyncReader, 0, CLSCTX_INPROC_SERVER,IID_IBaseFilter, (void**)(&pF_source));if (FAILED(hr)){printf("Failed to create File Source.\n");return -1;}hr = pGraph->AddFilter(pF_source, L"Lei's Source");if (FAILED(hr)){printf("Failed to add File Source to Filter Graph.\n");return -1;}IFileSourceFilter* pFileSource;pF_source->QueryInterface(IID_IFileSourceFilter, (void**)&pFileSource);pFileSource->Load(L"cuc_ieschool.mpg", NULL);pFileSource->Release();#if ADD_MANUAL//DemuxerIBaseFilter *pF_demuxer = 0;hr = CoCreateInstance(CLSID_MPEG1Splitter, 0, CLSCTX_INPROC_SERVER,IID_IBaseFilter, (void**)(&pF_demuxer));if (FAILED(hr)){printf("Failed to create Demuxer.\n");return -1;}hr = pGraph->AddFilter(pF_demuxer, L"Lei's Demuxer");if (FAILED(hr)){printf("Failed to add Demuxer to Filter Graph.\n");return -1;}//DecoderIBaseFilter *pF_decoder = 0;hr = CoCreateInstance(CLSID_CMpegVideoCodec, 0, CLSCTX_INPROC_SERVER,IID_IBaseFilter, (void**)(&pF_decoder));if (FAILED(hr)){printf("Failed to create Decoder.\n");return -1;}hr = pGraph->AddFilter(pF_decoder, L"Lei's Decoder");if (FAILED(hr)){printf("Failed to add Decoder to Filter Graph.\n");return -1;}//RenderIBaseFilter *pF_render = 0;hr = CoCreateInstance(CLSID_VideoRenderer, 0, CLSCTX_INPROC_SERVER,IID_IBaseFilter, (void**)(&pF_render));if (FAILED(hr)){printf("Failed to create Video Render.\n");return -1;}hr = pGraph->AddFilter(pF_render, L"Lei's Render");if (FAILED(hr)){printf("Failed to add Video Render to Filter Graph.\n");return -1;}//2. Connect Filters=======================hr = connect_filters(pGraph, pF_source, pF_demuxer);if (FAILED(hr)){printf("Failed to link Source and Demuxer.\n");return -1;}hr = connect_filters(pGraph, pF_demuxer, pF_decoder);if (FAILED(hr)){printf("Failed to link Demuxer and Decoder.\n");return -1;}hr = connect_filters(pGraph, pF_decoder, pF_render);if (FAILED(hr)){printf("Failed to link Decoder and Render.\n");return -1;}pF_source->Release();pF_demuxer->Release();pF_decoder->Release();pF_render->Release();
#elseIPin*	 Pin;ULONG	 fetched;//	get output pinIEnumPins* pEnumPins;hr = pF_source->EnumPins(&pEnumPins);hr = pEnumPins->Reset();hr = pEnumPins->Next(1, &Pin, &fetched);pEnumPins->Release();//	render pin, graph builder automatically complete rest workshr = pGraph->Render(Pin);
#endifif (SUCCEEDED(hr)){// Runhr = pControl->Run();if (SUCCEEDED(hr)){long evCode=0;pEvent->WaitForCompletion(INFINITE, &evCode);}}//ReleasepControl->Release();pEvent->Release();pGraph->Release();CoUninitialize();return 0;
}

运行结果

程序的运行结果如下图所示。运行后会播放“cuc_ieschool.mpg”文件。需要注意的是,本程序并没有加入音频解码和播放的Filter,所以播放视频的时候是没有声音的。


除了手动一个一个添加Filter之外,也可以在获得“源”Filter的Pin之后,直接调用IFilterGraph的Render()方法“智能”自动构建Filter Graph。注意Render()方法和RenderFile()方法是不一样的。RenderFile()是指定一个文件路径后,自动构建整个Filter Graph,相对来说更加简单些;而Render()方法则是首先要创建一个Source Filter之后,才可以自动构建整个Filter Graph。
可以通过修改源文件首部的宏定义ADD_MANUAL来设定是否手动添加Filter,如下所示。
//'1':Add filters manually
//'0':Add filters automatically
#define ADD_MANUAL 1
 

下载


Simplest DirectShow Example
 

项目主页

SourceForge:https://sourceforge.net/projects/simplestdirectshowexample/

Github:https://github.com/leixiaohua1020/simplest_directshow_example

开源中国:http://git.oschina.net/leixiaohua1020/simplest_directshow_example


CDSN下载地址: http://download.csdn.net/detail/leixiaohua1020/8348163
 
本程序包含了DirectShow开发的示例程序。适合DirectShow初学者进行学习。
它包含了以下几个子程序:
simplest_directshow_player: 最简单的基于DirectShow的视频播放器。
simplest_directshow_player_custom: 最简单的基于DirectShow的视频播放器(Custom)。
playerGUI: 最简单的基于DirectShow的播放器-图形界面版。
simplest_directshow_info: 最简单的Directshow信息显示例子。
simplest_directshow_filter: 目前还未完成。
 

http://www.ppmy.cn/news/498264.html

相关文章

<C++> C++11 新的类功能

C11 新的类功能 1.默认成员函数 原来C类中&#xff0c;有6个默认成员函数&#xff1a; 构造函数析构函数拷贝构造函数拷贝赋值重载取地址重载const取地址重载 最后重要的是前4个&#xff0c;后两个用处不大。默认成员函数就是我们不写编译器会生成一个默认的。 C11 新增了两…

springcloud的组件及其作用以及面试相关问题

Spring Cloud 是一个用于构建分布式系统和微服务架构的开发工具包&#xff0c;它基于Spring框架&#xff0c;提供了一系列组件和模块来简化分布式系统的开发和管理。以下是一些常见的Spring Cloud组件及其作用的介绍&#xff1a; Eureka&#xff08;服务注册与发现&#xff09…

值得收藏的 10个 Android 手机恢复丢失文件的工具榜单

尽管我们尽可能避免这种情况&#xff0c;但有时我们还是会不小心删除 Android 设备上的重要文件。无论是照片、视频、文档还是任何其他形式的数据&#xff0c;数据丢失都会带来巨大的痛苦。不幸的是&#xff0c;Android 设备没有内置恢复工具。但是&#xff0c;有一些第三方恢复…

Java乐观锁实现文章点击量、收藏计数、点赞计数

Java乐观锁实现文章点击量、收藏计数、点赞计数 &#x1f4d4; 千寻简笔记介绍 千寻简笔记已开源&#xff0c;Gitee与GitHub搜索chihiro-notes&#xff0c;包含笔记源文件.md&#xff0c;以及PDF版本方便阅读&#xff0c;且是用了精美主题&#xff0c;阅读体验更佳&#xff0…

Mendix 10 树形组件分析及应用

一、前言 产品研发目标是服务于业务&#xff0c;解决具体业务问题&#xff0c;带来业务价值。 因此&#xff0c;任何产品功能的推出&#xff0c;都应该秉承“从实践中来&#xff0c;到实践中去”的原则&#xff0c;在实战中发现问题&#xff0c;通过新功能设计和功能改进解决…

kettle开发-Day38-超好用自定义数据处理组件

目录 前言&#xff1a; 一、半斤八两&#xff0c;都不太行 1、表输入&#xff0c;速度快&#xff0c;但不稳妥 2、稳的一批&#xff0c;但是慢的像蜗牛 二、各诉衷肠&#xff0c;合作共赢 1、表输入&#xff0c;高效数据插入 2、插入更新&#xff0c;一个都不能少 三、表输…

D. Survey in Class(贪心 + 分类讨论)

Problem - D - Codeforces Zinaida Viktorovna 的历史课上有 n 名学生。今天的作业包括 m 个主题&#xff0c;但是学生们准备时间很短&#xff0c;所以第 i 个学生只学习了从 li 到 ri &#xff08;包括&#xff09;的主题。 在课开始时&#xff0c;每个学生都将手举在 0 处。…

计算机主机连接电视机,如何将电脑连接到电视机上_台式电脑怎么连接电视机屏幕-win7之家...

现在的电视机都是液晶显示屏&#xff0c;会配有无线、蓝牙功能等等&#xff0c;有时我们需要把电脑内容投射在电视剧上&#xff0c;这就先要连接电视机在进行连接&#xff0c;那么如何将电脑连接到电视机上呢&#xff0c;下面小编给大家分享台式电脑连接电视机屏幕的方法步骤。…