分享
 
 
 

DirectShow SDK笔记【关于DirectShow(3)】

王朝other·作者佚名  2006-11-16
窄屏简体版  字體: |||超大  

【续前一篇文章】

4.3 Filter States

Filter有三种状态,停止,暂停,运行。暂停状态是为了在Graph中Cue Data, 使得运行命令可以立即响应。Filter Graph Manager控制着所有状态的转换。当应用程序调用IMediaControl的Run, Pause, Stop方法时, Filter Graph Manager就调用所有Filter的相应IMediaFilter方法。停止,运行状态的切换总是要经过暂停,因此,当应用程序对停止的Graph 调用RUN命令时,Filter图表管理器在Run之前首先要暂停。对于大多数的Filter来说,Running和Paused状态是一样的。考虑如下的Filter Graph:

Source -> Transform -> Renderer

假定Source Filter不是实时源。当Source Filter暂停,它创建一个线程生成新的数据并尽可能快的写入到Media Samples. 线程通过调用Transform Filter的输入PIN上的IMemInputPin的 Receive方法把数据向下推。Transform Filter接收到在Source Filter的线程中的Sample.它可能用一个工作线程把Sample递送给Renderer,但是通常是在同一个线程完成。此时Renderer暂停,它等待接收Sample. 在接收到一个Sample后,它就阻塞线程并不定的保存数据。如果是视频Renderer, 把Sample作为张贴图像显示,如果必要就重画图像。

现在,数据流完全准备好进行提交。如果Graph保持暂停,在第一帧Sample后Sample就堆积在Graph, 直到每个Filter都阻塞在Receive或GetBuffer. 但是不会有数据丢失。一旦Source线程非阻塞,它简单从阻塞的点恢复。

Source Filter和Transform Filter忽略从暂停到运行的变换,它们简单的尽可能快的继续处理数据。当Render开始运行,它就开始提交Sample. 首先提交的就是当它阻塞时保存的Sample. 然后,每次都接收到新Sample. 计算Sample的提交时间。(细节可参考Time and Clocks in DirectShow),Render会保存每个Sample直到提交时间,到点后再提交Sample. 当它等待提交时间时,线程也阻塞在Receive方法,或者在工作线程用队列接收Sample. 从Renderer向上的所有Filter都不会陷于时间安排。

实时源(比如捕捉设备)与普通结构有些不同。在实时源中,不合适提前准备任何数据。应用程序可能暂停Graph,在运行前等待比较长的时间。Graph不应该提交过时Sample. 因此,暂停时实时源不会产生数据,只有运行时才有。为了把事件通知给Filter Graph Manager, Source Filter的IMediaFilter方法GetState返回VFW_S_CANT_CUE。此返回值表示Filter已经转换到暂停状态,即使Renderer没有接收到任何数据。

当一个Filter停止时,它拒绝发送给它的任何Samples,Source Filter关闭他们的Stream线程,其他Filter也关闭他们创建的工作线程,Pin再Decommit他们的内存分配器。

4.3.1 State Transitions

Filter Graph Manager按照逆流的方向来切换Filter的状态,从Renderer Filter到Source Filter,这种方式可以防止死锁和Sample的丢失。最关键的状态切换是暂停和停止之间。

·从停止到暂停,当Filter暂停时就准备好了从连接Filter接收Sample。Source Filter是最后一个暂停的。它创建Streaming线程发送Sample,因为下游的Filter的状态都已经切换到暂停了,所以,所有的Filter都可以接收Sample。只有当Graph所有的Renderer都接收到Sample,Filter Graph Manager才算完成了状态的切换(除了前面讲的实时源例外)。

·从暂停到停止。当Filter停止时它要释放它拥有的所有的Sample,以使得上一级Filter中的IMemAllocator::GetBuffer调用脱离阻塞。如果Filter在Receive函数中等待资源,它也会停止等待,并从Receive返回,以使调用Filter解锁。因此,当Filter Graph Manager停止连接的上一级Filter时,Filter就不会在GetBuffer和Receive函数中阻塞,也就能响应停止命令。上级Filter在得到停止命令前可以会递送一些额外Sample。但是下级Filter可能拒绝他们,因为他们已经停止。

