作者:iceiilin
转自:http://blog.csdn.net/iceiilin/article/details/7367284
winCE6.0摄像头驱动中,buffer的配置方式分为由驱动分配和由用户分配两种,用户指的一般为DirectShow。由用户分配里,如果驱动的DMA准备时间长,则驱动需要知道用户端所有的buffer,分配方式称为CSPROPERTY_BUFFER_CLIENT_LIMITED;如果驱动的DMA准备时间短,则用户可以任意处置其buffer,分配方式称为CSPROPERTY_BUFFER_CLIENT_UNLIMITED。本文分析的为CSPROPERTY_BUFFER_CLIENT_LIMITED分支下的情况。
根据MSDN,摄像头驱动的数据采集和DirectShow显示为异步过程,两者间通过消息机制进行传递。客户端创建消息并将句柄交给驱动。当接收到一帧时,驱动将其放到消息队列中。用户端有单独的线程监听该队列,当有数据时就进行相应处理。摄像头功能具体实现时,同时存在两条buffer队列,用户端管理idle buffer队列,驱动管理ready buffer队列。
1. 向驱动“注册”用户端buffer @ CS_ALLOCATE
驱动初始化时,所有buffer均为idle buffer;驱动知道它们的存在,但无法对其进行控制。“驱动知道它们的存在”步骤在IOCTL_CS_BUFFERS -> CS_ALLOCATE时进行。在DWORD CPinDevice::AllocateBuffer( PCS_STREAM_DESCRIPTOR pCsDescriptor, LPVOID pOutBuf, DWORD OutBufLen, DWORD *pdwBytesTransferred)中
将用户传入的流标识符pCsDescriptor复制到PinDevice的成员变量m_pStreamDescriptorList的影子标识符csStreamDescriptorShadow中,并调用子函数,生成相应的handle,告知用户端和驱动。
![](http://hi.csdn.net/attachment/201203/19/0_13321363214ZG5.gif)
2. 用户端将buffer使用权交给驱动 @ CS_ENQUEUE
用户端控制buffer的操作权限,在驱动需要操作buffer的时候,用户端使用CS_ENQUEUE命令把buffer从idle queue中移到ready queue中。子函数DWORD CPinDevice::EnqueueDescriptor( PCS_STREAM_DESCRIPTOR pUnMarshalCsDescriptor)是它的具体实现,参数从用户端传入。
之后的一段,
接着把用户端传入的流标识符(含数据buffer地址)映射后存储到该索引值处
注意上段倒数第二行pCsStreamDescriptorExternal变量,作为PinDevice的私有成员变量,只有此处将其赋非NULL值,可视为此索引值元素和用户端流标识符“绑定”的标志。至此,用户端将buffer交给了驱动。
3. 驱动填充buffer并交换给用户端 @ 硬件中断
在发生硬件帧同步的中断中,逐层向上回调并调用子函数,至bool CPinDevice::RemoveBufferFromList(PCS_STREAM_DESCRIPTOR * ppCsStreamDesc, PVOID * ppMappedData, PVOID * ppUnmappedData)
若pCsStreamDescriptorExternal变量非空,即用户端已将buffer交由驱动,则得到该数据buffer指针。接着调用BOOL CPinDevice::InitMsgQueueDescriptor (PCS_MSGQUEUE_BUFFER pCsMsgQBuff, PCS_STREAM_DESCRIPTOR pCsStreamDesc, PVOID pMappedData, PVOID pUnmappedData, BOOL bBufferFill),由硬件将数据写入buffer
并将包含buffer的整个流标识符放入消息内容中
最后驱动调用WriteMsgQueue函数把完成填充的一个buffer放入消息队列,供用户端异步处理。
至此完成了用户端和驱动间流媒体buffer的处理。只有在EnqueueDescriptor中将pCsStreamDescriptorExternal赋值,当其值非空时,才能在中断中将相应元素的buffer取出进行填充,取出后即将pCsStreamDescriptorExternal再次置空。手头暂时没有板子可以验证,但是可以推测,需要用户端重新发送CS_ENQUEUE控制命令,才能重新启动硬件下一帧的填充。