From a0f8ac7503b55ad5c4a75a06b694c9f5910972f7 Mon Sep 17 00:00:00 2001 From: christosts Date: Fri, 29 Oct 2021 13:31:12 +0000 Subject: [PATCH 1/6] ExoPlayer.Builder: lazily initialize default components Initialize default components lazily in ExoPlayer.Builder to avoid redundant component instantiations, useful in cases where apps overwrite default components with ExoPlayer.Builder setters. The fields in ExoPlayer.Builder are wrapped in a Supplier (rather than just making then nullable and initializing them in ExoPlayer.Builder.build()) so that we maintain the proguarding properties of this class. The exception is ExoPlayer.Builder.AnalyticsCollector which became nullable and is initialized in ExoPlayer.Builder.build() in order to use any Clock that has been set separately with ExoPlayer.Builder.setClock(). #minor-release PiperOrigin-RevId: 406345976 --- .../google/android/exoplayer2/ExoPlayer.java | 92 ++++++++++++------- .../android/exoplayer2/SimpleExoPlayer.java | 53 ++++++----- 2 files changed, 90 insertions(+), 55 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java index 11d84fc1d4..e594883c72 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer2; import static com.google.android.exoplayer2.util.Assertions.checkArgument; +import static com.google.android.exoplayer2.util.Assertions.checkNotNull; import static com.google.android.exoplayer2.util.Assertions.checkState; import android.content.Context; @@ -59,6 +60,7 @@ import com.google.android.exoplayer2.video.MediaCodecVideoRenderer; import com.google.android.exoplayer2.video.VideoFrameMetadataListener; import com.google.android.exoplayer2.video.VideoSize; import com.google.android.exoplayer2.video.spherical.CameraMotionListener; +import com.google.common.base.Supplier; import java.util.List; /** @@ -366,12 +368,12 @@ public interface ExoPlayer extends Player { /* package */ Clock clock; /* package */ long foregroundModeTimeoutMs; - /* package */ RenderersFactory renderersFactory; - /* package */ MediaSourceFactory mediaSourceFactory; - /* package */ TrackSelector trackSelector; - /* package */ LoadControl loadControl; - /* package */ BandwidthMeter bandwidthMeter; - /* package */ AnalyticsCollector analyticsCollector; + /* package */ Supplier renderersFactorySupplier; + /* package */ Supplier mediaSourceFactorySupplier; + /* package */ Supplier trackSelectorSupplier; + /* package */ Supplier loadControlSupplier; + /* package */ Supplier bandwidthMeterSupplier; + /* package */ Supplier analyticsCollectorSupplier; /* package */ Looper looper; @Nullable /* package */ PriorityTaskManager priorityTaskManager; /* package */ AudioAttributes audioAttributes; @@ -437,8 +439,8 @@ public interface ExoPlayer extends Player { public Builder(Context context) { this( context, - new DefaultRenderersFactory(context), - new DefaultMediaSourceFactory(context, new DefaultExtractorsFactory())); + () -> new DefaultRenderersFactory(context), + () -> new DefaultMediaSourceFactory(context, new DefaultExtractorsFactory())); } /** @@ -456,8 +458,8 @@ public interface ExoPlayer extends Player { public Builder(Context context, RenderersFactory renderersFactory) { this( context, - renderersFactory, - new DefaultMediaSourceFactory(context, new DefaultExtractorsFactory())); + () -> renderersFactory, + () -> new DefaultMediaSourceFactory(context, new DefaultExtractorsFactory())); } /** @@ -474,7 +476,7 @@ public interface ExoPlayer extends Player { * MediaItem}. */ public Builder(Context context, MediaSourceFactory mediaSourceFactory) { - this(context, new DefaultRenderersFactory(context), mediaSourceFactory); + this(context, () -> new DefaultRenderersFactory(context), () -> mediaSourceFactory); } /** @@ -494,14 +496,7 @@ public interface ExoPlayer extends Player { */ public Builder( Context context, RenderersFactory renderersFactory, MediaSourceFactory mediaSourceFactory) { - this( - context, - renderersFactory, - mediaSourceFactory, - new DefaultTrackSelector(context), - new DefaultLoadControl(), - DefaultBandwidthMeter.getSingletonInstance(context), - new AnalyticsCollector(Clock.DEFAULT)); + this(context, () -> renderersFactory, () -> mediaSourceFactory); } /** @@ -527,13 +522,48 @@ public interface ExoPlayer extends Player { LoadControl loadControl, BandwidthMeter bandwidthMeter, AnalyticsCollector analyticsCollector) { + this( + context, + () -> renderersFactory, + () -> mediaSourceFactory, + () -> trackSelector, + () -> loadControl, + () -> bandwidthMeter, + () -> analyticsCollector); + } + + private Builder( + Context context, + Supplier renderersFactorySupplier, + Supplier mediaSourceFactorySupplier) { + this( + context, + renderersFactorySupplier, + mediaSourceFactorySupplier, + () -> new DefaultTrackSelector(context), + DefaultLoadControl::new, + () -> DefaultBandwidthMeter.getSingletonInstance(context), + /* analyticsCollectorSupplier= */ null); + } + + private Builder( + Context context, + Supplier renderersFactorySupplier, + Supplier mediaSourceFactorySupplier, + Supplier trackSelectorSupplier, + Supplier loadControlSupplier, + Supplier bandwidthMeterSupplier, + @Nullable Supplier analyticsCollectorSupplier) { this.context = context; - this.renderersFactory = renderersFactory; - this.mediaSourceFactory = mediaSourceFactory; - this.trackSelector = trackSelector; - this.loadControl = loadControl; - this.bandwidthMeter = bandwidthMeter; - this.analyticsCollector = analyticsCollector; + this.renderersFactorySupplier = renderersFactorySupplier; + this.mediaSourceFactorySupplier = mediaSourceFactorySupplier; + this.trackSelectorSupplier = trackSelectorSupplier; + this.loadControlSupplier = loadControlSupplier; + this.bandwidthMeterSupplier = bandwidthMeterSupplier; + this.analyticsCollectorSupplier = + analyticsCollectorSupplier != null + ? analyticsCollectorSupplier + : () -> new AnalyticsCollector(checkNotNull(clock)); looper = Util.getCurrentOrMainLooper(); audioAttributes = AudioAttributes.DEFAULT; wakeMode = C.WAKE_MODE_NONE; @@ -573,7 +603,7 @@ public interface ExoPlayer extends Player { */ public Builder setRenderersFactory(RenderersFactory renderersFactory) { checkState(!buildCalled); - this.renderersFactory = renderersFactory; + this.renderersFactorySupplier = () -> renderersFactory; return this; } @@ -586,7 +616,7 @@ public interface ExoPlayer extends Player { */ public Builder setMediaSourceFactory(MediaSourceFactory mediaSourceFactory) { checkState(!buildCalled); - this.mediaSourceFactory = mediaSourceFactory; + this.mediaSourceFactorySupplier = () -> mediaSourceFactory; return this; } @@ -599,7 +629,7 @@ public interface ExoPlayer extends Player { */ public Builder setTrackSelector(TrackSelector trackSelector) { checkState(!buildCalled); - this.trackSelector = trackSelector; + this.trackSelectorSupplier = () -> trackSelector; return this; } @@ -612,7 +642,7 @@ public interface ExoPlayer extends Player { */ public Builder setLoadControl(LoadControl loadControl) { checkState(!buildCalled); - this.loadControl = loadControl; + this.loadControlSupplier = () -> loadControl; return this; } @@ -625,7 +655,7 @@ public interface ExoPlayer extends Player { */ public Builder setBandwidthMeter(BandwidthMeter bandwidthMeter) { checkState(!buildCalled); - this.bandwidthMeter = bandwidthMeter; + this.bandwidthMeterSupplier = () -> bandwidthMeter; return this; } @@ -652,7 +682,7 @@ public interface ExoPlayer extends Player { */ public Builder setAnalyticsCollector(AnalyticsCollector analyticsCollector) { checkState(!buildCalled); - this.analyticsCollector = analyticsCollector; + this.analyticsCollectorSupplier = () -> analyticsCollector; return this; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java b/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java index f0637df30a..3c6607611e 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java @@ -28,6 +28,7 @@ import static com.google.android.exoplayer2.Renderer.MSG_SET_SKIP_SILENCE_ENABLE import static com.google.android.exoplayer2.Renderer.MSG_SET_VIDEO_FRAME_METADATA_LISTENER; import static com.google.android.exoplayer2.Renderer.MSG_SET_VIDEO_OUTPUT; import static com.google.android.exoplayer2.Renderer.MSG_SET_VOLUME; +import static com.google.android.exoplayer2.util.Assertions.checkNotNull; import android.content.Context; import android.graphics.Rect; @@ -66,7 +67,6 @@ import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.trackselection.TrackSelectionParameters; import com.google.android.exoplayer2.trackselection.TrackSelector; import com.google.android.exoplayer2.upstream.BandwidthMeter; -import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Clock; import com.google.android.exoplayer2.util.ConditionVariable; import com.google.android.exoplayer2.util.Log; @@ -409,12 +409,14 @@ public class SimpleExoPlayer extends BasePlayer Clock clock, Looper applicationLooper) { this( - new ExoPlayer.Builder(context, renderersFactory) - .setTrackSelector(trackSelector) - .setMediaSourceFactory(mediaSourceFactory) - .setLoadControl(loadControl) - .setBandwidthMeter(bandwidthMeter) - .setAnalyticsCollector(analyticsCollector) + new ExoPlayer.Builder( + context, + renderersFactory, + mediaSourceFactory, + trackSelector, + loadControl, + bandwidthMeter, + analyticsCollector) .setUseLazyPreparation(useLazyPreparation) .setClock(clock) .setLooper(applicationLooper)); @@ -431,7 +433,7 @@ public class SimpleExoPlayer extends BasePlayer constructorFinished = new ConditionVariable(); try { applicationContext = builder.context.getApplicationContext(); - analyticsCollector = builder.analyticsCollector; + analyticsCollector = builder.analyticsCollectorSupplier.get(); priorityTaskManager = builder.priorityTaskManager; audioAttributes = builder.audioAttributes; videoScalingMode = builder.videoScalingMode; @@ -443,12 +445,15 @@ public class SimpleExoPlayer extends BasePlayer listeners = new CopyOnWriteArraySet<>(); Handler eventHandler = new Handler(builder.looper); renderers = - builder.renderersFactory.createRenderers( - eventHandler, - componentListener, - componentListener, - componentListener, - componentListener); + builder + .renderersFactorySupplier + .get() + .createRenderers( + eventHandler, + componentListener, + componentListener, + componentListener, + componentListener); // Set initial values. volume = 1; @@ -476,10 +481,10 @@ public class SimpleExoPlayer extends BasePlayer player = new ExoPlayerImpl( renderers, - builder.trackSelector, - builder.mediaSourceFactory, - builder.loadControl, - builder.bandwidthMeter, + builder.trackSelectorSupplier.get(), + builder.mediaSourceFactorySupplier.get(), + builder.loadControlSupplier.get(), + builder.bandwidthMeterSupplier.get(), analyticsCollector, builder.useLazyPreparation, builder.seekParameters, @@ -848,7 +853,7 @@ public class SimpleExoPlayer extends BasePlayer @Override public void addAnalyticsListener(AnalyticsListener listener) { // Don't verify application thread. We allow calls to this method from any thread. - Assertions.checkNotNull(listener); + checkNotNull(listener); analyticsCollector.addListener(listener); } @@ -874,7 +879,7 @@ public class SimpleExoPlayer extends BasePlayer return; } if (isPriorityTaskManagerRegistered) { - Assertions.checkNotNull(this.priorityTaskManager).remove(C.PRIORITY_PLAYBACK); + checkNotNull(this.priorityTaskManager).remove(C.PRIORITY_PLAYBACK); } if (priorityTaskManager != null && isLoading()) { priorityTaskManager.add(C.PRIORITY_PLAYBACK); @@ -982,7 +987,7 @@ public class SimpleExoPlayer extends BasePlayer @Override public void addListener(Listener listener) { - Assertions.checkNotNull(listener); + checkNotNull(listener); listeners.add(listener); EventListener eventListener = listener; addListener(eventListener); @@ -992,13 +997,13 @@ public class SimpleExoPlayer extends BasePlayer @Override public void addListener(Player.EventListener listener) { // Don't verify application thread. We allow calls to this method from any thread. - Assertions.checkNotNull(listener); + checkNotNull(listener); player.addEventListener(listener); } @Override public void removeListener(Listener listener) { - Assertions.checkNotNull(listener); + checkNotNull(listener); listeners.remove(listener); EventListener eventListener = listener; removeListener(eventListener); @@ -1322,7 +1327,7 @@ public class SimpleExoPlayer extends BasePlayer ownedSurface = null; } if (isPriorityTaskManagerRegistered) { - Assertions.checkNotNull(priorityTaskManager).remove(C.PRIORITY_PLAYBACK); + checkNotNull(priorityTaskManager).remove(C.PRIORITY_PLAYBACK); isPriorityTaskManagerRegistered = false; } currentCues = Collections.emptyList(); From 405b811454c60babf4e1f73d3a5b27e66ab48a0d Mon Sep 17 00:00:00 2001 From: ibaker Date: Fri, 29 Oct 2021 13:40:08 +0000 Subject: [PATCH 2/6] Update developer guide to use non-deprecated symbols #minor-release PiperOrigin-RevId: 406347412 --- docs/ad-insertion.md | 10 ++++++++-- docs/drm.md | 19 +++++++++++-------- docs/live-streaming.md | 2 +- docs/media-items.md | 36 ++++++++++++++++++++---------------- 4 files changed, 40 insertions(+), 27 deletions(-) diff --git a/docs/ad-insertion.md b/docs/ad-insertion.md index 0886ca6a92..4e42430f5b 100644 --- a/docs/ad-insertion.md +++ b/docs/ad-insertion.md @@ -178,7 +178,10 @@ MediaItem preRollAd = MediaItem.fromUri(preRollAdUri); MediaItem contentStart = new MediaItem.Builder() .setUri(contentUri) - .setClipEndPositionMs(120_000) + .setClippingConfiguration( + new ClippingConfiguration.Builder() + .setEndPositionMs(120_000) + .build()) .build(); // A mid-roll ad. MediaItem midRollAd = MediaItem.fromUri(midRollAdUri); @@ -186,7 +189,10 @@ MediaItem midRollAd = MediaItem.fromUri(midRollAdUri); MediaItem contentEnd = new MediaItem.Builder() .setUri(contentUri) - .setClipStartPositionMs(120_000) + .setClippingConfiguration( + new ClippingConfiguration.Builder() + .setStartPositionMs(120_000) + .build()) .build(); // Build the playlist. diff --git a/docs/drm.md b/docs/drm.md index a943f49ba7..4b64640989 100644 --- a/docs/drm.md +++ b/docs/drm.md @@ -24,7 +24,8 @@ outlined in the sections below. ### Key rotation ### To play streams with rotating keys, pass `true` to -`MediaItem.Builder.setDrmMultiSession` when building the media item. +`MediaItem.DrmConfiguration.Builder.setMultiSession` when building the media +item. ### Multi-key content ### @@ -49,8 +50,9 @@ to access the different streams. In this case, the license server is configured to respond with only the key specified in the request. Multi-key content can be played with this license -server configuration by passing `true` to `MediaItem.Builder.setDrmMultiSession` -when building the media item. +server configuration by passing `true` to +`MediaItem.DrmConfiguration.Builder.setMultiSession` when building the media +item. We do not recommend configuring your license server to behave in this way. It requires extra license requests to play multi-key content, which is less @@ -59,9 +61,9 @@ efficient and robust than the alternative described above. ### Offline keys ### An offline key set can be loaded by passing the key set ID to -`MediaItem.Builder.setDrmKeySetId` when building the media item. This -allows playback using the keys stored in the offline key set with the specified -ID. +`MediaItem.DrmConfiguration.Builder.setKeySetId` when building the media item. +This allows playback using the keys stored in the offline key set with the +specified ID. {% include known-issue-box.html issue-id="3872" description="Only one offline key set can be specified per playback. As a result, offline playback of @@ -75,8 +77,9 @@ clear content as are used when playing encrypted content. When media contains both clear and encrypted sections, you may want to use placeholder `DrmSessions` to avoid re-creation of decoders when transitions between clear and encrypted sections occur. Use of placeholder `DrmSessions` for audio and video tracks can -be enabled by passing `true` to `MediaItem.Builder.setDrmSessionForClearPeriods` -when building the media item. +be enabled by passing `true` to +`MediaItem.DrmConfiguration.Builder.forceSessionsForAudioAndVideoTracks` when +building the media item. ### Using a custom DrmSessionManager ### diff --git a/docs/live-streaming.md b/docs/live-streaming.md index d24d0f4dd0..f9091ba261 100644 --- a/docs/live-streaming.md +++ b/docs/live-streaming.md @@ -89,7 +89,7 @@ components to support additional modes when playing live streams. By default, ExoPlayer uses live playback parameters defined by the media. If you want to configure the live playback parameters yourself, you can set them on a -per `MediaItem` basis by calling `MediaItem.Builder.setLiveXXX` methods. If +per `MediaItem` basis by calling `MediaItem.Builder.setLiveConfiguration`. If you'd like to set these values globally for all items, you can set them on the `DefaultMediaSourceFactory` provided to the player. In both cases, the provided values will override parameters defined by the media. diff --git a/docs/media-items.md b/docs/media-items.md index 710ded16d6..f1c342c2a5 100644 --- a/docs/media-items.md +++ b/docs/media-items.md @@ -86,17 +86,17 @@ To sideload subtitle tracks, `MediaItem.Subtitle` instances can be added when when building a media item: ~~~ -MediaItem.Subtitle subtitle = - new MediaItem.Subtitle( - subtitleUri, - MimeTypes.APPLICATION_SUBRIP, // The correct MIME type. - language, // The subtitle language. May be null. - selectionFlags); // Selection flags for the track. - -MediaItem mediaItem = new MediaItem.Builder() - .setUri(videoUri) - .setSubtitles(Lists.newArrayList(subtitle)) - .build(); +MediaItem.SubtitleConfiguration subtitle = + new MediaItem.SubtitleConfiguration.Builder(subtitleUri) + .setMimeType(MimeTypes.APPLICATION_SUBRIP) // The correct MIME type (required). + .setLanguage(language) // The subtitle language (optional). + .setSelectionFlags(selectionFlags) // Selection flags for the track (optional). + .build(); +MediaItem mediaItem = + new MediaItem.Builder() + .setUri(videoUri) + .setSubtitleConfigurations(ImmutableList.of(subtitle)) + .build(); ~~~ {: .language-java} @@ -110,11 +110,15 @@ It's possible to clip the content referred to by a media item by setting custom start and end positions: ~~~ -MediaItem mediaItem = new MediaItem.Builder() - .setUri(videoUri) - .setClipStartPositionMs(startPositionMs) - .setClipEndPositionMs(endPositionMs) - .build(); +MediaItem mediaItem = + new MediaItem.Builder() + .setUri(videoUri) + .setClippingConfiguration( + new ClippingConfiguration.Builder() + .setStartPositionMs(startPositionMs) + .setEndPositionMs(endPositionMs) + .build()) + .build(); ~~~ {: .language-java} From 8e2083a27b5879dc50d6ac6c30ccc7d6a87ab145 Mon Sep 17 00:00:00 2001 From: olly Date: Fri, 29 Oct 2021 14:27:32 +0000 Subject: [PATCH 3/6] Remove dependency from common tests to exoplayer PiperOrigin-RevId: 406354526 --- .../{FakePlayer.java => FakeExoPlayer.java} | 9 +- .../exoplayer2/ext/ima/ImaAdsLoaderTest.java | 4 +- library/common/build.gradle | 1 - .../google/android/exoplayer2/FormatTest.java | 7 +- .../exoplayer2/ForwardingPlayerTest.java | 16 +- .../exoplayer2/metadata/MetadataTest.java | 5 +- .../TrackSelectionParametersTest.java | 15 - .../exoplayer2/util/AtomicFileTest.java | 2 +- .../exoplayer2/util/MediaFormatUtilTest.java | 6 +- .../DefaultTrackSelectorTest.java | 13 + .../testutil/FakeMetadataEntry.java | 78 ++++ .../exoplayer2/testutil/StubExoPlayer.java | 383 +---------------- .../exoplayer2/testutil/StubPlayer.java | 399 ++++++++++++++++++ 13 files changed, 510 insertions(+), 428 deletions(-) rename extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/{FakePlayer.java => FakeExoPlayer.java} (97%) create mode 100644 testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMetadataEntry.java create mode 100644 testutils/src/main/java/com/google/android/exoplayer2/testutil/StubPlayer.java diff --git a/extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/FakePlayer.java b/extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/FakeExoPlayer.java similarity index 97% rename from extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/FakePlayer.java rename to extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/FakeExoPlayer.java index e8f80feed0..fb2975920d 100644 --- a/extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/FakePlayer.java +++ b/extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/FakeExoPlayer.java @@ -15,9 +15,9 @@ */ package com.google.android.exoplayer2.ext.ima; -import android.content.Context; import android.os.Looper; import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Timeline; @@ -29,8 +29,8 @@ import com.google.android.exoplayer2.util.Clock; import com.google.android.exoplayer2.util.ListenerSet; import com.google.android.exoplayer2.util.Util; -/** A fake player for testing content/ad playback. */ -/* package */ final class FakePlayer extends StubExoPlayer { +/** A fake {@link ExoPlayer} for testing content/ad playback. */ +/* package */ final class FakeExoPlayer extends StubExoPlayer { private final ListenerSet listeners; private final Timeline.Period period; @@ -48,8 +48,7 @@ import com.google.android.exoplayer2.util.Util; private int adGroupIndex; private int adIndexInAdGroup; - public FakePlayer(Context context) { - super(context); + public FakeExoPlayer() { listeners = new ListenerSet<>( Looper.getMainLooper(), diff --git a/extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoaderTest.java b/extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoaderTest.java index cb0deb494b..0d9e7f042f 100644 --- a/extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoaderTest.java +++ b/extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoaderTest.java @@ -139,13 +139,13 @@ public final class ImaAdsLoaderTest { private ContentProgressProvider contentProgressProvider; private VideoAdPlayer videoAdPlayer; private TestAdsLoaderListener adsLoaderListener; - private FakePlayer fakePlayer; + private FakeExoPlayer fakePlayer; private ImaAdsLoader imaAdsLoader; @Before public void setUp() { setupMocks(); - fakePlayer = new FakePlayer(getApplicationContext()); + fakePlayer = new FakeExoPlayer(); adViewGroup = new FrameLayout(getApplicationContext()); View adOverlayView = new View(getApplicationContext()); adViewProvider = diff --git a/library/common/build.gradle b/library/common/build.gradle index 40d6c7c610..b59552d366 100644 --- a/library/common/build.gradle +++ b/library/common/build.gradle @@ -49,7 +49,6 @@ dependencies { testImplementation 'junit:junit:' + junitVersion testImplementation 'com.google.truth:truth:' + truthVersion testImplementation 'org.robolectric:robolectric:' + robolectricVersion - testImplementation project(modulePrefix + 'library-core') testImplementation project(modulePrefix + 'testutils') } diff --git a/library/common/src/test/java/com/google/android/exoplayer2/FormatTest.java b/library/common/src/test/java/com/google/android/exoplayer2/FormatTest.java index fbc40afd12..36c5e448b1 100644 --- a/library/common/src/test/java/com/google/android/exoplayer2/FormatTest.java +++ b/library/common/src/test/java/com/google/android/exoplayer2/FormatTest.java @@ -23,7 +23,7 @@ import static com.google.common.truth.Truth.assertThat; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.drm.DrmInitData; import com.google.android.exoplayer2.metadata.Metadata; -import com.google.android.exoplayer2.metadata.id3.TextInformationFrame; +import com.google.android.exoplayer2.testutil.FakeMetadataEntry; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.video.ColorInfo; import java.util.ArrayList; @@ -65,10 +65,7 @@ public final class FormatTest { byte[] projectionData = new byte[] {1, 2, 3}; - Metadata metadata = - new Metadata( - new TextInformationFrame("id1", "description1", "value1"), - new TextInformationFrame("id2", "description2", "value2")); + Metadata metadata = new Metadata(new FakeMetadataEntry("id1"), new FakeMetadataEntry("id2")); ColorInfo colorInfo = new ColorInfo( diff --git a/library/common/src/test/java/com/google/android/exoplayer2/ForwardingPlayerTest.java b/library/common/src/test/java/com/google/android/exoplayer2/ForwardingPlayerTest.java index 41f590ed03..9c6e481e50 100644 --- a/library/common/src/test/java/com/google/android/exoplayer2/ForwardingPlayerTest.java +++ b/library/common/src/test/java/com/google/android/exoplayer2/ForwardingPlayerTest.java @@ -24,10 +24,8 @@ import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import android.content.Context; -import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; -import com.google.android.exoplayer2.testutil.StubExoPlayer; +import com.google.android.exoplayer2.testutil.StubPlayer; import com.google.android.exoplayer2.util.FlagSet; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -48,7 +46,7 @@ public class ForwardingPlayerTest { @Test public void addListener_addsForwardingListener() { - FakePlayer player = new FakePlayer(ApplicationProvider.getApplicationContext()); + FakePlayer player = new FakePlayer(); Player.Listener listener1 = mock(Player.Listener.class); Player.Listener listener2 = mock(Player.Listener.class); @@ -63,7 +61,7 @@ public class ForwardingPlayerTest { @Test public void removeListener_removesForwardingListener() { - FakePlayer player = new FakePlayer(ApplicationProvider.getApplicationContext()); + FakePlayer player = new FakePlayer(); Player.Listener listener1 = mock(Player.Listener.class); Player.Listener listener2 = mock(Player.Listener.class); ForwardingPlayer forwardingPlayer = new ForwardingPlayer(player); @@ -81,7 +79,7 @@ public class ForwardingPlayerTest { @Test public void onEvents_passesForwardingPlayerAsArgument() { - FakePlayer player = new FakePlayer(ApplicationProvider.getApplicationContext()); + FakePlayer player = new FakePlayer(); Player.Listener listener = mock(Player.Listener.class); ForwardingPlayer forwardingPlayer = new ForwardingPlayer(player); forwardingPlayer.addListener(listener); @@ -180,14 +178,10 @@ public class ForwardingPlayerTest { throw new IllegalStateException(); } - private static class FakePlayer extends StubExoPlayer { + private static class FakePlayer extends StubPlayer { private final Set listeners = new HashSet<>(); - public FakePlayer(Context context) { - super(context); - } - @Override public void addListener(Listener listener) { listeners.add(listener); diff --git a/library/common/src/test/java/com/google/android/exoplayer2/metadata/MetadataTest.java b/library/common/src/test/java/com/google/android/exoplayer2/metadata/MetadataTest.java index ac3bfdcef9..f2457e0346 100644 --- a/library/common/src/test/java/com/google/android/exoplayer2/metadata/MetadataTest.java +++ b/library/common/src/test/java/com/google/android/exoplayer2/metadata/MetadataTest.java @@ -19,7 +19,7 @@ import static com.google.common.truth.Truth.assertThat; import android.os.Parcel; import androidx.test.ext.junit.runners.AndroidJUnit4; -import com.google.android.exoplayer2.metadata.id3.BinaryFrame; +import com.google.android.exoplayer2.testutil.FakeMetadataEntry; import org.junit.Test; import org.junit.runner.RunWith; @@ -30,8 +30,7 @@ public class MetadataTest { @Test public void parcelable() { Metadata metadataToParcel = - new Metadata( - new BinaryFrame("id1", new byte[] {1}), new BinaryFrame("id2", new byte[] {2})); + new Metadata(new FakeMetadataEntry("id1"), new FakeMetadataEntry("id2")); Parcel parcel = Parcel.obtain(); metadataToParcel.writeToParcel(parcel, 0); diff --git a/library/common/src/test/java/com/google/android/exoplayer2/trackselection/TrackSelectionParametersTest.java b/library/common/src/test/java/com/google/android/exoplayer2/trackselection/TrackSelectionParametersTest.java index 075898d4d0..7fab202421 100644 --- a/library/common/src/test/java/com/google/android/exoplayer2/trackselection/TrackSelectionParametersTest.java +++ b/library/common/src/test/java/com/google/android/exoplayer2/trackselection/TrackSelectionParametersTest.java @@ -19,10 +19,8 @@ import static androidx.test.core.app.ApplicationProvider.getApplicationContext; import static com.google.common.truth.Truth.assertThat; import androidx.test.ext.junit.runners.AndroidJUnit4; -import com.google.android.exoplayer2.Bundleable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; -import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.SelectionOverride; import com.google.android.exoplayer2.trackselection.TrackSelectionOverrides.TrackSelectionOverride; import com.google.android.exoplayer2.util.MimeTypes; import com.google.common.collect.ImmutableList; @@ -183,17 +181,4 @@ public final class TrackSelectionParametersTest { assertThat(parameters.viewportHeight).isEqualTo(Integer.MAX_VALUE); assertThat(parameters.viewportOrientationMayChange).isTrue(); } - - /** Tests {@link SelectionOverride}'s {@link Bundleable} implementation. */ - @Test - public void roundTripViaBundle_ofSelectionOverride_yieldsEqualInstance() { - SelectionOverride selectionOverrideToBundle = - new SelectionOverride(/* groupIndex= */ 1, /* tracks...= */ 2, 3); - - SelectionOverride selectionOverrideFromBundle = - DefaultTrackSelector.SelectionOverride.CREATOR.fromBundle( - selectionOverrideToBundle.toBundle()); - - assertThat(selectionOverrideFromBundle).isEqualTo(selectionOverrideToBundle); - } } diff --git a/library/common/src/test/java/com/google/android/exoplayer2/util/AtomicFileTest.java b/library/common/src/test/java/com/google/android/exoplayer2/util/AtomicFileTest.java index dd5ccac8cd..c5486b5f4a 100644 --- a/library/common/src/test/java/com/google/android/exoplayer2/util/AtomicFileTest.java +++ b/library/common/src/test/java/com/google/android/exoplayer2/util/AtomicFileTest.java @@ -39,7 +39,7 @@ public final class AtomicFileTest { @Before public void setUp() throws Exception { tempFolder = - Util.createTempDirectory(ApplicationProvider.getApplicationContext(), "ExoPlayerTest"); + Util.createTempDirectory(ApplicationProvider.getApplicationContext(), "AtomicFileTest"); file = new File(tempFolder, "atomicFile"); atomicFile = new AtomicFile(file); } diff --git a/library/common/src/test/java/com/google/android/exoplayer2/util/MediaFormatUtilTest.java b/library/common/src/test/java/com/google/android/exoplayer2/util/MediaFormatUtilTest.java index 0888796417..959b1279a0 100644 --- a/library/common/src/test/java/com/google/android/exoplayer2/util/MediaFormatUtilTest.java +++ b/library/common/src/test/java/com/google/android/exoplayer2/util/MediaFormatUtilTest.java @@ -31,7 +31,7 @@ import org.junit.runner.RunWith; public class MediaFormatUtilTest { @Test - public void createMediaFormatFromEmptyExoPlayerFormat_generatesExpectedEntries() { + public void createMediaFormatFromFormat_withEmptyFormat_generatesExpectedEntries() { MediaFormat mediaFormat = MediaFormatUtil.createMediaFormatFromFormat(new Format.Builder().build()); // Assert that no invalid keys are accidentally being populated. @@ -59,7 +59,7 @@ public class MediaFormatUtilTest { } @Test - public void createMediaFormatFromPopulatedExoPlayerFormat_generatesExpectedMediaFormatEntries() { + public void createMediaFormatFromFormat_withPopulatedFormat_generatesExpectedEntries() { Format format = new Format.Builder() .setAverageBitrate(1) @@ -145,7 +145,7 @@ public class MediaFormatUtilTest { } @Test - public void createMediaFormatWithExoPlayerPcmEncoding_containsExoPlayerSpecificEncoding() { + public void createMediaFormatFromFormat_withPcmEncoding_setsCustomPcmEncodingEntry() { Format format = new Format.Builder().setPcmEncoding(C.ENCODING_PCM_32BIT).build(); MediaFormat mediaFormat = MediaFormatUtil.createMediaFormatFromFormat(format); assertThat(mediaFormat.getInteger(MediaFormatUtil.KEY_EXO_PCM_ENCODING)) diff --git a/library/core/src/test/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelectorTest.java b/library/core/src/test/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelectorTest.java index 0a12b61a12..c2eaf5ddaf 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelectorTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelectorTest.java @@ -1750,6 +1750,19 @@ public final class DefaultTrackSelectorTest { assertThat(trackGroupInfos.get(0).getTrackSupport(0)).isEqualTo(FORMAT_HANDLED); } + /** Tests {@link SelectionOverride}'s {@link Bundleable} implementation. */ + @Test + public void roundTripViaBundle_ofSelectionOverride_yieldsEqualInstance() { + SelectionOverride selectionOverrideToBundle = + new SelectionOverride(/* groupIndex= */ 1, /* tracks...= */ 2, 3); + + SelectionOverride selectionOverrideFromBundle = + DefaultTrackSelector.SelectionOverride.CREATOR.fromBundle( + selectionOverrideToBundle.toBundle()); + + assertThat(selectionOverrideFromBundle).isEqualTo(selectionOverrideToBundle); + } + private static void assertSelections(TrackSelectorResult result, TrackSelection[] expected) { assertThat(result.length).isEqualTo(expected.length); for (int i = 0; i < expected.length; i++) { diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMetadataEntry.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMetadataEntry.java new file mode 100644 index 0000000000..e84d60dc4f --- /dev/null +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMetadataEntry.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer2.testutil; + +import static com.google.android.exoplayer2.util.Util.castNonNull; + +import android.os.Parcel; +import android.os.Parcelable; +import androidx.annotation.Nullable; +import com.google.android.exoplayer2.metadata.Metadata; + +/** A fake {@link Metadata.Entry}. */ +public final class FakeMetadataEntry implements Metadata.Entry { + + public final String data; + + public FakeMetadataEntry(String data) { + this.data = data; + } + + /* package */ FakeMetadataEntry(Parcel in) { + data = castNonNull(in.readString()); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + FakeMetadataEntry other = (FakeMetadataEntry) obj; + return data.equals(other.data); + } + + @Override + public int hashCode() { + return data.hashCode(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(data); + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + + @Override + public FakeMetadataEntry createFromParcel(Parcel in) { + return new FakeMetadataEntry(in); + } + + @Override + public FakeMetadataEntry[] newArray(int size) { + return new FakeMetadataEntry[size]; + } + }; +} diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java index 9a5a50d1d3..c0acae1da6 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java @@ -15,26 +15,14 @@ */ package com.google.android.exoplayer2.testutil; -import android.content.Context; import android.os.Looper; -import android.view.Surface; -import android.view.SurfaceHolder; -import android.view.SurfaceView; -import android.view.TextureView; import androidx.annotation.Nullable; -import com.google.android.exoplayer2.BasePlayer; -import com.google.android.exoplayer2.DeviceInfo; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.Format; -import com.google.android.exoplayer2.MediaItem; -import com.google.android.exoplayer2.MediaMetadata; -import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.PlayerMessage; import com.google.android.exoplayer2.SeekParameters; -import com.google.android.exoplayer2.Timeline; -import com.google.android.exoplayer2.TracksInfo; import com.google.android.exoplayer2.analytics.AnalyticsCollector; import com.google.android.exoplayer2.analytics.AnalyticsListener; import com.google.android.exoplayer2.audio.AudioAttributes; @@ -42,15 +30,10 @@ import com.google.android.exoplayer2.audio.AuxEffectInfo; import com.google.android.exoplayer2.decoder.DecoderCounters; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.ShuffleOrder; -import com.google.android.exoplayer2.source.TrackGroupArray; -import com.google.android.exoplayer2.text.Cue; -import com.google.android.exoplayer2.trackselection.TrackSelectionArray; -import com.google.android.exoplayer2.trackselection.TrackSelectionParameters; import com.google.android.exoplayer2.trackselection.TrackSelector; import com.google.android.exoplayer2.util.Clock; import com.google.android.exoplayer2.util.PriorityTaskManager; import com.google.android.exoplayer2.video.VideoFrameMetadataListener; -import com.google.android.exoplayer2.video.VideoSize; import com.google.android.exoplayer2.video.spherical.CameraMotionListener; import java.util.List; @@ -58,11 +41,7 @@ import java.util.List; * An abstract {@link ExoPlayer} implementation that throws {@link UnsupportedOperationException} * from every method. */ -public class StubExoPlayer extends BasePlayer implements ExoPlayer { - - public StubExoPlayer(Context context) { - super(); - } +public class StubExoPlayer extends StubPlayer implements ExoPlayer { @Override @Deprecated @@ -93,31 +72,16 @@ public class StubExoPlayer extends BasePlayer implements ExoPlayer { throw new UnsupportedOperationException(); } - @Override - public Looper getApplicationLooper() { - throw new UnsupportedOperationException(); - } - @Override public Clock getClock() { throw new UnsupportedOperationException(); } - @Override - public void addListener(Listener listener) { - throw new UnsupportedOperationException(); - } - @Override public void addListener(Player.EventListener listener) { throw new UnsupportedOperationException(); } - @Override - public void removeListener(Listener listener) { - throw new UnsupportedOperationException(); - } - @Override public void removeListener(Player.EventListener listener) { throw new UnsupportedOperationException(); @@ -148,68 +112,29 @@ public class StubExoPlayer extends BasePlayer implements ExoPlayer { throw new UnsupportedOperationException(); } - @Override - @State - public int getPlaybackState() { - throw new UnsupportedOperationException(); - } - - @Override - @PlaybackSuppressionReason - public int getPlaybackSuppressionReason() { - throw new UnsupportedOperationException(); - } - @Override public ExoPlaybackException getPlayerError() { throw new UnsupportedOperationException(); } - /** @deprecated Use {@link #prepare()} instead. */ @Deprecated @Override public void retry() { throw new UnsupportedOperationException(); } - /** - * @deprecated Use {@link #setMediaSource(MediaSource)} and {@link ExoPlayer#prepare()} instead. - */ - @Deprecated - @Override - public void prepare() { - throw new UnsupportedOperationException(); - } - - /** - * @deprecated Use {@link #setMediaSource(MediaSource)} and {@link ExoPlayer#prepare()} instead. - */ @Deprecated @Override public void prepare(MediaSource mediaSource) { throw new UnsupportedOperationException(); } - /** - * @deprecated Use {@link #setMediaSource(MediaSource, boolean)} and {@link ExoPlayer#prepare()} - * instead. - */ @Deprecated @Override public void prepare(MediaSource mediaSource, boolean resetPosition, boolean resetState) { throw new UnsupportedOperationException(); } - @Override - public void setMediaItems(List mediaItems, boolean resetPosition) { - throw new UnsupportedOperationException(); - } - - @Override - public void setMediaItems(List mediaItems, int startIndex, long startPositionMs) { - throw new UnsupportedOperationException(); - } - @Override public void setMediaSource(MediaSource mediaSource) { throw new UnsupportedOperationException(); @@ -241,11 +166,6 @@ public class StubExoPlayer extends BasePlayer implements ExoPlayer { throw new UnsupportedOperationException(); } - @Override - public void addMediaItems(int index, List mediaItems) { - throw new UnsupportedOperationException(); - } - @Override public void addMediaSource(MediaSource mediaSource) { throw new UnsupportedOperationException(); @@ -266,41 +186,6 @@ public class StubExoPlayer extends BasePlayer implements ExoPlayer { throw new UnsupportedOperationException(); } - @Override - public void moveMediaItems(int fromIndex, int toIndex, int newIndex) { - throw new UnsupportedOperationException(); - } - - @Override - public void removeMediaItems(int fromIndex, int toIndex) { - throw new UnsupportedOperationException(); - } - - @Override - public Commands getAvailableCommands() { - throw new UnsupportedOperationException(); - } - - @Override - public void setPlayWhenReady(boolean playWhenReady) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean getPlayWhenReady() { - throw new UnsupportedOperationException(); - } - - @Override - public void setRepeatMode(@RepeatMode int repeatMode) { - throw new UnsupportedOperationException(); - } - - @Override - public int getRepeatMode() { - throw new UnsupportedOperationException(); - } - @Override public void setShuffleOrder(ShuffleOrder shuffleOrder) { throw new UnsupportedOperationException(); @@ -381,51 +266,6 @@ public class StubExoPlayer extends BasePlayer implements ExoPlayer { throw new UnsupportedOperationException(); } - @Override - public void setShuffleModeEnabled(boolean shuffleModeEnabled) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean getShuffleModeEnabled() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isLoading() { - throw new UnsupportedOperationException(); - } - - @Override - public void seekTo(int mediaItemIndex, long positionMs) { - throw new UnsupportedOperationException(); - } - - @Override - public long getSeekBackIncrement() { - throw new UnsupportedOperationException(); - } - - @Override - public long getSeekForwardIncrement() { - throw new UnsupportedOperationException(); - } - - @Override - public long getMaxSeekToPreviousPosition() { - throw new UnsupportedOperationException(); - } - - @Override - public void setPlaybackParameters(PlaybackParameters playbackParameters) { - throw new UnsupportedOperationException(); - } - - @Override - public PlaybackParameters getPlaybackParameters() { - throw new UnsupportedOperationException(); - } - @Override public void setSeekParameters(@Nullable SeekParameters seekParameters) { throw new UnsupportedOperationException(); @@ -436,22 +276,6 @@ public class StubExoPlayer extends BasePlayer implements ExoPlayer { throw new UnsupportedOperationException(); } - @Override - public void stop() { - throw new UnsupportedOperationException(); - } - - @Deprecated - @Override - public void stop(boolean reset) { - throw new UnsupportedOperationException(); - } - - @Override - public void release() { - throw new UnsupportedOperationException(); - } - @Override public PlayerMessage createMessage(PlayerMessage.Target target) { throw new UnsupportedOperationException(); @@ -473,211 +297,6 @@ public class StubExoPlayer extends BasePlayer implements ExoPlayer { throw new UnsupportedOperationException(); } - @Override - public TrackGroupArray getCurrentTrackGroups() { - throw new UnsupportedOperationException(); - } - - @Override - public TrackSelectionArray getCurrentTrackSelections() { - throw new UnsupportedOperationException(); - } - - @Override - public TracksInfo getCurrentTracksInfo() { - throw new UnsupportedOperationException(); - } - - @Override - public TrackSelectionParameters getTrackSelectionParameters() { - throw new UnsupportedOperationException(); - } - - @Override - public void setTrackSelectionParameters(TrackSelectionParameters parameters) { - throw new UnsupportedOperationException(); - } - - @Override - public MediaMetadata getMediaMetadata() { - throw new UnsupportedOperationException(); - } - - @Override - public MediaMetadata getPlaylistMetadata() { - throw new UnsupportedOperationException(); - } - - @Override - public void setPlaylistMetadata(MediaMetadata mediaMetadata) { - throw new UnsupportedOperationException(); - } - - @Override - public Timeline getCurrentTimeline() { - throw new UnsupportedOperationException(); - } - - @Override - public int getCurrentPeriodIndex() { - throw new UnsupportedOperationException(); - } - - @Override - public int getCurrentMediaItemIndex() { - throw new UnsupportedOperationException(); - } - - @Override - public long getDuration() { - throw new UnsupportedOperationException(); - } - - @Override - public long getCurrentPosition() { - throw new UnsupportedOperationException(); - } - - @Override - public long getBufferedPosition() { - throw new UnsupportedOperationException(); - } - - @Override - public long getTotalBufferedDuration() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isPlayingAd() { - throw new UnsupportedOperationException(); - } - - @Override - public int getCurrentAdGroupIndex() { - throw new UnsupportedOperationException(); - } - - @Override - public int getCurrentAdIndexInAdGroup() { - throw new UnsupportedOperationException(); - } - - @Override - public long getContentPosition() { - throw new UnsupportedOperationException(); - } - - @Override - public long getContentBufferedPosition() { - throw new UnsupportedOperationException(); - } - - @Override - public AudioAttributes getAudioAttributes() { - throw new UnsupportedOperationException(); - } - - @Override - public void setVolume(float volume) { - throw new UnsupportedOperationException(); - } - - @Override - public float getVolume() { - throw new UnsupportedOperationException(); - } - - @Override - public void clearVideoSurface() { - throw new UnsupportedOperationException(); - } - - @Override - public void clearVideoSurface(@Nullable Surface surface) { - throw new UnsupportedOperationException(); - } - - @Override - public void setVideoSurface(@Nullable Surface surface) { - throw new UnsupportedOperationException(); - } - - @Override - public void setVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder) { - throw new UnsupportedOperationException(); - } - - @Override - public void clearVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder) { - throw new UnsupportedOperationException(); - } - - @Override - public void setVideoSurfaceView(@Nullable SurfaceView surfaceView) { - throw new UnsupportedOperationException(); - } - - @Override - public void clearVideoSurfaceView(@Nullable SurfaceView surfaceView) { - throw new UnsupportedOperationException(); - } - - @Override - public void setVideoTextureView(@Nullable TextureView textureView) { - throw new UnsupportedOperationException(); - } - - @Override - public void clearVideoTextureView(@Nullable TextureView textureView) { - throw new UnsupportedOperationException(); - } - - @Override - public VideoSize getVideoSize() { - throw new UnsupportedOperationException(); - } - - @Override - public List getCurrentCues() { - throw new UnsupportedOperationException(); - } - - @Override - public DeviceInfo getDeviceInfo() { - throw new UnsupportedOperationException(); - } - - @Override - public int getDeviceVolume() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isDeviceMuted() { - throw new UnsupportedOperationException(); - } - - @Override - public void setDeviceVolume(int volume) { - throw new UnsupportedOperationException(); - } - - @Override - public void increaseDeviceVolume() { - throw new UnsupportedOperationException(); - } - - @Override - public void decreaseDeviceVolume() { - throw new UnsupportedOperationException(); - } - - @Override - public void setDeviceMuted(boolean muted) { - throw new UnsupportedOperationException(); - } - @Override public void setForegroundMode(boolean foregroundMode) { throw new UnsupportedOperationException(); diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubPlayer.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubPlayer.java new file mode 100644 index 0000000000..79f1214810 --- /dev/null +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubPlayer.java @@ -0,0 +1,399 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer2.testutil; + +import android.os.Looper; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.TextureView; +import androidx.annotation.Nullable; +import com.google.android.exoplayer2.BasePlayer; +import com.google.android.exoplayer2.DeviceInfo; +import com.google.android.exoplayer2.MediaItem; +import com.google.android.exoplayer2.MediaMetadata; +import com.google.android.exoplayer2.PlaybackException; +import com.google.android.exoplayer2.PlaybackParameters; +import com.google.android.exoplayer2.Player; +import com.google.android.exoplayer2.Timeline; +import com.google.android.exoplayer2.TracksInfo; +import com.google.android.exoplayer2.audio.AudioAttributes; +import com.google.android.exoplayer2.source.TrackGroupArray; +import com.google.android.exoplayer2.text.Cue; +import com.google.android.exoplayer2.trackselection.TrackSelectionArray; +import com.google.android.exoplayer2.trackselection.TrackSelectionParameters; +import com.google.android.exoplayer2.video.VideoSize; +import java.util.List; + +/** + * An abstract {@link Player} implementation that throws {@link UnsupportedOperationException} from + * every method. + */ +public class StubPlayer extends BasePlayer { + + @Override + public Looper getApplicationLooper() { + throw new UnsupportedOperationException(); + } + + @Override + public void addListener(Listener listener) { + throw new UnsupportedOperationException(); + } + + @Override + public void removeListener(Listener listener) { + throw new UnsupportedOperationException(); + } + + @Override + @State + public int getPlaybackState() { + throw new UnsupportedOperationException(); + } + + @Override + @PlaybackSuppressionReason + public int getPlaybackSuppressionReason() { + throw new UnsupportedOperationException(); + } + + @Override + public PlaybackException getPlayerError() { + throw new UnsupportedOperationException(); + } + + @Override + public void prepare() { + throw new UnsupportedOperationException(); + } + + @Override + public void setMediaItems(List mediaItems, boolean resetPosition) { + throw new UnsupportedOperationException(); + } + + @Override + public void setMediaItems(List mediaItems, int startIndex, long startPositionMs) { + throw new UnsupportedOperationException(); + } + + @Override + public void addMediaItems(int index, List mediaItems) { + throw new UnsupportedOperationException(); + } + + @Override + public void moveMediaItems(int fromIndex, int toIndex, int newIndex) { + throw new UnsupportedOperationException(); + } + + @Override + public void removeMediaItems(int fromIndex, int toIndex) { + throw new UnsupportedOperationException(); + } + + @Override + public Commands getAvailableCommands() { + throw new UnsupportedOperationException(); + } + + @Override + public void setPlayWhenReady(boolean playWhenReady) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean getPlayWhenReady() { + throw new UnsupportedOperationException(); + } + + @Override + public void setRepeatMode(@RepeatMode int repeatMode) { + throw new UnsupportedOperationException(); + } + + @Override + public int getRepeatMode() { + throw new UnsupportedOperationException(); + } + + @Override + public void setShuffleModeEnabled(boolean shuffleModeEnabled) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean getShuffleModeEnabled() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isLoading() { + throw new UnsupportedOperationException(); + } + + @Override + public void seekTo(int mediaItemIndex, long positionMs) { + throw new UnsupportedOperationException(); + } + + @Override + public long getSeekBackIncrement() { + throw new UnsupportedOperationException(); + } + + @Override + public long getSeekForwardIncrement() { + throw new UnsupportedOperationException(); + } + + @Override + public long getMaxSeekToPreviousPosition() { + throw new UnsupportedOperationException(); + } + + @Override + public void setPlaybackParameters(PlaybackParameters playbackParameters) { + throw new UnsupportedOperationException(); + } + + @Override + public PlaybackParameters getPlaybackParameters() { + throw new UnsupportedOperationException(); + } + + @Override + public void stop() { + throw new UnsupportedOperationException(); + } + + @Deprecated + @Override + public void stop(boolean reset) { + throw new UnsupportedOperationException(); + } + + @Override + public void release() { + throw new UnsupportedOperationException(); + } + + @Override + public TrackGroupArray getCurrentTrackGroups() { + throw new UnsupportedOperationException(); + } + + @Override + public TrackSelectionArray getCurrentTrackSelections() { + throw new UnsupportedOperationException(); + } + + @Override + public TracksInfo getCurrentTracksInfo() { + throw new UnsupportedOperationException(); + } + + @Override + public TrackSelectionParameters getTrackSelectionParameters() { + throw new UnsupportedOperationException(); + } + + @Override + public void setTrackSelectionParameters(TrackSelectionParameters parameters) { + throw new UnsupportedOperationException(); + } + + @Override + public MediaMetadata getMediaMetadata() { + throw new UnsupportedOperationException(); + } + + @Override + public MediaMetadata getPlaylistMetadata() { + throw new UnsupportedOperationException(); + } + + @Override + public void setPlaylistMetadata(MediaMetadata mediaMetadata) { + throw new UnsupportedOperationException(); + } + + @Override + public Timeline getCurrentTimeline() { + throw new UnsupportedOperationException(); + } + + @Override + public int getCurrentPeriodIndex() { + throw new UnsupportedOperationException(); + } + + @Override + public int getCurrentMediaItemIndex() { + throw new UnsupportedOperationException(); + } + + @Override + public long getDuration() { + throw new UnsupportedOperationException(); + } + + @Override + public long getCurrentPosition() { + throw new UnsupportedOperationException(); + } + + @Override + public long getBufferedPosition() { + throw new UnsupportedOperationException(); + } + + @Override + public long getTotalBufferedDuration() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isPlayingAd() { + throw new UnsupportedOperationException(); + } + + @Override + public int getCurrentAdGroupIndex() { + throw new UnsupportedOperationException(); + } + + @Override + public int getCurrentAdIndexInAdGroup() { + throw new UnsupportedOperationException(); + } + + @Override + public long getContentPosition() { + throw new UnsupportedOperationException(); + } + + @Override + public long getContentBufferedPosition() { + throw new UnsupportedOperationException(); + } + + @Override + public AudioAttributes getAudioAttributes() { + throw new UnsupportedOperationException(); + } + + @Override + public void setVolume(float volume) { + throw new UnsupportedOperationException(); + } + + @Override + public float getVolume() { + throw new UnsupportedOperationException(); + } + + @Override + public void clearVideoSurface() { + throw new UnsupportedOperationException(); + } + + @Override + public void clearVideoSurface(@Nullable Surface surface) { + throw new UnsupportedOperationException(); + } + + @Override + public void setVideoSurface(@Nullable Surface surface) { + throw new UnsupportedOperationException(); + } + + @Override + public void setVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder) { + throw new UnsupportedOperationException(); + } + + @Override + public void clearVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder) { + throw new UnsupportedOperationException(); + } + + @Override + public void setVideoSurfaceView(@Nullable SurfaceView surfaceView) { + throw new UnsupportedOperationException(); + } + + @Override + public void clearVideoSurfaceView(@Nullable SurfaceView surfaceView) { + throw new UnsupportedOperationException(); + } + + @Override + public void setVideoTextureView(@Nullable TextureView textureView) { + throw new UnsupportedOperationException(); + } + + @Override + public void clearVideoTextureView(@Nullable TextureView textureView) { + throw new UnsupportedOperationException(); + } + + @Override + public VideoSize getVideoSize() { + throw new UnsupportedOperationException(); + } + + @Override + public List getCurrentCues() { + throw new UnsupportedOperationException(); + } + + @Override + public DeviceInfo getDeviceInfo() { + throw new UnsupportedOperationException(); + } + + @Override + public int getDeviceVolume() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isDeviceMuted() { + throw new UnsupportedOperationException(); + } + + @Override + public void setDeviceVolume(int volume) { + throw new UnsupportedOperationException(); + } + + @Override + public void increaseDeviceVolume() { + throw new UnsupportedOperationException(); + } + + @Override + public void decreaseDeviceVolume() { + throw new UnsupportedOperationException(); + } + + @Override + public void setDeviceMuted(boolean muted) { + throw new UnsupportedOperationException(); + } +} From 288899ee9d57926f02b3acff720c1359eb0860c6 Mon Sep 17 00:00:00 2001 From: samrobinson Date: Fri, 29 Oct 2021 14:35:53 +0000 Subject: [PATCH 4/6] Change Transformer to use Player.Listener. AnalyticsListener should not be used for non-analytical actions. PiperOrigin-RevId: 406355758 --- .../transformer/TranscodingTransformer.java | 18 ++++++++---------- .../exoplayer2/transformer/Transformer.java | 17 ++++++++--------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TranscodingTransformer.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TranscodingTransformer.java index d766987aa8..6f58812be4 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TranscodingTransformer.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TranscodingTransformer.java @@ -45,7 +45,6 @@ import com.google.android.exoplayer2.Renderer; import com.google.android.exoplayer2.RenderersFactory; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.TracksInfo; -import com.google.android.exoplayer2.analytics.AnalyticsListener; import com.google.android.exoplayer2.audio.AudioRendererEventListener; import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory; import com.google.android.exoplayer2.extractor.mp4.Mp4Extractor; @@ -573,8 +572,7 @@ public final class TranscodingTransformer { .setClock(clock) .build(); player.setMediaItem(mediaItem); - player.addAnalyticsListener( - new TranscodingTransformerAnalyticsListener(mediaItem, muxerWrapper)); + player.addListener(new TranscodingTransformerPlayerListener(mediaItem, muxerWrapper)); player.prepare(); progressState = PROGRESS_STATE_WAITING_FOR_AVAILABILITY; @@ -688,30 +686,30 @@ public final class TranscodingTransformer { } } - private final class TranscodingTransformerAnalyticsListener implements AnalyticsListener { + private final class TranscodingTransformerPlayerListener implements Player.Listener { private final MediaItem mediaItem; private final MuxerWrapper muxerWrapper; - public TranscodingTransformerAnalyticsListener(MediaItem mediaItem, MuxerWrapper muxerWrapper) { + public TranscodingTransformerPlayerListener(MediaItem mediaItem, MuxerWrapper muxerWrapper) { this.mediaItem = mediaItem; this.muxerWrapper = muxerWrapper; } @Override - public void onPlaybackStateChanged(EventTime eventTime, int state) { + public void onPlaybackStateChanged(int state) { if (state == Player.STATE_ENDED) { handleTransformationEnded(/* exception= */ null); } } @Override - public void onTimelineChanged(EventTime eventTime, int reason) { + public void onTimelineChanged(Timeline timeline, int reason) { if (progressState != PROGRESS_STATE_WAITING_FOR_AVAILABILITY) { return; } Timeline.Window window = new Timeline.Window(); - eventTime.timeline.getWindow(/* windowIndex= */ 0, window); + timeline.getWindow(/* windowIndex= */ 0, window); if (!window.isPlaceholder) { long durationUs = window.durationUs; // Make progress permanently unavailable if the duration is unknown, so that it doesn't jump @@ -726,7 +724,7 @@ public final class TranscodingTransformer { } @Override - public void onTracksInfoChanged(EventTime eventTime, TracksInfo tracksInfo) { + public void onTracksInfoChanged(TracksInfo tracksInfo) { if (muxerWrapper.getTrackCount() == 0) { handleTransformationEnded( new IllegalStateException( @@ -736,7 +734,7 @@ public final class TranscodingTransformer { } @Override - public void onPlayerError(EventTime eventTime, PlaybackException error) { + public void onPlayerError(PlaybackException error) { handleTransformationEnded(error); } diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java index 1a79061f66..0fd763ad26 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java @@ -46,7 +46,6 @@ import com.google.android.exoplayer2.Renderer; import com.google.android.exoplayer2.RenderersFactory; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.TracksInfo; -import com.google.android.exoplayer2.analytics.AnalyticsListener; import com.google.android.exoplayer2.audio.AudioRendererEventListener; import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory; import com.google.android.exoplayer2.extractor.mp4.Mp4Extractor; @@ -496,7 +495,7 @@ public final class Transformer { .setClock(clock) .build(); player.setMediaItem(mediaItem); - player.addAnalyticsListener(new TransformerAnalyticsListener(mediaItem, muxerWrapper)); + player.addListener(new TransformerPlayerListener(mediaItem, muxerWrapper)); player.prepare(); progressState = PROGRESS_STATE_WAITING_FOR_AVAILABILITY; @@ -606,30 +605,30 @@ public final class Transformer { } } - private final class TransformerAnalyticsListener implements AnalyticsListener { + private final class TransformerPlayerListener implements Player.Listener { private final MediaItem mediaItem; private final MuxerWrapper muxerWrapper; - public TransformerAnalyticsListener(MediaItem mediaItem, MuxerWrapper muxerWrapper) { + public TransformerPlayerListener(MediaItem mediaItem, MuxerWrapper muxerWrapper) { this.mediaItem = mediaItem; this.muxerWrapper = muxerWrapper; } @Override - public void onPlaybackStateChanged(EventTime eventTime, int state) { + public void onPlaybackStateChanged(int state) { if (state == Player.STATE_ENDED) { handleTransformationEnded(/* exception= */ null); } } @Override - public void onTimelineChanged(EventTime eventTime, int reason) { + public void onTimelineChanged(Timeline timeline, int reason) { if (progressState != PROGRESS_STATE_WAITING_FOR_AVAILABILITY) { return; } Timeline.Window window = new Timeline.Window(); - eventTime.timeline.getWindow(/* windowIndex= */ 0, window); + timeline.getWindow(/* windowIndex= */ 0, window); if (!window.isPlaceholder) { long durationUs = window.durationUs; // Make progress permanently unavailable if the duration is unknown, so that it doesn't jump @@ -644,7 +643,7 @@ public final class Transformer { } @Override - public void onTracksInfoChanged(EventTime eventTime, TracksInfo tracksInfo) { + public void onTracksInfoChanged(TracksInfo tracksInfo) { if (muxerWrapper.getTrackCount() == 0) { handleTransformationEnded( new IllegalStateException( @@ -654,7 +653,7 @@ public final class Transformer { } @Override - public void onPlayerError(EventTime eventTime, PlaybackException error) { + public void onPlayerError(PlaybackException error) { handleTransformationEnded(error); } From fa98935c0615efc9432de96c6cd397fa22204ec1 Mon Sep 17 00:00:00 2001 From: kimvde Date: Fri, 29 Oct 2021 16:39:07 +0000 Subject: [PATCH 5/6] WavExtractor: split read stages into states This refactoring is the basis to support RF64 (see Issue: google/ExoPlayer#9543). #minor-release PiperOrigin-RevId: 406377924 --- .../extractor/wav/WavExtractor.java | 148 ++++++++++++------ .../extractor/wav/WavHeaderReader.java | 10 +- 2 files changed, 100 insertions(+), 58 deletions(-) diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/wav/WavExtractor.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/wav/WavExtractor.java index c1dd2a6ddc..97d98b5fcc 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/wav/WavExtractor.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/wav/WavExtractor.java @@ -19,6 +19,7 @@ import static java.lang.Math.max; import static java.lang.Math.min; import android.util.Pair; +import androidx.annotation.IntDef; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.ParserException; @@ -34,8 +35,14 @@ import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.Util; import java.io.IOException; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import org.checkerframework.checker.nullness.qual.EnsuresNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.RequiresNonNull; /** Extracts data from WAV byte streams. */ public final class WavExtractor implements Extractor { @@ -50,13 +57,26 @@ public final class WavExtractor implements Extractor { /** Factory for {@link WavExtractor} instances. */ public static final ExtractorsFactory FACTORY = () -> new Extractor[] {new WavExtractor()}; + /** Parser state. */ + @Documented + @Retention(RetentionPolicy.SOURCE) + @Target({ElementType.TYPE_USE}) + @IntDef({STATE_READING_HEADER, STATE_SKIPPING_TO_SAMPLE_DATA, STATE_READING_SAMPLE_DATA}) + private @interface State {} + + private static final int STATE_READING_HEADER = 0; + private static final int STATE_SKIPPING_TO_SAMPLE_DATA = 1; + private static final int STATE_READING_SAMPLE_DATA = 2; + private @MonotonicNonNull ExtractorOutput extractorOutput; private @MonotonicNonNull TrackOutput trackOutput; + private @State int state; private @MonotonicNonNull OutputWriter outputWriter; private int dataStartPosition; private long dataEndPosition; public WavExtractor() { + state = STATE_READING_HEADER; dataStartPosition = C.POSITION_UNSET; dataEndPosition = C.POSITION_UNSET; } @@ -75,6 +95,7 @@ public final class WavExtractor implements Extractor { @Override public void seek(long position, long timeUs) { + state = position == 0 ? STATE_READING_HEADER : STATE_READING_SAMPLE_DATA; if (outputWriter != null) { outputWriter.reset(timeUs); } @@ -86,59 +107,21 @@ public final class WavExtractor implements Extractor { } @Override + @ReadResult public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException { assertInitialized(); - if (outputWriter == null) { - WavHeader header = WavHeaderReader.peek(input); - if (header == null) { - // Should only happen if the media wasn't sniffed. - throw ParserException.createForMalformedContainer( - "Unsupported or unrecognized wav header.", /* cause= */ null); - } - - if (header.formatType == WavUtil.TYPE_IMA_ADPCM) { - outputWriter = new ImaAdPcmOutputWriter(extractorOutput, trackOutput, header); - } else if (header.formatType == WavUtil.TYPE_ALAW) { - outputWriter = - new PassthroughOutputWriter( - extractorOutput, - trackOutput, - header, - MimeTypes.AUDIO_ALAW, - /* pcmEncoding= */ Format.NO_VALUE); - } else if (header.formatType == WavUtil.TYPE_MLAW) { - outputWriter = - new PassthroughOutputWriter( - extractorOutput, - trackOutput, - header, - MimeTypes.AUDIO_MLAW, - /* pcmEncoding= */ Format.NO_VALUE); - } else { - @C.PcmEncoding - int pcmEncoding = WavUtil.getPcmEncodingForType(header.formatType, header.bitsPerSample); - if (pcmEncoding == C.ENCODING_INVALID) { - throw ParserException.createForUnsupportedContainerFeature( - "Unsupported WAV format type: " + header.formatType); - } - outputWriter = - new PassthroughOutputWriter( - extractorOutput, trackOutput, header, MimeTypes.AUDIO_RAW, pcmEncoding); - } + switch (state) { + case STATE_READING_HEADER: + readHeader(input); + return Extractor.RESULT_CONTINUE; + case STATE_SKIPPING_TO_SAMPLE_DATA: + skipToSampleData(input); + return Extractor.RESULT_CONTINUE; + case STATE_READING_SAMPLE_DATA: + return readSampleData(input); + default: + throw new IllegalStateException(); } - - if (dataStartPosition == C.POSITION_UNSET) { - Pair dataBounds = WavHeaderReader.skipToData(input); - dataStartPosition = dataBounds.first.intValue(); - dataEndPosition = dataBounds.second; - outputWriter.init(dataStartPosition, dataEndPosition); - } else if (input.getPosition() == 0) { - input.skipFully(dataStartPosition); - } - - Assertions.checkState(dataEndPosition != C.POSITION_UNSET); - long bytesLeft = dataEndPosition - input.getPosition(); - return outputWriter.sampleData(input, bytesLeft) ? RESULT_END_OF_INPUT : RESULT_CONTINUE; } @EnsuresNonNull({"extractorOutput", "trackOutput"}) @@ -147,6 +130,71 @@ public final class WavExtractor implements Extractor { Util.castNonNull(extractorOutput); } + @RequiresNonNull({"extractorOutput", "trackOutput"}) + private void readHeader(ExtractorInput input) throws IOException { + Assertions.checkState(input.getPosition() == 0); + if (dataStartPosition != C.POSITION_UNSET) { + input.skipFully(dataStartPosition); + state = STATE_READING_SAMPLE_DATA; + return; + } + WavHeader header = WavHeaderReader.peek(input); + if (header == null) { + // Should only happen if the media wasn't sniffed. + throw ParserException.createForMalformedContainer( + "Unsupported or unrecognized wav header.", /* cause= */ null); + } + input.skipFully((int) (input.getPeekPosition() - input.getPosition())); + + if (header.formatType == WavUtil.TYPE_IMA_ADPCM) { + outputWriter = new ImaAdPcmOutputWriter(extractorOutput, trackOutput, header); + } else if (header.formatType == WavUtil.TYPE_ALAW) { + outputWriter = + new PassthroughOutputWriter( + extractorOutput, + trackOutput, + header, + MimeTypes.AUDIO_ALAW, + /* pcmEncoding= */ Format.NO_VALUE); + } else if (header.formatType == WavUtil.TYPE_MLAW) { + outputWriter = + new PassthroughOutputWriter( + extractorOutput, + trackOutput, + header, + MimeTypes.AUDIO_MLAW, + /* pcmEncoding= */ Format.NO_VALUE); + } else { + @C.PcmEncoding + int pcmEncoding = WavUtil.getPcmEncodingForType(header.formatType, header.bitsPerSample); + if (pcmEncoding == C.ENCODING_INVALID) { + throw ParserException.createForUnsupportedContainerFeature( + "Unsupported WAV format type: " + header.formatType); + } + outputWriter = + new PassthroughOutputWriter( + extractorOutput, trackOutput, header, MimeTypes.AUDIO_RAW, pcmEncoding); + } + state = STATE_SKIPPING_TO_SAMPLE_DATA; + } + + private void skipToSampleData(ExtractorInput input) throws IOException { + Pair dataBounds = WavHeaderReader.skipToSampleData(input); + dataStartPosition = dataBounds.first.intValue(); + dataEndPosition = dataBounds.second; + Assertions.checkNotNull(outputWriter).init(dataStartPosition, dataEndPosition); + state = STATE_READING_SAMPLE_DATA; + } + + @ReadResult + private int readSampleData(ExtractorInput input) throws IOException { + Assertions.checkState(dataEndPosition != C.POSITION_UNSET); + long bytesLeft = dataEndPosition - input.getPosition(); + return Assertions.checkNotNull(outputWriter).sampleData(input, bytesLeft) + ? RESULT_END_OF_INPUT + : RESULT_CONTINUE; + } + /** Writes to the extractor's output. */ private interface OutputWriter { diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/wav/WavHeaderReader.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/wav/WavHeaderReader.java index f794933d16..147fba9c53 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/wav/WavHeaderReader.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/wav/WavHeaderReader.java @@ -108,7 +108,7 @@ import java.io.IOException; * @throws ParserException If an error occurs parsing chunks. * @throws IOException If reading from the input fails. */ - public static Pair skipToData(ExtractorInput input) throws IOException { + public static Pair skipToSampleData(ExtractorInput input) throws IOException { Assertions.checkNotNull(input); // Make sure the peek position is set to the read position before we peek the first header. @@ -118,14 +118,8 @@ import java.io.IOException; // Skip all chunks until we find the data header. ChunkHeader chunkHeader = ChunkHeader.peek(input, scratch); while (chunkHeader.id != WavUtil.DATA_FOURCC) { - if (chunkHeader.id != WavUtil.RIFF_FOURCC && chunkHeader.id != WavUtil.FMT_FOURCC) { - Log.w(TAG, "Ignoring unknown WAV chunk: " + chunkHeader.id); - } + Log.w(TAG, "Ignoring unknown WAV chunk: " + chunkHeader.id); long bytesToSkip = ChunkHeader.SIZE_IN_BYTES + chunkHeader.size; - // Override size of RIFF chunk, since it describes its size as the entire file. - if (chunkHeader.id == WavUtil.RIFF_FOURCC) { - bytesToSkip = ChunkHeader.SIZE_IN_BYTES + 4; - } if (bytesToSkip > Integer.MAX_VALUE) { throw ParserException.createForUnsupportedContainerFeature( "Chunk is too large (~2GB+) to skip; id: " + chunkHeader.id); From c53924326dd100874d080c55812659a3cb9e843a Mon Sep 17 00:00:00 2001 From: huangdarwin Date: Fri, 29 Oct 2021 17:16:01 +0000 Subject: [PATCH 6/6] GL: Make ProjectionRenderer's GL Program @MonotonicNonNull. PiperOrigin-RevId: 406385758 --- .../video/spherical/ProjectionRenderer.java | 8 ++--- .../video/spherical/SceneRenderer.java | 2 +- .../transformer/TranscodingTransformer.java | 29 +++++++++++++++++++ .../transformer/Transformation.java | 3 ++ .../exoplayer2/transformer/Transformer.java | 2 ++ 5 files changed, 39 insertions(+), 5 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/video/spherical/ProjectionRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/video/spherical/ProjectionRenderer.java index f313225fc6..692b7e687e 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/video/spherical/ProjectionRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/video/spherical/ProjectionRenderer.java @@ -24,6 +24,7 @@ import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.util.GlUtil; import java.nio.FloatBuffer; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** * Utility class to render spherical meshes for video or images. Call {@link #init()} on the GL @@ -93,9 +94,9 @@ import java.nio.FloatBuffer; private int stereoMode; @Nullable private MeshData leftMeshData; @Nullable private MeshData rightMeshData; - @Nullable private GlUtil.Program program; + private GlUtil.@MonotonicNonNull Program program; - // Program related GL items. These are only valid if program is non-null. + // Program related GL items. These are only valid if Program is valid. private int mvpMatrixHandle; private int uTexMatrixHandle; private int positionHandle; @@ -195,11 +196,10 @@ import java.nio.FloatBuffer; GLES20.glDisableVertexAttribArray(texCoordsHandle); } - /** Cleans up the GL resources. */ + /** Cleans up GL resources. */ /* package */ void shutdown() { if (program != null) { program.delete(); - program = null; } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/video/spherical/SceneRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/video/spherical/SceneRenderer.java index dcc15d1fed..0fcff23c5e 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/video/spherical/SceneRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/video/spherical/SceneRenderer.java @@ -129,7 +129,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; projectionRenderer.draw(textureId, tempMatrix, rightEye); } - /** Cleans up the GL resources. */ + /** Cleans up GL resources. */ public void shutdown() { projectionRenderer.shutdown(); } diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TranscodingTransformer.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TranscodingTransformer.java index 6f58812be4..348f3dac04 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TranscodingTransformer.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TranscodingTransformer.java @@ -55,6 +55,7 @@ import com.google.android.exoplayer2.source.MediaSourceFactory; import com.google.android.exoplayer2.text.TextOutput; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.util.Clock; +import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.video.VideoRendererEventListener; @@ -91,12 +92,16 @@ public final class TranscodingTransformer { /** A builder for {@link TranscodingTransformer} instances. */ public static final class Builder { + // Mandatory field. private @MonotonicNonNull Context context; + + // Optional fields. private @MonotonicNonNull MediaSourceFactory mediaSourceFactory; private Muxer.Factory muxerFactory; private boolean removeAudio; private boolean removeVideo; private boolean flattenForSlowMotion; + private int outputHeight; private String outputMimeType; @Nullable private String audioMimeType; @Nullable private String videoMimeType; @@ -121,6 +126,7 @@ public final class TranscodingTransformer { this.removeAudio = transcodingTransformer.transformation.removeAudio; this.removeVideo = transcodingTransformer.transformation.removeVideo; this.flattenForSlowMotion = transcodingTransformer.transformation.flattenForSlowMotion; + this.outputHeight = transcodingTransformer.transformation.outputHeight; this.outputMimeType = transcodingTransformer.transformation.outputMimeType; this.audioMimeType = transcodingTransformer.transformation.audioMimeType; this.videoMimeType = transcodingTransformer.transformation.videoMimeType; @@ -213,6 +219,21 @@ public final class TranscodingTransformer { return this; } + /** + * Sets the output resolution for the video, using the output height. The default value is to + * use the same height as the input. Output width will scale to preserve the input video's + * aspect ratio. + * + *

