Add some correctness checks to min/max live latency values.
For DASH manifests, we merge min/max live latency values from various sources and they may not be consistent with each other. To ensure we use a sensible configuration in all cases, we can add more correctness checks: 1. Limit the min/max values to fall into the available live window. 2. Ensure that maxLatency >= minLatency in all cases. Issue: google/ExoPlayer#9750 PiperOrigin-RevId: 415282938
This commit is contained in:
parent
df8a3dc362
commit
422a003a03
@ -2,6 +2,10 @@
|
||||
{
|
||||
"name": "Clear DASH",
|
||||
"samples": [
|
||||
{
|
||||
"name": "Test",
|
||||
"uri": "https://01-str01-3201-prod.tv.cetin.rs/bpk-token/1AG@lk5yp2xm3d5rp5eik2lnd4r5y5s0h4d5lkhyurda/bpk-tv/RTS_1_2145/output1/manifest.mpd"
|
||||
},
|
||||
{
|
||||
"name": "HD (MP4, H264)",
|
||||
"uri": "https://storage.googleapis.com/wvmedia/clear/h264/tears/tears.mpd"
|
||||
|
@ -17,6 +17,8 @@ package androidx.media3.exoplayer.dash;
|
||||
|
||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||
import static androidx.media3.common.util.Assertions.checkState;
|
||||
import static androidx.media3.common.util.Util.constrainValue;
|
||||
import static androidx.media3.common.util.Util.usToMs;
|
||||
import static java.lang.Math.max;
|
||||
import static java.lang.Math.min;
|
||||
|
||||
@ -998,31 +1000,43 @@ public final class DashMediaSource extends BaseMediaSource {
|
||||
}
|
||||
|
||||
private void updateMediaItemLiveConfiguration(long nowInWindowUs, long windowDurationUs) {
|
||||
long maxLiveOffsetMs;
|
||||
// Default maximum offset: start of window.
|
||||
long maxPossibleLiveOffsetMs = usToMs(nowInWindowUs);
|
||||
long maxLiveOffsetMs = maxPossibleLiveOffsetMs;
|
||||
// Override maximum offset with user or media defined values if they are smaller.
|
||||
if (mediaItem.liveConfiguration.maxOffsetMs != C.TIME_UNSET) {
|
||||
maxLiveOffsetMs = mediaItem.liveConfiguration.maxOffsetMs;
|
||||
maxLiveOffsetMs = min(maxLiveOffsetMs, mediaItem.liveConfiguration.maxOffsetMs);
|
||||
} else if (manifest.serviceDescription != null
|
||||
&& manifest.serviceDescription.maxOffsetMs != C.TIME_UNSET) {
|
||||
maxLiveOffsetMs = manifest.serviceDescription.maxOffsetMs;
|
||||
} else {
|
||||
maxLiveOffsetMs = Util.usToMs(nowInWindowUs);
|
||||
maxLiveOffsetMs = min(maxLiveOffsetMs, manifest.serviceDescription.maxOffsetMs);
|
||||
}
|
||||
long minLiveOffsetMs;
|
||||
if (mediaItem.liveConfiguration.minOffsetMs != C.TIME_UNSET) {
|
||||
minLiveOffsetMs = mediaItem.liveConfiguration.minOffsetMs;
|
||||
} else if (manifest.serviceDescription != null
|
||||
&& manifest.serviceDescription.minOffsetMs != C.TIME_UNSET) {
|
||||
minLiveOffsetMs = manifest.serviceDescription.minOffsetMs;
|
||||
} else {
|
||||
minLiveOffsetMs = Util.usToMs(nowInWindowUs - windowDurationUs);
|
||||
// Default minimum offset: end of window.
|
||||
long minLiveOffsetMs = usToMs(nowInWindowUs - windowDurationUs);
|
||||
if (minLiveOffsetMs < 0 && maxLiveOffsetMs > 0) {
|
||||
// The current time is in the window, so assume all clocks are synchronized and set the
|
||||
// minimum to a live offset of zero.
|
||||
minLiveOffsetMs = 0;
|
||||
}
|
||||
if (manifest.minBufferTimeMs != C.TIME_UNSET) {
|
||||
minLiveOffsetMs = min(minLiveOffsetMs + manifest.minBufferTimeMs, maxLiveOffsetMs);
|
||||
// Ensure to leave one GOP as minimum and don't exceed the maximum possible offset.
|
||||
minLiveOffsetMs = min(minLiveOffsetMs + manifest.minBufferTimeMs, maxPossibleLiveOffsetMs);
|
||||
}
|
||||
// Override minimum offset with user and media defined values if they are larger, but don't
|
||||
// exceed the maximum possible offset.
|
||||
if (mediaItem.liveConfiguration.minOffsetMs != C.TIME_UNSET) {
|
||||
minLiveOffsetMs =
|
||||
constrainValue(
|
||||
mediaItem.liveConfiguration.minOffsetMs, minLiveOffsetMs, maxPossibleLiveOffsetMs);
|
||||
} else if (manifest.serviceDescription != null
|
||||
&& manifest.serviceDescription.minOffsetMs != C.TIME_UNSET) {
|
||||
minLiveOffsetMs =
|
||||
constrainValue(
|
||||
manifest.serviceDescription.minOffsetMs, minLiveOffsetMs, maxPossibleLiveOffsetMs);
|
||||
}
|
||||
if (minLiveOffsetMs > maxLiveOffsetMs) {
|
||||
// The values can be set by different sources and may disagree. Prefer the maximum offset
|
||||
// under the assumption that it is safer for playback.
|
||||
maxLiveOffsetMs = minLiveOffsetMs;
|
||||
}
|
||||
long targetOffsetMs;
|
||||
if (liveConfiguration.targetOffsetMs != C.TIME_UNSET) {
|
||||
@ -1043,9 +1057,9 @@ public final class DashMediaSource extends BaseMediaSource {
|
||||
long safeDistanceFromWindowStartUs =
|
||||
min(MIN_LIVE_DEFAULT_START_POSITION_US, windowDurationUs / 2);
|
||||
long maxTargetOffsetForSafeDistanceToWindowStartMs =
|
||||
Util.usToMs(nowInWindowUs - safeDistanceFromWindowStartUs);
|
||||
usToMs(nowInWindowUs - safeDistanceFromWindowStartUs);
|
||||
targetOffsetMs =
|
||||
Util.constrainValue(
|
||||
constrainValue(
|
||||
maxTargetOffsetForSafeDistanceToWindowStartMs, minLiveOffsetMs, maxLiveOffsetMs);
|
||||
}
|
||||
float minPlaybackSpeed = C.RATE_UNSET;
|
||||
|
@ -353,7 +353,7 @@ public final class DashMediaSourceTest {
|
||||
.setTargetOffsetMs(876L)
|
||||
.setMinPlaybackSpeed(23f)
|
||||
.setMaxPlaybackSpeed(42f)
|
||||
.setMinOffsetMs(200L)
|
||||
.setMinOffsetMs(600L)
|
||||
.setMaxOffsetMs(999L)
|
||||
.build())
|
||||
.build();
|
||||
@ -369,7 +369,7 @@ public final class DashMediaSourceTest {
|
||||
prepareAndWaitForTimelineRefresh(mediaSource).liveConfiguration;
|
||||
|
||||
assertThat(liveConfiguration.targetOffsetMs).isEqualTo(876L);
|
||||
assertThat(liveConfiguration.minOffsetMs).isEqualTo(200L);
|
||||
assertThat(liveConfiguration.minOffsetMs).isEqualTo(600L);
|
||||
assertThat(liveConfiguration.maxOffsetMs).isEqualTo(999L);
|
||||
assertThat(liveConfiguration.minPlaybackSpeed).isEqualTo(23f);
|
||||
assertThat(liveConfiguration.maxPlaybackSpeed).isEqualTo(42f);
|
||||
@ -425,6 +425,33 @@ public final class DashMediaSourceTest {
|
||||
assertThat(liveConfiguration.maxPlaybackSpeed).isEqualTo(42f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
prepare_withMinMaxOffsetOverridesOutsideOfLiveWindow_adjustsOverridesToBeWithinWindow()
|
||||
throws Exception {
|
||||
MediaItem mediaItem =
|
||||
new MediaItem.Builder()
|
||||
.setUri(Uri.EMPTY)
|
||||
.setLiveConfiguration(
|
||||
new MediaItem.LiveConfiguration.Builder()
|
||||
.setMinOffsetMs(0L)
|
||||
.setMaxOffsetMs(1_000_000_000L)
|
||||
.build())
|
||||
.build();
|
||||
DashMediaSource mediaSource =
|
||||
new DashMediaSource.Factory(
|
||||
() ->
|
||||
createSampleMpdDataSource(
|
||||
SAMPLE_MPD_LIVE_WITH_SUGGESTED_PRESENTATION_DELAY_2S_MIN_BUFFER_TIME_500MS))
|
||||
.createMediaSource(mediaItem);
|
||||
|
||||
MediaItem.LiveConfiguration liveConfiguration =
|
||||
prepareAndWaitForTimelineRefresh(mediaSource).liveConfiguration;
|
||||
|
||||
assertThat(liveConfiguration.minOffsetMs).isEqualTo(500L);
|
||||
assertThat(liveConfiguration.maxOffsetMs).isEqualTo(58_000L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void prepare_targetLiveOffsetInWindow_manifestTargetOffsetAndAlignedWindowStartPosition()
|
||||
throws InterruptedException {
|
||||
|
@ -4,7 +4,7 @@
|
||||
suggestedPresentationDelay="PT2S"
|
||||
availabilityStartTime="2020-01-01T00:00:00Z"
|
||||
minimumUpdatePeriod="PT4M"
|
||||
timeShiftBufferDepth="PT6.0S">
|
||||
timeShiftBufferDepth="PT8.0S">
|
||||
<UTCTiming
|
||||
schemeIdUri="urn:mpeg:dash:utc:direct:2014"
|
||||
value="2020-01-01T01:00:00Z" />
|
||||
|
Loading…
x
Reference in New Issue
Block a user