Support updating the mediaItem in ExternallyLoadedMediaSource
PiperOrigin-RevId: 571308986
This commit is contained in:
parent
addfd3e986
commit
e5010e3d71
@ -16,7 +16,9 @@
|
|||||||
package androidx.media3.exoplayer.source;
|
package androidx.media3.exoplayer.source;
|
||||||
|
|
||||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
|
import static androidx.media3.common.util.Util.msToUs;
|
||||||
|
|
||||||
|
import androidx.annotation.GuardedBy;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
@ -27,6 +29,7 @@ import androidx.media3.exoplayer.drm.DrmSessionManagerProvider;
|
|||||||
import androidx.media3.exoplayer.upstream.Allocator;
|
import androidx.media3.exoplayer.upstream.Allocator;
|
||||||
import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy;
|
import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy;
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link MediaSource} for media loaded outside of the usual ExoPlayer loading mechanism.
|
* A {@link MediaSource} for media loaded outside of the usual ExoPlayer loading mechanism.
|
||||||
@ -81,23 +84,26 @@ public final class ExternallyLoadedMediaSource extends BaseMediaSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final MediaItem mediaItem;
|
private final long timelineDurationUs;
|
||||||
private final Timeline timeline;
|
|
||||||
|
@GuardedBy("this")
|
||||||
|
private MediaItem mediaItem;
|
||||||
|
|
||||||
private ExternallyLoadedMediaSource(MediaItem mediaItem, long timelineDurationUs) {
|
private ExternallyLoadedMediaSource(MediaItem mediaItem, long timelineDurationUs) {
|
||||||
this.mediaItem = mediaItem;
|
this.mediaItem = mediaItem;
|
||||||
this.timeline =
|
this.timelineDurationUs = timelineDurationUs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void prepareSourceInternal(@Nullable TransferListener mediaTransferListener) {
|
||||||
|
Timeline timeline =
|
||||||
new SinglePeriodTimeline(
|
new SinglePeriodTimeline(
|
||||||
timelineDurationUs,
|
timelineDurationUs,
|
||||||
/* isSeekable= */ true,
|
/* isSeekable= */ true,
|
||||||
/* isDynamic= */ false,
|
/* isDynamic= */ false,
|
||||||
/* useLiveConfiguration= */ false,
|
/* useLiveConfiguration= */ false,
|
||||||
/* manifest= */ null,
|
/* manifest= */ null,
|
||||||
mediaItem);
|
getMediaItem());
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void prepareSourceInternal(@Nullable TransferListener mediaTransferListener) {
|
|
||||||
refreshSourceInfo(timeline);
|
refreshSourceInfo(timeline);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,10 +113,26 @@ public final class ExternallyLoadedMediaSource extends BaseMediaSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MediaItem getMediaItem() {
|
public synchronized MediaItem getMediaItem() {
|
||||||
return mediaItem;
|
return mediaItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized boolean canUpdateMediaItem(MediaItem mediaItem) {
|
||||||
|
@Nullable MediaItem.LocalConfiguration newConfiguration = mediaItem.localConfiguration;
|
||||||
|
MediaItem.LocalConfiguration oldConfiguration = checkNotNull(getMediaItem().localConfiguration);
|
||||||
|
return newConfiguration != null
|
||||||
|
&& newConfiguration.uri.equals(oldConfiguration.uri)
|
||||||
|
&& Objects.equals(newConfiguration.mimeType, oldConfiguration.mimeType)
|
||||||
|
&& (newConfiguration.imageDurationMs == C.TIME_UNSET
|
||||||
|
|| msToUs(newConfiguration.imageDurationMs) == timelineDurationUs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void updateMediaItem(MediaItem mediaItem) {
|
||||||
|
this.mediaItem = mediaItem;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void maybeThrowSourceInfoRefreshError() {
|
public void maybeThrowSourceInfoRefreshError() {
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
@ -118,6 +140,7 @@ public final class ExternallyLoadedMediaSource extends BaseMediaSource {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) {
|
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) {
|
||||||
|
MediaItem mediaItem = getMediaItem();
|
||||||
checkNotNull(mediaItem.localConfiguration);
|
checkNotNull(mediaItem.localConfiguration);
|
||||||
checkNotNull(
|
checkNotNull(
|
||||||
mediaItem.localConfiguration.mimeType, "Externally loaded mediaItems require a MIME type.");
|
mediaItem.localConfiguration.mimeType, "Externally loaded mediaItems require a MIME type.");
|
||||||
@ -126,5 +149,7 @@ public final class ExternallyLoadedMediaSource extends BaseMediaSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void releasePeriod(MediaPeriod mediaPeriod) {}
|
public void releasePeriod(MediaPeriod mediaPeriod) {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 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 androidx.media3.exoplayer.source;
|
||||||
|
|
||||||
|
import static androidx.media3.common.util.Util.msToUs;
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import androidx.media3.common.C;
|
||||||
|
import androidx.media3.common.MediaItem;
|
||||||
|
import androidx.media3.common.MimeTypes;
|
||||||
|
import androidx.media3.common.Timeline;
|
||||||
|
import androidx.media3.exoplayer.analytics.PlayerId;
|
||||||
|
import androidx.media3.test.utils.TestUtil;
|
||||||
|
import androidx.media3.test.utils.robolectric.RobolectricUtil;
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
/** Unit test for {@link ProgressiveMediaSource}. */
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class ExternallyLoadedMediaSourceTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void canUpdateMediaItem_withIrrelevantFieldsChanged_returnsTrue() {
|
||||||
|
MediaItem initialMediaItem =
|
||||||
|
new MediaItem.Builder()
|
||||||
|
.setUri("http://test.test")
|
||||||
|
.setImageDurationMs(5 * C.MILLIS_PER_SECOND)
|
||||||
|
.setMimeType(MimeTypes.APPLICATION_EXTERNALLY_LOADED_IMAGE)
|
||||||
|
.build();
|
||||||
|
MediaItem updatedMediaItem =
|
||||||
|
TestUtil.buildFullyCustomizedMediaItem()
|
||||||
|
.buildUpon()
|
||||||
|
.setUri("http://test.test")
|
||||||
|
.setImageDurationMs(5 * C.MILLIS_PER_SECOND)
|
||||||
|
.setMimeType(MimeTypes.APPLICATION_EXTERNALLY_LOADED_IMAGE)
|
||||||
|
.build();
|
||||||
|
MediaSource mediaSource = buildMediaSource(initialMediaItem);
|
||||||
|
|
||||||
|
boolean canUpdateMediaItem = mediaSource.canUpdateMediaItem(updatedMediaItem);
|
||||||
|
|
||||||
|
assertThat(canUpdateMediaItem).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void canUpdateMediaItem_withNullLocalConfiguration_returnsFalse() {
|
||||||
|
MediaItem initialMediaItem =
|
||||||
|
new MediaItem.Builder()
|
||||||
|
.setImageDurationMs(5 * C.MILLIS_PER_SECOND)
|
||||||
|
.setUri("http://test.test")
|
||||||
|
.build();
|
||||||
|
MediaItem updatedMediaItem =
|
||||||
|
new MediaItem.Builder()
|
||||||
|
.setImageDurationMs(5 * C.MILLIS_PER_SECOND)
|
||||||
|
.setMediaId("id")
|
||||||
|
.build();
|
||||||
|
MediaSource mediaSource = buildMediaSource(initialMediaItem);
|
||||||
|
|
||||||
|
boolean canUpdateMediaItem = mediaSource.canUpdateMediaItem(updatedMediaItem);
|
||||||
|
|
||||||
|
assertThat(canUpdateMediaItem).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void canUpdateMediaItem_withChangedUri_returnsFalse() {
|
||||||
|
MediaItem initialMediaItem =
|
||||||
|
new MediaItem.Builder()
|
||||||
|
.setImageDurationMs(5 * C.MILLIS_PER_SECOND)
|
||||||
|
.setUri("http://test.test")
|
||||||
|
.build();
|
||||||
|
MediaItem updatedMediaItem =
|
||||||
|
new MediaItem.Builder()
|
||||||
|
.setImageDurationMs(5 * C.MILLIS_PER_SECOND)
|
||||||
|
.setUri("http://test2.test")
|
||||||
|
.build();
|
||||||
|
MediaSource mediaSource = buildMediaSource(initialMediaItem);
|
||||||
|
|
||||||
|
boolean canUpdateMediaItem = mediaSource.canUpdateMediaItem(updatedMediaItem);
|
||||||
|
|
||||||
|
assertThat(canUpdateMediaItem).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateMediaItem_createsTimelineWithUpdatedItem() throws Exception {
|
||||||
|
MediaItem initialMediaItem =
|
||||||
|
new MediaItem.Builder()
|
||||||
|
.setUri("http://test.test")
|
||||||
|
.setImageDurationMs(5 * C.MILLIS_PER_SECOND)
|
||||||
|
.setTag("tag1")
|
||||||
|
.build();
|
||||||
|
MediaItem updatedMediaItem =
|
||||||
|
new MediaItem.Builder()
|
||||||
|
.setUri("http://test.test")
|
||||||
|
.setImageDurationMs(5 * C.MILLIS_PER_SECOND)
|
||||||
|
.setTag("tag2")
|
||||||
|
.build();
|
||||||
|
MediaSource mediaSource = buildMediaSource(initialMediaItem);
|
||||||
|
AtomicReference<Timeline> timelineReference = new AtomicReference<>();
|
||||||
|
|
||||||
|
mediaSource.updateMediaItem(updatedMediaItem);
|
||||||
|
mediaSource.prepareSource(
|
||||||
|
(source, timeline) -> timelineReference.set(timeline),
|
||||||
|
/* mediaTransferListener= */ null,
|
||||||
|
PlayerId.UNSET);
|
||||||
|
RobolectricUtil.runMainLooperUntil(() -> timelineReference.get() != null);
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
timelineReference
|
||||||
|
.get()
|
||||||
|
.getWindow(/* windowIndex= */ 0, new Timeline.Window())
|
||||||
|
.mediaItem)
|
||||||
|
.isEqualTo(updatedMediaItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MediaSource buildMediaSource(MediaItem mediaItem) {
|
||||||
|
return new ExternallyLoadedMediaSource.Factory(
|
||||||
|
msToUs(mediaItem.localConfiguration.imageDurationMs))
|
||||||
|
.createMediaSource(mediaItem);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user