Commit e77fa14e by tonihei

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

parents 1648b7d0 c5392432
Showing with 794 additions and 210 deletions
......@@ -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.
......
......@@ -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 ###
......
......@@ -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.
......
......@@ -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}
......
......@@ -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(),
......
......@@ -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 =
......
......@@ -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')
}
......
......@@ -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(
......
......@@ -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);
......
......@@ -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);
......
......@@ -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);
}
}
......@@ -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);
}
......
......@@ -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))
......
......@@ -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;
}
......
......@@ -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();
......
......@@ -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;
}
}
......
......@@ -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();
}
......
......@@ -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++) {
......
......@@ -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 {
......
......@@ -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);
......
......@@ -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;
......@@ -215,6 +220,21 @@ public final class TranscodingTransformer {
}
/**
* 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);
}
......
......@@ -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;
......
......@@ -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);
}
......
/*
* 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];
}
};
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment