mirror of
https://github.com/samsonjs/media.git
synced 2026-03-25 09:25:53 +00:00
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
This commit is contained in:
parent
d909dc1863
commit
494a41c8b2
7 changed files with 134 additions and 46 deletions
|
|
@ -21,11 +21,13 @@ 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.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 java.io.IOException;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ClippingMediaSource}.
|
||||
|
|
@ -40,11 +42,12 @@ public final class ClippingMediaSourceTest extends InstrumentationTestCase {
|
|||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
window = new Timeline.Window();
|
||||
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 clippedTimeline = getClippedTimeline(timeline, 0, TEST_PERIOD_DURATION_US);
|
||||
|
|
@ -55,7 +58,7 @@ public final class ClippingMediaSourceTest extends InstrumentationTestCase {
|
|||
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);
|
||||
|
||||
// If the unseekable window isn't clipped, clipping succeeds.
|
||||
|
|
@ -64,12 +67,12 @@ public final class ClippingMediaSourceTest extends InstrumentationTestCase {
|
|||
// If the unseekable window is clipped, clipping fails.
|
||||
getClippedTimeline(timeline, 1, TEST_PERIOD_DURATION_US);
|
||||
fail("Expected clipping to fail.");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Expected.
|
||||
} catch (IllegalClippingException e) {
|
||||
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 clippedTimeline = getClippedTimeline(timeline, TEST_CLIP_AMOUNT_US,
|
||||
|
|
@ -80,7 +83,7 @@ public final class ClippingMediaSourceTest extends InstrumentationTestCase {
|
|||
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 clippedTimeline = getClippedTimeline(timeline, 0,
|
||||
|
|
@ -91,7 +94,7 @@ public final class ClippingMediaSourceTest extends InstrumentationTestCase {
|
|||
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 clippedTimeline = getClippedTimeline(timeline, TEST_CLIP_AMOUNT_US,
|
||||
|
|
@ -102,7 +105,7 @@ public final class ClippingMediaSourceTest extends InstrumentationTestCase {
|
|||
clippedTimeline.getPeriod(0, period).getDurationUs());
|
||||
}
|
||||
|
||||
public void testWindowAndPeriodIndices() {
|
||||
public void testWindowAndPeriodIndices() throws IOException {
|
||||
Timeline timeline = new FakeTimeline(
|
||||
new TimelineWindowDefinition(1, 111, true, false, TEST_PERIOD_DURATION_US));
|
||||
Timeline clippedTimeline = getClippedTimeline(timeline, TEST_CLIP_AMOUNT_US,
|
||||
|
|
@ -122,7 +125,8 @@ public final class ClippingMediaSourceTest extends InstrumentationTestCase {
|
|||
/**
|
||||
* 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);
|
||||
ClippingMediaSource mediaSource = new ClippingMediaSource(fakeMediaSource, startMs, endMs);
|
||||
MediaSourceTestRunner testRunner = new MediaSourceTestRunner(mediaSource, null);
|
||||
|
|
|
|||
|
|
@ -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.MediaSourceTestRunner;
|
||||
import com.google.android.exoplayer2.testutil.TimelineAsserts;
|
||||
import java.io.IOException;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
|
|
@ -32,7 +33,7 @@ import junit.framework.TestCase;
|
|||
*/
|
||||
public final class ConcatenatingMediaSourceTest extends TestCase {
|
||||
|
||||
public void testEmptyConcatenation() {
|
||||
public void testEmptyConcatenation() throws IOException {
|
||||
for (boolean atomic : new boolean[] {false, true}) {
|
||||
Timeline timeline = getConcatenatedTimeline(atomic);
|
||||
TimelineAsserts.assertEmpty(timeline);
|
||||
|
|
@ -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));
|
||||
TimelineAsserts.assertWindowIds(timeline, 111);
|
||||
TimelineAsserts.assertPeriodCounts(timeline, 3);
|
||||
|
|
@ -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),
|
||||
createFakeTimeline(3, 333) };
|
||||
Timeline timeline = getConcatenatedTimeline(false, timelines);
|
||||
|
|
@ -121,7 +122,7 @@ public final class ConcatenatingMediaSourceTest extends TestCase {
|
|||
}
|
||||
}
|
||||
|
||||
public void testNestedMediaSources() {
|
||||
public void testNestedMediaSources() throws IOException {
|
||||
Timeline timeline = getConcatenatedTimeline(false,
|
||||
getConcatenatedTimeline(false, createFakeTimeline(1, 111), createFakeTimeline(1, 222)),
|
||||
getConcatenatedTimeline(true, createFakeTimeline(1, 333), createFakeTimeline(1, 444)));
|
||||
|
|
@ -149,7 +150,7 @@ public final class ConcatenatingMediaSourceTest extends TestCase {
|
|||
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).
|
||||
Timeline[] timelines = { Timeline.EMPTY, createFakeTimeline(1, 111), Timeline.EMPTY,
|
||||
Timeline.EMPTY, createFakeTimeline(2, 222), Timeline.EMPTY, createFakeTimeline(3, 333),
|
||||
|
|
@ -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.
|
||||
Timeline timelineContentOnly = new FakeTimeline(
|
||||
new TimelineWindowDefinition(2, 111, true, false, 10 * C.MICROS_PER_SECOND));
|
||||
|
|
@ -231,7 +232,7 @@ public final class ConcatenatingMediaSourceTest extends TestCase {
|
|||
* the concatenated timeline.
|
||||
*/
|
||||
private static Timeline getConcatenatedTimeline(boolean isRepeatOneAtomic,
|
||||
Timeline... timelines) {
|
||||
Timeline... timelines) throws IOException {
|
||||
MediaSource[] mediaSources = new MediaSource[timelines.length];
|
||||
for (int i = 0; i < timelines.length; i++) {
|
||||
mediaSources[i] = new FakeMediaSource(timelines[i], null);
|
||||
|
|
|
|||
|
|
@ -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.MediaSourceTestRunner;
|
||||
import com.google.android.exoplayer2.testutil.TimelineAsserts;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import junit.framework.TestCase;
|
||||
import org.mockito.Mockito;
|
||||
|
|
@ -55,7 +56,7 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase {
|
|||
testRunner.release();
|
||||
}
|
||||
|
||||
public void testPlaylistChangesAfterPreparation() {
|
||||
public void testPlaylistChangesAfterPreparation() throws IOException {
|
||||
Timeline timeline = testRunner.prepareSource();
|
||||
TimelineAsserts.assertEmpty(timeline);
|
||||
|
||||
|
|
@ -171,7 +172,7 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase {
|
|||
childSources[3].assertReleased();
|
||||
}
|
||||
|
||||
public void testPlaylistChangesBeforePreparation() {
|
||||
public void testPlaylistChangesBeforePreparation() throws IOException {
|
||||
FakeMediaSource[] childSources = createMediaSources(4);
|
||||
mediaSource.addMediaSource(childSources[0]);
|
||||
mediaSource.addMediaSource(childSources[1]);
|
||||
|
|
@ -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
|
||||
// updates need to be triggered.
|
||||
FakeMediaSource[] fastSources = createMediaSources(2);
|
||||
|
|
@ -290,7 +291,7 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase {
|
|||
}
|
||||
}
|
||||
|
||||
public void testEmptyTimelineMediaSource() {
|
||||
public void testEmptyTimelineMediaSource() throws IOException {
|
||||
Timeline timeline = testRunner.prepareSource();
|
||||
TimelineAsserts.assertEmpty(timeline);
|
||||
|
||||
|
|
@ -426,7 +427,7 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase {
|
|||
verify(runnable).run();
|
||||
}
|
||||
|
||||
public void testCustomCallbackAfterPreparationAddSingle() {
|
||||
public void testCustomCallbackAfterPreparationAddSingle() throws IOException {
|
||||
DummyMainThread dummyMainThread = new DummyMainThread();
|
||||
try {
|
||||
testRunner.prepareSource();
|
||||
|
|
@ -444,7 +445,7 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase {
|
|||
}
|
||||
}
|
||||
|
||||
public void testCustomCallbackAfterPreparationAddMultiple() {
|
||||
public void testCustomCallbackAfterPreparationAddMultiple() throws IOException {
|
||||
DummyMainThread dummyMainThread = new DummyMainThread();
|
||||
try {
|
||||
testRunner.prepareSource();
|
||||
|
|
@ -464,7 +465,7 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase {
|
|||
}
|
||||
}
|
||||
|
||||
public void testCustomCallbackAfterPreparationAddSingleWithIndex() {
|
||||
public void testCustomCallbackAfterPreparationAddSingleWithIndex() throws IOException {
|
||||
DummyMainThread dummyMainThread = new DummyMainThread();
|
||||
try {
|
||||
testRunner.prepareSource();
|
||||
|
|
@ -482,7 +483,7 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase {
|
|||
}
|
||||
}
|
||||
|
||||
public void testCustomCallbackAfterPreparationAddMultipleWithIndex() {
|
||||
public void testCustomCallbackAfterPreparationAddMultipleWithIndex() throws IOException {
|
||||
DummyMainThread dummyMainThread = new DummyMainThread();
|
||||
try {
|
||||
testRunner.prepareSource();
|
||||
|
|
@ -502,7 +503,7 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase {
|
|||
}
|
||||
}
|
||||
|
||||
public void testCustomCallbackAfterPreparationRemove() {
|
||||
public void testCustomCallbackAfterPreparationRemove() throws IOException {
|
||||
DummyMainThread dummyMainThread = new DummyMainThread();
|
||||
try {
|
||||
testRunner.prepareSource();
|
||||
|
|
@ -528,7 +529,7 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase {
|
|||
}
|
||||
}
|
||||
|
||||
public void testCustomCallbackAfterPreparationMove() {
|
||||
public void testCustomCallbackAfterPreparationMove() throws IOException {
|
||||
DummyMainThread dummyMainThread = new DummyMainThread();
|
||||
try {
|
||||
testRunner.prepareSource();
|
||||
|
|
@ -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.
|
||||
Timeline timelineContentOnly = new FakeTimeline(
|
||||
new TimelineWindowDefinition(2, 111, true, false, 10 * C.MICROS_PER_SECOND));
|
||||
|
|
|
|||
|
|
@ -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.MediaSourceTestRunner;
|
||||
import com.google.android.exoplayer2.testutil.TimelineAsserts;
|
||||
import java.io.IOException;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
|
|
@ -39,7 +40,7 @@ public class LoopingMediaSourceTest extends TestCase {
|
|||
new TimelineWindowDefinition(1, 222), new TimelineWindowDefinition(1, 333));
|
||||
}
|
||||
|
||||
public void testSingleLoop() {
|
||||
public void testSingleLoop() throws IOException {
|
||||
Timeline timeline = getLoopingTimeline(multiWindowTimeline, 1);
|
||||
TimelineAsserts.assertWindowIds(timeline, 111, 222, 333);
|
||||
TimelineAsserts.assertPeriodCounts(timeline, 1, 1, 1);
|
||||
|
|
@ -57,7 +58,7 @@ public class LoopingMediaSourceTest extends TestCase {
|
|||
}
|
||||
}
|
||||
|
||||
public void testMultiLoop() {
|
||||
public void testMultiLoop() throws IOException {
|
||||
Timeline timeline = getLoopingTimeline(multiWindowTimeline, 3);
|
||||
TimelineAsserts.assertWindowIds(timeline, 111, 222, 333, 111, 222, 333, 111, 222, 333);
|
||||
TimelineAsserts.assertPeriodCounts(timeline, 1, 1, 1, 1, 1, 1, 1, 1, 1);
|
||||
|
|
@ -77,7 +78,7 @@ public class LoopingMediaSourceTest extends TestCase {
|
|||
}
|
||||
}
|
||||
|
||||
public void testInfiniteLoop() {
|
||||
public void testInfiniteLoop() throws IOException {
|
||||
Timeline timeline = getLoopingTimeline(multiWindowTimeline, Integer.MAX_VALUE);
|
||||
TimelineAsserts.assertWindowIds(timeline, 111, 222, 333);
|
||||
TimelineAsserts.assertPeriodCounts(timeline, 1, 1, 1);
|
||||
|
|
@ -94,7 +95,7 @@ public class LoopingMediaSourceTest extends TestCase {
|
|||
}
|
||||
}
|
||||
|
||||
public void testEmptyTimelineLoop() {
|
||||
public void testEmptyTimelineLoop() throws IOException {
|
||||
Timeline timeline = getLoopingTimeline(Timeline.EMPTY, 1);
|
||||
TimelineAsserts.assertEmpty(timeline);
|
||||
|
||||
|
|
@ -109,7 +110,7 @@ public class LoopingMediaSourceTest extends TestCase {
|
|||
* Wraps the specified timeline in a {@link LoopingMediaSource} and returns
|
||||
* 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);
|
||||
LoopingMediaSource mediaSource = new LoopingMediaSource(fakeMediaSource, loopCount);
|
||||
MediaSourceTestRunner testRunner = new MediaSourceTestRunner(mediaSource, null);
|
||||
|
|
|
|||
|
|
@ -15,20 +15,68 @@
|
|||
*/
|
||||
package com.google.android.exoplayer2.source;
|
||||
|
||||
import android.support.annotation.IntDef;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.upstream.Allocator;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* {@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 {
|
||||
|
||||
/**
|
||||
* 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 long startUs;
|
||||
private final long endUs;
|
||||
|
|
@ -36,6 +84,7 @@ public final class ClippingMediaSource implements MediaSource, MediaSource.Liste
|
|||
private final ArrayList<ClippingMediaPeriod> mediaPeriods;
|
||||
|
||||
private MediaSource.Listener sourceListener;
|
||||
private IllegalClippingException clippingError;
|
||||
|
||||
/**
|
||||
* Creates a new clipping source that wraps the specified source.
|
||||
|
|
@ -88,6 +137,9 @@ public final class ClippingMediaSource implements MediaSource, MediaSource.Liste
|
|||
|
||||
@Override
|
||||
public void maybeThrowSourceInfoRefreshError() throws IOException {
|
||||
if (clippingError != null) {
|
||||
throw clippingError;
|
||||
}
|
||||
mediaSource.maybeThrowSourceInfoRefreshError();
|
||||
}
|
||||
|
||||
|
|
@ -115,8 +167,17 @@ public final class ClippingMediaSource implements MediaSource, MediaSource.Liste
|
|||
|
||||
@Override
|
||||
public void onSourceInfoRefreshed(MediaSource source, Timeline timeline, Object manifest) {
|
||||
sourceListener.onSourceInfoRefreshed(this, new ClippingTimeline(timeline, startUs, endUs),
|
||||
manifest);
|
||||
if (clippingError != null) {
|
||||
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();
|
||||
for (int i = 0; i < count; i++) {
|
||||
mediaPeriods.get(i).setClipping(startUs, endUs);
|
||||
|
|
@ -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 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.
|
||||
* @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);
|
||||
Assertions.checkArgument(timeline.getWindowCount() == 1);
|
||||
Assertions.checkArgument(timeline.getPeriodCount() == 1);
|
||||
if (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);
|
||||
long resolvedEndUs = endUs == C.TIME_END_OF_SOURCE ? window.durationUs : endUs;
|
||||
if (window.durationUs != C.TIME_UNSET) {
|
||||
if (resolvedEndUs > window.durationUs) {
|
||||
resolvedEndUs = window.durationUs;
|
||||
}
|
||||
Assertions.checkArgument(startUs == 0 || window.isSeekable);
|
||||
Assertions.checkArgument(startUs <= resolvedEndUs);
|
||||
if (startUs != 0 && !window.isSeekable) {
|
||||
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.endUs = resolvedEndUs;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,11 +45,11 @@ public final class MergingMediaSource implements MediaSource {
|
|||
@IntDef({REASON_WINDOWS_ARE_DYNAMIC, REASON_PERIOD_COUNT_MISMATCH})
|
||||
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;
|
||||
/**
|
||||
* 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;
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
|||
import com.google.android.exoplayer2.upstream.Allocator;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
|
@ -100,13 +100,25 @@ public class MediaSourceTestRunner {
|
|||
*
|
||||
* @return The initial {@link Timeline}.
|
||||
*/
|
||||
public Timeline prepareSource() {
|
||||
public Timeline prepareSource() throws IOException {
|
||||
final IOException[] prepareError = new IOException[1];
|
||||
runOnPlaybackThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue