blob: ae0c431bd371c1a93e57e1f6268bb22ff983b3a6 [file] [log] [blame]
/*
* Copyright (C) 2016 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.source;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import androidx.annotation.Nullable;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.Timeline.Period;
import com.google.android.exoplayer2.Timeline.Window;
import com.google.android.exoplayer2.source.ClippingMediaSource.IllegalClippingException;
import com.google.android.exoplayer2.source.MaskingMediaSource.DummyTimeline;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher;
import com.google.android.exoplayer2.testutil.FakeMediaPeriod;
import com.google.android.exoplayer2.testutil.FakeMediaSource;
import com.google.android.exoplayer2.testutil.FakeTimeline;
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
import com.google.android.exoplayer2.testutil.MediaSourceTestRunner;
import com.google.android.exoplayer2.testutil.TimelineAsserts;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.util.Util;
import java.io.IOException;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.annotation.LooperMode;
import org.robolectric.annotation.LooperMode.Mode;
/** Unit tests for {@link ClippingMediaSource}. */
@RunWith(AndroidJUnit4.class)
@LooperMode(Mode.PAUSED)
public final class ClippingMediaSourceTest {
private static final long TEST_PERIOD_DURATION_US = 1000000;
private static final long TEST_CLIP_AMOUNT_US = 300000;
private Window window;
private Period period;
@Before
public void setUp() throws Exception {
window = new Timeline.Window();
period = new Timeline.Period();
}
@Test
public void noClipping() throws IOException {
Timeline timeline =
new SinglePeriodTimeline(
TEST_PERIOD_DURATION_US,
/* isSeekable= */ true,
/* isDynamic= */ false,
/* isLive= */ false);
Timeline clippedTimeline = getClippedTimeline(timeline, 0, TEST_PERIOD_DURATION_US);
assertThat(clippedTimeline.getWindowCount()).isEqualTo(1);
assertThat(clippedTimeline.getPeriodCount()).isEqualTo(1);
assertThat(clippedTimeline.getWindow(0, window).getDurationUs())
.isEqualTo(TEST_PERIOD_DURATION_US);
assertThat(clippedTimeline.getPeriod(0, period).getDurationUs())
.isEqualTo(TEST_PERIOD_DURATION_US);
}
@Test
public void clippingUnseekableWindowThrows() throws IOException {
Timeline timeline =
new SinglePeriodTimeline(
TEST_PERIOD_DURATION_US,
/* isSeekable= */ false,
/* isDynamic= */ false,
/* isLive= */ false);
// If the unseekable window isn't clipped, clipping succeeds.
getClippedTimeline(timeline, 0, TEST_PERIOD_DURATION_US);
try {
// If the unseekable window is clipped, clipping fails.
getClippedTimeline(timeline, 1, TEST_PERIOD_DURATION_US);
fail("Expected clipping to fail.");
} catch (IllegalClippingException e) {
assertThat(e.reason).isEqualTo(IllegalClippingException.REASON_NOT_SEEKABLE_TO_START);
}
}
@Test
public void clippingUnseekableWindowWithUnknownDurationThrows() throws IOException {
Timeline timeline =
new SinglePeriodTimeline(
/* durationUs= */ C.TIME_UNSET,
/* isSeekable= */ false,
/* isDynamic= */ false,
/* isLive= */ false);
// If the unseekable window isn't clipped, clipping succeeds.
getClippedTimeline(timeline, /* startUs= */ 0, TEST_PERIOD_DURATION_US);
try {
// If the unseekable window is clipped, clipping fails.
getClippedTimeline(timeline, /* startUs= */ 1, TEST_PERIOD_DURATION_US);
fail("Expected clipping to fail.");
} catch (IllegalClippingException e) {
assertThat(e.reason).isEqualTo(IllegalClippingException.REASON_NOT_SEEKABLE_TO_START);
}
}
@Test
public void clippingStart() throws IOException {
Timeline timeline =
new SinglePeriodTimeline(
TEST_PERIOD_DURATION_US,
/* isSeekable= */ true,
/* isDynamic= */ false,
/* isLive= */ false);
Timeline clippedTimeline =
getClippedTimeline(timeline, TEST_CLIP_AMOUNT_US, TEST_PERIOD_DURATION_US);
assertThat(clippedTimeline.getWindow(0, window).getDurationUs())
.isEqualTo(TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US);
assertThat(clippedTimeline.getPeriod(0, period).getDurationUs())
.isEqualTo(TEST_PERIOD_DURATION_US);
}
@Test
public void clippingEnd() throws IOException {
Timeline timeline =
new SinglePeriodTimeline(
TEST_PERIOD_DURATION_US,
/* isSeekable= */ true,
/* isDynamic= */ false,
/* isLive= */ false);
Timeline clippedTimeline =
getClippedTimeline(timeline, 0, TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US);
assertThat(clippedTimeline.getWindow(0, window).getDurationUs())
.isEqualTo(TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US);
assertThat(clippedTimeline.getPeriod(0, period).getDurationUs())
.isEqualTo(TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US);
}
@Test
public void clippingStartAndEndInitial() throws IOException {
// Timeline that's dynamic and not seekable. A child source might report such a timeline prior
// to it having loaded sufficient data to establish its duration and seekability. Such timelines
// should not result in clipping failure.
Timeline timeline = new DummyTimeline(/* tag= */ null);
Timeline clippedTimeline =
getClippedTimeline(
timeline, TEST_CLIP_AMOUNT_US, TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US * 2);
assertThat(clippedTimeline.getWindow(0, window).getDurationUs())
.isEqualTo(TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US * 3);
assertThat(clippedTimeline.getPeriod(0, period).getDurationUs())
.isEqualTo(TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US * 2);
}
@Test
public void clippingToEndOfSourceWithDurationSetsDuration() throws IOException {
// Create a child timeline that has a known duration.
Timeline timeline =
new SinglePeriodTimeline(
/* durationUs= */ TEST_PERIOD_DURATION_US,
/* isSeekable= */ true,
/* isDynamic= */ false,
/* isLive= */ false);
// When clipping to the end, the clipped timeline should also have a duration.
Timeline clippedTimeline =
getClippedTimeline(timeline, TEST_CLIP_AMOUNT_US, C.TIME_END_OF_SOURCE);
assertThat(clippedTimeline.getWindow(/* windowIndex= */ 0, window).getDurationUs())
.isEqualTo(TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US);
}
@Test
public void clippingToEndOfSourceWithUnsetDurationDoesNotSetDuration() throws IOException {
// Create a child timeline that has an unknown duration.
Timeline timeline =
new SinglePeriodTimeline(
/* durationUs= */ C.TIME_UNSET,
/* isSeekable= */ true,
/* isDynamic= */ false,
/* isLive= */ false);
// When clipping to the end, the clipped timeline should also have an unset duration.
Timeline clippedTimeline =
getClippedTimeline(timeline, TEST_CLIP_AMOUNT_US, C.TIME_END_OF_SOURCE);
assertThat(clippedTimeline.getWindow(/* windowIndex= */ 0, window).getDurationUs())
.isEqualTo(C.TIME_UNSET);
}
@Test
public void clippingStartAndEnd() throws IOException {
Timeline timeline =
new SinglePeriodTimeline(
TEST_PERIOD_DURATION_US,
/* isSeekable= */ true,
/* isDynamic= */ false,
/* isLive= */ false);
Timeline clippedTimeline =
getClippedTimeline(
timeline, TEST_CLIP_AMOUNT_US, TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US * 2);
assertThat(clippedTimeline.getWindow(0, window).getDurationUs())
.isEqualTo(TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US * 3);
assertThat(clippedTimeline.getPeriod(0, period).getDurationUs())
.isEqualTo(TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US * 2);
}
@Test
public void clippingFromDefaultPosition() throws IOException {
Timeline timeline =
new SinglePeriodTimeline(
/* periodDurationUs= */ 3 * TEST_PERIOD_DURATION_US,
/* windowDurationUs= */ TEST_PERIOD_DURATION_US,
/* windowPositionInPeriodUs= */ TEST_PERIOD_DURATION_US,
/* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US,
/* isSeekable= */ true,
/* isDynamic= */ true,
/* isLive= */ true,
/* manifest= */ null,
/* tag= */ null);
Timeline clippedTimeline = getClippedTimeline(timeline, /* durationUs= */ TEST_CLIP_AMOUNT_US);
assertThat(clippedTimeline.getWindow(0, window).getDurationUs()).isEqualTo(TEST_CLIP_AMOUNT_US);
assertThat(clippedTimeline.getWindow(0, window).getDefaultPositionUs()).isEqualTo(0);
assertThat(clippedTimeline.getWindow(0, window).getPositionInFirstPeriodUs())
.isEqualTo(TEST_PERIOD_DURATION_US + TEST_CLIP_AMOUNT_US);
assertThat(clippedTimeline.getPeriod(0, period).getDurationUs())
.isEqualTo(TEST_PERIOD_DURATION_US + 2 * TEST_CLIP_AMOUNT_US);
}
@Test
public void allowDynamicUpdatesWithOverlappingLiveWindow() throws IOException {
Timeline timeline1 =
new SinglePeriodTimeline(
/* periodDurationUs= */ 2 * TEST_PERIOD_DURATION_US,
/* windowDurationUs= */ TEST_PERIOD_DURATION_US,
/* windowPositionInPeriodUs= */ TEST_PERIOD_DURATION_US,
/* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US,
/* isSeekable= */ true,
/* isDynamic= */ true,
/* isLive= */ true,
/* manifest= */ null,
/* tag= */ null);
Timeline timeline2 =
new SinglePeriodTimeline(
/* periodDurationUs= */ 3 * TEST_PERIOD_DURATION_US,
/* windowDurationUs= */ TEST_PERIOD_DURATION_US,
/* windowPositionInPeriodUs= */ 2 * TEST_PERIOD_DURATION_US,
/* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US,
/* isSeekable= */ true,
/* isDynamic= */ true,
/* isLive= */ true,
/* manifest= */ null,
/* tag= */ null);
Timeline[] clippedTimelines =
getClippedTimelines(
/* startUs= */ 0,
/* endUs= */ TEST_PERIOD_DURATION_US,
/* allowDynamicUpdates= */ true,
/* fromDefaultPosition= */ true,
timeline1,
timeline2);
assertThat(clippedTimelines[0].getWindow(0, window).getDurationUs())
.isEqualTo(TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US);
assertThat(clippedTimelines[0].getWindow(0, window).getDefaultPositionUs()).isEqualTo(0);
assertThat(clippedTimelines[0].getWindow(0, window).isDynamic).isTrue();
assertThat(clippedTimelines[0].getWindow(0, window).getPositionInFirstPeriodUs())
.isEqualTo(TEST_PERIOD_DURATION_US + TEST_CLIP_AMOUNT_US);
assertThat(clippedTimelines[0].getPeriod(0, period).getDurationUs())
.isEqualTo(2 * TEST_PERIOD_DURATION_US);
assertThat(clippedTimelines[1].getWindow(0, window).getDurationUs())
.isEqualTo(TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US);
assertThat(clippedTimelines[1].getWindow(0, window).getDefaultPositionUs()).isEqualTo(0);
assertThat(clippedTimelines[1].getWindow(0, window).isDynamic).isTrue();
assertThat(clippedTimelines[1].getWindow(0, window).getPositionInFirstPeriodUs())
.isEqualTo(2 * TEST_PERIOD_DURATION_US + TEST_CLIP_AMOUNT_US);
assertThat(clippedTimelines[1].getPeriod(0, period).getDurationUs())
.isEqualTo(3 * TEST_PERIOD_DURATION_US);
}
@Test
public void allowDynamicUpdatesWithNonOverlappingLiveWindow() throws IOException {
Timeline timeline1 =
new SinglePeriodTimeline(
/* periodDurationUs= */ 2 * TEST_PERIOD_DURATION_US,
/* windowDurationUs= */ TEST_PERIOD_DURATION_US,
/* windowPositionInPeriodUs= */ TEST_PERIOD_DURATION_US,
/* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US,
/* isSeekable= */ true,
/* isDynamic= */ true,
/* isLive= */ true,
/* manifest= */ null,
/* tag= */ null);
Timeline timeline2 =
new SinglePeriodTimeline(
/* periodDurationUs= */ 4 * TEST_PERIOD_DURATION_US,
/* windowDurationUs= */ TEST_PERIOD_DURATION_US,
/* windowPositionInPeriodUs= */ 3 * TEST_PERIOD_DURATION_US,
/* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US,
/* isSeekable= */ true,
/* isDynamic= */ true,
/* isLive= */ true,
/* manifest= */ null,
/* tag= */ null);
Timeline[] clippedTimelines =
getClippedTimelines(
/* startUs= */ 0,
/* endUs= */ TEST_PERIOD_DURATION_US,
/* allowDynamicUpdates= */ true,
/* fromDefaultPosition= */ true,
timeline1,
timeline2);
assertThat(clippedTimelines[0].getWindow(0, window).getDurationUs())
.isEqualTo(TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US);
assertThat(clippedTimelines[0].getWindow(0, window).getDefaultPositionUs()).isEqualTo(0);
assertThat(clippedTimelines[0].getWindow(0, window).isDynamic).isTrue();
assertThat(clippedTimelines[0].getWindow(0, window).getPositionInFirstPeriodUs())
.isEqualTo(TEST_PERIOD_DURATION_US + TEST_CLIP_AMOUNT_US);
assertThat(clippedTimelines[0].getPeriod(0, period).getDurationUs())
.isEqualTo(2 * TEST_PERIOD_DURATION_US);
assertThat(clippedTimelines[1].getWindow(0, window).getDurationUs())
.isEqualTo(TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US);
assertThat(clippedTimelines[1].getWindow(0, window).getDefaultPositionUs()).isEqualTo(0);
assertThat(clippedTimelines[1].getWindow(0, window).isDynamic).isTrue();
assertThat(clippedTimelines[1].getWindow(0, window).getPositionInFirstPeriodUs())
.isEqualTo(3 * TEST_PERIOD_DURATION_US + TEST_CLIP_AMOUNT_US);
assertThat(clippedTimelines[1].getPeriod(0, period).getDurationUs())
.isEqualTo(4 * TEST_PERIOD_DURATION_US);
}
@Test
public void disallowDynamicUpdatesWithOverlappingLiveWindow() throws IOException {
Timeline timeline1 =
new SinglePeriodTimeline(
/* periodDurationUs= */ 2 * TEST_PERIOD_DURATION_US,
/* windowDurationUs= */ TEST_PERIOD_DURATION_US,
/* windowPositionInPeriodUs= */ TEST_PERIOD_DURATION_US,
/* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US,
/* isSeekable= */ true,
/* isDynamic= */ true,
/* isLive= */ true,
/* manifest= */ null,
/* tag= */ null);
Timeline timeline2 =
new SinglePeriodTimeline(
/* periodDurationUs= */ 3 * TEST_PERIOD_DURATION_US,
/* windowDurationUs= */ TEST_PERIOD_DURATION_US,
/* windowPositionInPeriodUs= */ 2 * TEST_PERIOD_DURATION_US,
/* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US,
/* isSeekable= */ true,
/* isDynamic= */ true,
/* isLive= */ true,
/* manifest= */ null,
/* tag= */ null);
Timeline[] clippedTimelines =
getClippedTimelines(
/* startUs= */ 0,
/* endUs= */ TEST_PERIOD_DURATION_US,
/* allowDynamicUpdates= */ false,
/* fromDefaultPosition= */ true,
timeline1,
timeline2);
assertThat(clippedTimelines[0].getWindow(0, window).getDurationUs())
.isEqualTo(TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US);
assertThat(clippedTimelines[0].getWindow(0, window).getDefaultPositionUs()).isEqualTo(0);
assertThat(clippedTimelines[0].getWindow(0, window).isDynamic).isTrue();
assertThat(clippedTimelines[0].getWindow(0, window).getPositionInFirstPeriodUs())
.isEqualTo(TEST_PERIOD_DURATION_US + TEST_CLIP_AMOUNT_US);
assertThat(clippedTimelines[0].getPeriod(0, period).getDurationUs())
.isEqualTo(2 * TEST_PERIOD_DURATION_US);
assertThat(clippedTimelines[1].getWindow(0, window).getDurationUs())
.isEqualTo(TEST_CLIP_AMOUNT_US);
assertThat(clippedTimelines[1].getWindow(0, window).getDefaultPositionUs())
.isEqualTo(TEST_CLIP_AMOUNT_US);
assertThat(clippedTimelines[1].getWindow(0, window).isDynamic).isFalse();
assertThat(clippedTimelines[1].getWindow(0, window).getPositionInFirstPeriodUs())
.isEqualTo(2 * TEST_PERIOD_DURATION_US);
assertThat(clippedTimelines[1].getPeriod(0, period).getDurationUs())
.isEqualTo(2 * TEST_PERIOD_DURATION_US + TEST_CLIP_AMOUNT_US);
}
@Test
public void disallowDynamicUpdatesWithNonOverlappingLiveWindow() throws IOException {
Timeline timeline1 =
new SinglePeriodTimeline(
/* periodDurationUs= */ 2 * TEST_PERIOD_DURATION_US,
/* windowDurationUs= */ TEST_PERIOD_DURATION_US,
/* windowPositionInPeriodUs= */ TEST_PERIOD_DURATION_US,
/* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US,
/* isSeekable= */ true,
/* isDynamic= */ true,
/* isLive= */ true,
/* manifest= */ null,
/* tag= */ null);
Timeline timeline2 =
new SinglePeriodTimeline(
/* periodDurationUs= */ 4 * TEST_PERIOD_DURATION_US,
/* windowDurationUs= */ TEST_PERIOD_DURATION_US,
/* windowPositionInPeriodUs= */ 3 * TEST_PERIOD_DURATION_US,
/* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US,
/* isSeekable= */ true,
/* isDynamic= */ true,
/* isLive= */ true,
/* manifest= */ null,
/* tag= */ null);
Timeline[] clippedTimelines =
getClippedTimelines(
/* startUs= */ 0,
/* endUs= */ TEST_PERIOD_DURATION_US,
/* allowDynamicUpdates= */ false,
/* fromDefaultPosition= */ true,
timeline1,
timeline2);
assertThat(clippedTimelines[0].getWindow(0, window).getDurationUs())
.isEqualTo(TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US);
assertThat(clippedTimelines[0].getWindow(0, window).getDefaultPositionUs()).isEqualTo(0);
assertThat(clippedTimelines[0].getWindow(0, window).isDynamic).isTrue();
assertThat(clippedTimelines[0].getWindow(0, window).getPositionInFirstPeriodUs())
.isEqualTo(TEST_PERIOD_DURATION_US + TEST_CLIP_AMOUNT_US);
assertThat(clippedTimelines[0].getPeriod(0, period).getDurationUs())
.isEqualTo(2 * TEST_PERIOD_DURATION_US);
assertThat(clippedTimelines[1].getWindow(0, window).getDurationUs()).isEqualTo(0);
assertThat(clippedTimelines[1].getWindow(0, window).getDefaultPositionUs()).isEqualTo(0);
assertThat(clippedTimelines[1].getWindow(0, window).isDynamic).isFalse();
assertThat(clippedTimelines[1].getWindow(0, window).getPositionInFirstPeriodUs())
.isEqualTo(3 * TEST_PERIOD_DURATION_US);
assertThat(clippedTimelines[1].getPeriod(0, period).getDurationUs())
.isEqualTo(3 * TEST_PERIOD_DURATION_US);
}
@Test
public void windowAndPeriodIndices() throws IOException {
Timeline timeline =
new FakeTimeline(
new TimelineWindowDefinition(1, 111, true, false, TEST_PERIOD_DURATION_US));
Timeline clippedTimeline =
getClippedTimeline(
timeline, TEST_CLIP_AMOUNT_US, TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US);
TimelineAsserts.assertWindowTags(clippedTimeline, 111);
TimelineAsserts.assertPeriodCounts(clippedTimeline, 1);
TimelineAsserts.assertPreviousWindowIndices(
clippedTimeline, Player.REPEAT_MODE_OFF, false, C.INDEX_UNSET);
TimelineAsserts.assertPreviousWindowIndices(clippedTimeline, Player.REPEAT_MODE_ONE, false, 0);
TimelineAsserts.assertPreviousWindowIndices(clippedTimeline, Player.REPEAT_MODE_ALL, false, 0);
TimelineAsserts.assertNextWindowIndices(
clippedTimeline, Player.REPEAT_MODE_OFF, false, C.INDEX_UNSET);
TimelineAsserts.assertNextWindowIndices(clippedTimeline, Player.REPEAT_MODE_ONE, false, 0);
TimelineAsserts.assertNextWindowIndices(clippedTimeline, Player.REPEAT_MODE_ALL, false, 0);
}
@Test
public void eventTimeWithinClippedRange() throws IOException {
MediaLoadData mediaLoadData =
getClippingMediaSourceMediaLoadData(
/* clippingStartUs= */ TEST_CLIP_AMOUNT_US,
/* clippingEndUs= */ TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US,
/* eventStartUs= */ TEST_CLIP_AMOUNT_US + 1000,
/* eventEndUs= */ TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US - 1000);
assertThat(C.msToUs(mediaLoadData.mediaStartTimeMs)).isEqualTo(1000);
assertThat(C.msToUs(mediaLoadData.mediaEndTimeMs))
.isEqualTo(TEST_PERIOD_DURATION_US - 2 * TEST_CLIP_AMOUNT_US - 1000);
}
@Test
public void eventTimeOutsideClippedRange() throws IOException {
MediaLoadData mediaLoadData =
getClippingMediaSourceMediaLoadData(
/* clippingStartUs= */ TEST_CLIP_AMOUNT_US,
/* clippingEndUs= */ TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US,
/* eventStartUs= */ TEST_CLIP_AMOUNT_US - 1000,
/* eventEndUs= */ TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US + 1000);
assertThat(C.msToUs(mediaLoadData.mediaStartTimeMs)).isEqualTo(0);
assertThat(C.msToUs(mediaLoadData.mediaEndTimeMs))
.isEqualTo(TEST_PERIOD_DURATION_US - 2 * TEST_CLIP_AMOUNT_US);
}
@Test
public void unsetEventTime() throws IOException {
MediaLoadData mediaLoadData =
getClippingMediaSourceMediaLoadData(
/* clippingStartUs= */ TEST_CLIP_AMOUNT_US,
/* clippingEndUs= */ TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US,
/* eventStartUs= */ C.TIME_UNSET,
/* eventEndUs= */ C.TIME_UNSET);
assertThat(C.msToUs(mediaLoadData.mediaStartTimeMs)).isEqualTo(C.TIME_UNSET);
assertThat(C.msToUs(mediaLoadData.mediaEndTimeMs)).isEqualTo(C.TIME_UNSET);
}
@Test
public void eventTimeWithUnsetDuration() throws IOException {
MediaLoadData mediaLoadData =
getClippingMediaSourceMediaLoadData(
/* clippingStartUs= */ TEST_CLIP_AMOUNT_US,
/* clippingEndUs= */ C.TIME_END_OF_SOURCE,
/* eventStartUs= */ TEST_CLIP_AMOUNT_US,
/* eventEndUs= */ TEST_CLIP_AMOUNT_US + 1_000_000);
assertThat(C.msToUs(mediaLoadData.mediaStartTimeMs)).isEqualTo(0);
assertThat(C.msToUs(mediaLoadData.mediaEndTimeMs)).isEqualTo(1_000_000);
}
/**
* Wraps a timeline of duration {@link #TEST_PERIOD_DURATION_US} in a {@link ClippingMediaSource},
* sends a media source event from the child source and returns the reported {@link MediaLoadData}
* for the clipping media source.
*
* @param clippingStartUs The start time of the media source clipping, in microseconds.
* @param clippingEndUs The end time of the media source clipping, in microseconds.
* @param eventStartUs The start time of the media source event (before clipping), in
* microseconds.
* @param eventEndUs The end time of the media source event (before clipping), in microseconds.
* @return The reported {@link MediaLoadData} for that event.
*/
private static MediaLoadData getClippingMediaSourceMediaLoadData(
long clippingStartUs, long clippingEndUs, final long eventStartUs, final long eventEndUs)
throws IOException {
Timeline timeline =
new SinglePeriodTimeline(
TEST_PERIOD_DURATION_US,
/* isSeekable= */ true,
/* isDynamic= */ false,
/* isLive= */ false);
FakeMediaSource fakeMediaSource =
new FakeMediaSource(timeline) {
@Override
protected FakeMediaPeriod createFakeMediaPeriod(
MediaPeriodId id,
TrackGroupArray trackGroupArray,
Allocator allocator,
EventDispatcher eventDispatcher,
@Nullable TransferListener transferListener) {
eventDispatcher.downstreamFormatChanged(
new MediaLoadData(
C.DATA_TYPE_MEDIA,
C.TRACK_TYPE_UNKNOWN,
/* trackFormat= */ null,
C.SELECTION_REASON_UNKNOWN,
/* trackSelectionData= */ null,
C.usToMs(eventStartUs),
C.usToMs(eventEndUs)));
return super.createFakeMediaPeriod(
id, trackGroupArray, allocator, eventDispatcher, transferListener);
}
};
final ClippingMediaSource clippingMediaSource =
new ClippingMediaSource(fakeMediaSource, clippingStartUs, clippingEndUs);
MediaSourceTestRunner testRunner =
new MediaSourceTestRunner(clippingMediaSource, /* allocator= */ null);
final MediaLoadData[] reportedMediaLoadData = new MediaLoadData[1];
try {
testRunner.runOnPlaybackThread(
() ->
clippingMediaSource.addEventListener(
Util.createHandler(),
new MediaSourceEventListener() {
@Override
public void onDownstreamFormatChanged(
int windowIndex,
@Nullable MediaPeriodId mediaPeriodId,
MediaLoadData mediaLoadData) {
reportedMediaLoadData[0] = mediaLoadData;
}
}));
testRunner.prepareSource();
// Create period to send the test event configured above.
testRunner.createPeriod(
new MediaPeriodId(
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0));
assertThat(reportedMediaLoadData[0]).isNotNull();
} finally {
testRunner.release();
}
return reportedMediaLoadData[0];
}
/**
* Wraps the specified timeline in a {@link ClippingMediaSource} and returns the clipped timeline.
*/
private static Timeline getClippedTimeline(Timeline timeline, long startUs, long endUs)
throws IOException {
FakeMediaSource fakeMediaSource = new FakeMediaSource(timeline);
ClippingMediaSource mediaSource = new ClippingMediaSource(fakeMediaSource, startUs, endUs);
return getClippedTimelines(fakeMediaSource, mediaSource)[0];
}
/**
* Wraps the specified timeline in a {@link ClippingMediaSource} and returns the clipped timeline.
*/
private static Timeline getClippedTimeline(Timeline timeline, long durationUs)
throws IOException {
FakeMediaSource fakeMediaSource = new FakeMediaSource(timeline);
ClippingMediaSource mediaSource = new ClippingMediaSource(fakeMediaSource, durationUs);
return getClippedTimelines(fakeMediaSource, mediaSource)[0];
}
/**
* Wraps the specified timelines in a {@link ClippingMediaSource} and returns the clipped timeline
* for each timeline update.
*/
private static Timeline[] getClippedTimelines(
long startUs,
long endUs,
boolean allowDynamicUpdates,
boolean fromDefaultPosition,
Timeline firstTimeline,
Timeline... additionalTimelines)
throws IOException {
FakeMediaSource fakeMediaSource = new FakeMediaSource(firstTimeline);
ClippingMediaSource mediaSource =
new ClippingMediaSource(
fakeMediaSource,
startUs,
endUs,
/* enableInitialDiscontinuity= */ true,
allowDynamicUpdates,
fromDefaultPosition);
return getClippedTimelines(fakeMediaSource, mediaSource, additionalTimelines);
}
private static Timeline[] getClippedTimelines(
FakeMediaSource fakeMediaSource,
ClippingMediaSource clippingMediaSource,
Timeline... additionalTimelines)
throws IOException {
MediaSourceTestRunner testRunner =
new MediaSourceTestRunner(clippingMediaSource, /* allocator= */ null);
Timeline[] clippedTimelines = new Timeline[additionalTimelines.length + 1];
try {
clippedTimelines[0] = testRunner.prepareSource();
MediaPeriod mediaPeriod =
testRunner.createPeriod(
new MediaPeriodId(
clippedTimelines[0].getUidOfPeriod(/* periodIndex= */ 0),
/* windowSequenceNumber= */ 0));
for (int i = 0; i < additionalTimelines.length; i++) {
fakeMediaSource.setNewSourceInfo(additionalTimelines[i]);
clippedTimelines[i + 1] = testRunner.assertTimelineChangeBlocking();
}
testRunner.releasePeriod(mediaPeriod);
testRunner.releaseSource();
fakeMediaSource.assertReleased();
return clippedTimelines;
} finally {
testRunner.release();
}
}
}