kopia lustrzana https://github.com/TeamNewPipe/NewPipe
Prevent error msg: 'Unrecoverable player error occurred' while playing video during rotation (#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 https://github.com/google/ExoPlayer/issues/2703#issuecomment-300599981pull/6503/head
rodzic
d6e0bd8c26
commit
f78d2a5ed8
|
@ -33,6 +33,7 @@ import android.view.LayoutInflater;
|
|||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
|
@ -112,6 +113,7 @@ import org.schabi.newpipe.player.playback.CustomTrackSelector;
|
|||
import org.schabi.newpipe.player.playback.MediaSourceManager;
|
||||
import org.schabi.newpipe.player.playback.PlaybackListener;
|
||||
import org.schabi.newpipe.player.playback.PlayerMediaSession;
|
||||
import org.schabi.newpipe.player.playback.SurfaceHolderCallback;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueueAdapter;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
||||
|
@ -267,6 +269,7 @@ public final class Player implements
|
|||
private SimpleExoPlayer simpleExoPlayer;
|
||||
private AudioReactor audioReactor;
|
||||
private MediaSessionManager mediaSessionManager;
|
||||
@Nullable private SurfaceHolderCallback surfaceHolderCallback;
|
||||
|
||||
@NonNull private final CustomTrackSelector trackSelector;
|
||||
@NonNull private final LoadController loadController;
|
||||
|
@ -489,7 +492,7 @@ public final class Player implements
|
|||
registerBroadcastReceiver();
|
||||
|
||||
// Setup video view
|
||||
simpleExoPlayer.setVideoSurfaceView(binding.surfaceView);
|
||||
setupVideoSurface();
|
||||
simpleExoPlayer.addVideoListener(this);
|
||||
|
||||
// Setup subtitle view
|
||||
|
@ -768,8 +771,12 @@ public final class Player implements
|
|||
if (DEBUG) {
|
||||
Log.d(TAG, "destroyPlayer() called");
|
||||
}
|
||||
|
||||
cleanupVideoSurface();
|
||||
|
||||
if (!exoPlayerIsNull()) {
|
||||
simpleExoPlayer.removeListener(this);
|
||||
simpleExoPlayer.removeVideoListener(this);
|
||||
simpleExoPlayer.stop();
|
||||
simpleExoPlayer.release();
|
||||
}
|
||||
|
@ -4232,6 +4239,39 @@ public final class Player implements
|
|||
public PlayQueueAdapter getPlayQueueAdapter() {
|
||||
return playQueueAdapter;
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// SurfaceHolderCallback helpers
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
//region SurfaceHolderCallback helpers
|
||||
private void setupVideoSurface() {
|
||||
// make sure there is nothing left over from previous calls
|
||||
cleanupVideoSurface();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // >=API23
|
||||
surfaceHolderCallback = new SurfaceHolderCallback(context, 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 SurfaceHolderCallback helpers
|
||||
}
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
package org.schabi.newpipe.player.playback;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.SurfaceHolder;
|
||||
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.video.DummySurface;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* This implementation is based on:
|
||||
* 'ExoPlayer stuck in buffering after re-adding the surface view a few time #2703'
|
||||
* <p>
|
||||
* -> exoplayer fix suggestion link
|
||||
* https://github.com/google/ExoPlayer/issues/2703#issuecomment-300599981
|
||||
*/
|
||||
public final class SurfaceHolderCallback implements SurfaceHolder.Callback {
|
||||
|
||||
private final Context context;
|
||||
private final SimpleExoPlayer player;
|
||||
private DummySurface dummySurface;
|
||||
|
||||
public SurfaceHolderCallback(final Context context, final SimpleExoPlayer player) {
|
||||
this.context = context;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
Ładowanie…
Reference in New Issue