Add microsecond precision to MediaItem.ClippingConfiguration

PiperOrigin-RevId: 578881990
This commit is contained in:
tofunmi 2023-11-02 09:35:06 -07:00 committed by Copybara-Service
parent dab9eb33a4
commit 3253f1b5cd
6 changed files with 176 additions and 49 deletions

View File

@ -18,6 +18,8 @@ package androidx.media3.common;
import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkArgument;
import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.Assertions.checkState; import static androidx.media3.common.util.Assertions.checkState;
import static androidx.media3.common.util.Util.msToUs;
import static androidx.media3.common.util.Util.usToMs;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
@ -1837,20 +1839,20 @@ public final class MediaItem implements Bundleable {
/** Builder for {@link ClippingConfiguration} instances. */ /** Builder for {@link ClippingConfiguration} instances. */
public static final class Builder { public static final class Builder {
private long startPositionMs; private long startPositionUs;
private long endPositionMs; private long endPositionUs;
private boolean relativeToLiveWindow; private boolean relativeToLiveWindow;
private boolean relativeToDefaultPosition; private boolean relativeToDefaultPosition;
private boolean startsAtKeyFrame; private boolean startsAtKeyFrame;
/** Creates a new instance with default values. */ /** Creates a new instance with default values. */
public Builder() { public Builder() {
endPositionMs = C.TIME_END_OF_SOURCE; endPositionUs = C.TIME_END_OF_SOURCE;
} }
private Builder(ClippingConfiguration clippingConfiguration) { private Builder(ClippingConfiguration clippingConfiguration) {
startPositionMs = clippingConfiguration.startPositionMs; startPositionUs = clippingConfiguration.startPositionUs;
endPositionMs = clippingConfiguration.endPositionMs; endPositionUs = clippingConfiguration.endPositionUs;
relativeToLiveWindow = clippingConfiguration.relativeToLiveWindow; relativeToLiveWindow = clippingConfiguration.relativeToLiveWindow;
relativeToDefaultPosition = clippingConfiguration.relativeToDefaultPosition; relativeToDefaultPosition = clippingConfiguration.relativeToDefaultPosition;
startsAtKeyFrame = clippingConfiguration.startsAtKeyFrame; startsAtKeyFrame = clippingConfiguration.startsAtKeyFrame;
@ -1862,8 +1864,18 @@ public final class MediaItem implements Bundleable {
*/ */
@CanIgnoreReturnValue @CanIgnoreReturnValue
public Builder setStartPositionMs(@IntRange(from = 0) long startPositionMs) { public Builder setStartPositionMs(@IntRange(from = 0) long startPositionMs) {
Assertions.checkArgument(startPositionMs >= 0); return setStartPositionUs(msToUs(startPositionMs));
this.startPositionMs = startPositionMs; }
/**
* Sets the optional start position in microseconds which must be a value larger than or equal
* to zero (Default: 0).
*/
@UnstableApi
@CanIgnoreReturnValue
public Builder setStartPositionUs(@IntRange(from = 0) long startPositionUs) {
Assertions.checkArgument(startPositionUs >= 0);
this.startPositionUs = startPositionUs;
return this; return this;
} }
@ -1874,8 +1886,19 @@ public final class MediaItem implements Bundleable {
*/ */
@CanIgnoreReturnValue @CanIgnoreReturnValue
public Builder setEndPositionMs(long endPositionMs) { public Builder setEndPositionMs(long endPositionMs) {
Assertions.checkArgument(endPositionMs == C.TIME_END_OF_SOURCE || endPositionMs >= 0); return setEndPositionUs(msToUs(endPositionMs));
this.endPositionMs = endPositionMs; }
/**
* 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}).
*/
@UnstableApi
@CanIgnoreReturnValue
public Builder setEndPositionUs(long endPositionUs) {
Assertions.checkArgument(endPositionUs == C.TIME_END_OF_SOURCE || endPositionUs >= 0);
this.endPositionUs = endPositionUs;
return this; return this;
} }
@ -1932,12 +1955,23 @@ public final class MediaItem implements Bundleable {
@IntRange(from = 0) @IntRange(from = 0)
public final long startPositionMs; public final long startPositionMs;
/** The start position in microseconds. This is a value larger than or equal to zero. */
@UnstableApi
@IntRange(from = 0)
public final long startPositionUs;
/** /**
* The end position in milliseconds. This is a value larger than or equal to zero or {@link * The end position in milliseconds. This is a value larger than or equal to zero or {@link
* C#TIME_END_OF_SOURCE} to play to the end of the stream. * C#TIME_END_OF_SOURCE} to play to the end of the stream.
*/ */
public final long endPositionMs; public final long endPositionMs;
/**
* The end position in microseconds. This is a value larger than or equal to zero or {@link
* C#TIME_END_OF_SOURCE} to play to the end of the stream.
*/
@UnstableApi public final long endPositionUs;
/** /**
* Whether the clipping of active media periods moves with a live window. If {@code false}, * Whether the clipping of active media periods moves with a live window. If {@code false},
* playback ends when it reaches {@link #endPositionMs}. * playback ends when it reaches {@link #endPositionMs}.
@ -1954,8 +1988,10 @@ public final class MediaItem implements Bundleable {
public final boolean startsAtKeyFrame; public final boolean startsAtKeyFrame;
private ClippingConfiguration(Builder builder) { private ClippingConfiguration(Builder builder) {
this.startPositionMs = builder.startPositionMs; this.startPositionMs = usToMs(builder.startPositionUs);
this.endPositionMs = builder.endPositionMs; this.endPositionMs = usToMs(builder.endPositionUs);
this.startPositionUs = builder.startPositionUs;
this.endPositionUs = builder.endPositionUs;
this.relativeToLiveWindow = builder.relativeToLiveWindow; this.relativeToLiveWindow = builder.relativeToLiveWindow;
this.relativeToDefaultPosition = builder.relativeToDefaultPosition; this.relativeToDefaultPosition = builder.relativeToDefaultPosition;
this.startsAtKeyFrame = builder.startsAtKeyFrame; this.startsAtKeyFrame = builder.startsAtKeyFrame;
@ -1977,8 +2013,8 @@ public final class MediaItem implements Bundleable {
ClippingConfiguration other = (ClippingConfiguration) obj; ClippingConfiguration other = (ClippingConfiguration) obj;
return startPositionMs == other.startPositionMs return startPositionUs == other.startPositionUs
&& endPositionMs == other.endPositionMs && endPositionUs == other.endPositionUs
&& relativeToLiveWindow == other.relativeToLiveWindow && relativeToLiveWindow == other.relativeToLiveWindow
&& relativeToDefaultPosition == other.relativeToDefaultPosition && relativeToDefaultPosition == other.relativeToDefaultPosition
&& startsAtKeyFrame == other.startsAtKeyFrame; && startsAtKeyFrame == other.startsAtKeyFrame;
@ -1986,8 +2022,8 @@ public final class MediaItem implements Bundleable {
@Override @Override
public int hashCode() { public int hashCode() {
int result = (int) (startPositionMs ^ (startPositionMs >>> 32)); int result = (int) (startPositionUs ^ (startPositionUs >>> 32));
result = 31 * result + (int) (endPositionMs ^ (endPositionMs >>> 32)); result = 31 * result + (int) (endPositionUs ^ (endPositionUs >>> 32));
result = 31 * result + (relativeToLiveWindow ? 1 : 0); result = 31 * result + (relativeToLiveWindow ? 1 : 0);
result = 31 * result + (relativeToDefaultPosition ? 1 : 0); result = 31 * result + (relativeToDefaultPosition ? 1 : 0);
result = 31 * result + (startsAtKeyFrame ? 1 : 0); result = 31 * result + (startsAtKeyFrame ? 1 : 0);
@ -2001,6 +2037,8 @@ public final class MediaItem implements Bundleable {
private static final String FIELD_RELATIVE_TO_LIVE_WINDOW = Util.intToStringMaxRadix(2); private static final String FIELD_RELATIVE_TO_LIVE_WINDOW = Util.intToStringMaxRadix(2);
private static final String FIELD_RELATIVE_TO_DEFAULT_POSITION = Util.intToStringMaxRadix(3); private static final String FIELD_RELATIVE_TO_DEFAULT_POSITION = Util.intToStringMaxRadix(3);
private static final String FIELD_STARTS_AT_KEY_FRAME = Util.intToStringMaxRadix(4); private static final String FIELD_STARTS_AT_KEY_FRAME = Util.intToStringMaxRadix(4);
static final String FIELD_START_POSITION_US = Util.intToStringMaxRadix(5);
static final String FIELD_END_POSITION_US = Util.intToStringMaxRadix(6);
@UnstableApi @UnstableApi
@Override @Override
@ -2012,6 +2050,12 @@ public final class MediaItem implements Bundleable {
if (endPositionMs != UNSET.endPositionMs) { if (endPositionMs != UNSET.endPositionMs) {
bundle.putLong(FIELD_END_POSITION_MS, endPositionMs); bundle.putLong(FIELD_END_POSITION_MS, endPositionMs);
} }
if (startPositionUs != UNSET.startPositionUs) {
bundle.putLong(FIELD_START_POSITION_US, startPositionUs);
}
if (endPositionUs != UNSET.endPositionUs) {
bundle.putLong(FIELD_END_POSITION_US, endPositionUs);
}
if (relativeToLiveWindow != UNSET.relativeToLiveWindow) { if (relativeToLiveWindow != UNSET.relativeToLiveWindow) {
bundle.putBoolean(FIELD_RELATIVE_TO_LIVE_WINDOW, relativeToLiveWindow); bundle.putBoolean(FIELD_RELATIVE_TO_LIVE_WINDOW, relativeToLiveWindow);
} }
@ -2027,25 +2071,38 @@ public final class MediaItem implements Bundleable {
/** An object that can restore {@link ClippingConfiguration} from a {@link Bundle}. */ /** An object that can restore {@link ClippingConfiguration} from a {@link Bundle}. */
@UnstableApi @UnstableApi
public static final Creator<ClippingProperties> CREATOR = public static final Creator<ClippingProperties> CREATOR =
bundle -> bundle -> {
new ClippingConfiguration.Builder() ClippingConfiguration.Builder clippingConfiguration =
.setStartPositionMs( new ClippingConfiguration.Builder()
bundle.getLong( .setStartPositionMs(
FIELD_START_POSITION_MS, /* defaultValue= */ UNSET.startPositionMs)) bundle.getLong(
.setEndPositionMs( FIELD_START_POSITION_MS, /* defaultValue= */ UNSET.startPositionMs))
bundle.getLong(FIELD_END_POSITION_MS, /* defaultValue= */ UNSET.endPositionMs)) .setEndPositionMs(
.setRelativeToLiveWindow( bundle.getLong(
bundle.getBoolean( FIELD_END_POSITION_MS, /* defaultValue= */ UNSET.endPositionMs))
FIELD_RELATIVE_TO_LIVE_WINDOW, .setRelativeToLiveWindow(
/* defaultValue= */ UNSET.relativeToLiveWindow)) bundle.getBoolean(
.setRelativeToDefaultPosition( FIELD_RELATIVE_TO_LIVE_WINDOW,
bundle.getBoolean( /* defaultValue= */ UNSET.relativeToLiveWindow))
FIELD_RELATIVE_TO_DEFAULT_POSITION, .setRelativeToDefaultPosition(
/* defaultValue= */ UNSET.relativeToDefaultPosition)) bundle.getBoolean(
.setStartsAtKeyFrame( FIELD_RELATIVE_TO_DEFAULT_POSITION,
bundle.getBoolean( /* defaultValue= */ UNSET.relativeToDefaultPosition))
FIELD_STARTS_AT_KEY_FRAME, /* defaultValue= */ UNSET.startsAtKeyFrame)) .setStartsAtKeyFrame(
.buildClippingProperties(); bundle.getBoolean(
FIELD_STARTS_AT_KEY_FRAME, /* defaultValue= */ UNSET.startsAtKeyFrame));
long startPositionUs =
bundle.getLong(FIELD_START_POSITION_US, /* defaultValue= */ UNSET.startPositionUs);
if (startPositionUs != UNSET.startPositionUs) {
clippingConfiguration.setStartPositionUs(startPositionUs);
}
long endPositionUs =
bundle.getLong(FIELD_END_POSITION_US, /* defaultValue= */ UNSET.endPositionUs);
if (endPositionUs != UNSET.endPositionUs) {
clippingConfiguration.setEndPositionUs(endPositionUs);
}
return clippingConfiguration.buildClippingProperties();
};
} }
/** /**

View File

@ -15,6 +15,8 @@
*/ */
package androidx.media3.common; package androidx.media3.common;
import static androidx.media3.common.MediaItem.ClippingConfiguration.FIELD_END_POSITION_US;
import static androidx.media3.common.MediaItem.ClippingConfiguration.FIELD_START_POSITION_US;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertThrows;
@ -438,7 +440,9 @@ public class MediaItemTest {
// Please refrain from altering default values since doing so would cause issues with backwards // Please refrain from altering default values since doing so would cause issues with backwards
// compatibility. // compatibility.
assertThat(clippingConfiguration.startPositionMs).isEqualTo(0L); assertThat(clippingConfiguration.startPositionMs).isEqualTo(0L);
assertThat(clippingConfiguration.startPositionUs).isEqualTo(0L);
assertThat(clippingConfiguration.endPositionMs).isEqualTo(C.TIME_END_OF_SOURCE); assertThat(clippingConfiguration.endPositionMs).isEqualTo(C.TIME_END_OF_SOURCE);
assertThat(clippingConfiguration.endPositionUs).isEqualTo(C.TIME_END_OF_SOURCE);
assertThat(clippingConfiguration.relativeToLiveWindow).isFalse(); assertThat(clippingConfiguration.relativeToLiveWindow).isFalse();
assertThat(clippingConfiguration.relativeToDefaultPosition).isFalse(); assertThat(clippingConfiguration.relativeToDefaultPosition).isFalse();
assertThat(clippingConfiguration.startsAtKeyFrame).isFalse(); assertThat(clippingConfiguration.startsAtKeyFrame).isFalse();
@ -468,6 +472,7 @@ public class MediaItemTest {
MediaItem.ClippingConfiguration clippingConfiguration = MediaItem.ClippingConfiguration clippingConfiguration =
new MediaItem.ClippingConfiguration.Builder() new MediaItem.ClippingConfiguration.Builder()
.setStartPositionMs(1000L) .setStartPositionMs(1000L)
.setEndPositionUs(2000_031L)
.setStartsAtKeyFrame(true) .setStartsAtKeyFrame(true)
.build(); .build();
@ -477,6 +482,51 @@ public class MediaItemTest {
assertThat(clippingConfigurationFromBundle).isEqualTo(clippingConfiguration); assertThat(clippingConfigurationFromBundle).isEqualTo(clippingConfiguration);
} }
@Test
public void createClippingConfigurationInstance_viaBundleWithOnlyMs_yieldsEqualInstance() {
// Creates instance by setting some non-default values
MediaItem.ClippingConfiguration clippingConfiguration =
new MediaItem.ClippingConfiguration.Builder()
.setStartPositionMs(1000L)
.setEndPositionMs(2000L)
.setStartsAtKeyFrame(true)
.build();
Bundle clippingConfigurationBundle = clippingConfiguration.toBundle();
clippingConfigurationBundle.remove(FIELD_START_POSITION_US);
clippingConfigurationBundle.remove(FIELD_END_POSITION_US);
MediaItem.ClippingConfiguration clippingConfigurationFromBundle =
MediaItem.ClippingConfiguration.CREATOR.fromBundle(clippingConfigurationBundle);
assertThat(clippingConfigurationFromBundle).isEqualTo(clippingConfiguration);
}
@Test
public void createClippingConfigurationInstance_setsStartPositionInMsAndUs_fieldsAreConsistent() {
// Creates instance by setting some non-default values
MediaItem.ClippingConfiguration clippingConfiguration =
new MediaItem.ClippingConfiguration.Builder()
.setStartPositionMs(1000L)
.setStartPositionUs(200_203L)
.build();
assertThat(clippingConfiguration.startPositionMs).isEqualTo(200L);
assertThat(clippingConfiguration.startPositionUs).isEqualTo(200_203L);
}
@Test
public void createClippingConfigurationInstance_setsEndPositionInMsAndUs_fieldsAreConsistent() {
// Creates instance by setting some non-default values
MediaItem.ClippingConfiguration clippingConfiguration =
new MediaItem.ClippingConfiguration.Builder()
.setEndPositionUs(1000L)
.setEndPositionMs(C.TIME_END_OF_SOURCE)
.build();
assertThat(clippingConfiguration.endPositionMs).isEqualTo(C.TIME_END_OF_SOURCE);
assertThat(clippingConfiguration.endPositionMs).isEqualTo(C.TIME_END_OF_SOURCE);
}
@Test @Test
public void clippingConfigurationBuilder_throwsOnInvalidValues() { public void clippingConfigurationBuilder_throwsOnInvalidValues() {
MediaItem.ClippingConfiguration.Builder clippingConfigurationBuilder = MediaItem.ClippingConfiguration.Builder clippingConfigurationBuilder =

View File

@ -19,6 +19,7 @@ import static androidx.media3.common.util.Assertions.checkArgument;
import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.Assertions.checkState; import static androidx.media3.common.util.Assertions.checkState;
import static androidx.media3.common.util.Assertions.checkStateNotNull; import static androidx.media3.common.util.Assertions.checkStateNotNull;
import static androidx.media3.common.util.Util.usToMs;
import android.content.Context; import android.content.Context;
import android.net.Uri; import android.net.Uri;
@ -154,16 +155,17 @@ public final class ConcatenatingMediaSource2 extends CompositeMediaSource<Intege
@CanIgnoreReturnValue @CanIgnoreReturnValue
public Builder add(MediaItem mediaItem, long initialPlaceholderDurationMs) { public Builder add(MediaItem mediaItem, long initialPlaceholderDurationMs) {
checkNotNull(mediaItem); checkNotNull(mediaItem);
checkStateNotNull(
mediaSourceFactory,
"Must use useDefaultMediaSourceFactory or setMediaSourceFactory first.");
if (initialPlaceholderDurationMs == C.TIME_UNSET if (initialPlaceholderDurationMs == C.TIME_UNSET
&& mediaItem.clippingConfiguration.endPositionMs != C.TIME_END_OF_SOURCE) { && mediaItem.clippingConfiguration.endPositionMs != C.TIME_END_OF_SOURCE) {
// If the item is going to be clipped, we can provide a placeholder duration automatically. // If the item is going to be clipped, we can provide a placeholder duration automatically.
initialPlaceholderDurationMs = initialPlaceholderDurationMs =
mediaItem.clippingConfiguration.endPositionMs usToMs(
- mediaItem.clippingConfiguration.startPositionMs; mediaItem.clippingConfiguration.endPositionUs
- mediaItem.clippingConfiguration.startPositionUs);
} }
checkStateNotNull(
mediaSourceFactory,
"Must use useDefaultMediaSourceFactory or setMediaSourceFactory first.");
return add(mediaSourceFactory.createMediaSource(mediaItem), initialPlaceholderDurationMs); return add(mediaSourceFactory.createMediaSource(mediaItem), initialPlaceholderDurationMs);
} }
@ -364,7 +366,7 @@ public final class ConcatenatingMediaSource2 extends CompositeMediaSource<Intege
if (timeOffsetUs == null) { if (timeOffsetUs == null) {
return mediaTimeMs; return mediaTimeMs;
} }
return mediaTimeMs + Util.usToMs(timeOffsetUs); return mediaTimeMs + usToMs(timeOffsetUs);
} }
private boolean handleMessage(Message msg) { private boolean handleMessage(Message msg) {

View File

@ -554,15 +554,15 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
// internal methods // internal methods
private static MediaSource maybeClipMediaSource(MediaItem mediaItem, MediaSource mediaSource) { private static MediaSource maybeClipMediaSource(MediaItem mediaItem, MediaSource mediaSource) {
if (mediaItem.clippingConfiguration.startPositionMs == 0 if (mediaItem.clippingConfiguration.startPositionUs == 0
&& mediaItem.clippingConfiguration.endPositionMs == C.TIME_END_OF_SOURCE && mediaItem.clippingConfiguration.endPositionUs == C.TIME_END_OF_SOURCE
&& !mediaItem.clippingConfiguration.relativeToDefaultPosition) { && !mediaItem.clippingConfiguration.relativeToDefaultPosition) {
return mediaSource; return mediaSource;
} }
return new ClippingMediaSource( return new ClippingMediaSource(
mediaSource, mediaSource,
msToUs(mediaItem.clippingConfiguration.startPositionMs), mediaItem.clippingConfiguration.startPositionUs,
msToUs(mediaItem.clippingConfiguration.endPositionMs), mediaItem.clippingConfiguration.endPositionUs,
/* enableInitialDiscontinuity= */ !mediaItem.clippingConfiguration.startsAtKeyFrame, /* enableInitialDiscontinuity= */ !mediaItem.clippingConfiguration.startsAtKeyFrame,
/* allowDynamicClippingUpdates= */ mediaItem.clippingConfiguration.relativeToLiveWindow, /* allowDynamicClippingUpdates= */ mediaItem.clippingConfiguration.relativeToLiveWindow,
mediaItem.clippingConfiguration.relativeToDefaultPosition); mediaItem.clippingConfiguration.relativeToDefaultPosition);

View File

@ -17,7 +17,6 @@ package androidx.media3.transformer;
import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkArgument;
import static androidx.media3.common.util.Assertions.checkState; import static androidx.media3.common.util.Assertions.checkState;
import static androidx.media3.common.util.Util.msToUs;
import androidx.annotation.IntRange; import androidx.annotation.IntRange;
import androidx.media3.common.C; import androidx.media3.common.C;
@ -276,12 +275,12 @@ public final class EditedMediaItem {
} else { } else {
MediaItem.ClippingConfiguration clippingConfiguration = mediaItem.clippingConfiguration; MediaItem.ClippingConfiguration clippingConfiguration = mediaItem.clippingConfiguration;
checkArgument(!clippingConfiguration.relativeToDefaultPosition); checkArgument(!clippingConfiguration.relativeToDefaultPosition);
if (clippingConfiguration.endPositionMs == C.TIME_END_OF_SOURCE) { if (clippingConfiguration.endPositionUs == C.TIME_END_OF_SOURCE) {
presentationDurationUs = durationUs - msToUs(clippingConfiguration.startPositionMs); presentationDurationUs = durationUs - clippingConfiguration.startPositionUs;
} else { } else {
checkArgument(clippingConfiguration.endPositionMs <= durationUs); checkArgument(clippingConfiguration.endPositionUs <= durationUs);
presentationDurationUs = presentationDurationUs =
msToUs(clippingConfiguration.endPositionMs - clippingConfiguration.startPositionMs); clippingConfiguration.endPositionUs - clippingConfiguration.startPositionUs;
} }
} }
} }

View File

@ -102,6 +102,25 @@ public final class EditedMediaItemBuilderTest {
assertThat(editedMediaItem.presentationDurationUs).isEqualTo(200_000); assertThat(editedMediaItem.presentationDurationUs).isEqualTo(200_000);
} }
@Test
public void duration_withClippingConfigurationAndStartEndPositionInUs() {
MediaItem.ClippingConfiguration clippingConfiguration =
new MediaItem.ClippingConfiguration.Builder()
.setStartPositionUs(300_000)
.setEndPositionUs(500_000)
.build();
MediaItem mediaItem =
new MediaItem.Builder()
.setUri("Uri")
.setClippingConfiguration(clippingConfiguration)
.build();
EditedMediaItem editedMediaItem =
new EditedMediaItem.Builder(mediaItem).setDurationUs(1_000_000).build();
assertThat(editedMediaItem.presentationDurationUs).isEqualTo(200_000);
}
@Test @Test
public void duration_withClippingConfigurationAndStartPosition() { public void duration_withClippingConfigurationAndStartPosition() {
MediaItem.ClippingConfiguration clippingConfiguration = MediaItem.ClippingConfiguration clippingConfiguration =