Commit 38914f0b by andrewlewis Committed by Andrew Lewis

Allow clipping offset windows

ClippingMediaSource provides a timeline where the period and window have the
same start/end positions, so when clipping a child timeline with a non-zero
offset between the window and period it is necessary to clear the offset then
apply the offset to the start/end positions used in the ClippingMediaPeriod.

Also add a message to clipping exceptions.

Also fix adjustment of seeks to the start of the clipped view.

Issue: #3888

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=187292506
parent b2c44577
...@@ -7,6 +7,9 @@ ...@@ -7,6 +7,9 @@
* MediaSources: Allow reusing media sources after they have been released and * MediaSources: Allow reusing media sources after they have been released and
also in parallel to allow adding them multiple times to a concatenation. also in parallel to allow adding them multiple times to a concatenation.
([#3498](https://github.com/google/ExoPlayer/issues/3498)). ([#3498](https://github.com/google/ExoPlayer/issues/3498)).
* Allow clipping of child media sources where the period and window have a
non-zero offset with `ClippingMediaSource`
([#3888](https://github.com/google/ExoPlayer/issues/3888)).
### 2.7.0 ### ### 2.7.0 ###
......
...@@ -175,7 +175,7 @@ public final class ClippingMediaPeriod implements MediaPeriod, MediaPeriod.Callb ...@@ -175,7 +175,7 @@ public final class ClippingMediaPeriod implements MediaPeriod, MediaPeriod.Callb
@Override @Override
public long getAdjustedSeekPositionUs(long positionUs, SeekParameters seekParameters) { public long getAdjustedSeekPositionUs(long positionUs, SeekParameters seekParameters) {
if (positionUs == startUs) { if (positionUs == 0) {
// Never adjust seeks to the start of the clipped view. // Never adjust seeks to the start of the clipped view.
return 0; return 0;
} }
......
...@@ -29,53 +29,47 @@ import java.util.ArrayList; ...@@ -29,53 +29,47 @@ 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 must consist of a single period that starts at the beginning of the * positions. The wrapped source must consist of a single period.
* corresponding window.
*/ */
public final class ClippingMediaSource extends CompositeMediaSource<Void> { public final class ClippingMediaSource extends CompositeMediaSource<Void> {
/** /** Thrown when a {@link ClippingMediaSource} cannot clip its wrapped source. */
* Thrown when a {@link ClippingMediaSource} cannot clip its wrapped source.
*/
public static final class IllegalClippingException extends IOException { public static final class IllegalClippingException extends IOException {
/** /** The reason clipping failed. */
* The reason the clipping failed.
*/
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({REASON_INVALID_PERIOD_COUNT, REASON_PERIOD_OFFSET_IN_WINDOW, @IntDef({REASON_INVALID_PERIOD_COUNT, REASON_NOT_SEEKABLE_TO_START, REASON_START_EXCEEDS_END})
REASON_NOT_SEEKABLE_TO_START, REASON_START_EXCEEDS_END})
public @interface Reason {} public @interface Reason {}
/** /** The wrapped source doesn't consist of a single period. */
* The wrapped source doesn't consist of a single period.
*/
public static final int REASON_INVALID_PERIOD_COUNT = 0; public static final int REASON_INVALID_PERIOD_COUNT = 0;
/** /** The wrapped source is not seekable and a non-zero clipping start position was specified. */
* The wrapped source period doesn't start at the beginning of the corresponding window. public static final int REASON_NOT_SEEKABLE_TO_START = 1;
*/ /** The wrapped source ends before the specified clipping start position. */
public static final int REASON_PERIOD_OFFSET_IN_WINDOW = 1; public static final int REASON_START_EXCEEDS_END = 2;
/**
* 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. */
* The reason clipping failed. public final @Reason int reason;
*/
@Reason
public final int reason;
/** /**
* @param reason The reason clipping failed. * @param reason The reason clipping failed.
*/ */
public IllegalClippingException(@Reason int reason) { public IllegalClippingException(@Reason int reason) {
super("Illegal clipping: " + getReasonDescription(reason));
this.reason = reason; this.reason = reason;
} }
private static String getReasonDescription(@Reason int reason) {
switch (reason) {
case REASON_INVALID_PERIOD_COUNT:
return "invalid period count";
case REASON_NOT_SEEKABLE_TO_START:
return "not seekable to start";
case REASON_START_EXCEEDS_END:
return "start exceeds end";
default:
return "unknown";
}
}
} }
private final MediaSource mediaSource; private final MediaSource mediaSource;
...@@ -85,14 +79,16 @@ public final class ClippingMediaSource extends CompositeMediaSource<Void> { ...@@ -85,14 +79,16 @@ public final class ClippingMediaSource extends CompositeMediaSource<Void> {
private final ArrayList<ClippingMediaPeriod> mediaPeriods; private final ArrayList<ClippingMediaPeriod> mediaPeriods;
private IllegalClippingException clippingError; private IllegalClippingException clippingError;
private long periodStartUs;
private long periodEndUs;
/** /**
* Creates a new clipping source that wraps the specified source. * Creates a new clipping source that wraps the specified source.
* *
* @param mediaSource The single-period source to wrap. * @param mediaSource The single-period source to wrap.
* @param startPositionUs The start position within {@code mediaSource}'s timeline at which to * @param startPositionUs The start position within {@code mediaSource}'s window at which to start
* start providing samples, in microseconds. * providing samples, in microseconds.
* @param endPositionUs The end position within {@code mediaSource}'s timeline at which to stop * @param endPositionUs The end position within {@code mediaSource}'s window at which to stop
* providing samples, in microseconds. Specify {@link C#TIME_END_OF_SOURCE} to provide samples * providing samples, in microseconds. Specify {@link C#TIME_END_OF_SOURCE} to provide samples
* from the specified start point up to the end of the source. Specifying a position that * from the specified start point up to the end of the source. Specifying a position that
* exceeds the {@code mediaSource}'s duration will also result in the end of the source not * exceeds the {@code mediaSource}'s duration will also result in the end of the source not
...@@ -148,7 +144,7 @@ public final class ClippingMediaSource extends CompositeMediaSource<Void> { ...@@ -148,7 +144,7 @@ public final class ClippingMediaSource extends CompositeMediaSource<Void> {
ClippingMediaPeriod mediaPeriod = new ClippingMediaPeriod( ClippingMediaPeriod mediaPeriod = new ClippingMediaPeriod(
mediaSource.createPeriod(id, allocator), enableInitialDiscontinuity); mediaSource.createPeriod(id, allocator), enableInitialDiscontinuity);
mediaPeriods.add(mediaPeriod); mediaPeriods.add(mediaPeriod);
mediaPeriod.setClipping(startUs, endUs); mediaPeriod.setClipping(periodStartUs, periodEndUs);
return mediaPeriod; return mediaPeriod;
} }
...@@ -178,9 +174,16 @@ public final class ClippingMediaSource extends CompositeMediaSource<Void> { ...@@ -178,9 +174,16 @@ public final class ClippingMediaSource extends CompositeMediaSource<Void> {
return; return;
} }
refreshSourceInfo(clippingTimeline, manifest); refreshSourceInfo(clippingTimeline, manifest);
long windowPositionInPeriodUs =
timeline
.getWindow(/* windowIndex= */ 0, new Timeline.Window())
.getPositionInFirstPeriodUs();
periodStartUs = windowPositionInPeriodUs + startUs;
periodEndUs =
endUs == C.TIME_END_OF_SOURCE ? C.TIME_END_OF_SOURCE : windowPositionInPeriodUs + endUs;
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(periodStartUs, periodEndUs);
} }
} }
...@@ -191,6 +194,7 @@ public final class ClippingMediaSource extends CompositeMediaSource<Void> { ...@@ -191,6 +194,7 @@ public final class ClippingMediaSource extends CompositeMediaSource<Void> {
private final long startUs; private final long startUs;
private final long endUs; private final long endUs;
private final long durationUs;
/** /**
* Creates a new clipping timeline that wraps the specified timeline. * Creates a new clipping timeline that wraps the specified timeline.
...@@ -207,9 +211,6 @@ public final class ClippingMediaSource extends CompositeMediaSource<Void> { ...@@ -207,9 +211,6 @@ public final class ClippingMediaSource extends CompositeMediaSource<Void> {
if (timeline.getPeriodCount() != 1) { if (timeline.getPeriodCount() != 1) {
throw new IllegalClippingException(IllegalClippingException.REASON_INVALID_PERIOD_COUNT); 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) {
...@@ -225,13 +226,15 @@ public final class ClippingMediaSource extends CompositeMediaSource<Void> { ...@@ -225,13 +226,15 @@ public final class ClippingMediaSource extends CompositeMediaSource<Void> {
} }
this.startUs = startUs; this.startUs = startUs;
this.endUs = resolvedEndUs; this.endUs = resolvedEndUs;
durationUs = endUs == C.TIME_UNSET ? C.TIME_UNSET : endUs - startUs;
} }
@Override @Override
public Window getWindow(int windowIndex, Window window, boolean setIds, public Window getWindow(int windowIndex, Window window, boolean setIds,
long defaultPositionProjectionUs) { long defaultPositionProjectionUs) {
window = timeline.getWindow(0, window, setIds, defaultPositionProjectionUs); timeline.getWindow(/* windowIndex= */ 0, window, setIds, defaultPositionProjectionUs);
window.durationUs = endUs != C.TIME_UNSET ? endUs - startUs : C.TIME_UNSET; window.positionInFirstPeriodUs = 0;
window.durationUs = durationUs;
if (window.defaultPositionUs != C.TIME_UNSET) { if (window.defaultPositionUs != C.TIME_UNSET) {
window.defaultPositionUs = Math.max(window.defaultPositionUs, startUs); window.defaultPositionUs = Math.max(window.defaultPositionUs, startUs);
window.defaultPositionUs = endUs == C.TIME_UNSET ? window.defaultPositionUs window.defaultPositionUs = endUs == C.TIME_UNSET ? window.defaultPositionUs
...@@ -250,9 +253,9 @@ public final class ClippingMediaSource extends CompositeMediaSource<Void> { ...@@ -250,9 +253,9 @@ public final class ClippingMediaSource extends CompositeMediaSource<Void> {
@Override @Override
public Period getPeriod(int periodIndex, Period period, boolean setIds) { public Period getPeriod(int periodIndex, Period period, boolean setIds) {
period = timeline.getPeriod(0, period, setIds); timeline.getPeriod(/* periodIndex= */ 0, period, setIds);
period.durationUs = endUs != C.TIME_UNSET ? endUs - startUs : C.TIME_UNSET; return period.set(
return period; period.id, period.uid, /* windowIndex= */ 0, durationUs, /* positionInWindowUs= */ 0);
} }
} }
......
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