DirectShow 是一个 Windows 平台上的流媒体框架,是 DirectX 的组件之一,提供了高质量的多媒体流采集和回放功能。运用DirectShow,我们可以很方便地从支持WDM驱动模型的采集卡上捕获数据,并且进行相应的后期处理乃至存储到文件中。它广泛地支持各种媒体格式,包括Asf、Mpeg、Avi、Dv、Mp3、Wave等等,使得多媒体数据的回放变得轻而易举。DirectShow 是基于COM的,为了编写DirectShow应用程序,需要了解COM客户程序编写的基础知识。
1. 开发环境配置VS2022
2. DirectShow简单介绍
3. DirectShow开发流程
4. 简单播放一个视频例子
电脑系统Win10,采用VS2022对Directshow的开发环境进行配置。
1. 首先,下载 Directshow SDK,找不到的可以在此处下载一个

路径DirectShow\Samples\C++\DirectShow\BaseClasses中是directshow的基本类库,深入研究directshow可以好好看看。
路径DirectShow\Samples\C++\DirectShow\Filters中是自定义Filter的例子
路径DirectShow\Samples\C++\DirectShow\Capture中包含官方自带的例程,如下图所示

简单说明:
- AmCap:
录象程序,非常有价值(大部分功能实现):
(1)音视频采集设备filter的枚举选择.
(2)filter graph的创建和使用
(3)AVI mux filter的使用(包括加上encode filter)
(4)file write filter的使用.
(5)video source filter预览的使用.
- AudioCap:
录音程序,跟AmCap很类似,但是功能要弱一点(无视频).
- PlayCap:
简单的视频采集的预览.(注意RenderStream的使用)
- DVapp:
经典的例子,功能很多,视频采集、预览,文件保存等
- PlayCapMoniker:
选择系统中注册的第一个视频设备,把采集的视频在一个小窗口播放.(核心在于device moniker使用)
(1)、首先,解压DirectShow SDK到任意目录,特别的是,**需要我们要编译BaseClasses工程,才能生成我们需要的基本库baseclasses**。在VS2022中导入并编译 DirectShow SDK 安装路径下的\Samples\C++\DirectShow \BaseClasses 工程,可以编译为debug版本,也可以编译release 版本。此处选择debug_unicode版本,若编译成功,会在\Samples\C++\DirectShow\BaceClasses\Debug_Unicode\目录下将产生相应的库。


打开例程Amcap:

在属性管理器创建一个Drectshowdebug32属性表,以后直接添加进来即可,不用每次配置(记得保存的路径)

此处我保存在SDK相同目录下:

然后双击属性表进行配置:
VC++目录配置:包含目录编辑,添加对应SDK对应文件(我的SDK解压在D盘):
在包含目录中添加
DirectShow\Include;
DirectShow\Samples\C++\DirectShow\BaseClasses;
在库目录中添加
DirectShow\Lib\X86;
DirectShow\Samples\C++\DirectShow\BaseClasses\Debug_Unicode;


在库目录进行对应配置:

然后点击“配置属性-->链接器目录”中进行操作:
常规配置:
在附加库目录中添加
DirectShow\Samples\C++\DirectShow\BaseClasses\Debug_Unicode;
DirectShow\Lib\X86;

输入中的附加依赖项配置:
legacy_stdio_definitions.lib
winmm.lib
quartz.lib
odbc32.lib
odbccp32.lib
strmbasd.lib

点击工程“右键-->清理-->生成”,将生成exe可执行文件,即可调试。至此已成功搭建了DirectShow的开发环境。

编译的时候选择之前配置的环境。
Directshow系统位于应用层中,通过使用一种叫 Filter Graph 的模型来管理整个数据流的处理过程,参与数据处理的各个功能模块叫做 Filter;各个Filter 在 Filter Graph 中按一定的顺序连接成一条“流水线”协同工作。可以看到,Filter 一般分为三类:
Source Filters 负责获取数据(摄像头、网络、文件等),
Transform Filter负责数据转换,解码等,
Rendering Filters 主要负责数据的最终去向(显卡、声卡、或文件存储等)。

