DirectShow中可否通过IMediaEventSink在引脚方法中投递用户消息
DirectShow场景下IMediaEventSink投递自定义事件相关问题解答
1. 能否通过IMediaEventSink::Notify投递自定义消息传递FillBuffer错误
完全可行,这是DirectShow原生设计的滤镜层向上层应用传递事件的标准机制,不存在兼容性问题。
你只需要将自定义事件码定义在EC_USER(值为0x8000)及以上的区间,避免和系统预定义的事件码冲突即可。Notify方法携带的两个LONG_PTR类型参数可以自由定义用途,比如第一个参数传自定义错误码、第二个参数传上下文标识,上层应用通过滤镜图管理器的IMediaEvent::GetEvent接口就能从事件队列中取出对应消息,完全可以满足FillBuffer中数据源读取错误、流异常等场景的上报需求。
2. 输出引脚获取IMediaEventSink接口的正确方式
IMediaEventSink接口由滤镜图管理器实现,引脚和滤镜本身都没有实现该接口,不要尝试直接从引脚QueryInterface获取,正确获取路径如下:
- 承载FillBuffer逻辑的输出引脚通常属于CSource派生的自定义推源滤镜,基类CSource内部持有
m_pGraph成员,当滤镜被成功加入滤镜图时,该成员会被自动赋值为有效的IFilterGraph接口指针。 - 你可以在滤镜类重写的
JoinFilterGraph回调中,通过m_pGraph->QueryInterface(IID_IMediaEventSink, (void**)&m_pEventSink)拿到接口指针并缓存,避免每次FillBuffer调用都重复查询。注意不要在滤镜或引脚的构造函数中获取,此时滤镜尚未加入图,m_pGraph为空指针。 - 引脚类内部通过持有的m_pFilter指针访问所属滤镜的缓存m_pEventSink即可使用。
示例代码片段:
// 自定义滤镜类中重写JoinFilterGraph HRESULT CMySourceFilter::JoinFilterGraph(IFilterGraph* pGraph, LPCWSTR pName) { HRESULT hr = CSource::JoinFilterGraph(pGraph, pName); if (SUCCEEDED(hr) && pGraph) { // 释放之前缓存的接口 if (m_pEventSink) m_pEventSink->Release(); // 查询IMediaEventSink hr = pGraph->QueryInterface(IID_IMediaEventSink, (void**)&m_pEventSink); } return hr; } // FillBuffer中调用上报错误 HRESULT CMyOutputPin::FillBuffer(IMediaSample* pSample) { // 模拟读取外部数据源出错 int nErrCode = ReadDataFromExternalSource(pSample); if (nErrCode != ERROR_SUCCESS) { CMySourceFilter* pFilter = (CMySourceFilter*)m_pFilter; if (pFilter->m_pEventSink) { // 自定义事件码:EC_USER + 100,参数1传错误码,参数2传引脚标识 pFilter->m_pEventSink->Notify(EC_USER + 100, (LONG_PTR)nErrCode, (LONG_PTR)this->m_Id); } return S_FALSE; // 按规则返回流结束/错误标识 } // 正常填充逻辑省略 return S_OK; }
3. 流线程中调用Notify的线程安全性说明
IMediaEventSink::Notify是天然线程安全的,DirectShow滤镜图管理器内部的事件队列实现了完整的线程同步逻辑,不管你是在应用控制线程、流处理线程(就是FillBuffer运行的独立线程)还是其他工作线程调用该方法,事件都会被安全插入队列,不会出现竞态、数据错乱问题,不需要你额外加锁保护。
但调用时需要注意两个细节:
- 如果通过参数传递堆内存指针、字符串或自定义结构体,必须保证内存在上层应用取出并处理完事件前不会被释放,避免野指针问题,优先选择传整数类型的错误码、标识位可以规避这类问题。
- 不要直接通过参数传递未封送的COM接口指针,否则可能因为COM套间不匹配引发异常。
内容的提问来源于stack exchange,提问作者user19309573
相关产品推荐
相关产品推荐

