新闻中心

EEPW首页 > 消费电子 > 设计应用 > 基于多线程编程的视频监控系统四路回放的设计

基于多线程编程的视频监控系统四路回放的设计

作者:■ 上海交通大学电子信息学院 黎芳芳 余致春 杨煜普时间:2005-04-27来源:eaw收藏

摘    要:本文结合H.263解码、编程和的应用,介绍了系统回放的软件设计以及的实现。
关键词: 

引言
目前国内外市场上占主导地位数字系统。一般具有如下功能:监视、录像、回放、备份、报警、控制、远程连接等。目前的视频监控系统一般只能播放一路视频(简称单路回放),即同一时间只能播放一个视频文件。如果要想同时查看多个历史纪录文件,这种传统的回放方式略显不足。本文提出一种能同时播放四个任意选择的历史文件(简称)的设计方案。

关键技术
回放主要是将存储在硬盘中的压缩文件解码显示出来,所以在设计时必须根据压缩文件的格式,进行相应的解码。用于视频监控系统的编码标准主要有H.263、MPEG-4等。本视频监控系统采用的编码标准是H.263,所以回放的解码也要根据H.263标准进行解码。解码的过程大致是:首先打开编码视频文件,得到视频编码流后,先找到图像开始码,得到图像头。然后寻找帧数据宏块信息,确定帧的编码模式(帧内编码、帧间编码或是没有编码),针对不同模式的编码宏块分别进行相应的解码,解码后的数据传递给显示程序。不像编码程序,解码程序是基于宏块进行操作的,它并不需要把关键帧和非关键帧区分开来解码。
要把解码后的图像显示出来,可以使用。DirectDraw是微软发行的DirectX软件开发工具箱(SDK)中的一部分,是围绕OLE和COM接口来设计的。它允许程序员直接处理显示存储设备,支持硬件覆盖,支持页面翻转,使图形处理较传统的GDI图形处理有了较大的提高。
由于本设计的回放部分能够同时播放四个视频文件,若只有一个线程(线程是操作系统分配CPU时间的基本单位,一个线程可以执行应用程序的任何部分,一个应用程序至少包含一个主线程)来播放,很难实现四个文件的同时播放。如果设计成(应用程序中,除主线程外,还创建有其它线程),就可以有效的利用CPU资源,同时播放多个视频文件。进行程序设计时,一般可以调用Windows API函数CreateThread。此时需要考虑线程互斥和线程同步问题,以保证应用程序的正常执行。线程互斥是指对于共享的操作系统资源,在各线程访问时的排它性。当有若干个线程都要使用某一共享资源时,任何时刻只允许一个线程去访问,其它要使用该资源的线程必须等待,直到占用该资源者释放了该资源。线程同步是指若干个线程之间具有一种制约关系,一个线程执行依赖于另一个线程的消息,当一个线程没有等到另一个线程的消息时,应该等待,直到消息到达时,才被唤醒。

系统软件设计
视频监控系统的回放部分一般具有:历史文件搜索、基本播放控制以及对搜索出来的文件进行备份(单个文件或所有文件备份光盘),保存和打印播放视频文件的当前帧等其它功能。
根据上述基本功能,采用面向对象的程序设计方法,对回放部分进行设计的程序流程图如图 1 所示。
在图 1 所示的框图中,进入回放时,首先要进行系统变量、DirectDraw的初始化以及分配一些系统资源。退出回放时,若有回放线程在运行,首先要关闭回放线程,然后关闭解码器,释放系统资源,以便下次能够进入回放。根据文件的记录位置和文件名的特点(文件的记录位置为  Drivers:file记录通道日期,文件名为时和分的组合,例如,D:file120030620512为通道1在2003年6月20日5时12分记录在D: 盘的文件),设计搜索函数。在双击文件名的响应函数里,首先打开文件,然后打开解码器,创建播放线程,对文件进行播放。同时可以设置控制变量,对播放线程实行控制,以实现播放文件的暂停、快进、单帧、慢放、循环播放、缩放等操作。

