系列文章目录

ExoPlayer架构详解与源码剖析(1)——前言

ExoPlayer架构详解与源码剖析(2)——Player

ExoPlayer架构详解与源码剖析(3)——Timeline

ExoPlayer架构详解与源码剖析(4)——整体架构


前言

假如让你去开发一款播映器,第一步当然想到的便是规划。使用面向对象的思路,去确认ExoPlayer应该具有哪些功用,对外露出哪些操作,需求解决哪些问题。将这些功用进一步笼统,终究就产生了本文要说的Player接口,Player接口坐落整个播映器的最顶层,相当于描绘了整个播映器的蓝图。

规划播映器

Player接口除了界说了一些用于播映的高阶函数(如play、pause、seek、获取某些状况等)。还对对播映器规划了以下特性:

  • 一切办法(除非有特殊阐明)有必要在应用线程调用,大部分是主线程,相同回调有必要注册在同一线程里。

    这部分首要体现在ExoPlayerImpl和ExoPlayerImplInternal里,ExoPlayerImpl里简直一切办法都能够在主线程中直接调用,而ExoPlayerImplInternal里保护运转着播映线程(一个HandlerThread),主线程经过播映线程的Handler将音讯数据发生给播映线程用于操控播映,播映线程又经过主线程的Handler将播映的状况经过在主线程中回调监听的办法通知主线程。

  • 所提供的办法可能有是否可用的操控,播映器会提供一个可用办法集,用户只能调用里面的可用办法。

    Player中界说了 isCommandAvailable(int command)办法,用来查询当时操作办法是否可用,而可用办法集在播映器初始化时设置,而办法的可用操控首要完成在播映器上层,播映器只提供用于完成的才能。
    如PlayerControlView中,在设置播映速度时,就会先判别COMMAND_SET_SPEED_AND_PITCH这个权限。

    private void setPlaybackSpeed(float speed) {
        if (player == null || !player.isCommandAvailable(COMMAND_SET_SPEED_AND_PITCH)) {
          return;
        }
        player.setPlaybackParameters(player.getPlaybackParameters().withSpeed(speed));
      }
    
  • 用户经过注册Listener 来监听播映状况变化。

    Player中界说了addListener(Listener listener)办法用于注册监听,在初始化时设置监听,这些监听贯穿了播映器的整个生命周期,从资源的加载到播映过程中的状况变化都能够在Listener中获取到,Listener 只会在主线程回调。经过使用主线程的Handler将回调转发到主线程。
    addListener首要完成在ExoPlayerImpl中。

    @Override
    public void addListener(Listener listener) {
    // 这儿的办法调用前没有像其他办法一样校验是否在主线程,这个办法能够在任何线程调用,由于增加的listener终究都会被转发到主线程履行
    listeners.add(checkNotNull(listener));
    }
    //ExoPlayerImpl初始化时,会将主线程的Looper传入,用于转发Listener转发到主线程
    listeners =
          new ListenerSet<>(
              applicationLooper,//主线程Looper
              clock,
              (listener, flags) -> listener.onEvents(this.wrappingPlayer, new Events(flags)));
    
  • 有必要在办法调用后立即更新播映状况或许信息,即使实践发生变化的代码履行在后台线程乃至是其他设备上。这样是为了方便调用办法,无需考虑异步处理。

    为了完成这个特性ExoPlayerImpl中保护了播映的状况和信息,首要经过playbackInfo存储当时的状况,当调用Player某个办法时,在办法异步履行完毕前,Player会先更新当时的状况,此刻用户假如再去获取这个状况时,也无需等待异步办法履行完毕,能够直接获取到当时的状况。
    如在ExoPlayerImpl调用prepare时,底层prepare是异步履行于播映线程的,无需等待其调用完毕,直接更新状况信息,等底层prepare异步履行完毕后也会设置当时最新的playbackInfo。

    @Override
      public void prepare() {
        verifyApplicationThread();//判别主线程调用
        ...
        PlaybackInfo playbackInfo = this.playbackInfo.copyWithPlaybackError(null);//创立副本,防止多线程问题
        playbackInfo =//设置状况为STATE_ENDED 或许STATE_BUFFERING
            playbackInfo.copyWithPlaybackState(
                playbackInfo.timeline.isEmpty() ? STATE_ENDED : STATE_BUFFERING);
        pendingOperationAcks++;
        internalPlayer.prepare();
        updatePlaybackInfo(
            playbackInfo,
            /* ignored */ TIMELINE_CHANGE_REASON_SOURCE_UPDATE,
            /* ignored */ PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST,
            /* positionDiscontinuity= */ false,
            /* ignored */ DISCONTINUITY_REASON_INTERNAL,
            /* ignored */ C.TIME_UNSET,
            /* ignored */ C.INDEX_UNSET,
            /* repeatCurrentMediaItem= */ false);
      }
    
  • 能够操作播映列表,如设置、获取、增加、删除、移动或许替换播映列表。播映器还能够设置重复形式和随机播映。

    Player中界说了add、get、move、removeMediaItem等办法完成了播映列表的操作,setRepeatMode设置重复形式和setShuffleModeEnabled设置随机播映,经过设置ShuffleOrder完成自界说的随机算法。随机功用首要完成在MediaSourceList中,能够参阅后续的系列文章。

  • 能够获取轨道及正在播映的轨道,还能够挑选设置轨道。

    经过getCurrentTracks获取正在播映的轨道,经过setTrackSelectionParameters挑选设置轨道,终究会调用ExoPlayerImplInternal的reselectTracksInternal播映挑选的轨道。这部分内容会在系列文章4大组件TrackSelector中具体阐明。

  • 能够获取当时播映内容的元数据

    经过getMediaMetadata()获取元数据,数据存储在MediaMetadata 对象中。

  • 能够获取当时媒体中的广告信息,如当时播映的是否为广告。

    经过isPlayingAd判别是否为广告,getCurrentAdGroupIndex获取当时广告的索引。

  • 能够支持不同的视频烘托输出,如SurfaceView、TextureView。

    setVideoSurfaceView或许setVideoTextureView等办法指定烘托输出对象,这些设置终究会配置到MediaCodec 中,这部分内容会在系列文章4大组件Renderers中具体阐明。

  • 能够倍数播映,音频参数调理,音量调理。

    倍数播映经过调用setPlaybackSpeed办法完成,假如当时媒体包括音轨,终究是经过audioTrack.setPlaybackParams将音轨倍数,然后选用音轨的时钟做为参阅时钟,没有的音轨则直接倍数标准系统时钟,这部分内容会在系列文章4大组件Renderers中具体阐明,经过setAudioAttributes调理音频参数,setVolume调理音量。

      //DefaultAudioSink
      @RequiresApi(23)
      private void setAudioTrackPlaybackParametersV23() {
        ...
            audioTrack.setPlaybackParams(playbackParams);
         ...
      }
      //StandaloneMediaClock
      @Override
      public long getPositionUs() {
        long positionUs = baseUs;
        if (started) {
          long elapsedSinceBaseMs = clock.elapsedRealtime() - baseElapsedMs;
          if (playbackParameters.speed == 1f) {
            positionUs += Util.msToUs(elapsedSinceBaseMs);
          } else {
            positionUs += playbackParameters.getMediaTimeUsForPlayoutTimeMs(elapsedSinceBaseMs);
          }
        }
        return positionUs;
      }
    
  • 能够获取播映设备的信息,即使是长途设备,这样能够设置这些设备的音量。

    经过getDeviceInfo获取设备信息,设备信息里包括当时设备是否为长途设备,还有音量信息,这儿首要用于操控长途设备的音量。

确认播映需求保护的状况和信息

依据上面的规划需求,播映器首要需求保护以下的状况和信息:

  • 播映列表信息

    • 媒体信息封装在MediaItem类里,能够设置界说播映器需求播映的内容。
    • 当时的播映列表能够经过getCurrentTimeline获取,Timeline是ExoPlayer中一个重要的概念,笼统了播映时序,使其习惯各种类型的媒体播映,参阅系列文章ExoPlayer架构详解与源码剖析(3)——Timeline。
    • 当播映列表为空的时候,播映器状况只能是STATE_IDLE或许STATE_ENDED
  • 播映状况

    • STATE_IDLE: 初始状况,播映器停止时的状况,以及播映失败时的状况。在这种状况下,播映器只能具有有限的资源。有必要调用prepare 办法才能脱离此状况。
    • STATE_BUFFERING: 播映器无法立即从当时方位开端播映。出现这种情况首要是由于需求加载更多数据。
    • STATE_READY: 播映器能够立即从当时方位开端播映。
    • STATE_ENDED: 播映器以及播映完成一切内容,或许没有内容需求播映。
  • 播映/暂停,播映约束和正在播映状况

    • playWhenReady: 一个boolen值,用于标记用户是否要开端播映,能够经过调用play 或许 pause办法设置。
    • playback suppression: 用于标记播映被约束(即使playWhenReady=true)的原因。
    • isPlaying: 一个boolen值 ,用于标记播映器是否正在播映(播映进展在前进且播映的数据正在读取)。 这个值为true的条件是上面的 播映状况=STATE_READY 且 playWhenReady =true 且 播映没有被约束,能够看到这个状况是由上面三个状况核算而来的。

        @Override
        public final boolean isPlaying() {
          return getPlaybackState() == Player.STATE_READY
              && getPlayWhenReady()
              && getPlaybackSuppressionReason() == PLAYBACK_SUPPRESSION_REASON_NONE;
        }
      
  • 播映方位进展

    • media item index: 播映列表的索引。
    • ad insertion: 刺进的广告是否在播映,当时正在播映的广告组索引,以及当时广告在组中的索引,这儿能够看到广告是分组播映的,一组能够包括一个或许多个广告。
    • current position: 当时播映的进展。 假如没有播映刺进的广告这个进展等于content position,content position比current position多了一个广告的时长。
    • 需求留意的是Play不提供播映进展状况的回调的,需求以适当的频率去查询播映进展。

总结

本篇介绍相关的规划特性现已播映器保护的状况,分别进行一个简单的介绍,由于在后面的文章这些概念都会涉及到,到时候会具体解读。


版权声明

本文为作者山雨楼原创文章

转载请注明出处

原创不易,觉得有用的话,保藏转发点赞支持