正点原子imx6ull开发板视频监控项目实战系列2: MJPG-Streamer方案的实现及原理分析

  • Post author:
  • Post category:其他




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连接开发板。

连接成功以后,再次查看进程数:

在这里插入图片描述

验证结论正确。



版权声明:本文为cainiaofu原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。