一、背景介绍
随着 4G 网络的推广和网络带宽的晋升,视频成为互联网用户次要的生产载体,用户通过短视频来分享和浏览信息。由此视频的编辑性能越来越重要、越来越广泛。视频编辑的 App 也如雨后春笋般涌现。
为更好地推动得物 App 社区业务的倒退,得物也自研合乎得物需要的视频编辑工具。咱们致力于打造一个“更快、更强”的视频编辑工具。
二、视频编辑工具介绍
为了让大家更好地理解得物 App 的视频编辑工具,咱们先简略介绍一下视频编辑工具的次要性能。
上面是得物 App 视频编辑工具的次要性能:
视频编辑工具的重点如下:
-
视频编辑工具须要操作的资源:
- 文字:包含一般的文字、非凡的艺术字、花字等等;
- 图片:包含动态图,如 JPEG/PNG 等等,也包含 HEIC/GIF 等动态图;
- 视频:包含各种各样的视频(各种编码和封装格局),支流的格局个别是 MP4 的封装格局、H264 视频编码格局、AAC 音频编码格局等等;
- 音频:包含各种各样的音频(各种编码和封装格局),当然视频当然也是蕴含音频轨道的。
-
视频编辑工具次要的操作形式:
- 操作图片、视频帧:咱们晓得视频是一帧一帧的图片组成的,所以操作视频帧和操作图片是一样的情理,咱们通过增加一些特效在图片和视频帧下面,实现一些乏味的成果来吸引用户。
- 操作音频:支流的操作音频形式如倍速、调整音量、变调等等,都是现今短视频的次要玩法。
- 视频编辑工具最终生成的是一个新的视频,这个视频将特定的资源利用一些特效生成一个新的视频。
上面的流程图能够很不便地让大家理解视频编辑的工作流程。为了不便,咱们输出一个视频,加上一些特效,生成一个新的视频。
从下面的流程能够看进去,原始视频 A.mp4 通过解封装拆散出音频轨道和视频轨道,对它们解码之后,对音频数据利用音频特效、对视频帧数据利用视频特效,而后编码封装合成一个新的视频。当然解码和编码都是有一个队列管制的,流程图上标注了,没有深刻开展,大家理解即可。
通过下面的介绍,大家对视频编辑工具有了大略得理解,其实掂量一个视频编辑工具做得好不好,次要从上面这几个方面着手:
- 内存占用状况
- 导出视频的速度如何
- 导出视频的清晰度如何
上面从这三方面具体开展给大家论述得物 App 的视频编辑工具优化的心路历程。
三、内存优化
性能是所有程序好不好的首要指标,一个工具即便性能再弱小,然而一点就解体,或者用着用着内存暴涨、利用卡死,预计这个利用不能称为一个优良的利用,上面咱们具体谈一谈视频编辑工具的优化检测计划。
优化内存从良好的编码习惯开始,尤其对音视频这种对内存需要十分高的利用而言。例如一个 1080 1920 的视频,解码进去原始数据一帧图片大小也是 1080 1920,占用内存是 1080 1920 (8 * 3 ) / 8 = 5.93 MB,一个视频帧就占用这么大,1 秒个别有 30 帧,那得占用 177.9MB,如果不加管制,那不论多高性能的手机也经不住这样的折腾。心愿上面的内存检测和优化计划能够给你带来一些帮忙。
3.1 正当设计队列
下面咱们在介绍视频编辑流程的视频谈到了解码队列和编码队列的概念。其实队列这个概念在音视频中应用十分频繁,正是因为内存的限度,所以才引入队列这个管制形式。大家可能还有点懵,然而看完上面的流程图,我置信你肯定会恍然大悟。
咱们仅选取解码的局部来剖析一下队列的重要利用。
在视频编辑工具中有几个重要的队列:
-
解码过程中:
- Video Packet Queue:视频解码之前 Packet 寄存的队列,个别倡议的队列大小是 100
- Audio Packet Queue:音频解码之前 Packet 寄存的队列,个别倡议的队列大小是 150
- Video Frame Queue:视频解码之后 Frame 寄存的队列,个别倡议的队列大小是 3
- Audio Frame Queue:音频解码之后 Frame 寄存的队列,个别倡议的队列大小是 8
-
编码过程中:
- Encode Video Packet Queue:视频编码之后 Packet 寄存的队列,个别倡议的大小是 100
- Encode Audio Packet Queue:音频编码之后的 Packet 寄存的队列,个别倡议的大小是 150
依照下面的形式设计队列的大小,能够在保障性能失常的状况下最大水平的升高内存占用,晋升用户体验。
3.2 排查内存透露
Android 上排查内存透露的形式有很多,这里介绍两种:
- Asan 检测
- Profile 检测
Asan 全称是 AddressSanitizer 是一种基于编译器的疾速检测的工具,用于检测原生代码中的内存谬误问题,Asan 能够解决如下四种外围问题:
- 堆栈和堆缓冲区上溢、下溢
- 开释之后堆从新应用问题
- 超过范畴的堆栈应用状况
- 反复开释、谬误开释问题
Asan 的应用形式倡议参考 google 官网文档,这儿就不多作介绍了:
https://github.com/google/san…
对于 Profile 的应用,如果须要检测 Native 内存应用状况,须要满足 API>=29,大家在应用的时候须要十分留神。
上面是咱们在 demo 中利用 Asan 抓取的堆栈:
20042-20042/? A/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
20042-20042/? A/DEBUG: Build fingerprint: 'samsung/t2qzcx/t2q:11/RP1A.200720.012/G9960ZCU2AUGE:user/release-keys'
20042-20042/? A/DEBUG: Revision: '13'
20042-20042/? A/DEBUG: ABI: 'arm64'
20042-20042/? A/DEBUG: Timestamp: 2021-09-17 00:32:31+0800
20042-20042/? A/DEBUG: pid: 19946, tid: 20011, name: AudioTrack >>> com.jeffmony.audioplayer <<<
20042-20042/? A/DEBUG: uid: 10350
20042-20042/? A/DEBUG: signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr --------
2021-09-17 00:32:31.157 20042-20042/? A/DEBUG: Abort message: '=================================================================
==19946==ERROR: AddressSanitizer: heap-use-after-free on address 0x004ac1e41080 at pc 0x007157f69580 bp 0x00705c0bb350 sp 0x00705c0bab08
READ of size 1792 at 0x004ac1e41080 thread T32 (AudioTrack)
#0 0x7157f6957c (/data/app/~~G094WKQQj7KZvdhvGYDLDA==/com.jeffmony.audioplayer-kcu1nmgzpBIQDRJDxCJDOQ==/lib/arm64/libclang_rt.asan-aarch64-android.so+0x9f57c)
#1 0x706549c228 (/data/app/~~G094WKQQj7KZvdhvGYDLDA==/com.jeffmony.audioplayer-kcu1nmgzpBIQDRJDxCJDOQ==/lib/arm64/libltpaudio.so+0x14228)
#2 0x706549bcd4 (/data/app/~~G094WKQQj7KZvdhvGYDLDA==/com.jeffmony.audioplayer-kcu1nmgzpBIQDRJDxCJDOQ==/lib/arm64/libltpaudio.so+0x13cd4)
#3 0x70654994f0 (/data/app/~~G094WKQQj7KZvdhvGYDLDA==/com.jeffmony.audioplayer-kcu1nmgzpBIQDRJDxCJDOQ==/lib/arm64/libltpaudio.so+0x114f0)
#4 0x70654a9cbc (/data/app/~~G094WKQQj7KZvdhvGYDLDA==/com.jeffmony.audioplayer-kcu1nmgzpBIQDRJDxCJDOQ==/lib/arm64/libltpaudio.so+0x21cbc)
#5 0x70654a91d4 (/data/app/~~G094WKQQj7KZvdhvGYDLDA==/com.jeffmony.audioplayer-kcu1nmgzpBIQDRJDxCJDOQ==/lib/arm64/libltpaudio.so+0x211d4)
#6 0x715af9d188 (/system/lib64/libwilhelm.so+0x1c188)
#7 0x71570ea290 (/system/lib64/libaudioclient.so+0x8b290)
#8 0x71570e9480 (/system/lib64/libaudioclient.so+0x8a480)
#9 0x7156b664d4 (/system/lib64/libutils.so+0x154d4)
#10 0x71593e9974 (/system/lib64/libandroid_runtime.so+0xa5974)
#11 0x7156b65db0 (/system/lib64/libutils.so+0x14db0)
#12 0x7156ace234 (/apex/com.android.runtime/lib64/bionic/libc.so+0xb6234)
#13 0x7156a68e64 (/apex/com.android.runtime/lib64/bionic/libc.so+0x50e64)
0x004ac1e41080 is located 0 bytes inside of 1792-byte region [0x004ac1e41080,0x004ac1e41780) freed by thread T32 (AudioTrack) here: #0 0x7157f74c64 (/data/app/~~G094WKQQj7KZvdhvGYDLDA==/com.jeffmony.audioplayer-kcu1nmgzpBIQDRJDxCJDOQ==/lib/arm64/libclang_rt.asan-aarch64-android.so+0xaac64) #1 0x70654a6d2c (/data/app/~~G094WKQQj7KZvdhvGYDLDA==/com.jeffmony.audioplayer-kcu1nmgzpBIQDRJDxCJDOQ==/lib/arm64/libltpaudio.so+0x1ed2c) #2 0x70654a6af0 (/data/app/~~G094WKQQj7KZvdhvGYDLDA==/com.jeffmony.audioplayer-kcu1nmgzpBIQDRJDxCJDOQ==/lib/arm64/libltpaudio.so+0x1eaf0) #3 0x706549bf4c (/data/app/~~G094WKQQj7KZvdhvGYDLDA==/com.jeffmony.audioplayer-kcu1nmgzpBIQDRJDxCJDOQ==/lib/arm64/libltpaudio.so+0x13f4c) #4 0x706549bcd4 (/data/app/~~G094WKQQj7KZvdhvGYDLDA==/com.jeffmony.audioplayer-kcu1nmgzpBIQDRJDxCJDOQ==/lib/arm64/libltpaudio.so+0x13cd4) #5 0x70654994f0 (/data/app/~~G094WKQQj7KZvdhvGYDLDA==/com.jeffmony.audioplayer-kcu1nmgzpBIQDRJDxCJDOQ==/lib/arm64/libltpaudio.so+0x114f0) #6 0x70654a9cbc (/data/app/~~G094WKQQj7KZvdhvGYDLDA==/com.jeffmony.audioplayer-kcu1nmgzpBIQDRJDxCJDOQ==/lib/arm64/libltpaudio.so+0x21cbc) #7 0x70654a91d4 (/data/app/~~G094WKQQj7KZvdhvGYDLDA==/com.jeffmony.audioplayer-kcu1nmgzpBIQDRJDxCJDOQ==/lib/arm64/libltpaudio.so+0x211d4) #8 0x715af9d188 (/system/lib64/libwilhelm.so+0x1c188) #9 0x71570ea290 (/system/lib64/libaudioclient.so+0x8b290)
显示 message 是:heap-use-after-free on address 0x004ac1e41080 阐明是应用了曾经开释掉的内存了,再持续看,这个内存具体在什么中央被开释的?0x004ac1e41080 is located 0 bytes inside of 1792-byte region [0x004ac1e41080,0x004ac1e41780) Asan 一个很大的劣势就是能够追踪内存开释的门路,防止出现内存透露和野指针问题,特地是野指针,一旦呈现特地难排查,几乎是 C ++ 开发的噩梦,心愿大家用好工具,同时造就良好的 C ++ 编码习惯。
3.3 优化线程
另一个影响内存的重要因素是线程,视频编辑工具波及到的线程十分多,线程的应用得遵循一些根本的准则:
- 尽量少创立线程
- 尽量少应用 pthread_mutex_t
- 本着性能断绝准则应用线程
- 能同步就别异步
以编辑模块为例,这儿列一下咱们应用到的所有线程:
- GL 解决线程
- 视频解封装线程
- 视频中视频轨道解码线程
- 视频音频轨道解码线程
- 抽取缩略图线程
- 音频编码线程
- 视频编码线程
- 视频封装线程
如果插入了独立的音频文件,还须要增加两个额定的线程:
- 音乐文件播放线程
- 音乐文件解码线程
下面列出的是一个视频编辑工具能失常工作所必备的起码线程,如果你的视频编辑工具中多了什么线程,咱们倡议能够适当优化一下,毕竟少一个线程,能够少一分开销,而且少一分线程同步的工作。
咱们在底层也依照 Android 的音讯机制重写了一套 C ++ 层的音讯散发 SDK,这个咱们后续会另外分享文章阐释咱们定制的音讯散发 SDK,这儿点到为止。
四、晋升导出视频的速度
咱们应用视频编辑工具,最终是心愿导出一个视频,如果这个导出的过程很慢,那必定是无法忍受的,从下面的介绍咱们已知视频的导出须要通过“解码——利用特效——编码”的过程,其中解码和编码这两个过程对速度的影响至关重要。因为解码和编码视频须要消耗大量的资源,目前次要有两种形式——“软解 / 编码”和“硬解 / 编码”。
如果你应用过 FFmpeg 或者其余应用 CPU 进行视频编解码的来解决视频的话,你可能曾经遇到了处理速度慢的问题。这次要是因为软编码和软解码应用 CPU 进行运算,而 CPU 在解决视频上的速度远低于 DSP 芯片;简而言之“软解 / 编码”次要通过 CPU 来工作,通过 CPU 来主导大量的计算工作,是原始的解决形式,当然消耗的工夫也比拟长;“硬解 / 编码”是通过 GPU 来解决,GPU 是专用的图形处理芯片,对视频的解码和编码有专门的优化,所以编码和解码的速度十分快。
Android 上应用 MediaCodec 来实现“硬解 / 编码”,iOS 上应用 VideoToolBox 来实现“硬解 / 编码”,这里着重介绍 Android 上编码解码的速度优化。
从下面的流程咱们能够看出,编码在解码的前面, 一个时长 60s(30fps)的视频,须要解码 1800 帧 ,而后编码 1800 帧视频能力残缺生成另外一个视频,这样串行的期待是耗时的次要起因。
这时候咱们参考多线程计划,将一个 60s 的视频均分为两段,而后这两段视频同时进行解码操作,生成导出了两个 30s 的长期缓存视频文件,随后将这两个 30s 的视频合并为一个 60s 的 B.mp4 视频,最初删除长期缓存文件,这样咱们只须要同时解决 900 帧的数据,实践上能够晋升一倍的导出速度。
这就是并行导出,上面是得物 App 并行导出的根本流程。
首先咱们要明确导出视频是须要耗费资源的,这个资源就是 MediaCodec,最终是送入到 GPU 中解决,一个手机中的 MediaCodec 实例是无限的,失常状况下,一个手机能够提供的 MediaCodec 实例最多有 16 个,如果以后应用的 MediaCodec 实例超过 16 个,那么手机将无奈失常工作。MediaCodec 资源是手机中的所有 App 独特持有。所以并行分段的个数不是越多越好。
- 只有一段,须要两个 MediaCodec(一个用来解码视频,一个用来编码视频),留神:音频的解码和编码能够不要用 MediaCodec,毕竟音频的耗时少多了,不是瓶颈。
- 分成两段须要四个 MediaCodec,分成三段须要六个 MediaCodec,分成四段须要八个 MediaCodec,以此类推。
上面是并行导出的测试后果:
两段并行速度晋升 50% ~ 70%,内存减少 20%,三段并行速度晋升 60% ~ 90%,内存减少 80%;并行超过三段的话就无奈显著晋升速度了。咱们比拟倡议并行两段,在一些性能很好的机型上并行三段。
如果有些同学对视频导出过程中文件操作还有疑难的,上面的示意图能够比较清楚地看出并行导出操作本地文件的过程:
- 并行导出的过程中,生成了两个临时文件
- 并行导出实现后,这两个临时文件合并为一个新的文件,两个长期生成的文件被删除了(节俭用户贵重的存储空间)
- 原始文件 jeffmony_out.mp4 并没有被删除 / 批改
Tips:目前咱们在处理过程中生成的临时文件和最终的适配文件都会保留在 /sdcard/Pictures/duapp/Compile/ 下,而在解决实现后的临时文件清理过程会触发在某些机型上的爱护机制,倡议后续调整到 App 的公有目录下。
当然还有其余的晋升导出速度的倡议,例如在视频帧特效解决的过程中,咱们倡议:
- 尽量采纳 FBO/EBO/ABO 形式解决 texture
- 纹理如果过大要进行压缩
- 严禁采纳 glFinish()
这些做法都是咱们在视频编辑开发过程中的切实教训,心愿能给大家带来一些帮忙。
五、晋升导出视频的清晰度
一个视频编辑性能是否足够优良,其中的一个重要指标就是同等条件下导出的视频是否足够分明,通常而言,掂量视频是否清晰的有两种形式:
- 主观规范:找一些用户观看不同的视频,依据用户的观感输入视频清晰度的比照后果,用户个别依据色调、画面亮度、柔和度等来评估清晰度。
- 主观规范:利用算法计算视频画面质量分,目前比拟举荐 Netflix 推出的开源库 VMAF 来计算视频帧的品质分。
实际上主观规范是比拟精确的,然而可操作性比拟差,特地是解决海量视频的时候,须要大量的人力,无奈无效发展,因而日常工作中还是举荐主观规范进行海量计算,主观规范进行重点判断。具体的能够联合业务的重要水平来发展。
上面联合咱们理论的工作给出具体晋升视频清晰度的形式:
-
视频根底编码信息优化
- Profile 优化 :Profile 有三种 Level,别离是 Baseline、Main、High,其中 Baseline Profile 对应清晰度最低,Android 3.0 之后的版本都反对的,Main Profile 清晰度比 Baseline Profile 清晰度要好,然而从 Android 7.0 之后才反对,High Profile 清晰度最高,也是从 Android 7.0 之后才反对。咱们在设置 Encoder Profile Level 之前,须要判断一下以后是否反对。
- Bitrate 码率设置: 视频码率是视频数据传输时单位工夫内传送的数据位数。单位是 kbps,顾名思义,码率越大,单位工夫填充的数据就越多,视频品质就越高。但码率也不是设置的越大越好,超过必要限度,对视频画质的晋升已不显著,倡议采纳适合的 factor 来调整码率。Bitrate = width height frameRate * factor,其中 factor=0.15。
- Bitrate Mode: 有三种通过的编码模式——VBR(可变码率)、CBR(固定码率)、ABR(平均码率),其中 ABR 是最好的形式,能够兼顾品质和视频大小。
- B 帧设置: 视频有 I 帧、P 帧、B 帧形成,其中 I 帧最大,P 帧次之,B 帧最小,咱们在编码时尽量多设置 B 帧(在正当的范畴内),并不会升高清晰度,然而能够大大降低视频的大小,这样咱们就能够相应地调大码率,最终实现了晋升清晰度的指标。
- HEVC 编码优化: 应用 HEVC 编码,能够保障在不减少文件大小的状况下,大大晋升视频的清晰度。在雷同的图像品质下,HEVC 编码的视频比 H.264 编码的视频约缩小 40%
-
色调调优
- 综合调整亮度、对比度、色温、饱和度、锐度等色彩参数,进而优化整体的视频画面,让视频画面看上去“更清晰”。
-
超分算法 :采纳 ESRGAN 算法,利用机器学习的劣势对图片和视频进行去模糊、Resize、降噪、锐化等解决,重建图片,实现对图片的超分辨率解决。
- 特征提取:计算噪点
- 非线性映射:放大,模糊化噪点
- 图像重建:差分,平滑适度,去噪
- 上面是应用超分算法解决前后的比照图,能够很显著地看出左边的图更加清晰,少了很多噪点、图片更亮、适度更平滑。
如果大家想理解视频清晰度优化的技术细节,能够参考文章 – 视频清晰度优化指南
六、总结
本文开篇从介绍得物 App 的次要性能开展,提出了视频编辑工具优化的三个维度:
- 优化内存占用
- 晋升视频导出速度
- 晋升导出视频的清晰度
其中在“晋升视频导出速度”时重点谈到了“并行导出”的技术计划,从最终的后果来看,视频导出速度的晋升非常明显,同时也十分分明地解释了“并行导出”过程中为什么生成临时文件?为什么有必要在导出实现之后删除临时文件?尽力给用户带来较好的体验。
最初在“晋升导出视频的清晰度”中重点提到的超分算法利用成果晋升显著,超分之后的视频帧相比原帧图更加清晰、噪点更少,而且细节局部更加实在。
后续咱们还会联合 AR 特效输入更多有意义的技术分享,敬请期待。
* 文 /Jeff Mony
关注得物技术,每周一三五晚 18:30 更新技术干货
要是感觉文章对你有帮忙的话,欢送评论转发点赞~