Android视频播放方案选择——深刻分析android平台的视频播放优缺点

https://zhuanlan.zhihu.com/p/27029577?utm_source=qq&utm_medium=social

Android我还可以相信你多少系列文章二之音视频播放

  音频视频播放在现在的应用里面很常见,传统应用发展到一定阶段多少会引入音视频资源,特别是现在短视频被看作下一个增长爆发点,和之相关的创业层出不穷,作为开发者如何进行音视频技术选型非常关键

MediaPlayer和VideoView给我们提供了非常方便的播放音视频的能力,几乎不需要要写几行代码就可以完成。

我们也可以使用MediaPlayer结合SurfaceView或者TextureView来实现视频播放,本质和VideoView是一样的,不过有更多的灵活性。

正因为封装性太强,意味着定制化变弱。MediaPlayer提供的setDataSource方法支持http,file,content等协议,但仍然无法应对复杂的需求。所以更灵活的AudioTrack的出现,可以让我们直接传送解码后的byte[]给他,带来的问题就是自己要做解码。解码不是件简单的事情,往往我们利用MediaCodec(Android4.1增加)或者外部解码库(比如ffmpeg)来实现。自己来实现解码要特别注意不要丢失了硬件加速,音频软解码还好,视频解码软解码对CPU压力会大很多。

在做音视频业务的时候,经常会遇到这样几个问题需要设置代理,或者边播边缓存,缓存加密,失败重试,网络优化等等

因为我们无法干涉MediaPlayer的网络请求部分,所以一般会将原始的播放地址http://xxx.com/playurl转换成本机代理地址http://127.0.0.1:port?url=htt...,这样MediaPlayer就会来请求本机port端口上面起的一个代理服务,在这个代理端可以做很多优化逻辑,比如给真正发往服务端的请求加上代理;将请求到的数据写入磁盘缓存,这个代理端可以根据磁盘缓存来按需请求服务端(使用http的Range参数);还有一些失败重试等网络优化手段。这个代理层还有个特别的意义甚至可以接管webview里面的audio和video标签请求。

这种实现方式在实际运行中偶尔会出现本机代理无法启动的情况,原因是Socket无法bind到指定端口,往往我们会在bind的时候指定让系统来分配一个可用端口,所以这种失败情况很有可能是root手机或者一些安全管理软件禁用了权限。

特别再说下边播边缓存的实现,缓存文件允许空洞,每个缓存文件配备另外一个内容索引文件,MediaPlayer本身会根据解码情况发出多个带Range的请求,根据内容索引文件来确定当前请求从文件哪个位置读,接下去多少字节从文件读,多少字节从网络读,网络读的部分同时写回文件以保证下次请求可以复用,这样就实现了一个边播边缓存的逻辑,甚至我们还可以给本地缓存文件进行加密。同时这个缓存文件的加载百分比可以用来做UI界面上面的缓冲进度,监控下载速度进行网络请求优化。

2.MediaPlayer的Looper。新手往往可能不关心MediaPlayer的实现,打开它的构造器前面几行代码我们就会看到他默认使用的是当前线程的Looper,如果当前线程不是个Looper线程则使用MainLooper。这一点比较重要,因为我们知道即使MediaPlayer运行在Service里面,实际上还在跑在主线程,这样的结果导致后续所有的MediaPlayer回调操作都跑在主线程,这可能是隐藏的一个定时炸弹。

更优雅的设计我们建议将MediaPlayer的回调和主动操作(stop,reset等操作)都放入work线程,操作的串行化是种最简单的设计,也是最有效的设计。大概的代码形式是这样的:

MediaPlayer在PlayHandlerThread里面初始化,就保证了他里面使用的Looper也是这个PlayHandlerThread的,这样回调就都会在这个线程触发,同时我们也在这个线程里面做setDataSource等主动操作。

3.视频播放本质上也是用MediaPlayer实现的,所以读取数据上面没有特别差异。现在比较热的小视频需要显示在列表页面支持滚动播放一个视频,点击在新页面继续观看,一般采用MediaPlayer+TextureView来实现,MediaPlayer可以采用全局定义唯一一个,只是不同时刻把内容绑定显示在不同的TextureView上而已

4.MediaPlayer最大的问题还是在于其兼容性。从我们的经验来看可能会有这些问题:

——音频格式支持不全(ape,wma等原生系统不支持),

——未缓冲完不开始播放,

——播放过程中突然没有声音,

——播放存在跳帧,

——mediaserver died;

——视频播放只有声音没有画面,

——视频格式兼容性差无法播放等。这些问题在系统基础上基本无法解决。

最头疼的问题是MediaPlayer返回的errorcode很多都是厂家扩展出来的,文档上面提供的几个值基本也是表意不清到底什么问题。这给排查问题带来很大麻烦。最最头疼的是MediaPlayer的EventHandler里面处理异常直接导致程序崩溃,比如像这样:

11-04 13:43:08.966: E/AndroidRuntime(26482): java.lang.RuntimeException: failure code: -32
11-04 13:43:08.966: E/AndroidRuntime(26482):    at android.media.MediaPlayer.invoke(MediaPlayer.java:664)
11-04 13:43:08.966: E/AndroidRuntime(26482):    at android.media.MediaPlayer.getInbandTrackInfo(MediaPlayer.java:1692)
11-04 13:43:08.966: E/AndroidRuntime(26482):    at android.media.MediaPlayer.scanInternalSubtitleTracks(MediaPlayer.java:1851)
11-04 13:43:08.966: E/AndroidRuntime(26482):    at android.media.MediaPlayer.access$600(MediaPlayer.java:529)
11-04 13:43:08.966: E/AndroidRuntime(26482):    at android.media.MediaPlayer$EventHandler.handleMessage(MediaPlayer.java:2198)
11-04 13:43:08.966: E/AndroidRuntime(26482):    at android.os.Handler.dispatchMessage(Handler.java:102)
11-04 13:43:08.966: E/AndroidRuntime(26482):    at android.os.Looper.loop(Looper.java:137)
11-04 13:43:08.966: E/AndroidRuntime(26482):    at android.app.ActivityThread.main(ActivityThread.java:4998)
11-04 13:43:08.966: E/AndroidRuntime(26482):    at java.lang.reflect.Method.invokeNative(Native Method)
11-04 13:43:08.966: E/AndroidRuntime(26482):    at java.lang.reflect.Method.invoke(Method.java:515)
11-04 13:43:08.966: E/AndroidRuntime(26482):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:777)
11-04 13:43:08.966: E/AndroidRuntime(26482):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:593)
11-04 13:43:08.966: E/AndroidRuntime(26482):    at dalvik.system.NativeStart.main(Native Method)

除了反射替换MediaPlayer里面的EventHandler来抓住异常,其他没啥特别好的办法。

遇到这么多问题开发者只能另投他路。市面上采用自解码的方案也很多,比较主流的是使用MediaCodec和ffmpeg,ffmpeg更是因为MediaCodec版本限制原因,加上本来就闻名遐迩,被很多开发者青睐。主流的音视频播放器大部分都是在这个上面进行改造的。

ExoPlayer:https://github.com/google/Exo... ,作为google在MediaCodec的封装也是不错的推荐,相比自己要去抽取ffmpeg代码进行android适配编译来得容易得多

ffmepg:当然也有一些现成的实现:https://github.com/search?o=d... ,最出名的当是ijkplayer,哔哩哔哩出品,跨平台还有弹幕。做视频弹幕真是开箱即用。ffmpeg功能强大,唯一的缺点就是软解码,这也是他兼容性好的原因,我们知道硬解码依赖各个厂家硬件实现兼容性自然就下降了。

在使用自解码的时候,我们建议将自己的MediaPlayer封装成Android高版本上面添加的接口一样

/**
 * Sets the data source (MediaDataSource) to use.
 *
 * @param dataSource the MediaDataSource for the media you want to play
 * @throws IllegalStateException if it is called in an invalid state
 * @throws IllegalArgumentException if dataSource is not a valid MediaDataSource
 */
public void setDataSource(MediaDataSource dataSource)
        throws IllegalArgumentException, IllegalStateException {
    _setDataSource(dataSource);
}

这样做的好处是所有实现都对MediaPlayer透明,我们只需要定义好MediaDataSource接口,后面只需要专注于实现就可以了,比如HttpDataSource,FileDataSource,MemoryDataSource等。

或许自解码会引入更多的不确定性,但是这一步迟早都要迈出去。

(1)推荐小型app或者需求不强的产品使用系统解码,在我上面提到的一些解决思路上进行改进应该能满足绝大部分场景。

(2)而那些音视频作为主业务的产品则不得不面对自解码来提高兼容性。

于是我们又在造轮子了;)

时间: 2024-08-10 15:11:01

Android视频播放方案选择——深刻分析android平台的视频播放优缺点的相关文章

android 视频播放器选择界面弹出机制

1,file manager与videos识别视频的机制不同 a)   file manager简单根据后缀识别,3gp.mp4和avi分别被认为是三种不同的视频格式,因此会分别弹出视频播放器选择界面 b)   videos中视频识别是按照文件mimetype来设定,3gp属于简化的mp4,可以认为是同一类文件.在android设计里3gp和mp4文件使用同一个parser,两种文件的mimetype 也一致,都是video/mp4.由于android default将3gp和mp4使用同一个m

