From bc06039f7fcac0e80c8e6b6207b1c555a386dd5a Mon Sep 17 00:00:00 2001 From: ibaker Date: Thu, 22 Jun 2023 14:17:47 +0000 Subject: [PATCH] Add `Extractor.getUnderlyingImplementation` This change uses this new method everywhere we currently `instanceof` check an `Extractor` directly. This allows us to introduce wrapping/delegating `Extractor` instances - because the `instanceof` checks will continue to operate on the underlying instance. HLS is a slightly different case, because it directly re-instantiates `Extractor` instances, which is not compatible with an arbitrary wrapping structure. Luckily the only `Extractor` instances that HLS re-instantiates do not support muxed subtitles, so won't be wrapped in the first place (although future changes might use the delegating-`Extractor` pattern for other purposes, which might affect HLS). PiperOrigin-RevId: 542550928 --- RELEASENOTES.md | 4 ++++ .../source/BundledExtractorsAdapter.java | 8 +++++-- .../hls/BundledHlsMediaChunkExtractor.java | 21 ++++++++++++------- .../androidx/media3/extractor/Extractor.java | 11 ++++++++++ 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index af7eda3aea..c1dee68696 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -40,6 +40,10 @@ * Fix typo when determining `rotationDegrees`. Changed `projectionPosePitch` to `projectionPoseRoll` ([#461](https://github.com/androidx/media/pull/461)). + * Remove the assumption that `Extractor` instances can be directly + inspected with `instanceof`. If you want runtime access to the + implementation details of an `Extractor` you must first call + `Extractor.getUnderlyingInstance`. * Audio: * Audio Offload: * Add `AudioSink.getFormatOffloadSupport(Format)` that retrieves level of diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/BundledExtractorsAdapter.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/BundledExtractorsAdapter.java index 990ef008ae..7ca883fade 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/BundledExtractorsAdapter.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/BundledExtractorsAdapter.java @@ -108,8 +108,12 @@ public final class BundledExtractorsAdapter implements ProgressiveMediaExtractor @Override public void disableSeekingOnMp3Streams() { - if (extractor instanceof Mp3Extractor) { - ((Mp3Extractor) extractor).disableSeeking(); + if (extractor == null) { + return; + } + Extractor underlyingExtractor = extractor.getUnderlyingImplementation(); + if (underlyingExtractor instanceof Mp3Extractor) { + ((Mp3Extractor) underlyingExtractor).disableSeeking(); } } diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/BundledHlsMediaChunkExtractor.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/BundledHlsMediaChunkExtractor.java index c1ca6ce651..d39a701f28 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/BundledHlsMediaChunkExtractor.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/BundledHlsMediaChunkExtractor.java @@ -15,9 +15,10 @@ */ package androidx.media3.exoplayer.hls; +import static androidx.media3.common.util.Assertions.checkState; + import androidx.annotation.VisibleForTesting; import androidx.media3.common.Format; -import androidx.media3.common.util.Assertions; import androidx.media3.common.util.TimestampAdjuster; import androidx.media3.common.util.UnstableApi; import androidx.media3.extractor.Extractor; @@ -71,20 +72,26 @@ public final class BundledHlsMediaChunkExtractor implements HlsMediaChunkExtract @Override public boolean isPackedAudioExtractor() { - return extractor instanceof AdtsExtractor - || extractor instanceof Ac3Extractor - || extractor instanceof Ac4Extractor - || extractor instanceof Mp3Extractor; + Extractor underlyingExtractor = extractor.getUnderlyingImplementation(); + return underlyingExtractor instanceof AdtsExtractor + || underlyingExtractor instanceof Ac3Extractor + || underlyingExtractor instanceof Ac4Extractor + || underlyingExtractor instanceof Mp3Extractor; } @Override public boolean isReusable() { - return extractor instanceof TsExtractor || extractor instanceof FragmentedMp4Extractor; + Extractor underlyingExtractor = extractor.getUnderlyingImplementation(); + return underlyingExtractor instanceof TsExtractor + || underlyingExtractor instanceof FragmentedMp4Extractor; } @Override public HlsMediaChunkExtractor recreate() { - Assertions.checkState(!isReusable()); + checkState(!isReusable()); + checkState( + extractor.getUnderlyingImplementation() == extractor, + "Can't recreate wrapped extractors. Outer type: " + extractor.getClass()); Extractor newExtractorInstance; if (extractor instanceof WebvttExtractor) { newExtractorInstance = diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/Extractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/Extractor.java index 2c67cf99c5..02e8275f6f 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/Extractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/Extractor.java @@ -120,4 +120,15 @@ public interface Extractor { /** Releases all kept resources. */ void release(); + + /** + * Returns the 'real' {@code Extractor} implementation if this is a delegating instance, or {@code + * this} if this instance does the extraction directly without delegating (the default behaviour). + * + *

{@code Extractor} implementations that operate by delegating to another {@code Extractor} + * should override this method to return that delegate. + */ + default Extractor getUnderlyingImplementation() { + return this; + } }