Skip to content

Commit

Permalink
Prevent error msg: 'Unrecoverable player error occurred' while playin…
Browse files Browse the repository at this point in the history
…g video during rotation (TeamNewPipe#6502)

Playing a video in VideoDetailFragment and rotating the screen to landscape (back and forth more often)
can trigger this error message. Especially if rotation for whatever reason takes long or
playing a high resolution (1080p) video.

The underlying logcat error messages:
05-12 16:38:38.251 24920 26037 E Surface : getSlotFromBufferLocked: unknown buffer: 0x923fc810
05-12 16:38:38.251 24920 26037 W ACodec  : [OMX.qcom.video.decoder.avc] can not return buffer 35 to native window

The problem is that that Exoplayer is trying to write to our -- during rotation -- no longer existant
(VideoDetailFragment) SurfaceView.

Solution:
Implementing SurfaceHolder.Callback and using DummySurface we can now handle the lifecycle of the Surface.

How?: In case we are no longer able to write to the Surface eg. through rotation/putting in
background we can set a DummySurface. Although it only works on API >= 23.
Result: we get a little video interruption (audio is still fine) but we won't get the
'Unrecoverable player error occurred' error message.

This implementation is based on and more background information:
 'ExoPlayer stuck in buffering after re-adding the surface view a few time 2703'

 -> exoplayer fix suggestion link
  google/ExoPlayer#2703 (comment)
  • Loading branch information
evermind-zz committed Jun 17, 2021
1 parent d6e0bd8 commit 4580b5d
Showing 1 changed file with 91 additions and 1 deletion.
92 changes: 91 additions & 1 deletion app/src/main/java/org/schabi/newpipe/player/Player.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
Expand Down Expand Up @@ -77,6 +79,7 @@
import com.google.android.exoplayer2.ui.SubtitleView;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.DummySurface;
import com.google.android.exoplayer2.video.VideoListener;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.nostra13.universalimageloader.core.ImageLoader;
Expand Down Expand Up @@ -267,6 +270,7 @@ public final class Player implements
private SimpleExoPlayer simpleExoPlayer;
private AudioReactor audioReactor;
private MediaSessionManager mediaSessionManager;
private SurfaceHolderCallback surfaceHolderCallback;

@NonNull private final CustomTrackSelector trackSelector;
@NonNull private final LoadController loadController;
Expand Down Expand Up @@ -489,7 +493,7 @@ private void initPlayer(final boolean playOnReady) {
registerBroadcastReceiver();

// Setup video view
simpleExoPlayer.setVideoSurfaceView(binding.surfaceView);
setupVideoSurface();
simpleExoPlayer.addVideoListener(this);

// Setup subtitle view
Expand Down Expand Up @@ -768,8 +772,12 @@ private void destroyPlayer() {
if (DEBUG) {
Log.d(TAG, "destroyPlayer() called");
}

cleanupVideoSurface();

if (!exoPlayerIsNull()) {
simpleExoPlayer.removeListener(this);
simpleExoPlayer.removeVideoListener(this);
simpleExoPlayer.stop();
simpleExoPlayer.release();
}
Expand Down Expand Up @@ -4234,4 +4242,86 @@ public PlayQueueAdapter getPlayQueueAdapter() {
}

//endregion



//region SurfaceHandlerCallback
/**
* Prevent error message: 'Unrecoverable player error occurred'
* In case of rotation some users see this kind of an error which is preventable
* having a Callback that handles the lifecycle of the surface.
*
* How?: In case we are no longer able to write to the surface eg. through rotation/putting in
* background we set set a DummySurface. Although it it works on API >= 23 only.
* Result: we get a little video interruption (audio is still fine) but we won't get the
* 'Unrecoverable player error occurred' error message.
*
* This implementation is based on:
* 'ExoPlayer stuck in buffering after re-adding the surface view a few time #2703'
*
* -> exoplayer fix suggestion link
* https://github.com/google/ExoPlayer/issues/2703#issuecomment-300599981
*/
private final class SurfaceHolderCallback implements SurfaceHolder.Callback {

private final SimpleExoPlayer player;
private DummySurface dummySurface;

SurfaceHolderCallback(final SimpleExoPlayer player) {
this.player = player;
}

@Override
public void surfaceCreated(final SurfaceHolder holder) {
player.setVideoSurface(holder.getSurface());
}

@Override
public void surfaceChanged(final SurfaceHolder holder,
final int format,
final int width,
final int height) {
}

@Override
public void surfaceDestroyed(final SurfaceHolder holder) {
if (dummySurface == null) {
dummySurface = DummySurface.newInstanceV17(context, false);
}
player.setVideoSurface(dummySurface);
}

public void release() {
if (dummySurface != null) {
dummySurface.release();
dummySurface = null;
}
}
}

private void setupVideoSurface() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // >=API23
surfaceHolderCallback = new SurfaceHolderCallback(simpleExoPlayer);
binding.surfaceView.getHolder().addCallback(surfaceHolderCallback);
final Surface surface = binding.surfaceView.getHolder().getSurface();
// initially set the surface manually otherwise
// onRenderedFirstFrame() will not be called
simpleExoPlayer.setVideoSurface(surface);
} else {
simpleExoPlayer.setVideoSurfaceView(binding.surfaceView);
}
}

private void cleanupVideoSurface() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // >=API23
if (surfaceHolderCallback != null) {
if (binding != null) {
binding.surfaceView.getHolder().removeCallback(surfaceHolderCallback);
}
surfaceHolderCallback.release();
surfaceHolderCallback = null;
}
}
}
//endregion SurfaceHandlerCallback
}

0 comments on commit 4580b5d

Please sign in to comment.