exoplayer2:在开始时使用 ConcatenatingMediaSource 暂停视频

exoplayer2: pause video at start with ConcatenatingMediaSource

在播放 ConcatenatingMediaSource 播放列表中的视频时,我希望播放器在新项目开始时自动暂停,例如。不会自动播放。

使用演示应用程序,我修改了 onPositionDiscontinuity 函数以检测当前项目更改:

int currentPosition = 0;
@Override
public void onPositionDiscontinuity(@Player.DiscontinuityReason int reason) {
    if (inErrorState) {
        // This will only occur if the user has performed a seek whilst in the error state. Update
        // the resume position so that if the user then retries, playback will resume from the
        // position to which they seeked.
        updateResumePosition();
    }
    if (player.getCurrentWindowIndex() != currentPosition) {
        currentPosition = player.getCurrentWindowIndex();
        player.setPlayWhenReady(false);
    }
}

虽然此代码会暂停播放器,但它不会清除播放器使用的表面视图,因此我们仍然可以看到上一个视频的最后一帧。我想这个回调被调用得太快了,但这是我发现的唯一一个总是在播放列表项更改时调用的回调(onPlayerStateChanged 可能不会被调用)。

如何显示新当前项目的第一帧而不是上一个项目的最后一帧?

我的弱解决方法是使用 Handler().postDelayed({ mPlayer?.playWhenReady = false }, 200).

延迟 200 毫秒的调用

This is not supported yet:

The problem with using the event listener is that there is no way to ensure the request to pause in your event listener is handled while the first frame of the new playlist item is showing.

If stopping at approximately the right frame is fine, your solution of pausing the player on position discontinuity looks fine, but I think there's no guarantee that the video renderer will have kept up with the player position which is why you still see a frame from the previous source at the point of pausing the player. For what it's worth, in my testing the video renderer did advance to the first frame of the next playlist item before the request to pause was handled.

A couple of other suggestions:

  • You could try customizing your MediaSource to insert a position discontinuity at the start of each period. I think then you'd get onRenderedFirstFrame at the start of each item. If you pause the player there you can guarantee to pause the player at a frame in the new playlist item.
  • To get this to work perfectly, in the sense that the first frame of the new playlist item is shown and the player pauses before any other frame is shown, it will be necessary to coordinate showing a frame and blocking further rendering (on the playback thread) with pausing the player (on the thread your app is using to interact with the payer). This will require a bit of code, but I think it is probably possible roughly as follows: subclass MediaCodecVideoRenderer and override onStreamChanged to get the time offset of the new playlist item. Then override processOutputBuffer. In this method you can detect when the first frame of the new stream has been rendered (based on the time offset) and then prevent processing any output frames until you've paused the player on your app's thread. After the request to pause has been handled and the renderer is stopped (onStopped) you can unblock processOutputBuffer.

We could also look at supporting this directly in the player, but it will likely be a low priority at least for the moment.