网络摄像头3 cmos ov9650,plugins/input_s3c2410/
生活随笔
收集整理的這篇文章主要介紹了
网络摄像头3 cmos ov9650,plugins/input_s3c2410/
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
先貼出plugins/input_s3c2410/s3c2410.h里的幾個重要的macro和struct
/* in case default setting */ #define WIDTH 1280 #define HEIGHT 1024 #define BPPIN 8 #define OUTFRMNUMB 1 #define NB_BUFFER 4struct frame_t{char header[5];int nbframe; //記錄當前是第幾幀double seqtimes; //記錄轉換完當前幀時的時間。int deltatimes; //記錄從開始讀取數據到數據轉換完成所用時間int w;int h;int size; //記錄原始數據幀經過轉換后的圖像大小,即convertframe()的返回值int format; //記錄當前幀格式unsigned short bright;unsigned short contrast;unsigned short colors;unsigned short exposure;unsigned char wakeup;int acknowledge;} __attribute__ ((packed)); /* 此結構體用于描述圖像數據幀(指原始數據轉換后的)的信息,從s3c2410_Grab()的一些片段可看出。struct frame_t *headerframe;jpegsize= convertframe(vd->ptframe[vd->frame_cour]+ sizeof(struct frame_t),vd->pFramebuffer,vd->hdrwidth, vd->hdrheight,vd->formatIn,? qualite, vd->framesizeIn); headerframe=(struct frame_t*)vd->ptframe[vd->frame_cour];headerframe->seqtimes = ms_time();headerframe->deltatimes=(int)(headerframe->seqtimes-timecourant); headerframe->w = vd->hdrwidth;headerframe->h = vd->hdrheight;headerframe->size = (( jpegsize < 0)?0:jpegsize);; headerframe->format = vd->formatIn; headerframe->nbframe = frame++; */struct vdIn {int fd;char *videodevice ; //設備名字unsigned char *pFramebuffer; //存放從驅動中讀取的原始數據,vd->pFramebuffer=(unsigned char *) malloc ((size_t) vd->framesizeIn );unsigned char *ptframe[OUTFRMNUMB]; //用于在轉換convertframe()成圖像時的參數。#define OUTFRMNUMB 1,所以僅有一個元素ptframe[0]unsigned char *mem[NB_BUFFER];int framelock[OUTFRMNUMB];pthread_mutex_t grabmutex; //并發控制多個讀驅動線程對全局vd數據的訪問。但問題是好像只能有一個讀驅動線程,所以覺得可以省去的。int framesizeIn ; //記錄每一幀多少字節,是每次要從驅動讀取的字節數。vd->framesizeIn=width*height*2;? //RGB565 volatile int frame_cour; //當前幀索引。總是0(#define OUTFRMNUMB 1)int bppIn;int hdrwidth; //圖像widthint hdrheight; //圖像heightint formatIn;int signalquit; struct v4l2_capability cap;struct v4l2_format fmt;struct v4l2_buffer buf;struct v4l2_requestbuffers rb;int grayscale; //圖像灰度uint32_t quality; //圖像質量,用于將一幀原始數據轉化成圖像convertframe()時的參數。}; /* 此結構體用于保存從設備讀取的原始數據 */ 在plugins/input_s3c2410/input_s3c2410.c中聲明了幾個指針,全局的但只是在本input? plugin即input_s3c2410目錄里使用
#define INPUT_PLUGIN_NAME "S3C2410 embedded camera" #define MAX_ARGUMENTS 32/* private functions and variables to this plugin */ pthread_t cam;//從設備讀數據的線程標識符 struct vdIn *videoIn;static globals *pglobal;//此指針會指向在全局的mjpg_streamer.c中定義的global,以供本模塊訪問全局buf
如果使用如下指令啟動的mjpg_streamer
./mjpg_streamer -o "output_http.so -w ./www" -i "input_s3c2410.so -d /dev/camera" 則在mjpg_streamer.c中的兩條指令
global.in.init(&global.in.param) global.in.run() 分別是執行input_s3c2410.c中的
int input_init(input_parameter *param) // param.parameter_string="-d /dev/camera"int input_run(void)
搜索"見下面"取得線索。
***********************************************************init***************************************************************************
在input_s3c2410.c里,input_init源碼如下
/****************************************************************************** Description.: This function initializes the plugin. It parses the commandline-parameter and stores the default and parsed values in theappropriate variables. Input Value.: param contains among others the command-line string Return Value: 0 if everything is fine1 if "--help" was triggered, in this case the calling programmshould stop running and leave. ******************************************************************************/ int input_init(input_parameter *param) {char *dev = "/dev/video0", *s;//默認設備名是dev = "/dev/video0"int width=640, height=512, i;int argc=1;char *argv[MAX_ARGUMENTS]={NULL};uint32_t jpg_quality=1024;int grayscale=0;/* convert the single parameter-string to an array of strings */argv[0] = INPUT_PLUGIN_NAME;if ( param->parameter_string != NULL && strlen(param->parameter_string) != 0 ) {char *arg=NULL, *saveptr=NULL, *token=NULL;arg=(char *)strdup(param->parameter_string);if ( strchr(arg, ' ') != NULL ) {token=strtok_r(arg, " ", &saveptr);if ( token != NULL ) {argv[argc] = strdup(token);argc++;while ( (token=strtok_r(NULL, " ", &saveptr)) != NULL ) {argv[argc] = strdup(token);argc++;if (argc >= MAX_ARGUMENTS) {IPRINT("ERROR: too many arguments to input plugin\n");return 1;}}}}}/* show all parameters for DBG purposes */for (i=0; i<argc; i++) {DBG("argv[%d]=%s\n", i, argv[i]); /* 如果使用:-i "input_s3c2410.so -d /dev/camera"? 則DBG(input_s3c2410.c, input_init(), 95): argv[0]=S3C2410 embedded cameraDBG(input_s3c2410.c, input_init(), 95): argv[1]=-dDBG(input_s3c2410.c, input_init(), 95): argv[2]=/dev/camera */}/* parse the parameters */reset_getopt();while(1) {int option_index = 0, c=0;struct option long_options[] = \{{"help", no_argument, 0, 'h'},{"device", required_argument, 0, 'd'},{"resolution", required_argument, 0, 'r'},{"quality", required_argument, 0, 'q'},{"grayscale", no_argument, 0, 'g'},{0, 0, 0, 0}};c = getopt_long(argc, argv, "hd:r:q:g", long_options, &option_index);/* no more options to parse */if (c == -1) break;/* dispatch the given options */switch (c) {/* d, device */case 'd':DBG("case d\n");dev = strdup(optarg);break;//指定灰度,默認0case 'g':grayscale=1;break;/* r, resolution *///指定尺寸如 -r 320x240 ,默認640x512case 'r':DBG("case r\n");width = -1;height = -1;/* parse value as decimal value */width = strtol(optarg, &s, 10);height = strtol(s+1, NULL, 10);break;/* q, quality *///指定圖片質量 默認1024case 'q':DBG("case q\n");jpg_quality = atoi(optarg);break;/* h, help */case 'h':default:DBG("default case, h\n");help();return 1;}}/* keep a pointer to the global variables */pglobal = param->global;/* allocate webcam datastructure */videoIn = malloc(sizeof(struct vdIn)); //為模塊內全局指針videoIn分配內存if ( videoIn == NULL ) {IPRINT("not enough memory for videoIn\n");exit(EXIT_FAILURE);}memset(videoIn, 0, sizeof(struct vdIn)); //清0DBG("initializing s3c2410 device\n");/* display the parsed values */IPRINT("Using V4L2 device.: %s\n", dev);IPRINT("Desired Resolution: %i x %i\n", width, height);IPRINT("Grayscale mode: %s\n",grayscale?"on":"off");videoIn->grayscale=grayscale;videoIn->quality=jpg_quality;/* open video device and prepare data structure */if (init_s3c2410 (videoIn, dev, width, height) != 0) //調用實際的初始化函數對設備初始化,見下面{IPRINT("init_s3c2410 failed\n");closelog();exit(EXIT_FAILURE);}return 0; } 在plugins/input_s3c2410/s3c2410.c可以看到init_s3c2410 (videoIn, dev, width, height)的源碼
對設備文件/dev/camera進行讀寫等操作的函數均在此文件內
init_s3c2410()函數接收的
第1個參數是指針struct vdIn *vd
第2個參數是設備名
第3,4個參數是圖像尺寸
int init_s3c2410 (struct vdIn *vd, char *device, int width, int height) {int err = -1;int f;int i;if (vd == NULL || device == NULL)return -1;if (width == 0 || height == 0)return -1;vd->videodevice=strdup(device);//取得設備名,以便openvd->framesizeIn=width*height*2; //RGB565 vd->hdrwidth=width;vd->hdrheight=height;//printf("Allocating frame:%dx%d\n",width,height);vd->pFramebuffer=(unsigned char *) malloc ((size_t) vd->framesizeIn ); //just in casevd->formatIn=0;DBG("Opening device\n");if ((vd->fd = open( vd->videodevice, O_RDWR)) == -1)//打開設備 /dev/cameraexit_fatal ("ERROR opening V4L interface");DBG("Allocating input buffers\n");/* allocate the 4 frames output buffer */for (i = 0; i < OUTFRMNUMB; i++){vd->ptframe[i] = NULL;vd->ptframe[i] = (unsigned char *) malloc ((size_t) vd->framesizeIn+sizeof(struct frame_t) );vd->framelock[i] = 0;}vd->frame_cour = 0;pthread_mutex_init (&vd->grabmutex, NULL);//初始化互斥量,以便后面用到printf("Allocated\n");return 0; }
***********************************************************run**************************************************************************
在plugins/input_s3c2410/input_s3c2410.c,input_run源碼如下
/****************************************************************************** Description.: spins of a worker thread Input Value.: - Return Value: always 0 ******************************************************************************/ int input_run(void) {pglobal->buf = malloc(videoIn->framesizeIn);if (pglobal->buf == NULL) {fprintf(stderr, "could not allocate memory\n");exit(EXIT_FAILURE);}pthread_create(&cam, 0, cam_thread, NULL);//創建線程,線程函數見下面pthread_detach(cam);return 0; }說明
1.執行一次.mjpg_streamer會,會創建一個線程去從設備讀數據。并將數據處理后放在全局變量global里。而不管有沒有客戶端從global中讀取。
2.pthread_detach(cam);分離線程,這個線程的作用是從設備讀數據到全局變量,沒數據返回給主線程,所以主線程沒必要等待此線程的返回。這樣的話,這個線程結束時(何時?主進程結束比如kill xxx,比如pglobal->stop==1)就可以自動被系統回收。
在plugins/input_s3c2410/input_s3c2410.c,線程函數cam_thread源碼如下
/****************************************************************************** Description.: this thread worker grabs a frame and copies it to the global buffer Input Value.: unused Return Value: unused, always NULL ******************************************************************************/ void *cam_thread( void *arg ) {int iframe = 0;unsigned char *pictureData = NULL;struct frame_t *headerframe;int r;/* set cleanup handler to cleanup allocated ressources */pthread_cleanup_push(cam_cleanup, NULL);while( !pglobal->stop ) {/* grab a frame */r=s3c2410_Grab( videoIn );//從驅動抓取一幀原始數據并編碼,見下面if( r < 0 ) {IPRINT("Error grabbing frames\n");exit(EXIT_FAILURE);}if(!r) //not captured{//sleep(0);pthread_yield();continue;}iframe=(videoIn->frame_cour +(OUTFRMNUMB-1))% OUTFRMNUMB; //循環索引 //由于#define OUTFRMNUMB 1,所以iframe總是0videoIn->framelock[iframe]++; //這句和下面的那句--貌似沒怎么用用,注釋掉也可以 headerframe=(struct frame_t*)videoIn->ptframe[iframe]; //指向轉換后的圖像數據pictureData = videoIn->ptframe[iframe]+sizeof(struct frame_t); //指向數據區。videoIn->framelock[iframe]--;/* copy JPG picture to global buffer */pthread_mutex_lock( &pglobal->db ); //寫數據前上鎖(互斥量,互斥鎖),以防止客戶端線程此時去讀數據。 //如果此時客戶端線程在持有互斥鎖,則該線程阻塞,直到鎖可用。pglobal->size = get_jpegsize(pictureData, headerframe->size);memcpy(pglobal->buf, pictureData, pglobal->size); //全局變量,寫入數據大小pglobal->size,數據pglobal->buf/* signal fresh_frame */pthread_cond_broadcast(&pglobal->db_update); //有新數據的信號pthread_mutex_unlock( &pglobal->db ); //釋放互斥量}DBG("leaving input thread, calling cleanup function now\n");pthread_cleanup_pop(1);return NULL; }/* 在plugins/input_s3c2410/utils.c int get_jpegsize (unsigned char *buf, int insize) {int i; ?? ?for ( i= 1024 ; i< insize; i++) {if ((buf[i] == 0xFF) && (buf[i+1] == 0xD9)) return i+10; //圖像結束標志 0xFF 0xD9}return -1; } */
在plugins/input_s3c2410/s3c2410.c,s3c2410_Grab源碼如下,這個是從設備讀數據的關鍵部分
int s3c2410_Grab (struct vdIn *vd ) {static int frame = 0; //記錄抓取到第幾幀int len;int size;int err = 0;int jpegsize = 0;int qualite = 1024;struct frame_t *headerframe;double timecourant =0;double temps = 0; //記錄將原始數據轉換成圖像格式所用的時間,mstimecourant = ms_time(); //即將開始讀取,記下當前時間 /* read method */size = vd->framesizeIn; //要從驅動中讀取的字節數。 vd->framesizeIn=width*height*2;do //讀取數據,死循環,直到讀到數據。如果驅動從硬件采集數據較慢的話,該線程大多數的時間可能就在這個地方打轉。{len = read (vd->fd, vd->pFramebuffer, size); //從驅動中讀取size字節原始數據到vd->pFramebufferif(!len ) //not yet readysched_yield(); //如果驅動還未準備好數據,線程主動讓出cpu,即主動向os申請調度到可運行隊列末尾。但不是阻塞即不是調度到等待隊列。} while(!len);if(len<0) {printf ("2440 read error\n");return -1;}/* Is there someone using the frame */while((vd->framelock[vd->frame_cour] != 0)&& vd->signalquit)usleep(1000);pthread_mutex_lock (&vd->grabmutex); //寫數據前上鎖(互斥量,互斥鎖),是對本模塊內的全局結構體vd的并發保護。 //但我覺得可以不用保護,因為自始自終只有這一個線程可以操作這個數據,其他的socket線程根本不會接觸這個數據的。/*memcpy (vd->ptframe[vd->frame_cour]+ sizeof(struct frame_t), vd->pFramebuffer, vd->framesizeIn);jpegsize =jpeg_compress(vd->ptframe[vd->frame_cour]+ sizeof(struct frame_t),len,vd->pFramebuffer, vd->hdrwidth, vd->hdrheight, qualite); */temps = ms_time();jpegsize= convertframe(vd->ptframe[vd->frame_cour]+ sizeof(struct frame_t),//對原始數據轉換及編碼,見下面vd->pFramebuffer,vd->hdrwidth, vd->hdrheight,vd->formatIn, qualite, vd->framesizeIn); /* 函數原型是 int convertframe(unsigned char *dst,unsigned char *src,int width,int height, int formatIn, int qualite,int buf_size) 可見是將原始數據vd->pFramebuffer轉換成圖像數據,然后用比如vd->ptframe[0]指向之。*/ headerframe=(struct frame_t*)vd->ptframe[vd->frame_cour]; //真正的圖像幀格式headerframe指向轉換后的數據區snprintf(headerframe->header,5,"%s","2410"); headerframe->seqtimes = ms_time(); //讀取并轉換完畢,記下當前時間headerframe->deltatimes=(int)(headerframe->seqtimes-timecourant); //記錄從開始讀取數據到數據編碼完成所用時間headerframe->w = vd->hdrwidth;headerframe->h = vd->hdrheight;headerframe->size = (( jpegsize < 0)?0:jpegsize);; headerframe->format = vd->formatIn; headerframe->nbframe = frame++; DBG("compress frame %d times %f\n",frame, headerframe->seqtimes-temps); /* 打印出來的是轉換時間,不包括從驅動讀取數據用去的時間。所以比較均勻。 這個就是終端里一直打印出來的,比如 次數 所用時間msDBG(s3c2410.c, s3c2410_Grab(), 172): compress frame 34599 times 175.409058DBG(s3c2410.c, s3c2410_Grab(), 172): compress frame 34600 times 176.015015DBG(s3c2410.c, s3c2410_Grab(), 172): compress frame 34601 times 175.151001DBG(s3c2410.c, s3c2410_Grab(), 172): compress frame 34602 times 175.377930*/vd->frame_cour = (vd->frame_cour +1) % OUTFRMNUMB; //#define OUTFRMNUMB 1 /* 當前幀索引+1, 可以看出作者是每次從驅動中讀取一個原始數據幀放在vd->pFramebuffer,將數據轉換后使用當前幀vd->ptframe[vd->frame_cour]指向,而每讀取一幀則vd->frame_cour++。 由于#define OUTFRMNUMB 1,所以vd->ptframe[vd->frame_cour]總是vd->ptframe[0]. 而headerframe是本函數內的局部變量,總是指向vd->ptframe[0]。記錄轉換后的圖像信息可以打印出來以供調試參考 在s3c2410_Grab()返回點cam_thread()里面也有一個局部變量headerframe,也是指向vd->ptframe[0],但也不重要。重要的是那個pictureData 是全局global變量的buf和size的源頭活水,見cam_thread(). */pthread_mutex_unlock (&vd->grabmutex); /************************************/return jpegsize; }
在plugins/input_s3c2410/s3c2410.c,
int convertframe(unsigned char *dst,unsigned char *src, int width,int height, int formatIn, int qualite,int buf_size) { int ret=0;//unsigned char *tmp=malloc(width*height*2);RGB565_2_YCbCr420(src,src,width,height); //inplace conversion //見下面 ret=s_encode_image(src,dst,qualite,FORMAT_CbCr420,width,height,buf_size); //圖像編碼,見下面//free(tmp);return ret; }
在根目錄的simplified_jpeg_encoder.c,
/* translate RGB565 to YUV420 in input */ void RGB565_2_YCbCr420(uint8_t * input_ptr, uint8_t * output_ptr, uint32_t image_width,uint32_t image_height) {uint32_t i, j, size;uint8_t R, G, B, R1, G1, B1, Rd, Gd, Bd, Rd1, Gd1, Bd1;S_INT Y, Yd, Y11, Yd1, Cb, Cr;S_JPEG_RGB16 * inbuf = (S_JPEG_RGB16 *) input_ptr;S_JPEG_RGB16 * inbuf1 = inbuf + (image_width);size = image_width * image_height >> 2;for (i = size, j = 0; i > 0; i--){B = inbuf[0].blue << 3;G = inbuf[0].green << 2;R = inbuf[0].red << 3;B1 = inbuf[1].blue << 3;G1 = inbuf[1].green << 2;R1 = inbuf[1].red << 3;Bd = inbuf1[0].blue << 3;Gd = inbuf1[0].green << 2;Rd = inbuf1[0].red << 3;Bd1 = inbuf1[1].blue << 3;Gd1 = inbuf[1].green << 2;Rd1 = inbuf[1].red << 3;inbuf += 2;inbuf1 += 2;j++;if (j >= image_width / 2) {j = 0;inbuf += (image_width);inbuf1 += (image_width);}Y = CLIP((77 * R + 150 * G + 29 * B) >> 8);Y11 = CLIP((77 * R1 + 150 * G1 + 29 * B1) >> 8);Yd = CLIP((77 * Rd + 150 * Gd + 29 * Bd) >> 8);Yd1 = CLIP((77 * Rd1 + 150 * Gd1 + 29 * Bd1) >> 8);Cb = CLIP(((-43 * R - 85 * G + 128 * B) >> 8) + 128);Cr = CLIP(((128 * R - 107 * G - 21 * B) >> 8) + 128);*output_ptr++ = (uint8_t) Y;*output_ptr++ = (uint8_t) Y11;*output_ptr++ = (uint8_t) Yd;*output_ptr++ = (uint8_t) Yd1;*output_ptr++ = (uint8_t) Cb;*output_ptr++ = (uint8_t) Cr;} }
在根目錄的simplified_jpeg_encoder.c,
uint32_t s_encode_image(uint8_t * input_ptr, uint8_t * output_ptr,uint32_t quality_factor, int image_format,uint32_t image_width, uint32_t image_height,uint32_t output_buffer_size) {S_UINT i, j;S_UINT last_col;S_UINT last_row;uint8_t * output;S_JPEG_ENCODER_STRUCTURE JpegStruct;S_JPEG_ENCODER_STRUCTURE * enc = &JpegStruct;/*(S_JPEG_ENCODER_STRUCTURE *)malloc(sizeof(S_JPEG_ENCODER_STRUCTURE));memset(enc,0,sizeof(S_JPEG_ENCODER_STRUCTURE));*/output = output_ptr;/* Initialization of JPEG control structure */initialization(enc, image_format, image_width, image_height);/* Quantization Table Initialization */initialize_quantization_tables(enc,quality_factor);/* Writing Marker Data */output_ptr = write_markers(enc,output_ptr, image_format, image_width, image_height);last_row=enc->vertical_mcus-1;last_col=enc->horizontal_mcus-1;for (i = 0; i < enc->vertical_mcus; i++){if (i < last_row)enc->rows = enc->mcu_height;elseenc->rows = enc->rows_in_bottom_mcus;for (j = 0; j < enc->horizontal_mcus; j++){if (j < last_col){enc->cols = enc->mcu_width;enc->scan_line_incr = enc->length_minus_mcu_width;} else {enc->cols = enc->cols_in_right_mcus;enc->scan_line_incr = enc->length_minus_width;}enc->read_format(enc, input_ptr,i,j);/* Encode the data in MCU */output_ptr = encodeMCU(enc, image_format, output_ptr);//input_ptr += enc->mcu_width_size;}//input_ptr += enc->mcu_line_offset;}/* Close Routine */output_ptr = close_bitstream(enc,output_ptr);//free(enc);return (uint32_t)(output_ptr - output); }
/* in case default setting */ #define WIDTH 1280 #define HEIGHT 1024 #define BPPIN 8 #define OUTFRMNUMB 1 #define NB_BUFFER 4struct frame_t{char header[5];int nbframe; //記錄當前是第幾幀double seqtimes; //記錄轉換完當前幀時的時間。int deltatimes; //記錄從開始讀取數據到數據轉換完成所用時間int w;int h;int size; //記錄原始數據幀經過轉換后的圖像大小,即convertframe()的返回值int format; //記錄當前幀格式unsigned short bright;unsigned short contrast;unsigned short colors;unsigned short exposure;unsigned char wakeup;int acknowledge;} __attribute__ ((packed)); /* 此結構體用于描述圖像數據幀(指原始數據轉換后的)的信息,從s3c2410_Grab()的一些片段可看出。struct frame_t *headerframe;jpegsize= convertframe(vd->ptframe[vd->frame_cour]+ sizeof(struct frame_t),vd->pFramebuffer,vd->hdrwidth, vd->hdrheight,vd->formatIn,? qualite, vd->framesizeIn); headerframe=(struct frame_t*)vd->ptframe[vd->frame_cour];headerframe->seqtimes = ms_time();headerframe->deltatimes=(int)(headerframe->seqtimes-timecourant); headerframe->w = vd->hdrwidth;headerframe->h = vd->hdrheight;headerframe->size = (( jpegsize < 0)?0:jpegsize);; headerframe->format = vd->formatIn; headerframe->nbframe = frame++; */struct vdIn {int fd;char *videodevice ; //設備名字unsigned char *pFramebuffer; //存放從驅動中讀取的原始數據,vd->pFramebuffer=(unsigned char *) malloc ((size_t) vd->framesizeIn );unsigned char *ptframe[OUTFRMNUMB]; //用于在轉換convertframe()成圖像時的參數。#define OUTFRMNUMB 1,所以僅有一個元素ptframe[0]unsigned char *mem[NB_BUFFER];int framelock[OUTFRMNUMB];pthread_mutex_t grabmutex; //并發控制多個讀驅動線程對全局vd數據的訪問。但問題是好像只能有一個讀驅動線程,所以覺得可以省去的。int framesizeIn ; //記錄每一幀多少字節,是每次要從驅動讀取的字節數。vd->framesizeIn=width*height*2;? //RGB565 volatile int frame_cour; //當前幀索引。總是0(#define OUTFRMNUMB 1)int bppIn;int hdrwidth; //圖像widthint hdrheight; //圖像heightint formatIn;int signalquit; struct v4l2_capability cap;struct v4l2_format fmt;struct v4l2_buffer buf;struct v4l2_requestbuffers rb;int grayscale; //圖像灰度uint32_t quality; //圖像質量,用于將一幀原始數據轉化成圖像convertframe()時的參數。}; /* 此結構體用于保存從設備讀取的原始數據 */ 在plugins/input_s3c2410/input_s3c2410.c中聲明了幾個指針,全局的但只是在本input? plugin即input_s3c2410目錄里使用
#define INPUT_PLUGIN_NAME "S3C2410 embedded camera" #define MAX_ARGUMENTS 32/* private functions and variables to this plugin */ pthread_t cam;//從設備讀數據的線程標識符 struct vdIn *videoIn;static globals *pglobal;//此指針會指向在全局的mjpg_streamer.c中定義的global,以供本模塊訪問全局buf
如果使用如下指令啟動的mjpg_streamer
./mjpg_streamer -o "output_http.so -w ./www" -i "input_s3c2410.so -d /dev/camera" 則在mjpg_streamer.c中的兩條指令
global.in.init(&global.in.param) global.in.run() 分別是執行input_s3c2410.c中的
int input_init(input_parameter *param) // param.parameter_string="-d /dev/camera"int input_run(void)
搜索"見下面"取得線索。
***********************************************************init***************************************************************************
在input_s3c2410.c里,input_init源碼如下
/****************************************************************************** Description.: This function initializes the plugin. It parses the commandline-parameter and stores the default and parsed values in theappropriate variables. Input Value.: param contains among others the command-line string Return Value: 0 if everything is fine1 if "--help" was triggered, in this case the calling programmshould stop running and leave. ******************************************************************************/ int input_init(input_parameter *param) {char *dev = "/dev/video0", *s;//默認設備名是dev = "/dev/video0"int width=640, height=512, i;int argc=1;char *argv[MAX_ARGUMENTS]={NULL};uint32_t jpg_quality=1024;int grayscale=0;/* convert the single parameter-string to an array of strings */argv[0] = INPUT_PLUGIN_NAME;if ( param->parameter_string != NULL && strlen(param->parameter_string) != 0 ) {char *arg=NULL, *saveptr=NULL, *token=NULL;arg=(char *)strdup(param->parameter_string);if ( strchr(arg, ' ') != NULL ) {token=strtok_r(arg, " ", &saveptr);if ( token != NULL ) {argv[argc] = strdup(token);argc++;while ( (token=strtok_r(NULL, " ", &saveptr)) != NULL ) {argv[argc] = strdup(token);argc++;if (argc >= MAX_ARGUMENTS) {IPRINT("ERROR: too many arguments to input plugin\n");return 1;}}}}}/* show all parameters for DBG purposes */for (i=0; i<argc; i++) {DBG("argv[%d]=%s\n", i, argv[i]); /* 如果使用:-i "input_s3c2410.so -d /dev/camera"? 則DBG(input_s3c2410.c, input_init(), 95): argv[0]=S3C2410 embedded cameraDBG(input_s3c2410.c, input_init(), 95): argv[1]=-dDBG(input_s3c2410.c, input_init(), 95): argv[2]=/dev/camera */}/* parse the parameters */reset_getopt();while(1) {int option_index = 0, c=0;struct option long_options[] = \{{"help", no_argument, 0, 'h'},{"device", required_argument, 0, 'd'},{"resolution", required_argument, 0, 'r'},{"quality", required_argument, 0, 'q'},{"grayscale", no_argument, 0, 'g'},{0, 0, 0, 0}};c = getopt_long(argc, argv, "hd:r:q:g", long_options, &option_index);/* no more options to parse */if (c == -1) break;/* dispatch the given options */switch (c) {/* d, device */case 'd':DBG("case d\n");dev = strdup(optarg);break;//指定灰度,默認0case 'g':grayscale=1;break;/* r, resolution *///指定尺寸如 -r 320x240 ,默認640x512case 'r':DBG("case r\n");width = -1;height = -1;/* parse value as decimal value */width = strtol(optarg, &s, 10);height = strtol(s+1, NULL, 10);break;/* q, quality *///指定圖片質量 默認1024case 'q':DBG("case q\n");jpg_quality = atoi(optarg);break;/* h, help */case 'h':default:DBG("default case, h\n");help();return 1;}}/* keep a pointer to the global variables */pglobal = param->global;/* allocate webcam datastructure */videoIn = malloc(sizeof(struct vdIn)); //為模塊內全局指針videoIn分配內存if ( videoIn == NULL ) {IPRINT("not enough memory for videoIn\n");exit(EXIT_FAILURE);}memset(videoIn, 0, sizeof(struct vdIn)); //清0DBG("initializing s3c2410 device\n");/* display the parsed values */IPRINT("Using V4L2 device.: %s\n", dev);IPRINT("Desired Resolution: %i x %i\n", width, height);IPRINT("Grayscale mode: %s\n",grayscale?"on":"off");videoIn->grayscale=grayscale;videoIn->quality=jpg_quality;/* open video device and prepare data structure */if (init_s3c2410 (videoIn, dev, width, height) != 0) //調用實際的初始化函數對設備初始化,見下面{IPRINT("init_s3c2410 failed\n");closelog();exit(EXIT_FAILURE);}return 0; } 在plugins/input_s3c2410/s3c2410.c可以看到init_s3c2410 (videoIn, dev, width, height)的源碼
對設備文件/dev/camera進行讀寫等操作的函數均在此文件內
init_s3c2410()函數接收的
第1個參數是指針struct vdIn *vd
第2個參數是設備名
第3,4個參數是圖像尺寸
int init_s3c2410 (struct vdIn *vd, char *device, int width, int height) {int err = -1;int f;int i;if (vd == NULL || device == NULL)return -1;if (width == 0 || height == 0)return -1;vd->videodevice=strdup(device);//取得設備名,以便openvd->framesizeIn=width*height*2; //RGB565 vd->hdrwidth=width;vd->hdrheight=height;//printf("Allocating frame:%dx%d\n",width,height);vd->pFramebuffer=(unsigned char *) malloc ((size_t) vd->framesizeIn ); //just in casevd->formatIn=0;DBG("Opening device\n");if ((vd->fd = open( vd->videodevice, O_RDWR)) == -1)//打開設備 /dev/cameraexit_fatal ("ERROR opening V4L interface");DBG("Allocating input buffers\n");/* allocate the 4 frames output buffer */for (i = 0; i < OUTFRMNUMB; i++){vd->ptframe[i] = NULL;vd->ptframe[i] = (unsigned char *) malloc ((size_t) vd->framesizeIn+sizeof(struct frame_t) );vd->framelock[i] = 0;}vd->frame_cour = 0;pthread_mutex_init (&vd->grabmutex, NULL);//初始化互斥量,以便后面用到printf("Allocated\n");return 0; }
***********************************************************run**************************************************************************
在plugins/input_s3c2410/input_s3c2410.c,input_run源碼如下
/****************************************************************************** Description.: spins of a worker thread Input Value.: - Return Value: always 0 ******************************************************************************/ int input_run(void) {pglobal->buf = malloc(videoIn->framesizeIn);if (pglobal->buf == NULL) {fprintf(stderr, "could not allocate memory\n");exit(EXIT_FAILURE);}pthread_create(&cam, 0, cam_thread, NULL);//創建線程,線程函數見下面pthread_detach(cam);return 0; }說明
1.執行一次.mjpg_streamer會,會創建一個線程去從設備讀數據。并將數據處理后放在全局變量global里。而不管有沒有客戶端從global中讀取。
2.pthread_detach(cam);分離線程,這個線程的作用是從設備讀數據到全局變量,沒數據返回給主線程,所以主線程沒必要等待此線程的返回。這樣的話,這個線程結束時(何時?主進程結束比如kill xxx,比如pglobal->stop==1)就可以自動被系統回收。
在plugins/input_s3c2410/input_s3c2410.c,線程函數cam_thread源碼如下
/****************************************************************************** Description.: this thread worker grabs a frame and copies it to the global buffer Input Value.: unused Return Value: unused, always NULL ******************************************************************************/ void *cam_thread( void *arg ) {int iframe = 0;unsigned char *pictureData = NULL;struct frame_t *headerframe;int r;/* set cleanup handler to cleanup allocated ressources */pthread_cleanup_push(cam_cleanup, NULL);while( !pglobal->stop ) {/* grab a frame */r=s3c2410_Grab( videoIn );//從驅動抓取一幀原始數據并編碼,見下面if( r < 0 ) {IPRINT("Error grabbing frames\n");exit(EXIT_FAILURE);}if(!r) //not captured{//sleep(0);pthread_yield();continue;}iframe=(videoIn->frame_cour +(OUTFRMNUMB-1))% OUTFRMNUMB; //循環索引 //由于#define OUTFRMNUMB 1,所以iframe總是0videoIn->framelock[iframe]++; //這句和下面的那句--貌似沒怎么用用,注釋掉也可以 headerframe=(struct frame_t*)videoIn->ptframe[iframe]; //指向轉換后的圖像數據pictureData = videoIn->ptframe[iframe]+sizeof(struct frame_t); //指向數據區。videoIn->framelock[iframe]--;/* copy JPG picture to global buffer */pthread_mutex_lock( &pglobal->db ); //寫數據前上鎖(互斥量,互斥鎖),以防止客戶端線程此時去讀數據。 //如果此時客戶端線程在持有互斥鎖,則該線程阻塞,直到鎖可用。pglobal->size = get_jpegsize(pictureData, headerframe->size);memcpy(pglobal->buf, pictureData, pglobal->size); //全局變量,寫入數據大小pglobal->size,數據pglobal->buf/* signal fresh_frame */pthread_cond_broadcast(&pglobal->db_update); //有新數據的信號pthread_mutex_unlock( &pglobal->db ); //釋放互斥量}DBG("leaving input thread, calling cleanup function now\n");pthread_cleanup_pop(1);return NULL; }/* 在plugins/input_s3c2410/utils.c int get_jpegsize (unsigned char *buf, int insize) {int i; ?? ?for ( i= 1024 ; i< insize; i++) {if ((buf[i] == 0xFF) && (buf[i+1] == 0xD9)) return i+10; //圖像結束標志 0xFF 0xD9}return -1; } */
在plugins/input_s3c2410/s3c2410.c,s3c2410_Grab源碼如下,這個是從設備讀數據的關鍵部分
int s3c2410_Grab (struct vdIn *vd ) {static int frame = 0; //記錄抓取到第幾幀int len;int size;int err = 0;int jpegsize = 0;int qualite = 1024;struct frame_t *headerframe;double timecourant =0;double temps = 0; //記錄將原始數據轉換成圖像格式所用的時間,mstimecourant = ms_time(); //即將開始讀取,記下當前時間 /* read method */size = vd->framesizeIn; //要從驅動中讀取的字節數。 vd->framesizeIn=width*height*2;do //讀取數據,死循環,直到讀到數據。如果驅動從硬件采集數據較慢的話,該線程大多數的時間可能就在這個地方打轉。{len = read (vd->fd, vd->pFramebuffer, size); //從驅動中讀取size字節原始數據到vd->pFramebufferif(!len ) //not yet readysched_yield(); //如果驅動還未準備好數據,線程主動讓出cpu,即主動向os申請調度到可運行隊列末尾。但不是阻塞即不是調度到等待隊列。} while(!len);if(len<0) {printf ("2440 read error\n");return -1;}/* Is there someone using the frame */while((vd->framelock[vd->frame_cour] != 0)&& vd->signalquit)usleep(1000);pthread_mutex_lock (&vd->grabmutex); //寫數據前上鎖(互斥量,互斥鎖),是對本模塊內的全局結構體vd的并發保護。 //但我覺得可以不用保護,因為自始自終只有這一個線程可以操作這個數據,其他的socket線程根本不會接觸這個數據的。/*memcpy (vd->ptframe[vd->frame_cour]+ sizeof(struct frame_t), vd->pFramebuffer, vd->framesizeIn);jpegsize =jpeg_compress(vd->ptframe[vd->frame_cour]+ sizeof(struct frame_t),len,vd->pFramebuffer, vd->hdrwidth, vd->hdrheight, qualite); */temps = ms_time();jpegsize= convertframe(vd->ptframe[vd->frame_cour]+ sizeof(struct frame_t),//對原始數據轉換及編碼,見下面vd->pFramebuffer,vd->hdrwidth, vd->hdrheight,vd->formatIn, qualite, vd->framesizeIn); /* 函數原型是 int convertframe(unsigned char *dst,unsigned char *src,int width,int height, int formatIn, int qualite,int buf_size) 可見是將原始數據vd->pFramebuffer轉換成圖像數據,然后用比如vd->ptframe[0]指向之。*/ headerframe=(struct frame_t*)vd->ptframe[vd->frame_cour]; //真正的圖像幀格式headerframe指向轉換后的數據區snprintf(headerframe->header,5,"%s","2410"); headerframe->seqtimes = ms_time(); //讀取并轉換完畢,記下當前時間headerframe->deltatimes=(int)(headerframe->seqtimes-timecourant); //記錄從開始讀取數據到數據編碼完成所用時間headerframe->w = vd->hdrwidth;headerframe->h = vd->hdrheight;headerframe->size = (( jpegsize < 0)?0:jpegsize);; headerframe->format = vd->formatIn; headerframe->nbframe = frame++; DBG("compress frame %d times %f\n",frame, headerframe->seqtimes-temps); /* 打印出來的是轉換時間,不包括從驅動讀取數據用去的時間。所以比較均勻。 這個就是終端里一直打印出來的,比如 次數 所用時間msDBG(s3c2410.c, s3c2410_Grab(), 172): compress frame 34599 times 175.409058DBG(s3c2410.c, s3c2410_Grab(), 172): compress frame 34600 times 176.015015DBG(s3c2410.c, s3c2410_Grab(), 172): compress frame 34601 times 175.151001DBG(s3c2410.c, s3c2410_Grab(), 172): compress frame 34602 times 175.377930*/vd->frame_cour = (vd->frame_cour +1) % OUTFRMNUMB; //#define OUTFRMNUMB 1 /* 當前幀索引+1, 可以看出作者是每次從驅動中讀取一個原始數據幀放在vd->pFramebuffer,將數據轉換后使用當前幀vd->ptframe[vd->frame_cour]指向,而每讀取一幀則vd->frame_cour++。 由于#define OUTFRMNUMB 1,所以vd->ptframe[vd->frame_cour]總是vd->ptframe[0]. 而headerframe是本函數內的局部變量,總是指向vd->ptframe[0]。記錄轉換后的圖像信息可以打印出來以供調試參考 在s3c2410_Grab()返回點cam_thread()里面也有一個局部變量headerframe,也是指向vd->ptframe[0],但也不重要。重要的是那個pictureData 是全局global變量的buf和size的源頭活水,見cam_thread(). */pthread_mutex_unlock (&vd->grabmutex); /************************************/return jpegsize; }
在plugins/input_s3c2410/s3c2410.c,
int convertframe(unsigned char *dst,unsigned char *src, int width,int height, int formatIn, int qualite,int buf_size) { int ret=0;//unsigned char *tmp=malloc(width*height*2);RGB565_2_YCbCr420(src,src,width,height); //inplace conversion //見下面 ret=s_encode_image(src,dst,qualite,FORMAT_CbCr420,width,height,buf_size); //圖像編碼,見下面//free(tmp);return ret; }
在根目錄的simplified_jpeg_encoder.c,
/* translate RGB565 to YUV420 in input */ void RGB565_2_YCbCr420(uint8_t * input_ptr, uint8_t * output_ptr, uint32_t image_width,uint32_t image_height) {uint32_t i, j, size;uint8_t R, G, B, R1, G1, B1, Rd, Gd, Bd, Rd1, Gd1, Bd1;S_INT Y, Yd, Y11, Yd1, Cb, Cr;S_JPEG_RGB16 * inbuf = (S_JPEG_RGB16 *) input_ptr;S_JPEG_RGB16 * inbuf1 = inbuf + (image_width);size = image_width * image_height >> 2;for (i = size, j = 0; i > 0; i--){B = inbuf[0].blue << 3;G = inbuf[0].green << 2;R = inbuf[0].red << 3;B1 = inbuf[1].blue << 3;G1 = inbuf[1].green << 2;R1 = inbuf[1].red << 3;Bd = inbuf1[0].blue << 3;Gd = inbuf1[0].green << 2;Rd = inbuf1[0].red << 3;Bd1 = inbuf1[1].blue << 3;Gd1 = inbuf[1].green << 2;Rd1 = inbuf[1].red << 3;inbuf += 2;inbuf1 += 2;j++;if (j >= image_width / 2) {j = 0;inbuf += (image_width);inbuf1 += (image_width);}Y = CLIP((77 * R + 150 * G + 29 * B) >> 8);Y11 = CLIP((77 * R1 + 150 * G1 + 29 * B1) >> 8);Yd = CLIP((77 * Rd + 150 * Gd + 29 * Bd) >> 8);Yd1 = CLIP((77 * Rd1 + 150 * Gd1 + 29 * Bd1) >> 8);Cb = CLIP(((-43 * R - 85 * G + 128 * B) >> 8) + 128);Cr = CLIP(((128 * R - 107 * G - 21 * B) >> 8) + 128);*output_ptr++ = (uint8_t) Y;*output_ptr++ = (uint8_t) Y11;*output_ptr++ = (uint8_t) Yd;*output_ptr++ = (uint8_t) Yd1;*output_ptr++ = (uint8_t) Cb;*output_ptr++ = (uint8_t) Cr;} }
在根目錄的simplified_jpeg_encoder.c,
uint32_t s_encode_image(uint8_t * input_ptr, uint8_t * output_ptr,uint32_t quality_factor, int image_format,uint32_t image_width, uint32_t image_height,uint32_t output_buffer_size) {S_UINT i, j;S_UINT last_col;S_UINT last_row;uint8_t * output;S_JPEG_ENCODER_STRUCTURE JpegStruct;S_JPEG_ENCODER_STRUCTURE * enc = &JpegStruct;/*(S_JPEG_ENCODER_STRUCTURE *)malloc(sizeof(S_JPEG_ENCODER_STRUCTURE));memset(enc,0,sizeof(S_JPEG_ENCODER_STRUCTURE));*/output = output_ptr;/* Initialization of JPEG control structure */initialization(enc, image_format, image_width, image_height);/* Quantization Table Initialization */initialize_quantization_tables(enc,quality_factor);/* Writing Marker Data */output_ptr = write_markers(enc,output_ptr, image_format, image_width, image_height);last_row=enc->vertical_mcus-1;last_col=enc->horizontal_mcus-1;for (i = 0; i < enc->vertical_mcus; i++){if (i < last_row)enc->rows = enc->mcu_height;elseenc->rows = enc->rows_in_bottom_mcus;for (j = 0; j < enc->horizontal_mcus; j++){if (j < last_col){enc->cols = enc->mcu_width;enc->scan_line_incr = enc->length_minus_mcu_width;} else {enc->cols = enc->cols_in_right_mcus;enc->scan_line_incr = enc->length_minus_width;}enc->read_format(enc, input_ptr,i,j);/* Encode the data in MCU */output_ptr = encodeMCU(enc, image_format, output_ptr);//input_ptr += enc->mcu_width_size;}//input_ptr += enc->mcu_line_offset;}/* Close Routine */output_ptr = close_bitstream(enc,output_ptr);//free(enc);return (uint32_t)(output_ptr - output); }
轉載于:https://www.cnblogs.com/-song/archive/2011/11/25/3331929.html
總結
以上是生活随笔為你收集整理的网络摄像头3 cmos ov9650,plugins/input_s3c2410/的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mongodb指南(翻译)(一) - 翻
- 下一篇: 网络摄像头4 cmos 0v9650,d