之前有一个音视频合成的需求,还写了一篇文章无声视频合成音频。之前的需求是,视频整体时间控制在 10s(便于发布到朋友圈),现在朋友圈的限制改为 15s 了,所以产品决定不再限制视频的时长为 10s 了,改为最长 40s,若有发布到朋友圈的需求,则自己裁剪。大部分情况下,生成的视频长度会小于 15s,也不需要裁剪。需求调整后,之前文章里的音视频合成的方案就需要略作修改了。
说下之前的方案:利用录屏生成 10s 的无声视频,与正好 10s 的音频 aac 文件进行合成。之前的代码最终生成的文件长度为音视频文件的最大值(若视频为 10s,音频为 15s,则会生成 15s 的视频,视频最后 5s 一直展示视频的最后一帧,同时播放音乐)。现在调整视频时长后,产品定的需求是视频时长增加,然后音频循环播放。所以可能就是 24s 的无声视频合成 10s 的音频。所以之前的代码就得调整了:
1 | /** |
整体思路是这样的:先获取无声视频的长度,假设为 24s,然后 muxer.writeSampleData 写入所有的 videoTrack 数据,然后开始写入 audioTrack 的数据。当写完 10s 后,audioTrack 便结束了,但是此时才写入 10s,不足 24s,所以重新 seek 到初始位置,再继续写入。写到 20s 后,再写入 4s,判断满足视频时长 24s 了,则直接退出。
需要多说一下的是:生成视频的音频轨道 audioExtractor 获取到的 presentationTimeUs 正好就是音频播放的长度,而视频轨道由于有关键帧、非关键帧导致 videoExtractor presentationTimeUs 只能是递增,但却不能保证是播放长度。所以在写入音频数据时,可以根据 presentationTimeUs 来判断当前写入了多少秒,若已经达到视频长度,则可以直接退出,从而保证写入音频的时长与视频的时长是一致的。另外,当 audioExtractor 读完后,返回的 size 为 -1,判断此时时长不足,则直接 seek 到初始位置,同时需要累加 presentationTimeUs,所以代码里用到了 sampleTime 来记录 presentationTimeUs 的值。muxer.writeSampleData 时 presentationTimeUs 必须是递增的。
在 stackoverflow 上也搜到了同样的问题 How to mux (merge) video&audio, so that the audio will loop in the output video in case it’s too short in duration?,但是没有回答,于是舔着脸强行回答了一波,欢迎来点赞~