ExoPlayer 源码阅读小记--HLS播放带缓存加载M3U8文件过程

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

基于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)

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值