linux嵌入式视频流加速接入V4L2 , 自动驾驶边缘端设备或者服务器往往需要同步接入至少4路环视摄像头,更甚者需要接入12路摄像头,倘若单纯调用opencv集成的视频流读入代码,面对多路的摄像头的庞大视频流数据是无法满足高帧率的视频接入的,因此,需要专门的不依赖第三方库的视频流接入代码提供高速的视频流接入服务。 提供工程级MJPEG及YUYV视频流读入编解码代码,其中MJPEG的表现更是达到单帧30微秒左右。 需要的朋友可以直接将代码改写成多线程调用多路摄像头绝对会给您带来惊喜。
在自动驾驶边缘端设备以及服务器领域,对摄像头视频流的高效处理至关重要。想象一下,自动驾驶汽车需要实时感知周围环境,往往至少需要同步接入4路环视摄像头,有些更复杂的场景甚至要接入12路摄像头。如果只是简单地调用OpenCV集成的视频流读入代码,面对如此庞大的多路摄像头视频流数据,想要实现高帧率的视频接入简直是天方夜谭。这时候,就迫切需要专门的、不依赖第三方库的视频流接入代码来提供高速的视频流接入服务,而Linux嵌入式视频流加速接入V4L2就是解决方案之一。
1. 为何不用OpenCV?
先来看一段简单的OpenCV读视频流代码示例:
import cv2 cap = cv2.VideoCapture(0) while True: ret, frame = cap.read() if not ret: break cv2.imshow('Video Stream', frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()这段代码能读取单路摄像头视频流并展示。然而,当扩展到多路摄像头,随着摄像头数量增多,数据量呈指数级增长,OpenCV的处理效率会大幅下降,无法满足高帧率要求。
2. V4L2的优势与实现
V4L2(Video for Linux Two)是Linux下用于视频设备的内核驱动框架,它提供了底层的硬件访问接口,能够更高效地处理视频流。下面是一个简单的V4L2 MJPEG视频流读入代码示例(以C语言为例):
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <sys/mman.h> #include <linux/videodev2.h> #include <unistd.h> #define WIDTH 640 #define HEIGHT 480 #define BUFFER_COUNT 4 int main() { int fd; struct v4l2_capability cap; struct v4l2_format fmt; struct v4l2_requestbuffers req; void *buffers[BUFFER_COUNT]; size_t buffer_sizes[BUFFER_COUNT]; // 打开设备文件 fd = open("/dev/video0", O_RDWR); if (fd < 0) { perror("open"); return 1; } // 获取设备能力 if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) { perror("VIDIOC_QUERYCAP"); close(fd); return 1; } // 设置视频格式 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width = WIDTH; fmt.fmt.pix.height = HEIGHT; fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0) { perror("VIDIOC_S_FMT"); close(fd); return 1; } // 请求缓冲区 req.count = BUFFER_COUNT; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; if (ioctl(fd, VIDIOC_REQBUFS, &req) < 0) { perror("VIDIOC_REQBUFS"); close(fd); return 1; } // 映射缓冲区 for (int i = 0; i < req.count; i++) { struct v4l2_buffer buf; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = i; if (ioctl(fd, VIDIOC_QUERYBUF, &buf) < 0) { perror("VIDIOC_QUERYBUF"); close(fd); return 1; } buffers[i] = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset); buffer_sizes[i] = buf.length; } // 开启视频流捕获 enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (ioctl(fd, VIDIOC_STREAMON, &type) < 0) { perror("VIDIOC_STREAMON"); close(fd); return 1; } // 循环读取视频帧 for (int i = 0; i < 100; i++) { struct v4l2_buffer buf; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; if (ioctl(fd, VIDIOC_DQBUF, &buf) < 0) { perror("VIDIOC_DQBUF"); close(fd); return 1; } // 这里可以对读取到的帧数据进行处理,例如解码MJPEG // 处理完后将缓冲区重新入队 if (ioctl(fd, VIDIOC_QBUF, &buf) < 0) { perror("VIDIOC_QBUF"); close(fd); return 1; } } // 停止视频流捕获 type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (ioctl(fd, VIDIOC_STREAMOFF, &type) < 0) { perror("VIDIOC_STREAMOFF"); close(fd); return 1; } // 解除映射并关闭设备 for (int i = 0; i < req.count; i++) { if (munmap(buffers[i], buffer_sizes[i]) < 0) { perror("munmap"); close(fd); return 1; } } close(fd); return 0; }这段代码实现了基本的MJPEG视频流读入。首先打开视频设备文件,查询设备能力,接着设置视频格式为MJPEG,请求并映射缓冲区。之后开启视频流捕获,循环读取视频帧并处理,最后停止捕获,解除缓冲区映射并关闭设备。
MJPEG及YUYV视频流编解码
我们不仅提供了MJPEG视频流读入代码,还包含YUYV视频流读入编解码代码。MJPEG在性能上表现尤为出色,单帧处理时间能达到30微秒左右。如此高的效率得益于V4L2对底层硬件的直接访问和优化。
3. 多线程调用多路摄像头
对于需要接入多路摄像头的场景,只需将上述代码改写成多线程调用即可。例如,利用POSIX线程库(pthread):
#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <sys/mman.h> #include <linux/videodev2.h> #include <unistd.h> #define WIDTH 640 #define HEIGHT 480 #define BUFFER_COUNT 4 #define CAMERA_COUNT 4 typedef struct { int camera_index; } CameraArgs; void* read_camera(void* args) { CameraArgs* cam_args = (CameraArgs*)args; int fd; char device_path[20]; snprintf(device_path, sizeof(device_path), "/dev/video%d", cam_args->camera_index); // 后续打开设备、设置格式、请求缓冲区等操作与单路摄像头代码类似 // 这里省略重复代码 // 循环读取视频帧 for (int i = 0; i < 100; i++) { struct v4l2_buffer buf; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; if (ioctl(fd, VIDIOC_DQBUF, &buf) < 0) { perror("VIDIOC_DQBUF"); close(fd); pthread_exit(NULL); } // 处理帧数据 if (ioctl(fd, VIDIOC_QBUF, &buf) < 0) { perror("VIDIOC_QBUF"); close(fd); pthread_exit(NULL); } } // 停止视频流捕获、解除映射并关闭设备 pthread_exit(NULL); } int main() { pthread_t threads[CAMERA_COUNT]; CameraArgs args[CAMERA_COUNT]; for (int i = 0; i < CAMERA_COUNT; i++) { args[i].camera_index = i; if (pthread_create(&threads[i], NULL, read_camera, &args[i])!= 0) { perror("pthread_create"); return 1; } } for (int i = 0; i < CAMERA_COUNT; i++) { if (pthread_join(threads[i], NULL)!= 0) { perror("pthread_join"); return 1; } } return 0; }这段代码创建了多个线程,每个线程负责读取一路摄像头的视频流,通过这种方式,可以高效地实现多路摄像头的视频接入,极大提升系统整体的视频处理能力。
linux嵌入式视频流加速接入V4L2 , 自动驾驶边缘端设备或者服务器往往需要同步接入至少4路环视摄像头,更甚者需要接入12路摄像头,倘若单纯调用opencv集成的视频流读入代码,面对多路的摄像头的庞大视频流数据是无法满足高帧率的视频接入的,因此,需要专门的不依赖第三方库的视频流接入代码提供高速的视频流接入服务。 提供工程级MJPEG及YUYV视频流读入编解码代码,其中MJPEG的表现更是达到单帧30微秒左右。 需要的朋友可以直接将代码改写成多线程调用多路摄像头绝对会给您带来惊喜。
总之,在自动驾驶等对视频流处理要求极高的领域,通过Linux嵌入式视频流加速接入V4L2,配合MJPEG及YUYV视频流编解码代码,并采用多线程调用多路摄像头的方式,能够有效突破帧率瓶颈,满足实际应用需求。希望这些代码和思路能给需要的朋友带来帮助,祝大家在开发中一切顺利!