Skip to content

Latest commit

 

History

History
344 lines (309 loc) · 12.3 KB

5. ExoPlayer源码分析之PlayerView.md

File metadata and controls

344 lines (309 loc) · 12.3 KB

5.ExoPlayer源码分析之PlayerView

前面两篇文章大体看了一下ExoPlayer.prepare()部分,接下来,我们要看一下UI部分的封装,PlayerView:

xml声明:

<com.google.android.exoplayer2.ui.PlayerView
    android:id="@+id/mPlayerView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
private lateinit var mPlayer: SimpleExoPlayer
private fun initView(context: Context) {
    val view = View.inflate(context, R.layout.video_view, this)
    mPlayer = ExoPlayerFactory.newSimpleInstance(context)
    // 通过PlayerView.setPlayer方法来将ExoPlayer绑定到View上
    mPlayerView.player = mPlayer
}
public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider {
    // 构造函数
    public PlayerView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);

    LayoutInflater.from(context).inflate(playerLayoutId, this);
    componentListener = new ComponentListener();
    setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);

    // Content frame.
    contentFrame = findViewById(R.id.exo_content_frame);
    if (contentFrame != null) {
      setResizeModeRaw(contentFrame, resizeMode);
    }

    // Shutter view.
    shutterView = findViewById(R.id.exo_shutter);
    if (shutterView != null && shutterColorSet) {
      shutterView.setBackgroundColor(shutterColor);
    }

    // 看对应的SurfaceView的类型,创建SurfaceView或TextureView
    if (contentFrame != null && surfaceType != SURFACE_TYPE_NONE) {
      ViewGroup.LayoutParams params =
          new ViewGroup.LayoutParams(
              ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
      switch (surfaceType) {
        case SURFACE_TYPE_TEXTURE_VIEW:
          surfaceView = new TextureView(context);
          break;
        case SURFACE_TYPE_MONO360_VIEW:
          SphericalSurfaceView sphericalSurfaceView = new SphericalSurfaceView(context);
          sphericalSurfaceView.setSingleTapListener(componentListener);
          surfaceView = sphericalSurfaceView;
          break;
        default:
          surfaceView = new SurfaceView(context);
          break;
      }
      surfaceView.setLayoutParams(params);
      contentFrame.addView(surfaceView, 0);
    } else {
      surfaceView = null;
    }

    // Ad overlay frame layout.
    adOverlayFrameLayout = findViewById(R.id.exo_ad_overlay);

    // Overlay frame layout.
    overlayFrameLayout = findViewById(R.id.exo_overlay);

    // Artwork view.
    artworkView = findViewById(R.id.exo_artwork);
    this.useArtwork = useArtwork && artworkView != null;
    if (defaultArtworkId != 0) {
      defaultArtwork = ContextCompat.getDrawable(getContext(), defaultArtworkId);
    }

    // Subtitle view.
    subtitleView = findViewById(R.id.exo_subtitles);
    if (subtitleView != null) {
      subtitleView.setUserDefaultStyle();
      subtitleView.setUserDefaultTextSize();
    }

    // Buffering view.
    bufferingView = findViewById(R.id.exo_buffering);
    if (bufferingView != null) {
      bufferingView.setVisibility(View.GONE);
    }
    this.showBuffering = showBuffering;

    // Error message view.
    errorMessageView = findViewById(R.id.exo_error_message);
    if (errorMessageView != null) {
      errorMessageView.setVisibility(View.GONE);
    }

    // Playback control view.
    PlayerControlView customController = findViewById(R.id.exo_controller);
    View controllerPlaceholder = findViewById(R.id.exo_controller_placeholder);
    if (customController != null) {
      this.controller = customController;
    } else if (controllerPlaceholder != null) {
      // Propagate attrs as playbackAttrs so that PlayerControlView's custom attributes are
      // transferred, but standard attributes (e.g. background) are not.
      this.controller = new PlayerControlView(context, null, 0, attrs);
      controller.setId(R.id.exo_controller);
      controller.setLayoutParams(controllerPlaceholder.getLayoutParams());
      ViewGroup parent = ((ViewGroup) controllerPlaceholder.getParent());
      int controllerIndex = parent.indexOfChild(controllerPlaceholder);
      parent.removeView(controllerPlaceholder);
      parent.addView(controller, controllerIndex);
    } else {
      this.controller = null;
    }
    this.controllerShowTimeoutMs = controller != null ? controllerShowTimeoutMs : 0;
    this.controllerHideOnTouch = controllerHideOnTouch;
    this.controllerAutoShow = controllerAutoShow;
    this.controllerHideDuringAds = controllerHideDuringAds;
    this.useController = useController && controller != null;
    hideController();
  }


  public void setPlayer(@Nullable Player player) {
    if (this.player == player) {
      return;
    }
    if (this.player != null) {
      this.player.removeListener(componentListener);
      Player.TextComponent oldTextComponent = this.player.getTextComponent();
      if (oldTextComponent != null) {
        oldTextComponent.removeTextOutput(componentListener);
      }
    }
    this.player = player;
    // 把Player设置给PlayerControlView
    if (useController) {
      controller.setPlayer(player);
    }
    if (subtitleView != null) {
      subtitleView.setCues(null);
    }
    updateBuffering();
    updateErrorMessage();
    updateForCurrentTrackSelections(/* isNewPlayer= */ true);
    if (player != null) {
        // 注意下这里
      Player.VideoComponent newVideoComponent = player.getVideoComponent();
      if (newVideoComponent != null) {
        if (surfaceView instanceof TextureView) {
          newVideoComponent.setVideoTextureView((TextureView) surfaceView);
        } else if (surfaceView instanceof SphericalSurfaceView) {
          ((SphericalSurfaceView) surfaceView).setVideoComponent(newVideoComponent);
        } else if (surfaceView instanceof SurfaceView) {
          newVideoComponent.setVideoSurfaceView((SurfaceView) surfaceView);
        }
        newVideoComponent.addVideoListener(componentListener);
      }
      Player.TextComponent newTextComponent = player.getTextComponent();
      if (newTextComponent != null) {
        newTextComponent.addTextOutput(componentListener);
      }
      player.addListener(componentListener);
      maybeShowController(false);
    } else {
      hideController();
    }
  }
}

