[V4L2+RTP] FireWRT远程视频监控-2
2016-01-24 00:57
549 查看
[V4L2+RTP] FireWRT远程视频监控-2
发表于 2015-3-26 10:01:02 浏览:1193 | 回复:5 打印 只看该作者 [复制链接]楼主本帖最后由 代祥军 于 2015-4-16 09:13 编辑 1.准备 涉及的知识 V4L2采集视频数据 YUYV转化JPEG RTP打包JPEG图片数据 socket发送 程序调用流程: 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;//成功 } 复制代码 |
相关文章推荐
- FireWRT(MT7621)远程视频监控-1
- Linux常用命令_文件管理
- 使用shell/python获取hostname/fqdn释疑
- 如何从零开始搭建一个网站
- opengl es画凹多边形问题:切割凹多边形为三角形
- 什么是REST架构
- 设计自己的高并发架构
- HDU 1678 Shopaholic(贪心)
- Xshell配置ssh免密码登录-密钥公钥(Public key)与私钥(Private Key)登录
- Linux学习笔记(2)----文件权限
- VMware虚拟机克隆Linux系统后找不到eth0网卡的问题
- iTop4412时钟配置
- linux运行时动态调整内核参数工具sysctl学习小结
- docker容器的端口映射(构建nginx容器)
- docker查看镜像和删除镜像
- docker镜像的获取和推送
- android:内部存储空间 IO 操作openFileInput()、output(13)
- 如何让nodejs在linux后台运行
- OpenWrt编译记录
- 自带内存上的读写(openFileOutput和openFileInput)