The player instance that will play the loaded ads must be set before playback using {@link
+ * #setPlayer(Player)}. If the ads loader is no longer required, it must be released by calling
+ * {@link #release()}.
+ *
+ * The IMA SDK can take into account video control overlay views when calculating ad viewability.
+ * For more details see {@link AdDisplayContainer#registerVideoControlsOverlay(View)} and {@link
+ * AdViewProvider#getAdOverlayViews()}.
+ */
public final class ImaAdsLoader
implements Player.EventListener,
AdsLoader,
@@ -92,9 +102,9 @@ public final class ImaAdsLoader
private final Context context;
- private @Nullable ImaSdkSettings imaSdkSettings;
- private @Nullable AdEventListener adEventListener;
- private @Nullable Set adUiElements;
+ @Nullable private ImaSdkSettings imaSdkSettings;
+ @Nullable private AdEventListener adEventListener;
+ @Nullable private Set adUiElements;
private int vastLoadTimeoutMs;
private int mediaLoadTimeoutMs;
private int mediaBitrate;
@@ -316,10 +326,11 @@ public final class ImaAdsLoader
private final AdDisplayContainer adDisplayContainer;
private final com.google.ads.interactivemedia.v3.api.AdsLoader adsLoader;
+ @Nullable private Player nextPlayer;
private Object pendingAdRequestContext;
private List supportedMimeTypes;
- private EventListener eventListener;
- private Player player;
+ @Nullable private EventListener eventListener;
+ @Nullable private Player player;
private VideoProgressUpdate lastContentProgress;
private VideoProgressUpdate lastAdProgress;
private int lastVolumePercentage;
@@ -459,11 +470,11 @@ public final class ImaAdsLoader
}
imaSdkSettings.setPlayerType(IMA_SDK_SETTINGS_PLAYER_TYPE);
imaSdkSettings.setPlayerVersion(IMA_SDK_SETTINGS_PLAYER_VERSION);
- adsLoader = imaFactory.createAdsLoader(context, imaSdkSettings);
period = new Timeline.Period();
adCallbacks = new ArrayList<>(/* initialCapacity= */ 1);
adDisplayContainer = imaFactory.createAdDisplayContainer();
adDisplayContainer.setPlayer(/* videoAdPlayer= */ this);
+ adsLoader = imaFactory.createAdsLoader(context, imaSdkSettings, adDisplayContainer);
adsLoader.addAdErrorListener(/* adErrorListener= */ this);
adsLoader.addAdsLoadedListener(/* adsLoadedListener= */ this);
fakeContentProgressElapsedRealtimeMs = C.TIME_UNSET;
@@ -481,13 +492,29 @@ public final class ImaAdsLoader
return adsLoader;
}
+ /**
+ * Returns the {@link AdDisplayContainer} used by this loader.
+ *
+ * Note: any video controls overlays registered via {@link
+ * AdDisplayContainer#registerVideoControlsOverlay(View)} will be unregistered automatically when
+ * the media source detaches from this instance. It is therefore necessary to re-register views
+ * each time the ads loader is reused. Alternatively, provide overlay views via the {@link
+ * AdsLoader.AdViewProvider} when creating the media source to benefit from automatic
+ * registration.
+ */
+ public AdDisplayContainer getAdDisplayContainer() {
+ return adDisplayContainer;
+ }
+
/**
* Sets the slots for displaying companion ads. Individual slots can be created using {@link
* ImaSdkFactory#createCompanionAdSlot()}.
*
* @param companionSlots Slots for displaying companion ads.
* @see AdDisplayContainer#setCompanionSlots(Collection)
+ * @deprecated Use {@code getAdDisplayContainer().setCompanionSlots(...)}.
*/
+ @Deprecated
public void setCompanionSlots(Collection companionSlots) {
adDisplayContainer.setCompanionSlots(companionSlots);
}
@@ -499,14 +526,14 @@ public final class ImaAdsLoader
* called, so it is only necessary to call this method if you want to request ads before preparing
* the player.
*
- * @param adUiViewGroup A {@link ViewGroup} on top of the player that will show any ad UI.
+ * @param adViewGroup A {@link ViewGroup} on top of the player that will show any ad UI.
*/
- public void requestAds(ViewGroup adUiViewGroup) {
+ public void requestAds(ViewGroup adViewGroup) {
if (adPlaybackState != null || adsManager != null || pendingAdRequestContext != null) {
// Ads have already been requested.
return;
}
- adDisplayContainer.setAdContainer(adUiViewGroup);
+ adDisplayContainer.setAdContainer(adViewGroup);
pendingAdRequestContext = new Object();
AdsRequest request = imaFactory.createAdsRequest();
if (adTagUri != null) {
@@ -517,7 +544,6 @@ public final class ImaAdsLoader
if (vastLoadTimeoutMs != TIMEOUT_UNSET) {
request.setVastLoadTimeout(vastLoadTimeoutMs);
}
- request.setAdDisplayContainer(adDisplayContainer);
request.setContentProgressProvider(this);
request.setUserRequestContext(pendingAdRequestContext);
adsLoader.requestAds(request);
@@ -525,6 +551,14 @@ public final class ImaAdsLoader
// AdsLoader implementation.
+ @Override
+ public void setPlayer(@Nullable Player player) {
+ Assertions.checkState(Looper.getMainLooper() == Looper.myLooper());
+ Assertions.checkState(
+ player == null || player.getApplicationLooper() == Looper.getMainLooper());
+ nextPlayer = player;
+ }
+
@Override
public void setSupportedContentTypes(@C.ContentType int... contentTypes) {
List supportedMimeTypes = new ArrayList<>();
@@ -549,14 +583,20 @@ public final class ImaAdsLoader
}
@Override
- public void attachPlayer(ExoPlayer player, EventListener eventListener, ViewGroup adUiViewGroup) {
- Assertions.checkArgument(player.getApplicationLooper() == Looper.getMainLooper());
- this.player = player;
+ public void start(EventListener eventListener, AdViewProvider adViewProvider) {
+ Assertions.checkNotNull(
+ nextPlayer, "Set player using adsLoader.setPlayer before preparing the player.");
+ player = nextPlayer;
this.eventListener = eventListener;
lastVolumePercentage = 0;
lastAdProgress = null;
lastContentProgress = null;
- adDisplayContainer.setAdContainer(adUiViewGroup);
+ ViewGroup adViewGroup = adViewProvider.getAdViewGroup();
+ adDisplayContainer.setAdContainer(adViewGroup);
+ View[] adOverlayViews = adViewProvider.getAdOverlayViews();
+ for (View view : adOverlayViews) {
+ adDisplayContainer.registerVideoControlsOverlay(view);
+ }
player.addListener(this);
maybeNotifyPendingAdLoadError();
if (adPlaybackState != null) {
@@ -570,12 +610,12 @@ public final class ImaAdsLoader
startAdPlayback();
} else {
// Ads haven't loaded yet, so request them.
- requestAds(adUiViewGroup);
+ requestAds(adViewGroup);
}
}
@Override
- public void detachPlayer() {
+ public void stop() {
if (adsManager != null && imaPausedContent) {
adPlaybackState =
adPlaybackState.withAdResumePositionUs(
@@ -585,6 +625,7 @@ public final class ImaAdsLoader
lastVolumePercentage = getVolume();
lastAdProgress = getAdProgress();
lastContentProgress = getContentProgress();
+ adDisplayContainer.unregisterAllVideoControlsOverlays();
player.removeListener(this);
player = null;
eventListener = null;
@@ -1331,7 +1372,8 @@ public final class ImaAdsLoader
private static boolean isAdGroupLoadError(AdError adError) {
// TODO: Find out what other errors need to be handled (if any), and whether each one relates to
// a single ad, ad group or the whole timeline.
- return adError.getErrorCode() == AdErrorCode.VAST_LINEAR_ASSET_MISMATCH;
+ return adError.getErrorCode() == AdErrorCode.VAST_LINEAR_ASSET_MISMATCH
+ || adError.getErrorCode() == AdErrorCode.UNKNOWN_ERROR;
}
private static boolean hasMidrollAdGroups(long[] adGroupTimesUs) {
@@ -1357,9 +1399,9 @@ public final class ImaAdsLoader
AdDisplayContainer createAdDisplayContainer();
/** @see com.google.ads.interactivemedia.v3.api.ImaSdkFactory#createAdsRequest() */
AdsRequest createAdsRequest();
- /** @see ImaSdkFactory#createAdsLoader(Context, ImaSdkSettings) */
+ /** @see ImaSdkFactory#createAdsLoader(Context, ImaSdkSettings, AdDisplayContainer) */
com.google.ads.interactivemedia.v3.api.AdsLoader createAdsLoader(
- Context context, ImaSdkSettings imaSdkSettings);
+ Context context, ImaSdkSettings imaSdkSettings, AdDisplayContainer adDisplayContainer);
}
/** Default {@link ImaFactory} for non-test usage, which delegates to {@link ImaSdkFactory}. */
@@ -1386,8 +1428,9 @@ public final class ImaAdsLoader
@Override
public com.google.ads.interactivemedia.v3.api.AdsLoader createAdsLoader(
- Context context, ImaSdkSettings imaSdkSettings) {
- return ImaSdkFactory.getInstance().createAdsLoader(context, imaSdkSettings);
+ Context context, ImaSdkSettings imaSdkSettings, AdDisplayContainer adDisplayContainer) {
+ return ImaSdkFactory.getInstance()
+ .createAdsLoader(context, imaSdkSettings, adDisplayContainer);
}
}
}
diff --git a/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsMediaSource.java b/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsMediaSource.java
deleted file mode 100644
index 0978ee401c..0000000000
--- a/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsMediaSource.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * 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.ext.ima;
-
-import android.os.Handler;
-import android.support.annotation.Nullable;
-import android.view.ViewGroup;
-import com.google.android.exoplayer2.ExoPlayer;
-import com.google.android.exoplayer2.Timeline;
-import com.google.android.exoplayer2.source.BaseMediaSource;
-import com.google.android.exoplayer2.source.MediaPeriod;
-import com.google.android.exoplayer2.source.MediaSource;
-import com.google.android.exoplayer2.source.MediaSource.SourceInfoRefreshListener;
-import com.google.android.exoplayer2.source.ads.AdsMediaSource;
-import com.google.android.exoplayer2.upstream.Allocator;
-import com.google.android.exoplayer2.upstream.DataSource;
-import com.google.android.exoplayer2.upstream.TransferListener;
-import java.io.IOException;
-
-/**
- * A {@link MediaSource} that inserts ads linearly with a provided content media source.
- *
- * @deprecated Use com.google.android.exoplayer2.source.ads.AdsMediaSource with ImaAdsLoader.
- */
-@Deprecated
-public final class ImaAdsMediaSource extends BaseMediaSource implements SourceInfoRefreshListener {
-
- private final AdsMediaSource adsMediaSource;
-
- /**
- * Constructs a new source that inserts ads linearly with the content specified by
- * {@code contentMediaSource}.
- *
- * @param contentMediaSource The {@link MediaSource} providing the content to play.
- * @param dataSourceFactory Factory for data sources used to load ad media.
- * @param imaAdsLoader The loader for ads.
- * @param adUiViewGroup A {@link ViewGroup} on top of the player that will show any ad UI.
- */
- public ImaAdsMediaSource(MediaSource contentMediaSource, DataSource.Factory dataSourceFactory,
- ImaAdsLoader imaAdsLoader, ViewGroup adUiViewGroup) {
- this(contentMediaSource, dataSourceFactory, imaAdsLoader, adUiViewGroup, null, null);
- }
-
- /**
- * Constructs a new source that inserts ads linearly with the content specified by {@code
- * contentMediaSource}.
- *
- * @param contentMediaSource The {@link MediaSource} providing the content to play.
- * @param dataSourceFactory Factory for data sources used to load ad media.
- * @param imaAdsLoader The loader for ads.
- * @param adUiViewGroup A {@link ViewGroup} on top of the player that will show any ad UI.
- * @param eventHandler A handler for events. May be null if delivery of events is not required.
- * @param eventListener A listener of events. May be null if delivery of events is not required.
- */
- public ImaAdsMediaSource(
- MediaSource contentMediaSource,
- DataSource.Factory dataSourceFactory,
- ImaAdsLoader imaAdsLoader,
- ViewGroup adUiViewGroup,
- @Nullable Handler eventHandler,
- @Nullable AdsMediaSource.EventListener eventListener) {
- adsMediaSource = new AdsMediaSource(contentMediaSource, dataSourceFactory, imaAdsLoader,
- adUiViewGroup, eventHandler, eventListener);
- }
-
- @Override
- @Nullable
- public Object getTag() {
- return adsMediaSource.getTag();
- }
-
- @Override
- public void prepareSourceInternal(
- final ExoPlayer player,
- boolean isTopLevelSource,
- @Nullable TransferListener mediaTransferListener) {
- adsMediaSource.prepareSource(
- player, isTopLevelSource, /* listener= */ this, mediaTransferListener);
- }
-
- @Override
- public void maybeThrowSourceInfoRefreshError() throws IOException {
- adsMediaSource.maybeThrowSourceInfoRefreshError();
- }
-
- @Override
- public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) {
- return adsMediaSource.createPeriod(id, allocator, startPositionUs);
- }
-
- @Override
- public void releasePeriod(MediaPeriod mediaPeriod) {
- adsMediaSource.releasePeriod(mediaPeriod);
- }
-
- @Override
- public void releaseSourceInternal() {
- adsMediaSource.releaseSource(/* listener= */ this);
- }
-
- @Override
- public void onSourceInfoRefreshed(
- MediaSource source, Timeline timeline, @Nullable Object manifest) {
- refreshSourceInfo(timeline, manifest);
- }
-}
diff --git a/extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/FakePlayer.java b/extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/FakePlayer.java
index b8024d6534..d20ccbd728 100644
--- a/extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/FakePlayer.java
+++ b/extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/FakePlayer.java
@@ -20,6 +20,7 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.testutil.StubExoPlayer;
+import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import java.util.ArrayList;
/** A fake player for testing content/ad playback. */
@@ -109,6 +110,11 @@ import java.util.ArrayList;
// ExoPlayer methods. Other methods are unsupported.
+ @Override
+ public AudioComponent getAudioComponent() {
+ return null;
+ }
+
@Override
public Looper getApplicationLooper() {
return Looper.getMainLooper();
@@ -134,6 +140,16 @@ import java.util.ArrayList;
return playWhenReady;
}
+ @Override
+ public int getRendererCount() {
+ return 0;
+ }
+
+ @Override
+ public TrackSelectionArray getCurrentTrackSelections() {
+ return new TrackSelectionArray();
+ }
+
@Override
public Timeline getCurrentTimeline() {
return timeline;
diff --git a/extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoaderTest.java b/extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoaderTest.java
index b0fe731480..dabae2de4b 100644
--- a/extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoaderTest.java
+++ b/extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoaderTest.java
@@ -17,11 +17,13 @@ package com.google.android.exoplayer2.ext.ima;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.net.Uri;
import android.support.annotation.Nullable;
+import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.google.ads.interactivemedia.v3.api.Ad;
@@ -49,6 +51,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
@@ -73,7 +76,9 @@ public class ImaAdsLoaderTest {
private @Mock AdDisplayContainer adDisplayContainer;
private @Mock AdsManager adsManager;
private SingletonImaFactory testImaFactory;
- private ViewGroup adUiViewGroup;
+ private ViewGroup adViewGroup;
+ private View adOverlayView;
+ private AdsLoader.AdViewProvider adViewProvider;
private TestAdsLoaderListener adsLoaderListener;
private FakePlayer fakeExoPlayer;
private ImaAdsLoader imaAdsLoader;
@@ -90,7 +95,20 @@ public class ImaAdsLoaderTest {
adDisplayContainer,
fakeAdsRequest,
fakeAdsLoader);
- adUiViewGroup = new FrameLayout(RuntimeEnvironment.application);
+ adViewGroup = new FrameLayout(RuntimeEnvironment.application);
+ adOverlayView = new View(RuntimeEnvironment.application);
+ adViewProvider =
+ new AdsLoader.AdViewProvider() {
+ @Override
+ public ViewGroup getAdViewGroup() {
+ return adViewGroup;
+ }
+
+ @Override
+ public View[] getAdOverlayViews() {
+ return new View[] {adOverlayView};
+ }
+ };
}
@After
@@ -109,17 +127,18 @@ public class ImaAdsLoaderTest {
}
@Test
- public void testAttachPlayer_setsAdUiViewGroup() {
+ public void testStart_setsAdUiViewGroup() {
setupPlayback(CONTENT_TIMELINE, PREROLL_ADS_DURATIONS_US, PREROLL_CUE_POINTS_SECONDS);
- imaAdsLoader.attachPlayer(fakeExoPlayer, adsLoaderListener, adUiViewGroup);
+ imaAdsLoader.start(adsLoaderListener, adViewProvider);
- verify(adDisplayContainer, atLeastOnce()).setAdContainer(adUiViewGroup);
+ verify(adDisplayContainer, atLeastOnce()).setAdContainer(adViewGroup);
+ verify(adDisplayContainer, atLeastOnce()).registerVideoControlsOverlay(adOverlayView);
}
@Test
- public void testAttachPlayer_updatesAdPlaybackState() {
+ public void testStart_updatesAdPlaybackState() {
setupPlayback(CONTENT_TIMELINE, PREROLL_ADS_DURATIONS_US, PREROLL_CUE_POINTS_SECONDS);
- imaAdsLoader.attachPlayer(fakeExoPlayer, adsLoaderListener, adUiViewGroup);
+ imaAdsLoader.start(adsLoaderListener, adViewProvider);
assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
@@ -128,17 +147,17 @@ public class ImaAdsLoaderTest {
}
@Test
- public void testAttachAfterRelease() {
+ public void testStartAfterRelease() {
setupPlayback(CONTENT_TIMELINE, PREROLL_ADS_DURATIONS_US, PREROLL_CUE_POINTS_SECONDS);
imaAdsLoader.release();
- imaAdsLoader.attachPlayer(fakeExoPlayer, adsLoaderListener, adUiViewGroup);
+ imaAdsLoader.start(adsLoaderListener, adViewProvider);
}
@Test
- public void testAttachAndCallbacksAfterRelease() {
+ public void testStartAndCallbacksAfterRelease() {
setupPlayback(CONTENT_TIMELINE, PREROLL_ADS_DURATIONS_US, PREROLL_CUE_POINTS_SECONDS);
imaAdsLoader.release();
- imaAdsLoader.attachPlayer(fakeExoPlayer, adsLoaderListener, adUiViewGroup);
+ imaAdsLoader.start(adsLoaderListener, adViewProvider);
fakeExoPlayer.setPlayingContentPosition(/* position= */ 0);
fakeExoPlayer.setState(Player.STATE_READY, true);
@@ -146,7 +165,7 @@ public class ImaAdsLoaderTest {
// Note: we can't currently call getContentProgress/getAdProgress as a VerifyError is thrown
// when using Robolectric and accessing VideoProgressUpdate.VIDEO_TIME_NOT_READY, due to the IMA
// SDK being proguarded.
- imaAdsLoader.requestAds(adUiViewGroup);
+ imaAdsLoader.requestAds(adViewGroup);
imaAdsLoader.onAdEvent(getAdEvent(AdEventType.LOADED, UNSKIPPABLE_AD));
imaAdsLoader.loadAd(TEST_URI.toString());
imaAdsLoader.onAdEvent(getAdEvent(AdEventType.CONTENT_PAUSE_REQUESTED, UNSKIPPABLE_AD));
@@ -166,7 +185,7 @@ public class ImaAdsLoaderTest {
setupPlayback(CONTENT_TIMELINE, PREROLL_ADS_DURATIONS_US, PREROLL_CUE_POINTS_SECONDS);
// Load the preroll ad.
- imaAdsLoader.attachPlayer(fakeExoPlayer, adsLoaderListener, adUiViewGroup);
+ imaAdsLoader.start(adsLoaderListener, adViewProvider);
imaAdsLoader.onAdEvent(getAdEvent(AdEventType.LOADED, UNSKIPPABLE_AD));
imaAdsLoader.loadAd(TEST_URI.toString());
imaAdsLoader.onAdEvent(getAdEvent(AdEventType.CONTENT_PAUSE_REQUESTED, UNSKIPPABLE_AD));
@@ -201,6 +220,18 @@ public class ImaAdsLoaderTest {
.withAdResumePositionUs(/* adResumePositionUs= */ 0));
}
+ @Test
+ public void testStop_unregistersAllVideoControlOverlays() {
+ setupPlayback(CONTENT_TIMELINE, PREROLL_ADS_DURATIONS_US, PREROLL_CUE_POINTS_SECONDS);
+ imaAdsLoader.start(adsLoaderListener, adViewProvider);
+ imaAdsLoader.requestAds(adViewGroup);
+ imaAdsLoader.stop();
+
+ InOrder inOrder = inOrder(adDisplayContainer);
+ inOrder.verify(adDisplayContainer).registerVideoControlsOverlay(adOverlayView);
+ inOrder.verify(adDisplayContainer).unregisterAllVideoControlsOverlays();
+ }
+
private void setupPlayback(Timeline contentTimeline, long[][] adDurationsUs, Float[] cuePoints) {
fakeExoPlayer = new FakePlayer();
adsLoaderListener = new TestAdsLoaderListener(fakeExoPlayer, contentTimeline, adDurationsUs);
@@ -210,6 +241,7 @@ public class ImaAdsLoaderTest {
.setImaFactory(testImaFactory)
.setImaSdkSettings(imaSdkSettings)
.buildForAdTag(TEST_URI);
+ imaAdsLoader.setPlayer(fakeExoPlayer);
}
private static AdEvent getAdEvent(AdEventType adEventType, @Nullable Ad ad) {
diff --git a/extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/SingletonImaFactory.java b/extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/SingletonImaFactory.java
index dd46d8a68b..4efd8cf38c 100644
--- a/extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/SingletonImaFactory.java
+++ b/extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/SingletonImaFactory.java
@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.ext.ima;
import android.content.Context;
import com.google.ads.interactivemedia.v3.api.AdDisplayContainer;
+import com.google.ads.interactivemedia.v3.api.AdsLoader;
import com.google.ads.interactivemedia.v3.api.AdsRenderingSettings;
import com.google.ads.interactivemedia.v3.api.AdsRequest;
import com.google.ads.interactivemedia.v3.api.ImaSdkSettings;
@@ -64,8 +65,8 @@ final class SingletonImaFactory implements ImaAdsLoader.ImaFactory {
}
@Override
- public com.google.ads.interactivemedia.v3.api.AdsLoader createAdsLoader(
- Context context, ImaSdkSettings imaSdkSettings) {
+ public AdsLoader createAdsLoader(
+ Context context, ImaSdkSettings imaSdkSettings, AdDisplayContainer adDisplayContainer) {
return adsLoader;
}
}
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java
index 35fa85e467..de6e867514 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java
@@ -138,7 +138,6 @@ import java.util.concurrent.CopyOnWriteArraySet;
repeatMode,
shuffleModeEnabled,
eventHandler,
- this,
clock);
internalPlayerHandler = new Handler(internalPlayer.getPlaybackLooper());
}
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java
index 7f41719d1d..c31c6b75a5 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java
@@ -93,7 +93,6 @@ import java.util.Collections;
private final HandlerWrapper handler;
private final HandlerThread internalPlaybackThread;
private final Handler eventHandler;
- private final ExoPlayer player;
private final Timeline.Window window;
private final Timeline.Period period;
private final long backBufferDurationUs;
@@ -131,7 +130,6 @@ import java.util.Collections;
@Player.RepeatMode int repeatMode,
boolean shuffleModeEnabled,
Handler eventHandler,
- ExoPlayer player,
Clock clock) {
this.renderers = renderers;
this.trackSelector = trackSelector;
@@ -142,7 +140,6 @@ import java.util.Collections;
this.repeatMode = repeatMode;
this.shuffleModeEnabled = shuffleModeEnabled;
this.eventHandler = eventHandler;
- this.player = player;
this.clock = clock;
this.queue = new MediaPeriodQueue();
@@ -398,11 +395,7 @@ import java.util.Collections;
loadControl.onPrepared();
this.mediaSource = mediaSource;
setState(Player.STATE_BUFFERING);
- mediaSource.prepareSource(
- player,
- /* isTopLevelSource= */ true,
- /* listener= */ this,
- bandwidthMeter.getTransferListener());
+ mediaSource.prepareSource(/* listener= */ this, bandwidthMeter.getTransferListener());
handler.sendEmptyMessage(MSG_DO_SOME_WORK);
}
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java
index e3a2e1cd27..b389ec742f 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java
@@ -29,11 +29,11 @@ public final class ExoPlayerLibraryInfo {
/** The version of the library expressed as a string, for example "1.2.3". */
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION_INT) or vice versa.
- public static final String VERSION = "2.9.5";
+ public static final String VERSION = "2.9.6";
/** The version of the library expressed as {@code "ExoPlayerLib/" + VERSION}. */
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
- public static final String VERSION_SLASHY = "ExoPlayerLib/2.9.5";
+ public static final String VERSION_SLASHY = "ExoPlayerLib/2.9.6";
/**
* The version of the library expressed as an integer, for example 1002003.
@@ -43,7 +43,7 @@ public final class ExoPlayerLibraryInfo {
* integer version 123045006 (123-045-006).
*/
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
- public static final int VERSION_INT = 2009005;
+ public static final int VERSION_INT = 2009006;
/**
* Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions}
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java
index 0f1fd8f649..d7ed1c0c5b 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java
@@ -591,6 +591,14 @@ public final class FragmentedMp4Extractor implements Extractor {
long presentationTimeDeltaUs =
Util.scaleLargeTimestamp(atom.readUnsignedInt(), C.MICROS_PER_SECOND, timescale);
+ // The presentation_time_delta is accounted for by adjusting the sample timestamp, so we zero it
+ // in the sample data before writing it to the track outputs.
+ int position = atom.getPosition();
+ atom.data[position - 4] = 0;
+ atom.data[position - 3] = 0;
+ atom.data[position - 2] = 0;
+ atom.data[position - 1] = 0;
+
// Output the sample data.
for (TrackOutput emsgTrackOutput : emsgTrackOutputs) {
atom.setPosition(Atom.FULL_HEADER_SIZE);
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/metadata/emsg/EventMessage.java b/library/core/src/main/java/com/google/android/exoplayer2/metadata/emsg/EventMessage.java
index 7d70d9de1c..8a3467e2ed 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/metadata/emsg/EventMessage.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/metadata/emsg/EventMessage.java
@@ -44,13 +44,6 @@ public final class EventMessage implements Metadata.Entry {
*/
public final long durationMs;
- /**
- * The presentation time value of this event message in microseconds.
- *
- * Except in special cases, application code should not use this field.
- */
- public final long presentationTimeUs;
-
/**
* The instance identifier.
*/
@@ -70,22 +63,19 @@ public final class EventMessage implements Metadata.Entry {
* @param durationMs The duration of the event in milliseconds.
* @param id The instance identifier.
* @param messageData The body of the message.
- * @param presentationTimeUs The presentation time value of this event message in microseconds.
*/
- public EventMessage(String schemeIdUri, String value, long durationMs, long id,
- byte[] messageData, long presentationTimeUs) {
+ public EventMessage(
+ String schemeIdUri, String value, long durationMs, long id, byte[] messageData) {
this.schemeIdUri = schemeIdUri;
this.value = value;
this.durationMs = durationMs;
this.id = id;
this.messageData = messageData;
- this.presentationTimeUs = presentationTimeUs;
}
/* package */ EventMessage(Parcel in) {
schemeIdUri = castNonNull(in.readString());
value = castNonNull(in.readString());
- presentationTimeUs = in.readLong();
durationMs = in.readLong();
id = in.readLong();
messageData = castNonNull(in.createByteArray());
@@ -97,7 +87,6 @@ public final class EventMessage implements Metadata.Entry {
int result = 17;
result = 31 * result + (schemeIdUri != null ? schemeIdUri.hashCode() : 0);
result = 31 * result + (value != null ? value.hashCode() : 0);
- result = 31 * result + (int) (presentationTimeUs ^ (presentationTimeUs >>> 32));
result = 31 * result + (int) (durationMs ^ (durationMs >>> 32));
result = 31 * result + (int) (id ^ (id >>> 32));
result = 31 * result + Arrays.hashCode(messageData);
@@ -115,9 +104,11 @@ public final class EventMessage implements Metadata.Entry {
return false;
}
EventMessage other = (EventMessage) obj;
- return presentationTimeUs == other.presentationTimeUs && durationMs == other.durationMs
- && id == other.id && Util.areEqual(schemeIdUri, other.schemeIdUri)
- && Util.areEqual(value, other.value) && Arrays.equals(messageData, other.messageData);
+ return durationMs == other.durationMs
+ && id == other.id
+ && Util.areEqual(schemeIdUri, other.schemeIdUri)
+ && Util.areEqual(value, other.value)
+ && Arrays.equals(messageData, other.messageData);
}
@Override
@@ -136,7 +127,6 @@ public final class EventMessage implements Metadata.Entry {
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(schemeIdUri);
dest.writeString(value);
- dest.writeLong(presentationTimeUs);
dest.writeLong(durationMs);
dest.writeLong(id);
dest.writeByteArray(messageData);
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/metadata/emsg/EventMessageDecoder.java b/library/core/src/main/java/com/google/android/exoplayer2/metadata/emsg/EventMessageDecoder.java
index 14f678374c..33d79917eb 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/metadata/emsg/EventMessageDecoder.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/metadata/emsg/EventMessageDecoder.java
@@ -15,11 +15,11 @@
*/
package com.google.android.exoplayer2.metadata.emsg;
-import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.metadata.MetadataDecoder;
import com.google.android.exoplayer2.metadata.MetadataInputBuffer;
import com.google.android.exoplayer2.util.Assertions;
+import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.Util;
import java.nio.ByteBuffer;
@@ -27,12 +27,15 @@ import java.util.Arrays;
/**
* Decodes Event Message (emsg) atoms, as defined in ISO/IEC 23009-1:2014, Section 5.10.3.3.
- *
- * Atom data should be provided to the decoder without the full atom header (i.e. starting from the
- * first byte of the scheme_id_uri field).
+ *
+ *
Atom data should be provided to the decoder without the full atom header (i.e. starting from
+ * the first byte of the scheme_id_uri field). It is expected that the presentation_time_delta field
+ * should be 0, having already been accounted for by adjusting the sample timestamp.
*/
public final class EventMessageDecoder implements MetadataDecoder {
+ private static final String TAG = "EventMessageDecoder";
+
@SuppressWarnings("ByteBufferBackingArray")
@Override
public Metadata decode(MetadataInputBuffer inputBuffer) {
@@ -43,13 +46,16 @@ public final class EventMessageDecoder implements MetadataDecoder {
String schemeIdUri = Assertions.checkNotNull(emsgData.readNullTerminatedString());
String value = Assertions.checkNotNull(emsgData.readNullTerminatedString());
long timescale = emsgData.readUnsignedInt();
- long presentationTimeUs = Util.scaleLargeTimestamp(emsgData.readUnsignedInt(),
- C.MICROS_PER_SECOND, timescale);
+ long presentationTimeDelta = emsgData.readUnsignedInt();
+ if (presentationTimeDelta != 0) {
+ // We expect the source to have accounted for presentation_time_delta by adjusting the sample
+ // timestamp and zeroing the field in the sample data. Log a warning if the field is non-zero.
+ Log.w(TAG, "Ignoring non-zero presentation_time_delta: " + presentationTimeDelta);
+ }
long durationMs = Util.scaleLargeTimestamp(emsgData.readUnsignedInt(), 1000, timescale);
long id = emsgData.readUnsignedInt();
byte[] messageData = Arrays.copyOfRange(data, emsgData.getPosition(), size);
- return new Metadata(new EventMessage(schemeIdUri, value, durationMs, id, messageData,
- presentationTimeUs));
+ return new Metadata(new EventMessage(schemeIdUri, value, durationMs, id, messageData));
}
}
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/metadata/emsg/EventMessageEncoder.java b/library/core/src/main/java/com/google/android/exoplayer2/metadata/emsg/EventMessageEncoder.java
index eca498a6df..22708a8448 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/metadata/emsg/EventMessageEncoder.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/metadata/emsg/EventMessageEncoder.java
@@ -16,9 +16,6 @@
package com.google.android.exoplayer2.metadata.emsg;
import android.support.annotation.Nullable;
-import com.google.android.exoplayer2.C;
-import com.google.android.exoplayer2.util.Assertions;
-import com.google.android.exoplayer2.util.Util;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
@@ -37,27 +34,22 @@ public final class EventMessageEncoder {
}
/**
- * Encodes an {@link EventMessage} to a byte array that can be decoded by
- * {@link EventMessageDecoder}.
+ * Encodes an {@link EventMessage} to a byte array that can be decoded by {@link
+ * EventMessageDecoder}.
*
* @param eventMessage The event message to be encoded.
- * @param timescale Timescale of the event message, in units per second.
* @return The serialized byte array.
*/
@Nullable
- public byte[] encode(EventMessage eventMessage, long timescale) {
- Assertions.checkArgument(timescale >= 0);
+ public byte[] encode(EventMessage eventMessage) {
byteArrayOutputStream.reset();
try {
writeNullTerminatedString(dataOutputStream, eventMessage.schemeIdUri);
String nonNullValue = eventMessage.value != null ? eventMessage.value : "";
writeNullTerminatedString(dataOutputStream, nonNullValue);
- writeUnsignedInt(dataOutputStream, timescale);
- long presentationTime = Util.scaleLargeTimestamp(eventMessage.presentationTimeUs,
- timescale, C.MICROS_PER_SECOND);
- writeUnsignedInt(dataOutputStream, presentationTime);
- long duration = Util.scaleLargeTimestamp(eventMessage.durationMs, timescale, 1000);
- writeUnsignedInt(dataOutputStream, duration);
+ writeUnsignedInt(dataOutputStream, 1000); // timescale
+ writeUnsignedInt(dataOutputStream, 0); // presentation_time_delta
+ writeUnsignedInt(dataOutputStream, eventMessage.durationMs);
writeUnsignedInt(dataOutputStream, eventMessage.id);
dataOutputStream.write(eventMessage.messageData);
dataOutputStream.flush();
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/BaseMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/BaseMediaSource.java
index 2feac2978e..189467b47e 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/source/BaseMediaSource.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/source/BaseMediaSource.java
@@ -16,8 +16,8 @@
package com.google.android.exoplayer2.source;
import android.os.Handler;
+import android.os.Looper;
import android.support.annotation.Nullable;
-import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.util.Assertions;
@@ -35,9 +35,9 @@ public abstract class BaseMediaSource implements MediaSource {
private final ArrayList sourceInfoListeners;
private final MediaSourceEventListener.EventDispatcher eventDispatcher;
- private @Nullable ExoPlayer player;
- private @Nullable Timeline timeline;
- private @Nullable Object manifest;
+ @Nullable private Looper looper;
+ @Nullable private Timeline timeline;
+ @Nullable private Object manifest;
public BaseMediaSource() {
sourceInfoListeners = new ArrayList<>(/* initialCapacity= */ 1);
@@ -48,21 +48,16 @@ public abstract class BaseMediaSource implements MediaSource {
* Starts source preparation. This method is called at most once until the next call to {@link
* #releaseSourceInternal()}.
*
- * @param player The player for which this source is being prepared.
- * @param isTopLevelSource Whether this source has been passed directly to {@link
- * ExoPlayer#prepare(MediaSource)} or {@link ExoPlayer#prepare(MediaSource, boolean,
- * boolean)}.
* @param mediaTransferListener The transfer listener which should be informed of any media data
* transfers. May be null if no listener is available. Note that this listener should usually
* be only informed of transfers related to the media loads and not of auxiliary loads for
* manifests and other data.
*/
- protected abstract void prepareSourceInternal(
- ExoPlayer player, boolean isTopLevelSource, @Nullable TransferListener mediaTransferListener);
+ protected abstract void prepareSourceInternal(@Nullable TransferListener mediaTransferListener);
/**
* Releases the source. This method is called exactly once after each call to {@link
- * #prepareSourceInternal(ExoPlayer, boolean, TransferListener)}.
+ * #prepareSourceInternal(TransferListener)}.
*/
protected abstract void releaseSourceInternal();
@@ -135,21 +130,14 @@ public abstract class BaseMediaSource implements MediaSource {
@Override
public final void prepareSource(
- ExoPlayer player, boolean isTopLevelSource, SourceInfoRefreshListener listener) {
- prepareSource(player, isTopLevelSource, listener, /* mediaTransferListener= */ null);
- }
-
- @Override
- public final void prepareSource(
- ExoPlayer player,
- boolean isTopLevelSource,
SourceInfoRefreshListener listener,
@Nullable TransferListener mediaTransferListener) {
- Assertions.checkArgument(this.player == null || this.player == player);
+ Looper looper = Looper.myLooper();
+ Assertions.checkArgument(this.looper == null || this.looper == looper);
sourceInfoListeners.add(listener);
- if (this.player == null) {
- this.player = player;
- prepareSourceInternal(player, isTopLevelSource, mediaTransferListener);
+ if (this.looper == null) {
+ this.looper = looper;
+ prepareSourceInternal(mediaTransferListener);
} else if (timeline != null) {
listener.onSourceInfoRefreshed(/* source= */ this, timeline, manifest);
}
@@ -159,7 +147,7 @@ public abstract class BaseMediaSource implements MediaSource {
public final void releaseSource(SourceInfoRefreshListener listener) {
sourceInfoListeners.remove(listener);
if (sourceInfoListeners.isEmpty()) {
- player = null;
+ looper = null;
timeline = null;
manifest = null;
releaseSourceInternal();
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ClippingMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ClippingMediaSource.java
index fce1c4b877..d5399dc02d 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/source/ClippingMediaSource.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ClippingMediaSource.java
@@ -18,7 +18,6 @@ package com.google.android.exoplayer2.source;
import android.support.annotation.IntDef;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.C;
-import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.TransferListener;
@@ -223,11 +222,8 @@ public final class ClippingMediaSource extends CompositeMediaSource {
}
@Override
- public void prepareSourceInternal(
- ExoPlayer player,
- boolean isTopLevelSource,
- @Nullable TransferListener mediaTransferListener) {
- super.prepareSourceInternal(player, isTopLevelSource, mediaTransferListener);
+ public void prepareSourceInternal(@Nullable TransferListener mediaTransferListener) {
+ super.prepareSourceInternal(mediaTransferListener);
prepareChildSource(/* id= */ null, mediaSource);
}
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/CompositeMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/CompositeMediaSource.java
index 69fa4b094b..dbf5812f98 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/source/CompositeMediaSource.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/source/CompositeMediaSource.java
@@ -18,7 +18,6 @@ package com.google.android.exoplayer2.source;
import android.os.Handler;
import android.support.annotation.CallSuper;
import android.support.annotation.Nullable;
-import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.util.Assertions;
@@ -35,7 +34,6 @@ public abstract class CompositeMediaSource extends BaseMediaSource {
private final HashMap childSources;
- private @Nullable ExoPlayer player;
private @Nullable Handler eventHandler;
private @Nullable TransferListener mediaTransferListener;
@@ -46,11 +44,7 @@ public abstract class CompositeMediaSource extends BaseMediaSource {
@Override
@CallSuper
- public void prepareSourceInternal(
- ExoPlayer player,
- boolean isTopLevelSource,
- @Nullable TransferListener mediaTransferListener) {
- this.player = player;
+ public void prepareSourceInternal(@Nullable TransferListener mediaTransferListener) {
this.mediaTransferListener = mediaTransferListener;
eventHandler = new Handler();
}
@@ -71,7 +65,6 @@ public abstract class CompositeMediaSource extends BaseMediaSource {
childSource.mediaSource.removeEventListener(childSource.eventListener);
}
childSources.clear();
- player = null;
}
/**
@@ -105,11 +98,7 @@ public abstract class CompositeMediaSource extends BaseMediaSource {
MediaSourceEventListener eventListener = new ForwardingEventListener(id);
childSources.put(id, new MediaSourceAndListener(mediaSource, sourceListener, eventListener));
mediaSource.addEventListener(Assertions.checkNotNull(eventHandler), eventListener);
- mediaSource.prepareSource(
- Assertions.checkNotNull(player),
- /* isTopLevelSource= */ false,
- sourceListener,
- mediaTransferListener);
+ mediaSource.prepareSource(sourceListener, mediaTransferListener);
}
/**
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java
index 7baea9979f..961aaf105f 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java
@@ -22,7 +22,6 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Pair;
import com.google.android.exoplayer2.C;
-import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.ConcatenatingMediaSource.MediaSourceHolder;
import com.google.android.exoplayer2.source.ShuffleOrder.DefaultShuffleOrder;
@@ -428,10 +427,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource {
}
@Override
- public void prepareSourceInternal(
- ExoPlayer player,
- boolean isTopLevelSource,
- @Nullable TransferListener mediaTransferListener) {
- super.prepareSourceInternal(player, isTopLevelSource, mediaTransferListener);
+ public void prepareSourceInternal(@Nullable TransferListener mediaTransferListener) {
+ super.prepareSourceInternal(mediaTransferListener);
prepareChildSource(/* id= */ null, childSource);
}
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/MediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/MediaSource.java
index 801737faef..14f9a26245 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/source/MediaSource.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/source/MediaSource.java
@@ -25,23 +25,24 @@ import com.google.android.exoplayer2.upstream.TransferListener;
import java.io.IOException;
/**
- * Defines and provides media to be played by an {@link ExoPlayer}. A MediaSource has two main
- * responsibilities:
+ * Defines and provides media to be played by an {@link com.google.android.exoplayer2.ExoPlayer}. A
+ * MediaSource has two main responsibilities:
*
*
* - To provide the player with a {@link Timeline} defining the structure of its media, and to
* provide a new timeline whenever the structure of the media changes. The MediaSource
* provides these timelines by calling {@link SourceInfoRefreshListener#onSourceInfoRefreshed}
- * on the {@link SourceInfoRefreshListener}s passed to {@link #prepareSource(ExoPlayer,
- * boolean, SourceInfoRefreshListener, TransferListener)}.
+ * on the {@link SourceInfoRefreshListener}s passed to {@link
+ * #prepareSource(SourceInfoRefreshListener, TransferListener)}.
*
- To provide {@link MediaPeriod} instances for the periods in its timeline. MediaPeriods are
* obtained by calling {@link #createPeriod(MediaPeriodId, Allocator, long)}, and provide a
* way for the player to load and read the media.
*
*
* All methods are called on the player's internal playback thread, as described in the {@link
- * ExoPlayer} Javadoc. They should not be called directly from application code. Instances can be
- * re-used, but only for one {@link ExoPlayer} instance simultaneously.
+ * com.google.android.exoplayer2.ExoPlayer} Javadoc. They should not be called directly from
+ * application code. Instances can be re-used, but only for one {@link
+ * com.google.android.exoplayer2.ExoPlayer} instance simultaneously.
*/
public interface MediaSource {
@@ -226,11 +227,6 @@ public interface MediaSource {
return null;
}
- /** @deprecated Will be removed in the next release. */
- @Deprecated
- void prepareSource(
- ExoPlayer player, boolean isTopLevelSource, SourceInfoRefreshListener listener);
-
/**
* Starts source preparation if not yet started, and adds a listener for timeline and/or manifest
* updates.
@@ -242,11 +238,6 @@ public interface MediaSource {
* For each call to this method, a call to {@link #releaseSource(SourceInfoRefreshListener)} is
* needed to remove the listener and to release the source if no longer required.
*
- * @param player The player for which this source is being prepared.
- * @param isTopLevelSource Whether this source has been passed directly to {@link
- * ExoPlayer#prepare(MediaSource)} or {@link ExoPlayer#prepare(MediaSource, boolean,
- * boolean)}. If {@code false}, this source is being prepared by another source (e.g. {@link
- * ConcatenatingMediaSource}) for composition.
* @param listener The listener to be added.
* @param mediaTransferListener The transfer listener which should be informed of any media data
* transfers. May be null if no listener is available. Note that this listener should be only
@@ -254,10 +245,7 @@ public interface MediaSource {
* and other data.
*/
void prepareSource(
- ExoPlayer player,
- boolean isTopLevelSource,
- SourceInfoRefreshListener listener,
- @Nullable TransferListener mediaTransferListener);
+ SourceInfoRefreshListener listener, @Nullable TransferListener mediaTransferListener);
/**
* Throws any pending error encountered while loading or refreshing source information.
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/MergingMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/MergingMediaSource.java
index cc7202f9b2..1ea3404e81 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/source/MergingMediaSource.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/source/MergingMediaSource.java
@@ -17,7 +17,6 @@ package com.google.android.exoplayer2.source;
import android.support.annotation.IntDef;
import android.support.annotation.Nullable;
-import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.TransferListener;
@@ -105,11 +104,8 @@ public final class MergingMediaSource extends CompositeMediaSource {
}
@Override
- public void prepareSourceInternal(
- ExoPlayer player,
- boolean isTopLevelSource,
- @Nullable TransferListener mediaTransferListener) {
- super.prepareSourceInternal(player, isTopLevelSource, mediaTransferListener);
+ public void prepareSourceInternal(@Nullable TransferListener mediaTransferListener) {
+ super.prepareSourceInternal(mediaTransferListener);
for (int i = 0; i < mediaSources.length; i++) {
prepareChildSource(i, mediaSources[i]);
}
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java
index 046672bb77..c028a9e339 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java
@@ -18,7 +18,6 @@ package com.google.android.exoplayer2.source;
import android.net.Uri;
import android.os.Handler;
import android.support.annotation.Nullable;
-import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.upstream.Allocator;
@@ -304,10 +303,7 @@ public final class SingleSampleMediaSource extends BaseMediaSource {
}
@Override
- public void prepareSourceInternal(
- ExoPlayer player,
- boolean isTopLevelSource,
- @Nullable TransferListener mediaTransferListener) {
+ public void prepareSourceInternal(@Nullable TransferListener mediaTransferListener) {
transferListener = mediaTransferListener;
refreshSourceInfo(timeline, /* manifest= */ null);
}
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdsLoader.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdsLoader.java
index f041542356..b3054f69a0 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdsLoader.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdsLoader.java
@@ -15,9 +15,11 @@
*/
package com.google.android.exoplayer2.source.ads;
+import android.support.annotation.Nullable;
+import android.view.View;
import android.view.ViewGroup;
import com.google.android.exoplayer2.C;
-import com.google.android.exoplayer2.ExoPlayer;
+import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.source.ads.AdsMediaSource.AdLoadException;
import com.google.android.exoplayer2.upstream.DataSpec;
import java.io.IOException;
@@ -25,27 +27,25 @@ import java.io.IOException;
/**
* Interface for loaders of ads, which can be used with {@link AdsMediaSource}.
*
- * Ad loaders notify the {@link AdsMediaSource} about events via {@link EventListener}. In
+ *
Ads loaders notify the {@link AdsMediaSource} about events via {@link EventListener}. In
* particular, implementations must call {@link EventListener#onAdPlaybackState(AdPlaybackState)}
* with a new copy of the current {@link AdPlaybackState} whenever further information about ads
* becomes known (for example, when an ad media URI is available, or an ad has played to the end).
*
- *
{@link #attachPlayer(ExoPlayer, EventListener, ViewGroup)} will be called when the ads media
- * source first initializes, at which point the loader can request ads. If the player enters the
- * background, {@link #detachPlayer()} will be called. Loaders should maintain any ad playback state
- * in preparation for a later call to {@link #attachPlayer(ExoPlayer, EventListener, ViewGroup)}. If
- * an ad is playing when the player is detached, update the ad playback state with the current
- * playback position using {@link AdPlaybackState#withAdResumePositionUs(long)}.
+ *
{@link #start(EventListener, AdViewProvider)} will be called when the ads media source first
+ * initializes, at which point the loader can request ads. If the player enters the background,
+ * {@link #stop()} will be called. Loaders should maintain any ad playback state in preparation for
+ * a later call to {@link #start(EventListener, AdViewProvider)}. If an ad is playing when the
+ * player is detached, update the ad playback state with the current playback position using {@link
+ * AdPlaybackState#withAdResumePositionUs(long)}.
*
*
If {@link EventListener#onAdPlaybackState(AdPlaybackState)} has been called, the
- * implementation of {@link #attachPlayer(ExoPlayer, EventListener, ViewGroup)} should invoke the
- * same listener to provide the existing playback state to the new player.
+ * implementation of {@link #start(EventListener, AdViewProvider)} should invoke the same listener
+ * to provide the existing playback state to the new player.
*/
public interface AdsLoader {
- /**
- * Listener for ad loader events. All methods are called on the main thread.
- */
+ /** Listener for ads loader events. All methods are called on the main thread. */
interface EventListener {
/**
@@ -53,7 +53,7 @@ public interface AdsLoader {
*
* @param adPlaybackState The new ad playback state.
*/
- void onAdPlaybackState(AdPlaybackState adPlaybackState);
+ default void onAdPlaybackState(AdPlaybackState adPlaybackState) {}
/**
* Called when there was an error loading ads.
@@ -61,23 +61,62 @@ public interface AdsLoader {
* @param error The error.
* @param dataSpec The data spec associated with the load error.
*/
- void onAdLoadError(AdLoadException error, DataSpec dataSpec);
+ default void onAdLoadError(AdLoadException error, DataSpec dataSpec) {}
- /**
- * Called when the user clicks through an ad (for example, following a 'learn more' link).
- */
- void onAdClicked();
-
- /**
- * Called when the user taps a non-clickthrough part of an ad.
- */
- void onAdTapped();
+ /** Called when the user clicks through an ad (for example, following a 'learn more' link). */
+ default void onAdClicked() {}
+ /** Called when the user taps a non-clickthrough part of an ad. */
+ default void onAdTapped() {}
}
+ /** Provides views for the ad UI. */
+ interface AdViewProvider {
+
+ /** Returns the {@link ViewGroup} on top of the player that will show any ad UI. */
+ ViewGroup getAdViewGroup();
+
+ /**
+ * Returns an array of views that are shown on top of the ad view group, but that are essential
+ * for controlling playback and should be excluded from ad viewability measurements by the
+ * {@link AdsLoader} (if it supports this).
+ *
+ *
Each view must be either a fully transparent overlay (for capturing touch events), or a
+ * small piece of transient UI that is essential to the user experience of playback (such as a
+ * button to pause/resume playback or a transient full-screen or cast button). For more
+ * information see the documentation for your ads loader.
+ */
+ View[] getAdOverlayViews();
+ }
+
+ // Methods called by the application.
+
/**
- * Sets the supported content types for ad media. Must be called before the first call to
- * {@link #attachPlayer(ExoPlayer, EventListener, ViewGroup)}. Subsequent calls may be ignored.
+ * Sets the player that will play the loaded ads.
+ *
+ *
This method must be called before the player is prepared with media using this ads loader.
+ *
+ *
This method must also be called on the main thread and only players which are accessed on
+ * the main thread are supported ({@code player.getApplicationLooper() ==
+ * Looper.getMainLooper()}).
+ *
+ * @param player The player instance that will play the loaded ads. May be null to delete the
+ * reference to a previously set player.
+ */
+ void setPlayer(@Nullable Player player);
+
+ /**
+ * Releases the loader. Must be called by the application on the main thread when the instance is
+ * no longer needed.
+ */
+ void release();
+
+ // Methods called by AdsMediaSource.
+
+ /**
+ * Sets the supported content types for ad media. Must be called before the first call to {@link
+ * #start(EventListener, AdViewProvider)}. Subsequent calls may be ignored. Called on the main
+ * thread by {@link AdsMediaSource}.
*
* @param contentTypes The supported content types for ad media. Each element must be one of
* {@link C#TYPE_DASH}, {@link C#TYPE_HLS}, {@link C#TYPE_SS} and {@link C#TYPE_OTHER}.
@@ -85,32 +124,23 @@ public interface AdsLoader {
void setSupportedContentTypes(@C.ContentType int... contentTypes);
/**
- * Attaches a player that will play ads loaded using this instance. Called on the main thread by
- * {@link AdsMediaSource}.
+ * Starts using the ads loader for playback. Called on the main thread by {@link AdsMediaSource}.
*
- * @param player The player instance that will play the loaded ads. Only players which are
- * accessed on the main thread are supported ({@code player.getApplicationLooper() ==
- * Looper.getMainLooper()}).
* @param eventListener Listener for ads loader events.
- * @param adUiViewGroup A {@link ViewGroup} on top of the player that will show any ad UI.
+ * @param adViewProvider Provider of views for the ad UI.
*/
- void attachPlayer(ExoPlayer player, EventListener eventListener, ViewGroup adUiViewGroup);
+ void start(EventListener eventListener, AdViewProvider adViewProvider);
/**
- * Detaches the attached player and event listener. Called on the main thread by
- * {@link AdsMediaSource}.
+ * Stops using the ads loader for playback and deregisters the event listener. Called on the main
+ * thread by {@link AdsMediaSource}.
*/
- void detachPlayer();
-
- /**
- * Releases the loader. Called by the application on the main thread when the instance is no
- * longer needed.
- */
- void release();
+ void stop();
/**
* Notifies the ads loader that the player was not able to prepare media for a given ad.
* Implementations should update the ad playback state as the specified ad has failed to load.
+ * Called on the main thread by {@link AdsMediaSource}.
*
* @param adGroupIndex The index of the ad group.
* @param adIndexInAdGroup The index of the ad in the ad group.
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdsMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdsMediaSource.java
index 4bf661ddc0..c57ad6a223 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdsMediaSource.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdsMediaSource.java
@@ -20,9 +20,7 @@ import android.os.Handler;
import android.os.Looper;
import android.support.annotation.IntDef;
import android.support.annotation.Nullable;
-import android.view.ViewGroup;
import com.google.android.exoplayer2.C;
-import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.CompositeMediaSource;
import com.google.android.exoplayer2.source.DeferredMediaPeriod;
@@ -140,46 +138,6 @@ public final class AdsMediaSource extends CompositeMediaSource {
}
}
- /**
- * Listener for ads media source events.
- *
- * @deprecated To listen for ad load error events, add a listener via {@link
- * #addEventListener(Handler, MediaSourceEventListener)} and check for {@link
- * AdLoadException}s in {@link MediaSourceEventListener#onLoadError(int, MediaPeriodId,
- * LoadEventInfo, MediaLoadData, IOException, boolean)}. Individual ads loader implementations
- * should expose ad interaction events, if applicable.
- */
- @Deprecated
- public interface EventListener {
-
- /**
- * Called if there was an error loading one or more ads. The loader will skip the problematic
- * ad(s).
- *
- * @param error The error.
- */
- void onAdLoadError(IOException error);
-
- /**
- * Called when an unexpected internal error is encountered while loading ads. The loader will
- * skip all remaining ads, as the error is not recoverable.
- *
- * @param error The error.
- */
- void onInternalAdLoadError(RuntimeException error);
-
- /**
- * Called when the user clicks through an ad (for example, following a 'learn more' link).
- */
- void onAdClicked();
-
- /**
- * Called when the user taps a non-clickthrough part of an ad.
- */
- void onAdTapped();
-
- }
-
// Used to identify the content "child" source for CompositeMediaSource.
private static final MediaPeriodId DUMMY_CONTENT_MEDIA_PERIOD_ID =
new MediaPeriodId(/* periodUid= */ new Object());
@@ -187,9 +145,7 @@ public final class AdsMediaSource extends CompositeMediaSource {
private final MediaSource contentMediaSource;
private final MediaSourceFactory adMediaSourceFactory;
private final AdsLoader adsLoader;
- private final ViewGroup adUiViewGroup;
- @Nullable private final Handler eventHandler;
- @Nullable private final EventListener eventListener;
+ private final AdsLoader.AdViewProvider adViewProvider;
private final Handler mainHandler;
private final Map> deferredMediaPeriodByAdMediaSource;
private final Timeline.Period period;
@@ -209,20 +165,18 @@ public final class AdsMediaSource extends CompositeMediaSource {
* @param contentMediaSource The {@link MediaSource} providing the content to play.
* @param dataSourceFactory Factory for data sources used to load ad media.
* @param adsLoader The loader for ads.
- * @param adUiViewGroup A {@link ViewGroup} on top of the player that will show any ad UI.
+ * @param adViewProvider Provider of views for the ad UI.
*/
public AdsMediaSource(
MediaSource contentMediaSource,
DataSource.Factory dataSourceFactory,
AdsLoader adsLoader,
- ViewGroup adUiViewGroup) {
+ AdsLoader.AdViewProvider adViewProvider) {
this(
contentMediaSource,
new ExtractorMediaSource.Factory(dataSourceFactory),
adsLoader,
- adUiViewGroup,
- /* eventHandler= */ null,
- /* eventListener= */ null);
+ adViewProvider);
}
/**
@@ -232,85 +186,17 @@ public final class AdsMediaSource extends CompositeMediaSource {
* @param contentMediaSource The {@link MediaSource} providing the content to play.
* @param adMediaSourceFactory Factory for media sources used to load ad media.
* @param adsLoader The loader for ads.
- * @param adUiViewGroup A {@link ViewGroup} on top of the player that will show any ad UI.
+ * @param adViewProvider Provider of views for the ad UI.
*/
public AdsMediaSource(
MediaSource contentMediaSource,
MediaSourceFactory adMediaSourceFactory,
AdsLoader adsLoader,
- ViewGroup adUiViewGroup) {
- this(
- contentMediaSource,
- adMediaSourceFactory,
- adsLoader,
- adUiViewGroup,
- /* eventHandler= */ null,
- /* eventListener= */ null);
- }
-
- /**
- * Constructs a new source that inserts ads linearly with the content specified by {@code
- * contentMediaSource}. Ad media is loaded using {@link ExtractorMediaSource}.
- *
- * @param contentMediaSource The {@link MediaSource} providing the content to play.
- * @param dataSourceFactory Factory for data sources used to load ad media.
- * @param adsLoader The loader for ads.
- * @param adUiViewGroup A {@link ViewGroup} on top of the player that will show any ad UI.
- * @param eventHandler A handler for events. May be null if delivery of events is not required.
- * @param eventListener A listener of events. May be null if delivery of events is not required.
- * @deprecated To listen for ad load error events, add a listener via {@link
- * #addEventListener(Handler, MediaSourceEventListener)} and check for {@link
- * AdLoadException}s in {@link MediaSourceEventListener#onLoadError(int, MediaPeriodId,
- * LoadEventInfo, MediaLoadData, IOException, boolean)}. Individual ads loader implementations
- * should expose ad interaction events, if applicable.
- */
- @Deprecated
- public AdsMediaSource(
- MediaSource contentMediaSource,
- DataSource.Factory dataSourceFactory,
- AdsLoader adsLoader,
- ViewGroup adUiViewGroup,
- @Nullable Handler eventHandler,
- @Nullable EventListener eventListener) {
- this(
- contentMediaSource,
- new ExtractorMediaSource.Factory(dataSourceFactory),
- adsLoader,
- adUiViewGroup,
- eventHandler,
- eventListener);
- }
-
- /**
- * Constructs a new source that inserts ads linearly with the content specified by {@code
- * contentMediaSource}.
- *
- * @param contentMediaSource The {@link MediaSource} providing the content to play.
- * @param adMediaSourceFactory Factory for media sources used to load ad media.
- * @param adsLoader The loader for ads.
- * @param adUiViewGroup A {@link ViewGroup} on top of the player that will show any ad UI.
- * @param eventHandler A handler for events. May be null if delivery of events is not required.
- * @param eventListener A listener of events. May be null if delivery of events is not required.
- * @deprecated To listen for ad load error events, add a listener via {@link
- * #addEventListener(Handler, MediaSourceEventListener)} and check for {@link
- * AdLoadException}s in {@link MediaSourceEventListener#onLoadError(int, MediaPeriodId,
- * LoadEventInfo, MediaLoadData, IOException, boolean)}. Individual ads loader implementations
- * should expose ad interaction events, if applicable.
- */
- @Deprecated
- public AdsMediaSource(
- MediaSource contentMediaSource,
- MediaSourceFactory adMediaSourceFactory,
- AdsLoader adsLoader,
- ViewGroup adUiViewGroup,
- @Nullable Handler eventHandler,
- @Nullable EventListener eventListener) {
+ AdsLoader.AdViewProvider adViewProvider) {
this.contentMediaSource = contentMediaSource;
this.adMediaSourceFactory = adMediaSourceFactory;
this.adsLoader = adsLoader;
- this.adUiViewGroup = adUiViewGroup;
- this.eventHandler = eventHandler;
- this.eventListener = eventListener;
+ this.adViewProvider = adViewProvider;
mainHandler = new Handler(Looper.getMainLooper());
deferredMediaPeriodByAdMediaSource = new HashMap<>();
period = new Timeline.Period();
@@ -326,18 +212,12 @@ public final class AdsMediaSource extends CompositeMediaSource {
}
@Override
- public void prepareSourceInternal(
- final ExoPlayer player,
- boolean isTopLevelSource,
- @Nullable TransferListener mediaTransferListener) {
- super.prepareSourceInternal(player, isTopLevelSource, mediaTransferListener);
- Assertions.checkArgument(
- isTopLevelSource,
- "AdsMediaSource must be the top-level source used to prepare the player.");
- final ComponentListener componentListener = new ComponentListener();
+ public void prepareSourceInternal(@Nullable TransferListener mediaTransferListener) {
+ super.prepareSourceInternal(mediaTransferListener);
+ ComponentListener componentListener = new ComponentListener();
this.componentListener = componentListener;
prepareChildSource(DUMMY_CONTENT_MEDIA_PERIOD_ID, contentMediaSource);
- mainHandler.post(() -> adsLoader.attachPlayer(player, componentListener, adUiViewGroup));
+ mainHandler.post(() -> adsLoader.start(componentListener, adViewProvider));
}
@Override
@@ -406,7 +286,7 @@ public final class AdsMediaSource extends CompositeMediaSource {
adPlaybackState = null;
adGroupMediaSources = new MediaSource[0][];
adGroupTimelines = new Timeline[0][];
- mainHandler.post(adsLoader::detachPlayer);
+ mainHandler.post(adsLoader::stop);
}
@Override
@@ -446,6 +326,7 @@ public final class AdsMediaSource extends CompositeMediaSource {
}
private void onContentSourceInfoRefreshed(Timeline timeline, Object manifest) {
+ Assertions.checkArgument(timeline.getPeriodCount() == 1);
contentTimeline = timeline;
contentManifest = manifest;
maybeUpdateSourceInfo();
@@ -528,36 +409,6 @@ public final class AdsMediaSource extends CompositeMediaSource {
});
}
- @Override
- public void onAdClicked() {
- if (released) {
- return;
- }
- if (eventHandler != null && eventListener != null) {
- eventHandler.post(
- () -> {
- if (!released) {
- eventListener.onAdClicked();
- }
- });
- }
- }
-
- @Override
- public void onAdTapped() {
- if (released) {
- return;
- }
- if (eventHandler != null && eventListener != null) {
- eventHandler.post(
- () -> {
- if (!released) {
- eventListener.onAdTapped();
- }
- });
- }
- }
-
@Override
public void onAdLoadError(final AdLoadException error, DataSpec dataSpec) {
if (released) {
@@ -574,18 +425,6 @@ public final class AdsMediaSource extends CompositeMediaSource {
/* bytesLoaded= */ 0,
error,
/* wasCanceled= */ true);
- if (eventHandler != null && eventListener != null) {
- eventHandler.post(
- () -> {
- if (!released) {
- if (error.type == AdLoadException.TYPE_UNEXPECTED) {
- eventListener.onInternalAdLoadError(error.getRuntimeExceptionForUnexpected());
- } else {
- eventListener.onAdLoadError(error);
- }
- }
- });
- }
}
}
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java
index 5b35ee946a..3e262a567d 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java
@@ -1087,10 +1087,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
throws DecoderQueryException {
int maxWidth = format.width;
int maxHeight = format.height;
- if (codecNeedsMaxVideoSizeResetWorkaround(codecInfo.name)) {
- maxWidth = Math.max(maxWidth, 1920);
- maxHeight = Math.max(maxHeight, 1089);
- }
int maxInputSize = getMaxInputSize(codecInfo, format);
if (streamFormats.length == 1) {
// The single entry in streamFormats must correspond to the format for which the codec is
@@ -1278,18 +1274,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
return "NVIDIA".equals(Util.MANUFACTURER);
}
- /**
- * Returns whether the codec is known to have problems with the configuration for interlaced
- * content and needs minimum values for the maximum video size to force reset the configuration.
- *
- * See https://github.com/google/ExoPlayer/issues/5003.
- *
- * @param name The name of the codec.
- */
- private static boolean codecNeedsMaxVideoSizeResetWorkaround(String name) {
- return "OMX.amlogic.avc.decoder.awesome".equals(name) && Util.SDK_INT <= 25;
- }
-
/*
* TODO:
*
diff --git a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java
index d131ed0f51..fd9100338c 100644
--- a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java
+++ b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java
@@ -267,10 +267,8 @@ public final class ExoPlayerTest {
new FakeMediaSource(timeline, new Object(), Builder.VIDEO_FORMAT) {
@Override
public synchronized void prepareSourceInternal(
- ExoPlayer player,
- boolean isTopLevelSource,
@Nullable TransferListener mediaTransferListener) {
- super.prepareSourceInternal(player, isTopLevelSource, mediaTransferListener);
+ super.prepareSourceInternal(mediaTransferListener);
// We've queued a source info refresh on the playback thread's event queue. Allow the
// test thread to prepare the player with the third source, and block this thread (the
// playback thread) until the test thread's call to prepare() has returned.
diff --git a/library/core/src/test/java/com/google/android/exoplayer2/metadata/emsg/EventMessageDecoderTest.java b/library/core/src/test/java/com/google/android/exoplayer2/metadata/emsg/EventMessageDecoderTest.java
index c6558e3fc9..85d336b439 100644
--- a/library/core/src/test/java/com/google/android/exoplayer2/metadata/emsg/EventMessageDecoderTest.java
+++ b/library/core/src/test/java/com/google/android/exoplayer2/metadata/emsg/EventMessageDecoderTest.java
@@ -51,7 +51,6 @@ public final class EventMessageDecoderTest {
assertThat(eventMessage.durationMs).isEqualTo(3000);
assertThat(eventMessage.id).isEqualTo(1000403);
assertThat(eventMessage.messageData).isEqualTo(new byte[]{0, 1, 2, 3, 4});
- assertThat(eventMessage.presentationTimeUs).isEqualTo(1000000);
}
}
diff --git a/library/core/src/test/java/com/google/android/exoplayer2/metadata/emsg/EventMessageEncoderTest.java b/library/core/src/test/java/com/google/android/exoplayer2/metadata/emsg/EventMessageEncoderTest.java
index 7195548fbf..2869692272 100644
--- a/library/core/src/test/java/com/google/android/exoplayer2/metadata/emsg/EventMessageEncoderTest.java
+++ b/library/core/src/test/java/com/google/android/exoplayer2/metadata/emsg/EventMessageEncoderTest.java
@@ -33,25 +33,27 @@ public final class EventMessageEncoderTest {
@Test
public void testEncodeEventStream() throws IOException {
- EventMessage eventMessage = new EventMessage("urn:test", "123", 3000, 1000403,
- new byte[] {0, 1, 2, 3, 4}, 1000000);
- byte[] expectedEmsgBody = new byte[] {
- 117, 114, 110, 58, 116, 101, 115, 116, 0, // scheme_id_uri = "urn:test"
- 49, 50, 51, 0, // value = "123"
- 0, 0, -69, -128, // timescale = 48000
- 0, 0, -69, -128, // presentation_time_delta = 48000
- 0, 2, 50, -128, // event_duration = 144000
- 0, 15, 67, -45, // id = 1000403
- 0, 1, 2, 3, 4}; // message_data = {0, 1, 2, 3, 4}
- byte[] encodedByteArray = new EventMessageEncoder().encode(eventMessage, 48000);
+ EventMessage eventMessage =
+ new EventMessage("urn:test", "123", 3000, 1000403, new byte[] {0, 1, 2, 3, 4});
+ byte[] expectedEmsgBody =
+ new byte[] {
+ 117, 114, 110, 58, 116, 101, 115, 116, 0, // scheme_id_uri = "urn:test"
+ 49, 50, 51, 0, // value = "123"
+ 0, 0, 3, -24, // timescale = 1000
+ 0, 0, 0, 0, // presentation_time_delta = 0
+ 0, 0, 11, -72, // event_duration = 3000
+ 0, 15, 67, -45, // id = 1000403
+ 0, 1, 2, 3, 4
+ }; // message_data = {0, 1, 2, 3, 4}
+ byte[] encodedByteArray = new EventMessageEncoder().encode(eventMessage);
assertThat(encodedByteArray).isEqualTo(expectedEmsgBody);
}
@Test
public void testEncodeDecodeEventStream() throws IOException {
- EventMessage expectedEmsg = new EventMessage("urn:test", "123", 3000, 1000403,
- new byte[] {0, 1, 2, 3, 4}, 1000000);
- byte[] encodedByteArray = new EventMessageEncoder().encode(expectedEmsg, 48000);
+ EventMessage expectedEmsg =
+ new EventMessage("urn:test", "123", 3000, 1000403, new byte[] {0, 1, 2, 3, 4});
+ byte[] encodedByteArray = new EventMessageEncoder().encode(expectedEmsg);
MetadataInputBuffer buffer = new MetadataInputBuffer();
buffer.data = ByteBuffer.allocate(encodedByteArray.length).put(encodedByteArray);
@@ -63,30 +65,34 @@ public final class EventMessageEncoderTest {
@Test
public void testEncodeEventStreamMultipleTimesWorkingCorrectly() throws IOException {
- EventMessage eventMessage = new EventMessage("urn:test", "123", 3000, 1000403,
- new byte[] {0, 1, 2, 3, 4}, 1000000);
- byte[] expectedEmsgBody = new byte[] {
- 117, 114, 110, 58, 116, 101, 115, 116, 0, // scheme_id_uri = "urn:test"
- 49, 50, 51, 0, // value = "123"
- 0, 0, -69, -128, // timescale = 48000
- 0, 0, -69, -128, // presentation_time_delta = 48000
- 0, 2, 50, -128, // event_duration = 144000
- 0, 15, 67, -45, // id = 1000403
- 0, 1, 2, 3, 4}; // message_data = {0, 1, 2, 3, 4}
- EventMessage eventMessage1 = new EventMessage("urn:test", "123", 3000, 1000402,
- new byte[] {4, 3, 2, 1, 0}, 1000000);
- byte[] expectedEmsgBody1 = new byte[] {
- 117, 114, 110, 58, 116, 101, 115, 116, 0, // scheme_id_uri = "urn:test"
- 49, 50, 51, 0, // value = "123"
- 0, 0, -69, -128, // timescale = 48000
- 0, 0, -69, -128, // presentation_time_delta = 48000
- 0, 2, 50, -128, // event_duration = 144000
- 0, 15, 67, -46, // id = 1000402
- 4, 3, 2, 1, 0}; // message_data = {4, 3, 2, 1, 0}
+ EventMessage eventMessage =
+ new EventMessage("urn:test", "123", 3000, 1000403, new byte[] {0, 1, 2, 3, 4});
+ byte[] expectedEmsgBody =
+ new byte[] {
+ 117, 114, 110, 58, 116, 101, 115, 116, 0, // scheme_id_uri = "urn:test"
+ 49, 50, 51, 0, // value = "123"
+ 0, 0, 3, -24, // timescale = 1000
+ 0, 0, 0, 0, // presentation_time_delta = 0
+ 0, 0, 11, -72, // event_duration = 3000
+ 0, 15, 67, -45, // id = 1000403
+ 0, 1, 2, 3, 4
+ }; // message_data = {0, 1, 2, 3, 4}
+ EventMessage eventMessage1 =
+ new EventMessage("urn:test", "123", 3000, 1000402, new byte[] {4, 3, 2, 1, 0});
+ byte[] expectedEmsgBody1 =
+ new byte[] {
+ 117, 114, 110, 58, 116, 101, 115, 116, 0, // scheme_id_uri = "urn:test"
+ 49, 50, 51, 0, // value = "123"
+ 0, 0, 3, -24, // timescale = 1000
+ 0, 0, 0, 0, // presentation_time_delta = 0
+ 0, 0, 11, -72, // event_duration = 3000
+ 0, 15, 67, -46, // id = 1000402
+ 4, 3, 2, 1, 0
+ }; // message_data = {4, 3, 2, 1, 0}
EventMessageEncoder eventMessageEncoder = new EventMessageEncoder();
- byte[] encodedByteArray = eventMessageEncoder.encode(eventMessage, 48000);
+ byte[] encodedByteArray = eventMessageEncoder.encode(eventMessage);
assertThat(encodedByteArray).isEqualTo(expectedEmsgBody);
- byte[] encodedByteArray1 = eventMessageEncoder.encode(eventMessage1, 48000);
+ byte[] encodedByteArray1 = eventMessageEncoder.encode(eventMessage1);
assertThat(encodedByteArray1).isEqualTo(expectedEmsgBody1);
}
diff --git a/library/core/src/test/java/com/google/android/exoplayer2/metadata/emsg/EventMessageTest.java b/library/core/src/test/java/com/google/android/exoplayer2/metadata/emsg/EventMessageTest.java
index 30e1cd6c1f..f7970d1a16 100644
--- a/library/core/src/test/java/com/google/android/exoplayer2/metadata/emsg/EventMessageTest.java
+++ b/library/core/src/test/java/com/google/android/exoplayer2/metadata/emsg/EventMessageTest.java
@@ -30,8 +30,8 @@ public final class EventMessageTest {
@Test
public void testEventMessageParcelable() {
- EventMessage eventMessage = new EventMessage("urn:test", "123", 3000, 1000403,
- new byte[] {0, 1, 2, 3, 4}, 1000);
+ EventMessage eventMessage =
+ new EventMessage("urn:test", "123", 3000, 1000403, new byte[] {0, 1, 2, 3, 4});
// Write to parcel.
Parcel parcel = Parcel.obtain();
eventMessage.writeToParcel(parcel, 0);
diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java
index c65bfceb39..8b503989b7 100644
--- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java
+++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java
@@ -22,7 +22,6 @@ import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.SparseArray;
import com.google.android.exoplayer2.C;
-import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.Timeline;
@@ -614,10 +613,7 @@ public final class DashMediaSource extends BaseMediaSource {
}
@Override
- public void prepareSourceInternal(
- ExoPlayer player,
- boolean isTopLevelSource,
- @Nullable TransferListener mediaTransferListener) {
+ public void prepareSourceInternal(@Nullable TransferListener mediaTransferListener) {
this.mediaTransferListener = mediaTransferListener;
if (sideloadedManifest) {
processManifest(false);
diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/EventSampleStream.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/EventSampleStream.java
index 9f812b8e84..f06a709960 100644
--- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/EventSampleStream.java
+++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/EventSampleStream.java
@@ -112,8 +112,7 @@ import java.io.IOException;
}
}
int sampleIndex = currentIndex++;
- byte[] serializedEvent = eventMessageEncoder.encode(eventStream.events[sampleIndex],
- eventStream.timescale);
+ byte[] serializedEvent = eventMessageEncoder.encode(eventStream.events[sampleIndex]);
if (serializedEvent != null) {
buffer.ensureSpaceForWrite(serializedEvent.length);
buffer.setFlags(C.BUFFER_FLAG_KEY_FRAME);
diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java
index f017ae64ad..f34127273d 100644
--- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java
+++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java
@@ -834,13 +834,13 @@ public class DashManifestParser extends DefaultHandler
String schemeIdUri = parseString(xpp, "schemeIdUri", "");
String value = parseString(xpp, "value", "");
long timescale = parseLong(xpp, "timescale", 1);
- List eventMessages = new ArrayList<>();
+ List> eventMessages = new ArrayList<>();
ByteArrayOutputStream scratchOutputStream = new ByteArrayOutputStream(512);
do {
xpp.next();
if (XmlPullParserUtil.isStartTag(xpp, "Event")) {
- EventMessage event = parseEvent(xpp, schemeIdUri, value, timescale,
- scratchOutputStream);
+ Pair event =
+ parseEvent(xpp, schemeIdUri, value, timescale, scratchOutputStream);
eventMessages.add(event);
} else {
maybeSkipTag(xpp);
@@ -850,9 +850,9 @@ public class DashManifestParser extends DefaultHandler
long[] presentationTimesUs = new long[eventMessages.size()];
EventMessage[] events = new EventMessage[eventMessages.size()];
for (int i = 0; i < eventMessages.size(); i++) {
- EventMessage event = eventMessages.get(i);
- presentationTimesUs[i] = event.presentationTimeUs;
- events[i] = event;
+ Pair event = eventMessages.get(i);
+ presentationTimesUs[i] = event.first;
+ events[i] = event.second;
}
return buildEventStream(schemeIdUri, value, timescale, presentationTimesUs, events);
}
@@ -871,11 +871,12 @@ public class DashManifestParser extends DefaultHandler
* @param timescale The timescale of the parent EventStream.
* @param scratchOutputStream A {@link ByteArrayOutputStream} that is used when parsing event
* objects.
- * @return The {@link EventMessage} parsed from this EventStream node.
+ * @return A pair containing the node's presentation timestamp in microseconds and the parsed
+ * {@link EventMessage}.
* @throws XmlPullParserException If there is any error parsing this node.
* @throws IOException If there is any error reading from the underlying input stream.
*/
- protected EventMessage parseEvent(
+ protected Pair parseEvent(
XmlPullParser xpp,
String schemeIdUri,
String value,
@@ -890,13 +891,14 @@ public class DashManifestParser extends DefaultHandler
timescale);
String messageData = parseString(xpp, "messageData", null);
byte[] eventObject = parseEventObject(xpp, scratchOutputStream);
- return buildEvent(
- schemeIdUri,
- value,
- id,
- durationMs,
- messageData == null ? eventObject : Util.getUtf8Bytes(messageData),
- presentationTimesUs);
+ return Pair.create(
+ presentationTimesUs,
+ buildEvent(
+ schemeIdUri,
+ value,
+ id,
+ durationMs,
+ messageData == null ? eventObject : Util.getUtf8Bytes(messageData)));
}
/**
@@ -963,9 +965,9 @@ public class DashManifestParser extends DefaultHandler
return scratchOutputStream.toByteArray();
}
- protected EventMessage buildEvent(String schemeIdUri, String value, long id,
- long durationMs, byte[] messageData, long presentationTimeUs) {
- return new EventMessage(schemeIdUri, value, durationMs, id, messageData, presentationTimeUs);
+ protected EventMessage buildEvent(
+ String schemeIdUri, String value, long id, long durationMs, byte[] messageData) {
+ return new EventMessage(schemeIdUri, value, durationMs, id, messageData);
}
protected List parseSegmentTimeline(XmlPullParser xpp)
diff --git a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/EventSampleStreamTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/EventSampleStreamTest.java
index 9c3752551a..9621381eed 100644
--- a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/EventSampleStreamTest.java
+++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/EventSampleStreamTest.java
@@ -111,7 +111,7 @@ public final class EventSampleStreamTest {
@Test
public void testReadDataReturnDataAfterFormat() {
long presentationTimeUs = 1000000;
- EventMessage eventMessage = newEventMessageWithIdAndTime(1, presentationTimeUs);
+ EventMessage eventMessage = newEventMessageWithId(1);
EventStream eventStream = new EventStream(SCHEME_ID, VALUE, TIME_SCALE,
new long[] {presentationTimeUs}, new EventMessage[] {eventMessage});
EventSampleStream sampleStream = new EventSampleStream(eventStream, FORMAT, false);
@@ -133,8 +133,8 @@ public final class EventSampleStreamTest {
public void testSkipDataThenReadDataReturnDataFromSkippedPosition() {
long presentationTimeUs1 = 1000000;
long presentationTimeUs2 = 2000000;
- EventMessage eventMessage1 = newEventMessageWithIdAndTime(1, presentationTimeUs1);
- EventMessage eventMessage2 = newEventMessageWithIdAndTime(2, presentationTimeUs2);
+ EventMessage eventMessage1 = newEventMessageWithId(1);
+ EventMessage eventMessage2 = newEventMessageWithId(2);
EventStream eventStream = new EventStream(SCHEME_ID, VALUE, TIME_SCALE,
new long[] {presentationTimeUs1, presentationTimeUs2},
new EventMessage[] {eventMessage1, eventMessage2});
@@ -159,8 +159,8 @@ public final class EventSampleStreamTest {
public void testSeekToUsThenReadDataReturnDataFromSeekPosition() {
long presentationTimeUs1 = 1000000;
long presentationTimeUs2 = 2000000;
- EventMessage eventMessage1 = newEventMessageWithIdAndTime(1, presentationTimeUs1);
- EventMessage eventMessage2 = newEventMessageWithIdAndTime(2, presentationTimeUs2);
+ EventMessage eventMessage1 = newEventMessageWithId(1);
+ EventMessage eventMessage2 = newEventMessageWithId(2);
EventStream eventStream = new EventStream(SCHEME_ID, VALUE, TIME_SCALE,
new long[] {presentationTimeUs1, presentationTimeUs2},
new EventMessage[] {eventMessage1, eventMessage2});
@@ -186,9 +186,9 @@ public final class EventSampleStreamTest {
long presentationTimeUs1 = 1000000;
long presentationTimeUs2 = 2000000;
long presentationTimeUs3 = 3000000;
- EventMessage eventMessage1 = newEventMessageWithIdAndTime(1, presentationTimeUs1);
- EventMessage eventMessage2 = newEventMessageWithIdAndTime(2, presentationTimeUs2);
- EventMessage eventMessage3 = newEventMessageWithIdAndTime(3, presentationTimeUs3);
+ EventMessage eventMessage1 = newEventMessageWithId(1);
+ EventMessage eventMessage2 = newEventMessageWithId(2);
+ EventMessage eventMessage3 = newEventMessageWithId(3);
EventStream eventStream1 = new EventStream(SCHEME_ID, VALUE, TIME_SCALE,
new long[] {presentationTimeUs1, presentationTimeUs2},
new EventMessage[] {eventMessage1, eventMessage2});
@@ -220,9 +220,9 @@ public final class EventSampleStreamTest {
long presentationTimeUs1 = 1000000;
long presentationTimeUs2 = 2000000;
long presentationTimeUs3 = 3000000;
- EventMessage eventMessage1 = newEventMessageWithIdAndTime(1, presentationTimeUs1);
- EventMessage eventMessage2 = newEventMessageWithIdAndTime(2, presentationTimeUs2);
- EventMessage eventMessage3 = newEventMessageWithIdAndTime(3, presentationTimeUs3);
+ EventMessage eventMessage1 = newEventMessageWithId(1);
+ EventMessage eventMessage2 = newEventMessageWithId(2);
+ EventMessage eventMessage3 = newEventMessageWithId(3);
EventStream eventStream1 = new EventStream(SCHEME_ID, VALUE, TIME_SCALE,
new long[] {presentationTimeUs1, presentationTimeUs2},
new EventMessage[] {eventMessage1, eventMessage2});
@@ -253,9 +253,9 @@ public final class EventSampleStreamTest {
long presentationTimeUs1 = 1000000;
long presentationTimeUs2 = 2000000;
long presentationTimeUs3 = 3000000;
- EventMessage eventMessage1 = newEventMessageWithIdAndTime(1, presentationTimeUs1);
- EventMessage eventMessage2 = newEventMessageWithIdAndTime(2, presentationTimeUs2);
- EventMessage eventMessage3 = newEventMessageWithIdAndTime(3, presentationTimeUs3);
+ EventMessage eventMessage1 = newEventMessageWithId(1);
+ EventMessage eventMessage2 = newEventMessageWithId(2);
+ EventMessage eventMessage3 = newEventMessageWithId(3);
EventStream eventStream1 = new EventStream(SCHEME_ID, VALUE, TIME_SCALE,
new long[] {presentationTimeUs1},
new EventMessage[] {eventMessage1});
@@ -287,9 +287,9 @@ public final class EventSampleStreamTest {
long presentationTimeUs1 = 1000000;
long presentationTimeUs2 = 2000000;
long presentationTimeUs3 = 3000000;
- EventMessage eventMessage1 = newEventMessageWithIdAndTime(1, presentationTimeUs1);
- EventMessage eventMessage2 = newEventMessageWithIdAndTime(2, presentationTimeUs2);
- EventMessage eventMessage3 = newEventMessageWithIdAndTime(3, presentationTimeUs3);
+ EventMessage eventMessage1 = newEventMessageWithId(1);
+ EventMessage eventMessage2 = newEventMessageWithId(2);
+ EventMessage eventMessage3 = newEventMessageWithId(3);
EventStream eventStream1 = new EventStream(SCHEME_ID, VALUE, TIME_SCALE,
new long[] {presentationTimeUs1, presentationTimeUs2},
new EventMessage[] {eventMessage1, eventMessage2});
@@ -319,9 +319,9 @@ public final class EventSampleStreamTest {
long presentationTimeUs1 = 1000000;
long presentationTimeUs2 = 2000000;
long presentationTimeUs3 = 3000000;
- EventMessage eventMessage1 = newEventMessageWithIdAndTime(1, presentationTimeUs1);
- EventMessage eventMessage2 = newEventMessageWithIdAndTime(2, presentationTimeUs2);
- EventMessage eventMessage3 = newEventMessageWithIdAndTime(3, presentationTimeUs3);
+ EventMessage eventMessage1 = newEventMessageWithId(1);
+ EventMessage eventMessage2 = newEventMessageWithId(2);
+ EventMessage eventMessage3 = newEventMessageWithId(3);
EventStream eventStream1 = new EventStream(SCHEME_ID, VALUE, TIME_SCALE,
new long[] {presentationTimeUs1},
new EventMessage[] {eventMessage1});
@@ -345,12 +345,12 @@ public final class EventSampleStreamTest {
return sampleStream.readData(formatHolder, inputBuffer, false);
}
- private EventMessage newEventMessageWithIdAndTime(int id, long presentationTimeUs) {
- return new EventMessage(SCHEME_ID, VALUE, DURATION_MS, id, MESSAGE_DATA, presentationTimeUs);
+ private EventMessage newEventMessageWithId(int id) {
+ return new EventMessage(SCHEME_ID, VALUE, DURATION_MS, id, MESSAGE_DATA);
}
private byte[] getEncodedMessage(EventMessage eventMessage) {
- return eventMessageEncoder.encode(eventMessage, TIME_SCALE);
+ return eventMessageEncoder.encode(eventMessage);
}
}
diff --git a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParserTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParserTest.java
index a1693f6985..67d8dbecac 100644
--- a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParserTest.java
+++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParserTest.java
@@ -104,52 +104,53 @@ public class DashManifestParserTest {
"call",
10000,
0,
- "+ 1 800 10101010".getBytes(Charset.forName(C.UTF8_NAME)),
- 0);
+ "+ 1 800 10101010".getBytes(Charset.forName(C.UTF8_NAME)));
assertThat(eventStream1.events[0]).isEqualTo(expectedEvent1);
+ assertThat(eventStream1.presentationTimesUs[0]).isEqualTo(0);
// assert CData-structured event stream
EventStream eventStream2 = period.eventStreams.get(1);
assertThat(eventStream2.events.length).isEqualTo(1);
- assertThat(eventStream2.events[0])
- .isEqualTo(
- new EventMessage(
- "urn:dvb:iptv:cpm:2014",
- "",
- 1500000,
- 1,
- Util.getUtf8Bytes(
- "\n"
- + " \n"
- + " \n"
- + " The title\n"
- + " "
- + "The description\n"
- + " \n"
- + " \n"
- + " GB\n"
- + " \n"
- + " \n"
- + " ]]>"),
- 300000000));
+ EventMessage expectedEvent2 =
+ new EventMessage(
+ "urn:dvb:iptv:cpm:2014",
+ "",
+ 1500000,
+ 1,
+ Util.getUtf8Bytes(
+ "\n"
+ + " \n"
+ + " \n"
+ + " The title\n"
+ + " "
+ + "The description\n"
+ + " \n"
+ + " \n"
+ + " GB\n"
+ + " \n"
+ + " \n"
+ + " ]]>"));
+
+ assertThat(eventStream2.events[0]).isEqualTo(expectedEvent2);
+ assertThat(eventStream2.presentationTimesUs[0]).isEqualTo(300000000);
// assert xml-structured event stream
EventStream eventStream3 = period.eventStreams.get(2);
assertThat(eventStream3.events.length).isEqualTo(1);
- assertThat(eventStream3.events[0])
- .isEqualTo(
- new EventMessage(
- "urn:scte:scte35:2014:xml+bin",
- "",
- 1000000,
- 2,
- Util.getUtf8Bytes(
- "\n"
- + " \n"
- + " /DAIAAAAAAAAAAAQAAZ/I0VniQAQAgBDVUVJQAAAAH+cAAAAAA==\n"
- + " \n"
- + " "),
- 1000000000));
+ EventMessage expectedEvent3 =
+ new EventMessage(
+ "urn:scte:scte35:2014:xml+bin",
+ "",
+ 1000000,
+ 2,
+ Util.getUtf8Bytes(
+ "\n"
+ + " \n"
+ + " /DAIAAAAAAAAAAAQAAZ/I0VniQAQAgBDVUVJQAAAAH+cAAAAAA==\n"
+ + " \n"
+ + " "));
+ assertThat(eventStream3.events[0]).isEqualTo(expectedEvent3);
+ assertThat(eventStream3.presentationTimesUs[0]).isEqualTo(1000000000);
}
@Test
diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java
index 2afd041631..cd2cbbcab9 100644
--- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java
+++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java
@@ -19,7 +19,6 @@ import android.net.Uri;
import android.os.Handler;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.C;
-import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.source.BaseMediaSource;
@@ -397,10 +396,7 @@ public final class HlsMediaSource extends BaseMediaSource
}
@Override
- public void prepareSourceInternal(
- ExoPlayer player,
- boolean isTopLevelSource,
- @Nullable TransferListener mediaTransferListener) {
+ public void prepareSourceInternal(@Nullable TransferListener mediaTransferListener) {
this.mediaTransferListener = mediaTransferListener;
EventDispatcher eventDispatcher = createEventDispatcher(/* mediaPeriodId= */ null);
playlistTracker.start(manifestUri, eventDispatcher, /* listener= */ this);
diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStream.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStream.java
index f43d119018..cf879e91c6 100644
--- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStream.java
+++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStream.java
@@ -68,6 +68,10 @@ import java.io.IOException;
@Override
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer, boolean requireFormat) {
+ if (sampleQueueIndex == HlsSampleStreamWrapper.SAMPLE_QUEUE_INDEX_NO_MAPPING_NON_FATAL) {
+ buffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM);
+ return C.RESULT_BUFFER_READ;
+ }
return hasValidSampleQueueIndex()
? sampleStreamWrapper.readData(sampleQueueIndex, formatHolder, buffer, requireFormat)
: C.RESULT_NOTHING_READ;
diff --git a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java
index d025f8fa3a..fb64f43772 100644
--- a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java
+++ b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java
@@ -20,7 +20,6 @@ import android.os.Handler;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.C;
-import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.Timeline;
@@ -510,10 +509,7 @@ public final class SsMediaSource extends BaseMediaSource
}
@Override
- public void prepareSourceInternal(
- ExoPlayer player,
- boolean isTopLevelSource,
- @Nullable TransferListener mediaTransferListener) {
+ public void prepareSourceInternal(@Nullable TransferListener mediaTransferListener) {
this.mediaTransferListener = mediaTransferListener;
if (sideloadedManifest) {
manifestLoaderErrorThrower = new LoaderErrorThrower.Dummy();
diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerView.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerView.java
index 9742d0005a..9d66289e94 100644
--- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerView.java
+++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerView.java
@@ -54,6 +54,7 @@ import com.google.android.exoplayer2.Player.VideoComponent;
import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.metadata.id3.ApicFrame;
import com.google.android.exoplayer2.source.TrackGroupArray;
+import com.google.android.exoplayer2.source.ads.AdsLoader;
import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.text.TextOutput;
import com.google.android.exoplayer2.trackselection.TrackSelection;
@@ -69,6 +70,7 @@ import com.google.android.exoplayer2.video.VideoListener;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -221,6 +223,11 @@ import java.util.List;
*
* - Type: {@link PlayerControlView}
*
+ * {@code exo_ad_overlay} - A {@link FrameLayout} positioned on top of the player which
+ * is used to show ad UI (if applicable).
+ *
+ * - Type: {@link FrameLayout}
+ *
* {@code exo_overlay} - A {@link FrameLayout} positioned on top of the player which
* the app can access via {@link #getOverlayFrameLayout()}, provided for convenience.
*
@@ -239,7 +246,7 @@ import java.util.List;
* PlayerView. This will cause the specified layout to be inflated instead of {@code
* exo_player_view.xml} for only the instance on which the attribute is set.
*/
-public class PlayerView extends FrameLayout {
+public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider {
// LINT.IfChange
/**
@@ -278,9 +285,10 @@ public class PlayerView extends FrameLayout {
private final SubtitleView subtitleView;
@Nullable private final View bufferingView;
@Nullable private final TextView errorMessageView;
- private final PlayerControlView controller;
+ @Nullable private final PlayerControlView controller;
private final ComponentListener componentListener;
- private final FrameLayout overlayFrameLayout;
+ @Nullable private final FrameLayout adOverlayFrameLayout;
+ @Nullable private final FrameLayout overlayFrameLayout;
private Player player;
private boolean useController;
@@ -317,6 +325,7 @@ public class PlayerView extends FrameLayout {
errorMessageView = null;
controller = null;
componentListener = null;
+ adOverlayFrameLayout = null;
overlayFrameLayout = null;
ImageView logo = new ImageView(context);
if (Util.SDK_INT >= 23) {
@@ -411,6 +420,9 @@ public class PlayerView extends FrameLayout {
surfaceView = null;
}
+ // Ad overlay frame layout.
+ adOverlayFrameLayout = findViewById(R.id.exo_ad_overlay);
+
// Overlay frame layout.
overlayFrameLayout = findViewById(R.id.exo_overlay);
@@ -1012,6 +1024,7 @@ public class PlayerView extends FrameLayout {
* @return The overlay {@link FrameLayout}, or {@code null} if the layout has been customized and
* the overlay is not present.
*/
+ @Nullable
public FrameLayout getOverlayFrameLayout() {
return overlayFrameLayout;
}
@@ -1095,6 +1108,28 @@ public class PlayerView extends FrameLayout {
}
}
+ // AdsLoader.AdViewProvider implementation.
+
+ @Override
+ public ViewGroup getAdViewGroup() {
+ return Assertions.checkNotNull(
+ adOverlayFrameLayout, "exo_ad_overlay must be present for ad playback");
+ }
+
+ @Override
+ public View[] getAdOverlayViews() {
+ ArrayList overlayViews = new ArrayList<>();
+ if (overlayFrameLayout != null) {
+ overlayViews.add(overlayFrameLayout);
+ }
+ if (controller != null) {
+ overlayViews.add(controller);
+ }
+ return overlayViews.toArray(new View[0]);
+ }
+
+ // Internal methods.
+
private boolean toggleControllerVisibility() {
if (!useController || player == null) {
return false;
diff --git a/library/ui/src/main/res/layout/exo_simple_player_view.xml b/library/ui/src/main/res/layout/exo_simple_player_view.xml
index 167ac96222..65dea9271e 100644
--- a/library/ui/src/main/res/layout/exo_simple_player_view.xml
+++ b/library/ui/src/main/res/layout/exo_simple_player_view.xml
@@ -52,6 +52,10 @@
+
+
diff --git a/library/ui/src/main/res/values/ids.xml b/library/ui/src/main/res/values/ids.xml
index 184e51ac58..6f798adcb4 100644
--- a/library/ui/src/main/res/values/ids.xml
+++ b/library/ui/src/main/res/values/ids.xml
@@ -21,6 +21,7 @@
+
diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaSource.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaSource.java
index 999372b90a..de4be82b38 100644
--- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaSource.java
+++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaSource.java
@@ -22,7 +22,6 @@ import android.os.Handler;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.C;
-import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.Timeline.Period;
@@ -96,10 +95,7 @@ public class FakeMediaSource extends BaseMediaSource {
}
@Override
- public synchronized void prepareSourceInternal(
- ExoPlayer player,
- boolean isTopLevelSource,
- @Nullable TransferListener mediaTransferListener) {
+ public synchronized void prepareSourceInternal(@Nullable TransferListener mediaTransferListener) {
assertThat(preparedSource).isFalse();
transferListener = mediaTransferListener;
preparedSource = true;
diff --git a/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/MediaSourceTestRunner.java b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/MediaSourceTestRunner.java
index e6fb5bc5f3..9514768416 100644
--- a/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/MediaSourceTestRunner.java
+++ b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/MediaSourceTestRunner.java
@@ -17,17 +17,13 @@ package com.google.android.exoplayer2.testutil;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
-import static org.junit.Assert.fail;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
-import android.os.Message;
import android.support.annotation.Nullable;
import android.util.Pair;
-import com.google.android.exoplayer2.ExoPlaybackException;
-import com.google.android.exoplayer2.PlayerMessage;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.MediaPeriod;
import com.google.android.exoplayer2.source.MediaSource;
@@ -54,7 +50,6 @@ public class MediaSourceTestRunner {
public static final int TIMEOUT_MS = 10000;
- private final StubExoPlayer player;
private final MediaSource mediaSource;
private final MediaSourceListener mediaSourceListener;
private final HandlerThread playbackThread;
@@ -79,7 +74,6 @@ public class MediaSourceTestRunner {
playbackThread.start();
Looper playbackLooper = playbackThread.getLooper();
playbackHandler = new Handler(playbackLooper);
- player = new EventHandlingExoPlayer(playbackLooper);
mediaSourceListener = new MediaSourceListener();
timelines = new LinkedBlockingDeque<>();
completedLoads = new CopyOnWriteArrayList<>();
@@ -121,11 +115,7 @@ public class MediaSourceTestRunner {
final IOException[] prepareError = new IOException[1];
runOnPlaybackThread(
() -> {
- mediaSource.prepareSource(
- player,
- /* isTopLevelSource= */ true,
- mediaSourceListener,
- /* mediaTransferListener= */ null);
+ mediaSource.prepareSource(mediaSourceListener, /* mediaTransferListener= */ null);
try {
// TODO: This only catches errors that are set synchronously in prepareSource. To
// capture async errors we'll need to poll maybeThrowSourceInfoRefreshError until the
@@ -430,43 +420,4 @@ public class MediaSourceTestRunner {
Assertions.checkState(Looper.myLooper() == playbackThread.getLooper());
}
}
-
- private static class EventHandlingExoPlayer extends StubExoPlayer
- implements Handler.Callback, PlayerMessage.Sender {
-
- private final Handler handler;
-
- public EventHandlingExoPlayer(Looper looper) {
- this.handler = new Handler(looper, this);
- }
-
- @Override
- public Looper getApplicationLooper() {
- return handler.getLooper();
- }
-
- @Override
- public PlayerMessage createMessage(PlayerMessage.Target target) {
- return new PlayerMessage(
- /* sender= */ this, target, Timeline.EMPTY, /* defaultWindowIndex= */ 0, handler);
- }
-
- @Override
- public void sendMessage(PlayerMessage message) {
- handler.obtainMessage(0, message).sendToTarget();
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public boolean handleMessage(Message msg) {
- PlayerMessage message = (PlayerMessage) msg.obj;
- try {
- message.getTarget().handleMessage(message.getType(), message.getPayload());
- message.markAsProcessed(/* isDelivered= */ true);
- } catch (ExoPlaybackException e) {
- fail("Unexpected ExoPlaybackException.");
- }
- return true;
- }
- }
}