input_uvc.c源码分析 ----------------------------------------------------------------------------------------------------------------------- 分析input_uvc.c主要分析四个函数: input_init() input_stop() input_run() input_cmd() ----------------------------------------------------------------------------------------------------------------------- 现在来分析一下input_init()函数: 首先解析命令:将param->parameter_string字符串形式的命令转换为一个argv[MAX_ARGUMENTS]字符串数组; 其次就是用getopt_long_only()解析命令; 保留指向global全局变量的指针: pglobal = param->global; 分配一个webcam的结构体: videoIn = malloc(sizeof(struct vdIn)); struct vdIn { int fd; char *videodevice; char *status; char *pictName; struct v4l2_capability cap; struct v4l2_format fmt; struct v4l2_buffer buf; struct v4l2_requestbuffers rb; void *mem[NB_BUFFER]; unsigned char *tmpbuffer; unsigned char *framebuffer; int isstreaming; int grabmethod; int width; int height; int fps; int formatIn; int formatOut; int framesizeIn; int signalquit; int toggleAvi; int getPict; int rawFrameCapture; /* raw frame capture */ unsigned int fileCounter; /* raw frame stream capture */ unsigned int rfsFramesWritten; unsigned int rfsBytesWritten; /* raw stream capture */ FILE *captureFile; unsigned int framesWritten; unsigned int bytesWritten; int framecount; int recordstart; int recordtime; }; 结构体成员清零: memset(videoIn, 0, sizeof(struct vdIn)); 显示我们设置的参数: 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); 初始化videoIn结构体: init_videoIn(videoIn, dev, width, height, fps, format, 1); int init_videoIn(struct vdIn *vd, char *device, int width, int height, int fps, int format, int grabmethod) { if (vd == NULL || device == NULL) return -1; if (width == 0 || height == 0) return -1; if (grabmethod < 0 || grabmethod > 1) grabmethod = 1; //mmap by default; vd->videodevice = NULL; vd->status = NULL; vd->pictName = NULL; vd->videodevice = (char *) calloc (1, 16 * sizeof (char)); /* calloc分配并初始化为零 */ vd->status = (char *) calloc (1, 100 * sizeof (char)); vd->pictName = (char *) calloc (1, 80 * sizeof (char)); snprintf (vd->videodevice, 12, "%s", device); vd->toggleAvi = 0; vd->getPict = 0; vd->signalquit = 1; vd->width = width; vd->height = height; vd->fps = fps; vd->formatIn = format; vd->grabmethod = grabmethod; if (init_v4l2 (vd) < 0) { fprintf (stderr, " Init v4L2 failed !! exit fatal \n"); goto error;; } /* alloc a temp buffer to reconstruct the pict */ vd->framesizeIn = (vd->width * vd->height << 1); switch (vd->formatIn) { case V4L2_PIX_FMT_MJPEG: vd->tmpbuffer = (unsigned char *) calloc(1, (size_t) vd->framesizeIn); if (!vd->tmpbuffer) goto error; vd->framebuffer = (unsigned char *) calloc(1, (size_t) vd->width * (vd->height + 8) * 2); break; case V4L2_PIX_FMT_YUYV: vd->framebuffer = (unsigned char *) calloc(1, (size_t) vd->framesizeIn); break; default: fprintf(stderr, " should never arrive exit fatal !!\n"); goto error; break; } if (!vd->framebuffer) goto error; return 0; error: free(vd->videodevice); free(vd->status); free(vd->pictName); close(vd->fd); return -1; } 对摄像头的调整: if (dynctrls) initDynCtrls(videoIn->fd); 最后执行input_cmd函数: input_cmd(led, 0); 到此,inpu_init()函数结束,这个函数太长了~~~~~~~~~~ ----------------------------------------------------------------------------------------------------------------------- 现在开始分析input_cmd()函数: int input_cmd(in_cmd_type cmd, int value) { int res=0; static int pan=0, tilt=0, pan_tilt_valid=-1; static int focus=-1; const int one_degree = ONE_DEGREE; /* certain commands do not need the mutex */ if ( cmd != IN_CMD_RESET_PAN_TILT_NO_MUTEX ) pthread_mutex_lock( &controls_mutex ); switch (cmd) { case IN_CMD_HELLO: fprintf(stderr, "Hello from input plugin\n"); break; case IN_CMD_RESET: DBG("about to reset all image controls to defaults\n"); res = v4l2ResetControl(videoIn, V4L2_CID_BRIGHTNESS); res |= v4l2ResetControl(videoIn, V4L2_CID_CONTRAST); res |= v4l2ResetControl(videoIn, V4L2_CID_SATURATION); res |= v4l2ResetControl(videoIn, V4L2_CID_GAIN); if ( res != 0 ) res = -1; break; case IN_CMD_RESET_PAN_TILT: case IN_CMD_RESET_PAN_TILT_NO_MUTEX: DBG("about to set pan/tilt to default position\n"); if ( uvcPanTilt(videoIn->fd, 0, 0, 3) != 0 ) { res = -1; break; } pan_tilt_valid = 1; pan = tilt = 0; sleep(4); break; case IN_CMD_PAN_SET: DBG("set pan to %d degrees\n", value); /* in order to calculate absolute positions we must check for initialized values */ if ( pan_tilt_valid != 1 ) { if ( input_cmd(IN_CMD_RESET_PAN_TILT_NO_MUTEX, 0) == -1 ) { res = -1; break; } } /* limit pan-value to min and max, multiply it with constant "one_degree" */ value = MIN(MAX(value*one_degree, MIN_PAN), MAX_PAN); /* calculate the relative degrees to move to the desired absolute pan-value */ if( (res = value - pan) == 0 ) { /* do not move if this would mean to move by 0 degrees */ res = pan/one_degree; break; } /* move it */ pan = value; uvcPanTilt(videoIn->fd, res, 0, 0); res = pan/one_degree; DBG("pan: %d\n", pan); break; case IN_CMD_PAN_PLUS: DBG("pan +\n"); if ( pan_tilt_valid != 1 ) { if ( input_cmd(IN_CMD_RESET_PAN_TILT_NO_MUTEX, 0) == -1 ) { res = -1; break; } } if ( (MAX_PAN) >= (pan+MIN_RES) ) { pan += MIN_RES; uvcPanTilt(videoIn->fd, MIN_RES, 0, 0); } res = pan/one_degree; DBG("pan: %d\n", pan); break; case IN_CMD_PAN_MINUS: DBG("pan -\n"); if ( pan_tilt_valid != 1 ) { if ( input_cmd(IN_CMD_RESET_PAN_TILT_NO_MUTEX, 0) == -1 ) { res = -1; break; } } if ( (MIN_PAN) <= (pan-MIN_RES) ) { pan -= MIN_RES; uvcPanTilt(videoIn->fd, -MIN_RES, 0, 0); } res = pan/one_degree; DBG("pan: %d\n", pan); break; case IN_CMD_TILT_SET: DBG("set tilt to %d degrees\n", value); if ( pan_tilt_valid != 1 ) { if ( input_cmd(IN_CMD_RESET_PAN_TILT_NO_MUTEX, 0) == -1 ) { res = -1; break; } } /* limit pan-value to min and max, multiply it with constant "one_degree" */ value = MIN(MAX(value*one_degree, MIN_TILT), MAX_TILT); /* calculate the relative degrees to move to the desired absolute pan-value */ if( (res = value - tilt) == 0 ) { /* do not move if this would mean to move by 0 degrees */ res = tilt/one_degree; break; } /* move it */ tilt = value; uvcPanTilt(videoIn->fd, 0, res, 0); res = tilt/one_degree; DBG("tilt: %d\n", tilt); break; case IN_CMD_TILT_PLUS: DBG("tilt +\n"); if ( pan_tilt_valid != 1 ) { if ( input_cmd(IN_CMD_RESET_PAN_TILT_NO_MUTEX, 0) == -1 ) { res = -1; break; } } if ( (MAX_TILT) >= (tilt+MIN_RES) ) { tilt += MIN_RES; uvcPanTilt(videoIn->fd, 0, MIN_RES, 0); } res = tilt/one_degree; DBG("tilt: %d\n", tilt); break; case IN_CMD_TILT_MINUS: DBG("tilt -\n"); if ( pan_tilt_valid != 1 ) { if ( input_cmd(IN_CMD_RESET_PAN_TILT_NO_MUTEX, 0) == -1 ) { res = -1; break; } } if ( (MIN_TILT) <= (tilt-MIN_RES) ) { tilt -= MIN_RES; uvcPanTilt(videoIn->fd, 0, -MIN_RES, 0); } res = tilt/one_degree; DBG("tilt: %d\n", tilt); break; case IN_CMD_SATURATION_PLUS: DBG("saturation + (%d)\n", v4l2GetControl (videoIn, V4L2_CID_SATURATION)); res = v4l2UpControl(videoIn, V4L2_CID_SATURATION); break; case IN_CMD_SATURATION_MINUS: DBG("saturation - (%d)\n", v4l2GetControl (videoIn, V4L2_CID_SATURATION)); res = v4l2DownControl(videoIn, V4L2_CID_SATURATION); break; case IN_CMD_CONTRAST_PLUS: DBG("contrast + (%d)\n", v4l2GetControl (videoIn, V4L2_CID_CONTRAST)); res = v4l2UpControl(videoIn, V4L2_CID_CONTRAST); break; case IN_CMD_CONTRAST_MINUS: DBG("contrast - (%d)\n", v4l2GetControl (videoIn, V4L2_CID_CONTRAST)); res = v4l2DownControl(videoIn, V4L2_CID_CONTRAST); break; case IN_CMD_BRIGHTNESS_PLUS: DBG("brightness + (%d)\n", v4l2GetControl (videoIn, V4L2_CID_BRIGHTNESS)); res = v4l2UpControl(videoIn, V4L2_CID_BRIGHTNESS); break; case IN_CMD_BRIGHTNESS_MINUS: DBG("brightness - (%d)\n", v4l2GetControl (videoIn, V4L2_CID_BRIGHTNESS)); res = v4l2DownControl(videoIn, V4L2_CID_BRIGHTNESS); break; case IN_CMD_GAIN_PLUS: DBG("gain + (%d)\n", v4l2GetControl (videoIn, V4L2_CID_GAIN)); res = v4l2UpControl(videoIn, V4L2_CID_GAIN); break; case IN_CMD_GAIN_MINUS: DBG("gain - (%d)\n", v4l2GetControl (videoIn, V4L2_CID_GAIN)); res = v4l2DownControl(videoIn, V4L2_CID_GAIN); break; case IN_CMD_FOCUS_PLUS: DBG("focus + (%d)\n", focus); value=MIN(MAX(focus+10,0),255); if ( (res = v4l2SetControl(videoIn, V4L2_CID_FOCUS_LOGITECH, value)) == 0) { focus = value; } res = focus; break; case IN_CMD_FOCUS_MINUS: DBG("focus - (%d)\n", focus); value=MIN(MAX(focus-10,0),255); if ( (res = v4l2SetControl(videoIn, V4L2_CID_FOCUS_LOGITECH, value)) == 0) { focus = value; } res = focus; break; case IN_CMD_FOCUS_SET: value=MIN(MAX(value,0),255); DBG("set focus to %d\n", value); if ( (res = v4l2SetControl(videoIn, V4L2_CID_FOCUS_LOGITECH, value)) == 0) { focus = value; } res = focus; break; /* switch the webcam LED permanently on */ case IN_CMD_LED_ON: res = v4l2SetControl(videoIn, V4L2_CID_LED1_MODE_LOGITECH, 1); break; /* switch the webcam LED permanently off */ case IN_CMD_LED_OFF: res = v4l2SetControl(videoIn, V4L2_CID_LED1_MODE_LOGITECH, 0); break; /* switch the webcam LED on if streaming, off if not streaming */ case IN_CMD_LED_AUTO: res = v4l2SetControl(videoIn, V4L2_CID_LED1_MODE_LOGITECH, 3); break; /* let the webcam LED blink at a given hardcoded intervall */ case IN_CMD_LED_BLINK: res = v4l2SetControl(videoIn, V4L2_CID_LED1_MODE_LOGITECH, 2); res = v4l2SetControl(videoIn, V4L2_CID_LED1_FREQUENCY_LOGITECH, 255); break; default: DBG("nothing matched\n"); res = -1; } if ( cmd != IN_CMD_RESET_PAN_TILT_NO_MUTEX ) pthread_mutex_unlock( &controls_mutex ); return res; } 其中用到了v4l2ucv.c中的函数: int v4l2SetControl(struct vdIn *vd, int control, int value) { struct v4l2_control control_s; struct v4l2_queryctrl queryctrl; int min, max, step, val_def; int err; if (isv4l2Control(vd, control, &queryctrl) < 0) return -1; min = queryctrl.minimum; max = queryctrl.maximum; step = queryctrl.step; val_def = queryctrl.default_value; if ((value >= min) && (value <= max)) { control_s.id = control; control_s.value = value; if ((err = ioctl(vd->fd, VIDIOC_S_CTRL, &control_s)) < 0) { return -1; } } return 0; } ----------------------------------------------------------------------------------------------------------------------- 好,现在来分析input_run()函数: 哈哈,这个函数最简单~~~~~~~~~ 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); /* 创建cam线程 */ pthread_detach(cam); /* 将线程与父线程分离 */ return 0; } ----------------------------------------------------------------------------------------------------------------------- cma_thread()函数中用到的几个函数: int uvcGrab(struct vdIn *vd) /* 抓取函数 */ { #define HEADERFRAME1 0xaf int ret; if (!vd->isstreaming) if (video_enable(vd)) goto err; memset(&vd->buf, 0, sizeof(struct v4l2_buffer)); vd->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; vd->buf.memory = V4L2_MEMORY_MMAP; ret = ioctl(vd->fd, VIDIOC_DQBUF, &vd->buf); /* 清除缓冲区的内容 */ switch (vd->formatIn) { case V4L2_PIX_FMT_MJPEG: if (vd->buf.bytesused <= HEADERFRAME1) { /* Prevent crashon 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); else memcpy (vd->framebuffer, vd->mem[vd->buf.index], (size_t) vd->buf.bytesused); break; default: goto err; break; } ret = ioctl(vd->fd, VIDIOC_QBUF, &vd->buf); if (ret < 0) { perror("Unable to requeue buffer"); goto err; } return 0; err: vd->signalquit = 0; return -1; } int memcpy_picture(unsigned char *out, unsigned char *buf, int size) /* 复制jpeg格式的图片到pglobal->buf */ { unsigned char *ptdeb, *ptlimit, *ptcur = buf; int sizein, pos=0; if (!is_huffman(buf)) { ptdeb = ptcur = buf; ptlimit = buf + size; while ((((ptcur[0] << 8) | ptcur[1]) != 0xffc0) && (ptcur < ptlimit)) ptcur++; if (ptcur >= ptlimit) return pos; sizein = ptcur - ptdeb; memcpy(out+pos, buf, sizein); pos += sizein; memcpy(out+pos, dht_data, sizeof(dht_data)); pos += sizeof(dht_data); memcpy(out+pos, ptcur, size - sizein); pos += size-sizein; } else { memcpy(out+pos, ptcur, size); pos += size; } return pos; } ----------------------------------------------------------------------------------------------------------------------- 现在分析一下cma_thread()函数,这个线程很重要哦!!!!!!该函数的作用是抓取一帧的图像,并复制到全局缓冲区 void *cam_thread( void *arg ) { /* set cleanup handler to cleanup allocated ressources */ pthread_cleanup_push(cam_cleanup, NULL); /* 注册清除处理函数cam_cleanup() */ while( !pglobal->stop ) { /* grab a frame */ if( uvcGrab(videoIn) < 0 ) { /* 在videoIn结构体中抓取一个帧图像 */ IPRINT("Error grabbing frames\n"); exit(EXIT_FAILURE); } DBG("received frame of size: %d\n", videoIn->buf.bytesused); /* * Workaround for broken, corrupted frames: * Under low light conditions corrupted frames may get captured. * The good thing is such frames are quite small compared to the regular pictures. * For example a VGA (640x480) webcam picture is normally >= 8kByte large, * corrupted frames are smaller. */ if ( videoIn->buf.bytesused < minimum_size ) { DBG("dropping too small frame, assuming it as broken\n"); continue; } /* copy JPG picture to global buffer */ pthread_mutex_lock( &pglobal->db ); /* * 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); } #if 0 /* motion detection can be done just by comparing the picture size, but it is not very accurate!! */ if ( (prev_size - global->size)*(prev_size - global->size) > 4*1024*1024 ) { DBG("motion detected (delta: %d kB)\n", (prev_size - global->size) / 1024); } prev_size = global->size; #endif /* signal fresh_frame */ pthread_cond_broadcast(&pglobal->db_update); pthread_mutex_unlock( &pglobal->db ); DBG("waiting for next frame\n"); /* only use usleep if the fps is below 5, otherwise the overhead is too long */ if ( videoIn->fps < 5 ) { usleep(1000*1000/videoIn->fps); } } DBG("leaving input thread, calling cleanup function now\n"); pthread_cleanup_pop(1); return NULL; } ----------------------------------------------------------------------------------------------------------------------- |
|
来自: champion_xu > 《Mjpegstream》