4.4 Pull Model

在IMemInputPin接口中,上级Filter决定发送什么样数据,然后将数据推给下级Filter。但对某些Filter,拉模式更适合。现在,下级Filter向上级Filter请求数据。数据依然是从上级到下级,从输出Pin到输入Pin,但是由下级Filter发动数据流动。这种类型连接采用的是IAsyncReader接口。

拉模式的典型应用是文件回放。例如在AVI文件的回放Graph中,Async File Source Filter就执行通用文件读写操作,然后将数据以字节流的方式(不带格式信息)发送给下级Filter。AVI Splitter读取AVI头并把数据流分析为视频、音频流。AVI Splitter能比Async File Source Filter更好的决定它需要什么类型的数据,因此它用IMemInputPin代替IAsyncReader接口。

为了从输出PIN请求数据,输入PIN调用如下一种方法:

·IAsyncReader::Request

·IAsyncReader::SyncRead

·IAsyncReader::SyncReadAligned

第一个方法是异步的,支持多个重叠读写。其他是同步的。

理论上,任何Filter可以支持IAsyncReader,但是实际上它被设计来连接Source Filter和Parser Filter。Parser的功能与推模式中的Source Filter非常相同。当它暂停时,创建一个数据流线程从IAsyncReader连接拉数据,并推向下一级。输出PIN使用IMemInputPin,Graph的余下部分使用标准的推模式。

5. Event Notification in DirectShow

5.1 Overview of Event Notification

当某个事件发生时,比如数据流结束,产生一个错误,提交流失败等,Filter就给Filter Graph Manager发送一个事件通知。Filter Graph Manager自己处理部分事件,另一部分交给应用程序处理。如果Filter Graph Manager没有处理事件,它就把事件通知放入到一个队列中。图表管理器也可以将自己的事件通知放进队列中。

应用程序以事件队列获取事件并根据事件类型进行处理。DirectShow中的事件通知和Windows的消息机制差不多。应用程序也可以取消Filter Graph Manager特定事件的默认行为。Filter Graph Manager就直接把事件放入队列,留给应用程序来处理。

5.2 Retrieving Events

Filter Graph Manager暴露了三个接口处理事件通知:

·IMediaEventSink Filter用这个接口来Post事件。

·IMediaEvent 应用程序利用这个接口来从队列中查询消息。

·IMediaEventEx 是IMediaEvent的扩展。

Filter通过IMediaEventSink::Notify方法向Filter Graph Manager提交事件。事件通知包括一个事件Code,这个Code不仅仅代表了事件的类型,还包含两个DWORD类型的参数用来传递一些其他的信息。根据不同的事件Code,附加信息可能是指针、返回码、参考时间或其他信息。获取事件Code和参数的全部列表可参考Event Notification Codes.

应用程序通过调用IMediaEvent::GetEvent从事件队列中获取事件。如果有事件发生,该函数就返回一个事件码和两个参数,如果没有事件,则一直阻塞直到有事件发生和超过某个时间。调用GetEvent函数后,应用程序必须调用IMediaEvent::FreeEventParams来释放事件码所关联的资源。例如,参数可能是Filter Graph分配的BSTR内存。下面的代码演示了如何从事件队列中提取事件:

long evCode, param1, param2;

HRESULT hr;

while (hr = pEvent->GetEvent(&evCode, &param1, &param2, 0), SUCCEEDED(hr))

{

switch(evCode)

{

// Call application-defined functions for each

// type of event that you want to handle.

}

hr = pEvent->FreeEventParams(evCode, param1, param2);

}

为了重载Filter Graph Manager对事件的缺省处理,可以用某个事件码做参数调用IMediaEvent的CancelDefaultHandling方法。这样就可以屏蔽Filter Graph Manager对某个事件码的处理了。如果要恢复图表管理器对该事件码的缺省处理,可以调用RestoreDefaultHandling方法。如果Filter Graph对某个事件没有缺省的处理,调用这两个函数是不起作用的。

