使用v4l2框架+FFmpeg录制MP4视频:时长不足与播放卡顿问题
问题排查与解决方案
1. 时间戳(PTS/DTS)错误
这是时长不准、播放卡顿的核心诱因。如果采集帧时未正确传递硬件时间戳给FFmpeg,编码器或容器会使用默认的单调递增时间,或因时间戳不匹配导致帧被丢弃、时长计算偏差。
- 修复方式:从v4l2的
struct v4l2_buffer中提取硬件时间戳,转换为FFmpeg流的时间基准:
struct v4l2_buffer buf; // 执行VIDIOC_DQBUF获取帧数据后... AVPacket pkt; av_init_packet(&pkt); // 将v4l2的微秒时间戳转换为流的时间基准 pkt.pts = av_rescale_q(buf.timestamp.tv_sec * AV_TIME_BASE + buf.timestamp.tv_usec, AV_TIME_BASE_Q, stream->time_base); pkt.dts = pkt.pts; // 无B帧时DTS与PTS一致 pkt.stream_index = stream->index;
2. 帧率不匹配与USB带宽限制
- 先确认设备实际支持的帧率:用
v4l2-ctl --list-formats-ext查看目标分辨率下的稳定帧率范围。1080p分辨率对USB带宽要求极高,USB2.0通常无法支撑30fps的未压缩视频(如YUYV),强行设置会导致大量丢帧,直接缩短实际录制时长。 - 对齐采集与编码帧率:确保v4l2设置的
struct v4l2_streamparm中的帧率,与FFmpeg编码器上下文的framerate参数完全一致,避免编码器自动丢帧或重复帧。
3. v4l2缓冲区配置不足
缓冲区数量过少会导致采集时丢帧,尤其高分辨率下数据量更大:
- 调整缓冲区数量:调用
VIDIOC_REQBUFS时,将count设置为4-8个(根据设备支持情况调整),确保有足够的缓冲区缓存帧数据,避免因缓冲区填满导致设备停止输出:
struct v4l2_requestbuffers req = {0}; req.count = 6; // 增加缓冲区数量 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; ioctl(fd, VIDIOC_REQBUFS, &req);
4. FFmpeg封装收尾不完整
录制结束时未正确触发编码器输出缓存帧、未写入文件尾,会导致MP4文件末尾截断,时长不足:
- 正确的收尾流程:
// 向编码器发送结束信号 AVPacket pkt; av_init_packet(&pkt); pkt.data = NULL; pkt.size = 0; avcodec_send_packet(codec_ctx, &pkt); // 读取编码器缓存的所有剩余帧 while (avcodec_receive_packet(codec_ctx, &pkt) == 0) { av_interleaved_write_frame(fmt_ctx, &pkt); av_packet_unref(&pkt); } // 写入MP4文件尾 av_write_trailer(fmt_ctx);
5. 帧处理阻塞导致丢帧
如果采集、格式转换、编码在同一线程执行,高分辨率下处理耗时超过帧间隔,会导致v4l2缓冲区无法及时释放,新帧无法写入:
- 优化方案:
- 采用多线程架构:采集线程负责从v4l2取帧,编码线程负责处理和写入文件,用队列传递帧数据。
- 减少格式转换开销:让v4l2直接输出编码器支持的格式(如YUV420P),避免额外的
sws_scale转换。
内容的提问来源于stack exchange,提问作者Reena
相关产品推荐
相关产品推荐

