ExoPlayer里里外外之:核心类和数据流
来源:互联网 发布:云同步盘 linux 编辑:程序博客网 时间:2024/05/14 20:13
streaming那些事儿
相信大部分玩Android人并不陌生,ExoPlayer是Google在Android上除了MediaPlayer之外,提供的一套完全基于Java的播放器,https://github.com/google/ExoPlayer是官方地址,优点自不必多说,支持Dash、HLS、SS、RTMP等,最重要的是它完全是Java的,极大的方便了基于它的Video App的升级。
ExoPlayer不乏使用者,并且越来越流行,但彻底弄清楚它的里里外外的我相信是少数人,网上能看到的资料大部分都是翻译自官网的,详解它的实现的少之又少;由于它本身是开源的,我们会把当前掌握的一些技术细节给大家分享出来,大家一起探讨,后续会逐渐分享。另外我们在使用的过程中也做了不少优化,改进相当明显,其中非涉密的部分,我们也会分享给大家,还有一些有优化想法的部分,也会在叙述过程中罗列出来。
先大致描述一下ExoPlayer核心类库的关系,如下图:
ExoPlayerImplInternal是播放器主线程所在,它最核心依赖的两个类MediaSource和Renderer,MediaSource是HLS/Dash/SS等Source的接口定义,Renderer是MediaCodecRenderer的接口,MediaCodecRenderer最终会调用Android SDK的MediaCodec和Render(surface和track);MediaSource会create MediaPeriod,MediaPeriod是ExoPlayer中source部分大循环loop的入口,也许是受Dash的影响,这个入口被命名成了Peroid(Dash MPD最外层的XML Tag就是Peroid)。
整个ExoPlayer工作在ExoPlayerImplInternal Looper线程中,不停地通过MediaPeriod跟Source读数据,写到Buffer中,然后Renderer不停地从Buffer中取数据送给MediaCodec解码然后显示,完成整个播放的pipeline;那这个Buffer究竟是怎么工作的,形态是什么样的?我们参看下面的数据流图(以HLS播放为例):
上图以HLS播放为例,图中,蓝色部分表示的是ExoPlayer的主线程,绿色部分是下载线程,注意每个manifest或者chunk的下载都会启用一个新的线程,下载到的视频数据最终会add到DefaultTrackOutput的DataQueue中,Renderer会从DataQueue中读取数据;上图中虚线示意AdtsReader和H264Reader会并行工作,但A/V数据会分别放到两个不同的DefaultTrackOutput实例中。
DataQueue中存放的是demux后的数据,以Sample为单位,对video来说就是一帧;有些播放器的source buffer会直接存放demux前的数据,对HLS来说就是ts数据,这里有点不同;这么设计buffer的一个原因,我再次认为,是为Dash定制的(没办法,Dash仍是主流,有DRM就有Dash),因为Dash的MPD文件中音视频数据天然是分开的,Peroid中video和audio的AS是分开的,甚至Peroid内的A、V数据都可以在不同的CDN上。
你马上会想到,FMP4是不是不用做Demux了?没错,你说的对,后面的文章我们会陆续有详细分析。说到此,Dash的这个优点在高码率的情况下,实在是能节省不少CPU资源,由此想到能在配置并不高的电视上流畅的观看4K视频,这是一种怎样的体验?但这背后的技术支撑又岂是码农们一朝一夕的积累。
扯远了,回到DataQueue,DataQueue中只存ES数据,与这些数据相对应的Metadata信息,则是放在一个叫做InfoQueue的数据结构中,InfoQueue根据offset和size来管理DataQueue中的ES。
关于DataQueue和InfoQueue的具体数据结构,后面有单独篇幅做详细分析,插一句,我们也会穿插说下多媒体码农们跟“数据结构和算法”的关系,你会看到,关系还是:蛮大的,往后不要再埋怨面试的时候挂在“数据结构和算法”了?呵呵。。。
这里再提一下streaming时候,DataQueue的数据到底存放多少?原则是什么?有三个关键数据:
LOW_WATERMARK(15s)
HIGH_WATERMARK(30s)
TOTAL_BUFFER_SIZE(16M Bytes)
规则如下:
如果DataQueue中数据时长小于15s,则继续下载新的Chunk
如果DataQueue中数据时长大于30s,则停止下载新的Chunk
如果DataQueue中数据在介于15s~30s,并且targetBufferSize小于TOTAL_BUFFER_SIZE,继续下载
停止下载数据执行:priorityTaskManager.remove(LOADING_PRIORITY) 。
上面的逻辑在DefaultLoadControl中控制,DataQueue中数据时长计算,非常好理解:bufferedDurationUs = nextLoadPositionUs - rendererPositionUs。
Buffer就说到这里:下次写DataQueue和InfoQueue的详细说明,以及ExoPlayer中用到的其他数据结构:大家都熟悉的HashMap,以及,号称堪比Hash的SparseArray。
- ExoPlayer里里外外之:核心类和数据流
- ExoPlayer里里外外之:流媒体播放与数据结构
- ExoPlayer里里外外之:自适应码率切换
- ExoPlayer里里外外之:流媒体播放与数据结构
- android之ExoPlayer探索
- 媒体和相机:ExoPlayer
- ExoPlayer类说明
- ExoPlayer
- ExoPlayer
- Google Exoplayer之全面认识
- Google Exoplayer之全面认识
- Java 数据流之File类
- IO流之数据流DataOutputStream和DataInputStream
- 软件工程之数据流图和数据字典
- C#之数据流和字符串压缩
- ExoPlayer的使用之自定义UI界面
- IO基础之Properties类、数据流、RandomAccessFile类、管道流和nio的简单说明
- Java数据流之字节级输入输出类
- 麻省理工大学公开课笔记:算法导论(三)——渐近符号、递归及解法
- 3. Longest Substring Without Repeating Characters
- easyUI 设置文本框是否可编辑
- n皇后问题
- IOC控制反转(依赖注入)
- ExoPlayer里里外外之:核心类和数据流
- css3--flex
- C++友元函数和友元类
- QT 嵌入exe应用程序 并获取鼠标事件 mouse keyboard event
- Axure RP 8最新激活码
- js中基本类型操作-----数组基本操作、字符串操作、json操作
- 数据库_1
- oracle的rownum和rowid区别
- 压力测试工具JMeter入门教程