mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
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:
parent
e52a044ec5
commit
057771aeec
3 changed files with 115 additions and 17 deletions
|
|
@ -277,6 +277,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
long nowUnixTimeUs = C.msToUs(Util.getNowUnixTimeMs(elapsedRealtimeOffsetMs));
|
long nowUnixTimeUs = C.msToUs(Util.getNowUnixTimeMs(elapsedRealtimeOffsetMs));
|
||||||
|
long nowPeriodTimeUs = getNowPeriodTimeUs(nowUnixTimeUs);
|
||||||
MediaChunk previous = queue.isEmpty() ? null : queue.get(queue.size() - 1);
|
MediaChunk previous = queue.isEmpty() ? null : queue.get(queue.size() - 1);
|
||||||
MediaChunkIterator[] chunkIterators = new MediaChunkIterator[trackSelection.length()];
|
MediaChunkIterator[] chunkIterators = new MediaChunkIterator[trackSelection.length()];
|
||||||
for (int i = 0; i < chunkIterators.length; i++) {
|
for (int i = 0; i < chunkIterators.length; i++) {
|
||||||
|
|
@ -300,7 +301,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||||
} else {
|
} else {
|
||||||
chunkIterators[i] =
|
chunkIterators[i] =
|
||||||
new RepresentationSegmentIterator(
|
new RepresentationSegmentIterator(
|
||||||
representationHolder, segmentNum, lastAvailableSegmentNum, nowUnixTimeUs);
|
representationHolder, segmentNum, lastAvailableSegmentNum, nowPeriodTimeUs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -391,7 +392,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||||
segmentNum,
|
segmentNum,
|
||||||
maxSegmentCount,
|
maxSegmentCount,
|
||||||
seekTimeUs,
|
seekTimeUs,
|
||||||
nowUnixTimeUs);
|
nowPeriodTimeUs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -488,13 +489,16 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||||
}
|
}
|
||||||
long lastSegmentNum = representationHolders[0].getLastAvailableSegmentNum(nowUnixTimeUs);
|
long lastSegmentNum = representationHolders[0].getLastAvailableSegmentNum(nowUnixTimeUs);
|
||||||
long lastSegmentEndTimeUs = representationHolders[0].getSegmentEndTimeUs(lastSegmentNum);
|
long lastSegmentEndTimeUs = representationHolders[0].getSegmentEndTimeUs(lastSegmentNum);
|
||||||
long nowPeriodTimeUs =
|
long nowPeriodTimeUs = getNowPeriodTimeUs(nowUnixTimeUs);
|
||||||
nowUnixTimeUs
|
|
||||||
- C.msToUs(manifest.availabilityStartTimeMs + manifest.getPeriod(periodIndex).startMs);
|
|
||||||
long availabilityEndTimeUs = min(nowPeriodTimeUs, lastSegmentEndTimeUs);
|
long availabilityEndTimeUs = min(nowPeriodTimeUs, lastSegmentEndTimeUs);
|
||||||
return max(0, availabilityEndTimeUs - playbackPositionUs);
|
return max(0, availabilityEndTimeUs - playbackPositionUs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private long getNowPeriodTimeUs(long nowUnixTimeUs) {
|
||||||
|
return nowUnixTimeUs
|
||||||
|
- C.msToUs(manifest.availabilityStartTimeMs + manifest.getPeriod(periodIndex).startMs);
|
||||||
|
}
|
||||||
|
|
||||||
protected Chunk newInitializationChunk(
|
protected Chunk newInitializationChunk(
|
||||||
RepresentationHolder representationHolder,
|
RepresentationHolder representationHolder,
|
||||||
DataSource dataSource,
|
DataSource dataSource,
|
||||||
|
|
@ -535,7 +539,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||||
long firstSegmentNum,
|
long firstSegmentNum,
|
||||||
int maxSegmentCount,
|
int maxSegmentCount,
|
||||||
long seekTimeUs,
|
long seekTimeUs,
|
||||||
long nowUnixTimeUs) {
|
long nowPeriodTimeUs) {
|
||||||
Representation representation = representationHolder.representation;
|
Representation representation = representationHolder.representation;
|
||||||
long startTimeUs = representationHolder.getSegmentStartTimeUs(firstSegmentNum);
|
long startTimeUs = representationHolder.getSegmentStartTimeUs(firstSegmentNum);
|
||||||
RangedUri segmentUri = representationHolder.getSegmentUrl(firstSegmentNum);
|
RangedUri segmentUri = representationHolder.getSegmentUrl(firstSegmentNum);
|
||||||
|
|
@ -543,7 +547,8 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||||
if (representationHolder.chunkExtractor == null) {
|
if (representationHolder.chunkExtractor == null) {
|
||||||
long endTimeUs = representationHolder.getSegmentEndTimeUs(firstSegmentNum);
|
long endTimeUs = representationHolder.getSegmentEndTimeUs(firstSegmentNum);
|
||||||
int flags =
|
int flags =
|
||||||
representationHolder.isSegmentAvailableAtFullNetworkSpeed(firstSegmentNum, nowUnixTimeUs)
|
representationHolder.isSegmentAvailableAtFullNetworkSpeed(
|
||||||
|
firstSegmentNum, nowPeriodTimeUs)
|
||||||
? 0
|
? 0
|
||||||
: DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED;
|
: DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED;
|
||||||
DataSpec dataSpec = DashUtil.buildDataSpec(representation, segmentUri, flags);
|
DataSpec dataSpec = DashUtil.buildDataSpec(representation, segmentUri, flags);
|
||||||
|
|
@ -569,7 +574,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||||
? periodDurationUs
|
? periodDurationUs
|
||||||
: C.TIME_UNSET;
|
: C.TIME_UNSET;
|
||||||
int flags =
|
int flags =
|
||||||
representationHolder.isSegmentAvailableAtFullNetworkSpeed(segmentNum, nowUnixTimeUs)
|
representationHolder.isSegmentAvailableAtFullNetworkSpeed(segmentNum, nowPeriodTimeUs)
|
||||||
? 0
|
? 0
|
||||||
: DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED;
|
: DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED;
|
||||||
DataSpec dataSpec = DashUtil.buildDataSpec(representation, segmentUri, flags);
|
DataSpec dataSpec = DashUtil.buildDataSpec(representation, segmentUri, flags);
|
||||||
|
|
@ -597,7 +602,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||||
protected static final class RepresentationSegmentIterator extends BaseMediaChunkIterator {
|
protected static final class RepresentationSegmentIterator extends BaseMediaChunkIterator {
|
||||||
|
|
||||||
private final RepresentationHolder representationHolder;
|
private final RepresentationHolder representationHolder;
|
||||||
private final long currentUnixTimeUs;
|
private final long nowPeriodTimeUs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates iterator.
|
* Creates iterator.
|
||||||
|
|
@ -605,17 +610,17 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||||
* @param representation The {@link RepresentationHolder} to wrap.
|
* @param representation The {@link RepresentationHolder} to wrap.
|
||||||
* @param firstAvailableSegmentNum The number of the first available segment.
|
* @param firstAvailableSegmentNum The number of the first available segment.
|
||||||
* @param lastAvailableSegmentNum The number of the last available segment.
|
* @param lastAvailableSegmentNum The number of the last available segment.
|
||||||
* @param currentUnixTimeUs The current time in microseconds since the epoch used for
|
* @param nowPeriodTimeUs The current time in microseconds since the start of the period used
|
||||||
* calculating if segments are available at full network speed.
|
* for calculating if segments are available at full network speed.
|
||||||
*/
|
*/
|
||||||
public RepresentationSegmentIterator(
|
public RepresentationSegmentIterator(
|
||||||
RepresentationHolder representation,
|
RepresentationHolder representation,
|
||||||
long firstAvailableSegmentNum,
|
long firstAvailableSegmentNum,
|
||||||
long lastAvailableSegmentNum,
|
long lastAvailableSegmentNum,
|
||||||
long currentUnixTimeUs) {
|
long nowPeriodTimeUs) {
|
||||||
super(/* fromIndex= */ firstAvailableSegmentNum, /* toIndex= */ lastAvailableSegmentNum);
|
super(/* fromIndex= */ firstAvailableSegmentNum, /* toIndex= */ lastAvailableSegmentNum);
|
||||||
this.representationHolder = representation;
|
this.representationHolder = representation;
|
||||||
this.currentUnixTimeUs = currentUnixTimeUs;
|
this.nowPeriodTimeUs = nowPeriodTimeUs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -624,7 +629,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||||
long currentIndex = getCurrentIndex();
|
long currentIndex = getCurrentIndex();
|
||||||
RangedUri segmentUri = representationHolder.getSegmentUrl(currentIndex);
|
RangedUri segmentUri = representationHolder.getSegmentUrl(currentIndex);
|
||||||
int flags =
|
int flags =
|
||||||
representationHolder.isSegmentAvailableAtFullNetworkSpeed(currentIndex, currentUnixTimeUs)
|
representationHolder.isSegmentAvailableAtFullNetworkSpeed(currentIndex, nowPeriodTimeUs)
|
||||||
? 0
|
? 0
|
||||||
: DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED;
|
: DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED;
|
||||||
return DashUtil.buildDataSpec(representationHolder.representation, segmentUri, flags);
|
return DashUtil.buildDataSpec(representationHolder.representation, segmentUri, flags);
|
||||||
|
|
@ -787,8 +792,8 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||||
- 1;
|
- 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSegmentAvailableAtFullNetworkSpeed(long segmentNum, long nowUnixTimeUs) {
|
public boolean isSegmentAvailableAtFullNetworkSpeed(long segmentNum, long nowPeriodTimeUs) {
|
||||||
return getSegmentEndTimeUs(segmentNum) <= nowUnixTimeUs;
|
return getSegmentEndTimeUs(segmentNum) <= nowPeriodTimeUs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -17,7 +17,8 @@
|
||||||
timescale="1000000"
|
timescale="1000000"
|
||||||
duration="2000000"
|
duration="2000000"
|
||||||
availabilityTimeOffset="2"
|
availabilityTimeOffset="2"
|
||||||
startNumber="1"/>
|
startNumber="1"
|
||||||
|
media="chunk-$Number%05d$.mp4"/>
|
||||||
</Representation>
|
</Representation>
|
||||||
</AdaptationSet>
|
</AdaptationSet>
|
||||||
</Period>
|
</Period>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue