37、mipg-streamer的使用讲解

讲解mjpg-streamer

其功能:

1、控制摄像头采集数据(通过ioctl采集数据,所有不支持CMOS,CMOS之前写驱动的时候是通过read,所有需要修改mjpg-streamer的源码或者CMOS驱动);

2、把采集的数据传输到路由器上或者无线网卡上;

我们的JZ2440可以接一个usb-hub,上面接上无线网卡和摄像头,可以一起使用

1. 如何将mjpg-streamer移植到开发板上

文件系统:fs_mini_mdev_new_auto_wifi_ap.tar.bz2

(1) libjpeg 的移植
tar xzvf libjpeg-turbo-1.2.1.tar.gz
cd libjpeg-turbo-1.2.1
mkdir tmp
./configure --prefix=/work/jz2440/libjpeg-turbo-1.2.1/tmp --host=arm-linux
make
make install
cp /work/jz2440/libjpeg-turbo-1.2.1/tmp/lib/*so* /work/nfs_root/fs_mini_mdev_new/lib/ -d

(2) mjpg-streamer 的移植:
tar xvf mjpg-streamer-r63.tar.gz
cd mjpg-streamer-r63

修改所有的Makefile
--(1) 将 CC=gcc 修改为 CC=arm-linux-gcc
--(2) 修改plugins/input_uvc/Makfile
a. 将
CFLAGS += -O2 -DLINUX -D_GNU_SOURCE -Wall -shared -fPIC
改为
CFLAGS += -O2 -DLINUX -D_GNU_SOURCE -Wall -shared -fPIC -I /work/jz2440/libjpeg-turbo-1.2.1/tmp/include
注意:
-I /work/jz2440/libjpeg-turbo-1.2.1/tmp/include // 是编译libjpeg 生成的文件

b. 将
input_uvc.so: $(OTHER_HEADERS) input_uvc.c v4l2uvc.lo jpeg_utils.lo dynctrl.lo
      $(CC) $(CFLAGS) -ljpeg -o $@ input_uvc.c v4l2uvc.lo
jpeg_utils.lo dynctrl.lo
改为
input_uvc.so: $(OTHER_HEADERS) input_uvc.c v4l2uvc.lo jpeg_utils.lo dynctrl.lo
        $(CC) $(CFLAGS) -ljpeg -L /work/jz2440/libjpeg-turbo-1.2.1/tmp/lib -o
          $@ input_uvc.c v4l2uvc.lo jpeg_utils.lo dynctrl.lo

make
cp mjpg_streamer /work/nfs_root/fs_mini_mdev_new/bin/
cp *so* /work/nfs_root/fs_mini_mdev_new/lib/ -d

 运行指令:

二合一摄像头(-i表示输入渠道,-o表示输出渠道)
mjpg_streamer -i "input_uvc.so -f 10 -r 320*240" -o "output_http.so -w www"

一般摄像头
mjpg_streamer -i "input_uvc.so -f 10 -r 320*240 -y" -o "output_http.so -w www"(-y表示压缩图片成jpeg格式,方便传输)

2.分析源码

摄像头数据地址buf、源地址input 、目的地址output,都保存在Mipg_streamer.h这个头文件中的_globals结构体中

编译mjpg-streamer生成的so库中input开头的表示各个输入渠道,out开头的用于表示各个输出渠道

通过-i和-o指定输入和输出渠道后,每个input和output表示一个结构体:这个结构体有init函数、open函数、cmd函数、stop函数

举例:当执行mjpg_streamer -i "input_uvc.so -f 10 -r 320*240" -o "output_http.so -w www"指令时,对应的输入源代码参考input._uvc.c文件,里面有各个函数:

input_init

  init_videoIn(videoIn,dev,width,height,fps,format)//设置图像长、宽、帧率、格式

    设置vdIn结构体

    init_v4l2//里面就是v4l2应用程序的框架

      open()//打开摄像头的设备节点,根据传入的参数或者默认参数决定

      ioctl(VIDIOC_QUERYCAP)//查看打开的设备的属性,下面会判断是否是视频捕获设备

      ioctl(VIDIOC_S_FMT)//设置摄像头的输出格式(分辨率、输出格式)

      ioctl(VIDIOC_S_PARM)//设置摄像头的参数,比如输出帧率

      ioctl(VIDIOC_REQBUFS)//申请缓存

      for(缓存个数)  

        ioctl(VIDIOC_QUERYBUF)//获取内核空间的视频缓冲区的信息

        mmap()//根据获得的信息做映射操作

      for(缓存个数)

        ioctl(VIDIOC_QBUF)//将一个空的缓冲区放入视频缓冲区队列

    分配临时缓冲区(vd->framebuffer)用来接收摄像头数据,MJPEG需要分配两个,见代码

   if(dynctrls)//动态控制摄像头功能初始化

    initDynCtrls 

  input_cmd()//通过命令行动态控制摄像头参数,比如高级摄像头的焦距控制

input_run

  分配一段buf用来存储视频数据(仓库)

  pthread_create(cam_thread)//创建线程,线程函数是cam_thread

    cam_thread

      while(!pglobal->stop)//stop会在按下CTRL+C的时候会被置1

        uvcGrab//获得一帧数据

          video_enable//使能视频捕获设备

            ioctl(VIDIOC_STREANMON)//将一个空的缓冲区放入视频缓冲区队列 

          ioctl(VIDIOC_DQBUF)//从视频缓冲区队列取出一个已经存有一帧视频数据的缓冲区buf

          根据获得的信息处理数据及根据视频格式把数据复制到init阶段创建的vd->framebuffer中去,MJPEG格式稍微有点不同,其是放到tempbuffer中

           ioctl(VIDIOC_QBUF)//把这个已获得数据的空的缓冲区放入视频缓冲区队列

          

        判断数据的格式是YUV或者jpeg,如果是jpeg这直接放入globl里面的buf(仓库),否则将YUV转换为jpeg后在保存在buf

         pthread_cond_broadcase(&pglobal->db_update)//通知输出通道可以去数据发出了

对于output_http.so,就是使用sock编程还模拟http协议,查看源码,其中init函数做socket的初始化,run函数从buf中取出数据,然后发送数据

(见output_http.c)

output_init()

  仅解析指令参数,然后给相应的变量赋值

output_run()

  pthread_create(server_thread)//创建线程,线程函数是server_thread

    server_thread

      socket(PF_INET,SOCK_STREAM,0)//创建一个socket(其是作为服务器)    

      setsockopt()//当端口被其他进程使用时,通过这个函数里面指定的参数可以设置重复使用同一个IP和端口号

      bind()//绑定端口和IP地址

      listen()//启动检测数据,第二个参数表示最多客户端数量

      while((!pglobal->stop))//同input_run里面的分析

        分配一个cfd结构体

        accept()//休眠等待客户端连接,并建立连接

        pthread_create(client_thread)//创建线程,线程函数是client_thread

             client_thread//参数是上面设置的cfd结构体

            init_iobuffer(&iobuf)//初始化一个iobuf,在后面仅是一个临时缓存

            init_request(&req)//http协议,需要客户端给服务器发送一个请求,而request就是用来保存请求的

            _readline()//从客户端接受一些数据,用来表示客户端发来的请求,才知道给客户发什么数据

                  这个函数里面调用select函数,当客户端发来数据或者超时的时候返回去调用read函

                  数从客户端获得数据并保存在iobuf中,接着从iobuf中将数据拷贝到buffer中(这个过程

                  并不是简单的复制,可以查看代码分析)

                  因此客户端必须发送一个请求字符串,以换行符作为结束,比如当使用浏览器输入URL

                  的时候http://192.168.1.17:8080/?action=stream(action=stream这个就表示请求)

            从buffer中提取请求并保存在request中

··           do

             _readline() 再次读取一次客户端发送过来的字符串

             处理buffer中的数据并保存,比如如果有用户名,则保存到req.client;有密码保存到req.credential

            如果服务器有账户和密码,则比较客户端传入的账户密码是否匹配

            根据第一次客户端发来的请求做不同动作

            case A_SNAPSHOT(拍照请求)

              send_snapshot(发送照片)

                pthread_cond_wait(等待input通道通知有数据可取了)

                分配一帧缓冲区,并从global->buf(仓库)中取出数据保存

                构造一个http数据头在buffer数据,并将这个数据发送出去(这个数据头表示请求)

                如果上一步成功,就把一帧图片发送出去

            case A_STREAM

              send_stream 

                构造一个http数据头在buffer数据,并将这个数据发送出去(这个数据头表示请求)

                while(!pglobal->stop)

                  pthread_cond_wait(等待input通道通知有数据可取了)

                  如果帧framer空间不够就分配空间,并从global->buf(仓库)中取出数据保存

                  构造一个http数据头(报文)在buffer数据,并将这个数据发送出去(这个数据头表示请求,里面有下面发送帧图片数据大小)

                  如果上一步成功,就把一帧图片发送出去

                  接着发送一个“boundarydonotcross”字符串,表示一帧数据已接受完

                

input_init();
output_init();
input_run();
output_run();


3.自己写客服端(在“在LCD上显示摄像头”的7th_pc上修改程序)

(1).发送一个请求字符串
"GET /?action=snapshot "
"GET /?action=stream "
"GET /?action=command "

(2).再发送一次字符串
如果我们不使用登录密码功能!则只需发送任意长度为小于2字节的字符串,就会跳过处理账户和密码的代码比如:
"f "

如果发送的请求是:"GET /?action=snapshot "

(3).需要接收一次字符串(是服务器发过来的报文)

(4).接收一帧图片

如果发送的请求是:"GET /?action=stream "

(3).需要接收一次字符串(是服务器发过来的报文)

while(1)
{
(4).再接收一次报文,解析它,得到一帧图片的大小(size)

(5).接收size个字节的数据
}

1、在ubuntu上连接2440上无线网卡的wifi热点

2、按住ctrl+Alt+F1进入文本模式,输入用户名和密码

3、运行客户端程序( ./应用程序 开发板IP地址)

4、ctrl+Alt+F4

启动内核:
set ipaddr 192.168.7.122 && set serverip 192.168.7.111 && tftp 0x30000000 uImage
set bootargs root=/dev/nfs nfsroot=192.168.7.202:/work/nfs_root/fs_mini_mdev_new ip=192.168.7.17 console=ttySAC0,115200 && bootm 0x30000000

原文地址:https://www.cnblogs.com/liusiluandzhangkun/p/8971522.html