四路回放的实现
四路回放是整个回放部分设计的关键部分,其核心思想是创建四个播放线程,并行地在界面上不同的地方显示四个视频图像。基于界面设计的方便性和面向对象设计方法的考虑,在程序设计时,采用C++  Builder 5.0编程语言。四路回放流程图如图2所示。
首先,主要初始化解码器的缓冲区、DirectDraw表面以及各通道(四路回放会有四个通道)的句柄等;接着,根据选择的通道和文件,创建播放线程并启动。为了提高程序的可靠性,还需根据打开文件的前几个字节判断文件格式(正确的文件在编码时已经在文件头中写入了文件的信息);根据设置的标志判断该通道是否有线程在运行。线程被创建后,就可以解码打开的视频文件流,显示图像,并可以对播放的视频流文件进行控制,以实现暂停、单帧、快进、慢放等操作;若同时要在另一通道播放文件,可以先选择另一通道,然后进行同样的操作。这样在设计时最多允许在四个通道播放文件,若四个通道同时播放文件,就实现了四路回放。
播放线程函数是该设计部分的关键,播放线程函数的实现代码如下(用C++ Builder编程语言编写):
1    DWORD WINAPI PlayThread::VideoThread(LPVOID lParam)
2     {
3   PlayThread* pThis = (PlayThread*)lParam;
4      short nRetCode=0;
5      pThis->m_nThreadState=1;
6    while(pThis->m_nThreadState != 2)
7       {
8pThis->g_VideoTimeTick. TimeNow=timeGetTime();
9        pThis->SleepTime=pThis->g_VideoTimeTick.Tcount-
10  (pThis->g_VideoTimeTick.TimeNow-pThis->g_VideoTimeTick.TimePrev);
11     if(pThis->SleepTime<1)
12     pThis->SleepTime=1;
13     Sleep(pThis->SleepTime);
14  pThis->m_PlayBack->m_PlayThread=pThis;
15 nRetCode=pThis->m_PlayBack->DecodePictureHeader ();
16     if(nRetCode!=0)
17     {
18    pThis->m_PlayBack->DecodePicture();
19   pThis->src[0]=pThis->m_PlayBack->DecRet Value.pchPLum;
20    pThis->src[1]=pThis->m_PlayBack->DecRetValue.pchPCb;
21     pThis->src[2]=pThis->m_PlayBack->DecRetValue.pchPCr;
22     pThis->DrawImage(pThis->src,pThis);
23           }
24       }
25 }
在函数的开始,初始化局部变量nRetCode为0,线程状态变量m_nThreadState=1(m_nThreadState是自定义的线程状态变量,其意义是:m_nThreadSate=0,没有线程;m_nThreadState=1,线程在运行;m_nThreadState=2,线程中止)。程序的8—13行,实现播放时帧间的延时,以使视频文件能够流畅地播放;程序的15行通过调用自定义的函数DecodePictureHeader解码输入视频流的图像头;程序的18行解码单帧图像;程序的 22行把解码后视频流显示出来,在函数DrawImage中使用DirectDraw显示图像。
欲使播放线程结束,可以设计线程结束函数EndVideo,调用Windows API函数Terminate Thread,置m_nThread State=0,并复位与线程相关的变量。考虑到对播放的文件实现暂停、单帧、快进、慢放等操作,可以在线程播放函数中加入对应的控制变量,慢放时增加帧间的时延;快进时减少帧间的时延;暂停时,在帧间设置一个无限循环控制,直到控制变量改变,再进行下一帧的解码、显示。
由于应用多线程编程,在显示函数等中,多个线程会访问共享资源表面缓冲区。可通过在初始化过程中调用Windows API 函数CreateMutex创建互斥变量,然后在自定义的显示函数DrawImage中调用Windows API函数WaitForSingle Object (m_hMutext_Dieplay,1000)实现共享资源的互斥。访问结束时,调用Windows API函数ReleaseMutex (m_hMutext_ Dieplay),释放共享资源。
本程序可以方便地完成界面的设计,通过单步跟踪、断点等调试方法实现了对编写程序的调试。

结语
上述通过多线程设计的四路回放,充分利用了CPU 资源,使CPU能够流畅地实现解码,在不影响视频监控系统性能的情况下,能够同时播放多个文件,增加了系统的灵活性。■

参考文献
1 沈兰荪,卓力. 视频编码与低码率传输. 北京:电子工业出版社,2001.12
2 刘富强. 数字视频监控系统开发及应用. 北京:机械工业出版社,2003.3
3 彭达,汪道智. Visual C++ 多媒体编程技术. 北京:人民邮电出版社,1999.11
4 朱正茂,彭湃. Borland C++ Builder 5 实用编程技术. 北京:中国水利水力出版社,2001.1



评论


相关推荐

技术专区

关闭