文章

透明mp4

透明mp4

透明视频

使用案例

  • 陌陌

陌陌的资源目录 sdcard/immomo/gift_video_effect/..

开源

  • Mp4Composer-android

https://github.com/MasayukiSuda/Mp4Composer-android,基于 MediaPlayer>

remix 用的这个,基于 ExoPlayer

VAP 是企鹅电竞开发,用于播放特效动画的实现方案。具有高压缩率、硬件解码等优点。同时支持 iOS,Android,Web 平台。; 而且 VAP 还能在动画中融入自定义的属性(比如用户名称, 头像)

注意

  1. 视频编码手机默认的 MediaPlayer 兼容有很大问题
  2. 集成了 ExoPlayer 后会增大 APK 体积大概 0.4M

兼容问题

  1. 视频编码问题。采用系统 MediaPlayer 播放的话,设计采用 MPEG-2 编码的格式的 mp4 文件,在有些手机是不支持的,大部分手机用 H.264 都支持(mpeg-2 比 h.264 文件要小很多)
  2. 视频尺寸问题。oppo a37 播不了长尺寸视频,替换 exoplayer 后 oppo a37 能播短尺寸 +h.264 的,长尺寸和 mpeg-2 的都播不了;如果 mp4 文件的尺寸太大 (1440x1560),会出现有声音没画面的现象,改短一点尺寸 (1440x1280) 就可以了(如 vivo x5 pro)
  3. GalaxyA10 系统播放器不支持 h.264 的视频,mpeg-2 也不支持

nqxj4

读取 alpha 通道有问题

存在的问题:安卓版本读取 alpha 通道有问题

h9xft

正确的是黑色部分应该全透明;纯白→纯黑   0%透明度→100%透明度

zn8k0

Ref

VAP

https://github.com/Tencent/vap

相比 SVGA 的效率提升有多少

1
2
3
4
5
6
7
8
1 原理:
svga 原理与lottie相识,都是矢量动画方案,都是使用CPU进行绘制(开启硬件加速也只是canvas的硬件加速),从实现原理上来说,支持的特效样式有限,而且复杂动画性能低下(这个只测试了lottie,结论不一定适用svga)。

2 单帧解码时间对比:
矢量动画使用CPU做复杂动画与GPU播放视频(vap实质是视频方案)单帧耗时对比,CPU可能需要10+ms,动画越复杂时间越长(当然简单的矢量动画效率会更高,但限定在简单的动画效果,这个只测试了lottie,结论不一定适用svga);而视频解码1-3ms,无论内容多复杂,视频解码时间基本不会有太大波动。

3 实现效果上对比:
矢量动画方案天生决定某些特效无法支持,或者说代价异常高(比如复杂的粒子特效)。而vap实质是视频方案,理论上所有的视觉特效都能支持。

Vap 坑

VAP 播放 Assets 目录下的视频一定的几率失败

问题描述

  • 在 Assets 目录下的同一个视频文件反复播放,间隔 100 毫秒(不同视频文件连续播放也会,即使增加播放时间间隔到 2s),有一定的概率会出现:MediaExtractor exception:Failed to instantiate extractor

除了 Failed to instantiate extractor ,其他异常也很多。大部分没问题,偶尔就会出现异常

运行环境

  • Android 11 Google Pixel 3,但是其他所有手机也会这样,不同的系统版本都会

相关日志

1
2
3
4
5
6
7
8
9
10
11
12
2021-04-29 11:37:21.635 24084-26873/? E/AnimPlayer.HardDecoder: MediaExtractor exception e=java.io.IOException: Failed to instantiate extractor.
java.io.IOException: Failed to instantiate extractor.
at android.media.MediaExtractor.setDataSource(Native Method)
at com.tencent.qgame.animplayer.file.AssetsFileContainer.setDataSource(AssetsFileContainer.kt:42)
at com.tencent.qgame.animplayer.util.MediaUtil.getExtractor(MediaUtil.kt:40)
at com.tencent.qgame.animplayer.HardDecoder.startPlay(HardDecoder.kt:83)
at com.tencent.qgame.animplayer.HardDecoder.access$startPlay(HardDecoder.kt:28)
at com.tencent.qgame.animplayer.HardDecoder$start$1.run(HardDecoder.kt:44)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.os.HandlerThread.run(HandlerThread.java:67)

播放失败的文件

file:///android_assets/pk/live_pk_begin.mp4

问题分析

  • 1.通过 Vap 播放失败回调接口上报数据,并分析

QbStatService.onEvent(“vap_play_failed”, mAnimResPath, code, msg);

  • 2.从 数据 来看,99.9% 都是播放 Assets 目录下的资源
  • 3.从 vap 的 issue 作者的回答可以佐证

播放接口:fun startPlay(assetManager: AssetManager, assetsPath: String)

这里应该 MediaExtractor 的兼容问题,目前想到的解决方法是手动实现 mp4 封装,但工作量比较大,手动实现 mp4 解封装需要充分测试

解决方案

  • 1.应用启动时将 vap 视频资源从 assets 目录拷贝到 sdcard
  • 2.播放 vap 视频路径改为 fun startPlay(file: File) 的方式

个人猜想

  • 1.存放在 assets 目录下的文件,在编译打包时会将文件压缩以降低安装包大小,使用或者读取数据时需要先解压。所以播放 sdcard 下的文件没问题。

Android 文件大小限制 在 Android 2.3 以前的任何压缩的资源的原始大小超过 1M 将不能从 APK 中读出,如果你使用 AssetManager 或 Resources classes 方法来获取 InputStream,将抛出 java.io.IOException 的异常如下 DEBUG/asset(1123): Data exceeds。

1
2
3
4
5
6
7
8
9
       /* these formats are already compressed, or don't compress well */ 
  static const char* kNoCompressExt[] = { 
  ".jpg", ".jpeg", ".png", ".gif", 
  ".wav", ".mp2", ".mp3", ".ogg", ".aac", 
  ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet", 
  ".rtttl", ".imy", ".xmf", ".mp4", ".m4a", 
  ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2", 
  ".amr", ".awb", ".wma", ".wmv" 
  };

从上面截图来看,我们已经是.mp4 文件后缀了,应该不在压缩的资源之列。那么问题来了,到底哪里出轨了?

1
2
3
4
5
6
7
8
9
class AssetsFileContainer(assetManager: AssetManager, assetsPath: String): IFileContainer {

    ...

    private val assetFd: AssetFileDescriptor = assetManager.openFd(assetsPath)
    private val assetsInputStream: AssetManager.AssetInputStream =
        assetManager.open(assetsPath, AssetManager.ACCESS_STREAMING) as AssetManager.AssetInputStream

   ...
本文由作者按照 CC BY 4.0 进行授权