Commit 2480e0a4 by tonihei Committed by Ian Baker

Add option to align durations of MergingMediaSource.

Without this feature it's impossible to nicely merge multiple sources
with different durations if these durations are not known exactly
before the start of playback.

Issue: #8422
PiperOrigin-RevId: 350567625
parent 1ef06b8d
......@@ -42,6 +42,9 @@
* Populate codecs string for H.264/AVC in MP4, Matroska and FLV streams to
allow decoder capability checks based on codec profile/level
([#8393](https://github.com/google/ExoPlayer/issues/8393)).
* Add option to `MergingMediaSource` to clip the durations of all sources
to have the same length
([#8422](https://github.com/google/ExoPlayer/issues/8422)).
* Track selection:
* Allow parallel adaptation for video and audio
([#5111](https://github.com/google/ExoPlayer/issues/5111)).
......
......@@ -16,7 +16,7 @@
package com.google.android.exoplayer2.source;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import static org.junit.Assert.assertThrows;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.C;
......@@ -35,35 +35,65 @@ import org.junit.runner.RunWith;
public class MergingMediaSourceTest {
@Test
public void mergingDynamicTimelines() throws IOException {
FakeTimeline firstTimeline =
new FakeTimeline(new TimelineWindowDefinition(true, true, C.TIME_UNSET));
FakeTimeline secondTimeline =
new FakeTimeline(new TimelineWindowDefinition(true, true, C.TIME_UNSET));
testMergingMediaSourcePrepare(firstTimeline, secondTimeline);
public void prepare_withoutDurationClipping_usesTimelineOfFirstSource() throws IOException {
FakeTimeline timeline1 =
new FakeTimeline(
new TimelineWindowDefinition(
/* isSeekable= */ true, /* isDynamic= */ true, /* durationUs= */ 30));
FakeTimeline timeline2 =
new FakeTimeline(
new TimelineWindowDefinition(
/* isSeekable= */ true, /* isDynamic= */ true, /* durationUs= */ C.TIME_UNSET));
FakeTimeline timeline3 =
new FakeTimeline(
new TimelineWindowDefinition(
/* isSeekable= */ true, /* isDynamic= */ true, /* durationUs= */ 10));
Timeline mergedTimeline =
prepareMergingMediaSource(/* clipDurations= */ false, timeline1, timeline2, timeline3);
assertThat(mergedTimeline).isEqualTo(timeline1);
}
@Test
public void mergingStaticTimelines() throws IOException {
FakeTimeline firstTimeline = new FakeTimeline(new TimelineWindowDefinition(true, false, 20));
FakeTimeline secondTimeline = new FakeTimeline(new TimelineWindowDefinition(true, false, 10));
testMergingMediaSourcePrepare(firstTimeline, secondTimeline);
public void prepare_withDurationClipping_usesDurationOfShortestSource() throws IOException {
FakeTimeline timeline1 =
new FakeTimeline(
new TimelineWindowDefinition(
/* isSeekable= */ true, /* isDynamic= */ true, /* durationUs= */ 30));
FakeTimeline timeline2 =
new FakeTimeline(
new TimelineWindowDefinition(
/* isSeekable= */ true, /* isDynamic= */ true, /* durationUs= */ C.TIME_UNSET));
FakeTimeline timeline3 =
new FakeTimeline(
new TimelineWindowDefinition(
/* isSeekable= */ true, /* isDynamic= */ true, /* durationUs= */ 10));
Timeline mergedTimeline =
prepareMergingMediaSource(/* clipDurations= */ true, timeline1, timeline2, timeline3);
assertThat(mergedTimeline).isEqualTo(timeline3);
}
@Test
public void mergingTimelinesWithDifferentPeriodCounts() throws IOException {
FakeTimeline firstTimeline = new FakeTimeline(new TimelineWindowDefinition(1, null));
FakeTimeline secondTimeline = new FakeTimeline(new TimelineWindowDefinition(2, null));
try {
testMergingMediaSourcePrepare(firstTimeline, secondTimeline);
fail("Expected merging to fail.");
} catch (IllegalMergeException e) {
assertThat(e.reason).isEqualTo(IllegalMergeException.REASON_PERIOD_COUNT_MISMATCH);
}
public void prepare_differentPeriodCounts_fails() throws IOException {
FakeTimeline firstTimeline =
new FakeTimeline(new TimelineWindowDefinition(/* periodCount= */ 1, /* id= */ 1));
FakeTimeline secondTimeline =
new FakeTimeline(new TimelineWindowDefinition(/* periodCount= */ 2, /* id= */ 2));
IllegalMergeException exception =
assertThrows(
IllegalMergeException.class,
() ->
prepareMergingMediaSource(
/* clipDurations= */ false, firstTimeline, secondTimeline));
assertThat(exception.reason).isEqualTo(IllegalMergeException.REASON_PERIOD_COUNT_MISMATCH);
}
@Test
public void mergingMediaSourcePeriodCreation() throws Exception {
public void createPeriod_createsChildPeriods() throws Exception {
FakeMediaSource[] mediaSources = new FakeMediaSource[2];
for (int i = 0; i < mediaSources.length; i++) {
mediaSources[i] = new FakeMediaSource(new FakeTimeline(/* windowCount= */ 2));
......@@ -83,24 +113,26 @@ public class MergingMediaSourceTest {
}
/**
* Wraps the specified timelines in a {@link MergingMediaSource}, prepares it and checks that it
* forwards the first of the wrapped timelines.
* Wraps the specified timelines in a {@link MergingMediaSource}, prepares it and returns the
* merged timeline.
*/
private static void testMergingMediaSourcePrepare(Timeline... timelines) throws IOException {
private static Timeline prepareMergingMediaSource(boolean clipDurations, Timeline... timelines)
throws IOException {
FakeMediaSource[] mediaSources = new FakeMediaSource[timelines.length];
for (int i = 0; i < timelines.length; i++) {
mediaSources[i] = new FakeMediaSource(timelines[i]);
}
MergingMediaSource mergingMediaSource = new MergingMediaSource(mediaSources);
MediaSourceTestRunner testRunner = new MediaSourceTestRunner(mergingMediaSource, null);
MergingMediaSource mergingMediaSource =
new MergingMediaSource(/* adjustPeriodTimeOffsets= */ false, clipDurations, mediaSources);
MediaSourceTestRunner testRunner =
new MediaSourceTestRunner(mergingMediaSource, /* allocator= */ null);
try {
Timeline timeline = testRunner.prepareSource();
// The merged timeline should always be the one from the first child.
assertThat(timeline).isEqualTo(timelines[0]);
testRunner.releaseSource();
for (FakeMediaSource mediaSource : mediaSources) {
mediaSource.assertReleased();
}
return timeline;
} finally {
testRunner.release();
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment