unsigned char* b) // YUV是被欧洲电视系统所采用的一种颜色编码方法(属于PAL) { const int y2=(int)y; const int u2=(int)u-128; const int v2=(int)v-128; //std::cerr << \ // This is the normal YUV conversion, but // appears to be incorrect for the firewire cameras // int r2 = y2 + ( (v2*91947) >> 16); // int g2 = y2 - ( ((u2*22544) + (v2*46793)) >> 16 ); // int b2 = y2 + ( (u2*115999) >> 16); // This is an adjusted version (UV spread out a bit) int r2 = y2 + ( (v2*37221) >> 15); int g2 = y2 - ( ((u2*12975) + (v2*18949)) >> 15 ); int b2 = y2 + ( (u2*66883) >> 15); //std::cerr << \ RGB=(\ // Cap the values. *r=CLIPVALUE(r2); *g=CLIPVALUE(g2); *b=CLIPVALUE(b2); }
void uyvy2rgb (char *YUV, char *RGB, int NumPixels) //uyvy是YUV的一种格式 { int i, j; unsigned char y0, y1, u, v; unsigned char r, g, b; for (i = 0, j = 0; i < (NumPixels << 1); i += 4, j += 6) { u = (unsigned char) YUV[i + 0]; //uyvy: 用4个量表示rgb的6个量 y0 = (unsigned char) YUV[i + 1]; v = (unsigned char) YUV[i + 2]; y1 = (unsigned char) YUV[i + 3]; YUV2RGB (y0, u, v, &r, &g, &b); RGB[j + 0] = r; RGB[j + 1] = g; RGB[j + 2] = b; YUV2RGB (y1, u, v, &r, &g, &b); RGB[j + 3] = r; RGB[j + 4] = g; RGB[j + 5] = b; } }
static void yuyv2rgb(char *YUV, char *RGB, int NumPixels) //yuyv是YUV的一种格式 { int i, j; unsigned char y0, y1, u, v; unsigned char r, g, b; for (i = 0, j = 0; i < (NumPixels << 1); i += 4, j += 6) { y0 = (unsigned char) YUV[i + 0]; //yuyv: 用4个量表示rgb的6个量 u = (unsigned char) YUV[i + 1]; y1 = (unsigned char) YUV[i + 2]; v = (unsigned char) YUV[i + 3]; YUV2RGB (y0, u, v, &r, &g, &b); RGB[j + 0] = r; RGB[j + 1] = g; RGB[j + 2] = b;
31
YUV2RGB (y1, u, v, &r, &g, &b); RGB[j + 3] = r; RGB[j + 4] = g; RGB[j + 5] = b; } }
static int init_mjpeg_decoder(int image_width, int image_height) { avcodec_register_all(); //注册编解码器等的函数, //该函数在所有基于ffmpeg的应用程序中几乎都是第一个被调用的。只
有调用了该函数,才能使用编解码器等 //哪个头文件里的??? avcodec = avcodec_find_decoder(CODEC_ID_MJPEG); //根据编解码器的ID: CODEC_ID_MJPEG来寻找已经注册了的解
码器 // static AVCodec *avcodec = NULL if (!avcodec) //若没找到解码器 { ROS_ERROR(\); return 0; } avcodec_context = avcodec_alloc_context3(avcodec); // static AVCodecContext * avcodec_context = NULL avframe_camera = avcodec_alloc_frame(); //分配一个帧指针, 指向解码后的原始帧 // static AVFrame *avframe_camera =
NULL; //AVFrame是数据流在编解码过程中用来保存数据缓存的 avframe_rgb = avcodec_alloc_frame();//分配一个帧指针,指向存放转换成rgb后的帧? // static AVFrame *avframe_rgb =
NULL avpicture_alloc((AVPicture *)avframe_rgb, PIX_FMT_RGB24, image_width, image_height); //指定的目标格式为
PIX_FMT_RGB24 avcodec_context->codec_id = CODEC_ID_MJPEG; //编解码器的ID: CODEC_ID_MJPEG avcodec_context->width = image_width; avcodec_context->height = image_height;
#if LIBAVCODEC_VERSION_MAJOR > 52 avcodec_context->pix_fmt = PIX_FMT_YUV422P; avcodec_context->codec_type = AVMEDIA_TYPE_VIDEO; //编解码器的类型 #endif avframe_camera_size = avpicture_get_size(PIX_FMT_YUV422P, image_width, image_height);//对于给定的图片格式以及宽
和高, 计算所需占用多少内存 avframe_rgb_size = avpicture_get_size(PIX_FMT_RGB24, image_width, image_height); /* open it */ if (avcodec_open2(avcodec_context, avcodec, &avoptions) < 0) //打开解码器avcodec, 根据avcodec_context来设置解码器的
各项参数 // static AVDictionary *avoptions = NULL; 这一变量没什么用, 没必要定义 { ROS_ERROR(\); return 0; } return 1; }
static void mjpeg2rgb(char *MJPEG, int len, char *RGB, int NumPixels) { int got_picture; memset(RGB, 0, avframe_rgb_size);
#if LIBAVCODEC_VERSION_MAJOR > 52 int decoded_len;
32
AVPacket avpkt; av_init_packet(&avpkt); avpkt.size = len; avpkt.data = (unsigned char*)MJPEG; decoded_len = avcodec_decode_video2(avcodec_context, avframe_camera, &got_picture, &avpkt);//解码后的数据保存在avframe_camera中 if (decoded_len < 0) { ROS_ERROR(\); return; } #else avcodec_decode_video(avcodec_context, avframe_camera, &got_picture, (uint8_t *) MJPEG, len); #endif if (!got_picture) { ROS_ERROR(\); return; } int xsize = avcodec_context->width; int ysize = avcodec_context->height; int pic_size = avpicture_get_size(avcodec_context->pix_fmt, xsize, ysize); //对于给定的图片格式以及宽和高, 计算所需占
用多少内存 if (pic_size != avframe_camera_size) { ROS_ERROR(\ pic_size: %d bufsize: %d\\n\,pic_size, avframe_camera_size); return; } video_sws = sws_getContext( xsize, ysize, avcodec_context->pix_fmt, xsize, ysize, PIX_FMT_RGB24, SWS_BILINEAR,
NULL, NULL, NULL); //格式转换的初始化 //struct SwsContext *video_sws = NULL; //前三个参数分别代表 source 的宽、高及PixelFormat; 四到六个参数分别代表 destination 的宽、高及PixelFormat; 第七个参数则代表要使用哪种scale的方法 sws_scale(video_sws, avframe_camera->data, avframe_camera->linesize, 0, ysize, avframe_rgb->data, vframe_rgb->linesize ); //将某个PixelFormat转换至另一个PixelFormat //第一个参数即是由sws_getContext 所取得的参数。 //第二个 src 及第六个
dst 分别指向input 和 output 的 buffer。 //第三个 srcStride 及第七个 dstStride 分别指向 input 及 output 的 stride;如果不知道什麼是 stride,姑且可以先把它看成是每一列的 byte 数。 //第四个 srcSliceY,就注解的意思来看,是指第一列要处理的位置;这里我是从头处理,所以直接填0。想知道更详细说明的人,可以参考 swscale.h 的注解。//第五个srcSliceH指的是 source slice 的高度 sws_freeContext(video_sws); //结束 int size = avpicture_layout((AVPicture *) avframe_rgb, PIX_FMT_RGB24, xsize, ysize, (uint8_t *)RGB, avframe_rgb_size); if (size != avframe_rgb_size) { ROS_ERROR(\,size); return; } }
static void process_image(const void * src, int len, usb_cam_camera_image_t *dest) { if(pixelformat==V4L2_PIX_FMT_YUYV) yuyv2rgb((char*)src, dest->image, dest->width*dest->height); else if(pixelformat==V4L2_PIX_FMT_UYVY) uyvy2rgb((char*)src, dest->image, dest->width*dest->height); else if(pixelformat==V4L2_PIX_FMT_MJPEG) mjpeg2rgb((char*)src, len, dest->image, dest->width*dest->height); }
static int read_frame(usb_cam_camera_image_t *image) //从视频设备获取图像数据//将图像数据放到usb_cam_camera_image_t
结果体中
33
{
struct v4l2_buffer buf; //代表驱动中的一帧 unsigned int i; int len;
switch (io) {
case IO_METHOD_READ: len = read(fd, buffers[0].start, buffers[0].length); //关于IO_METHOD_READ, 没有图像缓存?? if (len==-1) { switch (errno) { case EAGAIN: return 0; case EIO: /* Could ignore EIO, see spec. */ // /* fall through */ default: errno_exit(\); } } process_image(buffers[0].start, len, image); //分配内存时, 得需要考虑图像压缩比例吧??????????????????????? break; case IO_METHOD_MMAP: CLEAR (buf); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP;
if (-1==xioctl(fd, VIDIOC_DQBUF, &buf)) {//已经暗含从设备中获取图像数据了 //出队列以取得已采集数据的帧缓冲,取得原始采集数据: VIDIOC_DQBUF; //VIDIOC_DQBUF:把数据放回缓存队列 switch (errno) { case EAGAIN: return 0; case EIO: /* Could ignore EIO, see spec. */ /* fall through */ default: errno_exit(\); } }
assert (buf.index < n_buffers); len = buf.bytesused;
process_image(buffers[buf.index].start, len, image);
if (-1==xioctl(fd, VIDIOC_QBUF, &buf)) //重新放入缓存队列 //将缓冲重新入队列尾,这样可以循环采集:
VIDIOC_QBUF //还是: 将申请到的帧缓冲全部入队列,以便存放采集到的数据.VIDIOC_QBUF,struct v4l2_buffer? //VIDIOC_QBUF:把数据从缓存中读取出来 ; //
//在每次用 VIDIOC_DQBUF 取出一个 buffer ,并且处理完数据后,一定要用 VIDIOC_QBUF 将这个 buffer 再次放回到
环形缓冲队列中
// VIDIOC_DQBUF与VIDIOC_QBUF是不是给buffer加锁与解锁的过程, 为了保证在复制图像数据前原始图像数据不变? errno_exit(\); break; case IO_METHOD_USERPTR: CLEAR (buf); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_USERPTR; if (-1==xioctl(fd, VIDIOC_DQBUF, &buf)) { // switch (errno) { case EAGAIN:
34
return 0; case EIO: /* Could ignore EIO, see spec. */ /* fall through */ default: errno_exit(\); } } for(i = 0; i static void stop_capturing(void) //停止视频的采集 { enum v4l2_buf_type type; switch (io) { case IO_METHOD_READ: /* Nothing to do. */ break; case IO_METHOD_MMAP: case IO_METHOD_USERPTR: type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (-1==xioctl(fd, VIDIOC_STREAMOFF, &type)) //停止视频的采集: VIDIOC_STREAMOFF errno_exit(\); break; } } // VIDIOC_QBUF, VIDIOC_STREAMON static void start_capturing(void) //对已关联的设备缓存与内存缓冲区进行相关设置??? { unsigned int i; enum v4l2_buf_type type; switch (io) { case IO_METHOD_READ: /* Nothing to do. */ break; case IO_METHOD_MMAP: for(i = 0; i 35