mirror of
https://github.com/samsonjs/media.git
synced 2026-04-13 12:35:48 +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 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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
duration="2000000"
|
||||
availabilityTimeOffset="2"
|
||||
startNumber="1"/>
|
||||
startNumber="1"
|
||||
media="chunk-$Number%05d$.mp4"/>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
</Period>
|
||||
|
|
|
|||
Loading…
Reference in a new issue