文章目录

#前言
相信很多搞过ARM摄像头的,都会想着怎么把摄像头数据继续传出去,做个远程监控什么的。记得当初学习的时候,不知道什么压缩,就按着自己的方法,把采集到的摄像头数据YUV422转为RGB565,然后再用Qt显示,另外还用TCP把数据传出去。结果可想而知,本来软件转码速度就极慢(VGA,640x480大概需要500ms,2帧。。。),然后当时刚学习,也没想着什么多线程,直接发送,然后接收端,大概5,6秒后才会有数据,就是说5~6秒的延迟,帧数还那么低,然后我就弃了。。。

最近无聊了,又把它们捡起来,发现VGA的数据似乎太大了,软件编码500ms,真的不能忍,换了JPEG硬编码发现也差不多,这就很尴尬了。。然后换成QVGA(320x240)用硬件MFC编码成H264,大概20~30ms完成一帧数据,还是慢。。。不知道是我的Tiny6410太慢了,还是哪里处理得不对。

#正文
mjpg-streamer,就像它的名字一样,jpg格式的流传输工具,用它可以实现对摄像头数据采集到传输的一整套过程。

mjpg-streamer的实现也相对比较简单:
##主函数
在main函数里面首先当然是实现命令输入解析,对一些信号的处理,主要是SIG_PIPE和SIG_INT:

 /* ignore SIGPIPE (send by OS if transmitting to closed TCP sockets) */signal(SIGPIPE, SIG_IGN);/* register signal handler for <CTRL>+C in order to clean up */if (signal(SIGINT, signal_handler) == SIG_ERR) {LOG("could not register signal handler\n");closelog();exit(EXIT_FAILURE);}

忽略掉SIG_PIPE信号,做Linux通信的一般都这么做,否则当出现Pipe broken时,程序会自动退出。
然后就是为SIG_INT添加处理函数。

然后就是加载动态链接库,因为mjpg-streamer的命令行输出就需要输入要使用的输入输出方式,也没想通为啥要运行时加载,反正加载不是重点,也不管了,重点是加载的内容。

##数据采集
通过加载动态链接库后就可以获取到要进行操作的方法,这些方法是init、stop、run和cmd这四种。因为是为了摄像头数据传输,别的我也不管了。首先是输入,文件是input_uvc.c:
在初始化函数中init(),当然是对摄像头参数设置和初始化啦:

/* open video device and prepare data structure */
if (init_videoIn(videoIn, dev, width, height, fps, format, 1) < 0) {IPRINT("init_VideoIn failed\n");closelog();exit(EXIT_FAILURE);
}

设置的参数,有摄像头设备,采集的分辨率,采集速度、格式以及压缩的JPEG的质量:

/* display the parsed values */
IPRINT("Using V4L2 device.: %s\n", dev);
IPRINT("Desired Resolution: %i x %i\n", width, height);
IPRINT("Frames Per Second.: %i\n", fps);
IPRINT("Format............: %s\n", (format == V4L2_PIX_FMT_YUYV) ? "YUV" : "MJPEG");
if ( format == V4L2_PIX_FMT_YUYV )IPRINT("JPEG Quality......: %d\n", gquality);

在摄像头初始化init_videoIn()中,就是Linux V4L2的摄像头设置:

if (init_v4l2 (vd) < 0) {fprintf (stderr, " Init v4L2 failed !! exit fatal \n");goto error;
}

在采集方法中,根据不同的数据格式,分别拷入到不同的buffer中:

switch (vd->formatIn) {case V4L2_PIX_FMT_MJPEG:if (vd->buf.bytesused <= HEADERFRAME1) {    /* Prevent crash* on empty image */fprintf(stderr, "Ignoring empty buffer ...\n");return 0;}memcpy(vd->tmpbuffer, vd->mem[vd->buf.index], vd->buf.bytesused);if (debug)fprintf(stderr, "bytes in used %d \n", vd->buf.bytesused);break;case V4L2_PIX_FMT_YUYV:if (vd->buf.bytesused > vd->framesizeIn)memcpy (vd->framebuffer, vd->mem[vd->buf.index], (size_t) vd->framesizeIn);elsememcpy (vd->framebuffer, vd->mem[vd->buf.index], (size_t) vd->buf.bytesused);break;default:goto err;break;
}

根据采集的数据格式判断是否需要压缩为JPEG:

