NT下视频采集及解压驱动程序的设计与实现
2009-03-26
作者:彭 勇 黄瑞光
摘 要: 分析了多媒体设备驱动程序的体系结构及视频采集与解压卡的驱动程序设计方案。描述了核心态驱动程序的处理流程,提供了用户态驱动程序的设计思路和具体算法。
关键词: 驱动程序 IRP(输入输出请求包) 驱动程序对象 设备对象
Windows NT的结构决定了应用程序不能直接操作硬件设备,它只能通过一个中间层来读写和控制设备,这个中间层就是驱动程序。驱动程序位于计算机软件的最低层(HAL为硬件抽象层),直接与硬件设备的特性联系在一起。编写驱动程序不仅要了解设备的特性,而且还要了解操作系统的结构,难度较大。本文比较详细地分析了视频采集与解压卡的驱动程序设计思路。
1 视频采集与解压卡驱动程序的结构
多媒体设备相对普通设备来说,有两个特点:数据流量大;对最终期限要求高(即实时性要求比较高)。对于视频采集与解压卡这类多媒体设备来说,驱动程序的编写有其特定的方式。多媒体驱动程序的结构如图1所示。一般说来,根据其代码运行的特权级可分为两层:核心态的驱动程序和用户态的驱动程序。核心态的驱动程序运行于内核模式,可以执行特权级指令,对任何I/O设备有全部的访问权,还能够访问任何虚地址和控制虚拟内存硬件。
用户模式的驱动程序实质上是一个动态链接库(DLL)。它运行在用户态,应用程序向这个接口发出消息请求一定的操作。它们调用WIN32函数与内核模式的驱动程序通讯(WIN32函数又调用NT执行体提供的函数,这些执行体函数提供从用户态到核心态的上下文转换)。用户模式的驱动程序根据接收的消息采取适当的操作,完成操作后将结果返回给应用程序。但是这种结构只适用于解压一幅幅的位图,并不适合采集和解压本设备产生的视频流。
此卡既要采集又要解压,若用标准模式进行设计,就要同时编写视频采集和视频解压的驱动程序,其中还要对解压程序进行改造,编写复杂,尤其调试会很困难。在这种情况下,使用本设计方案,既可满足要求,又可减小设计难度。
内核模式的驱动程序与一般驱动程序无多大的区别,只负责读取数据和进行设备控制。用户模式的驱动程序要处理大部分的事务。在解压时,驱动程序要向设备写入待解压的数据,从设备中取得解压后的数据,向应用程序提供一帧图像的RGB数据。从设备得到的数据是分场存放的4:2:2的YCrCb格式的数据,驱动程序将每帧数据按行进行格式转换,组合成完整的一帧数据(QCIF)交付给应用程序。
2 内核模式的驱动程序的设计
由于多媒体的数据量很大,按照常规的方法(采用IRP包进行数据传输)设计将面临着一个无法解决的问题——中断太快驱动程序将来不及处理。因此必须采用一种新的方法:在驱动程序中建立两块缓冲区(分别用于读写),应用层驱动程序与核心层驱动程序共用缓冲区。当设备中断发生时,根据发生的中断进行处理。如果是读中断,先把数据从设备中读到缓冲区中,发出一个DPC(推迟过程调用),通知应用层驱动程序该缓冲区数据可用,可以取走数据了。如果是写中断,先把缓冲区中的数据写到设备的FIFO中,然后发出一个DPC,通知用户模式驱动程序该缓冲区数据已失效,需要写入新数据。
驱动程序工作流程如图2所示。内核模式向外显露DriverEntry(驱动程序必须要有的一个例程)接口,其它的例程没有固定的名字,为了让I/O管理器找到这些例程,DriverEntry例程负责建立这些函数指针。I/O管理器从非分页系统内存分配一个IRP,响应一个I/O请求,基于由用户指定的I/O函数,把IRP传递给合适的驱动程序Dispatch例程,Dispatch例程检查请求的参数,如果它们是有效的,使用IRP的内容设置设备操作。当操作完成时,在IRP中存放最后的状态代码,并把它送回I/O管理器;I/O管理器使用IRP中的信息完成请求,并把最后状态发送给请求者。
当驱动程序被加载到系统中时,I/O管理器将创建一个驱动程序对象(在系统中代表一个独立的驱动程序,并且为I/O管理器记录每个驱动程序的调度例程的地址),然后调用其初始化例程。
在DriverEntry这个初始化例程中完成的任务是:
·把驱动程序的入口填入该驱动程序对象中。
·用设备的VID和DID查找每条总线上的每个插槽,找到这块图像压缩解压卡,并用IoCreateDevice创建设备对象,用IoCreateSymbolicLink建立符号连接。
·初始化放在非分页区中的Device Extension中的各个分量。包括读写IRP,以及保护各自队列的自旋锁等。
·设备有两块专有内存,必须将其总线相关地址转换成系统范围内的地址,并将其映射到系统虚空间。对于I/O端口,将其总线相关地址转换成系统范围内的地址即可。
·用ExAllocatePool()分配两块非分页缓存,并把它映射到用户地址中。
在驱动程序读写设备之前,必须进一步初始化,表明设备驱动程序的用途:解码或编码。上层的驱动程序调用DeviceIoControl对工作模式进行设置,驱动程序收到此IRP后设置压缩解压芯片的工作模式及视频采集芯片、总线接口芯片的参数。在开始编码或解码之前,上层的驱动程序必须调用DeviceIoControl取回共用缓冲区的地址。
3 用户模式的驱动程序设计
用户模式的驱动程序要完成以下功能:采集时,从设备中读取一定量(一般是一帧)的数据传递给应用程序。解码相对复杂,不仅要向设备写入待解压的数据,从设备中读取一帧解压后的数据,由于读取的数据是CCIR656的视频流不适合应用程序的显示,还要对其进行格式转换,转换成适合计算机显示的RGB格式,向应用程序提供QCIF(356×288)大小的图像。因此可将其划分为两部分:存取数据部分和数据转换部分。
3.1 数据存取
链接库向外输出一套函数供应用程序调用,包括初始化、采集、取得配置、设置配置参数、解压函数等等。在应用程序使用其它输出函数之前,必须调用初始化函数,指定此链接库的用途:编码(采集)还是解码,还要注册一个回调函数(驱动程序在采集或解压完一帧或规定的数据后将调用此函数)。用于编码端(采集)时,链接库的工作比较简单,根据应用程序的要求,从设备中采集一定量的数据,完成请求后,调用应用程序注册的回调函数,在此函数中应用程序处理采集到的数据。编码(采集)端开辟一个线程用于从设备中读取压缩后的数据,在进行读写设备时先打开在内核态驱动程序创建的用于通知的事件对象,等待其变为“已传信”,待共用缓冲区的数据可用,将共用缓冲区中的数据拷贝到另外一个中间缓存(此缓存将作为参数传递给回调函数)。
用于解码时,工作则要复杂得多。首先必须向设备写入待解压的数据,再从设备中读取解压后的数据,并把解压后的数据转换成RGB格式的数据,从中提取出356×288大小的图像数据。当完成这些工作后,调用回调函数。既要使设备充分工作,又不至于使系统开销过大,所以在解码端开辟两个线程,分别用于向设备写入待解压的数据和读取解压后的数据。同样,在进行读写设备时先打开在内核态驱动程序创建的用于通知的事件对象,等待其变为“已传信”,待共用缓冲区的数据可用(读数据)或数据需要更新时,根据要求处理缓冲区。需要注意的是在解码时既要从设备收集解压数据,同时又要对解压后的数据进行格式转换,进行格式转换是一件很费时间的工作,所以数据格式转换例程最好能采用另外一个线程。
3.2 数据格式转换
3.2.1 CCIR656数据流的格式
CCIR656的数据是分场的数据流(分为奇偶场),在每行数据的开始有SAV(有效视频数据的开始),数据的结尾有EAV(有效视频数据的结尾)。每场(奇场和偶场)数据、场逆程数据的SAV和EAV均不相同。CCIR656数据流格式如图3所示。
链接库只需向应用程序提供QCIF(356×244)的图像数据,而每行数据有720个有效象素,一般每行数据的开始和最后的几个象素点是无关紧要的,因此通过舍弃每行数据的开始和结束的4个象素后再进行点抽样,可得到每行356个有效象素。每场数据有244行有效数据(刚好是一场)刚好可以满足需求,并且还可容纳一定的错误。如果第一场数据出现错误,则丢掉此场数据,接着转换第二场数据;若第二场数据未发生错误,则将此场数据送给应用程序;若这一帧的两场数据均有问题,则只有丢掉此帧数据;若第一场数据正确,则不用转换第二场数据。
3.2.2 设计方案
在第一次调用此函数时,由于不知道有效数据从何处开始,所以需要在遍历缓冲区查找SAV,找到后再开始处理。在后面的运行中,起始位置可由前一个缓冲区的偏移位置提供定位参考。在抽样转换过程中,有可能遇到下一个有效象素点(全部或部分分量)或者要处理的定位码(SAV和EAV)不在此缓冲区内(在下一个缓冲区),这时应记录这个象素或者定位码所缺分量的偏移和已得到的部分分量。对于验证部分SAV,须决定从哪个字节开始验证。
如果缓冲区开始的数据就是有效数据的分量,首先查看上一个象素是否已得到完整的处理,否则找到所缺的分量,转换此象素,再对其余象素进行抽样转换处理。对于验证EAV,验证完毕后,跳过行逆程数据,设置合适的偏移量(下一个有效数据行的开始在缓冲区中的位置),进入下一次循环。对于出错的处理,如果已发现正在处理的那一场数据有错误(每个象素的分量不可能是255和0,若是说明出错),开始再次搜索SAV。此场余下的点不再转换,处理点位置只简单地向后移动,以便得到下一步处理所需的偏移。如果奇场数据出错,便处理偶场数据,否则偶场数据不用处理。格式转换流程如图4所示。
由于压缩后的数据较少,PIO(程序控制的I/O)所需的时间较短,并且不需进行后续处理;而解压后的数据量大,有27Mb/s,PIO所需的时间长,还要进行格式转换。所以本驱动程序每秒可压缩25~30帧图像,可解压10~15帧图像。若考虑在压缩时只传出一场压缩后的数据,可解压的帧数会更多,图像质量不会下降,但只能提供356×288格式的图像,不能提供更高分辨率的图像数据。
参考文献
1 Art Baker.Windows NT 设备驱动程序设计指南.北京:机械工业出版社,1997
2 Jeffrey Richer.Windows 高级编程(第三版).北京:清华大学出版社,1999
3 Walter Oney.Programing the windows driver model. Microsoft Press,1999
4 Peter G. Viscarola NT Device Driver Development.北京:电子工业出版社,2000
5 Windows NT技术内幕(第二版).Microsoft Press,1999
6 NT DDK document.Microsoft,1996
7 VC技术内幕(第四版).北京:清华大学出版社,1998
8 钱能.C++ 程序设计教程.北京:清华大学出版社,1999