又有坑是吧---MediaPlayer加载视频为什么这么慢

3,162 阅读5分钟

"今天又是充满希望的一天"

背景

最近在做一个视频播放的需求,因为比较简单,只需要实现基本的loading,播放,暂停等功能,所以就用Android系统的播放器MediaPlayer。本来开开心心打完了工,正偷偷给妹妹发微信,测试大姐拿着手机过来就噼里啪啦一顿输出

“啊,你们的视频加载怎么这么慢?!”

“你看看人家iOS就不这样,这样能上线吗?!”

卧槽,我测的时候明明好好的啊,怎么现在加载个视频就要30多秒。看着不断转圈圈的loading图,我心里大骂

“又tm有坑是吧”

为了不耽误各位男哥哥女哥哥的时间,直接上结论:视频元数据位置不对

???什么是视频元数据?哪里位置不对?你特么写技术文还要别人百度是吧?

别别别,听我解释

关键的一句话

实际项目中用到的视频格式大多是MP4,MP4格式的视频是由一个个Box组成的。这个Box可以理解为数据块。Box里面可以嵌套Box:

Mp4组成

(如果哥哥们想自己体验一把, mac可以用MediaParaser, window用Mp4Info)

ftyp,moov,mdat就是Box的名字。这里需要重点关注的是moov和mdat两个Box。

moov就是视频的元数据,存放着视频的总体信息,时长啦,码率啦,宽高等等

mdat是具体的媒体数据,也就是我们播放的内容

下面这句话很关键

播放器获取到moov box才能开始播放视频!!!

播放器获取到moov box才能开始播放视频!!!

播放器获取到moov box才能开始播放视频!!!

这句话是这篇文章的核心。其他可以不管,这个起码要记住

所以关于播放器加载视频慢的原因,聪明的哥哥一定早就有答案了,继续看无非就是验证一下啦

原因

上面那张图是理想状态下视频box的位置。

但一些视频在压制的时候,会把moov box放在视频尾部,也就是mdat之后。这就造成播放器必须把整个视频下载完才能获取到moov box,然后才能播放。

相当于你和妹子去约会,刚见面老板说线上有问题让你解决,你哼哧哼哧解决完继续约会你看妹妹会不会当场跟你分手

方案一

直接原因是找到了。聪明的哥哥一定会提出另一个问题

既然moov box这么关键,为什么有的视频会把它放在最后呢?

这个我做了一下实验,发现ffmpeg在转换视频格式的时候,会默认把moov box放在视频尾部。我猜是ffmpeg转化完整个视频之后才能确切知道新视频的时长,码率等信息,所以顺手就把它放到新视频的末尾了。

ffmpeg其实也提供了移动moov box到视频头部的命令

ffmpeg -i input.mp4 -c copy -f mp4 -movflags faststart output.mp4

这不就是解决方案之一嘛?

用新的视频一试,加载时间果然变成2~3秒了

真相大白!!!

方案二

正当我神清气爽准备再找妹妹交流感情之时,产品阿姨告诉我,我们视频有几万个,项目马上要上线了,谁给你时间一个个转换,你说啊,你说啊!!!

我就是个打工的.. 别骂我嘛..

实际开发中会有各种情况无法对视频源做修改,这时候我们只能自己想办法。

视频播放的前提是要获取到moov box,而视频源moov box在尾部,那么我们能不能

先请求视频的尾部获取到moov box,然后再从头请求视频呢?

当然可以。流媒体的请求并不是一次请求完成的,而是分片请求。 先发起一个http请求,读取响应body的开头,如果发现moov在开头就继续往下读mdat。如果没有发现,第二个请求直接读取文件末尾的数据,这样用两个请求也能获取到moov。这个方案要求服务端能支持Request-Range请求,也就是能通过Range直接读取文件尾部,不过一般的oss服务都支持。

流媒体请求

这个方案可以耶!可是难道要我重写一遍MediaPlayer的请求??那我的妹妹怎么办?!

转念一想,这个问题应该很普遍,聪明的哥哥们一定已经填过坑了。于是我用ijkPlayer和exoPlayer分别测了一下,果然这两个播放器都已经对这种情况做了处理,直接替换就好。ijkPlayer的官方so库不支持https请求,于是最终采用了exoPlayer

耶!终于结束了!虽然妹妹早就不耐烦地不回我消息了,但我相信只要坚持对她好她一定会感动的。下个坑,我一定不会再让妹妹久等了

我是方木

想把技术变得不那么枯燥

想用手里的技术去解决一些问题,实现一些东西

想要赚钱,想要成长

欢迎各位交流关注~~

参考文章 阿里巴巴工程师这样秒开短视频