MJPG-Streamer简介:
JPEG(简称JPG):
使用RGB、YUV格式编码出来的图片,文件都比较大。常见的BMP文件就是RGB编码。通过将BMP文件转成JPEG文件,大大的减小了文件的大小,但是画面的质量并不会发生什么变化。
MJPG:
JPEG是静态图片的编码格式;MJPG是动态视频的编码格式。
可以简单的理解,MJPG就是把多个JPEG图片连续显示出来。
MJPG的优点:
很多摄像头本身就支持JPEG,MJPG,所以处理器不需要做太多的处理。一般的低性能的处理器就可以传输MJPG视频流。
MJPG的缺点:
他不考虑图片前后的变化,即使变化很小,也会再次上传整张图片。
高级的传输方式:
只上传变化的部分,但是需要对比前后的变化,比较消耗CPU的资源。所以各有自己的优缺点。
2、实际配置:
在buildroot里,配置选择MJPG-streamer,直接编译生成映象文件。
具体操作:
1)、内核的配置:
配置usb驱动,v4l2驱动。
2)、根文件系统的配置:
在Buildroot根目录
make menuconfig
如下图选择MJPG-streamer
执行make
这会在Buildroot的dl/mjpg-streamer目录下自动下载源码,并编译
结果保存在output/images目录下。
3、启动开发板了
在这里,我用的是tftp启动:
启动开发板进入uboot命令模式:
输入:
1)、seyenv bootcam ‘tftp 80800000 zImage;tftp 83000000 xxx.dtb;bootz 80800000 – 83000000; ’
2)、setenv bootargs ‘console=tty1 console=ttymxc0,115200 root=/dev/nfs nfsroot=10.3.78.23:/home/fu/linux/nfs/5buildroot rw ip=10.3.78.33:10.3.78.23:10.3.78.1:255.255.255.0::eth0:off’
3)、saveenv
4)、boot。启动开发板。
4、开发板启动成功,输入命令:
mjpg_streamer -i “/usr/lib/mjpg-streamer/input_uvc.so -d /dev/video0 -f 30 -q 90 -n” -o “/usr/lib/mjpg-streamer/output_http.so -w /usr/share/mjpg-streamer/www”
这里我出现了错误,报错内容:
解决方法:
在/dev下面,只能找到video0,开发板并没有识别出,有新连接进来的摄像头。
是内核中v4l2驱动没有配制好,重新配置即可。
重新编译好内核以后。
再次在开发板输入命令:
mjpg_streamer -i “/usr/lib/mjpg-streamer/input_uvc.so -d /dev/video1 -f 30 -q 90 -n” -o “/usr/lib/mjpg-streamer/output_http.so -w /usr/share/mjpg-streamer/www”
成功!!
成功以后,在网页输入下面命令:
http://10.3.78.33:8080/stream.html
即可看到画面了:
5、目的实现了,现在开始分析他的原理
我通过buildroot得到的MJPG-streamer的源码在:
/home/fu/linux/imx6ull/fs_tool/buildroot-2019.02.6/dl/mjpg-streamer目录下:
首先,我么先看一下MJPG—streamer框架:
再次回顾,我们输入的命令是:
mjpg_streamer -i “/usr/lib/mjpg-streamer/input_uvc.so -d /dev/video1 -f 30 -q 90 -n” -o “/usr/lib/mjpg-streamer/output_http.so -w /usr/share/mjpg-streamer/www”
我们先是使用了-i命令制定输入动态库,用来处理输入的数据(从摄像头读取数据):/usr/lib/mjpg-streamer/input_uvc.so,并指定动态库的参数:-d /dev/video0 -f 30 -q 90 –n。
有用同样的方式,指定输出动态库,用来处理输出数据(将数据输出给浏览器):-o “/usr/lib/mjpg-streamer/output_http.so ,并指定动态库的参数:
-w /usr/share/mjpg-streamer/www”
从框图我们可以看到:
main线程会会调用dlopen()函数打开我们还指定的库文件,并运行里面的init()函数和run()函数。
那么,数据是怎么传给浏览器的呢?
在input_uvc.so这个文件里面,会运行run()函数来创建一个摄像头线程,这个摄像头线程会调用uvcGrab()函数,这个函数会读取摄像头,得到一帧的数据。然后调用函数convert to JPEG(),如果这一帧数据不是JPEG,函数convert to JPEG会将其转换成JPEG格式。转换结束以后,会使用函数copy to global buffer()将数据存放进一个全局的buffer(global buffer)。
同时:
output_http.so文件也会创建一个server线程,这个线程用来等待别人的连接。eg:本文是使用浏览器连接开发板的ip,一旦连接成功以后,server线程会创建一个client线程,这个client就用来从全局buffer(global buffer)里面去除数据,传给浏览器。
总结:这个框架,在没有连接的时候,有三个线程在运行。当有连接的时候,就是有“3+连接数”个线程在工作。
验证:
在命令后加”&”,进入后台模式:
1)、mjpg_streamer -i “/usr/lib/mjpg-streamer/input_uvc.so -d /dev/video1 -f 30 -q 90 -n” -o “/usr/lib/mjpg-streamer/output_http.so -w /usr/share/mjpg-streamer/www” &
2)、输入命令:ps
显示mipg_streamer的进程是152。
3)、cd /proc/152/task 查看这个进程下,对应的线程(线程:tid 进程:PID)
显示有三个线程在工作。
4)、在网页上,输入命令http://10.3.78.33:8080/stream.html连接开发板。
连接成功以后,再次查看进程数:
验证结论正确。