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采集视频数据
涉及的结构体:
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](https://s2.51cto.com/images/blog/202305/22112217_646adfe9ebe0b25715.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
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.
|