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;
|
||||
|
||||
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.media3.common.C;
|
||||
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.LoadErrorHandlingPolicy;
|
||||
import com.google.common.base.Charsets;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 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 Timeline timeline;
|
||||
private final long timelineDurationUs;
|
||||
|
||||
@GuardedBy("this")
|
||||
private MediaItem mediaItem;
|
||||
|
||||
private ExternallyLoadedMediaSource(MediaItem mediaItem, long timelineDurationUs) {
|
||||
this.mediaItem = mediaItem;
|
||||
this.timeline =
|
||||
this.timelineDurationUs = timelineDurationUs;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepareSourceInternal(@Nullable TransferListener mediaTransferListener) {
|
||||
Timeline timeline =
|
||||
new SinglePeriodTimeline(
|
||||
timelineDurationUs,
|
||||
/* isSeekable= */ true,
|
||||
/* isDynamic= */ false,
|
||||
/* useLiveConfiguration= */ false,
|
||||
/* manifest= */ null,
|
||||
mediaItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepareSourceInternal(@Nullable TransferListener mediaTransferListener) {
|
||||
getMediaItem());
|
||||
refreshSourceInfo(timeline);
|
||||
}
|
||||
|
||||
@ -107,10 +113,26 @@ public final class ExternallyLoadedMediaSource extends BaseMediaSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaItem getMediaItem() {
|
||||
public synchronized MediaItem getMediaItem() {
|
||||
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
|
||||
public void maybeThrowSourceInfoRefreshError() {
|
||||
// Do nothing.
|
||||
@ -118,6 +140,7 @@ public final class ExternallyLoadedMediaSource extends BaseMediaSource {
|
||||
|
||||
@Override
|
||||
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) {
|
||||
MediaItem mediaItem = getMediaItem();
|
||||
checkNotNull(mediaItem.localConfiguration);
|
||||
checkNotNull(
|
||||
mediaItem.localConfiguration.mimeType, "Externally loaded mediaItems require a MIME type.");
|
||||
@ -126,5 +149,7 @@ public final class ExternallyLoadedMediaSource extends BaseMediaSource {
|
||||
}
|
||||
|
||||
@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