如何用C++的V4L2 API持续采集H264视频并修改代码实现实时存储?
嘿,我来帮你逐个解决这些V4L2和摄像头采集的问题,都是我之前折腾Logitech C920时踩过的实际坑,直接上干货:
1. 如何使用C++的V4L2 API持续采集H264视频?
要实现持续采集,核心是按V4L2的流采集流程走,针对C920的H264硬编码支持,步骤如下:
- 初始化设备:打开摄像头设备(比如
/dev/video0),用open("/dev/video0", O_RDWR | O_NONBLOCK),非阻塞模式能让我们更灵活处理帧采集。 - 配置H264格式:填充
v4l2_format结构体,类型设为V4L2_BUF_TYPE_VIDEO_CAPTURE,像素格式指定V4L2_PIX_FMT_H264(C920原生支持这个硬编码格式,不用自己软编码),同时设置你需要的分辨率(比如1920x1080)和帧率。调用ioctl(fd, VIDIOC_S_FMT, &fmt)后,要检查设备是否真的接受了这个配置(有些设备会调整参数)。 - 申请并映射缓冲区:用
v4l2_requestbuffers申请3-4个缓冲区(数量太少容易丢帧),内存类型选V4L2_MEMORY_MMAP(直接映射内核缓冲区,效率最高)。然后逐个调用VIDIOC_QUERYBUF获取每个缓冲区的信息,再用mmap()把内核缓冲区映射到用户空间。 - 启动流采集:把每个映射好的缓冲区通过
VIDIOC_QBUF加入采集队列,然后调用VIDIOC_STREAMON启动流。 - 循环采集帧:进入循环,用
select()等待设备就绪(避免空轮询浪费CPU),就绪后调用VIDIOC_DQBUF取出填充好的H264帧数据。处理完帧(比如写入文件)后,记得用VIDIOC_QBUF把缓冲区放回队列,继续采集。 - 清理收尾:当用户触发退出时,调用
VIDIOC_STREAMOFF停止流,逐个munmap()解除缓冲区映射,最后close()设备文件。
2. 修改代码实现C920 H264实时流存储到MP4直至用户退出
你的现有代码能存单帧MP4,大概率是用了FFmpeg之类的封装库,要改成实时流存储,重点处理这几点:
- 先捕获SPS/PPS帧:C920输出的H264流开头会包含SPS(序列参数集)和PPS(图像参数集),这两个是MP4封装必须的元数据,必须先拿到它们才能正确初始化MP4的输出上下文。
- 实时写入与退出处理:
- 初始化FFmpeg的
AVFormatContext,指定输出格式为MP4,创建视频流,把捕获到的SPS/PPS数据写入流的extradata。 - 在循环采集H264帧时,要给每个帧设置正确的PTS/DTS时间戳(按帧率递增,比如30帧每秒的话,每帧PTS加1/30秒对应的时间基),然后调用
av_write_frame()写入文件。 - 监听用户退出信号(比如Ctrl+C触发的
SIGINT),在信号处理函数里,停止采集循环,调用av_write_trailer()完成MP4文件的收尾(这步很重要,没做的话MP4文件会损坏无法播放)。
- 初始化FFmpeg的
- 代码修改要点:
- 删掉单帧处理的逻辑,改成无限循环采集,直到收到退出信号。
- 如果之前没引入FFmpeg,需要添加FFmpeg的头文件(比如
libavformat/avformat.h、libavcodec/avcodec.h),编译时链接FFmpeg库:g++ -o capture capture.cpp -lavformat -lavcodec -lavutil。 - 注意:C920输出的是Annex B格式的H264,MP4需要AVCC格式,可以用FFmpeg的
av_bitstream_filter_init()配合h264_annexb2avcc过滤器完成转换,或者让FFmpeg自动处理格式兼容。
3. V4L文档示例缺失头文件无法运行的解决办法
这个是典型的开发依赖没装全,解决起来很简单:
- Debian/Ubuntu系系统:打开终端执行以下命令安装依赖:
sudo apt update && sudo apt install libv4l-dev v4l-utilslibv4l-dev包含所有V4L2的头文件(比如linux/videodev2.h、libv4l2.h),v4l-utils还附带了v4l2-ctl工具,用来调试摄像头参数,非常实用。 - RHEL/CentOS系系统:用dnf安装:
sudo dnf install libv4l-devel v4l-utils - 编译时注意:编译示例代码时,必须链接libv4l2库,比如:
如果用到了格式转换功能,还要加g++ -o v4l2_example v4l2_example.cpp -lv4l2-lv4lconvert。 - 如果还是找不到头文件:检查
/usr/include/linux/videodev2.h是否存在,编译时可以手动指定头文件路径:g++ -o v4l2_example v4l2_example.cpp -I/usr/include/linux -lv4l2
内容的提问来源于stack exchange,提问作者ajayramesh
相关产品推荐
相关产品推荐

