[V4L2+RTP] FireWRT远程视频监控-2

时间:2021-09-22 15:50:11

[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
涉及的结构体:
  1. struct buffer{
  2.     void *start;
  3.     size_t length;
  4. };
  5. struct camera{
  6.     char *device_name;//设备名字 /dev/videoX
  7.     int fd;//摄像头句柄
  8.     int width;//输出的视频宽度
  9.     int height;//高度
  10.     int display_depth;
  11.     int image_size;//一帧数据大小
  12.     int frame_number;
  13.     int support_fmt;//摄像头支持的格式如果支持Jpeg就以此获得数据,如果只支持yuyv就输出yuyv在用程序转化为jpeg
  14.     unsigned int n_buffers;//缓冲区个数
  15.     struct v4l2_capability v4l2_cap;
  16.     struct v4l2_cropcap v4l2_cropcap;
  17.     struct v4l2_format v4l2_fmt;
  18.     struct v4l2_crop v4l2_crop;
  19.     struct v4l2_fmtdesc v4l2_fmtdesc;
  20.     struct v4l2_streamparm v4l2_setfps;
  21.     struct buffer *buffers;
  22. };
  23. /*上面参数fmt_select选择*/
  24. #define FMT_JPEG    0x101
  25. #define FMT_YUYV422 0x102
  26. #define FMT_YUYV420 0x104
复制代码
涉及的函数:
[V4L2+RTP] FireWRT远程视频监控-2 
  1. /*外界接口*/
  2. void v4l2_init(struct camera *cam)
  3. {
  4.     open_camera(cam);
  5.     init_camera(cam);
  6.     start_capturing(cam);
  7. }
复制代码
2.1 open_camera 打开摄像头
  1. /*摄像头操作函数*/
  2. static void open_camera(struct camera *cam)
  3. {
  4.     struct stat st;

  5.     if (-1 == stat(cam->device_name, &st)) {
  6.         fprintf(stderr, "Cannot identify '%s': %d, %s\n", cam->device_name,errno, strerror(errno));
  7.         exit(EXIT_FAILURE);
  8.     }

  9.     if (!S_ISCHR(st.st_mode)) {
  10.         fprintf(stderr, "%s is no device\n", cam->device_name);
  11.         exit(EXIT_FAILURE);
  12.     }

  13.     cam->fd = open(cam->device_name, O_RDWR, 0); //  | O_NONBLOCK 打开摄像头 记录fd

  14.     if (-1 == cam->fd) {
  15.         fprintf(stderr, "Cannot open '%s': %d, %s\n", cam->device_name, errno,strerror(errno));
  16.         exit(EXIT_FAILURE);
  17.     }
  18. }
复制代码


2.2 init_camera 初始化摄像头
  1. static void init_camera(struct camera*cam)
  2. {
  3.     struct v4l2_capability  *cap        = &(cam->v4l2_cap);
  4.     struct v4l2_cropcap     *cropcap    = &(cam->v4l2_cropcap);
  5.     struct v4l2_crop        *crop       = &(cam->v4l2_crop);
  6.     struct v4l2_format      *fmt        = &(cam->v4l2_fmt);
  7.     struct v4l2_fmtdesc     *fmtdesc    = &(cam->v4l2_fmtdesc);
  8.     struct v4l2_streamparm  *streamparm = &(cam->v4l2_setfps);
  9.    
  10.     struct v4l2_requestbuffers req;
  11.     unsigned int min;
  12.     int pos = -1;
  13.     /*1.查询设备属性*/
  14.     if (-1 == xioctl(cam->fd, VIDIOC_QUERYCAP, cap)) {
  15.         if (EINVAL == errno) {
  16.             fprintf(stderr, "%s is no V4L2 device\n", cam->device_name);
  17.             exit(EXIT_FAILURE);
  18.         }else
  19.             errno_exit("VIDIOC_QUERYCAP");
  20.     }
  21.     /*2.是否支持图像获取*/
  22.     if (!(cap->capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
  23.         fprintf(stderr, "%s is no video capture device\n", cam->device_name);
  24.         exit(EXIT_FAILURE);
  25.     }
  26.     /*3.查询设备是否支持流输出*/
  27.     if (!(cap->capabilities & V4L2_CAP_STREAMING)) {
  28.         fprintf(stderr, "%s does not support streaming i/o\n",cam->device_name);
  29.         exit(EXIT_FAILURE);
  30.     }
  31. #ifdef OUTPUT_CAMINFO
  32.     printf("VIDOOC_QUERYCAP\n");
  33.     printf("driver:\t\t%s\n",cap->driver);
  34.     printf("card:\t\t%s\n",cap->card);
  35.     printf("bus_info:\t%s\n",cap->bus_info);
  36.     printf("version:\t%d\n",cap->version);
  37.     printf("capabilities:\t%x\n",cap->capabilities);
  38. #endif
  39.     /*打印摄像头支持的格式*/
  40.     fmtdesc->index = 0;
  41.     fmtdesc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  42. #ifdef OUTPUT_CAMINFO
  43.     printf("support format:\n");
  44. #endif
  45.     while(ioctl(cam->fd,VIDIOC_ENUM_FMT,fmtdesc) != -1){
  46. #ifdef OUTPUT_CAMINFO
  47.         printf("\t%d,%s\n",fmtdesc->index+1,fmtdesc->description);
  48. #endif
  49.         /*4.查看摄像头输出的 视频格式 jpeg yuyv420 yuyv422*/
  50.         pos = find_string(fmtdesc->description,"JPEG");
  51.         if(pos != -1){
  52.             cam->support_fmt |= FMT_JPEG;
  53.             goto CON;
  54.         }    
  55.         pos = find_string(fmtdesc->description,"4:2:2");
  56.         if(pos!=-1){
  57.             cam->support_fmt |= FMT_YUYV422;
  58.             goto CON;
  59.         }    
  60.         pos = find_string(fmtdesc->description,"4:2:0");
  61.         if(pos!=-1){
  62.             cam->support_fmt |= FMT_YUYV420;
  63.             goto CON;
  64.         }    
  65. CON:
  66.         fmtdesc->index++;
  67.     }
  68.     /*5.设置输出格式*/
  69.     CLEAR(*cropcap);

  70.     cropcap->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  71.     /*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(采
  72. 样类型,如 YUV4:2:2),然后通过 VIDIO_S_FMT 操作命令设置视频捕捉格式*/
  73.     crop->c.width = cam->width;
  74.     crop->c.height = cam->height;
  75.     crop->c.left = 0;
  76.     crop->c.top = 0;
  77.     crop->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  78.    
  79.     CLEAR(*fmt);
  80.     fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  81.     fmt->fmt.pix.width = cam->width;
  82.     fmt->fmt.pix.height = cam->height;
  83.     if( (cam->support_fmt & FMT_JPEG) == FMT_JPEG){
  84.         fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
  85.         cam->support_fmt = FMT_JPEG;/*记录设备输出的图像格式*/
  86.     }
  87.     else if((cam->support_fmt & FMT_YUYV422) == FMT_YUYV422){
  88.         fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
  89.         cam->support_fmt = FMT_YUYV422;
  90.     }
  91.     else if( (cam->support_fmt & FMT_YUYV420 ) == FMT_YUYV420 ){
  92.         fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
  93.         cam->support_fmt = FMT_YUYV420;
  94.     }
  95.     else
  96.         errno_exit("no support fmt");

  97.     fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;
  98.     
  99.     if (-1 == xioctl(cam->fd,VIDIOC_S_FMT,fmt))
  100.         errno_exit("VIDIOC_S_FMT fail");
  101.         
  102. #if 1
  103.     CLEAR(*streamparm);
  104.     streamparm->type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
  105.     streamparm->parm.capture.timeperframe.numerator   = 1;
  106.     streamparm->parm.capture.timeperframe.denominator = 25;
  107.     if(-1==xioctl(cam->fd,VIDIOC_S_PARM,streamparm))
  108.         errno_exit("VIDIOC_S_PARM");
  109. #endif
  110.     min = fmt->fmt.pix.width * 2;/*YUYV 每个像素占用2个字节*/
  111.     if (fmt->fmt.pix.bytesperline < min)
  112.         fmt->fmt.pix.bytesperline = min;
  113.     
  114.     min = fmt->fmt.pix.bytesperline * fmt->fmt.pix.height;
  115.     if (fmt->fmt.pix.sizeimage < min)
  116.         fmt->fmt.pix.sizeimage = min;
  117.     
  118.     
  119.     /*6.分配内存,映射内存*/
  120.     CLEAR(req);
  121.     req.count = 4;
  122.     req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  123.     req.memory = V4L2_MEMORY_MMAP;
  124.     
  125.     if (-1 == xioctl(cam->fd, VIDIOC_REQBUFS, &req)) {
  126.         if (EINVAL == errno) {
  127.             fprintf(stderr, "%s does not support "
  128.                     "memory mapping\n", cam->device_name);
  129.             exit(EXIT_FAILURE);
  130.         } else
  131.             errno_exit("VIDIOC_REQBUFS");
  132.     }

  133.     if (req.count < 2) {
  134.         fprintf(stderr, "Insufficient buffer memory on %s\n", cam->device_name);
  135.         exit(EXIT_FAILURE);
  136.     }

  137.     cam->buffers = calloc(req.count, sizeof(*(cam->buffers)));

  138.     if (!cam->buffers) {
  139.         fprintf(stderr, "Out of memory\n");
  140.         exit(EXIT_FAILURE);
  141.     }
  142.     for(cam->n_buffers = 0;cam->n_buffers < req.count;cam->n_buffers++){
  143.         struct v4l2_buffer buf;
  144.         CLEAR(buf);

  145.         buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  146.         buf.memory = V4L2_MEMORY_MMAP;
  147.         buf.index = cam->n_buffers;

  148.         if (-1 == xioctl(cam->fd, VIDIOC_QUERYBUF, &buf))
  149.             errno_exit("VIDIOC_QUERYBUF");
  150.         
  151.         cam->buffers[cam->n_buffers].length = buf.length;
  152. /*映射内存*/
  153.         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);
  154.     
  155.         if (MAP_FAILED == cam->buffers[cam->n_buffers].start)
  156.             errno_exit("mmap");
  157.     }
  158. }
复制代码
2.3 start_capturing 开始图像采集
  1. static void start_capturing(struct camera *cam)
  2. {
  3.     unsigned int i;
  4.     enum v4l2_buf_type type;

  5.     for (i = 0; i < cam->n_buffers; ++i) {
  6.         struct v4l2_buffer buf;
  7.         CLEAR(buf);
  8.         /*1.将内存放入缓冲区*/
  9.         buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  10.         buf.memory = V4L2_MEMORY_MMAP;
  11.         buf.index = i;

  12.         if (-1 == xioctl(cam->fd, VIDIOC_QBUF, &buf))
  13.             errno_exit("VIDIOC_QBUF");
  14.     }
  15.     type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  16. /*2.开始传输采集图像*/
  17.     if (-1 == xioctl(cam->fd, VIDIOC_STREAMON, &type))
  18.         errno_exit("VIDIOC_STREAMON");
  19. }
复制代码
2.3 read_frame 读一帧数据
  1. int read_frame(struct camera *cam,unsigned char *buffer,int *len/*数据大小*/)
  2. {
  3.     fd_set fds;
  4.     struct timeval tv;
  5.     int r;
  6.     struct v4l2_buffer buf;
  7.     
  8.     
  9.     FD_ZERO(&fds);
  10.     FD_SET(cam->fd,&fds);

  11.     tv.tv_sec  = 2;
  12.     tv.tv_usec = 0;

  13.     r = select(cam->fd+1,&fds,NULL,NULL,&tv);/*查询是否有数据*/
  14.     if(-1 == r){
  15.         if(EINTR == errno)
  16.             return 1;//表示应该继续读
  17.         errno_exit("select");
  18.     }
  19.     if(0 == r){
  20.         fprintf(stderr,"select timeout");
  21.         exit(EXIT_FAILURE);
  22.     }
  23.     
  24.     CLEAR(buf);
  25.     
  26.     buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  27.     buf.memory = V4L2_MEMORY_MMAP;

  28.         if (-1 == xioctl(cam->fd, VIDIOC_DQBUF, &buf)) {/*从内核环形缓冲区取出一帧数据*/
  29.             switch (errno) {
  30.                 case EAGAIN:
  31.                     return -1;
  32.                 case EIO:
  33.                 default:
  34.                 errno_exit("VIDIOC_DQBUF");
  35.             }
  36.         }
  37.     memcpy(buffer,(unsigned char *)cam->buffers[buf.index].start,buf.bytesused);/*将采集的一帧数据放到自定义的buffer中*/
  38.     //printf("cam->n_buffers=%d\nbuf.index=%d\nbuf.bytesused=%d\n",cam->n_buffers,buf.index,buf.bytesused);
  39.     *len = buf.bytesused;
  40.     if (-1 == xioctl(cam->fd, VIDIOC_QBUF, &buf))/*放回环形缓冲区*/
  41.         errno_exit("VIDIOC_QBUF");
  42.     dbug("video QBUF");
  43.     return 0;//成功
  44. }
复制代码