Chinaunix首页 | 论坛 | 博客
  • 博客访问: 59815
  • 博文数量: 25
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 101
  • 用 户 组: 普通用户
  • 注册时间: 2014-06-07 19:38
文章分类

全部博文(25)

文章存档

2014年(25)

我的朋友

分类: 嵌入式

2014-06-10 00:24:54

refer to


source/checkout

下载mjpg_streamer源码并观察源码结构如下
[root@localhost tmp]# svn checkout mjpg-streamer-mini2440-read-only
[root@localhost tmp]# cd mjpg-streamer-mini2440-read-only/
[root@localhost mjpg-streamer-mini2440-read-only]# pwd
/tmp/mjpg-streamer-mini2440-read-only
[root@localhost mjpg-streamer-mini2440-read-only]# tree
.

|-- CHANGELOG
|-- LICENSE
|-- Makefile
|-- README

|-- mjpg-streamer-mini2440.kdev4
|-- mjpg_streamer.c
|-- mjpg_streamer.h

|-- plugins
|   |-- input.h

|   |-- input_control
|   |   |-- Makefile
|   |   |-- dynctrl.c
|   |   |-- dynctrl.h
|   |   |-- input_uvc.c
|   |   |-- uvc_compat.h
|   |   `-- uvcvideo.h

|   |-- input_file
|   |   |-- Makefile
|   |   `-- input_file.c

|   |-- input_gspcav1
|   |   |-- Makefile
|   |   |-- encoder.c
|   |   |-- encoder.h
|   |   |-- huffman.c
|   |   |-- huffman.h
|   |   |-- input_gspcav1.c
|   |   |-- jconfig.h
|   |   |-- jdatatype.h
|   |   |-- marker.c
|   |   |-- marker.h
|   |   |-- quant.c
|   |   |-- quant.h
|   |   |-- readme.spcacat
|   |   |-- spcaframe.h
|   |   |-- spcav4l.c
|   |   |-- spcav4l.h
|   |   |-- utils.c
|   |   `-- utils.h

|   |-- input_s3c2410
|   |   |-- Makefile
|   |   |-- input_s3c2410.c
|   |   |-- readme.s3c2410
|   |   |-- s3c2410.c
|   |   |-- s3c2410.h
|   |   |-- utils.c
|   |   `-- utils.h

|   |-- input_testpicture
|   |   |-- Makefile
|   |   |-- input_testpicture.c
|   |   |-- pictures
|   |   |   |-- 160x120_1.jpg
|   |   |   |-- 160x120_2.jpg
|   |   |   |-- 320x240_1.jpg
|   |   |   |-- 320x240_2.jpg
|   |   |   |-- 640x480_1.jpg
|   |   |   |-- 640x480_2.jpg
|   |   |   |-- 960x720_1.jpg
|   |   |   `-- 960x720_2.jpg
|   |   `-- testpictures.h

|   |-- input_uvc
|   |   |-- Makefile
|   |   |-- dynctrl.c
|   |   |-- dynctrl.h
|   |   |-- huffman.h
|   |   |-- input_uvc.c
|   |   |-- jpeg_utils.c
|   |   |-- jpeg_utils.h
|   |   |-- uvc_compat.h
|   |   |-- uvcvideo.h
|   |   |-- v4l2uvc.c
|   |   `-- v4l2uvc.h

|   |-- output.h

|   |-- output_autofocus
|   |   |-- Makefile
|   |   |-- output_autofocus.c
|   |   |-- processJPEG_onlyCenter.c
|   |   `-- processJPEG_onlyCenter.h

|   |-- output_file
|   |   |-- Makefile
|   |   `-- output_file.c

|   `-- output_http
|       |-- Makefile
|       |-- httpd.c
|       |-- httpd.h
|       `-- output_http.c

|-- simplified_jpeg_encoder.c
|-- simplified_jpeg_encoder.h

|-- start_s3c2410.sh
|-- start_uvc.sh
|-- start_uvc_yuv.sh

|-- utils.c
|-- utils.h

`-- www
   |-- LICENSE.txt
   |-- bodybg.gif
   |-- cambozola.jar
   |-- control.htm
   |-- example.jpg
   |-- favicon.ico
   |-- favicon.png
   |-- fix.css
   |-- functions.js
   |-- index.html
   |-- java.html
   |-- java_control.html
   |-- java_simple.html
   |-- javascript.html
   |-- javascript_motiondetection.html
   |-- javascript_simple.html
   |-- sidebarbg.gif
   |-- static.html
   |-- static_simple.html
   |-- stream.html
   |-- stream_simple.html
   |-- style.css
   `-- videolan.html

12 directories, 104 files
[root@localhost mjpg-streamer-mini2440-read-only]#

其中
主目录下
|-- start_s3c2410.sh
|-- start_uvc.sh
|-- start_uvc_yuv.sh
这几个脚本用于执行mjpg_streamer并传递参数给main

|-- simplified_jpeg_encoder.c
|-- simplified_jpeg_encoder.h
用于对input模块从驱动中取得图像的数据进行编码

mjpg_streamer.h,定义了一个重要的全局结构体globals
mjpg_streamer.c,main函数所在文件,此文件分析启动命令并初始化,并定义静态全局变量 static globals global;

plugins/目录下是一些插件,以so文件形式提供,在装载时动态链接。其中
plugins/input_file
plugins/input_gspcav1
plugins/input_s3c2410目录,读取cmos摄像头驱动中的数据,保存到全局变量global
plugins/input_testpicture
plugins/input_uvc目录,读取usb摄像头驱动中的数据,保存到全局变量global

plugins/output_autofocus
plugins/output_file目录,用于将图像数据保存到文件
plugins/output_http目录,用于监视外部端口(默认8080)的socket请求,将图像数据通过socket发出。即是一个小型web服务器,支持同时发起的xx个链接。

www目录下是几个网页

**************************************************************************************************
对源码试着编译一下---在板子上先插入cmos摄像头,型号0v9650

先修改mjpg_streamer.h,添加DEBUG宏,以便出错时可以知道错在哪里
#define DEBUG
再修改Makefile,指定编译器,并指定生成input_s3c2410.so插件
CC = arm-linux-gcc
PLUGINS += input_s3c2410.so
执行
make
make package
此时会生成mjpg-streamer-mini2440-bin.tar.gz,考到板子上解压后进入它的目录

执行
./start_s3c2410.sh
结果如下
MJPG Streamer Version.: 2.0
 DBG(input_s3c2410.c, input_init(), 95): argv[0]=S3C2410 embedded camera
 DBG(input_s3c2410.c, input_init(), 169): initializing s3c2410 device
 i: Using V4L2 device.: /dev/video0
 i: Desired Resolution: 640 x 512
 i: Grayscale mode: off
 DBG(s3c2410.c, init_s3c2410(), 63): Opening device
ERROR opening V4L interface

即打开video0时出现问题,修改start_s3c2410.sh ,在-i参数里指定设备文件为/dev/camera,如下
export LD_LIBRARY_PATH="$(pwd)"                                                
./mjpg_streamer -o "output_http.so -w ./www" -i "input_s3c2410.so -d /dev/camera"

再次执行./start_s3c2410.sh
结果如下
MJPG Streamer Version.: 2.0
 DBG(input_s3c2410.c, input_init(), 95): argv[0]=S3C2410 embedded camera
 DBG(input_s3c2410.c, input_init(), 95): argv[1]=-d
 DBG(input_s3c2410.c, input_init(), 95): argv[2]=/dev/camera
 DBG(input_s3c2410.c, input_init(), 124): case d
 DBG(input_s3c2410.c, input_init(), 169): initializing s3c2410 device
 i: Using V4L2 device.: /dev/camera
 i: Desired Resolution: 640 x 512
 i: Grayscale mode: off
 DBG(s3c2410.c, init_s3c2410(), 63): Opening device
Error opening device /dev/camera: unable to query device.
 i: init_s3c2410 failed

即初始化时出现问题,不能查询设备。
根据提示找到源码s3c2410.c line 63附近
 DBG("Opening device\n");//line 63
 if ((vd->fd = open( vd->videodevice, O_RDWR)) == -1)
   exit_fatal ("ERROR opening V4L interface");

 memset(&vd->cap, 0, sizeof(struct v4l2_capability));
 err = ioctl(vd->fd, VIDIOC_QUERYCAP, &vd->cap);
 if (err < 0) {
   fprintf(stderr, "Error opening device %s: unable to query device.\n", vd->videodevice);
   return err;
 }

可知unable to query device.是由于设备不支持ioctl方法而导致的。
很奇怪哦,怎么友善给的编译好的mjpg-streamer-mini2440-bin-r6.tar.gz放在miroc2440上就可以直接使用,而下载的源码再编译就不可以了呢。。。
发现友善的编译好的是r6版本的,而用svn checkout mjpg-streamer-mini2440-read-only下载到的是最新版本,而在最新版本中加入了ioctl方法对摄像头操作。所以要下一个r6版的mjpg-streamer。不必全下,只把r6版的s3c2410.c下载下来替换掉最新版里的s3c2410.c即可。
source/browse/trunk/plugins/input_s3c2410/s3c2410.c?r=6

再次按照上面所讲编译后下到板子上,执行start_s3c2410.sh,success!如下
[root@FriendlyARM bin6]# ./start_s3c2410.sh
MJPG Streamer Version.: 2.0
DBG(input_s3c2410.c, input_init(), 95): argv[0]=S3C2410 embedded camera
DBG(input_s3c2410.c, input_init(), 95): argv[1]=-d
DBG(input_s3c2410.c, input_init(), 95): argv[2]=/dev/camera
DBG(input_s3c2410.c, input_init(), 124): case d
DBG(input_s3c2410.c, input_init(), 169): initializing s3c2410 device
i: Using V4L2 device.: /dev/camera
i: Desired Resolution: 640 x 512
i: Grayscale mode: off
DBG(s3c2410.c, init_s3c2410(), 45): Opening device
DBG(s3c2410.c, init_s3c2410(), 50): Allocating input buffers
Allocated
DBG(output_http.c, output_init(), 85): output #00
DBG(output_http.c, output_init(), 118): argv[0]=HTTP output plugin
DBG(output_http.c, output_init(), 118): argv[1]=-w
DBG(output_http.c, output_init(), 118): argv[2]=./www
DBG(output_http.c, output_init(), 176): case 6,7
o: www-folder-path...: ./www/
o: HTTP TCP port.....: 8080
o: username:password.: disabled
o: commands..........: enabled
DBG(mjpg_streamer.c, main(), 374): starting input plugin
……

我稍微修改过的mjpg-streamer源码

**************************************************************************************************先看几个结构体

typedef struct _globals globals;
struct _globals {
 int stop;   //控制读写线程的读写动作进行与否

 pthread_mutex_t db;  //互斥量,用于读和写线程的同步
 pthread_cond_t  db_update;


 unsigned char *buf;  //全局数据区域,图像数据
 int size;   //图像数据大小


 input in;   //输入模块

 output out[MAX_OUTPUT_PLUGINS];//输出模块  
#define MAX_OUTPUT_PLUGINS 10,可知最多能同时将图像数据输出到10个地方,比如到file,到http
 int outcnt;   //输出数目,即记录命令参数中有几个 -o
};



typedef struct _input input;
struct _input {
 char *plugin;   //so文件名,比如-i "input_s3c2410.so -d  /dev/camera -r 320x240",则plugin="input_s3c2410.so"
 void *handle;   //so句柄
 input_parameter param; //参数
 int (*init)(input_parameter *);//初始化函数指针
 int (*stop)(void);   //打开函数指针
 int (*run)(void);   //执行函数指针
 int (*cmd)(in_cmd_type, int);  //命令函数指针
};


typedef struct _input_parameter input_parameter;
struct _input_parameter {
 char *parameter_string; //比如-i "input_s3c2410.so -d  /dev/camera -r 320x240",则parameter_string="-d  /dev/camera -r 320x240"
 struct _globals *global; //回指全局结构体
};



typedef struct _output output;
struct _output {
 char *plugin;    //比如 -o "output_http.so -w ./www",则plugin="output_http.so"
 void *handle;    //so句柄
 output_parameter param;        //参数
 int (*init)(output_parameter *);
 int (*stop)(int);
 int (*run)(int);
 int (*cmd)(int, out_cmd_type, int);
};

typedef struct _output_parameter output_parameter;
struct _output_parameter {
 int id;    //记录这是第几个输出(-o)
 char *parameter_string;               //比如 -o "output_http.so -w ./www",则parameter_string="-w ./www"
 struct _globals *global;              //回指全局结构体
};

//mjpg-streamer.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "utils.h"
#include "mjpg_streamer.h"

static globals global;

void help(char *progname)
{
 fprintf(stderr, "-----------------------------------------------------------------------\n");
 fprintf(stderr, "Usage: %s\n" \
                 "  -i | --input \" [parameters]\"\n" \
                 "  -o | --output \" [parameters]\"\n" \
                 " [-h | --help ]........: display this help\n" \
                 " [-v | --version ].....: display version information\n" \
                 " [-b | --background]...: fork to the background, daemon mode\n", progname);
 fprintf(stderr, "-----------------------------------------------------------------------\n");
 fprintf(stderr, "Example #1:\n" \
                 " To open an UVC webcam \"/dev/video1\" and stream it via HTTP:\n" \
                 "  %s -i \"input_uvc.so -d /dev/video1\" -o \"output_http.so\"\n", progname);
 fprintf(stderr, "-----------------------------------------------------------------------\n");
 fprintf(stderr, "Example #2:\n" \
                 " To open an UVC webcam and stream via HTTP port 8090:\n" \
                 "  %s -i \"input_uvc.so\" -o \"output_http.so -p 8090\"\n", progname);
 fprintf(stderr, "-----------------------------------------------------------------------\n");
 fprintf(stderr, "Example #3:\n" \
                 " To get help for a certain input plugin:\n" \
                 "  %s -i \"input_uvc.so --help\"\n", progname);
 fprintf(stderr, "-----------------------------------------------------------------------\n");
 fprintf(stderr, "In case the modules (=plugins) can not be found:\n" \
                 " * Set the default search path for the modules with:\n" \
                 "   export LD_LIBRARY_PATH=/path/to/plugins,\n" \
                 " * or put the plugins into the \"/lib/\" or \"/usr/lib\" folder,\n" \
                 " * or instead of just providing the plugin file name, use a complete\n" \
                 "   path and filename:\n" \
                 "   %s -i \"/path/to/modules/input_uvc.so\"\n", progname);
 fprintf(stderr, "-----------------------------------------------------------------------\n");
}


void signal_handler(int sig)
{
 int i;

 LOG("setting signal to stop\n");
 global.stop = 1;
 usleep(1000*1000);

 LOG("force cancelation of threads and cleanup ressources\n");
 global.in.stop();
 for(i=0; i    global.out[i].stop(global.out[i].param.id);
 }
 usleep(1000*1000);

 dlclose(&global.in.handle);
 for(i=0; i  
   dlclose(global.out[i].handle);
 }
 DBG("all plugin handles closed\n");
 pthread_cond_destroy(&global.db_update);
 pthread_mutex_destroy(&global.db);
 LOG("done\n");
 closelog();
 exit(0);
 return;
}


int control(int command, char *details) {
 switch(command) {
   case CONTROL_CMD_RECONFIGURE_INPUT:
     printf("will reload input plugin: %s\n", details);
     break;
   default:
     LOG("unknown control command received\n");
 }
 return 0;
}


int main(int argc, char *argv[])
{
//默认参数;
 char *input  = "input_uvc.so --resolution 640x480 --fps 5 --device /dev/video0";
 char *output[MAX_OUTPUT_PLUGINS];
 int daemon=0, i;
 size_t tmp=0;
 output[0] = "output_http.so --port 8080";
 global.outcnt = 0;
 global.control = control;


 while(1) {
   int option_index = 0, c=0;
   static struct option long_options[] = \
   {
     {"h", no_argument, 0, 0},
     {"help", no_argument, 0, 0},
     {"i", required_argument, 0, 0},
     {"input", required_argument, 0, 0},
     {"o", required_argument, 0, 0},
     {"output", required_argument, 0, 0},
     {"v", no_argument, 0, 0},
     {"version", no_argument, 0, 0},
     {"b", no_argument, 0, 0},
     {"background", no_argument, 0, 0},
     {0, 0, 0, 0}
   };
   c = getopt_long_only(argc, argv, "", long_options, &option_index);
 
   if (c == -1) break;
 
   if(c=='?'){ help(argv[0]); return 0; }
   switch (option_index) {
   
     case 0:
     case 1:
       help(argv[0]);
       return 0;
       break;
   
     case 2:
     case 3:
       input = strdup(optarg);
       break;
   
     case 4:
     case 5:
       output[global.outcnt++] = strdup(optarg);//每遇到一个-o选项outcnt就+1,,所以outcnt代表输出的个数
       break;
   
     case 6:
     case 7:
       printf("MJPG Streamer Version: %s\n" \
              "Compilation Date.....: %s\n" \
              "Compilation Time.....: %s\n", SOURCE_VERSION, __DATE__, __TIME__);
       return 0;
       break;
   
     case 8:
     case 9:
       daemon=1;
       break;
     default:
       help(argv[0]);
       return 0;
   }
 }

 openlog("MJPG-streamer ", LOG_PID|LOG_CONS, LOG_USER);
 //openlog("MJPG-streamer ", LOG_PID|LOG_CONS|LOG_PERROR, LOG_USER);
 syslog(LOG_INFO, "starting application");

 if ( daemon ) {
   LOG("enabling daemon mode");
   daemon_mode();
 }



 global.stop      = 0;
 global.buf       = NULL;
 global.size      = 0;
 global.in.plugin = NULL;

 if( pthread_mutex_init(&global.db, NULL) != 0 ) {
   LOG("could not initialize mutex variable\n");
   closelog();
   exit(EXIT_FAILURE);
 }
 if( pthread_cond_init(&global.db_update, NULL) != 0 ) {
   LOG("could not initialize condition variable\n");
   closelog();
   exit(EXIT_FAILURE);
 }

 signal(SIGPIPE, SIG_IGN);

 if (signal(SIGINT, signal_handler) == SIG_ERR) {
   LOG("could not register signal handler\n");
   closelog();
   exit(EXIT_FAILURE);
 }

 LOG("MJPG Streamer Version.: %s\n", SOURCE_VERSION);

 if ( global.outcnt == 0 ) {
 
   global.outcnt = 1;
 }



 tmp = (size_t)(strchr(input, ' ')-input);

 global.in.plugin = (tmp > 0)?strndup(input, tmp):strdup(input);
  //比如./mjpg_streamer -o "output_http.so -w ./www" -i "input_s3c2410.so -d  /dev/camera -r 320x240"
  //则char*  input="input_s3c2410.so -d  /dev/camera -r 320x240"
  //则char*  global.in.plugin ="input_s3c2410.so"

 global.in.handle = dlopen(global.in.plugin, RTLD_LAZY);
  //以RTLD_LAZY方式打开input_s3c2410.so,装载进内存
  //RTLD_LAZY延迟绑定
  //可见各个so都是以插件形式装载进内存
  //一般需要动态链接器去装载
 if ( !global.in.handle ) {
   LOG("ERROR: could not find input plugin\n");
   LOG("       Perhaps you want to adjust the search path with:\n");
   LOG("       # export LD_LIBRARY_PATH=/path/to/plugin/folder\n");
   LOG("       dlopen: %s\n", dlerror() );
   closelog();
   exit(EXIT_FAILURE);
 }
 global.in.init = dlsym(global.in.handle, "input_init");
//查找符号,在刚装载的模块比如input_s3c2410.so内存单元范围内中查找符号input_init,其虚拟地址已经确定
//找到,返回地址,赋给结构体global的成员结构体in的成员init函数指针
//即用input_init去填充in结构体的函数指针init
 if ( global.in.init == NULL ) {
   LOG("%s\n", dlerror());
   exit(EXIT_FAILURE);
 }
 global.in.stop = dlsym(global.in.handle, "input_stop");
//查找符号,在刚装载的模块比如input_s3c2410.so内存单元范围内中查找符号input_stop,
 if ( global.in.stop == NULL ) {
   LOG("%s\n", dlerror());
   exit(EXIT_FAILURE);
 }
 global.in.run = dlsym(global.in.handle, "input_run");
//查找符号,在刚装载的模块比如input_s3c2410.so内存单元范围内中查找符号input_run,
 if ( global.in.run == NULL ) {
   LOG("%s\n", dlerror());
   exit(EXIT_FAILURE);
 }

 global.in.cmd = dlsym(global.in.handle, "input_cmd");
//查找符号,在刚装载的模块比如input_s3c2410.so内存单元范围内中查找符号input_cmd,
 global.in.param.parameter_string = strchr(input, ' ');
  //比如./mjpg_streamer -o "output_http.so -w ./www" -i "input_s3c2410.so -d /dev/camera -r 320x240"
  //则char*  input="input_s3c2410.so -d  /dev/camera -r 320x240"
  //则char*  global.in.plugin ="input_s3c2410.so"
  //则char*  global.in.param.parameter_string ="-d /dev/camera -r 320x240"
  global.in.param.global = &global;
 if ( global.in.init(&global.in.param) ) {
   LOG("input_init() return value signals to exit");
   closelog();
   exit(0);
 }


 for (i=0; i    tmp = (size_t)(strchr(output[i], ' ')-output[i]);
   global.out[i].plugin = (tmp > 0)?strndup(output[i], tmp):strdup(output[i]);
   global.out[i].handle = dlopen(global.out[i].plugin, RTLD_LAZY);
  //比如./mjpg_streamer -o "output_http.so -w ./www" -i "input_s3c2410.so -d  /dev/camera -r 320x240"
  //则char*  output[0]="output_http.so -w ./www"
  //则char*  global.out[0].plugin ="output_http.so"
   if ( !global.out[i].handle ) {
     LOG("ERROR: could not find output plugin %s\n", global.out[i].plugin);
     LOG("       Perhaps you want to adjust the search path with:\n");
     LOG("       # export LD_LIBRARY_PATH=/path/to/plugin/folder\n");
     LOG("       dlopen: %s\n", dlerror() );
     closelog();
     exit(EXIT_FAILURE);
   }
   global.out[i].init = dlsym(global.out[i].handle, "output_init");
   if ( global.out[i].init == NULL ) {
     LOG("%s\n", dlerror());
     exit(EXIT_FAILURE);
   }
   global.out[i].stop = dlsym(global.out[i].handle, "output_stop");
   if ( global.out[i].stop == NULL ) {
     LOG("%s\n", dlerror());
     exit(EXIT_FAILURE);
   }
   global.out[i].run = dlsym(global.out[i].handle, "output_run");
   if ( global.out[i].run == NULL ) {
     LOG("%s\n", dlerror());
     exit(EXIT_FAILURE);
   }
 
   global.out[i].cmd = dlsym(global.out[i].handle, "output_cmd");
   global.out[i].param.parameter_string = strchr(output[i], ' ');
  //比如./mjpg_streamer -o "output_http.so -w ./www" -i "input_s3c2410.so -d  /dev/camera -r 320x240"
  //则char*  output[0]="output_http.so -w ./www"
  //则char*  global.out[0].plugin ="output_http.so"
  //则char*  global.out[0].param.parameter_string ="-w ./www"
   global.out[i].param.global = &global;
   global.out[i].param.id = i;
//i用于记录-o的索引,表示第几个输出(-o)
   if ( global.out[i].init(&global.out[i].param) ) {
     LOG("output_init() return value signals to exit");
     closelog();
     exit(0);
   }
 }



 DBG("starting input plugin\n");
 syslog(LOG_INFO, "starting input plugin");
 if ( global.in.run() ) {
   LOG("can not run input plugin\n");
   closelog();
   return 1;
 }


 DBG("starting %d output plugin(s)\n", global.outcnt);
 for(i=0; i    syslog(LOG_INFO, "starting output plugin: %s (ID: d)", global.out[i].plugin, global.out[i].param.id);
   global.out[i].run(global.out[i].param.id);
////传递的是id(表示第几个-o)
 }

 pause();
 return 0;
}

可知mjpg-streamer.c的作用主要是接受并分析启动参数,然后去加载起读写作用的部分--插件即那几个so.并
从输入插件中寻找以下符号
input_init()
input_stop()
input_run()
input_cmd()
从输出插件中寻找以下符号
output_init()
output_stop()
output_run()
output_cmd()
分别赋给globals的对应函数指针
然后执行input_init(),output_init(),input_run(),output_run(),跳到对应代码里面。见下文。
阅读(1009) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~