基于ExoPlayer 2.17.1源码分析,基本是一边看一边写的流水账,记录下防止以后忘了:
第一步createMediaSource创建HlsMediaSource对象时同时会实例化出HlsPlaylistTracker.Factory
mediaSource = new HlsMediaSource.Factory(getDataSourceFactoryCache(mAppContext, cacheEnable, preview, cacheDir, uerAgent))
.setAllowChunklessPreparation(true)
.setPlaylistParserFactory(new HlsPlaylistParserFactory())
.createMediaSource(mediaItem);
public HlsMediaSource createMediaSource(MediaItem mediaItem) {
checkNotNull(mediaItem.localConfiguration);
HlsPlaylistParserFactory playlistParserFactory = this.playlistParserFactory;
List<StreamKey> streamKeys = mediaItem.localConfiguration.streamKeys;
if (!streamKeys.isEmpty()) {
playlistParserFactory =
new FilteringHlsPlaylistParserFactory(playlistParserFactory, streamKeys);
}
return new HlsMediaSource(
mediaItem,
hlsDataSourceFactory,
extractorFactory,
compositeSequenceableLoaderFactory,
drmSessionManagerProvider.get(mediaItem),
loadErrorHandlingPolicy,
playlistTrackerFactory.createTracker(
hlsDataSourceFactory, loadErrorHandlingPolicy, playlistParserFactory),//实例化TrackerFactory
elapsedRealTimeOffsetMs,
allowChunklessPreparation,
metadataType,
useSessionKeys);
}
第二步调用prepare(),在准备阶段将m3u8文件下载并解析
player.prepare();
//ExoPlayerImpl
public void prepare() {
....
internalPlayer.prepare();
......
}
//ExoPlayerImplInternal
private void prepareInternal() {
....
mediaSourceList.prepare(bandwidthMeter.getTransferListener());
....
}
//MediaSourceList
public void prepare(@Nullable TransferListener mediaTransferListener) {
....
for (int i = 0; i < mediaSourceHolders.size(); i++) {
MediaSourceHolder mediaSourceHolder = mediaSourceHolders.get(i);
prepareChildSource(mediaSourceHolder);
enabledMediaSourceHolders.add(mediaSourceHolder);
}
....
}
private void prepareChildSource(MediaSourceHolder holder) {
.....
mediaSource.prepareSource(caller, mediaTransferListener, playerId);
}
//BaseMediaSource
public final void prepareSource(
MediaSourceCaller caller,
@Nullable TransferListener mediaTransferListener,
PlayerId playerId) {
....
prepareSourceInternal(mediaTransferListener);
....
}
//HlsMediaSource
protected void prepareSourceInternal(@Nullable TransferListener mediaTransferListener) {
....
playlistTracker.start(
localConfiguration.uri, eventDispatcher, /* primaryPlaylistListener= */ this);
}
到这里使用HlsPlaylistTracker,这个类主要是管理播放列表(控制加载解析流程),主列表叫primary playlist,另外该类还能获取当前播放的列表快照,该类入口就在这个start方法
//DefaultHlsPlaylistTracker
public void start(
Uri initialPlaylistUri,
EventDispatcher eventDispatcher,
PrimaryPlaylistListener primaryPlaylistListener) {
....
ParsingLoadable<HlsPlaylist> multivariantPlaylistLoadable =
new ParsingLoadable<>(
dataSourceFactory.createDataSource(C.DATA_TYPE_MANIFEST),
initialPlaylistUri,
C.DATA_TYPE_MANIFEST,
playlistParserFactory.createPlaylistParser());
Assertions.checkState(initialPlaylistLoader == null);
initialPlaylistLoader = new Loader("DefaultHlsPlaylistTracker:MultivariantPlaylist");//创建m3u8文件的网络加载解析线程
long elapsedRealtime =
initialPlaylistLoader.startLoading(
multivariantPlaylistLoadable,
this,
loadErrorHandlingPolicy.getMinimumLoadableRetryCount(
multivariantPlaylistLoadable.type));
.....
}
ParsingLoadable主要代码就这个方法,先通过DataSource获取需要加载的文件流,将文件流传入解析器解析,这里说下DataSourceInputStream本质是InputStream子类,但是里面包含一个datasSource和dataSpec,datasSource可以理解为数据源访问工具(如网络数据源访问Okhttp)获取到数据流后会将流保存在datasSource以供读取,dataSpec用来标记当前数据范围,里面还封装了读取数据的方式参数(如http请求url和header)

本文详细分析了基于ExoPlayer 2.17.1的HLS流媒体播放过程,从创建HlsMediaSource到解析m3u8清单,再到加载和解析ts切片。通过跟踪代码,揭示了数据加载、缓存处理、播放列表管理及MediaSource、MediaPeriod和Renderer之间的交互机制,深入理解HLS播放的内部工作原理。

2022

被折叠的 条评论
为什么被折叠?



