input_uvc.c源码分析
-----------------------------------------------------------------------------------------------------------------------
重要函数解析:
char *strtok_r(char *str, const char *delim, char **saveptr);
与线程相关的函数:
线程可以安排它推出时需要调用的函数,这与进程可以用atexit函数安排进程退出时需要调用的函数是类似的.
这样的函数称为线程清理处理程序,线程可以建立多个清理处理程序.处理程序记录在栈内,也就是说它们的调
用顺序与它们的注册顺序相反
#include
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
int pthread_detach(pthread_t thread);
void pthread_cleanup_push(void (*routine)(void *),void *arg);
void pthread_cleanup_pop(int execute);
-----------------------------------------------------------------------------------------------------------------------
分析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
", dev);
IPRINT("Desired Resolution: %i x %i
", width, height);
IPRINT("Frames Per Second.: %i
", fps);
IPRINT("Format............: %s
", (format==V4L2_PIX_FMT_YUYV)?"YUV":"MJPEG");
if ( format == V4L2_PIX_FMT_YUYV )
IPRINT("JPEG Quality......: %d
", 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
");
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 !!
");
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
");
break;
case IN_CMD_RESET:
DBG("about to reset all image controls to defaults
");
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
");
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
", 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
", pan);
break;
case IN_CMD_PAN_PLUS:
DBG("pan +
");
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
", pan);
break;
case IN_CMD_PAN_MINUS:
DBG("pan -
");
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
", pan);
break;
case IN_CMD_TILT_SET:
DBG("set tilt to %d degrees
", 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
", tilt);
break;
case IN_CMD_TILT_PLUS:
DBG("tilt +
");
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
", tilt);
break;
case IN_CMD_TILT_MINUS:
DBG("tilt -
");
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
", tilt);
break;
case IN_CMD_SATURATION_PLUS:
DBG("saturation + (%d)
", v4l2GetControl (videoIn, V4L2_CID_SATURATION));
res = v4l2UpControl(videoIn, V4L2_CID_SATURATION);
break;
case IN_CMD_SATURATION_MINUS:
DBG("saturation - (%d)
", v4l2GetControl (videoIn, V4L2_CID_SATURATION));
res = v4l2DownControl(videoIn, V4L2_CID_SATURATION);
break;
case IN_CMD_CONTRAST_PLUS:
DBG("contrast + (%d)
", v4l2GetControl (videoIn, V4L2_CID_CONTRAST));
res = v4l2UpControl(videoIn, V4L2_CID_CONTRAST);
break;
case IN_CMD_CONTRAST_MINUS:
DBG("contrast - (%d)
", v4l2GetControl (videoIn, V4L2_CID_CONTRAST));
res = v4l2DownControl(videoIn, V4L2_CID_CONTRAST);
break;
case IN_CMD_BRIGHTNESS_PLUS:
DBG("brightness + (%d)
", v4l2GetControl (videoIn, V4L2_CID_BRIGHTNESS));
res = v4l2UpControl(videoIn, V4L2_CID_BRIGHTNESS);
break;
case IN_CMD_BRIGHTNESS_MINUS:
DBG("brightness - (%d)
", v4l2GetControl (videoIn, V4L2_CID_BRIGHTNESS));
res = v4l2DownControl(videoIn, V4L2_CID_BRIGHTNESS);
break;
case IN_CMD_GAIN_PLUS:
DBG("gain + (%d)
", v4l2GetControl (videoIn, V4L2_CID_GAIN));
res = v4l2UpControl(videoIn, V4L2_CID_GAIN);
break;
case IN_CMD_GAIN_MINUS:
DBG("gain - (%d)
", v4l2GetControl (videoIn, V4L2_CID_GAIN));
res = v4l2DownControl(videoIn, V4L2_CID_GAIN);
break;
case IN_CMD_FOCUS_PLUS:
DBG("focus + (%d)
", 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)
", 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
", 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
");
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
");
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 ...
");
return 0;
}
memcpy(vd->tmpbuffer, vd->mem[vd->buf.index], vd->buf.bytesused);
if (debug)
fprintf(stderr, "bytes in used %d
", 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 */
p