前面讲解了MIL视频的保存、加载和播放,那么有不少人私信我说按照我的讲解构建的视频播放器有问题,这里综合他们的问题,我这里演示一个简单的播放器,它实现了简单的视频播放功能:打开视频、播放视频和暂停视频。如果你想实现更复杂的功能可以在此基础上扩展。在此过程中,我会指出常犯的错误。
另外,需要补充说明的是,这里的MIL做的播放器,由于支持的视频编码的格式和完善程度有限,并不支持通用视频 的打开,例如如果你在网上下了一段AVI视频那么很可能用这个播放器是打不开的,我们用这里的视频功能主要是构建软件内建的视频录制、保存和播放功能,录制保存的视频用通用播放器是可以打开的。
先讲一下构建一个简单视频播放器的流程:
1.选择要播放的文件
2.查询要打开的视频的相关参数
3.根据查询到的相关参数分配和视频文件对应的MIL Buffer
4.当按开始播放时新建一个播放线程,在此线程中按照查询到的帧率参数间隔一定时间for循环读取视频文件一帧并显示,在for循环中必须有一个事件信号来控制是否继续播放
5.当按暂停播放时设置事件信号使for循环不再继续,即暂停播放
6.当按继续播放时设置事件信号使for循环继续,即继续播放
当然这里查询参数可以在新建线程外也可以在新建线程中来做,这里为了方便把2和3的工作放到了新建线程中做,那么新建线程的工作就是传入一个待播放的文件名,用一个事件信号控制播放线程的开始、继续和暂停。
这里常犯的错误是猜测待播放的视频文件参数来分配Buffer,前面已经讲过MIL Buffer的匹配转换问题,如果视频文件每一帧的大小(800*600等)、类型(8+UNSIGNED等)、格式(AVI_DIB等)和分配的Buffer不匹配的话就会在读入的时候做一个转换。如果只是大小不同做截断操作还好,如果是格式和类型不同那么会大大的消耗时间和性能,如果你的电脑上的显卡够好或安装了Matrox板卡,这时候还看不出大差别,如果你的电脑只是个普通的集显的话,那么基本上在播放的过程中你看不到画面,只有等待整个视频播放完了你看到最后一帧,因为在加载过程中CPU都被耗在Buffer转换上了。很多人反映在实验室的工控机上播放正常,在自己的电脑上播放不正常就是这个原理。总的来说,除非你完全确定,就是不要去猜测视频格式,同样的道理如果对Windows设备编程(包括普通的显示器GDI操作和工业设备程序等)理解比较深入的话也会理解,一般编写 面向设备的程序是不会去猜测其参数的,都会采取查询分配的形式。(有机会,我会考虑出Windows编程系列教程)
下面给出代码
选择要打开的文件,新建线程
void CMILPlayDlg::OnPlay() { //选择文件 CFileDialog openFileDlg(TRUE); openFileDlg.m_ofn.lpstrTitle = TEXT("打开待播放的AVI"); openFileDlg.m_ofn.lpstrFilter = TEXT("AVI(*.avi)\0*.avi\0\0"); openFileDlg.m_ofn.lpstrDefExt = TEXT("avi"); if (IDOK == openFileDlg.DoModal()) { params.csFileToPlay = openFileDlg.GetFileName(); params.hwnd = GetDlgItem(ID_SHOWAREA)->GetSafeHwnd(); params.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); _beginthread(ThreadPlay, 0, ¶ms); } }在新建线程中查询要打开的视频的相关参数、根据查询到的相关参数分配和视频文件对应的MIL Buffer、照查询到的帧率参数间隔一定时间for循环读取视频文件一帧并显示
void ThreadPlay(PVOID pvoid) { PPARAMS pparams = (PPARAMS)pvoid; CString filename; HWND hwnd; filename = pparams->csFileToPlay; hwnd = pparams->hwnd; long bufferNums = MbufDiskInquire(filename.GetBuffer(256), M_NUMBER_OF_IMAGES, M_NULL); long frameRate = MbufDiskInquire(filename.GetBuffer(256), M_FRAME_RATE, M_NULL); long xWidth = MbufDiskInquire(filename.GetBuffer(256), M_SIZE_X, M_NULL); long yHeight = MbufDiskInquire(filename.GetBuffer(256), M_SIZE_Y, M_NULL); long bufBand = MbufDiskInquire(filename.GetBuffer(256), M_SIZE_BAND , M_NULL); long bufType = MbufDiskInquire(filename.GetBuffer(256), M_TYPE, M_NULL); //预分配buffer if (M_NULL != MilBufferAvi) { MbufFree(MilBufferAvi); } MbufAllocColor(MilSystem, bufBand, xWidth, yHeight, bufType, M_DISP+M_IMAGE, &MilBufferAvi); MbufClear(MilBufferAvi, 0x0); //这里的ID_SHOWAREA一定不要分配成static text类型,若是static text类型对话框管理器默认会 //重绘它造成冲突这样导致播放出问题,最好是picture框架类型 MdispSelectWindow(MilDisplay, MilBufferAvi, hwnd); MbufImportSequence(filename.GetBuffer(256), M_DEFAULT, M_NULL, M_NULL, M_NULL, M_NULL, M_NULL, M_OPEN); int i; for (i= 0; i < bufferNums; i++) { WaitForSingleObject(pparams->hEvent, INFINITE); MbufImportSequence(filename.GetBuffer(256), M_DEFAULT, M_LOAD, M_NULL, &MilBufferAvi, i, 1, M_READ); Sleep(long(1.0 / frameRate * 1000)); } MbufImportSequence(filename.GetBuffer(256), M_DEFAULT, M_NULL, M_NULL, M_NULL, M_NULL, M_NULL, M_CLOSE); }
这里要说明是用于显示播放视频的子窗口类型一定不能是static text类型,按照MSDN描述“The control's parent window or dialog box must not process the WM_CTLCOLORSTATIC message.”即对话框管理会默认管理static text控件重绘,如果我们读取一帧图像并显示到该子窗口上的话,该子窗口会在对话框管理器的驱使下用原来的背景色重绘该区域,如果你播放视频的时候出现一闪一闪的情况,请检查是否设置子窗口为static text。
设置事件信号使for循环不再继续
void CMILPlayDlg::OnPause() { // TODO: Add your control notification handler code here ResetEvent(params.hEvent); }
设置事件信号使for循环继续
void CMILPlayDlg::OnContinue() { // TODO: Add your control notification handler code here SetEvent(params.hEvent); }
博客中完整源代码下载链接
至此,关于MIL的图像、采集和视频功能都已详细介绍其原理和使用方法,在这里可以下到笔者为实验室写的针对相机在线观测、录制视频和播放视频的一个小软件Grab,由于实验室版权限制,只有可执行文件,如果你也需要类似的软件那么Grab是个不错的选择。
原创,转载请注明来自http://blog.csdn.net/wenzhou1219