环境: ubuntu 13.04 (虚拟机)
SecureCRT (串口控制端)
tftpd32.exe (PC与开发板传输数据)
Cubieboard A10 (Cortex-A8开发板 )
ZC0301 (中星微 摄像头)
本软件实现的功能是在PC机上的web浏览器(例如谷歌浏览器)上动态显示由运行在开发板(Cubieboard A10)上的摄像头(中星微 ZC0301 )所捕捉到的画面。
一、准备工作:
首先要移植boa,具体步骤:http://www.cnblogs.com/yinsua/p/3251854.html
既然用到了boa,那么要想在这上面编程,肯定也用到了CGI(通用网关接口),因此涉及到了用什么语言来进行CGI编程呢,通常会有两种方法(任何编程语言皆可,只要符合输入输出):C语言和Perl语言。而本人选用的是C语言。而要想方便编程,本文使用了一个库:CGIC库,他是一个用C来编写CGI程序的一个标准C库,用起来方便省事。接下来的工作就是移植CGIC库:http://blog.chinaunix.net/uid-28811392-id-3666499.html
要在浏览器上面显示,就必须要把图片数据实时的传送到PC机上,因此需要协议的支持,本文使用的是HTTP1.0协议(移植的boa服务器支持的是HTTP1.0协议)。可以适当的了解一下HTTP协议。http://blog.csdn.net/gueter/article/details/1524447
二、编写视频软件:
(图片来源:http://blog.chinaunix.net/uid-26833883-id-3528832.html 借用一下~)
首先你编写一个这样一个web界面:上面有一个按钮,按下则会进入视频显示页面。
因此,我会这样编写:
1 cgiHeaderContentType("text/html"); 2 3 fprintf(cgiOut, "<HTML><HEAD> "); 4 fprintf(cgiOut, "<TITLE>cgic test</TITLE></HEAD> "); 5 fprintf(cgiOut, "<p> "); 6 fprintf(cgiOut, "<BODY><center><H1>cgic test</H1> "); 7 /* If a submit button has already been clicked, act on the 8 submission of the form. */ 9 if (cgiFormSubmitClicked("streamer") == cgiFormSuccess) 10 { 11 init(); 12 fprintf(cgiOut, "<hr> "); 13 } 14 15 fprintf(cgiOut, "<form method="POST" enctype="multipart/form-data" "); 16 fprintf(cgiOut, " action=""); 17 cgiValueEscape(cgiScriptName); 18 fprintf(cgiOut, ""> "); 19 fprintf(cgiOut, "<p> "); 20 fprintf(cgiOut, "<input type="submit" name="streamer" value="Get streamer"> "); 21 fprintf(cgiOut, "</form> "); 22 /* Finish up the page */ 23 fprintf(cgiOut, "</center></BODY></HTML> ");
这里面包含的这个界面的实现和按下按键之后应该跳到哪个函数去执行。
OK,那么按下按键之后你应该干嘛呢?
1.把SIGINT信号指向你所需要的自定义的函数,去做当你按下<Ctrl>+C之后清除申请的资源的释放和关闭已经打开的文件等这种类似清道夫的工作。
2.初始化互斥锁和条件变量。为什么要用这两个东西呢,因为要想显示更流畅的画面,就需要使抓取画面和显示画面更高效率的进行,让他们同时进行就是一个不错的方法,所以要给抓取和显示各创建一个线程(类似于生产者和消费者)。但是如果你简单的写一个拥有两个线程的一个进程的程序你会发现有时候一个线程会长时间的占用CPU的资源导致另一个线程不能及时的运行,因此会出现问题。互斥锁结合条件变量则可以较好的解决这种局面。
3.初始化摄像头。这一步肯定是必不可少的,你所需要的设备,在你不知道他已经设置过怎么样的参数或其它的情况下,当然要初始化一遍。然后使能摄像头的视频流输出。
4.创建两个线程,一个是抓取,一个是显示。
5.等待信号(按下<Ctrl>+C产生SIGINT信号)。
6.结束程序。
我们所有的代码都是在cgiMain()这个处于CGIC库的函数里面开始的。
需要注意的是,当你进入cgic test这个界面,按下Get streamer按钮之前,这是一个进程,你按下之后又是一个进程,这说明了你所要注册或者后续所要创建的东西都应该在按下按钮之后的函数里面去实现,而不是显示这个cgic test 界面的时候就全部鼓捣出来了,这是不可取的,可能会发生不可预见的错误。。
在cgiMain()这个函数里面是不能用pause()(等待信号)这个函数的,具体原因不清楚。不过你可以让他跳转到一个函数里面去再执行pause()。
三、一些细节:
1.好,假设我们已经得到了一帧,然后把它放到全局buf里面去了,那么该怎么办,基于HTTP协议传送到浏览器上面就完事了?然后刷新一遍再传送下一帧?显然这种方法的最终结果是效率比较低的,而且还要估计一些HTTP的格式问题等等。因此,本文参照MJPG-streamer视频软件使用了:应用网络协议---MJPEG协议 这样就提高了运行效率。
2.不过实现这个协议要把一个Header放到HTTP协议的前面去声明,不然的话是无效的。(当时因为实现这个烦了好几天。。)
3.CGIC库中的cgiOut是一个指向web服务器的文件,类型是FILE * 而不是通常的fd(文件描述符)。所以不能用fd的方法操作他。
4.抓取图片的时候用到了复用IO,把摄像头的fd放到一个fd集里面去,然后监测这个集里面的动态来决定是否进行抓取。换种说法就是每次抓取之前要看看摄像头有没有反应,在指定的时间内没反应的话就退出程序,有反应则开始抓取图片。
5.记得给你的cgi程序在开发板上给予权限777
6.要想监控web浏览器的输入输出你可以使用软件Fiddler,这样可以更好的调试你的程序。
四、一些未实现的和一些BUG:
1.在火狐浏览器下会一直提示要下载文件,但是可以正常显示。在谷歌浏览器下则只能运行2s就不再显示(程序还在运行,我推测应该是谷歌浏览器的一些特性屏蔽了一直提示下载文件这个消息,所以导致这样,本文网页不好,所以解决不了。。。)。
2.程序正常运行一段时间后会因为检测到摄像头没有反应而正常退出。
3.SIGINT信号不能检测到,我重新写了一个比较小的测试程序在开发板上运行则可以检测到,因此推测应该是因为在CGIC库环境下所导致的问题。
能力不够,所以有一些BUG。
2013-08-30 13:59:26
更正:
可以正常显示,流畅度一般。BUG:CTRL+C 不能使用。
2013-09-04 16:59:30