Commit 494a41c8 by olly Committed by Oliver Woodman

Improve ClippingMediaSource "cannot clip" behavior

This brings ClippingMediaSource clip failures in line with
what MergingMediaSource does when it cannot merge.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=176660123
parent d909dc18
...@@ -21,11 +21,13 @@ import com.google.android.exoplayer2.Player; ...@@ -21,11 +21,13 @@ import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.Timeline.Period; import com.google.android.exoplayer2.Timeline.Period;
import com.google.android.exoplayer2.Timeline.Window; import com.google.android.exoplayer2.Timeline.Window;
import com.google.android.exoplayer2.source.ClippingMediaSource.IllegalClippingException;
import com.google.android.exoplayer2.testutil.FakeMediaSource; import com.google.android.exoplayer2.testutil.FakeMediaSource;
import com.google.android.exoplayer2.testutil.FakeTimeline; import com.google.android.exoplayer2.testutil.FakeTimeline;
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition; import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
import com.google.android.exoplayer2.testutil.MediaSourceTestRunner; import com.google.android.exoplayer2.testutil.MediaSourceTestRunner;
import com.google.android.exoplayer2.testutil.TimelineAsserts; import com.google.android.exoplayer2.testutil.TimelineAsserts;
import java.io.IOException;
/** /**
* Unit tests for {@link ClippingMediaSource}. * Unit tests for {@link ClippingMediaSource}.
...@@ -40,11 +42,12 @@ public final class ClippingMediaSourceTest extends InstrumentationTestCase { ...@@ -40,11 +42,12 @@ public final class ClippingMediaSourceTest extends InstrumentationTestCase {
@Override @Override
protected void setUp() throws Exception { protected void setUp() throws Exception {
super.setUp();
window = new Timeline.Window(); window = new Timeline.Window();
period = new Timeline.Period(); period = new Timeline.Period();
} }
public void testNoClipping() { public void testNoClipping() throws IOException {
Timeline timeline = new SinglePeriodTimeline(C.msToUs(TEST_PERIOD_DURATION_US), true, false); Timeline timeline = new SinglePeriodTimeline(C.msToUs(TEST_PERIOD_DURATION_US), true, false);
Timeline clippedTimeline = getClippedTimeline(timeline, 0, TEST_PERIOD_DURATION_US); Timeline clippedTimeline = getClippedTimeline(timeline, 0, TEST_PERIOD_DURATION_US);
...@@ -55,7 +58,7 @@ public final class ClippingMediaSourceTest extends InstrumentationTestCase { ...@@ -55,7 +58,7 @@ public final class ClippingMediaSourceTest extends InstrumentationTestCase {
assertEquals(TEST_PERIOD_DURATION_US, clippedTimeline.getPeriod(0, period).getDurationUs()); assertEquals(TEST_PERIOD_DURATION_US, clippedTimeline.getPeriod(0, period).getDurationUs());
} }
public void testClippingUnseekableWindowThrows() { public void testClippingUnseekableWindowThrows() throws IOException {
Timeline timeline = new SinglePeriodTimeline(C.msToUs(TEST_PERIOD_DURATION_US), false, false); Timeline timeline = new SinglePeriodTimeline(C.msToUs(TEST_PERIOD_DURATION_US), false, false);
// If the unseekable window isn't clipped, clipping succeeds. // If the unseekable window isn't clipped, clipping succeeds.
...@@ -64,12 +67,12 @@ public final class ClippingMediaSourceTest extends InstrumentationTestCase { ...@@ -64,12 +67,12 @@ public final class ClippingMediaSourceTest extends InstrumentationTestCase {
// If the unseekable window is clipped, clipping fails. // If the unseekable window is clipped, clipping fails.
getClippedTimeline(timeline, 1, TEST_PERIOD_DURATION_US); getClippedTimeline(timeline, 1, TEST_PERIOD_DURATION_US);
fail("Expected clipping to fail."); fail("Expected clipping to fail.");
} catch (IllegalArgumentException e) { } catch (IllegalClippingException e) {
// Expected. assertEquals(IllegalClippingException.REASON_NOT_SEEKABLE_TO_START, e.reason);
} }
} }
public void testClippingStart() { public void testClippingStart() throws IOException {
Timeline timeline = new SinglePeriodTimeline(C.msToUs(TEST_PERIOD_DURATION_US), true, false); Timeline timeline = new SinglePeriodTimeline(C.msToUs(TEST_PERIOD_DURATION_US), true, false);
Timeline clippedTimeline = getClippedTimeline(timeline, TEST_CLIP_AMOUNT_US, Timeline clippedTimeline = getClippedTimeline(timeline, TEST_CLIP_AMOUNT_US,
...@@ -80,7 +83,7 @@ public final class ClippingMediaSourceTest extends InstrumentationTestCase { ...@@ -80,7 +83,7 @@ public final class ClippingMediaSourceTest extends InstrumentationTestCase {
clippedTimeline.getPeriod(0, period).getDurationUs()); clippedTimeline.getPeriod(0, period).getDurationUs());
} }
public void testClippingEnd() { public void testClippingEnd() throws IOException {
Timeline timeline = new SinglePeriodTimeline(C.msToUs(TEST_PERIOD_DURATION_US), true, false); Timeline timeline = new SinglePeriodTimeline(C.msToUs(TEST_PERIOD_DURATION_US), true, false);
Timeline clippedTimeline = getClippedTimeline(timeline, 0, Timeline clippedTimeline = getClippedTimeline(timeline, 0,
...@@ -91,7 +94,7 @@ public final class ClippingMediaSourceTest extends InstrumentationTestCase { ...@@ -91,7 +94,7 @@ public final class ClippingMediaSourceTest extends InstrumentationTestCase {
clippedTimeline.getPeriod(0, period).getDurationUs()); clippedTimeline.getPeriod(0, period).getDurationUs());
} }
public void testClippingStartAndEnd() { public void testClippingStartAndEnd() throws IOException {
Timeline timeline = new SinglePeriodTimeline(C.msToUs(TEST_PERIOD_DURATION_US), true, false); Timeline timeline = new SinglePeriodTimeline(C.msToUs(TEST_PERIOD_DURATION_US), true, false);
Timeline clippedTimeline = getClippedTimeline(timeline, TEST_CLIP_AMOUNT_US, Timeline clippedTimeline = getClippedTimeline(timeline, TEST_CLIP_AMOUNT_US,
...@@ -102,7 +105,7 @@ public final class ClippingMediaSourceTest extends InstrumentationTestCase { ...@@ -102,7 +105,7 @@ public final class ClippingMediaSourceTest extends InstrumentationTestCase {
clippedTimeline.getPeriod(0, period).getDurationUs()); clippedTimeline.getPeriod(0, period).getDurationUs());
} }
public void testWindowAndPeriodIndices() { public void testWindowAndPeriodIndices() throws IOException {
Timeline timeline = new FakeTimeline( Timeline timeline = new FakeTimeline(
new TimelineWindowDefinition(1, 111, true, false, TEST_PERIOD_DURATION_US)); new TimelineWindowDefinition(1, 111, true, false, TEST_PERIOD_DURATION_US));
Timeline clippedTimeline = getClippedTimeline(timeline, TEST_CLIP_AMOUNT_US, Timeline clippedTimeline = getClippedTimeline(timeline, TEST_CLIP_AMOUNT_US,
...@@ -122,7 +125,8 @@ public final class ClippingMediaSourceTest extends InstrumentationTestCase { ...@@ -122,7 +125,8 @@ public final class ClippingMediaSourceTest extends InstrumentationTestCase {
/** /**
* Wraps the specified timeline in a {@link ClippingMediaSource} and returns the clipped timeline. * Wraps the specified timeline in a {@link ClippingMediaSource} and returns the clipped timeline.
*/ */
private static Timeline getClippedTimeline(Timeline timeline, long startMs, long endMs) { private static Timeline getClippedTimeline(Timeline timeline, long startMs, long endMs)
throws IOException {
FakeMediaSource fakeMediaSource = new FakeMediaSource(timeline, null); FakeMediaSource fakeMediaSource = new FakeMediaSource(timeline, null);
ClippingMediaSource mediaSource = new ClippingMediaSource(fakeMediaSource, startMs, endMs); ClippingMediaSource mediaSource = new ClippingMediaSource(fakeMediaSource, startMs, endMs);
MediaSourceTestRunner testRunner = new MediaSourceTestRunner(mediaSource, null); MediaSourceTestRunner testRunner = new MediaSourceTestRunner(mediaSource, null);
......
...@@ -25,6 +25,7 @@ import com.google.android.exoplayer2.testutil.FakeTimeline; ...@@ -25,6 +25,7 @@ import com.google.android.exoplayer2.testutil.FakeTimeline;
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition; import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
import com.google.android.exoplayer2.testutil.MediaSourceTestRunner; import com.google.android.exoplayer2.testutil.MediaSourceTestRunner;
import com.google.android.exoplayer2.testutil.TimelineAsserts; import com.google.android.exoplayer2.testutil.TimelineAsserts;
import java.io.IOException;
import junit.framework.TestCase; import junit.framework.TestCase;
/** /**
...@@ -32,7 +33,7 @@ import junit.framework.TestCase; ...@@ -32,7 +33,7 @@ import junit.framework.TestCase;
*/ */
public final class ConcatenatingMediaSourceTest extends TestCase { public final class ConcatenatingMediaSourceTest extends TestCase {
public void testEmptyConcatenation() { public void testEmptyConcatenation() throws IOException {
for (boolean atomic : new boolean[] {false, true}) { for (boolean atomic : new boolean[] {false, true}) {
Timeline timeline = getConcatenatedTimeline(atomic); Timeline timeline = getConcatenatedTimeline(atomic);
TimelineAsserts.assertEmpty(timeline); TimelineAsserts.assertEmpty(timeline);
...@@ -45,7 +46,7 @@ public final class ConcatenatingMediaSourceTest extends TestCase { ...@@ -45,7 +46,7 @@ public final class ConcatenatingMediaSourceTest extends TestCase {
} }
} }
public void testSingleMediaSource() { public void testSingleMediaSource() throws IOException {
Timeline timeline = getConcatenatedTimeline(false, createFakeTimeline(3, 111)); Timeline timeline = getConcatenatedTimeline(false, createFakeTimeline(3, 111));
TimelineAsserts.assertWindowIds(timeline, 111); TimelineAsserts.assertWindowIds(timeline, 111);
TimelineAsserts.assertPeriodCounts(timeline, 3); TimelineAsserts.assertPeriodCounts(timeline, 3);
...@@ -75,7 +76,7 @@ public final class ConcatenatingMediaSourceTest extends TestCase { ...@@ -75,7 +76,7 @@ public final class ConcatenatingMediaSourceTest extends TestCase {
} }
} }
public void testMultipleMediaSources() { public void testMultipleMediaSources() throws IOException {
Timeline[] timelines = { createFakeTimeline(3, 111), createFakeTimeline(1, 222), Timeline[] timelines = { createFakeTimeline(3, 111), createFakeTimeline(1, 222),
createFakeTimeline(3, 333) }; createFakeTimeline(3, 333) };
Timeline timeline = getConcatenatedTimeline(false, timelines); Timeline timeline = getConcatenatedTimeline(false, timelines);
...@@ -121,7 +122,7 @@ public final class ConcatenatingMediaSourceTest extends TestCase { ...@@ -121,7 +122,7 @@ public final class ConcatenatingMediaSourceTest extends TestCase {
} }
} }
public void testNestedMediaSources() { public void testNestedMediaSources() throws IOException {
Timeline timeline = getConcatenatedTimeline(false, Timeline timeline = getConcatenatedTimeline(false,
getConcatenatedTimeline(false, createFakeTimeline(1, 111), createFakeTimeline(1, 222)), getConcatenatedTimeline(false, createFakeTimeline(1, 111), createFakeTimeline(1, 222)),
getConcatenatedTimeline(true, createFakeTimeline(1, 333), createFakeTimeline(1, 444))); getConcatenatedTimeline(true, createFakeTimeline(1, 333), createFakeTimeline(1, 444)));
...@@ -149,7 +150,7 @@ public final class ConcatenatingMediaSourceTest extends TestCase { ...@@ -149,7 +150,7 @@ public final class ConcatenatingMediaSourceTest extends TestCase {
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ALL, true, 2, 0, 3, 1); TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ALL, true, 2, 0, 3, 1);
} }
public void testEmptyTimelineMediaSources() { public void testEmptyTimelineMediaSources() throws IOException {
// Empty timelines in the front, back, and the middle (single and multiple in a row). // Empty timelines in the front, back, and the middle (single and multiple in a row).
Timeline[] timelines = { Timeline.EMPTY, createFakeTimeline(1, 111), Timeline.EMPTY, Timeline[] timelines = { Timeline.EMPTY, createFakeTimeline(1, 111), Timeline.EMPTY,
Timeline.EMPTY, createFakeTimeline(2, 222), Timeline.EMPTY, createFakeTimeline(3, 333), Timeline.EMPTY, createFakeTimeline(2, 222), Timeline.EMPTY, createFakeTimeline(3, 333),
...@@ -197,7 +198,7 @@ public final class ConcatenatingMediaSourceTest extends TestCase { ...@@ -197,7 +198,7 @@ public final class ConcatenatingMediaSourceTest extends TestCase {
} }
} }
public void testPeriodCreationWithAds() throws InterruptedException { public void testPeriodCreationWithAds() throws IOException, InterruptedException {
// Create media source with ad child source. // Create media source with ad child source.
Timeline timelineContentOnly = new FakeTimeline( Timeline timelineContentOnly = new FakeTimeline(
new TimelineWindowDefinition(2, 111, true, false, 10 * C.MICROS_PER_SECOND)); new TimelineWindowDefinition(2, 111, true, false, 10 * C.MICROS_PER_SECOND));
...@@ -231,7 +232,7 @@ public final class ConcatenatingMediaSourceTest extends TestCase { ...@@ -231,7 +232,7 @@ public final class ConcatenatingMediaSourceTest extends TestCase {
* the concatenated timeline. * the concatenated timeline.
*/ */
private static Timeline getConcatenatedTimeline(boolean isRepeatOneAtomic, private static Timeline getConcatenatedTimeline(boolean isRepeatOneAtomic,
Timeline... timelines) { Timeline... timelines) throws IOException {
MediaSource[] mediaSources = new MediaSource[timelines.length]; MediaSource[] mediaSources = new MediaSource[timelines.length];
for (int i = 0; i < timelines.length; i++) { for (int i = 0; i < timelines.length; i++) {
mediaSources[i] = new FakeMediaSource(timelines[i], null); mediaSources[i] = new FakeMediaSource(timelines[i], null);
......
...@@ -30,6 +30,7 @@ import com.google.android.exoplayer2.testutil.FakeTimeline; ...@@ -30,6 +30,7 @@ import com.google.android.exoplayer2.testutil.FakeTimeline;
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition; import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
import com.google.android.exoplayer2.testutil.MediaSourceTestRunner; import com.google.android.exoplayer2.testutil.MediaSourceTestRunner;
import com.google.android.exoplayer2.testutil.TimelineAsserts; import com.google.android.exoplayer2.testutil.TimelineAsserts;
import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.mockito.Mockito; import org.mockito.Mockito;
...@@ -55,7 +56,7 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase { ...@@ -55,7 +56,7 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase {
testRunner.release(); testRunner.release();
} }
public void testPlaylistChangesAfterPreparation() { public void testPlaylistChangesAfterPreparation() throws IOException {
Timeline timeline = testRunner.prepareSource(); Timeline timeline = testRunner.prepareSource();
TimelineAsserts.assertEmpty(timeline); TimelineAsserts.assertEmpty(timeline);
...@@ -171,7 +172,7 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase { ...@@ -171,7 +172,7 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase {
childSources[3].assertReleased(); childSources[3].assertReleased();
} }
public void testPlaylistChangesBeforePreparation() { public void testPlaylistChangesBeforePreparation() throws IOException {
FakeMediaSource[] childSources = createMediaSources(4); FakeMediaSource[] childSources = createMediaSources(4);
mediaSource.addMediaSource(childSources[0]); mediaSource.addMediaSource(childSources[0]);
mediaSource.addMediaSource(childSources[1]); mediaSource.addMediaSource(childSources[1]);
...@@ -201,7 +202,7 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase { ...@@ -201,7 +202,7 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase {
} }
} }
public void testPlaylistWithLazyMediaSource() { public void testPlaylistWithLazyMediaSource() throws IOException {
// Create some normal (immediately preparing) sources and some lazy sources whose timeline // Create some normal (immediately preparing) sources and some lazy sources whose timeline
// updates need to be triggered. // updates need to be triggered.
FakeMediaSource[] fastSources = createMediaSources(2); FakeMediaSource[] fastSources = createMediaSources(2);
...@@ -290,7 +291,7 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase { ...@@ -290,7 +291,7 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase {
} }
} }
public void testEmptyTimelineMediaSource() { public void testEmptyTimelineMediaSource() throws IOException {
Timeline timeline = testRunner.prepareSource(); Timeline timeline = testRunner.prepareSource();
TimelineAsserts.assertEmpty(timeline); TimelineAsserts.assertEmpty(timeline);
...@@ -426,7 +427,7 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase { ...@@ -426,7 +427,7 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase {
verify(runnable).run(); verify(runnable).run();
} }
public void testCustomCallbackAfterPreparationAddSingle() { public void testCustomCallbackAfterPreparationAddSingle() throws IOException {
DummyMainThread dummyMainThread = new DummyMainThread(); DummyMainThread dummyMainThread = new DummyMainThread();
try { try {
testRunner.prepareSource(); testRunner.prepareSource();
...@@ -444,7 +445,7 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase { ...@@ -444,7 +445,7 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase {
} }
} }
public void testCustomCallbackAfterPreparationAddMultiple() { public void testCustomCallbackAfterPreparationAddMultiple() throws IOException {
DummyMainThread dummyMainThread = new DummyMainThread(); DummyMainThread dummyMainThread = new DummyMainThread();
try { try {
testRunner.prepareSource(); testRunner.prepareSource();
...@@ -464,7 +465,7 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase { ...@@ -464,7 +465,7 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase {
} }
} }
public void testCustomCallbackAfterPreparationAddSingleWithIndex() { public void testCustomCallbackAfterPreparationAddSingleWithIndex() throws IOException {
DummyMainThread dummyMainThread = new DummyMainThread(); DummyMainThread dummyMainThread = new DummyMainThread();
try { try {
testRunner.prepareSource(); testRunner.prepareSource();
...@@ -482,7 +483,7 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase { ...@@ -482,7 +483,7 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase {
} }
} }
public void testCustomCallbackAfterPreparationAddMultipleWithIndex() { public void testCustomCallbackAfterPreparationAddMultipleWithIndex() throws IOException {
DummyMainThread dummyMainThread = new DummyMainThread(); DummyMainThread dummyMainThread = new DummyMainThread();
try { try {
testRunner.prepareSource(); testRunner.prepareSource();
...@@ -502,7 +503,7 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase { ...@@ -502,7 +503,7 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase {
} }
} }
public void testCustomCallbackAfterPreparationRemove() { public void testCustomCallbackAfterPreparationRemove() throws IOException {
DummyMainThread dummyMainThread = new DummyMainThread(); DummyMainThread dummyMainThread = new DummyMainThread();
try { try {
testRunner.prepareSource(); testRunner.prepareSource();
...@@ -528,7 +529,7 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase { ...@@ -528,7 +529,7 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase {
} }
} }
public void testCustomCallbackAfterPreparationMove() { public void testCustomCallbackAfterPreparationMove() throws IOException {
DummyMainThread dummyMainThread = new DummyMainThread(); DummyMainThread dummyMainThread = new DummyMainThread();
try { try {
testRunner.prepareSource(); testRunner.prepareSource();
...@@ -556,7 +557,7 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase { ...@@ -556,7 +557,7 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase {
} }
} }
public void testPeriodCreationWithAds() throws InterruptedException { public void testPeriodCreationWithAds() throws IOException, InterruptedException {
// Create dynamic media source with ad child source. // Create dynamic media source with ad child source.
Timeline timelineContentOnly = new FakeTimeline( Timeline timelineContentOnly = new FakeTimeline(
new TimelineWindowDefinition(2, 111, true, false, 10 * C.MICROS_PER_SECOND)); new TimelineWindowDefinition(2, 111, true, false, 10 * C.MICROS_PER_SECOND));
......
...@@ -23,6 +23,7 @@ import com.google.android.exoplayer2.testutil.FakeTimeline; ...@@ -23,6 +23,7 @@ import com.google.android.exoplayer2.testutil.FakeTimeline;
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition; import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
import com.google.android.exoplayer2.testutil.MediaSourceTestRunner; import com.google.android.exoplayer2.testutil.MediaSourceTestRunner;
import com.google.android.exoplayer2.testutil.TimelineAsserts; import com.google.android.exoplayer2.testutil.TimelineAsserts;
import java.io.IOException;
import junit.framework.TestCase; import junit.framework.TestCase;
/** /**
...@@ -39,7 +40,7 @@ public class LoopingMediaSourceTest extends TestCase { ...@@ -39,7 +40,7 @@ public class LoopingMediaSourceTest extends TestCase {
new TimelineWindowDefinition(1, 222), new TimelineWindowDefinition(1, 333)); new TimelineWindowDefinition(1, 222), new TimelineWindowDefinition(1, 333));
} }
public void testSingleLoop() { public void testSingleLoop() throws IOException {
Timeline timeline = getLoopingTimeline(multiWindowTimeline, 1); Timeline timeline = getLoopingTimeline(multiWindowTimeline, 1);
TimelineAsserts.assertWindowIds(timeline, 111, 222, 333); TimelineAsserts.assertWindowIds(timeline, 111, 222, 333);
TimelineAsserts.assertPeriodCounts(timeline, 1, 1, 1); TimelineAsserts.assertPeriodCounts(timeline, 1, 1, 1);
...@@ -57,7 +58,7 @@ public class LoopingMediaSourceTest extends TestCase { ...@@ -57,7 +58,7 @@ public class LoopingMediaSourceTest extends TestCase {
} }
} }
public void testMultiLoop() { public void testMultiLoop() throws IOException {
Timeline timeline = getLoopingTimeline(multiWindowTimeline, 3); Timeline timeline = getLoopingTimeline(multiWindowTimeline, 3);
TimelineAsserts.assertWindowIds(timeline, 111, 222, 333, 111, 222, 333, 111, 222, 333); TimelineAsserts.assertWindowIds(timeline, 111, 222, 333, 111, 222, 333, 111, 222, 333);
TimelineAsserts.assertPeriodCounts(timeline, 1, 1, 1, 1, 1, 1, 1, 1, 1); TimelineAsserts.assertPeriodCounts(timeline, 1, 1, 1, 1, 1, 1, 1, 1, 1);
...@@ -77,7 +78,7 @@ public class LoopingMediaSourceTest extends TestCase { ...@@ -77,7 +78,7 @@ public class LoopingMediaSourceTest extends TestCase {
} }
} }
public void testInfiniteLoop() { public void testInfiniteLoop() throws IOException {
Timeline timeline = getLoopingTimeline(multiWindowTimeline, Integer.MAX_VALUE); Timeline timeline = getLoopingTimeline(multiWindowTimeline, Integer.MAX_VALUE);
TimelineAsserts.assertWindowIds(timeline, 111, 222, 333); TimelineAsserts.assertWindowIds(timeline, 111, 222, 333);
TimelineAsserts.assertPeriodCounts(timeline, 1, 1, 1); TimelineAsserts.assertPeriodCounts(timeline, 1, 1, 1);
...@@ -94,7 +95,7 @@ public class LoopingMediaSourceTest extends TestCase { ...@@ -94,7 +95,7 @@ public class LoopingMediaSourceTest extends TestCase {
} }
} }
public void testEmptyTimelineLoop() { public void testEmptyTimelineLoop() throws IOException {
Timeline timeline = getLoopingTimeline(Timeline.EMPTY, 1); Timeline timeline = getLoopingTimeline(Timeline.EMPTY, 1);
TimelineAsserts.assertEmpty(timeline); TimelineAsserts.assertEmpty(timeline);
...@@ -109,7 +110,7 @@ public class LoopingMediaSourceTest extends TestCase { ...@@ -109,7 +110,7 @@ public class LoopingMediaSourceTest extends TestCase {
* Wraps the specified timeline in a {@link LoopingMediaSource} and returns * Wraps the specified timeline in a {@link LoopingMediaSource} and returns
* the looping timeline. * the looping timeline.
*/ */
private static Timeline getLoopingTimeline(Timeline timeline, int loopCount) { private static Timeline getLoopingTimeline(Timeline timeline, int loopCount) throws IOException {
FakeMediaSource fakeMediaSource = new FakeMediaSource(timeline, null); FakeMediaSource fakeMediaSource = new FakeMediaSource(timeline, null);
LoopingMediaSource mediaSource = new LoopingMediaSource(fakeMediaSource, loopCount); LoopingMediaSource mediaSource = new LoopingMediaSource(fakeMediaSource, loopCount);
MediaSourceTestRunner testRunner = new MediaSourceTestRunner(mediaSource, null); MediaSourceTestRunner testRunner = new MediaSourceTestRunner(mediaSource, null);
......
...@@ -15,20 +15,68 @@ ...@@ -15,20 +15,68 @@
*/ */
package com.google.android.exoplayer2.source; package com.google.android.exoplayer2.source;
import android.support.annotation.IntDef;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import java.io.IOException; import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList; import java.util.ArrayList;
/** /**
* {@link MediaSource} that wraps a source and clips its timeline based on specified start/end * {@link MediaSource} that wraps a source and clips its timeline based on specified start/end
* positions. The wrapped source may only have a single period/window. * positions. The wrapped source must consist of a single period that starts at the beginning of the
* corresponding window.
*/ */
public final class ClippingMediaSource implements MediaSource, MediaSource.Listener { public final class ClippingMediaSource implements MediaSource, MediaSource.Listener {
/**
* Thrown when a {@link ClippingMediaSource} cannot clip its wrapped source.
*/
public static final class IllegalClippingException extends IOException {
/**
* The reason the clipping failed.
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({REASON_INVALID_PERIOD_COUNT, REASON_PERIOD_OFFSET_IN_WINDOW,
REASON_NOT_SEEKABLE_TO_START, REASON_START_EXCEEDS_END})
public @interface Reason {}
/**
* The wrapped source doesn't consist of a single period.
*/
public static final int REASON_INVALID_PERIOD_COUNT = 0;
/**
* The wrapped source period doesn't start at the beginning of the corresponding window.
*/
public static final int REASON_PERIOD_OFFSET_IN_WINDOW = 1;
/**
* The wrapped source is not seekable and a non-zero clipping start position was specified.
*/
public static final int REASON_NOT_SEEKABLE_TO_START = 2;
/**
* The wrapped source ends before the specified clipping start position.
*/
public static final int REASON_START_EXCEEDS_END = 3;
/**
* The reason clipping failed.
*/
@Reason
public final int reason;
/**
* @param reason The reason clipping failed.
*/
public IllegalClippingException(@Reason int reason) {
this.reason = reason;
}
}
private final MediaSource mediaSource; private final MediaSource mediaSource;
private final long startUs; private final long startUs;
private final long endUs; private final long endUs;
...@@ -36,6 +84,7 @@ public final class ClippingMediaSource implements MediaSource, MediaSource.Liste ...@@ -36,6 +84,7 @@ public final class ClippingMediaSource implements MediaSource, MediaSource.Liste
private final ArrayList<ClippingMediaPeriod> mediaPeriods; private final ArrayList<ClippingMediaPeriod> mediaPeriods;
private MediaSource.Listener sourceListener; private MediaSource.Listener sourceListener;
private IllegalClippingException clippingError;
/** /**
* Creates a new clipping source that wraps the specified source. * Creates a new clipping source that wraps the specified source.
...@@ -88,6 +137,9 @@ public final class ClippingMediaSource implements MediaSource, MediaSource.Liste ...@@ -88,6 +137,9 @@ public final class ClippingMediaSource implements MediaSource, MediaSource.Liste
@Override @Override
public void maybeThrowSourceInfoRefreshError() throws IOException { public void maybeThrowSourceInfoRefreshError() throws IOException {
if (clippingError != null) {
throw clippingError;
}
mediaSource.maybeThrowSourceInfoRefreshError(); mediaSource.maybeThrowSourceInfoRefreshError();
} }
...@@ -115,8 +167,17 @@ public final class ClippingMediaSource implements MediaSource, MediaSource.Liste ...@@ -115,8 +167,17 @@ public final class ClippingMediaSource implements MediaSource, MediaSource.Liste
@Override @Override
public void onSourceInfoRefreshed(MediaSource source, Timeline timeline, Object manifest) { public void onSourceInfoRefreshed(MediaSource source, Timeline timeline, Object manifest) {
sourceListener.onSourceInfoRefreshed(this, new ClippingTimeline(timeline, startUs, endUs), if (clippingError != null) {
manifest); return;
}
ClippingTimeline clippingTimeline;
try {
clippingTimeline = new ClippingTimeline(timeline, startUs, endUs);
} catch (IllegalClippingException e) {
clippingError = e;
return;
}
sourceListener.onSourceInfoRefreshed(this, clippingTimeline, manifest);
int count = mediaPeriods.size(); int count = mediaPeriods.size();
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
mediaPeriods.get(i).setClipping(startUs, endUs); mediaPeriods.get(i).setClipping(startUs, endUs);
...@@ -138,22 +199,30 @@ public final class ClippingMediaSource implements MediaSource, MediaSource.Liste ...@@ -138,22 +199,30 @@ public final class ClippingMediaSource implements MediaSource, MediaSource.Liste
* @param startUs The number of microseconds to clip from the start of {@code timeline}. * @param startUs The number of microseconds to clip from the start of {@code timeline}.
* @param endUs The end position in microseconds for the clipped timeline relative to the start * @param endUs The end position in microseconds for the clipped timeline relative to the start
* of {@code timeline}, or {@link C#TIME_END_OF_SOURCE} to clip no samples from the end. * of {@code timeline}, or {@link C#TIME_END_OF_SOURCE} to clip no samples from the end.
* @throws IllegalClippingException If the timeline could not be clipped.
*/ */
public ClippingTimeline(Timeline timeline, long startUs, long endUs) { public ClippingTimeline(Timeline timeline, long startUs, long endUs)
throws IllegalClippingException {
super(timeline); super(timeline);
Assertions.checkArgument(timeline.getWindowCount() == 1); if (timeline.getPeriodCount() != 1) {
Assertions.checkArgument(timeline.getPeriodCount() == 1); throw new IllegalClippingException(IllegalClippingException.REASON_INVALID_PERIOD_COUNT);
}
if (timeline.getPeriod(0, new Period()).getPositionInWindowUs() != 0) {
throw new IllegalClippingException(IllegalClippingException.REASON_PERIOD_OFFSET_IN_WINDOW);
}
Window window = timeline.getWindow(0, new Window(), false); Window window = timeline.getWindow(0, new Window(), false);
long resolvedEndUs = endUs == C.TIME_END_OF_SOURCE ? window.durationUs : endUs; long resolvedEndUs = endUs == C.TIME_END_OF_SOURCE ? window.durationUs : endUs;
if (window.durationUs != C.TIME_UNSET) { if (window.durationUs != C.TIME_UNSET) {
if (resolvedEndUs > window.durationUs) { if (resolvedEndUs > window.durationUs) {
resolvedEndUs = window.durationUs; resolvedEndUs = window.durationUs;
} }
Assertions.checkArgument(startUs == 0 || window.isSeekable); if (startUs != 0 && !window.isSeekable) {
Assertions.checkArgument(startUs <= resolvedEndUs); throw new IllegalClippingException(IllegalClippingException.REASON_NOT_SEEKABLE_TO_START);
}
if (startUs > resolvedEndUs) {
throw new IllegalClippingException(IllegalClippingException.REASON_START_EXCEEDS_END);
}
} }
Period period = timeline.getPeriod(0, new Period());
Assertions.checkArgument(period.getPositionInWindowUs() == 0);
this.startUs = startUs; this.startUs = startUs;
this.endUs = resolvedEndUs; this.endUs = resolvedEndUs;
} }
......
...@@ -45,11 +45,11 @@ public final class MergingMediaSource implements MediaSource { ...@@ -45,11 +45,11 @@ public final class MergingMediaSource implements MediaSource {
@IntDef({REASON_WINDOWS_ARE_DYNAMIC, REASON_PERIOD_COUNT_MISMATCH}) @IntDef({REASON_WINDOWS_ARE_DYNAMIC, REASON_PERIOD_COUNT_MISMATCH})
public @interface Reason {} public @interface Reason {}
/** /**
* The merge failed because one of the sources being merged has a dynamic window. * One of the sources being merged has a dynamic window.
*/ */
public static final int REASON_WINDOWS_ARE_DYNAMIC = 0; public static final int REASON_WINDOWS_ARE_DYNAMIC = 0;
/** /**
* The merge failed because the sources have different period counts. * The sources have different period counts.
*/ */
public static final int REASON_PERIOD_COUNT_MISMATCH = 1; public static final int REASON_PERIOD_COUNT_MISMATCH = 1;
......
...@@ -32,7 +32,7 @@ import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; ...@@ -32,7 +32,7 @@ import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.io.IOException;
import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
...@@ -100,13 +100,25 @@ public class MediaSourceTestRunner { ...@@ -100,13 +100,25 @@ public class MediaSourceTestRunner {
* *
* @return The initial {@link Timeline}. * @return The initial {@link Timeline}.
*/ */
public Timeline prepareSource() { public Timeline prepareSource() throws IOException {
final IOException[] prepareError = new IOException[1];
runOnPlaybackThread(new Runnable() { runOnPlaybackThread(new Runnable() {
@Override @Override
public void run() { public void run() {
mediaSource.prepareSource(player, true, mediaSourceListener); mediaSource.prepareSource(player, true, mediaSourceListener);
try {
// TODO: This only catches errors that are set synchronously in prepareSource. To capture
// async errors we'll need to poll maybeThrowSourceInfoRefreshError until the first call
// to onSourceInfoRefreshed.
mediaSource.maybeThrowSourceInfoRefreshError();
} catch (IOException e) {
prepareError[0] = e;
}
} }
}); });
if (prepareError[0] != null) {
throw prepareError[0];
}
return assertTimelineChangeBlocking(); return assertTimelineChangeBlocking();
} }
......
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