接上篇我们继续聊一下VideoEditor中视频导出,这次我们谈谈【如何优化视频导出的速度】,视频的导出速度算是VideoEditor的一个非常重要的衡量指标,如何导出速度太慢了,那么VideoEditor肯定是流失相当一部分用户的。本文关于视频导出速度的优化都是经过实践证明过的,非常具有参考意义。
要想提升导出速度,毫无疑问肯定要使用硬解码和硬编码,毕竟GPU处理的速度比CPU不是快一点点,但是软解码和软编码也是需要支持的,你需要考虑特殊的情况,Android平台上手机五花八门,你不得不兼容所有可能的情况。
上一篇文章比较清晰地介绍了视频导出的基本流程:带你梳理VideoEditor视频导出流程,视频导出经过了解码 ——> 声音帧和视频帧处理 ——> 编码 的过程,很简单就能看出可能耗时的点。就一个视频帧的处理过程而言,它是串行的,就是必须要将视频Packet解码,才能处理这个视频帧,处理完成之后,才能对它进行编码。(这儿可以忽略解封的时间,解封装的耗时比较少,所以不需要过多的考虑)。
解码和编码的过程可以看成是互为逆过程,有两种选择,硬解码/硬编码或者软解码/软编码,硬解码/硬编码底层是采用GPU处理的,软解码/软编码是采用CPU计算的,所以硬解码/硬编码效率更高,采用哪种方式,要结合很多种情况:
我们在上一篇文章也表示了,视频宽高、码率、帧率、格式是导出的必备参数,这些参数也可以用来确定内置的MediaCodec是否支持?下面判断是否支持特定格式的硬编码和硬解码。
public static boolean isMediaCodecEncodeSupported(String mimeType, int videoWidth, int videoHeight) {
MediaCodecList codecList = new MediaCodecList(MediaCodecList.ALL_CODECS);
final MediaCodecInfo[] infoList = codecList.getCodecInfos();
for (int index = 0; index < infoList.length; index++) {
MediaCodecInfo codecInfo = infoList[index];
if (!codecInfo.isEncoder()) {
continue;
}
String[] types = codecInfo.getSupportedTypes();
for (String type : types) {
if (!mimeType.equalsIgnoreCase(type)) {
continue;
}
MediaCodecInfo.CodecCapabilities codecCapabilities = codecInfo.getCapabilitiesForType(type);
MediaCodecInfo.VideoCapabilities videoCapabilities = codecCapabilities.getVideoCapabilities();
if (videoCapabilities != null) {
return videoCapabilities.isSizeSupported(videoWidth, videoHeight);
}
}
}
return false;
}
public static boolean isMediaCodecDecodeSupported(String mimeType, int videoWidth, int videoHeight) {
MediaCodecList codecList = new MediaCodecList(MediaCodecList.ALL_CODECS);
final MediaCodecInfo[] infoList = codecList.getCodecInfos();
for (int index = 0; index < infoList.length; index++) {
MediaCodecInfo codecInfo = infoList[index];
if (codecInfo.isEncoder()) {
continue;
}
String[] types = codecInfo.getSupportedTypes();
for (String type : types) {
if (!mimeType.equalsIgnoreCase(type)) {
continue;
}
MediaCodecInfo.CodecCapabilities codecCapabilities = codecInfo.getCapabilitiesForType(type);
MediaCodecInfo.VideoCapabilities videoCapabilities = codecCapabilities.getVideoCapabilities();
if (videoCapabilities != null) {
return videoCapabilities.isSizeSupported(videoWidth, videoHeight);
}
}
}
return false;
}
送入MediaCodec内部进行解码和编码,时长就不是我们能控制的,这部分时间不可优化。所以只要保证解码和编码使用的是MediaCodec,那过程1和过程3没有可以优化的空间。
过程2是处理Frame的过程,一般采用OpenGL,这个过程我们一般有三个原则:
上面谈的一帧视频的处理过程,完整的视频有若干帧,例如一个14s、25fps的视频,共有350帧,那就要经历350个上面的过程。加入1帧处理需要100ms,那么总共需要35000ms才能导出这个视频。这个视频就比较长了。
既然一帧视频的处理过程是比较独立的,那可不可以将350帧分为多段,例如分为两段,分别是175帧,导出两个7s的视频,然后将这两个视频合成一个新的视频。
这就是本文要讨论的并行导出,就是将一个完整的视频分为两段或者多段,然后分别处理,最后将各小段视频合成一个完整的视频。例如导出一个14s的视频需要35000ms,拆分成两个7s的视频,就可以大大缩短导出的时间了。那是不是切分越多越好?
首先我们要明确导出视频是需要消耗资源的,这个资源就是MediaCodec,最终是送入到GPU中处理,一个手机中的MediaCodec实例是有限的,正常情况下,一个手机可以提供的MediaCodec实例最多有16个,如果当前使用的MediaCodec实例超过16个,那么手机将无法正常工作。MediaCodec资源是手机中的所有App共同持有。所以并行分段的个数不是越多越好。
简单分享一下结论吧,两段并行速度提升50% ~ 70%,内存增加20%, 三段并行速度提升60% ~ 90%,内存增加80%;并行超过三段的话就无法明显提升速度了。我比较建议并行两段,在一些性能很好的机型上并行三段。
并行转码有什么注意的点,大家使用的时候要着重注意下面的问题:
如果你在实践过程中遇到什么问题,欢迎来咨询讨论。聊完了【视频的导出速度】,接下来我们将要涉及【视频的清晰度】,这也是非常重要的一个章节,除了基本的编码优化,有没有一些AI的方式能提升视频的清晰度呢?欢迎大家来脑暴,可以将你的想法私信发给我,我们一起进步。