ExoPlayer
1.简述与应用范围
ExpPlayer是一个应用于Android系统4.1以上的,开源的,App等级的媒体API,它的开源项目包含了library和示例。它用Java实现了解封装,用MediaCodec实现硬件解码。
ExoPlayer相较于MediaPlayer有很多优点:
支持基于http的移动流媒体协议,包括DASH,HLS,Smooth Stream。同时也支持文件流和udp流等。(通过URI)
支持更多媒体封装格式,包括mp4,mp3,Webm,aac,mkv,mpeg-ts。
支持DRM(Digital Right Management 数字版权管理)。
支持HD高清播放。
支持自定义和拓展使用场景。
2.上层调用方式
(本节说明重点为demo。)
简单来说,上层调用方式基本为:
PlayerActivity -> DemoPlayer -> ExoPlayer
PlayerActivity -> RendererBuilder -> ExtractorRendererBuilder
类图为:
其中PlayerActivity面向UI层,一方面控制了播放器DemoPlayer,一方面选择了Renderer。
这里的Renderer指定了数据源格式、解码方式和缓冲区大小等。(说明,这里的缓冲区大小指RollingSampleBuffer的大小,不会影响进入播放的速度,只会影响缓存数据的最大值)
ExoPlayer则是媒体API接口。
DemoPlayer中直接封装了ExoPlayer和相关回调接口,负责播放器的逻辑控制和传入SurfaceView等操作,而非播放器的内部原理。
这里通过时序图来说明Demo中几个类的调用和封装方式。
3.代码结构
简单来说,代码结构是这样:
ExoPlayer ->ExoPlayerImpl -> ExoPlayerImplInternal -> TrackRenderer
MediaCodecVideoTrackRenderer & MediaCodecAudioTrackRenderer -> MediaCodecTrackRenderer -> SampleSourceTrackRenderer -> SampleSource,SampleSourceReader
ExtractorSampleSource -> DataSource & Extractor & Loader
这里,ExoPlayer为接口。ExoPlayerImpl为实现,实现的一些详细步骤在ExoPlayerImplInternal中。后者用Handler消息机制进行异步通信,必要时会阻塞。
TrackRenderer是渲染器接口。
MediaCodecTrackRenderer中加入了MediaCodec(Android硬解码)。这里能看出,ExoPlayer用的是硬解,并且要求4.1以上Android系统。
SampleSourceTrackRenderer中调用了SampleSource,SampleSourceReader接口。SampleSource在这里指的是解封装后的媒体数据。
ExtractorSampleSource相当于一个核心控制器,它实现了SampleSource和SampleSourceReader接口。它通过实际的控制线程Loader,把从某DataSource即数据源中传过来的原始数据,传递给某Extractor来解封装。原始数据解析成SampleSource后,储存在RollingSampleBuffer即环形缓冲区中。
MediaCodecTrackRenderer会间接通过ExtractorSampleSource间接从RollingSampleBuffer中读取数据并渲染成画面,显示到SurfaceView中。
最后的过程有些复杂,流程图如下所示:
从这张图我们也可以看出,ExoPlayer是如何支持自定义和拓展使用场景的。
4.代码原理
1.ExoPlayer -> ExoPlayerImpl -> ExoPlayerImplInternal
通过以下这段ExoPlayerImpl的构造方法代码,可以看出来ExoPlayerImpl中持有一个ExoPlayerImplInternal对象来控制播放器。创建ExoPlayerImplInternal对象时传入了一个eventHandler对象,把底层的错误信息和状态改变信息传递给上层处理。
ExoPlayerImpl类中构造方法:
eventHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
ExoPlayerImpl.this.handleEvent(msg);
}
};
internalPlayer = new ExoPlayerImplInternal(eventHandler, playWhenReady, selectedTrackIndices,
minBufferMs, minRebufferMs);
具体的功能性代码块,都在ExoPlayerImplInternal中实现。
状态改变信息和错误信息会通过eventHandler传上来进行处理。
ExoPlayerImpl类:
// Not private so it can be called from an inner class without going through
// a thunk method.
/* package */ void handleEvent(Message msg) {
switch (msg.what) {
case ExoPlayerImplInternal.MSG_PREPARED: {
System.arraycopy(msg.obj, 0, trackFormats, 0, trackFormats.length);
playbackState = msg.arg1;
for (Listener listener : listeners) {
listener.onPlayerStateChanged(playWhenReady, playbackState);
}
break;
}
case ExoPlayerImplInternal.MSG_STATE_CHANGED: {
playbackState = msg.arg1;
for (Listener listener : listeners) {
listener.onPlayerStateChanged(playWhenReady, playbackState);
}
break;
}
case ExoPlayerImplInternal.MSG_SET_PLAY_WHEN_READY_ACK: {
pendingPlayWhenReadyAcks--;
if (pendingPlayWhenReadyAcks == 0) {
for (Listener listener : listeners) {
listener.onPlayWhenReadyCommitted();
}
}
break;
}
case ExoPlayerImplInternal.MSG_ERROR: {
ExoPlaybackException exception = (ExoPlaybackException) msg.obj;
for (Listener listener : listeners) {
listener.onPlayerError(exception);
}
break;
}
}
}
这里的