构造函数里面只是创建了SurfaceView,然后添加了一些其他的View,例如PlayerControlView。

看到这里有个问题,PlayerView里面会去创建SurfaceView,那它是怎么传递到ExoPlayerl里面的?

     // 注意下这里
      Player.VideoComponent newVideoComponent = player.getVideoComponent();
      if (newVideoComponent != null) {
        if (surfaceView instanceof TextureView) {
          newVideoComponent.setVideoTextureView((TextureView) surfaceView);
        } else if (surfaceView instanceof SphericalSurfaceView) {
          ((SphericalSurfaceView) surfaceView).setVideoComponent(newVideoComponent);
        } else if (surfaceView instanceof SurfaceView) {
          newVideoComponent.setVideoSurfaceView((SurfaceView) surfaceView);
        }
        newVideoComponent.addVideoListener(componentListener);
      }

newVideoComponent.setVideoTextureView里面会调用SimpleExoPlayer中的setVideoSurfaceView:

 @Override
  public void setVideoSurfaceView(SurfaceView surfaceView) {
    setVideoSurfaceHolder(surfaceView == null ? null : surfaceView.getHolder());
  }

  @Override
  public void setVideoSurfaceHolder(SurfaceHolder surfaceHolder) {
    verifyApplicationThread();
    removeSurfaceCallbacks();
    this.surfaceHolder = surfaceHolder;
    if (surfaceHolder == null) {
      setVideoSurfaceInternal(null, false);
      maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0);
    } else {
      surfaceHolder.addCallback(componentListener);
      Surface surface = surfaceHolder.getSurface();
      if (surface != null && surface.isValid()) {
        setVideoSurfaceInternal(surface, /* ownsSurface= */ false);
        Rect surfaceSize = surfaceHolder.getSurfaceFrame();
        maybeNotifySurfaceSizeChanged(surfaceSize.width(), surfaceSize.height());
      } else {
        setVideoSurfaceInternal(/* surface= */ null, /* ownsSurface= */ false);
        maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0);
      }
    }
  }

