修改现有filter
如果需要修改现有filter
,可以停止该图,进行更改,然后重新启动该图。 这通常是最佳方法。 但是,在某些情况下,你可能希望在图形仍在运行时对其进行更改。 例如:
- 应用程序在播放期间插入视频效果
filter
。 - 源
filter
将媒体类型切换为中流,可能需要新的解压缩filter
。 - 应用程序将新的视频流添加到图形。
这些都是 动态图形生成的示例,该术语涵盖在图形继续运行时对filter Graph
所做的任何类型的更改。 动态图形生成可以由应用程序或图形中的filter
启动。 可以采用三种不同的方案: - 动态格式更改:
filter
可以在中游更改格式,而无需删除或替换任何filter
。 - 动态重新连接:通过添加或删除
filter
来更改图形。 -
filter
链:添加、删除和控制filter
链。
动态重新连接
在大多数 DirectShow filter
中,当图形主动流式传输数据时,无法重新连接pin
。 应用程序必须在重新连接pin
之前停止图形。 但是,某些filter
在图形运行时支持pin
重新连接,此过程称为动态重新连接。 这可以通过应用程序或图形中的filter
来完成。
例如,请考虑下图中的图。
动态重新连接的一种方案可能是在图形正在运行时从图形中删除filter
2,并将其替换为另一个filter
。 若要使此方案正常工作,必须满足以下条件:
-
filter
3 (pin
D) 上的输入pin
必须支持 IPinConnection 接口。 此接口允许在不停止filter
的情况下重新连接pin
。 -
filter
1 上的输出pin
(pin
A) 必须能够在重新连接发生时阻止媒体数据流。 在重新连接期间,任何数据都不能在pin
A 和pin
D 之间传输。 通常,这意味着输出pin
必须支持 IPinFlowControl 接口。 但是,如果filter
1 是启动重新连接的filter
,则它可能有一些内部机制来阻止其自己的数据流。
动态重新连接将涉及以下步骤:
- 阻止来自
pin
A 的数据流。 - 重新连接
pin
A 到pin
D,可能通过新的中间filter
。 - 取消阻止固定 A,以便数据再次开始流动。
阻止数据流
若要阻止数据流,请在pin
A 上调用 IPinFlowControl::Block 。可以异步或同步调用此方法。 若要 异步调用 方法,请创建 Win32 事件对象并将事件句柄传递给 Block 方法。 方法将立即返回。 使用 WaitForSingleObject 等函数等待事件发出信号。 pin
在事件阻止数据流时发出信号。 例如:
// Create an event
HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (hEvent != NULL)
{
// Block the data flow.
hr = pFlowControl->Block(AM_PIN_FLOW_CONTROL_BLOCK, hEvent);
if (SUCCEEDED(hr))
{
// Wait for the pin to finish.
DWORD dwRes = WaitForSingleObject(hEvent, dwMilliseconds);
}
}
若要 同步调用 方法,只需传递值 NULL 而不是事件句柄。 现在, 方法将阻止,直到操作完成。 在pin
准备好提供新示例之前,可能不会发生这种情况。 如果filter
已暂停,则可能需要任意时间长度。 因此,不要从main应用程序线程进行同步调用。 使用工作线程,或者异步调用 方法。
重新连接pin
若要重新连接pin
,请查询 IGraphConfig 接口的 Filter Graph 管理器,并调用 IGraphConfig::Reconnect 或 IGraphConfig::Reconfigure。 重新连接方法更易于使用;它执行以下操作:
停止示例) 中 (filter
2 的中间filter
,并将其从图中删除。
根据需要添加新的中间filter
。
连接所有pin
。
暂停或运行任何新filter
,以匹配图形的状态。
重新连接方法有多个可选参数,可用于指定pin
连接的媒体类型和要使用的中间filter
。 例如:
pGraph->AddFilter(pNewFilter, L"New Filter for the Graph");
pConfig->Reconnect(
pPinA, // Reconnect this output pin...
pPinD, // ... to this input pin.
pMediaType, // Use this media type.
pNewFilter, // Connect them through this filter.
NULL,
0);
有关详细信息,请参阅参考页。 如果 重新连接 方法不够灵活,可以使用 Reconfigure 方法,该方法调用应用程序定义的回调方法来重新连接pin
。 若要使用此方法,请在应用程序中实现 IGraphConfigCallback 接口。
在调用 重新配置之前,请阻止来自输出pin
的数据流,如前所述。 然后推送正在重新连接的关系图部分仍处于挂起状态的任何数据,如下所示:
在示例) 中,在重新连接链中最远下游的输入pin
上调用 IPinConnection::NotifyEndOfStream (pin
D。 将句柄传递到 Win32 事件。
在从阻止数据流的输出pin
直接下游的输入pin
上调用 IPin::EndOfStream 。 (在此示例中,数据流在pin
A 处被阻止,因此可以在pin
B.)
等待事件发出信号。 输入pin
(pin
D) 接收流结束通知时向事件发出信号。 这表示没有数据在pin
之间传输,并且调用方可以安全地重新连接pin
。
请注意, IGraphConfig::重新连接 方法会自动处理前面的步骤。 仅当使用 Reconfigure 方法时,才需要执行这些步骤。
通过图形推送数据后,调用 重新配置 并将指针传递到 IGraphConfigCallback 回调接口。 Filter Graph 管理器将调用你提供的 IGraphConfigCallback::Reconfigure 方法。
取消阻止数据流
重新连接pin
后,请为第一个参数调用值为零的 IPinFlowControl::Block 来取消阻止数据流。
备注
如果动态重新连接由filter
执行,则必须注意一些线程问题。 如果 Filter Graph 管理器尝试停止filter
,它可能会死锁,因为图形等待filter
停止,同时filter
可能正在等待数据通过图形推送。 为了防止可能出现的死锁,本部分所述的某些方法会处理 Win32 事件。 如果 Filter Graph 管理器尝试停止filter
,filter
应发出事件信号。 有关详细信息,请参阅 IGraphConfig 和 IPinConnection。
filter
链
filter
链是满足以下条件的filter
序列:
- 链中的每个
filter
最多有一个连接的输入pin
和一个连接的输出pin
。 - 无需遍历链外部的
filter
即可遍历链中的每个filter
。
例如,在下图中,filter
A–B、C-D 和 F–G-H 是filter
链。 F-G-H (F-G 和 G-H) 中的每个子链也是一个filter
链。 filter
链可以包含单个filter
,因此filter
A、B、C、D、F、G 和 H 也是不同的filter
链。 filter
E 具有两个输入连接,因此包含filter
E 的任何filter
序列都不是filter
链。
filter
链 (示例 1)
IFilterChain 接口提供用于控制filter
链的以下方法:
Label 值
IFilterChain::StartChain 启动链。
IFilterChain::StopChain 停止链。
IFilterChain::P auseChain 暂停链。
IFilterChain::RemoveChain 从图中删除链。
没有用于添加链的特定方法。 若要添加链,请使用 IFilterGraph::AddFilter 方法插入新filter
。 然后,通过调用 IGraphBuilder::Connect、 IGraphBuilder::Render 或类似方法连接filter
。
当图形运行时,filter
链可以在运行和停止之间切换。 当图形暂停时,它可以在暂停和停止之间切换。 这些是filter
链唯一可能的状态转换。
filter
链准则
使用 IFilterChain 方法时,请务必确保图中的filter
可以支持filter
链接操作。 否则,可能会导致死锁或图形错误。 在链更改状态后,连接到链的filter
必须正常运行。
使用 IFilterChain 的最佳方式是使用一组专为链接设计的filter
。 使用以下准则来确保filter
对filter
链操作是安全的。 这些要点参考下图。
filter
链 (示例 2)
在filter
链的状态更改之前,必须完成filter
链边界上的所有数据处理调用。 此规则适用于 IMemInputPin::Receive、 IPin::NewSegment 和 IPin::EndOfStream 的方法。 链中的filter
必须从对链外filter
进行的这些方法的调用返回;和 链外部的filter
必须从链中的filter
进行的调用返回。
例如,在上图中,filter
B 必须完成来自filter
A 的任何数据处理调用,而filter
E 必须完成来自filter
D 的任何调用。如果pin
公开 IPinFlowControl 和 IPinConnection 接口,可以通过调用 IPinFlowControl::Block 和 IGraphConfig::P ushThroughData 方法通过图形推送数据,如 动态重新连接中所述。 filter
可能还支持用于推送数据的专用方法。
上游filter
必须预期链的状态会发生更改。 例如,在上图中,假设链已停止,但filter
A 调用 IMemInputPin::Receive。 调用失败,filter
A 的响应是停止流式处理。 当应用程序重启链时,它不起作用,因为filter
A 不再流式处理数据。
下游filter
还必须预期链的状态会发生更改。 否则,下游filter
可能会在等待从未到达的样本时阻止。 例如,多路复用器 (MUX) filter
通常需要来自其所有输入pin
的数据。 从一个输入pin
停止数据流可能会阻止其他流进行处理。 这可能会导致图形死锁。
从链外filter
到链内filter
的每个pin
连接都应具有其自己的分配器,而其他连接不共享该分配器。 当链更改状态或从图中删除时,分配器可能已取消提交。 如果其他连接使用相同的分配器,则它们无法再处理示例。
除非连接到链的filter
支持动态断开连接,否则请勿删除链。 通常,连接的filter
将支持 IPinConnection 或 IPinFlowControl 接口,但可能改为支持专用接口。