ExoPlayer架构详解与源码分析(1)——前言
ExoPlayer架构详解与源码分析(2)——Player
ExoPlayer架构详解与源码分析(3)——Timeline
ExoPlayer架构详解与源码分析(4)——整体架构
ExoPlayer架构详解与源码分析(5)——MediaSource
ExoPlayer架构详解与源码分析(6)——MediaPeriod
ExoPlayer架构详解与源码分析(7)——SampleQueue
上篇看完了MediaSource,发现其中真正发挥作用的是其中的MediaPeriod,如果MediaSource是燃料系统的外壳,那么MediaPeriod就是其外壳下的核心,媒体数据的的加载获取甚至是解析主要就靠它了。
MediaPeriod主要用于加载于Timeline中的一个Period对于的媒体数据。换句话说Timeline中有多少个Period就会对于多少个MediaPeriod。
MediaPeriod的所有方法都在播放器内部线程调用。MediaPeriod是在MediaSource.createPeriod创建的,MediaSource中媒体数据的加载读取解复用最终提供数据给Renderer等都通过它来实现。MediaPeriod 中每个轨道对应一个SampleStream,MediaPeriod 在同一时间可能只能为一个SampleStream提供数据,但是当前的SampleStream 可以切换(因为MediaPeriod在读取数据时是单线程的,获取到数据后会同步解析数据,通过TrackId 将其关联到不同的轨道的SampleStream 中,这个后面具体会讲到)。
看下几个重要的方法定义
prepare异步准备当前的MediaPeriod,因为是异步的方法入参提供了一个Callback在prepared后通知调用者,在通知调用者前搜先会调用MediaSource的onSourceInfoRefreshed通知MediaSource更新Timeline,然后完成解轨道数据的解析提供给getTrackGroups方法,目的是提前准备好Timeline,如果prepare失败就会调用MediaPeriod的maybeThrowPrepareError方法
maybeThrowPrepareError功能如上,这个方法只会在prepare完成前调用
getTrackGroups获取解析出的轨道数据,因为解析轨道是prepare时完成的,所以这个方法只能在prepared之后调用。
selectTracks执行轨道选择,这是一个重要的方法,就是在这个方法中将轨道数据提供给上层使用者的确切的说是Renderer,同时Renderer也通过mayRetainStreamFlags 告诉MediaPeriod使用保留当前的SampleStream。MediaPeriod如果创建了新的流或者更新了SampleStream也会通过streamResetFlags=true来告诉Renderer需要重置当前的Renderer了。另外每次调用都应该更新TrackSelections。同样这个方法肯定也只能在prepared之后调用。
long selectTracks(
@NullableType ExoTrackSelection[] selections,//TrackSelector提供的轨道选择
boolean[] mayRetainStreamFlags,//需要保留的流
@NullableType SampleStream[] streams,//提供的数据流
boolean[] streamResetFlags,//需要替换的流
long positionUs);//当前的播放位置,如果当前的period还没有播放,这个值就是起始播放位置
seekToUs Seek到指定位置。仅当至少选择一个轨道时才会调用此方法。
continueLoading 当需要继续加载数据时调用,MediaPeriod在prepareing或者prepared后都可以调用这个方法。
reevaluateBuffer 释放SampleStream的相关缓存,同样这个方法肯定也只能在prepared之后调用。
getStreamKeys 当MediaPeriod由多个播放源时,如HLS,这个方法返回一个多个源关于选中轨道的KeyList
MediaPeriod接口定义了数据解析的大致过程:
可以看到基本每一种ExoPlayer支持的媒体都实现了一个MediaPeriod,ProgressiveMediaPeriod相对比较基础和典型,后面也将重点解析它,剩余其他几种类型的可以先做了解,后面有时间再扩充。
ProgressiveMediaSource的MediaPeriod实现,主要用于渐进式媒体文件的加载,如本地或远程的单个视频文件,
先看下ProgressiveMediaPeriod整体结构
ProgressiveMediaPeriod主要分2部分:
上图中并没有MediaPeriod提到的SampleStream,其实ProgressiveMediaPeriod内部类中实现了SampleStream ,通过内部类将
对SampleStream 操作通过trackid关联转发给SampleQueue来实现,所以上图中的SampleQueue其实就是相当于SampleStream。
private final class SampleStreamImpl implements SampleStream {
private final int track;
public SampleStreamImpl(int track) {
this.track = track;
}
@Override
public boolean isReady() {
return ProgressiveMediaPeriod.this.isReady(track);
}
@Override
public void maybeThrowError() throws IOException {
ProgressiveMediaPeriod.this.maybeThrowError(track);
}
@Override
public int readData(
FormatHolder formatHolder, DecoderInputBuffer buffer, @ReadFlags int readFlags) {
return ProgressiveMediaPeriod.this.readData(track, formatHolder, buffer, readFlags);
}
@Override
public int skipData(long positionUs) {
return ProgressiveMediaPeriod.this.skipData(track, positionUs);
}
}
/* package */ int readData(
int sampleQueueIndex,
FormatHolder formatHolder,
DecoderInputBuffer buffer,
@ReadFlags int readFlags) {
if (suppressRead()) {
return C.RESULT_NOTHING_READ;
}
maybeNotifyDownstreamFormat(sampleQueueIndex);
int result =//sampleQueueIndex查询对应的sampleQueues读取数据
sampleQueues[sampleQueueIndex].read(formatHolder, buffer, readFlags, loadingFinished);
if (result == C.RESULT_NOTHING_READ) {
maybeStartDeferredRetry(sampleQueueIndex);
}
return result;
}
Extractor和DataSource那一块还没扩展开,目前就已经很复杂了,DrmSessionManager和LoadErrorHandlingPolicy不是主线任务就跳过了,重点关注Loder、SampleQueue、DataSource、Extractor。
MediaPeriod接口只是定义了标准的执行逻辑,具体如何实现其中定义的每个方法其实每种MediaPeriod实现都会有很大的差异,因为媒体结构本身就千变万化。ProgressiveMediaPeriod实现了其中一种相对基础的媒体类型,但是结构也很复杂,预计下面分多篇将ProgressiveMediaPeriod的SampleQueue、Loder、DataSource、Extractor分析一遍,下篇先从SampleQueue说起。
版权声明 ©
本文为CSDN作者山雨楼原创文章
转载请注明出处
原创不易,觉得有用的话,收藏转发点赞支持