然后会调用setVideoSurfaceInternal:

  private void setVideoSurfaceInternal(@Nullable Surface surface, boolean ownsSurface) {
    // Note: We don't turn this method into a no-op if the surface is being replaced with itself
    // so as to ensure onRenderedFirstFrame callbacks are still called in this case.
    List<PlayerMessage> messages = new ArrayList<>();
    // 遍历所有renderer,如果是视频类型
    for (Renderer renderer : renderers) {
      if (renderer.getTrackType() == C.TRACK_TYPE_VIDEO) {
        messages.add(
            // 发送msg给MediaCodecVideoRenderer
            player.createMessage(renderer).setType(C.MSG_SET_SURFACE).setPayload(surface).send());
      }
    }
    // 如果之前有surface,把之前的释放掉
    if (this.surface != null && this.surface != surface) {
      // We're replacing a surface. Block to ensure that it's not accessed after the method returns.
      try {
        for (PlayerMessage message : messages) {
          message.blockUntilDelivered();
        }
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
      }
      // If we created the previous surface, we are responsible for releasing it.
      if (this.ownsSurface) {
        this.surface.release();
      }
    }
    this.surface = surface;
    this.ownsSurface = ownsSurface;
  }

接下来继续看一下MediaCodecVideoRenderer里面处理Message的部分:

 @Override
  public void handleMessage(int messageType, @Nullable Object message) throws ExoPlaybackException {
    if (messageType == C.MSG_SET_SURFACE) {
      // 直接调用setSurface()方法
      setSurface((Surface) message);
    } else if (messageType == C.MSG_SET_SCALING_MODE) {
      scalingMode = (Integer) message;
      MediaCodec codec = getCodec();
      if (codec != null) {
        codec.setVideoScalingMode(scalingMode);
      }
    } else if (messageType == C.MSG_SET_VIDEO_FRAME_METADATA_LISTENER) {
      frameMetadataListener = (VideoFrameMetadataListener) message;
    } else {
      super.handleMessage(messageType, message);
    }
  }

接着看一下setSurface()方法:

private void setSurface(Surface surface) throws ExoPlaybackException {
    if (surface == null) {
      // Use a dummy surface if possible.
      if (dummySurface != null) {
        surface = dummySurface;
      } else {
        MediaCodecInfo codecInfo = getCodecInfo();
        if (codecInfo != null && shouldUseDummySurface(codecInfo)) {
          dummySurface = DummySurface.newInstanceV17(context, codecInfo.secure);
          surface = dummySurface;
        }
      }
    }
    // We only need to update the codec if the surface has changed.
    if (this.surface != surface) {
      this.surface = surface;
      @State int state = getState();
      MediaCodec codec = getCodec();
      if (codec != null) {
        if (Util.SDK_INT >= 23 && surface != null && !codecNeedsSetOutputSurfaceWorkaround) {
          setOutputSurfaceV23(codec, surface);
        } else {
          releaseCodec();
          maybeInitCodec();
        }
      }
      if (surface != null && surface != dummySurface) {
        // If we know the video size, report it again immediately.
        maybeRenotifyVideoSizeChanged();
        // We haven't rendered to the new surface yet.
        clearRenderedFirstFrame();
        if (state == STATE_STARTED) {
          setJoiningDeadlineMs();
        }
      } else {
        // The surface has been removed.
        clearReportedVideoSize();
        clearRenderedFirstFrame();
      }
    } else if (surface != null && surface != dummySurface) {
      // The surface is set and unchanged. If we know the video size and/or have already rendered to
      // the surface, report these again immediately.
      maybeRenotifyVideoSizeChanged();
      maybeRenotifyRenderedFirstFrame();
    }
  }

上一篇: 4. ExoPlayer源码分析之prepare序列图