VC6中利用多线程处理多视频
2019-12-27宋勇强
宋勇强
(山西大众电子信息产业集团有限公司,山西 太原 030024)
多路视频实时传输往往对实时性有很高的要求,要在满足实时性的同时保证视频的码率,不仅要求有完善的视频编解码功能,而且对网络传输控制也要严格控制,还要考虑网络中断的异常处理,重连机制,虽然网上有很多类似的例子,但是要不就是不开源,修改困难,要不就是没有结合项目,很难在实际中应用。为了真正实现此功能,我以Microsoft Visual C++6.0作为编译环境,用多线程的方式实现了对8路视频的编解码显示处理。下面结合项目进行详细叙述。
1 概述
本软件主要功能为通过网口同时接收8路视频,根据实际需求同时显示其中3路视频到三个显示器,界面上方的状态指示以及右侧列表是通过串口接收操纵杆数据进行相应处理,显示操纵杆的实时状态和命令数据,此文章不作为重点,不再详细描述,界面左下绿色矩形显示经过处理解码后的视频,软件形成最终效果如图1。
软件通过在主对话框定义三个视频对话框对象实现不同显示器的图像显示,网络的初始化以及8路视频接收线程函数都在主对话框中定义实现,主对话框收到视频数据后通过消息发送到相应的视频对话框,视频对话框对视频数据进行解码处理并显示。软件组成图如图2 。
图1 软件界面效果图
图2 软件组成图
2 程序的主要实现
2.1 主对话框类CMultipleVideoDlg的设计
CMultipleVideoDlg类是软件的主对话框类,主要功能包括对网络的初始化,8个视频处理线程的实现等。
2.1.1 初始化网络
初始化网络SOCKET功能,需要在CMultipleVideoApp的InitInstance()函数中添加
if (!AfxSocketInit())
{
AfxMessageBox(IDP_SOCKETS_INIT_FAILED);
return FALSE;
}
然后通过WSAStartup(MAKEWORD(2,2),&WSAData))函数安装SOCKET。这样就可以正常使用网络接收发送函数了。
2.1.2 接收处理线程的实现
主对话框通过AfxBeginThread(AFX_THREADPROC(&RecvData1),this)启动8个线程来对8路视频数据同时进行接收及处理。线程是系统分配处理器时间资源的基本单位,是程序执行流的最小单元,编程中往往把接收各种端口数据作为独立的线程执行,保障在软件进行其他操作时接收线程能够实时接收数据。在进行网络编程时,我们常常见到同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种程序调用方式。同步就是程序调用一个功能,该功能没有结束前,程序死等结果。异步就是程序调用一个功能,不需要知道该功能结果,该功能有结果后通知程序(回调通知)。阻塞就是调用函数,函数没有接收完数据或者没有得到结果之前,程序不会返回。非阻塞就是调用函数,函数立即返回,通过select通知调用者[1]。本软件因为要同时接收8路视频,如果某一路视频断开连接或数据错误,必须保证其他视频正常接收处理,所以必须采用异步非阻塞模式。当把网络SOCKET设置为非阻塞模式时,相当于通知系统当数据没有准备好的时候不要让线程睡眠,而是应立即返回一个错误代码;当数据准备就绪时,返回成功指示,应用程序开始处理数据。处理线程间通信有临界区、互斥、信号量、事件等方法,本软件采用了利用事件对象的方法处理进程异步,一个事件对象有两种状态:信号态与非信号态。线程能监视于信号态的事件,以便在适当的时间完成对事件的操作。8个线程对应的事件都在初始时候建立,默认状态为非信号态,然后在线程内部判断时间状态,当需要把某一路视频显示到视频界面时,利用SetEvent()把事件置为信号态,平时线程处于挂起状态,当事件变为信号态时,线程继续执行。接收线程采用非阻塞模式,如果网络端口暂时无数据则稍后再试,如果数据错误,则断开SOCKET,间隔时间进行重连,如果对方SOCKET正常结束,则正常退出,线程挂起。线程处理流程如图3。
图3 线程处理流程图
2.1.3 网络连接函数的实现
初始化SOCKET网络连接函数是程序一开始和出现网络故障或数据错误时进行的操作,,因为接收线程中经常需要重新初始化SOCKET进行重练,为了避免线程卡死,连接函数必须采用无阻塞的方式,当无法连接或连接错误时程序需要直接返回错误信息,软件通过对错误信息的判断进行重连或其他操作,不影响主程序的执行,网络连接函数的流程如图4。
图4 网络连接流程图
2.2 CMulVideo类的设计
CMulVideo类共有三个对象,分别对应3个视频显示对话框,本类的主要功能是接收视频数据进行解码并显示。CMulVideo类接收线程收到正确视频数据后发送的消息进行解码处理。然后以40 ms每帧进行显示。软件编解码采用了H264格式,通过timeSetEvent()函数(VC提供的精确定时器,精确到毫秒级)来控制显示视频的周期。显示图像时没有采用DIRECTX,而是采用了YUV转RGB,然后调用MFC系统绘图函数实现图像的显示,其目的是为了方便以后在视频上进行绘图,画准心等操作,而且DIRECTX对多显示的处理比较繁琐,需要获取显卡驱动,而RGB只需要扩展坐标显示就可以实现三个显示器显示不同视频的功能。
3 结束语
本文结合实际项目讲解了多线程处理多路视频的方法,尤其是对非阻塞方式的网络连接、接收处理数据以及多线程同步,数据交互进行了详细描述,对视频编解码以及精确定时也有一定的阐述,而且此项目经过验证,在实际中有了良好的应用。是多线程编程和视频处理很好地参考资料。