Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid Video frame render before exoPlayer.setPlayWhenReady(true) #6886

Closed
adrian-linca opened this issue Jan 17, 2020 · 9 comments
Closed

Avoid Video frame render before exoPlayer.setPlayWhenReady(true) #6886

adrian-linca opened this issue Jan 17, 2020 · 9 comments
Assignees
Labels

Comments

@adrian-linca
Copy link

I'm using a SimpleExoPlayer instance to play videos with audio.
I'm playing video files from local storage.
Basic stuff, just like the Hello World setup (https://exoplayer.dev/hello-world.html)
The issue I have is that the first frame of the video is rendered before I call exoPlayer.setPlayWhenReady(true).
This, combined with audio latency, creates a small freeze on the first frame of each video when playing back to back videos.
I was able to modify MediaCodecVideoRenderer to avoid this, but my question is if there is a setting to avoid the first frame being rendered before exoPlayer.setPlayWhenReady(true) without modifying the code?
If there is no way I think this should be improved.

@andrewlewis
Copy link
Collaborator

You could show an opaque "shutter" view on top of the player layout until you want to show the first frame. PlayerView does something similar internally, but as you've noted it hides the shutter when a frame is first rendered rather than when starting playback. The frame layout returned by PlayerView.getOverlayFrameLayout() may be a convenient place to put your extra shutter view. Does that do what you need or are you looking for something different?

@adrian-linca
Copy link
Author

@andrewlewis what I want to achieve is smooth transitions between videos, so an opaque view is not a solution.

@andrewlewis
Copy link
Collaborator

Can you use playlists for that? If not please could you describe in a bit more detail what you're trying to achieve?

@adrian-linca
Copy link
Author

I cannot use playlist or concatenated source because I only know what needs to play next after current video finishes.
I'm just trying to play back to back videos smoothly.
I don't need a workaround, for my project I modified the MediaCodecVideoRenderer and it works fine.
I just think this issue looks like something that should be improved, rendering should not start unless requested by the client.

@adrian-linca
Copy link
Author

I initially opened this issue as a Question, I thought I was missing something.
Maybe it should be labeled as a feature request or a bug.

@andrewlewis
Copy link
Collaborator

andrewlewis commented Jan 17, 2020

Is the desired behavior to stay showing the last frame of the video that just ended while buffering the next video, then only advance to the first frame of the next video and resume when it can be played without buffering at the start?

@marcbaechinger, @tonihei I tried implementing this using a concatenation and pausing then adding a new media source after getting to the ended state. However, there isn't really an easy way to resume playback only when the next item is loaded far enough to play seamlessly, as it seems to be necessary to issue a seek to exit the ended state. Any ideas on a better way to do this?

Aside: I think a simple way to reduce the pause that might be sufficient for this use case (not using a concatenation) would be to customize load control to require a very small duration to be buffered before transitioning to the ready state (the content is local so this shouldn't be an issue).

@tonihei
Copy link
Collaborator

tonihei commented Jan 23, 2020

The suggestion above seems to work for me using code like that:

    player.addListener(new EventListener() {
      @Override
      public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
        if (playbackState == Player.STATE_ENDED) {
          player.pause();
          player.addMediaSource(/* your new MediaSource */);
          player.seekTo(player.getCurrentPosition());
        } else if (playbackState == Player.STATE_READY) {
          player.play();
        }
      }

      @Override
      public void onPositionDiscontinuity(@Player.DiscontinuityReason  int reason) {
        if (reason == Player.DISCONTINUITY_REASON_PERIOD_TRANSITION) {
          player.removeMediaItem(0);
        }
      }
    });

Note that seeking to position-1 is needed because the player seems to ignore the seek to the current position even though it should change back to buffering state. Marking as bug to fix this issue.

@tonihei tonihei added the bug label Jan 23, 2020
@tonihei
Copy link
Collaborator

tonihei commented Jan 23, 2020

The comment above is using the new playlist APIs that will be added in 2.12. For the current release you can the equivalent with ConcatenatingMediaSource like that:

    player.addListener(new EventListener() {
      private boolean isPendingSeekToEnd;

      @Override
      public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
        if (playbackState == Player.STATE_ENDED 
            && concatenatingMediaSource.getSize() == 1) {
          player.pause();
          concatenatingMediaSource.addMediaSource(/* your new MediaSource */);
          isPendingSeekToEnd = true;
        } else if (playbackState == Player.STATE_READY) {
          player.play();
        }
      }

      @Override
      public void onTimelineChanged(
          Timeline timeline, Object manifest, @Player.TimelineChangeReason int reason) {
        if (timeline.getWindowCount() > 1 && isPendingSeekToEnd) {
          player.seekTo(player.getCurrentPosition() - 1);
          isPendingSeekToEnd = false;
        }
      }

      @Override
      public void onPositionDiscontinuity(@Player.DiscontinuityReason  int reason) {
        if (reason == Player.DISCONTINUITY_REASON_PERIOD_TRANSITION) {
          concatenatingMediaSource.removeMediaSource(0);
        }
      }
    });

@ojw28 ojw28 assigned tonihei and unassigned andrewlewis Jan 31, 2020
@ojw28 ojw28 removed the question label Jan 31, 2020
ojw28 pushed a commit that referenced this issue Feb 28, 2020
We should only ignore seek to the current position if we are
currently READY or BUFFERING. Also, pending initial seek positions
should only be saved while we have an empty timeline, independent of
the player state.

Issue:#6886
PiperOrigin-RevId: 297813854
@tonihei
Copy link
Collaborator

tonihei commented Mar 12, 2020

We pushed a fix so that you no longer have to seek to position-1 in the example above. I'll update the code snippet for future reference of how it should look like.

@tonihei tonihei closed this as completed Mar 12, 2020
@google google locked and limited conversation to collaborators May 12, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

4 participants