张 奔,李大明
(中国电子科技集团公司第二十八研究所,江苏 南京 210007)
在值班系统中对重点区域的实时监控是必不可少的,值班人员能够掌握监控区域的实时视频画面,通过视频实时掌握区域动向[1-3]。随着计算机性能的提升,用计算机处理视频信息和数字视频传输已经得到广泛应用。目前,网络传输的技术越来越成熟,网络带宽也越来越高,但是视频的数据量一般都非常大,如果不通过任何压缩技术传递视频,会造成数据堵塞、延迟甚至丢失现象,视频画面会严重卡顿。因此,在传递视频时对视频图像压缩成为必然的环节。在诸多视频压缩标准中,MPEG-4以其高压缩比和高性能备受人们的青睐,被广泛应用于网络环境下的视频传输。本文采用Divx编解码器[4-6]对视频进行编码、压缩。Divx编码后形成以帧为格式的MPEG-4流,Divx解码也是以帧的格式解码,通过Divx编解码器有效地解决了由于网络的局部不稳定导致的视频图像重影、抖动、花瓶,得到了良好质量的视频画面。
系统硬件设备由CCD摄像头、采集卡、服务器、交换机和若干个客户端席位组成,组成架构如图1所示。系统支持5~10个客户端席位,在服务器上安装一个基于PCIe总线的采集卡,摄像头通过AV线与采集卡相连,服务器采集到的视频流经过压缩成为一个信息包,通过网络传送到客户端,客户端接收到信息包进行解压并实时显示,从而得到清晰流畅的运动图像。同时服务器端也可以实时显示所采集的视频。
图1 系统硬件框图
图2 系统软件流程图
装有视频采集卡的服务端接收摄像头拍摄的视频图像,服务端的软件采用VFW技术对视频图像进行捕获,VFW是微软公司推出的一款关于数字视频的软件开发包,其优势简单直观,能够快速地运用回调函数完成对视频的实时捕获并对视频源进行控制。在视频捕获前先创建一个视频捕获窗口,通过capDriverConnect()关联捕获视频窗口到视频驱动程序上,对视频捕获参数和窗口显示模式进行设计,通过capSetCallbackOnFrame()注册回调函数,捕获图像到缓存并进行相应处理,完成捕获。
视频采集的数据是位图型式的视频帧,发送前要对视频帧进行编码,利用Divx编码器压缩以后形成以帧为格式的Mpeg4流。Divx解码器也是以帧的格式解压。为了在接收端能够方便地提取出一帧,提出格式组建帧(见图3)。
图3 视频帧格式
每个视频帧由5个字段组成,各个字段的描述如下:第1个字段为占用4个字节的帧开始标志,标志着一帧的开始;第2个字段为占用4个字节的帧大小,表示整个帧的大小;第3个字段为占用4个字节的帧编号,表示帧的顺序编号;第4个字段为占用1个字节的帧类型,标志此帧是否是关键帧;第5个字段为帧数据,存放压缩后一帧的完整数据,大小不定,压缩比越高,帧数据的大小越小。
为了保证接收端能够及时接收到发送过来的视频数据,在发送端专门创建一个线程用来发送数据。同时主线程循环地对采集到的视频数据进行压缩编码。发送线程的工作流程如图4所示。
图4 发送线程工作流程
线程中发送的数据帧是按照上一节中的方法组建好的数据帧。该方法能够保证正在发送的当前帧能够完整地到达接收端。
注意此线程中刚开始或者每当发送完一帧以后,线程就转到挂起状态,等待外界唤醒。这个任务由回调函数完成,在回调函数中,判定如果发送线程准备就绪(处于挂起状态),则进行图像压缩,然后唤醒线程发送压缩完的数据,否则直接跳出,等待下一次调用回调函数。
接收端最重要的是从接收的数据流中提取出完整的一帧。该方法的思想是:首先从数据流中寻找帧开始标志,再从紧挨后面的数据中提取出帧的大小,然后从接收缓冲区中读入该帧剩余的数据,最后寻找下一帧的开始标志,如此往复。接收端的工作流程如图5所示。
图5 接收端的工作流程
Java Native Interface(JNI)是Java语言的本地编程接口,是J2SDK的一部分,已经被集成到标准Java平台之中。在Java程序中,可以通过JNI实现一些用Java语言不便实现的功能。使用JNI提供的方法,Java代码能够直接与特定操作系统和硬件平台中的本地共享二进制库进行交互。这个交互过程发生在相同的Java虚拟机进程之中。
将Java类中声明的类型为“Native”的Java方法映像到共享二进制库中的相应函数上,并且将这两者加载到相同的进程空间。JNI框架使得本地方法使用Java对象的方式和Java代码使用这些对象相同。一个本地方法能够创建Java对象,然后检查并使用这些对象,也可以检查并使用由Java应用程序代码创建的对象,甚至能够修改它创建的或传递给它的Java对象。因此,本地语言和Java应用程序都能创建、修改、访问Java对象,并在它们之间共享这些对象。本地方法也能够很容易地调用Java方法、传递方法所需的参数并得到返回的结果。
1)步骤1:在MyEclipse里建立一个包含Compressor类的Java工程,该类中包含了需要调用的本地化方法的描述。其中,本地化方法应当用native关键词声明。使用System.LoadLibrary()方法加载需要的动态链接库。代码如下:
Package com.chnic.jni
Public class Compressor{
Static{
System.LoadLibrary(“DeCompressorEnd”);
}
Public Compressor (){}
Public native void InitCompressor();
Public native void FillBitmapstruct();
Public native void UnInitCompressor();
Public native void UnCompress(byte[] in,int width,int heigh,byte[] out);
}
2)步骤2:将步骤1生成的源文件用Java类编译器编译成二进制字节码文件,接下来要用javah方法来生成一个相对应的.h头文件。Javah是一个专门为JNI生成头文件的命令。CMD打开控制台之后输入javah回车就能看到javah的一些参数,在控制台进入到工程的根目录之后,然后输入命令:Javah-jni com.chnic.jni.Compressor。
命令执行完之后,在工程的根目录下就会发现com_chnic_jni_Compressor.h这个头文件。
ifdef__cplusplus
extern "C" {
endif
/*
* Class: com_chnic_jni_Compressor
* Method: InitCompressor
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_chnic_jni_Compressor_InitCompressor
(JNIEnv *, jobject);
/*
* Class: com_chnic_jni_Compressor
* Method: FillBitmapStruct
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_chnic_jni_Compressor_FillBitmapStruct
(JNIEnv *, jobject);
/*
* Class: com_chnic_jni_Compressor
* Method: UnInitCompressor
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_chnic_jni_Compressor_UnInitCompressor
(JNIEnv *, jobject);
/*
* Class: com_chnic_jni_Compressor
* Method: Compress
* Signature: ([BI[B)V
*/
JNIEXPORT void JNICALL Java_com_chnic_jni_Compressor_UnCompress
(JNIEnv *, jobject, jbyteArray, jint, jint,jintArray);
ifdef __cplusplus
}
endif
endif
本系统在Windows 7平台下服务器采集到的视频数据压缩之后以广播方式将数据发送出去,图像大小为640*512的位图,每秒采集25帧,压缩比最大时为100,且延迟较小,用户只需在客户端登录Web网页即可查看到解压后的视频流。经过比较,服务器上显示的视频和客户端显示的视频基本相同,并且客户端的视频和服务器上的视频基本同步,流畅性也较好。服务端和客户端的视频截图分别如图6和图7所示。
图6 服务器显示的视频
图7 客户端显示的视频
本文对实时视频传输系统的开发设计做了较为详细的描述,重点介绍了基于Divx的视频编解码技术、JNI技术以及基于B/S结构的实时视频数据传输显示。试验表明,通过上述方法实现的网络视频传输能达到良好质量的视频画面,满足了视频实时传输的要求,达到了预期的效果。