5.3 Learning When an Event Occurs

为了处理DirectShow事件,应用程序需要一种机制来知道什么时候队列中有等待的事件。Filter Graph Manager提供了两种方法:

·窗口通知:Filter Graph Manager发送开发者自己定义的窗口消息

·事件信号:如果队列中有DirectShow事件,就用事件信号通知应用程序,如果队列为空就重新设置事件信号。

应用程序可以使用这两种技术。窗口通知通常更简单。

5.3.1 Window Notification

建立窗口通知可调用IMediaEventEx::SetNotifyWindow方法,并指定一个私有消息。应用程序可使用从WM_APP到0XBFFF之间的消息值作为私有消息。只要Filter Graph Manager把一个新事件放入到队列时,就向目标窗口发送消息。应用程序从消息队列响应消息。

下面的代码演示了如何利用消息通知:

#define WM_GRAPHNOTIFY WM_APP+1 // Private message.

pEvent->SetNotifyWindow((OAHWND)g_hwnd, WM_GRAPHNOTIFY, 0);

消息是一个普通的窗口消息,是从DirectShow的事件通知队列单独提交的。这种方法的好处多数程序已经实现了消息循环。因此,我们可以合并DirectShow的事件通知而不需要过多额外工作。下面的代码说明了如何响应消息通知的框架。完整的例子可参考Responding to Events.

LRESULT CALLBACK WindowProc( HWND hwnd, UINT msg, UINT wParam, LONG lParam)

{

switch (msg)

{

case WM_GRAPHNOTIFY:

HandleEvent(); // Application-defined function.

break;

// Handle other Windows messages here too.

}

return (DefWindowProc(hwnd, msg, wParam, lParam));

}

由于事件通知和窗口的消息循环都是异步的,因此,当你的应用程序处理消息的时候,队列中或许有N个事件等待处理。同样,如果事件失效,它也可能从事件队列清除掉。因此,在你调用GetEvent的时候,一定要循环调用,直到返回一个错误码,这表明队列是空的。

当你释放IMediaEventEx指针时,你可以调用SetNotifyWindow来取消事件通知,记住此时要给这个函数传递一个NULL指针。在你的事件处理程序中,在调用GetEvent之前一定要检查IMediaEventEx指针是否为空,这样就可以避免错误。因为有时在释放IMediaEventEx指针后可能会得到通知。

5.3.2 Event Signaling

在Filter Graph Manager里有一个手动设置的Event内核对象,用来反映事件队列的状态。如果队列中有等待处理的事件,Event就处于通知状态,如果队列是空的,IMediaEvent::GetEvent函数调用就会重置该Event对象。应用程序可利用此事件来判断队列的状态。

应用程序可以调用IMediaEvent::GetEventHandle获得Event内核对象的句柄,然后就可以调用WaitForMultipleObjects来等待事件的发生,如果Event被通知了,就可以调用GetEvent来获得DirectShow的事件。下面的代码演示了事件信号。返回事件句柄,再等待1000毫秒。如果事件变为信号状态,调用GetEvent返回事件Code和参数。获取到EC_COMPLETE事件Code时表示回放结束中止循环:

HANDLE hEvent;

long evCode, param1, param2;

BOOLEAN bDone = FALSE;

HRESULT hr = S_OK;

hr = pEvent->GetEventHandle((OAEVENT*)&hEvent);

if (FAILED(hr)

{

/* Insert failure-handling code here. */

}

while(!bDone)

{

if (WAIT_OBJECT_0 == WaitForSingleObject(hEvent, 100))

{

while (hr = pEvent->GetEvent(&evCode, &param1, &param2, 0), SUCCEEDED(hr))

{

printf("Event code: %#04x\n Params: %d, %d\n", evCode, param1, param2);

pEvent->FreeEventParams(evCode, param1, param2);

switch (evCode)

{

case EC_COMPLETE: // Fall through.

case EC_USERABORT: // Fall through.

case EC_ERRORABORT:

CleanUp();

PostQuitMessage(0);

return;

}

}

}

}

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有