Refactor multi-period Period updates.

The main change here is to add PeriodHolder.updatePeriod, which is
analogous to RepresentationHolder.updateRepresentation.
This commit is contained in:
Oliver Woodman 2015-09-01 14:12:52 +01:00
parent b55a80dbf9
commit ebeafe23c0

View File

@ -542,7 +542,6 @@ public class DashChunkSource implements ChunkSource {
} }
RepresentationHolder representationHolder = periodHolder.representationHolders.get(formatId); RepresentationHolder representationHolder = periodHolder.representationHolders.get(formatId);
if (initializationChunk.hasFormat()) { if (initializationChunk.hasFormat()) {
representationHolder.mediaFormat = initializationChunk.getFormat(); representationHolder.mediaFormat = initializationChunk.getFormat();
} }
@ -550,7 +549,7 @@ public class DashChunkSource implements ChunkSource {
representationHolder.segmentIndex = new DashWrappingSegmentIndex( representationHolder.segmentIndex = new DashWrappingSegmentIndex(
(ChunkIndex) initializationChunk.getSeekMap(), (ChunkIndex) initializationChunk.getSeekMap(),
initializationChunk.dataSpec.uri.toString(), initializationChunk.dataSpec.uri.toString(),
representationHolder.representation.periodStartMs * 1000); periodHolder.startTimeUs);
} }
// The null check avoids overwriting drmInitData obtained from the manifest with drmInitData // The null check avoids overwriting drmInitData obtained from the manifest with drmInitData
@ -726,53 +725,34 @@ public class DashChunkSource implements ChunkSource {
} }
private void processManifest(MediaPresentationDescription manifest) { private void processManifest(MediaPresentationDescription manifest) {
Period firstPeriod = manifest.periods.get(0); List<Period> newPeriods = manifest.periods;
// Remove old periods.
Period firstPeriod = newPeriods.get(0);
while (periodHolders.size() > 0 while (periodHolders.size() > 0
&& periodHolders.valueAt(0).startTimeUs < firstPeriod.startMs * 1000) { && periodHolders.valueAt(0).startTimeUs < firstPeriod.startMs * 1000) {
// this existing period is no longer on the manifest, we need to remove it
PeriodHolder periodHolder = periodHolders.valueAt(0); PeriodHolder periodHolder = periodHolders.valueAt(0);
// TODO: a better call would be periodHolders.removeAt(0), but that was added in // TODO: Use periodHolders.removeAt(0) if the minimum API level is ever increased to 11.
// API 11 and this project currently uses API 9; if that changes, we should switch
// this to removeAt(0);
periodHolders.remove(periodHolder.manifestIndex); periodHolders.remove(periodHolder.manifestIndex);
} }
for (int i = 0; i < manifest.periods.size(); i++) { // Update existing periods.
Period period = manifest.periods.get(i); try {
AdaptationSet adaptationSet = period.adaptationSets.get(adaptationSetIndex); for (int i = 0; i < periodHolders.size(); i++) {
List<Representation> representations = adaptationSet.representations; periodHolders.valueAt(i).updatePeriod(newPeriods.get(i));
Representation newRepresentations[];
if (representationIndices == null) {
newRepresentations = new Representation[representations.size()];
representations.toArray(newRepresentations);
} else {
newRepresentations = new Representation[representationIndices.length];
for (int j = 0; j < representationIndices.length; j++) {
newRepresentations[j] = representations.get(representationIndices[j]);
}
} }
} catch (BehindLiveWindowException e) {
fatalError = e;
return;
}
if (i < periodHolders.size()) { // Add new periods.
// this is an existing period, we need to update it for (int i = periodHolders.size(); i < newPeriods.size(); i++) {
PeriodHolder periodHolder = periodHolders.valueAt(i); Period period = newPeriods.get(i);
for (int j = 0; j < newRepresentations.length; j++) { PeriodHolder periodHolder = new PeriodHolder(periodHolderNextIndex, period,
RepresentationHolder representationHolder = adaptationSetIndex, representationIndices);
periodHolder.representationHolders.get(newRepresentations[j].format.id); periodHolders.put(periodHolderNextIndex, periodHolder);
try { periodHolderNextIndex++;
representationHolder.updateRepresentation(newRepresentations[j]);
} catch (BehindLiveWindowException e) {
fatalError = e;
return;
}
}
} else {
// this is a new period, we need to add it
long periodStartUs = period.startMs * 1000;
PeriodHolder periodHolder = new PeriodHolder(periodHolderNextIndex, periodStartUs,
newRepresentations);
periodHolders.put(periodHolderNextIndex, periodHolder);
periodHolderNextIndex++;
}
} }
currentManifest = manifest; currentManifest = manifest;
@ -870,21 +850,35 @@ public class DashChunkSource implements ChunkSource {
public final Format[] formats; public final Format[] formats;
public final HashMap<String, RepresentationHolder> representationHolders; public final HashMap<String, RepresentationHolder> representationHolders;
private final int adaptationSetIndex;
private final int[] representationIndices;
private final int maxWidth; private final int maxWidth;
private final int maxHeight; private final int maxHeight;
public PeriodHolder(int manifestIndex, long startTimeUs, Representation[] representations) { private boolean indexIsUnbounded;
private boolean indexIsExplicit;
private long availableStartTimeUs;
private long availableEndTimeUs;
public PeriodHolder(int manifestIndex, Period period, int adaptationSetIndex,
int[] representationIndices) {
this.manifestIndex = manifestIndex; this.manifestIndex = manifestIndex;
this.startTimeUs = startTimeUs; this.adaptationSetIndex = adaptationSetIndex;
this.representationIndices = representationIndices;
this.formats = new Format[representations.length]; List<Representation> periodRepresentations =
this.representationHolders = new HashMap<>(); period.adaptationSets.get(adaptationSetIndex).representations;
int representationCount = representationIndices != null ? representationIndices.length
: periodRepresentations.size();
formats = new Format[representationCount];
representationHolders = new HashMap<>(representationCount);
int maxWidth = 0; int maxWidth = -1;
int maxHeight = 0; int maxHeight = -1;
String mimeType = ""; String mimeType = "";
for (int i = 0; i < representations.length; i++) { for (int i = 0; i < representationCount; i++) {
Representation representation = representations[i]; int representationIndex = representationIndices != null ? representationIndices[i] : i;
Representation representation = periodRepresentations.get(representationIndex);
formats[i] = representation.format; formats[i] = representation.format;
mimeType = getMediaMimeType(representation); mimeType = getMediaMimeType(representation);
maxWidth = Math.max(formats[i].width, maxWidth); maxWidth = Math.max(formats[i].width, maxWidth);
@ -899,69 +893,68 @@ public class DashChunkSource implements ChunkSource {
this.maxHeight = maxHeight; this.maxHeight = maxHeight;
this.mimeType = mimeType; this.mimeType = mimeType;
long durationMs = startTimeUs = period.startMs * 1000;
representationHolders.get(formats[0].id).representation.periodDurationMs; long durationMs = period.durationMs;
if (durationMs == TrackRenderer.UNKNOWN_TIME_US) { if (durationMs == -1) {
durationUs = TrackRenderer.UNKNOWN_TIME_US; durationUs = TrackRenderer.UNKNOWN_TIME_US;
} else { } else {
durationUs = durationMs * 1000; durationUs = durationMs * 1000;
} }
Arrays.sort(formats, new DecreasingBandwidthComparator()); Arrays.sort(formats, new DecreasingBandwidthComparator());
updateRepresentationIndependentProperties();
}
public void updatePeriod(Period period) throws BehindLiveWindowException {
List<Representation> representations =
period.adaptationSets.get(adaptationSetIndex).representations;
int representationCount = formats.length;
for (int i = 0; i < representationCount; i++) {
int representationIndex = representationIndices != null ? representationIndices[i] : i;
Representation representation = representations.get(representationIndex);
representationHolders.get(representation.format.id).updateRepresentation(representation);
}
updateRepresentationIndependentProperties();
} }
public long getAvailableStartTimeUs() { public long getAvailableStartTimeUs() {
RepresentationHolder representationHolder = representationHolders.get(formats[0].id); return availableStartTimeUs;
// in this case, we only want to use the segment index if it was defined in the manifest,
// otherwise we should just base this on the period information that was in the manifest
DashSegmentIndex segmentIndex = representationHolder.representation.getIndex();
if (segmentIndex != null) {
return segmentIndex.getTimeUs(segmentIndex.getFirstSegmentNum());
} else {
return startTimeUs;
}
} }
public long getAvailableEndTimeUs() { public long getAvailableEndTimeUs() {
RepresentationHolder representationHolder = representationHolders.get(formats[0].id); if (isIndexUnbounded()) {
// in this case, we only want to use the segment index if it was defined in the manifest, throw new IllegalStateException("Period has unbounded index");
// otherwise we should just base this on the period information that was in the manifest
DashSegmentIndex segmentIndex = representationHolder.representation.getIndex();
if (segmentIndex != null) {
int lastSegmentNum = segmentIndex.getLastSegmentNum();
if (lastSegmentNum == DashSegmentIndex.INDEX_UNBOUNDED) {
throw new IllegalStateException("Can't call this method on a period with and unbounded "
+ "index");
}
return segmentIndex.getTimeUs(lastSegmentNum) + segmentIndex.getDurationUs(lastSegmentNum);
} else {
return startTimeUs + (representationHolder.representation.periodDurationMs * 1000);
} }
return availableEndTimeUs;
} }
public boolean isIndexUnbounded() { public boolean isIndexUnbounded() {
RepresentationHolder representationHolder = representationHolders.get(formats[0].id); return indexIsUnbounded;
// in this case, we only want to use the segment index if it was defined in the manifest,
// otherwise we should just base this on the period information that was in the manifest
DashSegmentIndex segmentIndex = representationHolder.representation.getIndex();
if (segmentIndex != null) {
int lastSegmentNum = segmentIndex.getLastSegmentNum();
if (lastSegmentNum == DashSegmentIndex.INDEX_UNBOUNDED) {
return lastSegmentNum == DashSegmentIndex.INDEX_UNBOUNDED;
}
}
return false;
} }
public boolean isIndexExplicit() { public boolean isIndexExplicit() {
RepresentationHolder representationHolder = representationHolders.get(formats[0].id); return indexIsExplicit;
// in this case, we only want to use the segment index if it was defined in the manifest, }
// otherwise we should just base this on the period information that was in the manifest
DashSegmentIndex segmentIndex = representationHolder.representation.getIndex(); private void updateRepresentationIndependentProperties() {
// Arbitrarily use the first representation to derive representation independent properties.
Representation representation = representationHolders.get(formats[0].id).representation;
DashSegmentIndex segmentIndex = representation.getIndex();
if (segmentIndex != null) { if (segmentIndex != null) {
return segmentIndex.isExplicit(); int lastSegmentNum = segmentIndex.getLastSegmentNum();
indexIsUnbounded = lastSegmentNum == DashSegmentIndex.INDEX_UNBOUNDED;
indexIsExplicit = segmentIndex.isExplicit();
availableStartTimeUs = segmentIndex.getTimeUs(segmentIndex.getFirstSegmentNum());
if (!indexIsUnbounded) {
availableEndTimeUs = segmentIndex.getTimeUs(lastSegmentNum)
+ segmentIndex.getDurationUs(lastSegmentNum);
}
} else {
indexIsUnbounded = false;
indexIsExplicit = true;
availableStartTimeUs = startTimeUs;
availableEndTimeUs = startTimeUs + durationUs;
} }
return true;
} }
} }