Refactor #6.5: Restore DASH UTC timing element support.
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=121002218
This commit is contained in:
parent
0841d043b8
commit
23cb9532c5
@ -46,6 +46,8 @@ import com.google.android.exoplayer.upstream.DataSpec;
|
||||
import com.google.android.exoplayer.util.MimeTypes;
|
||||
import com.google.android.exoplayer.util.Util;
|
||||
|
||||
import android.os.SystemClock;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@ -62,6 +64,7 @@ public class DashChunkSource implements ChunkSource {
|
||||
private final boolean[] adaptiveFormatBlacklistFlags;
|
||||
private final DataSource dataSource;
|
||||
private final FormatEvaluator adaptiveFormatEvaluator;
|
||||
private final long elapsedRealtimeOffsetUs;
|
||||
private final Evaluation evaluation;
|
||||
|
||||
private MediaPresentationDescription manifest;
|
||||
@ -77,15 +80,19 @@ public class DashChunkSource implements ChunkSource {
|
||||
* @param tracks The indices of the selected tracks within the adaptation set.
|
||||
* @param dataSource A {@link DataSource} suitable for loading the media data.
|
||||
* @param adaptiveFormatEvaluator For adaptive tracks, selects from the available formats.
|
||||
* @param elapsedRealtimeOffsetMs If known, an estimate of the instantaneous difference between
|
||||
* server-side unix time and {@link SystemClock#elapsedRealtime()} in milliseconds, specified
|
||||
* as the server's unix time minus the local elapsed time. It unknown, set to 0.
|
||||
*/
|
||||
public DashChunkSource(MediaPresentationDescription manifest, int adaptationSetIndex,
|
||||
TrackGroup trackGroup, int[] tracks, DataSource dataSource,
|
||||
FormatEvaluator adaptiveFormatEvaluator) {
|
||||
FormatEvaluator adaptiveFormatEvaluator, long elapsedRealtimeOffsetMs) {
|
||||
this.manifest = manifest;
|
||||
this.adaptationSetIndex = adaptationSetIndex;
|
||||
this.trackGroup = trackGroup;
|
||||
this.dataSource = dataSource;
|
||||
this.adaptiveFormatEvaluator = adaptiveFormatEvaluator;
|
||||
this.elapsedRealtimeOffsetUs = elapsedRealtimeOffsetMs * 1000;
|
||||
this.evaluation = new Evaluation();
|
||||
|
||||
Period period = manifest.getPeriod(0);
|
||||
@ -197,9 +204,7 @@ public class DashChunkSource implements ChunkSource {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO[REFACTOR]: Bring back UTC timing element support.
|
||||
long nowUs = System.currentTimeMillis() * 1000;
|
||||
|
||||
long nowUs = getNowUnixTimeUs();
|
||||
int firstAvailableSegmentNum = representationHolder.getFirstSegmentNum();
|
||||
int lastAvailableSegmentNum = representationHolder.getLastSegmentNum();
|
||||
boolean indexUnbounded = lastAvailableSegmentNum == DashSegmentIndex.INDEX_UNBOUNDED;
|
||||
@ -278,6 +283,14 @@ public class DashChunkSource implements ChunkSource {
|
||||
|
||||
// Private methods.
|
||||
|
||||
private long getNowUnixTimeUs() {
|
||||
if (elapsedRealtimeOffsetUs != 0) {
|
||||
return (SystemClock.elapsedRealtime() * 1000) + elapsedRealtimeOffsetUs;
|
||||
} else {
|
||||
return System.currentTimeMillis() * 1000;
|
||||
}
|
||||
}
|
||||
|
||||
private Chunk newInitializationChunk(RangedUri initializationUri, RangedUri indexUri,
|
||||
Representation representation, ChunkExtractorWrapper extractor, DataSource dataSource,
|
||||
int trigger) {
|
||||
|
@ -33,17 +33,20 @@ import com.google.android.exoplayer.dash.mpd.MediaPresentationDescription;
|
||||
import com.google.android.exoplayer.dash.mpd.MediaPresentationDescriptionParser;
|
||||
import com.google.android.exoplayer.dash.mpd.Period;
|
||||
import com.google.android.exoplayer.dash.mpd.Representation;
|
||||
import com.google.android.exoplayer.dash.mpd.UtcTimingElement;
|
||||
import com.google.android.exoplayer.dash.mpd.UtcTimingElementResolver;
|
||||
import com.google.android.exoplayer.dash.mpd.UtcTimingElementResolver.UtcTimingCallback;
|
||||
import com.google.android.exoplayer.upstream.BandwidthMeter;
|
||||
import com.google.android.exoplayer.upstream.DataSource;
|
||||
import com.google.android.exoplayer.upstream.DataSourceFactory;
|
||||
import com.google.android.exoplayer.upstream.DefaultAllocator;
|
||||
import com.google.android.exoplayer.util.Assertions;
|
||||
import com.google.android.exoplayer.util.ManifestFetcher;
|
||||
import com.google.android.exoplayer.util.Util;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -53,7 +56,9 @@ import java.util.List;
|
||||
/**
|
||||
* A {@link SampleSource} for DASH media.
|
||||
*/
|
||||
public final class DashSampleSource implements SampleSource {
|
||||
public final class DashSampleSource implements SampleSource, UtcTimingCallback {
|
||||
|
||||
private static final String TAG = "DashSampleSource";
|
||||
|
||||
private final ManifestFetcher<MediaPresentationDescription> manifestFetcher;
|
||||
private final DataSourceFactory dataSourceFactory;
|
||||
@ -63,8 +68,10 @@ public final class DashSampleSource implements SampleSource {
|
||||
private final LoadControl loadControl;
|
||||
|
||||
private boolean prepared;
|
||||
private boolean released;
|
||||
private long durationUs;
|
||||
private MediaPresentationDescription currentManifest;
|
||||
private long elapsedRealtimeOffset;
|
||||
private MediaPresentationDescription manifest;
|
||||
private TrackGroupArray trackGroups;
|
||||
private int[] trackGroupAdaptationSetIndices;
|
||||
private boolean pendingReset;
|
||||
@ -96,20 +103,24 @@ public final class DashSampleSource implements SampleSource {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (currentManifest == null) {
|
||||
currentManifest = manifestFetcher.getManifest();
|
||||
if (currentManifest == null) {
|
||||
if (manifest == null) {
|
||||
manifest = manifestFetcher.getManifest();
|
||||
if (manifest == null) {
|
||||
manifestFetcher.maybeThrowError();
|
||||
manifestFetcher.requestRefresh();
|
||||
return false;
|
||||
}
|
||||
durationUs = manifest.dynamic ? C.UNSET_TIME_US : manifest.duration * 1000;
|
||||
buildTrackGroups(manifest);
|
||||
if (manifest.utcTiming != null) {
|
||||
UtcTimingElementResolver.resolveTimingElement(dataSourceFactory.createDataSource(),
|
||||
manifest.utcTiming, manifestFetcher.getManifestLoadCompleteTimestamp(), this);
|
||||
} else {
|
||||
prepared = true;
|
||||
}
|
||||
}
|
||||
|
||||
durationUs = currentManifest.dynamic ? C.UNSET_TIME_US : currentManifest.duration * 1000;
|
||||
buildTrackGroups(currentManifest);
|
||||
|
||||
prepared = true;
|
||||
return true;
|
||||
return prepared;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -125,8 +136,6 @@ public final class DashSampleSource implements SampleSource {
|
||||
@Override
|
||||
public TrackStream[] selectTracks(List<TrackStream> oldStreams,
|
||||
List<TrackSelection> newSelections, long positionUs) {
|
||||
Assertions.checkState(prepared);
|
||||
|
||||
int newEnabledSourceCount = trackStreams.length + newSelections.size() - oldStreams.size();
|
||||
DashChunkSource[] newChunkSources = new DashChunkSource[newEnabledSourceCount];
|
||||
ChunkTrackStream[] newTrackStreams = new ChunkTrackStream[newEnabledSourceCount];
|
||||
@ -161,16 +170,16 @@ public final class DashSampleSource implements SampleSource {
|
||||
|
||||
@Override
|
||||
public void continueBuffering(long positionUs) {
|
||||
if (currentManifest.dynamic) {
|
||||
if (manifest.dynamic) {
|
||||
MediaPresentationDescription newManifest = manifestFetcher.getManifest();
|
||||
if (newManifest != currentManifest) {
|
||||
currentManifest = newManifest;
|
||||
if (newManifest != manifest) {
|
||||
manifest = newManifest;
|
||||
for (DashChunkSource chunkSource : chunkSources) {
|
||||
chunkSource.updateManifest(newManifest);
|
||||
}
|
||||
}
|
||||
|
||||
long minUpdatePeriod = currentManifest.minUpdatePeriod;
|
||||
long minUpdatePeriod = manifest.minUpdatePeriod;
|
||||
if (minUpdatePeriod == 0) {
|
||||
// TODO: This is a temporary hack to avoid constantly refreshing the MPD in cases where
|
||||
// minUpdatePeriod is set to 0. In such cases we shouldn't refresh unless there is explicit
|
||||
@ -233,6 +242,28 @@ public final class DashSampleSource implements SampleSource {
|
||||
for (ChunkTrackStream trackStream : trackStreams) {
|
||||
trackStream.release();
|
||||
}
|
||||
released = true;
|
||||
}
|
||||
|
||||
// UtcTimingCallback implementation.
|
||||
|
||||
@Override
|
||||
public void onTimestampResolved(UtcTimingElement utcTiming, long elapsedRealtimeOffset) {
|
||||
if (released) {
|
||||
return;
|
||||
}
|
||||
this.elapsedRealtimeOffset = elapsedRealtimeOffset;
|
||||
prepared = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimestampError(UtcTimingElement utcTiming, IOException e) {
|
||||
if (released) {
|
||||
return;
|
||||
}
|
||||
Log.e(TAG, "Failed to resolve UtcTiming element [" + utcTiming + "]", e);
|
||||
// Be optimistic and continue in the hope that the device clock is correct.
|
||||
prepared = true;
|
||||
}
|
||||
|
||||
// Internal methods.
|
||||
@ -271,13 +302,14 @@ public final class DashSampleSource implements SampleSource {
|
||||
FormatEvaluator adaptiveEvaluator = selectedTracks.length > 1
|
||||
? new AdaptiveEvaluator(bandwidthMeter) : null;
|
||||
int adaptationSetIndex = trackGroupAdaptationSetIndices[selection.group];
|
||||
AdaptationSet adaptationSet = currentManifest.getPeriod(0).adaptationSets.get(
|
||||
AdaptationSet adaptationSet = manifest.getPeriod(0).adaptationSets.get(
|
||||
adaptationSetIndex);
|
||||
int adaptationSetType = adaptationSet.type;
|
||||
int bufferSize = Util.getDefaultBufferSize(adaptationSetType);
|
||||
DataSource dataSource = dataSourceFactory.createDataSource(bandwidthMeter);
|
||||
DashChunkSource chunkSource = new DashChunkSource(currentManifest, adaptationSetIndex,
|
||||
trackGroups.get(selection.group), selectedTracks, dataSource, adaptiveEvaluator);
|
||||
DashChunkSource chunkSource = new DashChunkSource(manifest, adaptationSetIndex,
|
||||
trackGroups.get(selection.group), selectedTracks, dataSource, adaptiveEvaluator,
|
||||
elapsedRealtimeOffset);
|
||||
ChunkTrackStream trackStream = new ChunkTrackStream(chunkSource, loadControl, bufferSize,
|
||||
positionUs, eventHandler, eventListener, adaptationSetType);
|
||||
return Pair.create(chunkSource, trackStream);
|
||||
|
@ -37,7 +37,6 @@ import com.google.android.exoplayer.upstream.BandwidthMeter;
|
||||
import com.google.android.exoplayer.upstream.DataSource;
|
||||
import com.google.android.exoplayer.upstream.DataSourceFactory;
|
||||
import com.google.android.exoplayer.upstream.DefaultAllocator;
|
||||
import com.google.android.exoplayer.util.Assertions;
|
||||
import com.google.android.exoplayer.util.ManifestFetcher;
|
||||
import com.google.android.exoplayer.util.MimeTypes;
|
||||
import com.google.android.exoplayer.util.Util;
|
||||
@ -67,7 +66,6 @@ public final class SmoothStreamingSampleSource implements SampleSource {
|
||||
private final LoadControl loadControl;
|
||||
private final ManifestFetcher<SmoothStreamingManifest> manifestFetcher;
|
||||
|
||||
private boolean prepared;
|
||||
private long durationUs;
|
||||
private SmoothStreamingManifest currentManifest;
|
||||
private TrackEncryptionBox[] trackEncryptionBoxes;
|
||||
@ -102,22 +100,20 @@ public final class SmoothStreamingSampleSource implements SampleSource {
|
||||
|
||||
@Override
|
||||
public boolean prepare(long positionUs) throws IOException {
|
||||
if (prepared) {
|
||||
if (currentManifest != null) {
|
||||
// Already prepared.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (currentManifest == null) {
|
||||
currentManifest = manifestFetcher.getManifest();
|
||||
if (currentManifest == null) {
|
||||
manifestFetcher.maybeThrowError();
|
||||
manifestFetcher.requestRefresh();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
durationUs = currentManifest.durationUs;
|
||||
buildTrackGroups(currentManifest);
|
||||
|
||||
ProtectionElement protectionElement = currentManifest.protectionElement;
|
||||
if (protectionElement != null) {
|
||||
byte[] keyId = getProtectionElementKeyId(protectionElement.data);
|
||||
@ -127,8 +123,6 @@ public final class SmoothStreamingSampleSource implements SampleSource {
|
||||
drmInitData.put(protectionElement.uuid,
|
||||
new SchemeInitData(MimeTypes.VIDEO_MP4, protectionElement.data));
|
||||
}
|
||||
|
||||
prepared = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -145,8 +139,6 @@ public final class SmoothStreamingSampleSource implements SampleSource {
|
||||
@Override
|
||||
public TrackStream[] selectTracks(List<TrackStream> oldStreams,
|
||||
List<TrackSelection> newSelections, long positionUs) {
|
||||
Assertions.checkState(prepared);
|
||||
|
||||
int newEnabledSourceCount = trackStreams.length + newSelections.size() - oldStreams.size();
|
||||
SmoothStreamingChunkSource[] newChunkSources =
|
||||
new SmoothStreamingChunkSource[newEnabledSourceCount];
|
||||
|
Loading…
x
Reference in New Issue
Block a user