/*
* If capturing in YUV mode convert to JPEG now.
* This compression requires many CPU cycles, so try to avoid YUV format.
* Getting JPEGs straight from the webcam, is one of the major advantages of
* Linux-UVC compatible devices.
*/
if (videoIn->formatIn == V4L2_PIX_FMT_YUYV) {DBG("compressing frame\n");pglobal->size = compress_yuyv_to_jpeg(videoIn, pglobal->buf, videoIn->framesizeIn, gquality);
} else {DBG("copying frame\n");pglobal->size = memcpy_picture(pglobal->buf, videoIn->tmpbuffer, videoIn->buf.bytesused);
}

其中pglobal->buf为输出的buffer,也就是在后面需要传输的数据。
##数据传输
既然是为了远程视频传输,当然这里输出应该选择output_http.c,即网页传输。输出方法和输入方法一样,主要为init,stop,run和cmd四个函数组成,这里就主要讲输出传输的过程,主要是run函数中,创建了服务器thread:

/* create thread and pass context to thread function */
pthread_create(&(servers[id].threadID), NULL, server_thread, &(servers[id]));
pthread_detach(servers[id].threadID);

其中server_thread()线程回调函数在httpd.c文件中,其中是一个TCP Server的初始化过程,并且设置的最多listen 10个client:

/* start listening on socket */
if ( listen(pcontext->sd, 10) != 0 ) {fprintf(stderr, "listen failed\n");exit(EXIT_FAILURE);
}

为accept的每一个client创建单独的线程,用于处理请求和发送数据:

/* create a child for every client that connects */
while ( !pglobal->stop ) {//int *pfd = (int *)malloc(sizeof(int));cfd *pcfd = malloc(sizeof(cfd));if (pcfd == NULL) {fprintf(stderr, "failed to allocate (a very small amount of) memory\n");exit(EXIT_FAILURE);}DBG("waiting for clients to connect\n");pcfd->fd = accept(pcontext->sd, (struct sockaddr *)&client_addr, &addr_len);pcfd->pc = pcontext;/* start new thread that will handle this TCP connected client */DBG("create thread to handle client that just established a connection\n");syslog(LOG_INFO, "serving client: %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));if( pthread_create(&client, NULL, &client_thread, pcfd) != 0 ) {DBG("could not launch another client thread\n");close(pcfd->fd);free(pcfd);continue;}pthread_detach(client);

在client thread中,根据用户发来的request来选择后面的操作:

/* What does the client want to receive? Read the request. */
memset(buffer, 0, sizeof(buffer));
if ( (cnt = _readline(lcfd.fd, &iobuf, buffer, sizeof(buffer)-1, 5)) == -1 ) {close(lcfd.fd);return NULL;
}/* determine what to deliver */
if ( strstr(buffer, "GET /?action=snapshot") != NULL ) {req.type = A_SNAPSHOT;
} else if ( strstr(buffer, "GET /?action=stream") != NULL ) {req.type = A_STREAM;
}·····/* now it's time to answer */
switch ( req.type ) {case A_SNAPSHOT:DBG("Request for snapshot\n");send_snapshot(lcfd.fd);break;
case A_STREAM:DBG("Request for stream\n");send_stream(lcfd.fd);break;
case A_COMMAND:if ( lcfd.pc->conf.nocommands ) {send_error(lcfd.fd, 501, "this server is configured to not accept commands");break;}command(lcfd.pc->id, lcfd.fd, req.parameter);break;
case A_FILE:if ( lcfd.pc->conf.www_folder == NULL )send_error(lcfd.fd, 501, "no www-folder configured");elsesend_file(lcfd.pc->id, lcfd.fd, req.parameter);break;
default:DBG("unknown request\n");
}

在send_stream中,发送我们已经处理好的JPEG数据流:

memcpy(frame, pglobal->buf, frame_size);
DBG("got frame (size: %d kB)\n", frame_size / 1024);DBG("sending frame\n");
if( write(fd, frame, frame_size) < 0 ) break;

其他的数据发送这里就不详细介绍了,搞过嵌入式网页的都知道,就是直接发送html数据,用于在网页上就可以看到响应的页面。

mjpg-streamer简单解析相关推荐

  1. 插件化框架DL源码的简单解析

    目前行业内已经有较多的插件化实现方案.本文主要对DL(DynamicLoadApk)这一个开源的侵入式插件化方案进行简单分析.因为Service组件插件化的实现逻辑和Activity大体相似,所以在这 ...

  2. java 解析xls 文件_java简单解析xls文件的方法示例【读取和写入】

    本文实例讲述了java简单解析xls文件的方法.分享给大家供大家参考,具体如下: 读取: import java.io.*; import jxl.*; import jxl.write.*; imp ...

  3. [ 转载 ] Java基础10--关于Object类下所有方法的简单解析

    关于Object类下所有方法的简单解析 类Object是类层次结构的根类,是每一个类的父类,所有的对象包括数组,String,Integer等包装类,所以了解Object是很有必要的,话不多说,我们直 ...

  4. java在线打开xml文件_java实现简单解析XML文件功能示例

    本文实例讲述了java实现简单解析XML文件功能.分享给大家供大家参考,具体如下: package demo; import java.io.File; import java.io.IOExcept ...

  5. java:AXIS调用webService接口,返回String类型xml,并用dom4j简单解析xml

    一.使用axis调用webService接口,返回String类型xml 1.导入axis依赖 2.直接贴代码 /*** 调用webservice接口的方法,并返回String类型的xml* @par ...

  6. HTML-HTML协议简单解析

    HTML-HTML协议简单解析 在浏览器访问一个地址: 127.0.0.1:7890/html/html.htm //代表访问当地服务器路径下的/html的html.htm文件 客户端发送的请求命令是 ...

  7. C++生成LNK文件及LNK文件简单解析

    C++生成LNK文件及LNK文件简单解析 话不多说,直接上代码吧. 生成快捷方式代码: int CreateLnk(const wchar_t* TARGET, const wchar_t* LNKF ...

  8. 大数据培训课程数据清洗案例实操-简单解析版

    数据清洗(ETL) 在运行核心业务MapReduce程序之前,往往要先对数据进行清洗,清理掉不符合用户要求的数据.清理的过程往往只需要运行Mapper程序,不需要运行Reduce程序.大数据培训 数据 ...

  9. 邻近算法(KNN)原理简单解析

    邻近算法(KNN)原理简单解析 一.什么是邻近算法 1.1简介 1.2核心思想 1.3 算法流程 1.4 优缺点 二.实例演示KNN算法 一.什么是邻近算法 1.1简介 邻近算法,或者说K最近邻(KN ...

  10. 锐速与BBR的原理简单解析

    锐速与BBR的原理简单解析  4 前言 昨天,有一位朋友在我的文章下留言说,锐速和BBR不都是一样,是拥塞算法嘛.因为这方面需要讲的东西比较多,所以我还是专门水一篇文章吧. 锐速 参考资料: http ...

最新文章

  1. Logback 配置文件这样优化,TPS提高 10 倍
  2. 不用图片的DIV圆角(兼容各浏览器)
  3. python 解析url上的xml_如何从python中的URL读取XML文件?
  4. 推荐搜索炼丹笔记:向量召回 MIND多兴趣双塔模型
  5. 卡斯特罗的离去对古巴科技产业的未来有何影响?
  6. js正则表达exec和match的区别(转)
  7. MongoDB一 之增删改查
  8. Halcon 和 C# 联合编程 - 如何使用开源项目 ViewROI
  9. eclipse中安装flex插件
  10. css3直线运动_纯css3实现曲线运动——贝塞尔曲线(cubic-bezier)
  11. 2021-01-26数据治理具备哪些优势
  12. python教程-4.数据处理numpy-pandas
  13. DevOps运维开发一体化
  14. 统计github本地仓库的代码行数
  15. 计算机第二章测试题及答案,计算机组成原理第二章练习题及答案
  16. linux h5cc 编译,雷霆传奇H5源码编译+Linux+Release+Docker_2021/02/04
  17. 泛型编程 与 STL
  18. 转专业2017武汉大学计算机学,武大,10届考生谈谈转专业~`~
  19. 17track包裹单个物流轨迹抓取(一)
  20. 怎样建立产品体系?(六)- 主流产品开发流程

热门文章

  1. Tone Mapping算法系列一:基于Fast Bilateral Filtering 算法的 High-Dynamic Range(HDR) 图像显示技术
  2. 《python基础教程》答案(第六章)
  3. c++ 读取内存数据 基址_内存管理(仅学习)
  4. Android mk方式使用动态库和静态库
  5. 关于戴尔更新AWCC缓慢,无法更新的解决办法
  6. 烈火霸业--ios技术支持
  7. 『C/C++养成计划』C++中的双冒号::名解析(Scope Resolution Operator)
  8. 【学习记录】激光雷达与相机标定
  9. Android OkHttp使用和源码详解
  10. GitLab服务器IP地址设置