热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

英语流利说Android音视频处理相关实践与优化

前言:移动互联网的火爆改变了人们一系列的生活方式,从社交、购物、教育等方方面面渗透进大众的生活,移动端的迅速崛起和高速发展离不开移动端背后的技术演进和迭代,产品更新迭代过


前言:

移动互联网的火爆改变了人们一系列的生活方式,从社交、购物、教育等方方面面渗透进大众的生活,移动端的迅速崛起和高速发展离不开移动端背后的技术演进和迭代,产品更新迭代过程中如何优化它的性能?实际实践中遇到的坑又该如何处理?本文是根据七牛架构师实践日上海站,英语流利说Android 端开发人员王聪武的演讲内容进行整理,介绍了英语流利说移动应用在Android音视频处理相关实践。



英语流利说 APP 有一个配音功能,功能简而言之就是将一系列录音片段和视频背景声做混合,最后合成输出视频。本次分享『音视频相关的实践与优化』主要讲得就是对功能实现与优化的一个过程,就此分享出来希望对大家有所帮助。


一、方案的考量指标

实现功能需要选择方案,选择方案需要确立对应的指标。就此归纳了如下四个指标。

内存占用:

Android App 的内存占用一般分两种 Java Heap 和 Native Heap,可以采用 Android Monitor 和 GT 来看,也可以自己动手通过 Android Api 周期性采集数据。

执行耗时: 

方法耗时的统计通用的做法就是打 log 记录耗时。倘若想要整体了解一系列耗时,其中 Java 层可以用 Device Monitor 提供的 method profiling,native 可以采用 valgrind。

代码大小:

一般自己实现的代码量有限,这里指特指方案依赖 Library 的大小。library 的种类也一般分两种. jar 与 .so。jar 的大小会影响 dex method count,method count 过大不仅 Apk 会变大,也会影响运行时性能。so 的问题则是,需要多种 cpu 架构会让 so 的大小成倍放大,最终导致 Apk 急剧膨胀。

复杂度:

包含两个考量点,一是开发语言,java 与 c/c++ 的选择,一般情况下后者会更加复杂。  二是实现的代码量,实现所需的代码量一般越短越易于理解与维护。


二、方案优化的方法

代码指令上面的优化:一般可以选取更合适的算法或者内存占用更少的数据结构,如果是 native 层可以试试 neon 指令有没有帮助。

预处理:把原先需要客户端及时处理生成的,交给服务单预先生成好,提前下发客户端避免客户端处理耗时,空间换时间。

后台处理:也是预处理一种,区别在于这个预处理是客户端将任务放在后台预处理,当需要用到相关数据的时候,可以 join 这个后台任务

不完美的替代方案:曲线优化选用一个性能更好的近似方案来替代。


三、案例分析

案例一

0?wx_fmt=jpeg


配音第一步,需要将多段人声与背景音做混合。

0?wx_fmt=jpeg


第一个方案需要引入 AacEncoder Mp3Decoder FlacDecode,虽然有现成的实现,但是三者 api 接口不一致,有一定使用成本,并且在 java 层来做 mix 效率也不行,所以不考虑。第二个方案采用 ffmpeg命令行的方式调用,会因为命令的不灵活无法一条命令完成,需要产生一些中间文件,导致产生很多多余步骤。第三个方案采用 Sox/libSox 来,Sox 被称为音频处理的瑞士军刀,专注于音频领域,整个项目代码量不大能够通读了解以便定制开发。针对这个功能参考 Sox 中 mix 的实现调用 libSox 来实现 mix 即可。

0?wx_fmt=jpeg


libSox 中定义支持编解码的支持很简单,只需要在 soxconfig.h 开启配置,并且将相对应的 编解码库挂载编译即可,而后 libSox 则会通过统一的封装自动处理编码解码。


0?wx_fmt=jpeg


libSox 将音频处理抽象成添加 effect,将多个 effect 添加到 chain 上处理即可。其中 input_combiner_effect_fn 会提供读取输入文件进行 mix的 effect,而 output_aac_effect_fn 则会提供输出到 aac 文件的 effect。( aac 在 libSox 里面的实现是依赖 ffmpeg 的,所以需要手工引入编码器,自定义 effect 来做输出处理)


0?wx_fmt=jpeg


effect_hanlder 将音频处理抽象出 start drain stop 这几个阶段,libSox 会将数据流依次流经这几个 callback 进行处理。


0?wx_fmt=jpeg


这里 mix 操作是将音频 sample 进行加和进行处理。在 combinder_drain 中将 ibuf 中的音频进行加和后输出到 obuf 即可。


0?wx_fmt=jpeg


针对于 mix 这个场景主要耗时分布在 IO 读写文件、编解码文件、mix 操作。先针对 mix 操作进行优化,会发现 在 combinder_drain 中我们对音频进行加和,将 sample 转成 double 后进行处理的方式是有优化空间的。可以采用 Neon 指令中 VQADDS32 指令,该指令会自动对溢出进行处理,避免原先需要转换成浮点型进行计算,并且在计算后还需要比较是否有溢出。用 neon 实现替换后,会发现对应的汇编指令少了不少,并且整体 mix 的操作耗时减少了 10%-20%左右。