For example, a 1920x1440 video can be scaled to 640x480 by calling setResolution(480). + * + * @param outputHeight The output height for the video, in pixels. + * @return This builder. + */ + public Builder setResolution(int outputHeight) { + this.outputHeight = outputHeight; + return this; + } + /** * Sets the MIME type of the output. The default value is {@link MimeTypes#VIDEO_MP4}. Supported * values are: @@ -356,6 +377,12 @@ public final class TranscodingTransformer { checkState( muxerFactory.supportsOutputMimeType(outputMimeType), "Unsupported output MIME type: " + outputMimeType); + // TODO(ME): Test with values of 10, 100, 1000). + Log.e("TranscodingTransformer", "outputHeight = " + outputHeight); + if (outputHeight == 0) { + // TODO(ME): get output height from input video. + outputHeight = 480; + } if (audioMimeType != null) { checkSampleMimeType(audioMimeType); } @@ -367,6 +394,7 @@ public final class TranscodingTransformer { removeAudio, removeVideo, flattenForSlowMotion, + outputHeight, outputMimeType, audioMimeType, videoMimeType); @@ -453,6 +481,7 @@ public final class TranscodingTransformer { checkState( !transformation.removeAudio || !transformation.removeVideo, "Audio and video cannot both be removed."); + checkState(!(transformation.removeVideo)); this.context = context; this.mediaSourceFactory = mediaSourceFactory; this.muxerFactory = muxerFactory; diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformation.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformation.java index e273c1fde5..ed8bf64bf3 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformation.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformation.java @@ -24,6 +24,7 @@ import androidx.annotation.Nullable; public final boolean removeAudio; public final boolean removeVideo; public final boolean flattenForSlowMotion; + public final int outputHeight; public final String outputMimeType; @Nullable public final String audioMimeType; @Nullable public final String videoMimeType; @@ -32,12 +33,14 @@ import androidx.annotation.Nullable; boolean removeAudio, boolean removeVideo, boolean flattenForSlowMotion, + int outputHeight, String outputMimeType, @Nullable String audioMimeType, @Nullable String videoMimeType) { this.removeAudio = removeAudio; this.removeVideo = removeVideo; this.flattenForSlowMotion = flattenForSlowMotion; + this.outputHeight = outputHeight; this.outputMimeType = outputMimeType; this.audioMimeType = audioMimeType; this.videoMimeType = videoMimeType; diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java index 0fd763ad26..093f63caeb 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java @@ -297,11 +297,13 @@ public final class Transformer { checkState( muxerFactory.supportsOutputMimeType(outputMimeType), "Unsupported output MIME type: " + outputMimeType); + int outputHeight = 0; // TODO(ME): How do we get the input height here? Transformation transformation = new Transformation( removeAudio, removeVideo, flattenForSlowMotion, + outputHeight, outputMimeType, /* audioMimeType= */ null, /* videoMimeType= */ null);