Add MediaItem.ClippingProperties.Builder

PiperOrigin-RevId: 398232186
This commit is contained in:
ibaker 2021-09-22 15:12:09 +01:00 committed by bachinger
parent 1372300073
commit 59cd783dd4
2 changed files with 205 additions and 80 deletions

View File

@ -67,11 +67,9 @@ public final class MediaItem implements Bundleable {
@Nullable private String mediaId;
@Nullable private Uri uri;
@Nullable private String mimeType;
private long clipStartPositionMs;
private long clipEndPositionMs;
private boolean clipRelativeToLiveWindow;
private boolean clipRelativeToDefaultPosition;
private boolean clipStartsAtKeyFrame;
// TODO: Change this to ClippingProperties once all the deprecated individual setters are
// removed.
private ClippingProperties.Builder clippingProperties;
// TODO: Change this to @Nullable DrmConfiguration once all the deprecated individual setters
// are removed.
private DrmConfiguration.Builder drmConfiguration;
@ -88,7 +86,7 @@ public final class MediaItem implements Bundleable {
/** Creates a builder. */
@SuppressWarnings("deprecation") // Temporarily uses DrmConfiguration.Builder() constructor.
public Builder() {
clipEndPositionMs = C.TIME_END_OF_SOURCE;
clippingProperties = new ClippingProperties.Builder();
drmConfiguration = new DrmConfiguration.Builder();
streamKeys = Collections.emptyList();
subtitles = Collections.emptyList();
@ -97,11 +95,7 @@ public final class MediaItem implements Bundleable {
private Builder(MediaItem mediaItem) {
this();
clipEndPositionMs = mediaItem.clippingProperties.endPositionMs;
clipRelativeToLiveWindow = mediaItem.clippingProperties.relativeToLiveWindow;
clipRelativeToDefaultPosition = mediaItem.clippingProperties.relativeToDefaultPosition;
clipStartPositionMs = mediaItem.clippingProperties.startPositionMs;
clipStartsAtKeyFrame = mediaItem.clippingProperties.startsAtKeyFrame;
clippingProperties = mediaItem.clippingProperties.buildUpon();
mediaId = mediaItem.mediaId;
mediaMetadata = mediaItem.mediaMetadata;
liveConfiguration = mediaItem.liveConfiguration.buildUpon();
@ -168,52 +162,59 @@ public final class MediaItem implements Bundleable {
return this;
}
/** Sets the {@link ClippingProperties}, defaults to {@link ClippingProperties#UNSET}. */
public Builder setClippingProperties(ClippingProperties clippingProperties) {
this.clippingProperties = clippingProperties.buildUpon();
return this;
}
/**
* Sets the optional start position in milliseconds which must be a value larger than or equal
* to zero (Default: 0).
* @deprecated Use {@link #setClippingProperties(ClippingProperties)} and {@link
* ClippingProperties.Builder#setStartPositionMs(long)} instead.
*/
@Deprecated
public Builder setClipStartPositionMs(@IntRange(from = 0) long startPositionMs) {
Assertions.checkArgument(startPositionMs >= 0);
this.clipStartPositionMs = startPositionMs;
clippingProperties.setStartPositionMs(startPositionMs);
return this;
}
/**
* Sets the optional end position in milliseconds which must be a value larger than or equal to
* zero, or {@link C#TIME_END_OF_SOURCE} to end when playback reaches the end of media (Default:
* {@link C#TIME_END_OF_SOURCE}).
* @deprecated Use {@link #setClippingProperties(ClippingProperties)} and {@link
* ClippingProperties.Builder#setEndPositionMs(long)} instead.
*/
@Deprecated
public Builder setClipEndPositionMs(long endPositionMs) {
Assertions.checkArgument(endPositionMs == C.TIME_END_OF_SOURCE || endPositionMs >= 0);
this.clipEndPositionMs = endPositionMs;
clippingProperties.setEndPositionMs(endPositionMs);
return this;
}
/**
* Sets whether the start/end positions should move with the live window for live streams. If
* {@code false}, live streams end when playback reaches the end position in live window seen
* when the media is first loaded (Default: {@code false}).
* @deprecated Use {@link #setClippingProperties(ClippingProperties)} and {@link
* ClippingProperties.Builder#setRelativeToLiveWindow(boolean)} instead.
*/
@Deprecated
public Builder setClipRelativeToLiveWindow(boolean relativeToLiveWindow) {
this.clipRelativeToLiveWindow = relativeToLiveWindow;
clippingProperties.setRelativeToLiveWindow(relativeToLiveWindow);
return this;
}
/**
* Sets whether the start position and the end position are relative to the default position in
* the window (Default: {@code false}).
* @deprecated Use {@link #setClippingProperties(ClippingProperties)} and {@link
* ClippingProperties.Builder#setRelativeToDefaultPosition(boolean)} instead.
*/
@Deprecated
public Builder setClipRelativeToDefaultPosition(boolean relativeToDefaultPosition) {
this.clipRelativeToDefaultPosition = relativeToDefaultPosition;
clippingProperties.setRelativeToDefaultPosition(relativeToDefaultPosition);
return this;
}
/**
* Sets whether the start point is guaranteed to be a key frame. If {@code false}, the playback
* transition into the clip may not be seamless (Default: {@code false}).
* @deprecated Use {@link #setClippingProperties(ClippingProperties)} and {@link
* ClippingProperties.Builder#setStartsAtKeyFrame(boolean)} instead.
*/
@Deprecated
public Builder setClipStartsAtKeyFrame(boolean startsAtKeyFrame) {
this.clipStartsAtKeyFrame = startsAtKeyFrame;
clippingProperties.setStartsAtKeyFrame(startsAtKeyFrame);
return this;
}
@ -503,12 +504,7 @@ public final class MediaItem implements Bundleable {
}
return new MediaItem(
mediaId != null ? mediaId : DEFAULT_MEDIA_ID,
new ClippingProperties(
clipStartPositionMs,
clipEndPositionMs,
clipRelativeToLiveWindow,
clipRelativeToDefaultPosition,
clipStartsAtKeyFrame),
clippingProperties.build(),
playbackProperties,
liveConfiguration.build(),
mediaMetadata != null ? mediaMetadata : MediaMetadata.EMPTY);
@ -1347,7 +1343,89 @@ public final class MediaItem implements Bundleable {
/** Optionally clips the media item to a custom start and end position. */
public static final class ClippingProperties implements Bundleable {
/** A clipping properties configuration with default values. */
public static final ClippingProperties UNSET = new ClippingProperties.Builder().build();
/** Builder for {@link ClippingProperties} instances. */
public static final class Builder {
private long startPositionMs;
private long endPositionMs;
private boolean relativeToLiveWindow;
private boolean relativeToDefaultPosition;
private boolean startsAtKeyFrame;
/** Constructs an instance. */
public Builder() {
endPositionMs = C.TIME_END_OF_SOURCE;
}
private Builder(ClippingProperties clippingProperties) {
startPositionMs = clippingProperties.startPositionMs;
endPositionMs = clippingProperties.endPositionMs;
relativeToLiveWindow = clippingProperties.relativeToLiveWindow;
relativeToDefaultPosition = clippingProperties.relativeToDefaultPosition;
startsAtKeyFrame = clippingProperties.startsAtKeyFrame;
}
/**
* Sets the optional start position in milliseconds which must be a value larger than or equal
* to zero (Default: 0).
*/
public Builder setStartPositionMs(@IntRange(from = 0) long startPositionMs) {
Assertions.checkArgument(startPositionMs >= 0);
this.startPositionMs = startPositionMs;
return this;
}
/**
* Sets the optional end position in milliseconds which must be a value larger than or equal
* to zero, or {@link C#TIME_END_OF_SOURCE} to end when playback reaches the end of media
* (Default: {@link C#TIME_END_OF_SOURCE}).
*/
public Builder setEndPositionMs(long endPositionMs) {
Assertions.checkArgument(endPositionMs == C.TIME_END_OF_SOURCE || endPositionMs >= 0);
this.endPositionMs = endPositionMs;
return this;
}
/**
* Sets whether the start/end positions should move with the live window for live streams. If
* {@code false}, live streams end when playback reaches the end position in live window seen
* when the media is first loaded (Default: {@code false}).
*/
public Builder setRelativeToLiveWindow(boolean relativeToLiveWindow) {
this.relativeToLiveWindow = relativeToLiveWindow;
return this;
}
/**
* Sets whether the start position and the end position are relative to the default position
* in the window (Default: {@code false}).
*/
public Builder setRelativeToDefaultPosition(boolean relativeToDefaultPosition) {
this.relativeToDefaultPosition = relativeToDefaultPosition;
return this;
}
/**
* Sets whether the start point is guaranteed to be a key frame. If {@code false}, the
* playback transition into the clip may not be seamless (Default: {@code false}).
*/
public Builder setStartsAtKeyFrame(boolean startsAtKeyFrame) {
this.startsAtKeyFrame = startsAtKeyFrame;
return this;
}
/**
* Returns a {@link ClippingProperties} instance initialized with the values of this builder.
*/
public ClippingProperties build() {
return new ClippingProperties(this);
}
}
/** The start position in milliseconds. This is a value larger than or equal to zero. */
@IntRange(from = 0)
public final long startPositionMs;
/**
@ -1371,17 +1449,17 @@ public final class MediaItem implements Bundleable {
/** Sets whether the start point is guaranteed to be a key frame. */
public final boolean startsAtKeyFrame;
private ClippingProperties(
long startPositionMs,
long endPositionMs,
boolean relativeToLiveWindow,
boolean relativeToDefaultPosition,
boolean startsAtKeyFrame) {
this.startPositionMs = startPositionMs;
this.endPositionMs = endPositionMs;
this.relativeToLiveWindow = relativeToLiveWindow;
this.relativeToDefaultPosition = relativeToDefaultPosition;
this.startsAtKeyFrame = startsAtKeyFrame;
private ClippingProperties(Builder builder) {
this.startPositionMs = builder.startPositionMs;
this.endPositionMs = builder.endPositionMs;
this.relativeToLiveWindow = builder.relativeToLiveWindow;
this.relativeToDefaultPosition = builder.relativeToDefaultPosition;
this.startsAtKeyFrame = builder.startsAtKeyFrame;
}
/** Returns a {@link Builder} initialized with the values of this instance. */
public Builder buildUpon() {
return new Builder(this);
}
@Override
@ -1445,13 +1523,20 @@ public final class MediaItem implements Bundleable {
/** Object that can restore {@link ClippingProperties} from a {@link Bundle}. */
public static final Creator<ClippingProperties> CREATOR =
bundle ->
new ClippingProperties(
bundle.getLong(keyForField(FIELD_START_POSITION_MS), /* defaultValue= */ 0),
bundle.getLong(
keyForField(FIELD_END_POSITION_MS), /* defaultValue= */ C.TIME_END_OF_SOURCE),
bundle.getBoolean(keyForField(FIELD_RELATIVE_TO_LIVE_WINDOW), false),
bundle.getBoolean(keyForField(FIELD_RELATIVE_TO_DEFAULT_POSITION), false),
bundle.getBoolean(keyForField(FIELD_STARTS_AT_KEY_FRAME), false));
new ClippingProperties.Builder()
.setStartPositionMs(
bundle.getLong(keyForField(FIELD_START_POSITION_MS), /* defaultValue= */ 0))
.setEndPositionMs(
bundle.getLong(
keyForField(FIELD_END_POSITION_MS),
/* defaultValue= */ C.TIME_END_OF_SOURCE))
.setRelativeToLiveWindow(
bundle.getBoolean(keyForField(FIELD_RELATIVE_TO_LIVE_WINDOW), false))
.setRelativeToDefaultPosition(
bundle.getBoolean(keyForField(FIELD_RELATIVE_TO_DEFAULT_POSITION), false))
.setStartsAtKeyFrame(
bundle.getBoolean(keyForField(FIELD_STARTS_AT_KEY_FRAME), false))
.build();
private static String keyForField(@ClippingProperties.FieldNumber int field) {
return Integer.toString(field, Character.MAX_RADIX);
@ -1589,13 +1674,7 @@ public final class MediaItem implements Bundleable {
Bundle clippingPropertiesBundle = bundle.getBundle(keyForField(FIELD_CLIPPING_PROPERTIES));
ClippingProperties clippingProperties;
if (clippingPropertiesBundle == null) {
clippingProperties =
new ClippingProperties(
/* startPositionMs= */ 0,
/* endPositionMs= */ C.TIME_END_OF_SOURCE,
/* relativeToLiveWindow= */ false,
/* relativeToDefaultPosition= */ false,
/* startsAtKeyFrame= */ false);
clippingProperties = ClippingProperties.UNSET;
} else {
clippingProperties = ClippingProperties.CREATOR.fromBundle(clippingPropertiesBundle);
}

View File

@ -307,6 +307,58 @@ public class MediaItemTest {
}
@Test
public void builderSetClippingProperties() {
MediaItem mediaItem =
new MediaItem.Builder()
.setUri(URI_STRING)
.setClippingProperties(
new MediaItem.ClippingProperties.Builder()
.setStartPositionMs(1000L)
.setEndPositionMs(2000L)
.setRelativeToLiveWindow(true)
.setRelativeToDefaultPosition(true)
.setStartsAtKeyFrame(true)
.build())
.build();
assertThat(mediaItem.clippingProperties.startPositionMs).isEqualTo(1000L);
assertThat(mediaItem.clippingProperties.endPositionMs).isEqualTo(2000L);
assertThat(mediaItem.clippingProperties.relativeToLiveWindow).isTrue();
assertThat(mediaItem.clippingProperties.relativeToDefaultPosition).isTrue();
assertThat(mediaItem.clippingProperties.startsAtKeyFrame).isTrue();
}
@Test
public void clippingPropertiesDefaults() {
MediaItem.ClippingProperties clippingProperties =
new MediaItem.ClippingProperties.Builder().build();
assertThat(clippingProperties.startPositionMs).isEqualTo(0L);
assertThat(clippingProperties.endPositionMs).isEqualTo(C.TIME_END_OF_SOURCE);
assertThat(clippingProperties.relativeToLiveWindow).isFalse();
assertThat(clippingProperties.relativeToDefaultPosition).isFalse();
assertThat(clippingProperties.startsAtKeyFrame).isFalse();
assertThat(clippingProperties).isEqualTo(MediaItem.ClippingProperties.UNSET);
}
@Test
public void clippingPropertiesBuilder_throwsOnInvalidValues() {
MediaItem.ClippingProperties.Builder clippingPropertiesBuilder =
new MediaItem.ClippingProperties.Builder();
assertThrows(
IllegalArgumentException.class, () -> clippingPropertiesBuilder.setStartPositionMs(-1));
assertThrows(
IllegalArgumentException.class, () -> clippingPropertiesBuilder.setEndPositionMs(-1));
MediaItem.ClippingProperties clippingProperties = clippingPropertiesBuilder.build();
// Check neither of the setters succeeded in mutating the builder.
assertThat(clippingProperties.startPositionMs).isEqualTo(0L);
assertThat(clippingProperties.endPositionMs).isEqualTo(C.TIME_END_OF_SOURCE);
}
@Test
@SuppressWarnings("deprecation") // Testing deprecated setter.
public void builderSetStartPositionMs_setsStartPositionMs() {
MediaItem mediaItem =
new MediaItem.Builder().setUri(URI_STRING).setClipStartPositionMs(1000L).build();
@ -315,13 +367,7 @@ public class MediaItemTest {
}
@Test
public void builderSetStartPositionMs_zeroByDefault() {
MediaItem mediaItem = new MediaItem.Builder().setUri(URI_STRING).build();
assertThat(mediaItem.clippingProperties.startPositionMs).isEqualTo(0);
}
@Test
@SuppressWarnings("deprecation") // Testing deprecated setter.
public void builderSetStartPositionMs_negativeValue_throws() {
MediaItem.Builder builder = new MediaItem.Builder();
@ -329,6 +375,7 @@ public class MediaItemTest {
}
@Test
@SuppressWarnings("deprecation") // Testing deprecated setter.
public void builderSetEndPositionMs_setsEndPositionMs() {
MediaItem mediaItem =
new MediaItem.Builder().setUri(URI_STRING).setClipEndPositionMs(1000L).build();
@ -337,13 +384,7 @@ public class MediaItemTest {
}
@Test
public void builderSetEndPositionMs_timeEndOfSourceByDefault() {
MediaItem mediaItem = new MediaItem.Builder().setUri(URI_STRING).build();
assertThat(mediaItem.clippingProperties.endPositionMs).isEqualTo(C.TIME_END_OF_SOURCE);
}
@Test
@SuppressWarnings("deprecation") // Testing deprecated setter.
public void builderSetEndPositionMs_timeEndOfSource_setsEndPositionMs() {
MediaItem mediaItem =
new MediaItem.Builder()
@ -356,6 +397,7 @@ public class MediaItemTest {
}
@Test
@SuppressWarnings("deprecation") // Testing deprecated setter.
public void builderSetEndPositionMs_negativeValue_throws() {
MediaItem.Builder builder = new MediaItem.Builder();
@ -363,6 +405,7 @@ public class MediaItemTest {
}
@Test
@SuppressWarnings("deprecation") // Testing deprecated setter.
public void builderSetClippingFlags_setsClippingFlags() {
MediaItem mediaItem =
new MediaItem.Builder()
@ -561,11 +604,14 @@ public class MediaItemTest {
new MediaItem.Builder()
.setAdsConfiguration(
new MediaItem.AdsConfiguration.Builder(Uri.parse(URI_STRING)).build())
.setClipEndPositionMs(1000)
.setClipRelativeToDefaultPosition(true)
.setClipRelativeToLiveWindow(true)
.setClipStartPositionMs(100)
.setClipStartsAtKeyFrame(true)
.setClippingProperties(
new MediaItem.ClippingProperties.Builder()
.setEndPositionMs(1000)
.setRelativeToDefaultPosition(true)
.setRelativeToLiveWindow(true)
.setStartPositionMs(100)
.setStartsAtKeyFrame(true)
.build())
.setCustomCacheKey("key")
.setDrmConfiguration(
new MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID)