1. 程式人生 > >MJPG-streamer原始碼簡析

MJPG-streamer原始碼簡析

  MJPG-streamer主體上是由main函式和輸入外掛、輸出外掛組成。

  軟體執行的流程是先對攝像頭進行初始化然後設定基本的輸入輸出引數,接著從攝像頭中獲取資料放到全域性記憶體中,然後通知輸出函式來取出,接著輸出。

  攝像頭的初始化由結構體vdIn來進行。

複製程式碼
 1 struct vdIn {
 2     int fd;
 3     char *videodevice;
 4     char *status;
 5     char *pictName;
 6     struct v4l2_capability cap;
 7     struct v4l2_format fmt;
8 struct v4l2_buffer buf; 9 struct v4l2_requestbuffers rb; 10 void *mem[NB_BUFFER]; 11 unsigned char *tmpbuffer; 12 unsigned char *framebuffer; 13 int isstreaming; 14 int grabmethod; 15 int width; 16 int height; 17 int fps; 18 int formatIn; 19 int formatOut;
20 int framesizeIn; 21 int signalquit; 22 int toggleAvi; 23 int getPict; 24 int rawFrameCapture; 25 /* raw frame capture */ 26 unsigned int fileCounter; 27 /* raw frame stream capture */ 28 unsigned int rfsFramesWritten; 29 unsigned int rfsBytesWritten; 30 /* raw stream capture
*/ 31 FILE *captureFile; 32 unsigned int framesWritten; 33 unsigned int bytesWritten; 34 int framecount; 35 int recordstart; 36 int recordtime; 37 };
複製程式碼

  資料傳輸的開始、停止、結束等函式在輸入外掛中,它們有一個結構體output統領著。而資料輸出的開始、停止、結束等函式則是在輸出外掛中,由結構體output指揮。

複製程式碼
 1 struct _input {
 2   char *plugin;
 3   void *handle;
 4   input_parameter param;
 5 
 6   int (*init)(input_parameter *);
 7   int (*stop)(void);
 8   int (*run)(void);
 9   int (*cmd)(in_cmd_type, int);
10 };
複製程式碼 複製程式碼
 1 struct _output {
 2   char *plugin;
 3   void *handle;
 4   output_parameter param;
 5 
 6   int (*init)(output_parameter *);
 7   int (*stop)(int);
 8   int (*run)(int);
 9   int (*cmd)(int, out_cmd_type, int);
10 };
複製程式碼

  這些函式編譯成了兩個動態連結庫,分別是input_uvc.so和output_http.so

  這些函式以動態連結庫的方式在main函式中開啟並呼叫。

複製程式碼
 1   /* open input plugin */
 2   tmp = (size_t)(strchr(input, ' ')-input);
 3   global.in.plugin = (tmp > 0)?strndup(input, tmp):strdup(input);
 4   global.in.handle = dlopen(global.in.plugin, RTLD_LAZY);
 5   if ( !global.in.handle ) {
 6     LOG("ERROR: could not find input plugin\n");
 7     LOG("       Perhaps you want to adjust the search path with:\n");
 8     LOG("       # export LD_LIBRARY_PATH=/path/to/plugin/folder\n");
 9     LOG("       dlopen: %s\n", dlerror() );
10     closelog();
11     exit(EXIT_FAILURE);
12   }
13   global.in.init = dlsym(global.in.handle, "input_init");
14   if ( global.in.init == NULL ) {
15     LOG("%s\n", dlerror());
16     exit(EXIT_FAILURE);
17   }
18   global.in.stop = dlsym(global.in.handle, "input_stop");
19   if ( global.in.stop == NULL ) {
20     LOG("%s\n", dlerror());
21     exit(EXIT_FAILURE);
22   }
23   global.in.run = dlsym(global.in.handle, "input_run");
24   if ( global.in.run == NULL ) {
25     LOG("%s\n", dlerror());
26     exit(EXIT_FAILURE);
27   }
28   /* try to find optional command */
29   global.in.cmd = dlsym(global.in.handle, "input_cmd");
30 
31   global.in.param.parameter_string = strchr(input, ' ');
32   global.in.param.global = &global;
33 
34   if ( global.in.init(&global.in.param) ) {
35     LOG("input_init() return value signals to exit");
36     closelog();
37     exit(0);
38   }
複製程式碼 複製程式碼
 1   /* open output plugin */
 2   for (i=0; i<global.outcnt; i++) {
 3     tmp = (size_t)(strchr(output[i], ' ')-output[i]);
 4     global.out[i].plugin = (tmp > 0)?strndup(output[i], tmp):strdup(output[i]);
 5     global.out[i].handle = dlopen(global.out[i].plugin, RTLD_LAZY);
 6     if ( !global.out[i].handle ) {
 7       LOG("ERROR: could not find output plugin %s\n", global.out[i].plugin);
 8       LOG("       Perhaps you want to adjust the search path with:\n");
 9       LOG("       # export LD_LIBRARY_PATH=/path/to/plugin/folder\n");
10       LOG("       dlopen: %s\n", dlerror() );
11       closelog();
12       exit(EXIT_FAILURE);
13     }
14     global.out[i].init = dlsym(global.out[i].handle, "output_init");
15     if ( global.out[i].init == NULL ) {
16       LOG("%s\n", dlerror());
17       exit(EXIT_FAILURE);
18     }
19     global.out[i].stop = dlsym(global.out[i].handle, "output_stop");
20     if ( global.out[i].stop == NULL ) {
21       LOG("%s\n", dlerror());
22       exit(EXIT_FAILURE);
23     }
24     global.out[i].run = dlsym(global.out[i].handle, "output_run");
25     if ( global.out[i].run == NULL ) {
26       LOG("%s\n", dlerror());
27       exit(EXIT_FAILURE);
28     }
29     /* try to find optional command */
30     global.out[i].cmd = dlsym(global.out[i].handle, "output_cmd");
31 
32     global.out[i].param.parameter_string = strchr(output[i], ' ');
33     global.out[i].param.global = &global;
34     global.out[i].param.id = i;
35     if ( global.out[i].init(&global.out[i].param) ) {
36       LOG("output_init() return value signals to exit");
37       closelog();
38       exit(0);
39     }
40   }
複製程式碼

  開始執行輸入資料的處理以及放到全域性記憶體中。

1 2 3 4 /* start to read the input, push pictures into global buffer */ DBG("starting input plugin\n"); syslog(LOG_INFO, "starting input plugin"); global.in.run();

  開始執行從全域性記憶體中取出資料並輸出。

1 2 3 4 5 DBG("starting %d output plugin(s)\n", global.outcnt); for(i=0; i<global.outcnt; i++) { syslog(LOG_INFO, "starting output plugin: %s (ID: %02d)", global.out[i].plugin, global.out[i].param.id); global.out[i].run(global.out[i].param.id); }

一:main函式

  首先設定預設的輸入輸出動態連結庫,接著是一個while(1)的迴圈,它是對輸入引數的判斷並執行,然後判斷是否是守護程序和對全域性變數global的一些基本引數設定。接著建立並初始化執行緒(用來執行資料採集及輸出的函式)。然後開啟輸入、輸出的動態連結庫,把裡面的初始化、執行、停止、結束等函式進行一個匯出(相當於從一個庫裡面取出一個已經寫好了的函式然後呼叫,沒別的什麼複雜的)。最後就執行從庫中得到的執行函式,然後等待執行緒結束。

二:輸入

  Input_uvc.c函式中:

  

  1.執行緒的建立和清理,用來採集資料

  2.輸入資料的初始化和停止、執行、命令等

  而這些函式具體的行動則是在V4l2uvc.c中實現的:

複製程式碼
 1 int init_videoIn(struct vdIn *vd, char *device, int width, int height, int fps, int format, int grabmethod)
 2 {
 3   if (vd == NULL || device == NULL)
 4     return -1;
 5   if (width == 0 || height == 0)
 6     return -1;
 7   if (grabmethod < 0 || grabmethod > 1)
 8     grabmethod = 1;        //mmap by default;
 9   vd->videodevice = NULL;
10   vd->status = NULL;
11   vd->pictName = NULL;
12   vd->videodevice = (char *) calloc (1, 16 * sizeof (char));
13   vd->status = (char *) calloc (1, 100 * sizeof (char));
14   vd->pictName = (char *) calloc (1, 80 * sizeof (char));
15   snprintf (vd->videodevice, 12, "%s", device);
16   vd->toggleAvi = 0;
17   vd->getPict = 0;
18   vd->signalquit = 1;
19   vd->width = width;
20   vd->height = height;
21   vd->fps = fps;
22   vd->formatIn = format;
23   vd->grabmethod = grabmethod;
24   if (init_v4l2 (vd) < 0) {
25     fprintf (stderr, " Init v4L2 failed !! exit fatal \n");
26     goto error;;
27   }
28   /* alloc a temp buffer to reconstruct the pict */
29   vd->framesizeIn = (vd->width * vd->height << 1);
30   switch (vd->formatIn) {
31   case V4L2_PIX_FMT_MJPEG:
32     vd->tmpbuffer = (unsigned char *) calloc(1, (size_t) vd->framesizeIn);
33     if (!vd->tmpbuffer)
34       goto error;
35     vd->framebuffer =
36         (unsigned char *) calloc(1, (size_t) vd->width * (vd->height + 8) * 2);
37     break;
38   case V4L2_PIX_FMT_YUYV:
39     vd->framebuffer =
40         (unsigned char *) calloc(1, (size_t) vd->framesizeIn);
41     break;
42   default:
43     fprintf(stderr, " should never arrive exit fatal !!\n");
44     goto error;
45     break;
46   }
47   if (!vd->framebuffer)
48     goto error;
49   return 0;
50 error:
51   free(vd->videodevice);
52   free(vd->status);
53   free(vd->pictName);
54   close(vd->fd);
55   return -1;
56 }
複製程式碼 複製程式碼
  1 static int init_v4l2(struct vdIn *vd)
  2 {
  3   int i;
  4   int ret = 0;
  5 
  6   if ((vd->fd = open(vd->videodevice, O_RDWR)) == -1) {
  7     perror("ERROR opening V4L interface");
  8     return -1;
  9   }
 10 
 11   memset(&vd->cap, 0, sizeof(struct v4l2_capability));
 12   ret = ioctl(vd->fd, VIDIOC_QUERYCAP, &vd->cap);
 13   if (ret < 0) {
 14     fprintf(stderr, "Error opening device %s: unable to query device.\n", vd->videodevice);
 15     goto fatal;
 16   }
 17 
 18   if ((vd->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) {
 19     fprintf(stderr, "Error opening device %s: video capture not supported.\n",
 20            vd->videodevice);
 21     goto fatal;;
 22   }
 23 
 24   if (vd->grabmethod) {
 25     if (!(vd->cap.capabilities & V4L2_CAP_STREAMING)) {
 26       fprintf(stderr, "%s does not support streaming i/o\n", vd->videodevice);
 27       goto fatal;
 28     }
 29   } else {
 30     if (!(vd->cap.capabilities & V4L2_CAP_READWRITE)) {
 31       fprintf(stderr, "%s does not support read i/o\n", vd->videodevice);
 32       goto fatal;
 33     }
 34   }
 35 
 36   /*
 37    * set format in
 38    */
 39   memset(&vd->fmt, 0, sizeof(struct v4l2_format));
 40   vd->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 41   vd->fmt.fmt.pix.width = vd->width;
 42   vd->fmt.fmt.pix.height = vd->height;
 43   vd->fmt.fmt.pix.pixelformat = vd->formatIn;
 44   vd->fmt.fmt.pix.field = V4L2_FIELD_ANY;
 45   ret = ioctl(vd->fd, VIDIOC_S_FMT, &vd->fmt);
 46   if (ret < 0) {
 47     perror("Unable to set format");
 48     goto fatal;
 49   }
 50 
 51   if ((vd->fmt.fmt.pix.width != vd->width) ||
 52       (vd->fmt.fmt.pix.height != vd->height)) {
 53     fprintf(stderr, " format asked unavailable get width %d height %d \n", vd->fmt.fmt.pix.width, vd->fmt.fmt.pix.height);
 54     vd->width = vd->fmt.fmt.pix.width;
 55     vd->height = vd->fmt.fmt.pix.height;
 56     /*
 57      * look the format is not part of the deal ???
 58      */
 59      //vd->formatIn = vd->fmt.fmt.pix.pixelformat;
 60   }
 61 
 62   /*
 63    * set framerate
 64    */
 65   struct v4l2_streamparm *setfps;
 66   setfps = (struct v4l2_streamparm *) calloc(1, sizeof(struct v4l2_streamparm));
 67   memset(setfps, 0, sizeof(struct v4l2_streamparm));
 68   setfps->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 69   setfps->parm.capture.timeperframe.numerator = 1;
 70   setfps->parm.capture.timeperframe.denominator = vd->fps;
 71   ret = ioctl(vd->fd, VIDIOC_S_PARM, setfps);
 72 
 73   /*
 74    * request buffers
 75    */
 76   memset(&vd->rb, 0, sizeof(struct v4l2_requestbuffers));
 77   vd->rb.count = NB_BUFFER;
 78   vd->rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 79   vd->rb.memory = V4L2_MEMORY_MMAP;
 80 
 81   ret = ioctl(vd->fd, VIDIOC_REQBUFS, &vd->rb);
 82   if (ret < 0) {
 83     perror("Unable to allocate buffers");
 84     goto fatal;
 85   }
 86 
 87   /*
 88    * map the buffers
 89    */
 90   for (i = 0; i < NB_BUFFER; i++) {
 91     memset(&vd->buf, 0, sizeof(struct v4l2_buffer));
 92     vd->buf.index = i;
 93     vd->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 94     vd->buf.memory = V4L2_MEMORY_MMAP;
 95     ret = ioctl(vd->fd, VIDIOC_QUERYBUF, &vd->buf);
 96     if (ret < 0) {
 97       perror("Unable to query buffer");
 98       goto fatal;
 99     }
100 
101     if (debug)
102       fprintf(stderr, "length: %u offset: %u\n", vd->buf.length, vd->buf.m.offset);
103 
104     vd->mem[i] = mmap(0 /* start anywhere */ ,
105                       vd->buf.length, PROT_READ, MAP_SHARED, vd->fd,
106                       vd->buf.m.offset);
107     if (vd->mem[i] == MAP_FAILED) {
108       perror("Unable to map buffer");
109       goto fatal;
110     }
111     if (debug)
112       fprintf(stderr, "Buffer mapped at address %p.\n", vd->mem[i]);
113   }
114 
115   /*
116    * Queue the buffers.
117    */
118   for (i = 0; i < NB_BUFFER; ++i) {
119     memset(&vd->buf, 0, sizeof(struct v4l2_buffer));
120     vd->buf.index = i;
121     vd->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
122     vd->buf.memory = V4L2_MEMORY_MMAP;
123     ret = ioctl(vd->fd, VIDIOC_QBUF, &vd->buf);
124     if (ret < 0) {
125       perror("Unable to queue buffer");
126       goto fatal;;
127     }
128   }
129   return 0;
130 fatal:
131   return -1;
132 
133 }
複製程式碼

攝像頭的使能和禁止:

複製程式碼
 1 static int video_enable(struct vdIn *vd)
 2 {
 3   int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 4   int ret;
 5 
 6   ret = ioctl(vd->fd, VIDIOC_STREAMON, &type);
 7   if (ret < 0) {
 8     perror("Unable to start capture");
 9     return ret;
10   }
11   vd->isstreaming = 1;
12   return 0;
13 }
複製程式碼 複製程式碼
 1 static int video_disable(struct vdIn *vd)
 2 {
 3   int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 4   int ret;
 5 
 6   ret = ioctl(vd->fd, VIDIOC_STREAMOFF, &type);
 7   if (ret < 0) {
 8     perror("Unable to stop capture");
 9     return ret;
10   }
11   vd->isstreaming = 0;
12   return 0;
13 }
複製程式碼

將攝像頭中的資料拷貝到全域性記憶體中:

複製程式碼
 1 int memcpy_picture(unsigned char *out, unsigned char *buf, int size)
 2 {
 3   unsigned char *ptdeb, *ptlimit, *ptcur = buf;
 4   int sizein, pos=0;
 5 
 6   if (!is_huffman(buf)) {
 7     ptdeb = ptcur = buf;
 8     ptlimit = buf + size;
 9     while ((((ptcur[0] << 8) | ptcur[1]) != 0xffc0) && (ptcur < ptlimit))
10       ptcur++;
11     if (ptcur >= ptlimit)
12         return pos;
13     sizein = ptcur - ptdeb;
14 
15     memcpy(out+pos, buf, sizein); pos += sizein;
16     memcpy(out+pos, dht_data, sizeof(dht_data)); pos += sizeof(dht_data);
17     memcpy(out+pos, ptcur, size - sizein); pos += size-sizein;
18   } else {
19     memcpy(out+pos, ptcur, size); pos += size;
20   }
21   return pos;
22 }

相關推薦

MJPG-streamer原始碼

  MJPG-streamer主體上是由main函式和輸入外掛、輸出外掛組成。   軟體執行的流程是先對攝像頭進行初始化然後設定基本的輸入輸出引數,接著從攝像頭中獲取資料放到全域性記憶體中,然後通知輸出函式來取出,接著輸出。   攝像頭的初始化由結構體vdIn來進行

JAVA8的IO流原始碼

讓我們來分析一下java8裡面的IO原始碼。 一般來說分兩類,即位元組流和字元流,通過下面的思維導向圖總結下: 關於流有幾點是要注意的: 第一,讀寫流要及時的關閉,使用close方法。 第二,深入理解read和readline的區別。具體請看下面的原始碼: 需要注意

ffmpeg原始碼(十三)ffmpeg API變更 2009-03-01—— 2017-05-09變更

The last version increases were: libavcodec: 2015-08-28 libavdevice: 2015-08-28 libavfilter: 2015-08-28 libavformat: 2015-08-28 libavresample: 201

ffmpeg原始碼(一)結構總覽

未畢業通過校招進入了某做機的公司從事camera方面的工作。比較悲劇的是做了將近一年的Camera之後,正要研究Camera上下層打通任督二脈的時候,公司架構調整加上OS版本大變動,被調到了多媒體組(不過也好,我對編碼解碼這塊也是嚮往已久)。以前大學的時候用vi

Hadoop之job提交流程原始碼

1. 進入Job提交方法 public boolean waitForCompletion(boolean verbose               

ffmpeg原始碼(二)av_register_all(),avcodec_register_all()

av_register_all() 該函式在所有基於ffmpeg的應用程式中幾乎都是第一個被呼叫的。只有呼叫了該函式,才能使用複用器,編碼器等。 av_register_all()呼叫了avcodec_register_all()。avcodec_regis

SparseArray詳解及原始碼

一、前言 SparseArray 是 Android 在 Android SdK 為我們提供的一個基礎的資料結構,其功能類似於 HashMap。與 HashMap 不同的是它的 Key 只能是 int 值,不能是其他的型別。 二、程式碼分析 1. demo 及其簡析 首先也還是先通過 demo 來看一

MJPG-streamer原始碼分析-輸出部分

MJPG-streamer可以建立多個輸出,簡單的說,根據主函式中輸入的引數解析的結果,確定輸出通道的個數,至少保證有一個輸出通道在程式執行時存在。從引數解析結果確定每個輸出通道的引數,並以這些引數為每個通道建立傳送執行緒。在每個傳送執行緒上,不斷偵聽是否有連線請求。每當有連

二、View Animation動畫原始碼——動畫的啟動執行

不知道大夥有沒有想過,當我們呼叫了 View.startAnimation(animation) 之後,動畫是不是馬上就開始執行了? ——我們先來看看 View.startAnimation(animation) 方法裡都做那那些事情。 public voi

MJPG-streamer原始碼分析-輸入部分

MJPG-streamer僅可以建立一個輸入通道,對一個相機進行採集(如需多個,需另行修改)。在進行相機採集開始前,需要對其進行初始化,包括採集過程的全域性變數定義和記憶體開闢。執行過程則是將相機採集得到的資料,根據從相機對映到記憶體的地址上,按照不同的格式要求(YUV或M

MJPG-Streamer原始碼分析(一)

-------------------------------------------------------------------------------------------------- 另一片篇推薦的博文:http://www.armbbs.net/foru

ffmpeg原始碼(九)av_log(),AVClass,AVOption

1.av_log() av_log()是FFmpeg中輸出日誌的函式。隨便開啟一個FFmpeg的原始碼檔案,就會發現其中遍佈著av_log()函式。一般情況下FFmpeg類庫的原始碼中是不允許使用printf()這種的函式的,所有的輸出一律使用av_log()

解析 | openshift原始碼之pod網路配置(下)

【編者按】openshift底層是通過kubelet來管理pod,kubelet通過CNI外掛來配置pod網路.openshift node節點在啟動的時會在一個goroutine中啟動kubelet, 由kubelet來負責pod的管理工作。 本文主要從原始碼的角度

ElementUI 原始碼——原始碼結構篇

ElementUI 作為當前運用的最廣的 Vue PC 端元件庫,很多 Vue 元件庫的架構都是參照 ElementUI 做的。作為一個有夢想的前端(鹹魚),當然需要好好學習一番這套比較成熟的架構。 目錄結構解析 首先,我們先來看看 ElementUI 的目錄結構,總體來說,ElementUI 的目錄結構與

vuex原始碼

前言 基於 vuex 3.1.2 按如下流程進行分析: Vue.use(Vuex); const store = new Vuex.Store({ actions, getters, state, mutations, modules // ... });

併發系列(二)——FutureTask類原始碼

背景   本文基於JDK 11,主要介紹FutureTask類中的run()、get()和cancel() 方法,沒有過多解析相應interface中的註釋,但閱讀原始碼時建議先閱讀註釋,明白方法的主要的功能,再去看原始碼會更快。   文中若有不正確的地方歡迎大夥留言指出,謝謝了! 1、FutureTask類

MyBatis原始碼解析之資料來源(含資料庫連線池

一.概述: 常見的資料來源元件都實現了javax.sql.DataSource介面; MyBatis不但要能整合第三方的資料來源元件,自身也提供了資料來源的實現; 一般情況下,資料來源的初始化過程引數較多,比較複雜; 二.設計模式: 為什麼要使用工廠模式     資料來

Ribbon原始碼

  本篇不糾結原始碼細節,原始碼走讀可以參看Spring Cloud原始碼分析(二)Ribbon和深入理解Ribbon之原始碼解析。Ribbon這一塊原始碼的設計模式非常值得借鑑學習,符合開閉原則,對擴充套件開放,對修改封閉。所以大致看下原始碼這塊的程式設計的思

以太坊原始碼解讀(3)以太坊啟動流程

啟動命令: geth --identity "TestNode1" --datadir "data0" --rpc --rpcapi "db,eth,net,web3" --port "30303" --networkid "29382" --ws --wsorigins

mjpg-streamer專案原始碼分析 2

mjpg-streamer專案原始碼分析   2013-09-09 10:41:05|  分類: GT2440 |舉報|字號 訂閱  mjpg-streamer專案原始碼分析 2012-11-06 10:04 397人閱讀 評論(2) 收藏 舉報         前一段時間