From 245c9fec5a0ec42598dcc0113c325ae916bd323b Mon Sep 17 00:00:00 2001 From: Steve Mayhew Date: Fri, 15 Nov 2019 13:35:48 -0800 Subject: [PATCH] Cleanup tunneled onFrameRenderListener Cleanup the handler to prevent stale messages and potential handler leaks --- .../video/MediaCodecVideoRenderer.java | 48 ++++++++++++++++++- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java index 14f864b08a..68ebb3893d 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java @@ -563,6 +563,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { clearReportedVideoSize(); clearRenderedFirstFrame(); frameReleaseTimeHelper.disable(); + if (tunnelingOnFrameRenderedListener != null && getCodec() != null) { + tunnelingOnFrameRenderedListener.destroyHandler(getCodec()); + } tunnelingOnFrameRenderedListener = null; try { super.onDisabled(); @@ -687,6 +690,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { } codec.configure(mediaFormat, surface, crypto, 0); if (Util.SDK_INT >= 23 && tunneling) { + if (tunnelingOnFrameRenderedListener != null) { + tunnelingOnFrameRenderedListener.destroyHandler(codec); + } tunnelingOnFrameRenderedListener = new OnFrameRenderedListenerV23(codec); } } @@ -1228,6 +1234,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { MediaCodec codec = getCodec(); // If codec is null then the listener will be instantiated in configureCodec. if (codec != null) { + if (tunnelingOnFrameRenderedListener != null) { + tunnelingOnFrameRenderedListener.destroyHandler(codec); + } tunnelingOnFrameRenderedListener = new OnFrameRenderedListenerV23(codec); } } @@ -1808,8 +1817,23 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { @TargetApi(23) private final class OnFrameRenderedListenerV23 implements MediaCodec.OnFrameRenderedListener { + private final Handler handler; + private OnFrameRenderedListenerV23(MediaCodec codec) { - codec.setOnFrameRenderedListener(/* listener= */ this, Util.createHandler()); + handler = new Handler(); + codec.setOnFrameRenderedListener(/* listener= */ this, handler); + } + + /** + * Cleanup any messages pending for this listener before removing references to it + * + * @param codec optional, if the codec is still around it should be passed here + */ + void destroyHandler(@Nullable MediaCodec codec) { + if (codec != null) { + codec.setOnFrameRenderedListener(null, null); + } + handler.removeCallbacksAndMessages(null); } @Override @@ -1818,12 +1842,32 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { // Stale event. return; } + + // Work around bug in MediaCodec that causes deadlocks if you call back a + // MediaCodec API that requires MediaCodec to use it's Looper, as it calls this + // listener method holding locks. This was fixed in: + // https://android-review.googlesource.com/1156807 + // + // The work around simply queues the processing to a subsequent message, where + // the lock will not be held. + // + if (Util.SDK_INT < 30) { + handler.post(() -> { + if (this == tunnelingOnFrameRenderedListener) { // event not stale + handleFrameRendered(presentationTimeUs); + } + }); + } else { + handleFrameRendered(presentationTimeUs); + } + } + + private void handleFrameRendered(long presentationTimeUs) { if (presentationTimeUs == TUNNELING_EOS_PRESENTATION_TIME_US) { onProcessedTunneledEndOfStream(); } else { onProcessedTunneledBuffer(presentationTimeUs); } } - } }