FFmpeg-合并两个视频文件

少于 1 分钟阅读时长

发布时间:

使用 FFmpeg 合并两个视频文件时区别AVFormatContext->duration和AVStream->duration

今天使用 FFmpeg 编写C++代码合并两个视频文件。整体流程如下:

  1. 打开两个输入视频文件
  2. 创建输出视频文件
  3. 对于第一个视频文件,直接写入输出视频文件
  4. 对于第二个视频文件,要修改视频流的时间戳,再写入输出视频文件
  5. 关闭所有文件
  6. 释放资源

上面过程中,修改第二个视频流的时间戳逻辑如下:

// 获取第一个视频的时常,单位微秒
int64_t begin_sec = vec_src_fmt_ctx[0]->duration;
// 计算第一个视频末尾的时间基,作为第二个视频开始的时间基
int64_t begin_video_pts = begin_sec / (1000.0 * 1000.0 * av_q2d(vec_src_video_stream[0]->time_base));
// 解码得到frame
// ...
// 将第二个视频的时间戳的时间基转换为输出视频的时间基(这里和第一个视频时间基一样)
int64_t pts = av_rescale_q(frame->pts, vec_src_video_stream[1]->time_base, vec_src_video_stream[0]->time_base);
// 转换后时间基再加上增量
frame->pts = pts + begin_video_pts;

在开发中将vec_src_fmt_ctx[0]->duration错误写出vec_src_video_stream[0]->duration,导致第二个视频的时间基错误,最终导致合并后的视频文件无法播放。这个错误在调试时很难发现,因为 FFmpeg 的日志输出没有提示错误信息。经过仔细检查代码,才发现了这个问题。

下面总结一下上面两个duration的区别(问的chatGPT):

AVFormatContext->duration vs AVStream->duration 对比

特性AVFormatContext->durationAVStream->duration
作用范围整个媒体文件单个流(如视频、音频、字幕)
单位固定为微秒(µs,AV_TIME_BASE流的时间基(time_base,可变单位)
计算公式duration / AV_TIME_BASE → 秒duration * av_q2d(time_base) → 秒
数据来源容器层(文件头信息)流层(帧数据统计)
可靠性容器未存储时可能不准确通常更精确(依赖实际帧数据)
典型场景显示文件总时长音视频同步、流剪切
特殊值AV_NOPTS_VALUE(未定义)AV_NOPTS_VALUE(未定义)
替代计算需遍历所有包估算可通过nb_frames * avg_frame_rate计算

单位换算示例代码

// 文件总时长(秒)
double fmt_duration = fmt_ctx->duration / (double)AV_TIME_BASE;

// 视频流时长(秒)
double stream_duration = video_stream->duration * av_q2d(video_stream->time_base);

本次代码可以在GitHub上找到,欢迎大家交流~ 如果有问题可以在下面留言~