系列文章目录
ExoPlayer架构详解与源码分析(1)——前言
ExoPlayer架构详解与源码分析(2)——Player
ExoPlayer架构详解与源码分析(3)——Timeline
ExoPlayer架构详解与源码分析(4)——整体架构
ExoPlayer架构详解与源码分析(5)——MediaSource
ExoPlayer架构详解与源码分析(6)——MediaPeriod
ExoPlayer架构详解与源码分析(7)——SampleQueue
ExoPlayer架构详解与源码分析(8)——Loader
ExoPlayer架构详解与源码分析(9)——TsExtractor
ExoPlayer架构详解与源码分析(10)——H264Reader
ExoPlayer架构详解与源码分析(11)——DataSource
ExoPlayer架构详解与源码分析(12)——Cache
ExoPlayer架构详解与源码分析(13)——TeeDataSource和CacheDataSource
ExoPlayer架构详解与源码分析(14)——ProgressiveMediaPeriod
ExoPlayer架构详解与源码分析(15)——Renderer
ExoPlayer架构详解与源码分析(16)——LoadControl
ExoPlayer架构详解与源码分析(17)——TrackSelector
文章目录
前言
好了四大组件就剩TrackSelector没讲了。这里回顾下系列开头的构架图。
TrackSelector负责选择每个播放器渲染器要使用的轨道。
与播放器交互
在播放期间,播放器与TrackSelector之间会发生以下交互:
- 创建播放器时,它将通过调用 init 来初始化 TrackSelector。
- 当播放器需要进行轨道选择时,它将调用 selectTracks。通常发生在播放开始的时候。
- 播放器需要在 Renderer 渲染数据前选择好轨道。例如播放器在缓存数据时,当MediaPeriod preare完成时就会选择轨道。播放器通过调用 onSelectionActivated 告诉TrackSelector当前变为Activated状态。
- 如果TrackSelector希望向播放器指出以前做出的选择是无效的,TrackSelector 可以通过调用onTrackSelectionsInvalidated。这个监听是在播放器调用 TrackSelector.init 时注册给 TrackSelector 的。如果轨道选择器的配置已更改,则调用intrackSelectionsInvaliDated通知,例如,如果现在希望使用特定语言的音轨。这将触发播放器进行新的轨道选择。请注意,如果当前播放期的新选择的规定与之前轨道选择不同,则播放前将重新缓冲。
- 当播放器被释放时,它会通过调用release来释放曲目选择器。
渲染器配置
selectTracks 返回的 TrackSelectorResult 不仅包含每个渲染器的 TrackSelections,还包含定义Renderer在使用相应媒体时应应用的配置参数的 RendererConfigurations。TrackSelector指定 Renderer 配置信息似乎不符合常识,但实际上两者紧密结合在一起。如果以特定方式配置Renderer,则可能只能播放某些组合轨道。同样,如果选择了某些轨道,则可能只能以特定方式配置渲染器。因此,需要同步确定轨道选择和相应的 Renderer 配置。
线程模型
播放器对TrackSelector进行的所有调用都在播放器的内部播放线程上进行。TrackSelector可能从任何线程回调 onTrackSelectionsInvalidated,线程模型参考ExoPlayer架构详解与源码分析(4)——整体架构。
MappingTrackSelector
MappingTrackSelector首先在TrackGroup和Renderer之间建立映射,然后根据该映射为每个渲染器创建ExoTrackSelection 。
@Override
//播放器调用执行轨道选择
public final TrackSelectorResult selectTracks(
RendererCapabilities[] rendererCapabilities,//包含了所有Renderer的信息,播放器初始化时会获取,RendererCapabilities定义了Renderer 的功能相关信息
TrackGroupArray trackGroups,//MediaPeriod prepared 后,播放器通过getTrackGroups从中获取的所有轨道信息
MediaPeriodId periodId,//关联当前的MediaPeriod
Timeline timeline)//当前的Timeline
throws ExoPlaybackException {
//选择期间被写入的数据结构,最后一位用来记录可以与任何renderer关联的轨道相关数据
int[] rendererTrackGroupCounts = new int[rendererCapabilities.length + 1];
TrackGroup[][] rendererTrackGroups = new TrackGroup[rendererCapabilities.length + 1][];
@Capabilities int[][][] rendererFormatSupports = new int[rendererCapabilities.length + 1][][];
for (int i = 0; i < rendererTrackGroups.length; i++) {
rendererTrackGroups[i] = new TrackGroup[trackGroups.length];
rendererFormatSupports[i] = new int[trackGroups.length][];
}
//确定每个renderer 能适应混合 mimeType,默认的MediaCodec能适应但是可能会出现短暂的不连续性返回8,字幕和其他渲染器一般不支持返回0
@AdaptiveSupport
int[] rendererMixedMimeTypeAdaptationSupports =
getMixedMimeTypeAdaptationSupports(rendererCapabilities);
for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) {
TrackGroup group = trackGroups.get(groupIndex);
// 为每个轨道 查找一个合适的renderer
boolean preferUnassociatedRenderer = group.type == C.TRACK_TYPE_METADATA;
int rendererIndex =
findRenderer(
rendererCapabilities, group, rendererTrackGroupCounts, preferUnassociatedRenderer);
// 确定查找出的Renderer是否支持当前轨道 格式
@Capabilities
int[] rendererFormatSupport =
rendererIndex == rendererCapabilities.length
? new int[group.length]
: getFormatSupport(rendererCapabilities[rendererIndex], group);
// 保存结果
int rendererTrackGroupCount = rendererTrackGroupCounts[rendererIndex];
rendererTrackGroups[rendererIndex][rendererTrackGroupCount] = group;
rendererFormatSupports[rendererIndex][rendererTrackGroupCount] = rendererFormatSupport;
rendererTrackGroupCounts[rendererIndex]++;
}
// 为每个renderer创建一个TrackGroupArray
TrackGroupArray[] rendererTrackGroupArrays = new TrackGroupArray[rendererCapabilities.length];
String[] rendererNames = new String[rendererCapabilities.length];
int[] rendererTrackTypes = new int[rendererCapabilities.length];
for (int i = 0; i < rendererCapabilities.length; i++) {
int rendererTrackGroupCount = rendererTrackGroupCounts[i];
rendererTrackGroupArrays[i] =
new TrackGroupArray(
Util.nullSafeArrayCopy(rendererTrackGroups[i], rendererTrackGroupCount));
rendererFormatSupports[i] =
Util.nullSafeArrayCopy(rendererFormatSupports[i], rendererTrackGroupCount);
rendererNames[i] = rendererCapabilities[i].getName();
rendererTrackTypes[i] = rendererCapabilities[i].getTrackType();
}
// Create a track group array for track groups not mapped to a renderer.
int unmappedTrackGroupCount = rendererTrackGroupCounts[rendererCapabilities.length];
TrackGroupArray unmappedTrackGroupArray =
new TrackGroupArray(
Util.nullSafeArrayCopy(
rendererTrackGroups[rendererCapabilities.length], unmappedTrackGroupCount));
// 构建出mappedTrackInfo
MappedTrackInfo mappedTrackInfo =
new MappedTrackInfo(
rendererNames,
rendererTrackTypes,
rendererTrackGroupArrays,
rendererMixedMimeTypeAdaptationSupports,
rendererFormatSupports,
unmappedTrackGroupArray);
Pair<@NullableType RendererConfiguration[], @NullableType ExoTrackSelection[]> result =
selectTracks(//调用子类选择轨道
mappedTrackInfo,
rendererFormatSupports,
rendererMixedMimeTypeAdaptationSupports,
periodId,
timeline);
//最终获取Tracks