所以,从上述可以知道,Filter是Directshow中的最基本概念,由Filter Graph Manager进行管理。Directshow 提供了大量的Filter 用以支持最基本的应用,并且用户可以根据自己的需要,开发自己的 Filter。Filter 一般由一个或多个Pin组成,Filter 之间通过 Pin 相互连接。如下图所示。

Filter 实质是一个 COM 组件,所以学习开发 Filter 前应该对 COM 相关知识有点了解。COM 组件的实质是实现了纯虚指针接口的 C++ 对象。 应用程序开发者只需要基本的 COM 组件知识:实例化COM组件、调用接口、管理接口的引用计数。Filter 开发者则需要更多。 开发者应该清楚这所使用的Filter 在整个 Filter Graph 的位置,这个 Filter 的输入是什么数据,输出是什么数据,有几个输入 Pin、几个输出 Pin 等。 在 DirectShow 中,应用程序要实现功能就必须将这些 Filter 链接在一起,因而一个 Filter 的输出就变成了另一个 Filter 的输入。这一系列串在一起的 Filter 称为 Filter Graph。Filter具有三个状态,运行,停止,暂停。当一个Filter运行时,它就处理媒体数据流,当停止时,Filter就不再处理数据,暂停状态常用来给运行状态之前提示数据。 Filter作为一种COM组件,为了实现在Filter Graph 中的统一操作,每一个Filter 都至少实现了IBaseFilter 接口。实现Filter 的文件一般是一个DLL文件(.dll),但也有的时候是(.ax),和普通的COM组件一样,Filter 的创建是通过 API 函数 CoCreateInstance 来完成的。
Filter Graph Manager: Filter Graph Manager是一个 COM 对象,用于控制 filter graph 中的 filter。它执行许多功能,包括:
协调Filter之间的状态变化
建立参考时钟
将 filter 事件消息传递回应用程序
为应用程序提供构建Filter graph的方法
如何构建 Filter graph: Filter graph 的图形构建组件(Graph-Building Components ),包括:
Filter Graph Manager
Capture Graph Builder
Filter Mapper
System Device Enumerator
DVD Graph Builder 要创建 Filter gragh,就要创建 Filter Graph Manager的实例:
IGraphBuilder* pIGB;
HRESULT hr = CoCreateInstance(CLSID_FilterGraph,
NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder,
(void **)&pIGB); Filter Graph Manager 支持以下方法:
IFilterGraph::ConnectDirect尝试在两个引脚之间建立直接连接。如果引脚无法连接,则该方法失败。
IGraphBuilder::Connect连接两个引脚。如果可能,它会直接连接。否则,它使用中间过滤器来完成连接。
IGraphBuilder::Render从输出引脚开始并构建图形的其余部分。此方法根据需要添加过滤器,在下游工作,直到到达渲染器过滤器。
IGraphBuilder::RenderFile构建一个完整的文件播放图。
IFilterGraph::AddFilter向图中添加一个过滤器。它不连接过滤器。您必须在调用此方法之前创建过滤器,方法是调用CoCreateInstance或使用过滤器映射器或系统设备枚举器。
第一阶段:创建一个 Filter Graph Manager 组件(创建Filter 管理器),代码如下:

第二阶段:根据实际的应用,创建一条完整的Filter 链路。比如播放一个本地文件,最简单快速的代码如下:

第三阶段:调用Filter Graph Manager 上(或者直接在某个Filter上)的各个接口方法进行控制,并且完成 Filter Graph Manager 与应用程序的事件交互。比如调用 IMediaControl 接口方法控制Filter Graph 的状态转换。

最后等待数据处理完成后,要释放过滤器图管理器和所有的Filter,也就是所有的COM对象。
参考:https://docs.microsoft.com/en-us/windows/win32/directshow/how-to-play-a-file 以播放 AVI 视频文件的例子来说明每个步骤的 Filters : 1、从文件中读取原始数据作为字节流(文件源过滤器 File Source filter) 2、检查 AVI 标头,并将字节流解析为单独的视频帧和音频样本(AVI 分离器过滤器 AVI Splitter filter) 3、解码视频帧(各种解码器过滤器,取决于压缩格式 various decoder filters, depending on the compression format) 4、绘制视频帧(视频渲染器过滤器 Video Renderer filter) 5、将音频样本发送到声卡(默认 DirectSound 设备过滤器 Default DirectSound Device filter)

1、 首先,初始化COM口,调用CoInitialize或CoInitializeEx
HRESULT hr = CoInitialize(NULL);
if (FAILED(hr))
{
// Add error-handling code here. (Omitted for clarity.)
} 2、调用CoCreateInstance来创建 Filter Graph Manager:
IGraphBuilder *pGraph;
HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL,
CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&pGraph); 类标识符 (CLSID) 是 CLSID_FilterGraph,代码内部直接提供。对CoCreateInstance的调用返回IGraphBuilder接口,该接口主要包含用于构建过滤器图的方法。此示例需要另外两个接口: IMediaControl控制流式传输。它包含停止和启动图形的方法。 IMediaEvent具有从 Filter Graph Manager 获取事件的方法。在本例中,该接口用于等待播放完成。
IMediaControl *pControl;
IMediaEvent *pEvent;
hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent); 3、构建 filter graph,对于文件播放,这是通过一个方法调用完成的:
hr = pGraph->RenderFile(L"C:\\Example.avi", NULL); IGraphBuilder::RenderFile方法构建一个可以播放指定文件的过滤器图。第一个参数是文件名,表示为一个宽字符(2 字节)字符串。第二个参数是保留的,必须等于NULL。如果指定的文件不存在,或者文件格式无法识别,此方法可能会失败。但是,假设该方法成功,过滤器图现在可以播放了。要运行图表,请调用IMediaControl::Run方法:
hr = pControl->Run(); 当 Filter Graph 运行时,数据会通过 Filter并呈现视频和音频,播放会发生在单独的线程上。您可以通过调用IMediaEvent::WaitForCompletion方法等待播放完成:
long evCode = 0;
pEvent->WaitForCompletion(INFINITE, &evCode); 此方法会一直阻塞,直到文件播放完毕或指定的超时间隔过去。值 INFINITE 表示应用程序无限期地阻塞,直到文件播放完毕。 4、应用程序完成后,释放接口指针并关闭COM库:
pControl->Release();
pEvent->Release();
pGraph->Release();
CoUninitialize(); 完整实例代码:
#include <dshow.h>
void main(void)
{
IGraphBuilder *pGraph = NULL;
IMediaControl *pControl = NULL;
IMediaEvent *pEvent = NULL;
// Initialize the COM library.
HRESULT hr = CoInitialize(NULL);
if (FAILED(hr))
{
printf("ERROR - Could not initialize COM library");
return;
}
// Create the filter graph manager and query for interfaces.
hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void **)&pGraph);
if (FAILED(hr))
{
printf("ERROR - Could not create the Filter Graph Manager.");
return;
}
hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);
// Build the graph. IMPORTANT: Change this string to a file on your system.
hr = pGraph->RenderFile(L"C:\\Example.avi", NULL);//视频用avi的测试
if (SUCCEEDED(hr))
{
// Run the graph.
hr = pControl->Run();
if (SUCCEEDED(hr))
{
// Wait for completion.
long evCode;
pEvent->WaitForCompletion(INFINITE, &evCode);
// Note: Do not use INFINITE in a real application, because it
// can block indefinitely.
}
}
pControl->Release();
pEvent->Release();
pGraph->Release();
CoUninitialize();
}