本帖最后由 代祥军 于 2015-4-16 09:13 编辑
1.准备 涉及的知识
程序调用流程: main.c: usage->init_sock/*判断参数,初始化套接字*/ /*初始化V4L2*/ start_engine -->cam_init /*给struct cam 分配内存,并且初始化cam结构体*/ --> v4l2_init -->open_camera /*打开摄像头*/ -->init_camera /*初始化摄像头*/ -->start_capturing /*开始采集图像*/ -->create_cam_pthread -->pthread_create:capture_encode_thread 创建线程 -->capture_encode_thread -->while: 循环 -->read_frame 读一帧数据 --> jpeg_rtp 如果采集的数据时jpeg直接rtp打包发送 -->send_jpeg_rtp_for_fmt_mjpeg 设rtp某些动态参数 -->SendFrame rtp打包 socket发送VLC -->jpeg_encode_yuyv422_rtp 如果采集的视频数据时yuyv先转化为jepg在rtp打包发送 -->yuyv422torgb 将yuyv数据装化为RGB -->encode_rgb_to_jpeg_mem RGB转化为jpeg放到内存 -->send_jpeg_rtp 设置rtp参数 -->SendFrame rtp打包数据发送 当按下ctrl+c时:stop_engine() engine.c:
2.V4L2采集视频数据 video_capture.c 涉及的结构体:
- struct buffer{
- void *start;
- size_t length;
- };
- struct camera{
- char *device_name;//设备名字 /dev/videoX
- int fd;//摄像头句柄
- int width;//输出的视频宽度
- int height;//高度
- int display_depth;
- int image_size;//一帧数据大小
- int frame_number;
- int support_fmt;//摄像头支持的格式如果支持Jpeg就以此获得数据,如果只支持yuyv就输出yuyv在用程序转化为jpeg
- unsigned int n_buffers;//缓冲区个数
- struct v4l2_capability v4l2_cap;
- struct v4l2_cropcap v4l2_cropcap;
- struct v4l2_format v4l2_fmt;
- struct v4l2_crop v4l2_crop;
- struct v4l2_fmtdesc v4l2_fmtdesc;
- struct v4l2_streamparm v4l2_setfps;
- struct buffer *buffers;
- };
- /*上面参数fmt_select选择*/
- #define FMT_JPEG 0x101
- #define FMT_YUYV422 0x102
- #define FMT_YUYV420 0x104
复制代码
涉及的函数:
- /*外界接口*/
- void v4l2_init(struct camera *cam)
- {
- open_camera(cam);
- init_camera(cam);
- start_capturing(cam);
- }
复制代码
2.1 open_camera 打开摄像头
- /*摄像头操作函数*/
- static void open_camera(struct camera *cam)
- {
- struct stat st;
- if (-1 == stat(cam->device_name, &st)) {
- fprintf(stderr, "Cannot identify '%s': %d, %s\n", cam->device_name,errno, strerror(errno));
- exit(EXIT_FAILURE);
- }
- if (!S_ISCHR(st.st_mode)) {
- fprintf(stderr, "%s is no device\n", cam->device_name);
- exit(EXIT_FAILURE);
- }
- cam->fd = open(cam->device_name, O_RDWR, 0); // | O_NONBLOCK 打开摄像头 记录fd
- if (-1 == cam->fd) {
- fprintf(stderr, "Cannot open '%s': %d, %s\n", cam->device_name, errno,strerror(errno));
- exit(EXIT_FAILURE);
- }
- }
复制代码
2.2 init_camera 初始化摄像头
- static void init_camera(struct camera*cam)
- {
- struct v4l2_capability *cap = &(cam->v4l2_cap);
- struct v4l2_cropcap *cropcap = &(cam->v4l2_cropcap);
- struct v4l2_crop *crop = &(cam->v4l2_crop);
- struct v4l2_format *fmt = &(cam->v4l2_fmt);
- struct v4l2_fmtdesc *fmtdesc = &(cam->v4l2_fmtdesc);
- struct v4l2_streamparm *streamparm = &(cam->v4l2_setfps);
-
- struct v4l2_requestbuffers req;
- unsigned int min;
- int pos = -1;
- /*1.查询设备属性*/
- if (-1 == xioctl(cam->fd, VIDIOC_QUERYCAP, cap)) {
- if (EINVAL == errno) {
- fprintf(stderr, "%s is no V4L2 device\n", cam->device_name);
- exit(EXIT_FAILURE);
- }else
- errno_exit("VIDIOC_QUERYCAP");
- }
- /*2.是否支持图像获取*/
- if (!(cap->capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
- fprintf(stderr, "%s is no video capture device\n", cam->device_name);
- exit(EXIT_FAILURE);
- }
- /*3.查询设备是否支持流输出*/
- if (!(cap->capabilities & V4L2_CAP_STREAMING)) {
- fprintf(stderr, "%s does not support streaming i/o\n",cam->device_name);
- exit(EXIT_FAILURE);
- }
- #ifdef OUTPUT_CAMINFO
- printf("VIDOOC_QUERYCAP\n");
- printf("driver:\t\t%s\n",cap->driver);
- printf("card:\t\t%s\n",cap->card);
- printf("bus_info:\t%s\n",cap->bus_info);
- printf("version:\t%d\n",cap->version);
- printf("capabilities:\t%x\n",cap->capabilities);
- #endif
- /*打印摄像头支持的格式*/
- fmtdesc->index = 0;
- fmtdesc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- #ifdef OUTPUT_CAMINFO
- printf("support format:\n");
- #endif
- while(ioctl(cam->fd,VIDIOC_ENUM_FMT,fmtdesc) != -1){
- #ifdef OUTPUT_CAMINFO
- printf("\t%d,%s\n",fmtdesc->index+1,fmtdesc->description);
- #endif
- /*4.查看摄像头输出的 视频格式 jpeg yuyv420 yuyv422*/
- pos = find_string(fmtdesc->description,"JPEG");
- if(pos != -1){
- cam->support_fmt |= FMT_JPEG;
- goto CON;
- }
- pos = find_string(fmtdesc->description,"4:2:2");
- if(pos!=-1){
- cam->support_fmt |= FMT_YUYV422;
- goto CON;
- }
- pos = find_string(fmtdesc->description,"4:2:0");
- if(pos!=-1){
- cam->support_fmt |= FMT_YUYV420;
- goto CON;
- }
- CON:
- fmtdesc->index++;
- }
- /*5.设置输出格式*/
- CLEAR(*cropcap);
- cropcap->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- /*v4l2_cropcap 结构体用来设置摄像头的捕捉能力,在捕捉上视频时应先先设置v4l2_cropcap 的 type 域,再通过 VIDIO_CROPCAP 操作命令获取设备捕捉能力的参数,保存于 v4l2_cropcap 结构体中,包括 bounds(最大捕捉方框的左上角坐标和宽高),defrect(默认捕捉方框的左上角坐标和宽高)等。v4l2_format 结构体用来设置摄像头的视频制式、帧格式等,在设置这个参数时应先填 好 v4l2_format 的各个域,如 type(传输流类型),fmt.pix.width(宽),fmt.pix.heigth(高),fmt.pix.field(采样区域,如隔行采样),fmt.pix.pixelformat(采
- 样类型,如 YUV4:2:2),然后通过 VIDIO_S_FMT 操作命令设置视频捕捉格式*/
- crop->c.width = cam->width;
- crop->c.height = cam->height;
- crop->c.left = 0;
- crop->c.top = 0;
- crop->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- CLEAR(*fmt);
- fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- fmt->fmt.pix.width = cam->width;
- fmt->fmt.pix.height = cam->height;
- if( (cam->support_fmt & FMT_JPEG) == FMT_JPEG){
- fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
- cam->support_fmt = FMT_JPEG;/*记录设备输出的图像格式*/
- }
- else if((cam->support_fmt & FMT_YUYV422) == FMT_YUYV422){
- fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
- cam->support_fmt = FMT_YUYV422;
- }
- else if( (cam->support_fmt & FMT_YUYV420 ) == FMT_YUYV420 ){
- fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
- cam->support_fmt = FMT_YUYV420;
- }
- else
- errno_exit("no support fmt");
- fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;
-
- if (-1 == xioctl(cam->fd,VIDIOC_S_FMT,fmt))
- errno_exit("VIDIOC_S_FMT fail");
-
- #if 1
- CLEAR(*streamparm);
- streamparm->type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
- streamparm->parm.capture.timeperframe.numerator = 1;
- streamparm->parm.capture.timeperframe.denominator = 25;
- if(-1==xioctl(cam->fd,VIDIOC_S_PARM,streamparm))
- errno_exit("VIDIOC_S_PARM");
- #endif
- min = fmt->fmt.pix.width * 2;/*YUYV 每个像素占用2个字节*/
- if (fmt->fmt.pix.bytesperline < min)
- fmt->fmt.pix.bytesperline = min;
-
- min = fmt->fmt.pix.bytesperline * fmt->fmt.pix.height;
- if (fmt->fmt.pix.sizeimage < min)
- fmt->fmt.pix.sizeimage = min;
-
-
- /*6.分配内存,映射内存*/
- CLEAR(req);
- req.count = 4;
- req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- req.memory = V4L2_MEMORY_MMAP;
-
- if (-1 == xioctl(cam->fd, VIDIOC_REQBUFS, &req)) {
- if (EINVAL == errno) {
- fprintf(stderr, "%s does not support "
- "memory mapping\n", cam->device_name);
- exit(EXIT_FAILURE);
- } else
- errno_exit("VIDIOC_REQBUFS");
- }
- if (req.count < 2) {
- fprintf(stderr, "Insufficient buffer memory on %s\n", cam->device_name);
- exit(EXIT_FAILURE);
- }
- cam->buffers = calloc(req.count, sizeof(*(cam->buffers)));
- if (!cam->buffers) {
- fprintf(stderr, "Out of memory\n");
- exit(EXIT_FAILURE);
- }
- for(cam->n_buffers = 0;cam->n_buffers < req.count;cam->n_buffers++){
- struct v4l2_buffer buf;
- CLEAR(buf);
- buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- buf.memory = V4L2_MEMORY_MMAP;
- buf.index = cam->n_buffers;
- if (-1 == xioctl(cam->fd, VIDIOC_QUERYBUF, &buf))
- errno_exit("VIDIOC_QUERYBUF");
-
- cam->buffers[cam->n_buffers].length = buf.length;
- /*映射内存*/
- cam->buffers[cam->n_buffers].start = mmap(NULL /* start anywhere */,buf.length, PROT_READ | PROT_WRITE /* required */,MAP_SHARED /* recommended */, cam->fd, buf.m.offset);
-
- if (MAP_FAILED == cam->buffers[cam->n_buffers].start)
- errno_exit("mmap");
- }
- }
复制代码
2.3 start_capturing 开始图像采集
- static void start_capturing(struct camera *cam)
- {
- unsigned int i;
- enum v4l2_buf_type type;
- for (i = 0; i < cam->n_buffers; ++i) {
- struct v4l2_buffer buf;
- CLEAR(buf);
- /*1.将内存放入缓冲区*/
- buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- buf.memory = V4L2_MEMORY_MMAP;
- buf.index = i;
- if (-1 == xioctl(cam->fd, VIDIOC_QBUF, &buf))
- errno_exit("VIDIOC_QBUF");
- }
- type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- /*2.开始传输采集图像*/
- if (-1 == xioctl(cam->fd, VIDIOC_STREAMON, &type))
- errno_exit("VIDIOC_STREAMON");
- }
复制代码
2.3 read_frame 读一帧数据
- int read_frame(struct camera *cam,unsigned char *buffer,int *len/*数据大小*/)
- {
- fd_set fds;
- struct timeval tv;
- int r;
- struct v4l2_buffer buf;
-
-
- FD_ZERO(&fds);
- FD_SET(cam->fd,&fds);
- tv.tv_sec = 2;
- tv.tv_usec = 0;
- r = select(cam->fd+1,&fds,NULL,NULL,&tv);/*查询是否有数据*/
- if(-1 == r){
- if(EINTR == errno)
- return 1;//表示应该继续读
- errno_exit("select");
- }
- if(0 == r){
- fprintf(stderr,"select timeout");
- exit(EXIT_FAILURE);
- }
-
- CLEAR(buf);
-
- buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- buf.memory = V4L2_MEMORY_MMAP;
- if (-1 == xioctl(cam->fd, VIDIOC_DQBUF, &buf)) {/*从内核环形缓冲区取出一帧数据*/
- switch (errno) {
- case EAGAIN:
- return -1;
- case EIO:
- default:
- errno_exit("VIDIOC_DQBUF");
- }
- }
- memcpy(buffer,(unsigned char *)cam->buffers[buf.index].start,buf.bytesused);/*将采集的一帧数据放到自定义的buffer中*/
- //printf("cam->n_buffers=%d\nbuf.index=%d\nbuf.bytesused=%d\n",cam->n_buffers,buf.index,buf.bytesused);
- *len = buf.bytesused;
- if (-1 == xioctl(cam->fd, VIDIOC_QBUF, &buf))/*放回环形缓冲区*/
- errno_exit("VIDIOC_QBUF");
- dbug("video QBUF");
- return 0;//成功
- }
-
复制代码
|