diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 1ed740f23a..e0dd6991ab 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -9,6 +9,8 @@ correctly implemented until now. * Normalize MIME types set by app code or read from media to be fully lower-case. + * Define ads with a full `MediaItem` instead of a single `Uri` in + `AdPlaybackState`. * ExoPlayer: * Add `PreloadMediaSource` and `PreloadMediaPeriod` that allows apps to preload the media source at a specific start position before playback, diff --git a/libraries/common/src/main/java/androidx/media3/common/AdPlaybackState.java b/libraries/common/src/main/java/androidx/media3/common/AdPlaybackState.java index 98508cbe0e..e65c7fec89 100644 --- a/libraries/common/src/main/java/androidx/media3/common/AdPlaybackState.java +++ b/libraries/common/src/main/java/androidx/media3/common/AdPlaybackState.java @@ -16,6 +16,7 @@ package androidx.media3.common; import static androidx.media3.common.util.Assertions.checkArgument; +import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Assertions.checkState; import static java.lang.Math.max; import static java.lang.annotation.ElementType.FIELD; @@ -30,6 +31,7 @@ import androidx.annotation.CheckResult; import androidx.annotation.IntDef; import androidx.annotation.IntRange; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import androidx.media3.common.util.NullableType; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; @@ -74,8 +76,13 @@ public final class AdPlaybackState implements Bundleable { */ public final int originalCount; - /** The URI of each ad in the ad group. */ - public final @NullableType Uri[] uris; + /** + * @deprecated Use {@link #mediaItems} instead. + */ + @Deprecated public final @NullableType Uri[] uris; + + /** The {@link MediaItem} instances for each ad in the ad group, or null if not yet known. */ + public final @NullableType MediaItem[] mediaItems; /** The state of each ad in the ad group. */ public final @AdState int[] states; @@ -104,7 +111,7 @@ public final class AdPlaybackState implements Bundleable { /* count= */ C.LENGTH_UNSET, /* originalCount= */ C.LENGTH_UNSET, /* states= */ new int[0], - /* uris= */ new Uri[0], + /* mediaItems= */ new MediaItem[0], /* durationsUs= */ new long[0], /* contentResumeOffsetUs= */ 0, /* isServerSideInserted= */ false); @@ -115,19 +122,23 @@ public final class AdPlaybackState implements Bundleable { int count, int originalCount, @AdState int[] states, - @NullableType Uri[] uris, + @NullableType MediaItem[] mediaItems, long[] durationsUs, long contentResumeOffsetUs, boolean isServerSideInserted) { - checkArgument(states.length == uris.length); + checkArgument(states.length == mediaItems.length); this.timeUs = timeUs; this.count = count; this.originalCount = originalCount; this.states = states; - this.uris = uris; + this.mediaItems = mediaItems; this.durationsUs = durationsUs; this.contentResumeOffsetUs = contentResumeOffsetUs; this.isServerSideInserted = isServerSideInserted; + this.uris = new Uri[mediaItems.length]; + for (int i = 0; i < uris.length; i++) { + uris[i] = mediaItems[i] == null ? null : checkNotNull(mediaItems[i].localConfiguration).uri; + } } /** @@ -195,7 +206,7 @@ public final class AdPlaybackState implements Bundleable { return timeUs == adGroup.timeUs && count == adGroup.count && originalCount == adGroup.originalCount - && Arrays.equals(uris, adGroup.uris) + && Arrays.equals(mediaItems, adGroup.mediaItems) && Arrays.equals(states, adGroup.states) && Arrays.equals(durationsUs, adGroup.durationsUs) && contentResumeOffsetUs == adGroup.contentResumeOffsetUs @@ -207,7 +218,7 @@ public final class AdPlaybackState implements Bundleable { int result = count; result = 31 * result + originalCount; result = 31 * result + (int) (timeUs ^ (timeUs >>> 32)); - result = 31 * result + Arrays.hashCode(uris); + result = 31 * result + Arrays.hashCode(mediaItems); result = 31 * result + Arrays.hashCode(states); result = 31 * result + Arrays.hashCode(durationsUs); result = 31 * result + (int) (contentResumeOffsetUs ^ (contentResumeOffsetUs >>> 32)); @@ -223,7 +234,7 @@ public final class AdPlaybackState implements Bundleable { count, originalCount, states, - uris, + mediaItems, durationsUs, contentResumeOffsetUs, isServerSideInserted); @@ -234,38 +245,47 @@ public final class AdPlaybackState implements Bundleable { public AdGroup withAdCount(int count) { @AdState int[] states = copyStatesWithSpaceForAdCount(this.states, count); long[] durationsUs = copyDurationsUsWithSpaceForAdCount(this.durationsUs, count); - @NullableType Uri[] uris = Arrays.copyOf(this.uris, count); + @NullableType MediaItem[] mediaItems = Arrays.copyOf(this.mediaItems, count); return new AdGroup( timeUs, count, originalCount, states, - uris, + mediaItems, durationsUs, contentResumeOffsetUs, isServerSideInserted); } /** - * Returns a new instance with the specified {@code uri} set for the specified ad, and the ad - * marked as {@link #AD_STATE_AVAILABLE}. + * @deprecated Use {@link #withAdMediaItem} instead. */ + @Deprecated @CheckResult public AdGroup withAdUri(Uri uri, @IntRange(from = 0) int index) { + return withAdMediaItem(MediaItem.fromUri(uri), index); + } + + /** + * Returns a new instance with the specified {@link MediaItem} set for the specified ad, and the + * ad marked as {@link #AD_STATE_AVAILABLE}. + */ + @CheckResult + public AdGroup withAdMediaItem(MediaItem mediaItem, @IntRange(from = 0) int index) { @AdState int[] states = copyStatesWithSpaceForAdCount(this.states, index + 1); long[] durationsUs = this.durationsUs.length == states.length ? this.durationsUs : copyDurationsUsWithSpaceForAdCount(this.durationsUs, states.length); - @NullableType Uri[] uris = Arrays.copyOf(this.uris, states.length); - uris[index] = uri; + @NullableType MediaItem[] mediaItems = Arrays.copyOf(this.mediaItems, states.length); + mediaItems[index] = mediaItem; states[index] = AD_STATE_AVAILABLE; return new AdGroup( timeUs, count, originalCount, states, - uris, + mediaItems, durationsUs, contentResumeOffsetUs, isServerSideInserted); @@ -292,15 +312,17 @@ public final class AdPlaybackState implements Bundleable { ? this.durationsUs : copyDurationsUsWithSpaceForAdCount(this.durationsUs, states.length); @NullableType - Uri[] uris = - this.uris.length == states.length ? this.uris : Arrays.copyOf(this.uris, states.length); + MediaItem[] mediaItems = + this.mediaItems.length == states.length + ? this.mediaItems + : Arrays.copyOf(this.mediaItems, states.length); states[index] = state; return new AdGroup( timeUs, count, originalCount, states, - uris, + mediaItems, durationsUs, contentResumeOffsetUs, isServerSideInserted); @@ -309,17 +331,17 @@ public final class AdPlaybackState implements Bundleable { /** Returns a new instance with the specified ad durations, in microseconds. */ @CheckResult public AdGroup withAdDurationsUs(long[] durationsUs) { - if (durationsUs.length < uris.length) { - durationsUs = copyDurationsUsWithSpaceForAdCount(durationsUs, uris.length); - } else if (count != C.LENGTH_UNSET && durationsUs.length > uris.length) { - durationsUs = Arrays.copyOf(durationsUs, uris.length); + if (durationsUs.length < mediaItems.length) { + durationsUs = copyDurationsUsWithSpaceForAdCount(durationsUs, mediaItems.length); + } else if (count != C.LENGTH_UNSET && durationsUs.length > mediaItems.length) { + durationsUs = Arrays.copyOf(durationsUs, mediaItems.length); } return new AdGroup( timeUs, count, originalCount, states, - uris, + mediaItems, durationsUs, contentResumeOffsetUs, isServerSideInserted); @@ -333,7 +355,7 @@ public final class AdPlaybackState implements Bundleable { count, originalCount, states, - uris, + mediaItems, durationsUs, contentResumeOffsetUs, isServerSideInserted); @@ -347,7 +369,7 @@ public final class AdPlaybackState implements Bundleable { count, originalCount, states, - uris, + mediaItems, durationsUs, contentResumeOffsetUs, isServerSideInserted); @@ -360,7 +382,7 @@ public final class AdPlaybackState implements Bundleable { count, originalCount, states, - uris, + mediaItems, durationsUs, contentResumeOffsetUs, isServerSideInserted); @@ -370,7 +392,7 @@ public final class AdPlaybackState implements Bundleable { public AdGroup withLastAdRemoved() { int newCount = states.length - 1; @AdState int[] newStates = Arrays.copyOf(states, newCount); - @NullableType Uri[] newUris = Arrays.copyOf(uris, newCount); + @NullableType MediaItem[] newMediaItems = Arrays.copyOf(mediaItems, newCount); long[] newDurationsUs = durationsUs; if (durationsUs.length > newCount) { newDurationsUs = Arrays.copyOf(durationsUs, newCount); @@ -380,7 +402,7 @@ public final class AdPlaybackState implements Bundleable { newCount, originalCount, newStates, - newUris, + newMediaItems, newDurationsUs, /* contentResumeOffsetUs= */ Util.sum(newDurationsUs), isServerSideInserted); @@ -398,7 +420,7 @@ public final class AdPlaybackState implements Bundleable { /* count= */ 0, originalCount, /* states= */ new int[0], - /* uris= */ new Uri[0], + /* mediaItems= */ new MediaItem[0], /* durationsUs= */ new long[0], contentResumeOffsetUs, isServerSideInserted); @@ -415,7 +437,7 @@ public final class AdPlaybackState implements Bundleable { count, originalCount, states, - uris, + mediaItems, durationsUs, contentResumeOffsetUs, isServerSideInserted); @@ -436,7 +458,7 @@ public final class AdPlaybackState implements Bundleable { if (states[i] == AD_STATE_PLAYED || states[i] == AD_STATE_SKIPPED || states[i] == AD_STATE_ERROR) { - states[i] = uris[i] == null ? AD_STATE_UNAVAILABLE : AD_STATE_AVAILABLE; + states[i] = mediaItems[i] == null ? AD_STATE_UNAVAILABLE : AD_STATE_AVAILABLE; } } return new AdGroup( @@ -444,7 +466,7 @@ public final class AdPlaybackState implements Bundleable { count, originalCount, states, - uris, + mediaItems, durationsUs, contentResumeOffsetUs, isServerSideInserted); @@ -478,6 +500,7 @@ public final class AdPlaybackState implements Bundleable { private static final String FIELD_CONTENT_RESUME_OFFSET_US = Util.intToStringMaxRadix(5); private static final String FIELD_IS_SERVER_SIDE_INSERTED = Util.intToStringMaxRadix(6); private static final String FIELD_ORIGINAL_COUNT = Util.intToStringMaxRadix(7); + @VisibleForTesting static final String FIELD_MEDIA_ITEMS = Util.intToStringMaxRadix(8); // putParcelableArrayList actually supports null elements. @SuppressWarnings("nullness:argument") @@ -489,6 +512,7 @@ public final class AdPlaybackState implements Bundleable { bundle.putInt(FIELD_ORIGINAL_COUNT, originalCount); bundle.putParcelableArrayList( FIELD_URIS, new ArrayList<@NullableType Uri>(Arrays.asList(uris))); + bundle.putParcelableArrayList(FIELD_MEDIA_ITEMS, getMediaItemsArrayBundles()); bundle.putIntArray(FIELD_STATES, states); bundle.putLongArray(FIELD_DURATIONS_US, durationsUs); bundle.putLong(FIELD_CONTENT_RESUME_OFFSET_US, contentResumeOffsetUs); @@ -514,6 +538,9 @@ public final class AdPlaybackState implements Bundleable { int originalCount = bundle.getInt(FIELD_ORIGINAL_COUNT); @Nullable ArrayList<@NullableType Uri> uriList = bundle.getParcelableArrayList(FIELD_URIS); @Nullable + ArrayList<@NullableType Bundle> mediaItemBundleList = + bundle.getParcelableArrayList(FIELD_MEDIA_ITEMS); + @Nullable @AdState int[] states = bundle.getIntArray(FIELD_STATES); @Nullable long[] durationsUs = bundle.getLongArray(FIELD_DURATIONS_US); @@ -524,11 +551,41 @@ public final class AdPlaybackState implements Bundleable { count, originalCount, states == null ? new int[0] : states, - uriList == null ? new Uri[0] : uriList.toArray(new Uri[0]), + getMediaItemsFromBundleArrays(mediaItemBundleList, uriList), durationsUs == null ? new long[0] : durationsUs, contentResumeOffsetUs, isServerSideInserted); } + + private ArrayList<@NullableType Bundle> getMediaItemsArrayBundles() { + ArrayList<@NullableType Bundle> bundles = new ArrayList<>(); + for (@Nullable MediaItem mediaItem : mediaItems) { + bundles.add(mediaItem == null ? null : mediaItem.toBundleIncludeLocalConfiguration()); + } + return bundles; + } + + private static @NullableType MediaItem[] getMediaItemsFromBundleArrays( + @Nullable ArrayList<@NullableType Bundle> mediaItemBundleList, + @Nullable ArrayList<@NullableType Uri> uriList) { + if (mediaItemBundleList != null) { + @NullableType MediaItem[] mediaItems = new MediaItem[mediaItemBundleList.size()]; + for (int i = 0; i < mediaItemBundleList.size(); i++) { + @Nullable Bundle mediaItemBundle = mediaItemBundleList.get(i); + mediaItems[i] = mediaItemBundle == null ? null : MediaItem.fromBundle(mediaItemBundle); + } + return mediaItems; + } else if (uriList != null) { + @NullableType MediaItem[] mediaItems = new MediaItem[uriList.size()]; + for (int i = 0; i < uriList.size(); i++) { + @Nullable Uri uri = uriList.get(i); + mediaItems[i] = uri == null ? null : MediaItem.fromUri(uri); + } + return mediaItems; + } else { + return new MediaItem[0]; + } + } } /** @@ -764,19 +821,35 @@ public final class AdPlaybackState implements Bundleable { } /** - * Returns an instance with the specified ad URI and the ad marked as {@linkplain - * #AD_STATE_AVAILABLE available}. - * - * @throws IllegalStateException If {@link Uri#EMPTY} is passed as argument for a client-side - * inserted ad group. + * @deprecated Use {@link #withAvailableAdMediaItem} instead. */ + @Deprecated @CheckResult public AdPlaybackState withAvailableAdUri( @IntRange(from = 0) int adGroupIndex, @IntRange(from = 0) int adIndexInAdGroup, Uri uri) { + return withAvailableAdMediaItem(adGroupIndex, adIndexInAdGroup, MediaItem.fromUri(uri)); + } + + /** + * Returns an instance with the specified ad {@link MediaItem} and the ad marked as {@linkplain + * #AD_STATE_AVAILABLE available}. + * + * @throws IllegalStateException If a {@link MediaItem} with an empty {@link + * MediaItem.LocalConfiguration#uri} is passed as argument for a client-side inserted ad + * group. + */ + @CheckResult + public AdPlaybackState withAvailableAdMediaItem( + @IntRange(from = 0) int adGroupIndex, + @IntRange(from = 0) int adIndexInAdGroup, + MediaItem mediaItem) { int adjustedIndex = adGroupIndex - removedAdGroupCount; AdGroup[] adGroups = Util.nullSafeArrayCopy(this.adGroups, this.adGroups.length); - checkState(!Uri.EMPTY.equals(uri) || adGroups[adjustedIndex].isServerSideInserted); - adGroups[adjustedIndex] = adGroups[adjustedIndex].withAdUri(uri, adIndexInAdGroup); + checkState( + adGroups[adjustedIndex].isServerSideInserted + || (mediaItem.localConfiguration != null + && !mediaItem.localConfiguration.uri.equals(Uri.EMPTY))); + adGroups[adjustedIndex] = adGroups[adjustedIndex].withAdMediaItem(mediaItem, adIndexInAdGroup); return new AdPlaybackState( adsId, adGroups, adResumePositionUs, contentDurationUs, removedAdGroupCount); } @@ -785,7 +858,7 @@ public final class AdPlaybackState implements Bundleable { * Returns an instance with the specified ad marked as {@linkplain #AD_STATE_AVAILABLE available}. * *

Must not be called with client side inserted ad groups. Client side inserted ads should use - * {@link #withAvailableAdUri}. + * {@link #withAvailableAdMediaItem}. * * @throws IllegalStateException in case this methods is called on an ad group that {@linkplain * AdGroup#isServerSideInserted is not server side inserted}. @@ -793,7 +866,7 @@ public final class AdPlaybackState implements Bundleable { @CheckResult public AdPlaybackState withAvailableAd( @IntRange(from = 0) int adGroupIndex, @IntRange(from = 0) int adIndexInAdGroup) { - return withAvailableAdUri(adGroupIndex, adIndexInAdGroup, Uri.EMPTY); + return withAvailableAdMediaItem(adGroupIndex, adIndexInAdGroup, MediaItem.fromUri(Uri.EMPTY)); } /** Returns an instance with the specified ad marked as {@linkplain #AD_STATE_PLAYED played}. */ @@ -1058,7 +1131,7 @@ public final class AdPlaybackState implements Bundleable { adGroup.count, adGroup.originalCount, Arrays.copyOf(adGroup.states, adGroup.states.length), - Arrays.copyOf(adGroup.uris, adGroup.uris.length), + Arrays.copyOf(adGroup.mediaItems, adGroup.mediaItems.length), Arrays.copyOf(adGroup.durationsUs, adGroup.durationsUs.length), adGroup.contentResumeOffsetUs, adGroup.isServerSideInserted); diff --git a/libraries/common/src/test/java/androidx/media3/common/AdPlaybackStateTest.java b/libraries/common/src/test/java/androidx/media3/common/AdPlaybackStateTest.java index facc994bdc..215361436e 100644 --- a/libraries/common/src/test/java/androidx/media3/common/AdPlaybackStateTest.java +++ b/libraries/common/src/test/java/androidx/media3/common/AdPlaybackStateTest.java @@ -35,7 +35,7 @@ import org.junit.runner.RunWith; public class AdPlaybackStateTest { private static final long[] TEST_AD_GROUP_TIMES_US = new long[] {0, 5_000_000, 10_000_000}; - private static final Uri TEST_URI = Uri.parse("http://www.google.com"); + private static final MediaItem TEST_MEDIA_ITEM = MediaItem.fromUri("http://www.google.com"); private static final Object TEST_ADS_ID = new Object(); @Test @@ -50,16 +50,18 @@ public class AdPlaybackStateTest { } @Test - public void setAdUriBeforeAdCount() { + public void setAdMediaItemBeforeAdCount() { AdPlaybackState state = new AdPlaybackState(TEST_ADS_ID, TEST_AD_GROUP_TIMES_US).withRemovedAdGroupCount(1); - state = state.withAvailableAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 1, TEST_URI); + state = + state.withAvailableAdMediaItem( + /* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 1, TEST_MEDIA_ITEM); state = state.withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 2); - assertThat(state.getAdGroup(1).uris[0]).isNull(); + assertThat(state.getAdGroup(1).mediaItems[0]).isNull(); assertThat(state.getAdGroup(1).states[0]).isEqualTo(AdPlaybackState.AD_STATE_UNAVAILABLE); - assertThat(state.getAdGroup(1).uris[1]).isSameInstanceAs(TEST_URI); + assertThat(state.getAdGroup(1).mediaItems[1]).isSameInstanceAs(TEST_MEDIA_ITEM); assertThat(state.getAdGroup(1).states[1]).isEqualTo(AdPlaybackState.AD_STATE_AVAILABLE); } @@ -71,7 +73,7 @@ public class AdPlaybackStateTest { state = state.withAdLoadError(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0); state = state.withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 2); - assertThat(state.getAdGroup(1).uris[0]).isNull(); + assertThat(state.getAdGroup(1).mediaItems[0]).isNull(); assertThat(state.getAdGroup(1).states[0]).isEqualTo(AdPlaybackState.AD_STATE_ERROR); assertThat(state.isAdInErrorState(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0)).isTrue(); assertThat(state.getAdGroup(1).states[1]).isEqualTo(AdPlaybackState.AD_STATE_UNAVAILABLE); @@ -101,7 +103,8 @@ public class AdPlaybackStateTest { .withRemovedAdGroupCount(1) .withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 2) .withAdCount(/* adGroupIndex= */ 2, /* adCount= */ 1) - .withAvailableAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 1, TEST_URI) + .withAvailableAdMediaItem( + /* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 1, TEST_MEDIA_ITEM) .withSkippedAd(/* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 0); state = @@ -113,7 +116,7 @@ public class AdPlaybackStateTest { assertThat(state.adGroupCount).isEqualTo(6); assertThat(state.getAdGroup(1).count).isEqualTo(C.INDEX_UNSET); assertThat(state.getAdGroup(2).count).isEqualTo(2); - assertThat(state.getAdGroup(2).uris[1]).isSameInstanceAs(TEST_URI); + assertThat(state.getAdGroup(2).mediaItems[1]).isSameInstanceAs(TEST_MEDIA_ITEM); assertThat(state.getAdGroup(3).count).isEqualTo(C.INDEX_UNSET); assertThat(state.getAdGroup(4).count).isEqualTo(1); assertThat(state.getAdGroup(4).states[0]).isEqualTo(AdPlaybackState.AD_STATE_SKIPPED); @@ -141,8 +144,12 @@ public class AdPlaybackStateTest { AdPlaybackState state = new AdPlaybackState(TEST_ADS_ID, TEST_AD_GROUP_TIMES_US).withRemovedAdGroupCount(1); state = state.withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 3); - state = state.withAvailableAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0, TEST_URI); - state = state.withAvailableAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 2, TEST_URI); + state = + state.withAvailableAdMediaItem( + /* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0, TEST_MEDIA_ITEM); + state = + state.withAvailableAdMediaItem( + /* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 2, TEST_MEDIA_ITEM); assertThat(state.getAdGroup(1).getFirstAdIndexToPlay()).isEqualTo(0); } @@ -152,8 +159,12 @@ public class AdPlaybackStateTest { AdPlaybackState state = new AdPlaybackState(TEST_ADS_ID, TEST_AD_GROUP_TIMES_US).withRemovedAdGroupCount(1); state = state.withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 3); - state = state.withAvailableAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0, TEST_URI); - state = state.withAvailableAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 2, TEST_URI); + state = + state.withAvailableAdMediaItem( + /* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0, TEST_MEDIA_ITEM); + state = + state.withAvailableAdMediaItem( + /* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 2, TEST_MEDIA_ITEM); state = state.withPlayedAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0); @@ -167,8 +178,12 @@ public class AdPlaybackStateTest { AdPlaybackState state = new AdPlaybackState(TEST_ADS_ID, TEST_AD_GROUP_TIMES_US).withRemovedAdGroupCount(1); state = state.withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 3); - state = state.withAvailableAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0, TEST_URI); - state = state.withAvailableAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 2, TEST_URI); + state = + state.withAvailableAdMediaItem( + /* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0, TEST_MEDIA_ITEM); + state = + state.withAvailableAdMediaItem( + /* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 2, TEST_MEDIA_ITEM); state = state.withSkippedAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0); @@ -182,8 +197,12 @@ public class AdPlaybackStateTest { AdPlaybackState state = new AdPlaybackState(TEST_ADS_ID, TEST_AD_GROUP_TIMES_US).withRemovedAdGroupCount(1); state = state.withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 3); - state = state.withAvailableAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0, TEST_URI); - state = state.withAvailableAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 2, TEST_URI); + state = + state.withAvailableAdMediaItem( + /* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0, TEST_MEDIA_ITEM); + state = + state.withAvailableAdMediaItem( + /* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 2, TEST_MEDIA_ITEM); state = state.withPlayedAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0); state = state.withAdLoadError(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 1); @@ -196,7 +215,9 @@ public class AdPlaybackStateTest { AdPlaybackState state = new AdPlaybackState(TEST_ADS_ID, TEST_AD_GROUP_TIMES_US).withRemovedAdGroupCount(1); state = state.withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 3); - state = state.withAvailableAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 1, TEST_URI); + state = + state.withAvailableAdMediaItem( + /* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 1, TEST_MEDIA_ITEM); state = state.withAdLoadError(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 1); @@ -209,9 +230,15 @@ public class AdPlaybackStateTest { new AdPlaybackState(TEST_ADS_ID, TEST_AD_GROUP_TIMES_US).withRemovedAdGroupCount(1); state = state.withIsServerSideInserted(/* adGroupIndex= */ 1, /* isServerSideInserted= */ true); state = state.withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 3); - state = state.withAvailableAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0, TEST_URI); - state = state.withAvailableAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 1, TEST_URI); - state = state.withAvailableAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 2, TEST_URI); + state = + state.withAvailableAdMediaItem( + /* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0, TEST_MEDIA_ITEM); + state = + state.withAvailableAdMediaItem( + /* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 1, TEST_MEDIA_ITEM); + state = + state.withAvailableAdMediaItem( + /* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 2, TEST_MEDIA_ITEM); state = state.withPlayedAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0); @@ -224,9 +251,15 @@ public class AdPlaybackStateTest { new AdPlaybackState(TEST_ADS_ID, TEST_AD_GROUP_TIMES_US).withRemovedAdGroupCount(1); state = state.withIsServerSideInserted(/* adGroupIndex= */ 1, /* isServerSideInserted= */ true); state = state.withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 3); - state = state.withAvailableAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0, TEST_URI); - state = state.withAvailableAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 1, TEST_URI); - state = state.withAvailableAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 2, TEST_URI); + state = + state.withAvailableAdMediaItem( + /* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0, TEST_MEDIA_ITEM); + state = + state.withAvailableAdMediaItem( + /* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 1, TEST_MEDIA_ITEM); + state = + state.withAvailableAdMediaItem( + /* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 2, TEST_MEDIA_ITEM); state = state.withPlayedAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0); state = state.withPlayedAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 1); @@ -265,9 +298,9 @@ public class AdPlaybackStateTest { .asList() .containsExactly(AD_STATE_UNAVAILABLE, AD_STATE_UNAVAILABLE, AD_STATE_AVAILABLE) .inOrder(); - assertThat(state.getAdGroup(adGroupIndex).uris) + assertThat(state.getAdGroup(adGroupIndex).mediaItems) .asList() - .containsExactly(null, null, Uri.EMPTY) + .containsExactly(null, null, MediaItem.fromUri(Uri.EMPTY)) .inOrder(); state = @@ -364,10 +397,18 @@ public class AdPlaybackStateTest { state = state.withAdDurationsUs( /* adGroupIndex= */ 1, /* adDurationsUs...= */ 1_000L, 2_000L, 3_000L, 4_000L, 5_000L); - state = state.withAvailableAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 1, TEST_URI); - state = state.withAvailableAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 2, TEST_URI); - state = state.withAvailableAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 3, TEST_URI); - state = state.withAvailableAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 4, TEST_URI); + state = + state.withAvailableAdMediaItem( + /* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 1, TEST_MEDIA_ITEM); + state = + state.withAvailableAdMediaItem( + /* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 2, TEST_MEDIA_ITEM); + state = + state.withAvailableAdMediaItem( + /* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 3, TEST_MEDIA_ITEM); + state = + state.withAvailableAdMediaItem( + /* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 4, TEST_MEDIA_ITEM); state = state.withPlayedAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 2); state = state.withSkippedAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 3); state = state.withAdLoadError(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 4); @@ -393,9 +434,9 @@ public class AdPlaybackStateTest { AD_STATE_AVAILABLE, AD_STATE_AVAILABLE) .inOrder(); - assertThat(state.getAdGroup(/* adGroupIndex= */ 1).uris) + assertThat(state.getAdGroup(/* adGroupIndex= */ 1).mediaItems) .asList() - .containsExactly(null, TEST_URI, TEST_URI, TEST_URI, TEST_URI) + .containsExactly(null, TEST_MEDIA_ITEM, TEST_MEDIA_ITEM, TEST_MEDIA_ITEM, TEST_MEDIA_ITEM) .inOrder(); assertThat(state.getAdGroup(/* adGroupIndex= */ 1).durationsUs) .asList() @@ -445,12 +486,15 @@ public class AdPlaybackStateTest { .withRemovedAdGroupCount(1) .withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 1) .withPlayedAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0) - .withAvailableAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0, TEST_URI) + .withAvailableAdMediaItem( + /* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0, TEST_MEDIA_ITEM) .withAdCount(/* adGroupIndex= */ 2, /* adCount= */ 2) .withSkippedAd(/* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 0) .withPlayedAd(/* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 1) - .withAvailableAdUri(/* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 0, TEST_URI) - .withAvailableAdUri(/* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 1, TEST_URI) + .withAvailableAdMediaItem( + /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 0, TEST_MEDIA_ITEM) + .withAvailableAdMediaItem( + /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 1, TEST_MEDIA_ITEM) .withContentResumeOffsetUs(/* adGroupIndex= */ 1, /* contentResumeOffsetUs= */ 4444) .withContentResumeOffsetUs(/* adGroupIndex= */ 2, /* contentResumeOffsetUs= */ 3333) .withIsServerSideInserted(/* adGroupIndex= */ 1, /* isServerSideInserted= */ true) @@ -478,8 +522,10 @@ public class AdPlaybackStateTest { .withAdCount(2) .withAdState(AD_STATE_AVAILABLE, /* index= */ 0) .withAdState(AD_STATE_PLAYED, /* index= */ 1) - .withAdUri(Uri.parse("https://www.google.com"), /* index= */ 0) - .withAdUri(Uri.EMPTY, /* index= */ 1) + .withAdMediaItem( + new MediaItem.Builder().setUri("https://www.google.com").setMediaId("id").build(), + /* index= */ 0) + .withAdMediaItem(new MediaItem.Builder().setUri(Uri.EMPTY).build(), /* index= */ 1) .withAdDurationsUs(new long[] {1234, 5678}) .withContentResumeOffsetUs(4444) .withIsServerSideInserted(true); @@ -487,6 +533,25 @@ public class AdPlaybackStateTest { assertThat(AdPlaybackState.AdGroup.fromBundle(adGroup.toBundle())).isEqualTo(adGroup); } + @Test + public void fromBundle_ofAdGroupWithOnlyUris_yieldsCorrectInstance() { + AdPlaybackState.AdGroup adGroup = + new AdPlaybackState.AdGroup(/* timeUs= */ 42) + .withAdCount(2) + .withAdState(AD_STATE_AVAILABLE, /* index= */ 0) + .withAdState(AD_STATE_PLAYED, /* index= */ 1) + .withAdMediaItem( + new MediaItem.Builder().setUri("https://www.google.com").build(), /* index= */ 0) + .withAdMediaItem(new MediaItem.Builder().setUri(Uri.EMPTY).build(), /* index= */ 1) + .withAdDurationsUs(new long[] {1234, 5678}) + .withContentResumeOffsetUs(4444) + .withIsServerSideInserted(true); + Bundle bundle = adGroup.toBundle(); + bundle.remove(AdPlaybackState.AdGroup.FIELD_MEDIA_ITEMS); + + assertThat(AdPlaybackState.AdGroup.fromBundle(bundle)).isEqualTo(adGroup); + } + @Test public void withLivePostrollPlaceholderAppended_emptyAdPlaybackState_insertsPlaceholder() { AdPlaybackState adPlaybackState = diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/AdsMediaSource.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/AdsMediaSource.java index ead06f3ea5..64c16b199f 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/AdsMediaSource.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/AdsMediaSource.java @@ -19,7 +19,6 @@ import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Assertions.checkState; import static java.lang.annotation.ElementType.TYPE_USE; -import android.net.Uri; import android.os.Handler; import android.os.Looper; import android.os.SystemClock; @@ -331,16 +330,16 @@ public final class AdsMediaSource extends CompositeMediaSource { AdPlaybackState.AdGroup adGroup = adPlaybackState.getAdGroup(adGroupIndex); if (adMediaSourceHolder != null && !adMediaSourceHolder.hasMediaSource() - && adIndexInAdGroup < adGroup.uris.length) { - @Nullable Uri adUri = adGroup.uris[adIndexInAdGroup]; - if (adUri != null) { - MediaItem.Builder adMediaItem = new MediaItem.Builder().setUri(adUri); + && adIndexInAdGroup < adGroup.mediaItems.length) { + @Nullable MediaItem adMediaItem = adGroup.mediaItems[adIndexInAdGroup]; + if (adMediaItem != null) { // Propagate the content's DRM config into the ad media source. if (contentDrmConfiguration != null) { - adMediaItem.setDrmConfiguration(contentDrmConfiguration); + adMediaItem = + adMediaItem.buildUpon().setDrmConfiguration(contentDrmConfiguration).build(); } - MediaSource adMediaSource = adMediaSourceFactory.createMediaSource(adMediaItem.build()); - adMediaSourceHolder.initializeWithMediaSource(adMediaSource, adUri); + MediaSource adMediaSource = adMediaSourceFactory.createMediaSource(adMediaItem); + adMediaSourceHolder.initializeWithMediaSource(adMediaSource, adMediaItem); } } } @@ -432,10 +431,10 @@ public final class AdsMediaSource extends CompositeMediaSource { private final class AdPrepareListener implements MaskingMediaPeriod.PrepareListener { - private final Uri adUri; + private final MediaItem adMediaItem; - public AdPrepareListener(Uri adUri) { - this.adUri = adUri; + public AdPrepareListener(MediaItem adMediaItem) { + this.adMediaItem = adMediaItem; } @Override @@ -454,7 +453,7 @@ public final class AdsMediaSource extends CompositeMediaSource { .loadError( new LoadEventInfo( LoadEventInfo.getNewId(), - new DataSpec(adUri), + new DataSpec(checkNotNull(adMediaItem.localConfiguration).uri), /* elapsedRealtimeMs= */ SystemClock.elapsedRealtime()), C.DATA_TYPE_AD, AdLoadException.createForAd(exception), @@ -474,7 +473,7 @@ public final class AdsMediaSource extends CompositeMediaSource { private final MediaPeriodId id; private final List activeMediaPeriods; - private @MonotonicNonNull Uri adUri; + private @MonotonicNonNull MediaItem adMediaItem; private @MonotonicNonNull MediaSource adMediaSource; private @MonotonicNonNull Timeline timeline; @@ -483,13 +482,13 @@ public final class AdsMediaSource extends CompositeMediaSource { activeMediaPeriods = new ArrayList<>(); } - public void initializeWithMediaSource(MediaSource adMediaSource, Uri adUri) { + public void initializeWithMediaSource(MediaSource adMediaSource, MediaItem adMediaItem) { this.adMediaSource = adMediaSource; - this.adUri = adUri; + this.adMediaItem = adMediaItem; for (int i = 0; i < activeMediaPeriods.size(); i++) { MaskingMediaPeriod maskingMediaPeriod = activeMediaPeriods.get(i); maskingMediaPeriod.setMediaSource(adMediaSource); - maskingMediaPeriod.setPrepareListener(new AdPrepareListener(adUri)); + maskingMediaPeriod.setPrepareListener(new AdPrepareListener(adMediaItem)); } prepareChildSource(id, adMediaSource); } @@ -501,7 +500,7 @@ public final class AdsMediaSource extends CompositeMediaSource { activeMediaPeriods.add(maskingMediaPeriod); if (adMediaSource != null) { maskingMediaPeriod.setMediaSource(adMediaSource); - maskingMediaPeriod.setPrepareListener(new AdPrepareListener(checkNotNull(adUri))); + maskingMediaPeriod.setPrepareListener(new AdPrepareListener(checkNotNull(adMediaItem))); } if (timeline != null) { Object periodUid = timeline.getUidOfPeriod(/* periodIndex= */ 0); diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java index 0b3a2b0b7e..98548045e9 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java @@ -4989,8 +4989,10 @@ public final class ExoPlayerTest { new AdPlaybackState(/* adsId= */ new Object(), /* adGroupTimesUs...= */ 0); adPlaybackState = adPlaybackState.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1); adPlaybackState = - adPlaybackState.withAvailableAdUri( - /* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, Uri.parse("https://google.com/ad")); + adPlaybackState.withAvailableAdMediaItem( + /* adGroupIndex= */ 0, + /* adIndexInAdGroup= */ 0, + MediaItem.fromUri("https://google.com/ad")); long[][] durationsUs = new long[1][]; durationsUs[0] = new long[] {Util.msToUs(adDurationMs)}; adPlaybackState = adPlaybackState.withAdDurationsUs(durationsUs); @@ -5091,8 +5093,10 @@ public final class ExoPlayerTest { new AdPlaybackState(/* adsId= */ new Object(), /* adGroupTimesUs...= */ 0); adPlaybackState = adPlaybackState.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1); adPlaybackState = - adPlaybackState.withAvailableAdUri( - /* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, Uri.parse("https://google.com/ad")); + adPlaybackState.withAvailableAdMediaItem( + /* adGroupIndex= */ 0, + /* adIndexInAdGroup= */ 0, + MediaItem.fromUri("https://google.com/ad")); long[][] durationsUs = new long[1][]; durationsUs[0] = new long[] {Util.msToUs(adDurationMs)}; adPlaybackState = adPlaybackState.withAdDurationsUs(durationsUs); @@ -5173,10 +5177,10 @@ public final class ExoPlayerTest { AdPlaybackState adPlaybackState = new AdPlaybackState(/* adsId= */ new Object(), /* adGroupTimesUs...= */ 0) .withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1) - .withAvailableAdUri( + .withAvailableAdMediaItem( /* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, - Uri.parse("https://google.com/ad")); + MediaItem.fromUri("https://google.com/ad")); long[][] durationsUs = new long[1][]; durationsUs[0] = new long[] {Util.msToUs(adDurationMs)}; adPlaybackState = adPlaybackState.withAdDurationsUs(durationsUs); @@ -9505,10 +9509,10 @@ public final class ExoPlayerTest { AdPlaybackState adPlaybackState = new AdPlaybackState(/* adsId= */ new Object(), /* adGroupTimesUs...= */ 0) .withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1) - .withAvailableAdUri( + .withAvailableAdMediaItem( /* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, - Uri.parse("https://google.com/ad")) + MediaItem.fromUri("https://google.com/ad")) .withAdDurationsUs(/* adDurationUs= */ new long[][] {{Util.msToUs(4_000)}}); Timeline adTimeline = new FakeTimeline( diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaPeriodQueueTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaPeriodQueueTest.java index ae35ac47b6..f0ea953a01 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaPeriodQueueTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaPeriodQueueTest.java @@ -28,7 +28,6 @@ import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.robolectric.Shadows.shadowOf; -import android.net.Uri; import android.os.Looper; import android.util.Pair; import androidx.annotation.Nullable; @@ -77,7 +76,7 @@ public final class MediaPeriodQueueTest { private static final long FIRST_AD_START_TIME_US = 10 * C.MICROS_PER_SECOND; private static final long SECOND_AD_START_TIME_US = 20 * C.MICROS_PER_SECOND; - private static final Uri AD_URI = Uri.parse("https://google.com/empty"); + private static final MediaItem AD_MEDIA_ITEM = MediaItem.fromUri("https://google.com/empty"); private static final Timeline CONTENT_TIMELINE = new SinglePeriodTimeline( CONTENT_DURATION_US, @@ -85,7 +84,7 @@ public final class MediaPeriodQueueTest { /* isDynamic= */ false, /* useLiveConfiguration= */ false, /* manifest= */ null, - MediaItem.fromUri(AD_URI)); + AD_MEDIA_ITEM); private MediaPeriodQueue mediaPeriodQueue; private AdPlaybackState adPlaybackState; @@ -1487,7 +1486,7 @@ public final class MediaPeriodQueueTest { adPlaybackState = adPlaybackState .withAdCount(adGroupIndex, /* adCount= */ 1) - .withAvailableAdUri(adGroupIndex, /* adIndexInAdGroup= */ 0, AD_URI) + .withAvailableAdMediaItem(adGroupIndex, /* adIndexInAdGroup= */ 0, AD_MEDIA_ITEM) .withAdDurationsUs(newDurations); updateTimeline(); } diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/ads/AdsMediaSourceTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/ads/AdsMediaSourceTest.java index 32e158ba14..b81679ad5d 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/ads/AdsMediaSourceTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/ads/AdsMediaSourceTest.java @@ -89,8 +89,10 @@ public final class AdsMediaSourceTest { new AdPlaybackState(/* adsId= */ new Object(), /* adGroupTimesUs...= */ 0) .withContentDurationUs(CONTENT_DURATION_US) .withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1) - .withAvailableAdUri( - /* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, Uri.parse("https://google.com/ad")) + .withAvailableAdMediaItem( + /* adGroupIndex= */ 0, + /* adIndexInAdGroup= */ 0, + MediaItem.fromUri("https://google.com/ad")) .withPlayedAd(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0) .withAdResumePositionUs(/* adResumePositionUs= */ 0); diff --git a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/AdTagLoader.java b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/AdTagLoader.java index c1dd3737a3..cf99362415 100644 --- a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/AdTagLoader.java +++ b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/AdTagLoader.java @@ -27,7 +27,6 @@ import static java.lang.Math.max; import static java.lang.annotation.ElementType.TYPE_USE; import android.content.Context; -import android.net.Uri; import android.os.Handler; import android.os.SystemClock; import android.view.ViewGroup; @@ -37,6 +36,7 @@ import androidx.media3.common.AdOverlayInfo; import androidx.media3.common.AdPlaybackState; import androidx.media3.common.AdViewProvider; import androidx.media3.common.C; +import androidx.media3.common.MediaItem; import androidx.media3.common.MediaLibraryInfo; import androidx.media3.common.PlaybackException; import androidx.media3.common.Player; @@ -980,9 +980,10 @@ import java.util.Map; } } - Uri adUri = Uri.parse(adMediaInfo.getUrl()); + MediaItem.Builder adMediaItem = new MediaItem.Builder().setUri(adMediaInfo.getUrl()); adPlaybackState = - adPlaybackState.withAvailableAdUri(adInfo.adGroupIndex, adInfo.adIndexInAdGroup, adUri); + adPlaybackState.withAvailableAdMediaItem( + adInfo.adGroupIndex, adInfo.adIndexInAdGroup, adMediaItem.build()); updateAdPlaybackState(); } diff --git a/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/ImaAdsLoaderTest.java b/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/ImaAdsLoaderTest.java index 753be43218..895ae72f8e 100644 --- a/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/ImaAdsLoaderTest.java +++ b/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/ImaAdsLoaderTest.java @@ -42,6 +42,7 @@ import androidx.media3.common.AdOverlayInfo; import androidx.media3.common.AdPlaybackState; import androidx.media3.common.AdViewProvider; import androidx.media3.common.C; +import androidx.media3.common.MediaItem; import androidx.media3.common.MimeTypes; import androidx.media3.common.PlaybackException; import androidx.media3.common.Player; @@ -108,6 +109,7 @@ public final class ImaAdsLoaderTest { private static final long CONTENT_PERIOD_DURATION_US = CONTENT_TIMELINE.getPeriod(/* periodIndex= */ 0, new Period()).durationUs; private static final Uri TEST_URI = Uri.parse("https://www.google.com"); + private static final MediaItem TEST_MEDIA_ITEM = MediaItem.fromUri(TEST_URI); private static final DataSpec TEST_DATA_SPEC = new DataSpec(TEST_URI); private static final Object TEST_ADS_ID = new Object(); private static final AdMediaInfo TEST_AD_MEDIA_INFO = new AdMediaInfo("https://www.google.com"); @@ -315,7 +317,8 @@ public final class ImaAdsLoaderTest { new AdPlaybackState(TEST_ADS_ID, /* adGroupTimesUs...= */ 0) .withContentDurationUs(CONTENT_PERIOD_DURATION_US) .withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1) - .withAvailableAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, TEST_URI) + .withAvailableAdMediaItem( + /* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, TEST_MEDIA_ITEM) .withAdDurationsUs(new long[][] {{TEST_AD_DURATION_US}}) .withPlayedAd(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0) .withAdResumePositionUs(/* adResumePositionUs= */ 0)); @@ -1063,7 +1066,8 @@ public final class ImaAdsLoaderTest { new AdPlaybackState(TEST_ADS_ID, getAdGroupTimesUsForCuePoints(cuePoints)) .withContentDurationUs(CONTENT_PERIOD_DURATION_US) .withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1) - .withAvailableAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, TEST_URI) + .withAvailableAdMediaItem( + /* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, TEST_MEDIA_ITEM) .withAdDurationsUs(new long[][] {{TEST_AD_DURATION_US}})); } @@ -1117,7 +1121,8 @@ public final class ImaAdsLoaderTest { new AdPlaybackState(TEST_ADS_ID, /* adGroupTimesUs...= */ 0) .withContentDurationUs(CONTENT_PERIOD_DURATION_US) .withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1) - .withAvailableAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, TEST_URI) + .withAvailableAdMediaItem( + /* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, TEST_MEDIA_ITEM) .withAdDurationsUs(new long[][] {{TEST_AD_DURATION_US}}) .withPlayedAd(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0) .withAdResumePositionUs(/* adResumePositionUs= */ 0)); @@ -1184,7 +1189,8 @@ public final class ImaAdsLoaderTest { new AdPlaybackState(TEST_ADS_ID, /* adGroupTimesUs...= */ 0) .withContentDurationUs(CONTENT_PERIOD_DURATION_US) .withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1) - .withAvailableAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, TEST_URI) + .withAvailableAdMediaItem( + /* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, TEST_MEDIA_ITEM) .withAdDurationsUs(new long[][] {{TEST_AD_DURATION_US}}) .withPlayedAd(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0) .withAdResumePositionUs(/* adResumePositionUs= */ 0)); @@ -1404,7 +1410,7 @@ public final class ImaAdsLoaderTest { long[][] adDurationsUs = new long[adPlaybackState.adGroupCount][]; for (int adGroupIndex = 0; adGroupIndex < adPlaybackState.adGroupCount; adGroupIndex++) { adDurationsUs[adGroupIndex] = - new long[adPlaybackState.getAdGroup(adGroupIndex).uris.length]; + new long[adPlaybackState.getAdGroup(adGroupIndex).mediaItems.length]; Arrays.fill(adDurationsUs[adGroupIndex], TEST_AD_DURATION_US); } adPlaybackState = adPlaybackState.withAdDurationsUs(adDurationsUs); diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeTimeline.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeTimeline.java index ab37db5e38..1e4e7c9dce 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeTimeline.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeTimeline.java @@ -295,10 +295,10 @@ public final class FakeTimeline extends Timeline { adPlaybackState = adPlaybackState.withAdCount(/* adGroupIndex= */ i, adsPerAdGroup); for (int j = 0; j < adsPerAdGroup; j++) { adPlaybackState = - adPlaybackState.withAvailableAdUri( + adPlaybackState.withAvailableAdMediaItem( /* adGroupIndex= */ i, /* adIndexInAdGroup= */ j, - Uri.parse("https://example.com/ad/" + i + "/" + j)); + MediaItem.fromUri("https://example.com/ad/" + i + "/" + j)); } adDurationsUs[i] = new long[adsPerAdGroup]; Arrays.fill(adDurationsUs[i], AD_DURATION_US);