Support for added and removed DASH periods.

Logic and some code is copied from V1.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=128706688
This commit is contained in:
eguven 2016-07-28 09:24:09 -07:00 committed by Oliver Woodman
parent c82a3db5ac
commit 2040615cc8
3 changed files with 76 additions and 30 deletions

View File

@ -87,6 +87,10 @@ import java.util.List;
} }
} }
public long getStartUs() {
return period.startMs * 1000;
}
// MediaPeriod implementation. // MediaPeriod implementation.
@Override @Override

View File

@ -41,6 +41,7 @@ import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Locale; import java.util.Locale;
import java.util.TimeZone; import java.util.TimeZone;
@ -72,7 +73,7 @@ public final class DashMediaSource implements MediaSource {
private long manifestLoadEndTimestamp; private long manifestLoadEndTimestamp;
private DashManifest manifest; private DashManifest manifest;
private Handler manifestRefreshHandler; private Handler manifestRefreshHandler;
private DashMediaPeriod[] periods; private ArrayList<DashMediaPeriod> periods;
private long elapsedRealtimeOffset; private long elapsedRealtimeOffset;
public DashMediaSource(Uri manifestUri, DataSource.Factory manifestDataSourceFactory, public DashMediaSource(Uri manifestUri, DataSource.Factory manifestDataSourceFactory,
@ -114,10 +115,9 @@ public final class DashMediaSource implements MediaSource {
if (id == null) { if (id == null) {
break; break;
} }
for (int i = 0; i < periods.length; i++) { int index = periods.indexOf(id);
if (periods[i] == id) { if (index != -1) {
return i; return index;
}
} }
periodIndex++; periodIndex++;
} }
@ -126,11 +126,11 @@ public final class DashMediaSource implements MediaSource {
@Override @Override
public MediaPeriod createPeriod(int index) throws IOException { public MediaPeriod createPeriod(int index) throws IOException {
if (periods == null) { if (periods == null || periods.size() <= index) {
loader.maybeThrowError(); loader.maybeThrowError();
return null; return null;
} }
return periods[index]; return periods.get(index);
} }
@Override @Override
@ -156,7 +156,29 @@ public final class DashMediaSource implements MediaSource {
long elapsedRealtimeMs, long loadDurationMs) { long elapsedRealtimeMs, long loadDurationMs) {
eventDispatcher.loadCompleted(loadable.dataSpec, loadable.type, elapsedRealtimeMs, eventDispatcher.loadCompleted(loadable.dataSpec, loadable.type, elapsedRealtimeMs,
loadDurationMs, loadable.bytesLoaded()); loadDurationMs, loadable.bytesLoaded());
manifest = loadable.getResult(); DashManifest newManifest = loadable.getResult();
int periodsToRemoveCount = 0;
if (periods != null) {
int periodCount = periods.size();
long newFirstPeriodStartTimeUs = newManifest.getPeriod(0).startMs * 1000;
while (periodsToRemoveCount < periodCount
&& periods.get(periodsToRemoveCount).getStartUs() < newFirstPeriodStartTimeUs) {
periodsToRemoveCount++;
}
// After discarding old periods, we should never have more periods than listed in the new
// manifest. That would mean that a previously announced period is no longer advertised. If
// this condition occurs, assume that we are hitting a manifest server that is out of sync and
// behind, discard this manifest, and try again later.
if (periodCount - periodsToRemoveCount > newManifest.getPeriodCount()) {
Log.w(TAG, "Out of sync manifest");
scheduleManifestRefresh();
return;
}
}
manifest = newManifest;
manifestLoadStartTimestamp = elapsedRealtimeMs - loadDurationMs; manifestLoadStartTimestamp = elapsedRealtimeMs - loadDurationMs;
manifestLoadEndTimestamp = elapsedRealtimeMs; manifestLoadEndTimestamp = elapsedRealtimeMs;
if (manifest.location != null) { if (manifest.location != null) {
@ -167,16 +189,29 @@ public final class DashMediaSource implements MediaSource {
if (manifest.utcTiming != null) { if (manifest.utcTiming != null) {
resolveUtcTimingElement(manifest.utcTiming); resolveUtcTimingElement(manifest.utcTiming);
} else { } else {
finishPrepare(); finishManifestProcessing();
} }
} else { } else {
for (int i = 0; i < periods.length; i++) { // Remove old periods.
periods[i].updateManifest(manifest, i); while (periodsToRemoveCount-- > 0) {
} periods.remove(0);
scheduleManifestRefresh();
} }
invalidationListener.onTimelineChanged(new DashTimeline(manifest, periods)); // Update existing periods. Only the first and the last periods can change.
int periodCount = periods.size();
if (periodCount > 0) {
updatePeriod(0);
if (periodCount > 1) {
updatePeriod(periodCount - 1);
}
}
finishManifestProcessing();
}
}
private void updatePeriod(int index) {
periods.get(index).updateManifest(manifest, index);
} }
/* package */ int onManifestLoadError(ParsingLoadable<DashManifest> loadable, /* package */ int onManifestLoadError(ParsingLoadable<DashManifest> loadable,
@ -247,22 +282,26 @@ public final class DashMediaSource implements MediaSource {
private void onUtcTimestampResolved(long elapsedRealtimeOffsetMs) { private void onUtcTimestampResolved(long elapsedRealtimeOffsetMs) {
this.elapsedRealtimeOffset = elapsedRealtimeOffsetMs; this.elapsedRealtimeOffset = elapsedRealtimeOffsetMs;
finishPrepare(); finishManifestProcessing();
} }
private void onUtcTimestampResolutionError(IOException error) { private void onUtcTimestampResolutionError(IOException error) {
Log.e(TAG, "Failed to resolve UtcTiming element.", error); Log.e(TAG, "Failed to resolve UtcTiming element.", error);
// Be optimistic and continue in the hope that the device clock is correct. // Be optimistic and continue in the hope that the device clock is correct.
finishPrepare(); finishManifestProcessing();
} }
private void finishPrepare() { private void finishManifestProcessing() {
int periodCount = manifest.getPeriodCount(); if (periods == null) {
periods = new DashMediaPeriod[periodCount]; periods = new ArrayList<>();
for (int i = 0; i < periodCount; i++) {
periods[i] = new DashMediaPeriod(manifest, i, chunkSourceFactory, minLoadableRetryCount,
eventDispatcher, elapsedRealtimeOffset, loader);
} }
int periodCount = manifest.getPeriodCount();
for (int i = periods.size(); i < periodCount; i++) {
periods.add(new DashMediaPeriod(manifest, i, chunkSourceFactory, minLoadableRetryCount,
eventDispatcher, elapsedRealtimeOffset, loader));
}
invalidationListener.onTimelineChanged(new DashTimeline(manifest,
periods.toArray(new DashMediaPeriod[periods.size()])));
scheduleManifestRefresh(); scheduleManifestRefresh();
} }

View File

@ -90,6 +90,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
private final Evaluation evaluation; private final Evaluation evaluation;
private DashManifest manifest; private DashManifest manifest;
private int periodIndex;
private boolean lastChunkWasInitialization; private boolean lastChunkWasInitialization;
private IOException fatalError; private IOException fatalError;
@ -117,11 +118,12 @@ public class DefaultDashChunkSource implements DashChunkSource {
this.trackSelection = trackSelection; this.trackSelection = trackSelection;
this.dataSource = dataSource; this.dataSource = dataSource;
this.adaptiveFormatEvaluator = adaptiveFormatEvaluator; this.adaptiveFormatEvaluator = adaptiveFormatEvaluator;
this.periodIndex = periodIndex;
this.elapsedRealtimeOffsetUs = elapsedRealtimeOffsetMs * 1000; this.elapsedRealtimeOffsetUs = elapsedRealtimeOffsetMs * 1000;
this.evaluation = new Evaluation(); this.evaluation = new Evaluation();
long periodDurationUs = getPeriodDurationUs(periodIndex); long periodDurationUs = getPeriodDurationUs();
List<Representation> representations = getRepresentations(periodIndex); List<Representation> representations = getRepresentations();
representationHolders = new RepresentationHolder[trackSelection.length]; representationHolders = new RepresentationHolder[trackSelection.length];
for (int i = 0; i < trackSelection.length; i++) { for (int i = 0; i < trackSelection.length; i++) {
Representation representation = representations.get(trackSelection.getTrack(i)); Representation representation = representations.get(trackSelection.getTrack(i));
@ -136,11 +138,12 @@ public class DefaultDashChunkSource implements DashChunkSource {
} }
@Override @Override
public void updateManifest(DashManifest newManifest, int periodIndex) { public void updateManifest(DashManifest newManifest, int newPeriodIndex) {
try { try {
manifest = newManifest; manifest = newManifest;
long periodDurationUs = getPeriodDurationUs(periodIndex); periodIndex = newPeriodIndex;
List<Representation> representations = getRepresentations(periodIndex); long periodDurationUs = getPeriodDurationUs();
List<Representation> representations = getRepresentations();
for (int i = 0; i < trackSelection.length; i++) { for (int i = 0; i < trackSelection.length; i++) {
Representation representation = representations.get(trackSelection.getTrack(i)); Representation representation = representations.get(trackSelection.getTrack(i));
representationHolders[i].updateRepresentation(periodDurationUs, representation); representationHolders[i].updateRepresentation(periodDurationUs, representation);
@ -249,7 +252,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
if (segmentNum > lastAvailableSegmentNum if (segmentNum > lastAvailableSegmentNum
|| (missingLastSegment && segmentNum >= lastAvailableSegmentNum)) { || (missingLastSegment && segmentNum >= lastAvailableSegmentNum)) {
// This is beyond the last chunk in the current manifest. // This is beyond the last chunk in the current manifest.
out.endOfStream = !manifest.dynamic; out.endOfStream = !manifest.dynamic || (periodIndex < manifest.getPeriodCount() - 1);
return; return;
} }
@ -309,7 +312,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
// Private methods. // Private methods.
private List<Representation> getRepresentations(int periodIndex) { private List<Representation> getRepresentations() {
return manifest.getPeriod(periodIndex).adaptationSets.get(adaptationSetIndex).representations; return manifest.getPeriod(periodIndex).adaptationSets.get(adaptationSetIndex).representations;
} }
@ -362,7 +365,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
} }
} }
private long getPeriodDurationUs(int periodIndex) { private long getPeriodDurationUs() {
long durationMs = manifest.getPeriodDuration(periodIndex); long durationMs = manifest.getPeriodDuration(periodIndex);
if (durationMs == -1) { if (durationMs == -1) {
return C.UNSET_TIME_US; return C.UNSET_TIME_US;