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


发表于 2015-3-26 10:01:02       浏览:1193  |  回复:5   




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_#endif

 



    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. 6.      if (-1 == stat(cam->device_name, &st)) {
    7.          fprintf(stderr, "Cannot identify '%s': %d, %s\n", cam->device_name,errno, strerror(errno));
    8.          exit(EXIT_FAILURE);
    9.      }
    10. 11.      if (!S_ISCHR(st.st_mode)) {
    12.          fprintf(stderr, "%s is no device\n", cam->device_name);
    13.          exit(EXIT_FAILURE);
    14.      }
    15. 16.      cam->fd = open(cam->device_name, O_RDWR, 0); //  | O_NONBLOCK 打开摄像头 记录fd
    17. 18.      if (-1 == cam->fd) {
    19.          fprintf(stderr, "Cannot open '%s': %d, %s\n", cam->device_name, errno,strerror(errno));
    20.          exit(EXIT_FAILURE);
    21.      }
    22.  }




    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. 71.      cropcap->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    72.      /*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(采
    73.  样类型,如 YUV4:2:2),然后通过 VIDIO_S_FMT 操作命令设置视频捕捉格式*/
    74.      crop->c.width = cam->width;
    75.      crop->c.height = cam->height;
    76.      crop->c.left = 0;
    77.      crop->c.top = 0;
    78.      crop->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    79.     
    80.      CLEAR(*fmt);
    81.      fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    82.      fmt->fmt.pix.width = cam->width;
    83.      fmt->fmt.pix.height = cam->height;
    84.      if( (cam->support_fmt & FMT_JPEG) == FMT_JPEG){
    85.          fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
    86.          cam->support_fmt = FMT_JPEG;/*记录设备输出的图像格式*/
    87.      }
    88.      else if((cam->support_fmt & FMT_YUYV422) == FMT_YUYV422){
    89.          fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
    90.          cam->support_fmt = FMT_YUYV422;
    91.      }
    92.      else if( (cam->support_fmt & FMT_YUYV420 ) == FMT_YUYV420 ){
    93.          fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
    94.          cam->support_fmt = FMT_YUYV420;
    95.      }
    96.      else
    97.          errno_exit("no support fmt");
    98. 99.      fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;
    100.      
    101.      if (-1 == xioctl(cam->fd,VIDIOC_S_FMT,fmt))
    102.          errno_exit("VIDIOC_S_FMT fail");
    103.          
    104.  #if 1
    105.      CLEAR(*streamparm);
    106.      streamparm->type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    107.      streamparm->parm.capture.timeperframe.numerator   = 1;
    108.      streamparm->parm.capture.timeperframe.denominator = 25;
    109.      if(-1==xioctl(cam->fd,VIDIOC_S_PARM,streamparm))
    110.          errno_exit("VIDIOC_S_PARM");
    111.  #endif
    112.      min = fmt->fmt.pix.width * 2;/*YUYV 每个像素占用2个字节*/
    113.      if (fmt->fmt.pix.bytesperline < min)
    114.          fmt->fmt.pix.bytesperline = min;
    115.      
    116.      min = fmt->fmt.pix.bytesperline * fmt->fmt.pix.height;
    117.      if (fmt->fmt.pix.sizeimage < min)
    118.          fmt->fmt.pix.sizeimage = min;
    119.      
    120.      
    121.      /*6.分配内存,映射内存*/
    122.      CLEAR(req);
    123.      req.count = 4;
    124.      req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    125.      req.memory = V4L2_MEMORY_MMAP;
    126.      
    127.      if (-1 == xioctl(cam->fd, VIDIOC_REQBUFS, &req)) {
    128.          if (EINVAL == errno) {
    129.              fprintf(stderr, "%s does not support "
    130.                      "memory mapping\n", cam->device_name);
    131.              exit(EXIT_FAILURE);
    132.          } else
    133.              errno_exit("VIDIOC_REQBUFS");
    134.      }
    135. 136.      if (req.count < 2) {
    137.          fprintf(stderr, "Insufficient buffer memory on %s\n", cam->device_name);
    138.          exit(EXIT_FAILURE);
    139.      }
    140. 141.      cam->buffers = calloc(req.count, sizeof(*(cam->buffers)));
    142. 143.      if (!cam->buffers) {
    144.          fprintf(stderr, "Out of memory\n");
    145.          exit(EXIT_FAILURE);
    146.      }
    147.      for(cam->n_buffers = 0;cam->n_buffers < req.count;cam->n_buffers++){
    148.          struct v4l2_buffer buf;
    149.          CLEAR(buf);
    150. 151.          buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    152.          buf.memory = V4L2_MEMORY_MMAP;
    153.          buf.index = cam->n_buffers;
    154. 155.          if (-1 == xioctl(cam->fd, VIDIOC_QUERYBUF, &buf))
    156.              errno_exit("VIDIOC_QUERYBUF");
    157.          
    158.          cam->buffers[cam->n_buffers].length = buf.length;
    159.  /*映射内存*/
    160.          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);
    161.      
    162.          if (MAP_FAILED == cam->buffers[cam->n_buffers].start)
    163.              errno_exit("mmap");
    164.      }
    165.  }
    
    
            复制代码
    
     2.3 start_capturing 开始图像采集
    
    
    1.  static void start_capturing(struct camera *cam)
    2.  {
    3.      unsigned int i;
    4.      enum v4l2_buf_type type;
    5. 6.      for (i = 0; i < cam->n_buffers; ++i) {
    7.          struct v4l2_buffer buf;
    8.          CLEAR(buf);
    9.          /*1.将内存放入缓冲区*/
    10.          buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    11.          buf.memory = V4L2_MEMORY_MMAP;
    12.          buf.index = i;
    13. 14.          if (-1 == xioctl(cam->fd, VIDIOC_QBUF, &buf))
    15.              errno_exit("VIDIOC_QBUF");
    16.      }
    17.      type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    18.  /*2.开始传输采集图像*/
    19.      if (-1 == xioctl(cam->fd, VIDIOC_STREAMON, &type))
    20.          errno_exit("VIDIOC_STREAMON");
    21.  }
    
    
            复制代码
    
     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. 12.      tv.tv_sec  = 2;
    13.      tv.tv_usec = 0;
    14. 15.      r = select(cam->fd+1,&fds,NULL,NULL,&tv);/*查询是否有数据*/
    16.      if(-1 == r){
    17.          if(EINTR == errno)
    18.              return 1;//表示应该继续读
    19.          errno_exit("select");
    20.      }
    21.      if(0 == r){
    22.          fprintf(stderr,"select timeout");
    23.          exit(EXIT_FAILURE);
    24.      }
    25.      
    26.      CLEAR(buf);
    27.      
    28.      buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    29.      buf.memory = V4L2_MEMORY_MMAP;
    30. 31.          if (-1 == xioctl(cam->fd, VIDIOC_DQBUF, &buf)) {/*从内核环形缓冲区取出一帧数据*/
    32.              switch (errno) {
    33.                  case EAGAIN:
    34.                      return -1;
    35.                  case EIO:
    36.                  default:
    37.                  errno_exit("VIDIOC_DQBUF");
    38.              }
    39.          }
    40.      memcpy(buffer,(unsigned char *)cam->buffers[buf.index].start,buf.bytesused);/*将采集的一帧数据放到自定义的buffer中*/
    41.      //printf("cam->n_buffers=%d\nbuf.index=%d\nbuf.bytesused=%d\n",cam->n_buffers,buf.index,buf.bytesused);
    42.      *len = buf.bytesused;
    43.      if (-1 == xioctl(cam->fd, VIDIOC_QBUF, &buf))/*放回环形缓冲区*/
    44.          errno_exit("VIDIOC_QBUF");
    45.      dbug("video QBUF");
    46.      return 0;//成功
    47.  }
    48.