之前写过HLS视频合并为一个MP4视频:
M3U8-TS文件合并为MP4文件
HLS公开课(五)HLS合成为一整个视频
HLS公开课(七)HLS合成为一整个视频解决dts不连续的问题
有人肯定会说多个MP4视频合成一个MP4视频和HLS合成一个MP4视频的做法是不是一样的啊?
其实还是不一样的。
讲几点理由吧:
- HLS本质上是一个视频,所以不存在多个视频对接PTS和DTS问题,多个TS文件中的PTS和DTS已经帮忙对应好了内部的关系
- 即使内部的TS文件可能存在PTS和DTS的问题,但是这个一般情况下是切片异常导致的问题,并不是HLS合成MP4必须要解决的问题
- 几个新的MP4文件合成成一个MP4文件,文件衔接处的PTS和DTS需要特别小心的处理。
多个MP4视频合成一个MP4视频现在在短视频应用中应用非常广泛:主要有以下应用场景。
- 视频录制的过程中可以让用户选择分段录制,这样会产生多个MP4视频文件,生成的视频又需要生成一个MP4文件
- 音视频编辑SDK中可以导入多个视频,然后分别处理每个视频,最终合成一个视频
但是也有需要注意的点:
- 完整的操作过程中只是解封装和重新封装的工作,不应该存在解码和编码的工作,因为移动端解码和编码实在非常耗时,用户体验这一关过不去。
- 合并的视频应该是封装格式一样、分辨率一样,不然合并出来的视频会出现异常。好在移动端导入的视频都是使用手机拍摄的,所以上面两个条件应该是可以满足的。
如何解决合并过程的PTS和DTS问题
记住文件结尾的PTS和DTS
在执行av_read_frame的时候,如果发现读到文件末尾了,就会出现EOF:
AVPacket packet;
int result = av_read_frame(input_context_, &packet);
if (result < 0) {
av_packet_unref(&packet);
//说明当前的文件已经读到结尾了
//下面如果还要读取新的文件,就要更新pts和dts了
if (result == AVERROR_EOF) {
current_video_index_++;
if (current_video_index_ >= input_size_) {
//视频序列已经遍历结束了
break;
}
int ret;
do {
ret = OpenInputVideo(current_video_index_);
if (ret != 0) {
current_video_index_++;
}
} while (ret != 0);
continue;
}
continue;
}
这个文件读到结尾时,开始读下一个文件。
下一个文件对接上一个文件的PTS和DTS
全局保存到的几个关键数据:
int64_t last_video_pts_;
int64_t last_video_dts_;
int64_t last_audio_pts_;
int64_t last_audio_dts_;
int64_t current_video_pts_;
int64_t current_video_dts_;
int64_t current_audio_pts_;
int64_t current_audio_dts_;
保存上一个视频的pts和dts信息,记录当前写视频的pts和dts信息,这样在解封和封装视频的时候不断校验pts和dts信息,完全可以保证pts和dts信息是正常排列的。
注意原始视频的旋转角度问题
检查一下每个视频的旋转角度信息,我们在开始的视频要读出这个信息,设置到输出的视频metadata中,因为旋转角度的信息也需要保持一致,不然合成之后的视频会存在问题。
if (input_stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
AVDictionaryEntry *rotateEntry = av_dict_get(input_stream->metadata, "rotate", nullptr, 0);
if (rotateEntry != nullptr) {
int rotate = atoi(rotateEntry->value);
if (rotate != 0) {
char rotate_str[1024];
sprintf(rotate_str, "%d", rotate);
av_dict_set(&output_stream->metadata, "rotate", rotate_str, 0);
}
}
}
将原始视频的旋转角度记录下来,写入生成的文件的metadata中,这样生成的文件的旋转角度和原始视频是一样的。
项目已开源
开源项目参考:github.com/JeffMony/Je…
在开源项目中,提供了一个测试用例:
files文件夹下面有4个视频,其中input1.mp4、input2.mp4、input3.mp4合并成output.mp4视频,可以参考下
input1.mp4
Duration: 00:00:10.86, start: 0.000000, bitrate: 11161 kb/s
input2.mp4
Duration: 00:00:11.22, start: 0.000000, bitrate: 10798 kb/s
input3.mp4
Duration: 00:00:11.46, start: 0.000000, bitrate: 11378 kb/s
output.mp4
Duration: 00:00:33.63, start: 0.000000, bitrate: 10513 kb/s
可以看出来output.mp4确实是input1.mp4/input2.mp4/input3.mp4三个视频合并而成的。