android脱壳之DexExtractor原理分析[zhuan]

http://www.cnblogs.com/jiaoxiake/p/6818786.html内容如下 导语: 上一篇我们分析android脱壳使用对dvmDexFileOpenPartial下断点的原理,使用这种方法脱壳的有2个缺点: 1.  需要动态调试 2.  对抗反调试方案 为了提高工作效率, 我们不希望把宝贵的时间浪费去和加固的安全工程师去做对抗.作为一个高效率的逆向分析师, 笔者是忍不了的,所以我今天给大家带来一种的新的脱壳方法——DexExtractor脱壳法. 资源地址: Dex

android脱壳之DexExtractor原理分析

导语: 上一篇我们分析android脱壳使用对dvmDexFileOpenPartial下断点的原理,使用这种方法脱壳的有2个缺点: 1.  需要动态调试 2.  对抗反调试方案 为了提高工作效率, 我们不希望把宝贵的时间浪费去和加固的安全工程师去做对抗.作为一个高效率的逆向分析师, 笔者是忍不了的,所以我今天给大家带来一种的新的脱壳方法--DexExtractor脱壳法. 资源地址: DexExtractor源码:https://github.com/bunnyblue/DexExtracto

Android基础性能检测与分析

本文内容:基于Android基础性能检测与分析 版权声明:本文为原创文章,未经允许不得转载 博客地址:http://blog.csdn.net/kevindgk 前言 UI性能分析 应用启动时间计算以及程序启动白屏问题 内存分析 内存优化原则 内存区分 内存分析 内存泄露工具MAT 内存泄露工具LeakCanary 耗电量分析 性能检测和分析工具 1 高通性能分析器 - TrepnProfiler 2 高通调试器 - TuneUpKit 3 阿里-易测 云测平台 引用 联系方式 前言 最近一段时

Android TraceView 最权威的性能分析工具

TraceView是什么 Traceview是android平台配备一个很好的性能分析的工具.它可以通过图形化的方式让我们了解我们要跟踪的程序的性能,并且能具体到method. Traceview的作用 查看跟踪代码的执行时间,分析哪些是耗时操作 可以用于跟踪方法的调用,尤其是Android Framework层的方法调用关系 如何使用TraceView 使用TraceView主要有两种方式: 最简单的方式就是直接打开DDMS,选择一个进程,然后按上面的"Start Method Profili

Android学习能力之移动应用分析

1.移动应用分析是什么 移动应用是移动互联网的重要载体,移动应用分析是指在获得移动用户使用等基本数据情况下,由服务端进行数据分析,深入挖掘用户使用的特点,找到产品设计的不足,发现运营推广的机遇,优化产品及运营策略,提升移动应用的质量. 2.移动应用分析的意义 1.监控移动应用运营状态 移动应用分析最基本的使用场景就是实时监控应用的运营状态.通过对数据分析,以日报.周报.月报的形式进行系统监控.通过分析和比较这些核心数据的变化趋势,用数据说话,可以帮助运营和推广人员多维度观察应用的运营状态,及时发

Android Bitmap 开源图片框架分析(精华三)

主要介绍这三个框架,都挺有名的,其他的框架估计也差不多了 Android-Universal-Image-Loaderhttps://github.com/nostra13/Android-Universal-Image-Loader ImageLoaderhttps://github.com/novoda/ImageLoader Volley(综合框架,包含图片部分)https://github.com/mcxiaoke/android-volley 扯淡时间,可以跳过这段这些开源框架的源码还

阿里安卓面试分析: Android应用的闪退(crash)问题跟踪和解析

一:问题描述    闪退(Crash)是客户端程序在运行时遭遇无法处理的异常或错误时而退出应用程序的表现,请从crash发生的原因分类与解决方法.在出现crash后如何捕捉并分析异常这两个问题给出自己的解决方案.    我们以Android平台为例,介绍下如何捕获Android应用的闪退信息,以帮助我们定位和解决导致闪退的问题代码.二:Android中的闪退    在讲解Android中的闪退之前,我们先来简单的复习下Java中的异常.1.Java中的异常    Java中的异常层次结构如下图所

android关于AndroidManifest.xml详细分析

转:http://www.cnblogs.com/zady/archive/2013/10/14/3368385.html 一.关于AndroidManifest.xmlAndroidManifest.xml 是每个android程序中必须的文件.它位于整个项目的根目录,描述了package中暴露的组件(activities, services, 等等),他们各自的实现类,各种能被处理的数据和启动位置. 除了能声明程序中的Activities, ContentProviders, Service