写点什么

坑!页面短视频加载又卡又慢?阿里 P8 大佬教你两套办法秒开短视频

用户头像
Android架构
关注
发布于: 2 小时前


这里不详细介绍 MP4 的格式信息。但是可以看出 moov box 对播放很关键,它提供的信息如:宽高、时长、码率、编码格式、帧列表、关键帧列表等等。播放器没有获取到 moov box 是没办法进行播放的。所以下载的数据应该要包含 moov box 再加上几十帧的数据。


做了一个简单的计算:闲鱼短视频一般最长是 30s,feeds 里面的分辨率是 320p,码率是 1141kb/s,ftyp+moov 这个视频的数据量在 31kb 左右(打开文件可以看出 mdat 是从 31754byte 的位置开始的),所以,头部信息+10 帧的数据大约是:(31kb + 1141kb/3)/8 = 51KB


[](


)Proxy




第二个问题:缓存数据播放完成后该怎么切换到网络数据呢?在本地数据播放完成之后,设置一个网络地址给播放器,告诉播放器下载的 offset 是多少,然后继续从网络下载数据播放。这样看起来可行,但是需要播放器提供支持:本地数据播放完成的回调;设置网络 url 并支持 offset。另外,服务端需要支持 range 参数,而且切换到网络播放的时候需要新建立网络连接,很可能会造成卡顿。


最终,我们选择了 proxy 的方式,把 proxy 作为中间人,负责预加载数据、给播放器提供数据,切换逻辑在 proxy 里面来完成。未加入 proxy 之前流程是这样的:



加入了 proxy 之后流程是这样的:



这样做的好处很明显,我们可以在 proxy 里面做很多事情:例如本地文件缓存数据和网络数据的切换工作。甚至是和 CDN 使用其它的协议进行通信。我们这里假定预加载工作已经完成,看看播放器是怎么和 proxy 进行交互的。播放的时候会用 Proxy 提供的一个 localhost 的 url 进行播放,这样代理服务器会收到网络请求,把本地预加载的数据返回给播放器。播放器完全感知不到 proxy 模块、预加载模块的存在。播放器、预加载模块都是 Proxy 的 client,调用逻辑都是一样。图示说明如下:



下面逐步解释一下,数据的加载过程:


  • Client 发起 http 请求获取数据,箭头 1 所示

  • 文件缓存如果存在所请求的数据则直接返回数据,箭头 2 所示

  • 若本地文件缓存数据不够,则发起网络请求,向 CDN 请求数据,箭头 3 所示

  • 获取网络数据,写入文件缓存,箭头 4 所示

  • 返回请求的数据给 Client,箭头 2 所示


[](


)实现模块




[](


)预加载模块




确定了技术方案后,预加载模块还是有很多工作要做的。在列表网络数据解析完成后会触发视频预加载,首先会根据 url 生成 md5 值,然后去查看这个 md5 值对应的任务是否存在,如果存在则不会重复提交。生成任务后会提交到线程池,在后台线程进行处理。网络从 Wifi 切换到 3G 的时候,会把任务取消,防止消耗用户的数据流量。


预加载任务在线程池执行的时候,其流程是这样的:首先会获取一个本地代理的 url。然后发起 http 请求。Proxy 会收到 http 请求进行处理,开始做真正的数据预加载工作。预加载模块读取到指定的数据量后终止。到此,预加载的任务就已完成。流程图如下所示:



在用户快速滑动的时候,怎么能保证视频还能继续秒开呢?预加载模块对于每一个任务都会维护一个状态机,在 Fling 的时候会把划过的任务暂停下,把最新要显示的任务优先级提高,让其优先执行。


[](


)Proxy 模块




Proxy 内部有个 local 的 httpServer 负责拦截播放器和预加载模块的 http 请求。client 在请求时会带入 CDN 的 url,在本地缓存数据没有的时候会去 CDN 获取新鲜数据。因为有多个地方向 Proxy 请求数据,所以用线程池来处理多个 client 的连接很有必要,这样多个 client 可以并行,不会因为前面有 client 在请求而阻塞。文件缓存使用 LruDiskCache,在超过指定文件大小后,老的缓存文件会删除,这是一个在使用文件缓存时很容易忽视的问题。由于我们的场景视频是连续播放的,不存在 seek 的情况,所以文件缓存相对比较简单,不用考虑文件分段的情况。Proxy 内部对于同一个 url 会映射到一个 client,如果预加载和播放同时进行,数据只会有一份,不会去重复下载数据。再来一个 Proxy 内部构造示意图:



[](


)遇到的问题




在测试中发现,有的视频还是会播放很慢,仔细查看本地的确缓存了期望的数据大小,但是播放的时候还是有较长的等待时间,这种视频有个特点:moov box 在尾部。对于 moov 在尾部的视频,是整个文件都下载完成后才进行播放的,原因是 moov box 里面存了很多关键信息,前面分析 mp4 格式的时候有提到。对于这个问题有两个解法:


  • 解法一:


服务端在进行转码的时候保证 moov 的头部在前面,发现 moov 位置不正确的视频服务端进行订正。


PS:查看 moov 在文件中的位置可以用 hex 文本编辑器打开,按字符搜索 moov 所在的位置即可,MAC 上面还可以使用[MediaParser](


) , 另外还可以用 ffmpeg 命令生成 moov 在头部或者尾部的 mp4 文件。


例如:


从 1.mp4 copy 一个文件,使其 moov 头在尾部


json


ffmpeg -i 1.mp4 -c copy -f mp4 output.mp4


从 1.mp4 copy 一个文件,使其 moov 头在头部:


json


ffmpeg -i 1.mp4 -c copy -f mp4 -movflags faststart output2.mp4


  • 解法二


不用修改 moov box 的位置,而是在播放端进行处理,播放端需要检测流信息,如果 moov 前面没有,就去请求文件的尾部信息。具体就是:发起 HTTP MP4 请求,读取响应 body 的开头,如果发现 moov 在开头,就接着往下读 mdat。如果发现开头没有,先读到 mdat,马上 RESET 这个连接,然后通过 Range 头读取文件末尾数据,因为前面一个 HTTP 请求已经获取到了 Content-Length ,知道了 MP4 文件的整个大小,通过 Range 头读取部分文件尾部数据也是可以的。示意图如下



这个方案的缺点是:对于 moov box 在尾部的视频会多两次 http connection。


[](


)总结




本文介绍了常见的视频编码格式,视频封装格式,介绍了 moov 头信息对于视频播放的影响。随着对于播放流程的分析,我们找到了问题的切入点。简单说就是围绕着数据预加载展开,把网络请求数据的工作提前完成,播放的时候直接从缓存读取,而且后续的视频回看都是从缓存读取,不仅解决了视频初始化播放慢的问题,还解决了播放缓存问题,可以说是一箭双雕。Proxy 是这个方案的核心思想,本地 localhost 的 url 是一个关键纽带,视频预加载模块和播放器模块解耦彻底,换了播放器照样可以使用。到此为止,视频 feeds 秒开优化就已完成。上线后的数据来看视频打开速度在 800ms 左右。


回过头来,或许我们还可以更进一步,可以对预加载收到的数据进行验证,确保缓存了准确的信息,而不是固定的数值。还可以进行更加深度的优化,让用户观看视频的体验更加顺滑。

[](

)Android 音视频开发入门到精通


**以下音视频学习路线及学校笔记 PDF 电子书版可在[我的 GitHub](


)免费下载,记得给个 Star 哦~**


**快速入手通道:[(点这里)](


)群文件免费下载获取。**


[](


)一,初级入门篇:


=======================================================================


一 绘制图片


1. ImageView 绘制图片


2. SurfaceView 绘制图片


3. 自定义 View 绘制图片



二、AudioRecord API 详解



三、使用 AudioRecord 实现录音,并生成 wav


  • 创建一个 AudioRecord 对象

  • 初始化一个 buffer

  • 开始录音

  • 创建一个数据流,一边从 AudioRecord 中读取声音数据到初始化的 buffer,一边将 buffer 中数据导入数据流。

  • 关闭数据流

  • 停止录音



四、用 AudioTrack 播放 PCM 音频


1.AudioTrack 基本使用


2.AudioTrack 详解


3. AudioTrack 与 MediaPlayer 的对比



五、使用 Camera API 采集视频数据


1.预览 Camera 数据


2.取到 NV21 的数据回调


六、使用 MediaExtractor 和 MediaMuxer API 解析和封装 mp4 文件


1.MediaExtractor API 介绍


2.MediaMuxer API 介绍


3.使用情境



七. MediaCodec API 详解


1.MediaCodec 介绍


2.MediaCodec API 说明


3.MediaCodec 流控



由于文章篇幅受限,剩余内容过多,文中插图有限,下文只能截图目录展示:


[](


)二,中级进阶篇:


=======================================================================


  • Android OpenGL ES 开发(一): OpenGL ES 介绍

  • Android OpenGL ES 开发(二): OpenGL ES 环境搭建

  • Android OpenGL ES 开发(三): OpenGL ES 定义形状

  • Android OpenGL ES 开发(四): OpenGL ES 绘制形状

  • Android OpenGL ES 开发(五): OpenGL ES 使用投影和相机视图

  • Android OpenGL ES 开发(六): OpenGL ES 添加运动效果

  • Android OpenGL ES 开发(七): OpenGL ES 响应触摸事件

  • Android OpenGL ES 开发(八): OpenGL ES 着色器语言 GLSL

  • Android OpenGL ES 开发(九): OpenGL ES 纹理贴图

  • Android OpenGL ES 开发(十): 通过 GLES20 与着色器交互

  • 使用 OpenGL 显示一张图片

  • GLSurfaceviw 绘制 Camera 预览画面及实现拍照

  • 使用 OpenGL ES 完成视频的录制,并实现视频水印效果



[](


)高级探究篇:


=====================================================================


  • 深入学习音视频编码,如 H.264,AAC,研究使用开源编解码库,如 x.264,JM 等

  • 深入研究音视频相关的网络协议,如 rtmp,hls,以及封包格式,如:flv,mp4

  • 深入学习一些音视频领域的开源项目,如 webrtc,ffmpeg,ijkplayer,librtmp 等等

  • 将 ffmpeg 库移植到 Android 平台,结合上面积累的经验,编写一款简易的音视频播放器

  • 将 x264 库移植到 Android 平台,结合上面积累的经验,完成视频数据 H264 软编功能

  • 将 librtmp 库移植到 Android 平台,结合上面积累的经验,完成 Android RTMP 推流功能


音视频编解码技术


  • 音视频编解码技术(一):MPEG-4/H.264 AVC 编解码标准

  • 音视频编解码技术(二):AAC 音频编码技术


流媒体协议

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
坑!页面短视频加载又卡又慢?阿里P8大佬教你两套办法秒开短视频