Fix calculation for isSegmentAvailableAtFullNetworkSpeed.

The current caluclation was comparing Unix time with period time.
Fix it by always comparing against period time.

Issue: #4904
PiperOrigin-RevId: 338235754
This commit is contained in:
tonihei 2020-10-21 12:01:45 +01:00 committed by Oliver Woodman
parent e52a044ec5
commit 057771aeec
3 changed files with 115 additions and 17 deletions

View file

@ -277,6 +277,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
}
long nowUnixTimeUs = C.msToUs(Util.getNowUnixTimeMs(elapsedRealtimeOffsetMs));
long nowPeriodTimeUs = getNowPeriodTimeUs(nowUnixTimeUs);
MediaChunk previous = queue.isEmpty() ? null : queue.get(queue.size() - 1);
MediaChunkIterator[] chunkIterators = new MediaChunkIterator[trackSelection.length()];
for (int i = 0; i < chunkIterators.length; i++) {
@ -300,7 +301,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
} else {
chunkIterators[i] =
new RepresentationSegmentIterator(
representationHolder, segmentNum, lastAvailableSegmentNum, nowUnixTimeUs);
representationHolder, segmentNum, lastAvailableSegmentNum, nowPeriodTimeUs);
}
}
}
@ -391,7 +392,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
segmentNum,
maxSegmentCount,
seekTimeUs,
nowUnixTimeUs);
nowPeriodTimeUs);
}
@Override
@ -488,13 +489,16 @@ public class DefaultDashChunkSource implements DashChunkSource {
}
long lastSegmentNum = representationHolders[0].getLastAvailableSegmentNum(nowUnixTimeUs);
long lastSegmentEndTimeUs = representationHolders[0].getSegmentEndTimeUs(lastSegmentNum);
long nowPeriodTimeUs =
nowUnixTimeUs
- C.msToUs(manifest.availabilityStartTimeMs + manifest.getPeriod(periodIndex).startMs);
long nowPeriodTimeUs = getNowPeriodTimeUs(nowUnixTimeUs);
long availabilityEndTimeUs = min(nowPeriodTimeUs, lastSegmentEndTimeUs);
return max(0, availabilityEndTimeUs - playbackPositionUs);
}
private long getNowPeriodTimeUs(long nowUnixTimeUs) {
return nowUnixTimeUs
- C.msToUs(manifest.availabilityStartTimeMs + manifest.getPeriod(periodIndex).startMs);
}
protected Chunk newInitializationChunk(
RepresentationHolder representationHolder,
DataSource dataSource,
@ -535,7 +539,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
long firstSegmentNum,
int maxSegmentCount,
long seekTimeUs,
long nowUnixTimeUs) {
long nowPeriodTimeUs) {
Representation representation = representationHolder.representation;
long startTimeUs = representationHolder.getSegmentStartTimeUs(firstSegmentNum);
RangedUri segmentUri = representationHolder.getSegmentUrl(firstSegmentNum);
@ -543,7 +547,8 @@ public class DefaultDashChunkSource implements DashChunkSource {
if (representationHolder.chunkExtractor == null) {
long endTimeUs = representationHolder.getSegmentEndTimeUs(firstSegmentNum);
int flags =
representationHolder.isSegmentAvailableAtFullNetworkSpeed(firstSegmentNum, nowUnixTimeUs)
representationHolder.isSegmentAvailableAtFullNetworkSpeed(
firstSegmentNum, nowPeriodTimeUs)
? 0
: DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED;
DataSpec dataSpec = DashUtil.buildDataSpec(representation, segmentUri, flags);
@ -569,7 +574,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
? periodDurationUs
: C.TIME_UNSET;
int flags =
representationHolder.isSegmentAvailableAtFullNetworkSpeed(segmentNum, nowUnixTimeUs)
representationHolder.isSegmentAvailableAtFullNetworkSpeed(segmentNum, nowPeriodTimeUs)
? 0
: DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED;
DataSpec dataSpec = DashUtil.buildDataSpec(representation, segmentUri, flags);
@ -597,7 +602,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
protected static final class RepresentationSegmentIterator extends BaseMediaChunkIterator {
private final RepresentationHolder representationHolder;
private final long currentUnixTimeUs;
private final long nowPeriodTimeUs;
/**
* Creates iterator.
@ -605,17 +610,17 @@ public class DefaultDashChunkSource implements DashChunkSource {
* @param representation The {@link RepresentationHolder} to wrap.
* @param firstAvailableSegmentNum The number of the first available segment.
* @param lastAvailableSegmentNum The number of the last available segment.
* @param currentUnixTimeUs The current time in microseconds since the epoch used for
* calculating if segments are available at full network speed.
* @param nowPeriodTimeUs The current time in microseconds since the start of the period used
* for calculating if segments are available at full network speed.
*/
public RepresentationSegmentIterator(
RepresentationHolder representation,
long firstAvailableSegmentNum,
long lastAvailableSegmentNum,
long currentUnixTimeUs) {
long nowPeriodTimeUs) {
super(/* fromIndex= */ firstAvailableSegmentNum, /* toIndex= */ lastAvailableSegmentNum);
this.representationHolder = representation;
this.currentUnixTimeUs = currentUnixTimeUs;
this.nowPeriodTimeUs = nowPeriodTimeUs;
}
@Override
@ -624,7 +629,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
long currentIndex = getCurrentIndex();
RangedUri segmentUri = representationHolder.getSegmentUrl(currentIndex);
int flags =
representationHolder.isSegmentAvailableAtFullNetworkSpeed(currentIndex, currentUnixTimeUs)
representationHolder.isSegmentAvailableAtFullNetworkSpeed(currentIndex, nowPeriodTimeUs)
? 0
: DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED;
return DashUtil.buildDataSpec(representationHolder.representation, segmentUri, flags);
@ -787,8 +792,8 @@ public class DefaultDashChunkSource implements DashChunkSource {
- 1;
}
public boolean isSegmentAvailableAtFullNetworkSpeed(long segmentNum, long nowUnixTimeUs) {
return getSegmentEndTimeUs(segmentNum) <= nowUnixTimeUs;
public boolean isSegmentAvailableAtFullNetworkSpeed(long segmentNum, long nowPeriodTimeUs) {
return getSegmentEndTimeUs(segmentNum) <= nowPeriodTimeUs;
}
@Nullable

View file

@ -0,0 +1,92 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.source.dash;
import static com.google.common.truth.Truth.assertThat;
import android.net.Uri;
import android.os.SystemClock;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.chunk.ChunkHolder;
import com.google.android.exoplayer2.source.dash.manifest.DashManifest;
import com.google.android.exoplayer2.source.dash.manifest.DashManifestParser;
import com.google.android.exoplayer2.testutil.FakeDataSource;
import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.trackselection.FixedTrackSelection;
import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.upstream.LoaderErrorThrower;
import com.google.common.collect.ImmutableList;
import org.junit.Test;
import org.junit.runner.RunWith;
/** Unit test for {@link DefaultDashChunkSource}. */
@RunWith(AndroidJUnit4.class)
public class DefaultDashChunkSourceTest {
private static final String SAMPLE_MPD_LIVE_WITH_OFFSET_INSIDE_WINDOW =
"media/mpd/sample_mpd_live_with_offset_inside_window";
@Test
public void getNextChunk_forLowLatencyManifest_setsCorrectMayNotLoadAtFullNetworkSpeedFlag()
throws Exception {
long nowMs = 2_000_000_000_000L;
SystemClock.setCurrentTimeMillis(nowMs);
DashManifest manifest =
new DashManifestParser()
.parse(
Uri.parse("https://example.com/test.mpd"),
TestUtil.getInputStream(
ApplicationProvider.getApplicationContext(),
SAMPLE_MPD_LIVE_WITH_OFFSET_INSIDE_WINDOW));
DefaultDashChunkSource chunkSource =
new DefaultDashChunkSource(
new LoaderErrorThrower.Dummy(),
manifest,
/* periodIndex= */ 0,
/* adaptationSetIndices= */ new int[] {0},
new FixedTrackSelection(new TrackGroup(new Format.Builder().build()), /* track= */ 0),
C.TRACK_TYPE_VIDEO,
new FakeDataSource(),
/* elapsedRealtimeOffsetMs= */ 0,
/* maxSegmentsPerLoad= */ 1,
/* enableEventMessageTrack= */ false,
/* closedCaptionFormats */ ImmutableList.of(),
/* playerTrackEmsgHandler= */ null);
long nowInPeriodUs = C.msToUs(nowMs - manifest.availabilityStartTimeMs);
ChunkHolder output = new ChunkHolder();
chunkSource.getNextChunk(
/* playbackPositionUs= */ nowInPeriodUs - 5 * C.MICROS_PER_SECOND,
/* loadPositionUs= */ nowInPeriodUs - 5 * C.MICROS_PER_SECOND,
/* queue= */ ImmutableList.of(),
output);
assertThat(output.chunk.dataSpec.flags & DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED)
.isEqualTo(0);
chunkSource.getNextChunk(
/* playbackPositionUs= */ nowInPeriodUs,
/* loadPositionUs= */ nowInPeriodUs,
/* queue= */ ImmutableList.of(),
output);
assertThat(output.chunk.dataSpec.flags & DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED)
.isNotEqualTo(0);
}
}

View file

@ -17,7 +17,8 @@
timescale="1000000"
duration="2000000"
availabilityTimeOffset="2"
startNumber="1"/>
startNumber="1"
media="chunk-$Number%05d$.mp4"/>
</Representation>
</AdaptationSet>
</Period>