如何为DirectShow编写源筛选器
本主题介绍如何为DirectShow编写自定义源筛选器。
注意
本主题仅介绍推送源;它不描述拉取源,例如异步读取器筛选器或连接到拉取源的拆分器筛选器。 有关推送和拉取源之间的区别,请参阅筛选器开发人员数据流。
DirectShow流式处理模型
编写源筛选器时,请务必了解推送源与实时源不同。 实时源从某些外部源(例如相机或网络流)获取数据。 通常,实时源无法控制传入的数据速率。 如果下游筛选器没有足够的速度使用数据,则源将需要删除样本。
但推送源不必是实时源。 例如,推送源可以从本地文件读取数据。 在这种情况下,下游呈现器筛选器根据引用时钟和样本时间戳来确定它们使用源中的数据的速度。 源筛选器会尽快提供样本,但实际数据流受呈现器的限制。 筛选开发人员的数据流介绍了用于筛选数据流的机制。
源筛选器上的每个输出引脚创建一个名为 流式处理线程的线程。 引脚在流式处理线程上提供示例。 通常,所有解码、处理和呈现都发生在此线程上,尽管一些下游筛选器可能会创建其他线程来对其输出样本进行排队。
流式处理线程使用以下结构运行循环:
until (stopped)
1. Get a media sample from the allocator.
2. Fill the sample with data.
3. Time stamp the sample.
4. Deliver the sample downstream.
如果没有可用的示例,则步骤 1 会阻止示例,直到示例可用。 步骤 4 还可以阻止;例如,在暂停图形时,它可以阻止它。
循环尽快运行,但受呈现器筛选器呈现每个样本的速度的限制。 假设筛选器图有参考时钟,则速率由样本上的表示时间确定。 如果没有引用时钟,呈现器会尽快使用样本。
使用 CSource 和 CSourceStream
DirectShow基类包括两个支持推送源的类:CSource 和 CSourceStream。
- CSource 是筛选器的基类,并实现 IBaseFilter 接口。
- CSourceStream 是输出引脚的基类,并实现 IPin 接口。
输出引脚
源筛选器可以有多个输出引脚。 在筛选器的构造函数方法中,创建一个或多个派生自 CSourceStream 的引脚, (每个输出流) 一个引脚。 无需存储指向引脚的指针;创建引脚时,它们会自动添加到筛选器。
输出格式
输出引脚使用以下 CSourceStream 方法处理格式协商:
方法 | 说明 |
---|---|
GetMediaType | 从输出引脚获取媒体类型。 引脚必须建议至少一种媒体类型,因为下游筛选器可能不建议任何类型。 在大多数情况下,下游筛选器将是解码器或呈现器,具体取决于源筛选器是否提供压缩或未压缩的数据。 呈现器筛选器通常需要完整的媒体类型,其中包含呈现流所需的所有格式信息。 对于解码器,媒体类型中所需的信息量在很大程度上取决于编码格式。 |
CheckMediaType | 检查输出引脚是否接受给定媒体类型。 重写此方法是可选的,具体取决于实现 GetMediaType 的方式。 |
GetMediaType 方法重载:
- GetMediaType (1) 采用单个参数(指向 CMediaType 对象的指针)。
- GetMediaType (2) 采用索引变量和指向 CMediaType 对象的指针。
如果源筛选器的输出引脚仅支持一种媒体格式,则应重写 (1) 以使用该格式初始化 CMediaType 对象。 保留 (2) 的默认实现,同时保留 CheckMediaType 的默认实现。
如果引脚支持多个格式,请替代 (2) 。 根据索引变量的值初始化 CMediaType 对象。 引脚应返回格式作为有序列表。 在这种情况下,还必须重写 CheckMediaType ,以便根据格式列表检查媒体类型。
对于未压缩的视频格式,请记住下游筛选器可以提出具有各种步幅值的格式。 筛选器应接受任何有效的步幅值。 有关详细信息,请参阅 BITMAPINFOHEADER。
还必须重写纯虚拟 CBaseOutputPin::D ecideBufferSize 方法。 使用此方法可设置示例缓冲区的大小。
流式处理
CSourceStream 类为引脚创建流式处理线程。 线程过程在 CSourceStream::D oBufferProcessingLoop 方法中实现。 此方法调用纯虚拟 CSourceStream::FillBuffer 方法,派生类必须重写该方法。 此方法是引脚用数据填充缓冲区的位置。 例如,如果筛选器提供未压缩的视频,则可以在此处绘制视频帧。
当筛选器暂停或停止时,基类会在正确的时间自动启动和停止线程循环。 发生这种情况时, CSourceStream 类会调用一些方法来通知派生类:
如果需要添加任何特殊处理,可以重写这些方法。 否则,默认实现只需返回 S_OK。
寻求
如果源筛选器具有一个输出引脚,则可以使用 CSourceSeeking 类作为实现查找的起点。 从 CSourceStream 和 CSourceSeeking 继承 pin 类。
注意
对于具有多个输出引脚的筛选器,不建议使用 CSourceSeeking。 主要问题是只有一个引脚应响应请求。 通常,这需要在引脚和筛选器之间进行通信。
CSourceSeeking 类管理播放速率、开始时间、停止时间和持续时间。 派生类应设置初始停止时间和持续时间。 每当其中一个值发生更改时,将根据需要调用 CSourceSeeking::ChangeRate、CSourceSeeking::ChangeStart 或 CSourceSeeking::ChangeStop 方法。 方法都是纯虚拟方法。 派生的 pin 类将重写这些方法以执行以下操作:
- 在下游引脚上调用 IPin::BeginFlush 。 这会导致下游筛选器释放它们持有的样本,并拒绝新样本。
- 调用 CSourceStream::Stop 以停止流式处理线程。 源筛选器暂停生成新数据。
- 在下游引脚上调用 IPin::EndFlush 。 这表示下游筛选器接受新数据。
- 使用新的开始和停止时间和速率调用 IPin::NewSegment 。
- 在下一个示例中设置不连续属性。
有关详细信息,请参阅 支持在源筛选器中查找。
如果筛选器支持查找,则流位置现在独立于演示时间。 查找后,时间戳将重置为零。 时间戳的一般公式为:
- 示例开始时间 = 已用时间/播放速率
- 示例结束时间 = 示例开始时间 + 每个帧/播放速率 (时间)
其中 ,经过时间 是自筛选器开始运行或自上次查找命令以来已用完的时间。
查找的时间格式
默认情况下,seek 命令以 100 纳秒为单位。 源筛选器可以支持其他时间格式,例如按帧编号查找。 每次由 GUID 标识格式时;请参阅 时间格式 GUID。
若要支持其他时间格式,必须在输出引脚上实现以下方法:
- IMediaSeeking::ConvertTimeFormat
- IMediaSeeking::GetTimeFormat
- IMediaSeeking::IsFormatSupported
- IMediaSeeking::IsUsingTimeFormat
- IMediaSeeking::QueryPreferredFormat
- IMediaSeeking::SetTimeFormat
如果应用程序设置新的时间格式, 则 IMediaSeeking 方法中的所有位置参数都以新的时间格式进行解释。 例如,如果时间格式为帧, 则 IMediaSeeking::GetDuration 方法必须返回帧中的持续时间。
实际上,很少有DirectShow筛选器支持额外的时间格式,因此,很少有DirectShow应用程序利用此功能。