Merge branch 'dev-v2' of https://github.com/google/ExoPlayer into dev-v2

This commit is contained in:
tonihei 2021-11-01 10:54:02 +00:00
commit e77fa14eb0
26 changed files with 795 additions and 592 deletions

View File

@ -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.

View File

@ -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 ###

View File

@ -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.

View File

@ -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}

View File

@ -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<Listener> 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(),

View File

@ -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 =

View File

@ -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')
}

View File

@ -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(

View File

@ -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<Listener> listeners = new HashSet<>();
public FakePlayer(Context context) {
super(context);
}
@Override
public void addListener(Listener listener) {
listeners.add(listener);

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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))

View File

@ -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<RenderersFactory> renderersFactorySupplier;
/* package */ Supplier<MediaSourceFactory> mediaSourceFactorySupplier;
/* package */ Supplier<TrackSelector> trackSelectorSupplier;
/* package */ Supplier<LoadControl> loadControlSupplier;
/* package */ Supplier<BandwidthMeter> bandwidthMeterSupplier;
/* package */ Supplier<AnalyticsCollector> 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<RenderersFactory> renderersFactorySupplier,
Supplier<MediaSourceFactory> mediaSourceFactorySupplier) {
this(
context,
renderersFactorySupplier,
mediaSourceFactorySupplier,
() -> new DefaultTrackSelector(context),
DefaultLoadControl::new,
() -> DefaultBandwidthMeter.getSingletonInstance(context),
/* analyticsCollectorSupplier= */ null);
}
private Builder(
Context context,
Supplier<RenderersFactory> renderersFactorySupplier,
Supplier<MediaSourceFactory> mediaSourceFactorySupplier,
Supplier<TrackSelector> trackSelectorSupplier,
Supplier<LoadControl> loadControlSupplier,
Supplier<BandwidthMeter> bandwidthMeterSupplier,
@Nullable Supplier<AnalyticsCollector> 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;
}

View File

@ -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();

View File

@ -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;
}
}

View File

@ -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();
}

View File

@ -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++) {

View File

@ -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<Long, Long> 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<Long, Long> 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 {

View File

@ -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<Long, Long> skipToData(ExtractorInput input) throws IOException {
public static Pair<Long, Long> 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);

View File

@ -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;
@ -56,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;
@ -92,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;
@ -122,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;
@ -214,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.
*
* <p>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:
@ -357,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);
}
@ -368,6 +394,7 @@ public final class TranscodingTransformer {
removeAudio,
removeVideo,
flattenForSlowMotion,
outputHeight,
outputMimeType,
audioMimeType,
videoMimeType);
@ -454,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;
@ -573,8 +601,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 +715,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 +753,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 +763,7 @@ public final class TranscodingTransformer {
}
@Override
public void onPlayerError(EventTime eventTime, PlaybackException error) {
public void onPlayerError(PlaybackException error) {
handleTransformationEnded(error);
}

View File

@ -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;

View File

@ -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;
@ -298,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);
@ -496,7 +497,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 +607,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 +645,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 +655,7 @@ public final class Transformer {
}
@Override
public void onPlayerError(EventTime eventTime, PlaybackException error) {
public void onPlayerError(PlaybackException error) {
handleTransformationEnded(error);
}

View File

@ -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<FakeMetadataEntry> CREATOR =
new Parcelable.Creator<FakeMetadataEntry>() {
@Override
public FakeMetadataEntry createFromParcel(Parcel in) {
return new FakeMetadataEntry(in);
}
@Override
public FakeMetadataEntry[] newArray(int size) {
return new FakeMetadataEntry[size];
}
};
}

View File

@ -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<MediaItem> mediaItems, boolean resetPosition) {
throw new UnsupportedOperationException();
}
@Override
public void setMediaItems(List<MediaItem> 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<MediaItem> 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<Cue> 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();

View File

@ -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<MediaItem> mediaItems, boolean resetPosition) {
throw new UnsupportedOperationException();
}
@Override
public void setMediaItems(List<MediaItem> mediaItems, int startIndex, long startPositionMs) {
throw new UnsupportedOperationException();
}
@Override
public void addMediaItems(int index, List<MediaItem> 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<Cue> 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();
}
}