案例二

0?wx_fmt=jpeg


配音第二步骤,需要将处理完的音频 mux 到视频上。mux 也有两个方案,一是 Java 的 Mp4parser 二是 ffmpeg。


0?wx_fmt=jpeg


首先比较一下Mp4Parser和FFmpeg的代码库大小,前者大小会小很多。当然此处 ffmpeg 这个尺寸大小是因为没有经过细致裁剪导致的,如果支持 mux 可能只需要 几百kb,但是因为 so 的大小和支持的 cpu 架构有关,所以整体上的大小肯定是 ffmpeg 大的。


0?wx_fmt=jpeg


然后再对比一下性能。发现首次调用的时候两者耗时差距比较大,在多次调用后差距逐渐缩小。就此倘若解决这个首次调用的耗时问题,选用 mp4parse 也未尝不可。


0?wx_fmt=jpeg


使用 Method Profiling 对 mp4parse 的调用进行分析,会发现首次运行主要耗时在 getResourceAsStream 这个方法,而 getResourceAsStream 这个方法在 Android 上的实现非常慢。所以解决这个问题也很简单,参照 jodatime-android 的实现,用 android get resource 的方式即可。


案例三

0?wx_fmt=jpeg


Mix+Mux 的耗时是受到视频长度,音频长度影响的,所以总耗时无法控制,需要用进度动画来提示用户,但是这在一定程度上是打断用户操作。


0?wx_fmt=jpeg


就此曲线优化,在预览的时候并不合成,而是定制播放器来实现实时的预览。通过定制 exoPlayer 支持播放时指定外部音轨和 注入 MixAudioProcessor 来实现实时混合。


案例四


0?wx_fmt=gif


配音作品中加上了添加版权信息。


0?wx_fmt=jpeg


采用 ffmpeg 的实现,取视频最后一帧作为背景,并且动态生成版权消息绘制到图片上,最后将图片生成1.5s的视频,将这视频拼接到作品上即可。其中最后一帧图片的获取转为服务端生成下发,可以避免30%的耗时。


0?wx_fmt=jpeg


虽然以上优化减少了一定耗时,但是视频编码操作仍然非常耗时,所以采用 rxJava 提供的 BehaviorSubject 将生成操作提交交给后台进行预处理,而后将依赖于他的合成操作用 flatMap 串联即可。





推荐阅读
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 本文概述了JNI的原理以及常用方法。JNI提供了一种Java字节码调用C/C++的解决方案,但引用类型不能直接在Native层使用,需要进行类型转化。多维数组(包括二维数组)都是引用类型,需要使用jobjectArray类型来存取其值。此外,由于Java支持函数重载,根据函数名无法找到对应的JNI函数,因此介绍了JNI函数签名信息的解决方案。 ... [详细]
  • 本文总结了在编写JS代码时,不同浏览器间的兼容性差异,并提供了相应的解决方法。其中包括阻止默认事件的代码示例和猎取兄弟节点的函数。这些方法可以帮助开发者在不同浏览器上实现一致的功能。 ... [详细]
  • struts2重点——ValueStack和OGNL
    一、值栈(ValueStack)1.实现类:OGNLValueStack2.对象栈:CompoundRoot( ... [详细]
  • 近来有一个需求,是需要在androidjava基础库中插入一些log信息,完成这个工作需要的前置条件有编译好的android源码具体android源码如何编译,这 ... [详细]
  • 开发笔记:图像识别基于主成分分析算法实现人脸二维码识别
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了图像识别基于主成分分析算法实现人脸二维码识别相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • 本文介绍了Android中的assets目录和raw目录的共同点和区别,包括获取资源的方法、目录结构的限制以及列出资源的能力。同时,还解释了raw目录中资源文件生成的ID,并说明了这些目录的使用方法。 ... [详细]
  • Spring框架《一》简介
    Spring框架《一》1.Spring概述1.1简介1.2Spring模板二、IOC容器和Bean1.IOC和DI简介2.三种通过类型获取bean3.给bean的属性赋值3.1依赖 ... [详细]
  • OpenMap教程4 – 图层概述
    本文介绍了OpenMap教程4中关于地图图层的内容,包括将ShapeLayer添加到MapBean中的方法,OpenMap支持的图层类型以及使用BufferedLayer创建图像的MapBean。此外,还介绍了Layer背景标志的作用和OMGraphicHandlerLayer的基础层类。 ... [详细]
  • 使用freemaker生成Java代码的步骤及示例代码
    本文介绍了使用freemaker这个jar包生成Java代码的步骤,通过提前编辑好的模板,可以避免写重复代码。首先需要在springboot的pom.xml文件中加入freemaker的依赖包。然后编写模板,定义要生成的Java类的属性和方法。最后编写生成代码的类,通过加载模板文件和数据模型,生成Java代码文件。本文提供了示例代码,并展示了文件目录结构。 ... [详细]
  • 今天就跟大家聊聊有关怎么在Android应用中实现一个换肤功能,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根 ... [详细]
  • 在工作中,遇到需要将excel表中的特定数据提取出来,并将数据以键值对的形式存储到map集合中。因为我用的是maven管理的jar包,所 ... [详细]
author-avatar
爱你116564
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有