Add min/max live offset to MediaItem.LiveConfiguration.

This allows the user and the media to define bounds in which the live offset
can vary.

Issue: #4904
PiperOrigin-RevId: 339214605
This commit is contained in:
tonihei 2020-10-27 10:12:48 +00:00 committed by Oliver Woodman
parent d436a69d8f
commit e57193676a
5 changed files with 175 additions and 8 deletions

View File

@ -77,6 +77,8 @@ public final class MediaItem {
@Nullable private Object tag;
@Nullable private MediaMetadata mediaMetadata;
private long liveTargetOffsetMs;
private long liveMinOffsetMs;
private long liveMaxOffsetMs;
private float liveMinPlaybackSpeed;
private float liveMaxPlaybackSpeed;
@ -88,6 +90,8 @@ public final class MediaItem {
streamKeys = Collections.emptyList();
subtitles = Collections.emptyList();
liveTargetOffsetMs = C.TIME_UNSET;
liveMinOffsetMs = C.TIME_UNSET;
liveMaxOffsetMs = C.TIME_UNSET;
liveMinPlaybackSpeed = C.RATE_UNSET;
liveMaxPlaybackSpeed = C.RATE_UNSET;
}
@ -102,6 +106,8 @@ public final class MediaItem {
mediaId = mediaItem.mediaId;
mediaMetadata = mediaItem.mediaMetadata;
liveTargetOffsetMs = mediaItem.liveConfiguration.targetLiveOffsetMs;
liveMinOffsetMs = mediaItem.liveConfiguration.minLiveOffsetMs;
liveMaxOffsetMs = mediaItem.liveConfiguration.maxLiveOffsetMs;
liveMinPlaybackSpeed = mediaItem.liveConfiguration.minPlaybackSpeed;
liveMaxPlaybackSpeed = mediaItem.liveConfiguration.maxPlaybackSpeed;
@Nullable PlaybackProperties playbackProperties = mediaItem.playbackProperties;
@ -436,6 +442,32 @@ public final class MediaItem {
return this;
}
/**
* Sets the optional minimum offset from the live edge for live streams, in milliseconds.
*
* <p>See {@code Player#getCurrentLiveOffset()}.
*
* @param liveMinOffsetMs The minimum allowed live offset, in milliseconds, or {@link
* C#TIME_UNSET} to use the media-defined default.
*/
public Builder setLiveMinOffsetMs(long liveMinOffsetMs) {
this.liveMinOffsetMs = liveMinOffsetMs;
return this;
}
/**
* Sets the optional maximum offset from the live edge for live streams, in milliseconds.
*
* <p>See {@code Player#getCurrentLiveOffset()}.
*
* @param liveMaxOffsetMs The maximum allowed live offset, in milliseconds, or {@link
* C#TIME_UNSET} to use the media-defined default.
*/
public Builder setLiveMaxOffsetMs(long liveMaxOffsetMs) {
this.liveMaxOffsetMs = liveMaxOffsetMs;
return this;
}
/**
* Sets the optional minimum playback speed for live stream speed adjustment.
*
@ -519,7 +551,12 @@ public final class MediaItem {
clipRelativeToDefaultPosition,
clipStartsAtKeyFrame),
playbackProperties,
new LiveConfiguration(liveTargetOffsetMs, liveMinPlaybackSpeed, liveMaxPlaybackSpeed),
new LiveConfiguration(
liveTargetOffsetMs,
liveMinOffsetMs,
liveMaxOffsetMs,
liveMinPlaybackSpeed,
liveMaxPlaybackSpeed),
mediaMetadata != null ? mediaMetadata : new MediaMetadata.Builder().build());
}
}
@ -712,7 +749,7 @@ public final class MediaItem {
/** A live playback configuration with unset values. */
public static final LiveConfiguration UNSET =
new LiveConfiguration(C.TIME_UNSET, C.RATE_UNSET, C.RATE_UNSET);
new LiveConfiguration(C.TIME_UNSET, C.TIME_UNSET, C.TIME_UNSET, C.RATE_UNSET, C.RATE_UNSET);
/**
* Target live offset, in milliseconds, or {@link C#TIME_UNSET} to use the media-defined
@ -720,6 +757,18 @@ public final class MediaItem {
*/
public final long targetLiveOffsetMs;
/**
* The minimum allowed live offset, in milliseconds, or {@link C#TIME_UNSET} to use the
* media-defined default.
*/
public final long minLiveOffsetMs;
/**
* The maximum allowed live offset, in milliseconds, or {@link C#TIME_UNSET} to use the
* media-defined default.
*/
public final long maxLiveOffsetMs;
/** Minimum playback speed, or {@link C#RATE_UNSET} to use the media-defined default. */
public final float minPlaybackSpeed;
@ -731,14 +780,24 @@ public final class MediaItem {
*
* @param targetLiveOffsetMs Target live offset, in milliseconds, or {@link C#TIME_UNSET} to use
* the media-defined default.
* @param minLiveOffsetMs The minimum allowed live offset, in milliseconds, or {@link
* C#TIME_UNSET} to use the media-defined default.
* @param maxLiveOffsetMs The maximum allowed live offset, in milliseconds, or {@link
* C#TIME_UNSET} to use the media-defined default.
* @param minPlaybackSpeed Minimum playback speed, or {@link C#RATE_UNSET} to use the
* media-defined default.
* @param maxPlaybackSpeed Maximum playback speed, or {@link C#RATE_UNSET} to use the
* media-defined default.
*/
public LiveConfiguration(
long targetLiveOffsetMs, float minPlaybackSpeed, float maxPlaybackSpeed) {
long targetLiveOffsetMs,
long minLiveOffsetMs,
long maxLiveOffsetMs,
float minPlaybackSpeed,
float maxPlaybackSpeed) {
this.targetLiveOffsetMs = targetLiveOffsetMs;
this.minLiveOffsetMs = minLiveOffsetMs;
this.maxLiveOffsetMs = maxLiveOffsetMs;
this.minPlaybackSpeed = minPlaybackSpeed;
this.maxPlaybackSpeed = maxPlaybackSpeed;
}
@ -754,6 +813,8 @@ public final class MediaItem {
LiveConfiguration other = (LiveConfiguration) obj;
return targetLiveOffsetMs == other.targetLiveOffsetMs
&& minLiveOffsetMs == other.minLiveOffsetMs
&& maxLiveOffsetMs == other.maxLiveOffsetMs
&& minPlaybackSpeed == other.minPlaybackSpeed
&& maxPlaybackSpeed == other.maxPlaybackSpeed;
}
@ -761,6 +822,8 @@ public final class MediaItem {
@Override
public int hashCode() {
int result = (int) (targetLiveOffsetMs ^ (targetLiveOffsetMs >>> 32));
result = 31 * result + (int) (minLiveOffsetMs ^ (minLiveOffsetMs >>> 32));
result = 31 * result + (int) (maxLiveOffsetMs ^ (maxLiveOffsetMs >>> 32));
result = 31 * result + (minPlaybackSpeed != 0 ? Float.floatToIntBits(minPlaybackSpeed) : 0);
result = 31 * result + (maxPlaybackSpeed != 0 ? Float.floatToIntBits(maxPlaybackSpeed) : 0);
return result;

View File

@ -295,7 +295,7 @@ public class MediaItemTest {
}
@Test
public void builderSetLiveTargetLatencyMs_setsLiveTargetLatencyMs() {
public void builderSetLiveTargetOffsetMs_setsLiveTargetOffsetMs() {
MediaItem mediaItem =
new MediaItem.Builder().setUri(URI_STRING).setLiveTargetOffsetMs(10_000).build();
@ -318,6 +318,22 @@ public class MediaItemTest {
assertThat(mediaItem.liveConfiguration.maxPlaybackSpeed).isEqualTo(1.1f);
}
@Test
public void builderSetMinLiveOffset_setsMinLiveOffset() {
MediaItem mediaItem =
new MediaItem.Builder().setUri(URI_STRING).setLiveMinOffsetMs(1234).build();
assertThat(mediaItem.liveConfiguration.minLiveOffsetMs).isEqualTo(1234);
}
@Test
public void builderSetMaxLiveOffset_setsMaxLiveOffset() {
MediaItem mediaItem =
new MediaItem.Builder().setUri(URI_STRING).setLiveMaxOffsetMs(1234).build();
assertThat(mediaItem.liveConfiguration.maxLiveOffsetMs).isEqualTo(1234);
}
@Test
public void buildUpon_equalsToOriginal() {
MediaItem mediaItem =
@ -346,6 +362,8 @@ public class MediaItemTest {
.setLiveTargetOffsetMs(20_000)
.setLiveMinPlaybackSpeed(.9f)
.setLiveMaxPlaybackSpeed(1.1f)
.setLiveMinOffsetMs(2222)
.setLiveMaxOffsetMs(4444)
.setSubtitles(
Collections.singletonList(
new MediaItem.Subtitle(

View File

@ -110,6 +110,8 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
@Nullable private List<StreamKey> streamKeys;
@Nullable private LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private long liveTargetOffsetMs;
private long liveMinOffsetMs;
private long liveMaxOffsetMs;
private float liveMinSpeed;
private float liveMaxSpeed;
@ -161,6 +163,8 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
supportedTypes[i] = mediaSourceFactories.keyAt(i);
}
liveTargetOffsetMs = C.TIME_UNSET;
liveMinOffsetMs = C.TIME_UNSET;
liveMaxOffsetMs = C.TIME_UNSET;
liveMinSpeed = C.RATE_UNSET;
liveMaxSpeed = C.RATE_UNSET;
}
@ -201,6 +205,30 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
return this;
}
/**
* Sets the minimum offset from the live edge for live streams, in milliseconds.
*
* @param liveMinOffsetMs The minimum allowed live offset, in milliseconds, or {@link
* C#TIME_UNSET} to use the media-defined default.
* @return This factory, for convenience.
*/
public DefaultMediaSourceFactory setLiveMinOffsetMs(long liveMinOffsetMs) {
this.liveMinOffsetMs = liveMinOffsetMs;
return this;
}
/**
* Sets the maximum offset from the live edge for live streams, in milliseconds.
*
* @param liveMaxOffsetMs The maximum allowed live offset, in milliseconds, or {@link
* C#TIME_UNSET} to use the media-defined default.
* @return This factory, for convenience.
*/
public DefaultMediaSourceFactory setLiveMaxOffsetMs(long liveMaxOffsetMs) {
this.liveMaxOffsetMs = liveMaxOffsetMs;
return this;
}
/**
* Sets the minimum playback speed for live streams.
*
@ -294,7 +322,11 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
|| (mediaItem.liveConfiguration.minPlaybackSpeed == C.RATE_UNSET
&& liveMinSpeed != C.RATE_UNSET)
|| (mediaItem.liveConfiguration.maxPlaybackSpeed == C.RATE_UNSET
&& liveMaxSpeed != C.RATE_UNSET)) {
&& liveMaxSpeed != C.RATE_UNSET)
|| (mediaItem.liveConfiguration.minLiveOffsetMs == C.TIME_UNSET
&& liveMinOffsetMs != C.TIME_UNSET)
|| (mediaItem.liveConfiguration.maxLiveOffsetMs == C.TIME_UNSET
&& liveMaxOffsetMs != C.TIME_UNSET)) {
mediaItem =
mediaItem
.buildUpon()
@ -310,6 +342,14 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
mediaItem.liveConfiguration.maxPlaybackSpeed == C.RATE_UNSET
? liveMaxSpeed
: mediaItem.liveConfiguration.maxPlaybackSpeed)
.setLiveMinOffsetMs(
mediaItem.liveConfiguration.minLiveOffsetMs == C.TIME_UNSET
? liveMinOffsetMs
: mediaItem.liveConfiguration.minLiveOffsetMs)
.setLiveMaxOffsetMs(
mediaItem.liveConfiguration.maxLiveOffsetMs == C.TIME_UNSET
? liveMaxOffsetMs
: mediaItem.liveConfiguration.maxLiveOffsetMs)
.build();
}
MediaSource mediaSource = mediaSourceFactory.createMediaSource(mediaItem);

View File

@ -42,7 +42,11 @@ public class DefaultLivePlaybackSpeedControlTest {
new DefaultLivePlaybackSpeedControl.Builder().build();
defaultLivePlaybackSpeedControl.setLiveConfiguration(
new LiveConfiguration(
/* targetLiveOffsetMs= */ 42, /* minPlaybackSpeed= */ 1f, /* maxPlaybackSpeed= */ 1f));
/* targetLiveOffsetMs= */ 42,
/* minLiveOffsetMs= */ 200,
/* maxLiveOffsetMs= */ 400,
/* minPlaybackSpeed= */ 1f,
/* maxPlaybackSpeed= */ 1f));
assertThat(defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs()).isEqualTo(42_000);
}
@ -55,7 +59,11 @@ public class DefaultLivePlaybackSpeedControlTest {
defaultLivePlaybackSpeedControl.setTargetLiveOffsetOverrideUs(123_456_789);
defaultLivePlaybackSpeedControl.setLiveConfiguration(
new LiveConfiguration(
/* targetLiveOffsetMs= */ 42, /* minPlaybackSpeed= */ 1f, /* maxPlaybackSpeed= */ 1f));
/* targetLiveOffsetMs= */ 42,
/* minLiveOffsetMs= */ 200,
/* maxLiveOffsetMs= */ 400,
/* minPlaybackSpeed= */ 1f,
/* maxPlaybackSpeed= */ 1f));
long targetLiveOffsetUs = defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs();
@ -82,7 +90,11 @@ public class DefaultLivePlaybackSpeedControlTest {
defaultLivePlaybackSpeedControl.setTargetLiveOffsetOverrideUs(123_456_789);
defaultLivePlaybackSpeedControl.setLiveConfiguration(
new LiveConfiguration(
/* targetLiveOffsetMs= */ 42, /* minPlaybackSpeed= */ 1f, /* maxPlaybackSpeed= */ 1f));
/* targetLiveOffsetMs= */ 42,
/* minLiveOffsetMs= */ 200,
/* maxLiveOffsetMs= */ 400,
/* minPlaybackSpeed= */ 1f,
/* maxPlaybackSpeed= */ 1f));
defaultLivePlaybackSpeedControl.setTargetLiveOffsetOverrideUs(C.TIME_UNSET);
long targetLiveOffsetUs = defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs();
@ -97,6 +109,8 @@ public class DefaultLivePlaybackSpeedControlTest {
defaultLivePlaybackSpeedControl.setLiveConfiguration(
new LiveConfiguration(
/* targetLiveOffsetMs= */ 2_000,
/* minLiveOffsetMs= */ C.TIME_UNSET,
/* maxLiveOffsetMs= */ C.TIME_UNSET,
/* minPlaybackSpeed= */ C.RATE_UNSET,
/* maxPlaybackSpeed= */ C.RATE_UNSET));
@ -113,6 +127,8 @@ public class DefaultLivePlaybackSpeedControlTest {
defaultLivePlaybackSpeedControl.setLiveConfiguration(
new LiveConfiguration(
/* targetLiveOffsetMs= */ 2_000,
/* minLiveOffsetMs= */ C.TIME_UNSET,
/* maxLiveOffsetMs= */ C.TIME_UNSET,
/* minPlaybackSpeed= */ C.RATE_UNSET,
/* maxPlaybackSpeed= */ C.RATE_UNSET));
@ -138,6 +154,8 @@ public class DefaultLivePlaybackSpeedControlTest {
defaultLivePlaybackSpeedControl.setLiveConfiguration(
new LiveConfiguration(
/* targetLiveOffsetMs= */ 2_000,
/* minLiveOffsetMs= */ C.TIME_UNSET,
/* maxLiveOffsetMs= */ C.TIME_UNSET,
/* minPlaybackSpeed= */ C.RATE_UNSET,
/* maxPlaybackSpeed= */ C.RATE_UNSET));
@ -157,6 +175,8 @@ public class DefaultLivePlaybackSpeedControlTest {
defaultLivePlaybackSpeedControl.setLiveConfiguration(
new LiveConfiguration(
/* targetLiveOffsetMs= */ 2_000,
/* minLiveOffsetMs= */ C.TIME_UNSET,
/* maxLiveOffsetMs= */ C.TIME_UNSET,
/* minPlaybackSpeed= */ C.RATE_UNSET,
/* maxPlaybackSpeed= */ C.RATE_UNSET));
@ -176,6 +196,8 @@ public class DefaultLivePlaybackSpeedControlTest {
defaultLivePlaybackSpeedControl.setLiveConfiguration(
new LiveConfiguration(
/* targetLiveOffsetMs= */ 2_000,
/* minLiveOffsetMs= */ C.TIME_UNSET,
/* maxLiveOffsetMs= */ C.TIME_UNSET,
/* minPlaybackSpeed= */ C.RATE_UNSET,
/* maxPlaybackSpeed= */ C.RATE_UNSET));
@ -194,6 +216,8 @@ public class DefaultLivePlaybackSpeedControlTest {
defaultLivePlaybackSpeedControl.setLiveConfiguration(
new LiveConfiguration(
/* targetLiveOffsetMs= */ 2_000,
/* minLiveOffsetMs= */ C.TIME_UNSET,
/* maxLiveOffsetMs= */ C.TIME_UNSET,
/* minPlaybackSpeed= */ C.RATE_UNSET,
/* maxPlaybackSpeed= */ C.RATE_UNSET));
@ -212,6 +236,8 @@ public class DefaultLivePlaybackSpeedControlTest {
defaultLivePlaybackSpeedControl.setLiveConfiguration(
new LiveConfiguration(
/* targetLiveOffsetMs= */ 2_000,
/* minLiveOffsetMs= */ C.TIME_UNSET,
/* maxLiveOffsetMs= */ C.TIME_UNSET,
/* minPlaybackSpeed= */ C.RATE_UNSET,
/* maxPlaybackSpeed= */ 2f));
@ -230,6 +256,8 @@ public class DefaultLivePlaybackSpeedControlTest {
defaultLivePlaybackSpeedControl.setLiveConfiguration(
new LiveConfiguration(
/* targetLiveOffsetMs= */ 2_000,
/* minLiveOffsetMs= */ C.TIME_UNSET,
/* maxLiveOffsetMs= */ C.TIME_UNSET,
/* minPlaybackSpeed= */ 0.2f,
/* maxPlaybackSpeed= */ C.RATE_UNSET));
@ -247,6 +275,8 @@ public class DefaultLivePlaybackSpeedControlTest {
defaultLivePlaybackSpeedControl.setLiveConfiguration(
new LiveConfiguration(
/* targetLiveOffsetMs= */ 2_000,
/* minLiveOffsetMs= */ C.TIME_UNSET,
/* maxLiveOffsetMs= */ C.TIME_UNSET,
/* minPlaybackSpeed= */ C.RATE_UNSET,
/* maxPlaybackSpeed= */ C.RATE_UNSET));
@ -270,6 +300,8 @@ public class DefaultLivePlaybackSpeedControlTest {
defaultLivePlaybackSpeedControl.setLiveConfiguration(
new LiveConfiguration(
/* targetLiveOffsetMs= */ 2_000,
/* minLiveOffsetMs= */ C.TIME_UNSET,
/* maxLiveOffsetMs= */ C.TIME_UNSET,
/* minPlaybackSpeed= */ C.RATE_UNSET,
/* maxPlaybackSpeed= */ C.RATE_UNSET));
@ -278,6 +310,8 @@ public class DefaultLivePlaybackSpeedControlTest {
defaultLivePlaybackSpeedControl.setLiveConfiguration(
new LiveConfiguration(
/* targetLiveOffsetMs= */ 2_000,
/* minLiveOffsetMs= */ C.TIME_UNSET,
/* maxLiveOffsetMs= */ C.TIME_UNSET,
/* minPlaybackSpeed= */ C.RATE_UNSET,
/* maxPlaybackSpeed= */ C.RATE_UNSET));
float adjustedSpeed2 =
@ -293,6 +327,8 @@ public class DefaultLivePlaybackSpeedControlTest {
defaultLivePlaybackSpeedControl.setLiveConfiguration(
new LiveConfiguration(
/* targetLiveOffsetMs= */ 2_000,
/* minLiveOffsetMs= */ C.TIME_UNSET,
/* maxLiveOffsetMs= */ C.TIME_UNSET,
/* minPlaybackSpeed= */ C.RATE_UNSET,
/* maxPlaybackSpeed= */ C.RATE_UNSET));

View File

@ -246,6 +246,8 @@ public final class DefaultMediaSourceFactoryTest {
MediaItem mediaItemFromSource = mediaSource.getMediaItem();
assertThat(mediaItemFromSource.liveConfiguration.targetLiveOffsetMs).isEqualTo(C.TIME_UNSET);
assertThat(mediaItemFromSource.liveConfiguration.minLiveOffsetMs).isEqualTo(C.TIME_UNSET);
assertThat(mediaItemFromSource.liveConfiguration.maxLiveOffsetMs).isEqualTo(C.TIME_UNSET);
assertThat(mediaItemFromSource.liveConfiguration.minPlaybackSpeed).isEqualTo(C.RATE_UNSET);
assertThat(mediaItemFromSource.liveConfiguration.maxPlaybackSpeed).isEqualTo(C.RATE_UNSET);
}
@ -255,6 +257,8 @@ public final class DefaultMediaSourceFactoryTest {
DefaultMediaSourceFactory defaultMediaSourceFactory =
new DefaultMediaSourceFactory((Context) ApplicationProvider.getApplicationContext())
.setLiveTargetOffsetMs(20)
.setLiveMinOffsetMs(2222)
.setLiveMaxOffsetMs(4444)
.setLiveMinSpeed(.1f)
.setLiveMaxSpeed(2.0f);
MediaItem mediaItem = new MediaItem.Builder().setUri(URI_MEDIA + "/file.mp4").build();
@ -263,6 +267,8 @@ public final class DefaultMediaSourceFactoryTest {
MediaItem mediaItemFromSource = mediaSource.getMediaItem();
assertThat(mediaItemFromSource.liveConfiguration.targetLiveOffsetMs).isEqualTo(20);
assertThat(mediaItemFromSource.liveConfiguration.minLiveOffsetMs).isEqualTo(2222);
assertThat(mediaItemFromSource.liveConfiguration.maxLiveOffsetMs).isEqualTo(4444);
assertThat(mediaItemFromSource.liveConfiguration.minPlaybackSpeed).isEqualTo(.1f);
assertThat(mediaItemFromSource.liveConfiguration.maxPlaybackSpeed).isEqualTo(2.0f);
}
@ -272,12 +278,16 @@ public final class DefaultMediaSourceFactoryTest {
DefaultMediaSourceFactory defaultMediaSourceFactory =
new DefaultMediaSourceFactory((Context) ApplicationProvider.getApplicationContext())
.setLiveTargetOffsetMs(20)
.setLiveMinOffsetMs(2222)
.setLiveMinOffsetMs(4444)
.setLiveMinSpeed(.1f)
.setLiveMaxSpeed(2.0f);
MediaItem mediaItem =
new MediaItem.Builder()
.setUri(URI_MEDIA + "/file.mp4")
.setLiveTargetOffsetMs(10)
.setLiveMinOffsetMs(1111)
.setLiveMinOffsetMs(3333)
.setLiveMinPlaybackSpeed(20.0f)
.setLiveMaxPlaybackSpeed(20.0f)
.build();