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.
@Override

View File

@ -41,6 +41,7 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Locale;
import java.util.TimeZone;
@ -72,7 +73,7 @@ public final class DashMediaSource implements MediaSource {
private long manifestLoadEndTimestamp;
private DashManifest manifest;
private Handler manifestRefreshHandler;
private DashMediaPeriod[] periods;
private ArrayList<DashMediaPeriod> periods;
private long elapsedRealtimeOffset;
public DashMediaSource(Uri manifestUri, DataSource.Factory manifestDataSourceFactory,
@ -114,10 +115,9 @@ public final class DashMediaSource implements MediaSource {
if (id == null) {
break;
}
for (int i = 0; i < periods.length; i++) {
if (periods[i] == id) {
return i;
}
int index = periods.indexOf(id);
if (index != -1) {
return index;
}
periodIndex++;
}
@ -126,11 +126,11 @@ public final class DashMediaSource implements MediaSource {
@Override
public MediaPeriod createPeriod(int index) throws IOException {
if (periods == null) {
if (periods == null || periods.size() <= index) {
loader.maybeThrowError();
return null;
}
return periods[index];
return periods.get(index);
}
@Override
@ -156,7 +156,29 @@ public final class DashMediaSource implements MediaSource {
long elapsedRealtimeMs, long loadDurationMs) {
eventDispatcher.loadCompleted(loadable.dataSpec, loadable.type, elapsedRealtimeMs,
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;
manifestLoadEndTimestamp = elapsedRealtimeMs;
if (manifest.location != null) {
@ -167,16 +189,29 @@ public final class DashMediaSource implements MediaSource {
if (manifest.utcTiming != null) {
resolveUtcTimingElement(manifest.utcTiming);
} else {
finishPrepare();
finishManifestProcessing();
}
} else {
for (int i = 0; i < periods.length; i++) {
periods[i].updateManifest(manifest, i);
// Remove old periods.
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,
@ -247,22 +282,26 @@ public final class DashMediaSource implements MediaSource {
private void onUtcTimestampResolved(long elapsedRealtimeOffsetMs) {
this.elapsedRealtimeOffset = elapsedRealtimeOffsetMs;
finishPrepare();
finishManifestProcessing();
}
private void onUtcTimestampResolutionError(IOException error) {
Log.e(TAG, "Failed to resolve UtcTiming element.", error);
// Be optimistic and continue in the hope that the device clock is correct.
finishPrepare();
finishManifestProcessing();
}
private void finishPrepare() {
int periodCount = manifest.getPeriodCount();
periods = new DashMediaPeriod[periodCount];
for (int i = 0; i < periodCount; i++) {
periods[i] = new DashMediaPeriod(manifest, i, chunkSourceFactory, minLoadableRetryCount,
eventDispatcher, elapsedRealtimeOffset, loader);
private void finishManifestProcessing() {
if (periods == null) {
periods = new ArrayList<>();
}
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();
}

View File

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