Fix DASH Live edge calculation.
Also added clamping to getSegmentNum in one case where it was not already implemented, and defined this behavior property in the getSegmentNum javadoc. Issue: #262
This commit is contained in:
parent
a64df69f85
commit
f1a7784eb1
@ -349,7 +349,7 @@ public class DashChunkSource implements ChunkSource {
|
||||
int segmentNum;
|
||||
if (queue.isEmpty()) {
|
||||
if (currentManifest.dynamic) {
|
||||
seekPositionUs = getLiveSeekPosition(nowUs, indexUnbounded);
|
||||
seekPositionUs = getLiveSeekPosition(nowUs, indexUnbounded, segmentIndex.isExplicit());
|
||||
}
|
||||
segmentNum = segmentIndex.getSegmentNum(seekPositionUs);
|
||||
} else {
|
||||
@ -476,9 +476,10 @@ public class DashChunkSource implements ChunkSource {
|
||||
*
|
||||
* @param nowUs An estimate of the current server time, in microseconds.
|
||||
* @param indexUnbounded True if the segment index for this source is unbounded. False otherwise.
|
||||
* @param indexExplicit True if the segment index is explicit. False otherwise.
|
||||
* @return The seek position in microseconds.
|
||||
*/
|
||||
private long getLiveSeekPosition(long nowUs, boolean indexUnbounded) {
|
||||
private long getLiveSeekPosition(long nowUs, boolean indexUnbounded, boolean indexExplicit) {
|
||||
long liveEdgeTimestampUs;
|
||||
if (indexUnbounded) {
|
||||
liveEdgeTimestampUs = nowUs - currentManifest.availabilityStartTime * 1000;
|
||||
@ -491,6 +492,12 @@ public class DashChunkSource implements ChunkSource {
|
||||
+ segmentIndex.getDurationUs(lastSegmentNum);
|
||||
liveEdgeTimestampUs = Math.max(liveEdgeTimestampUs, indexLiveEdgeTimestampUs);
|
||||
}
|
||||
if (!indexExplicit) {
|
||||
// Some segments defined by the index may not be available yet. Bound the calculated live
|
||||
// edge based on the elapsed time since the manifest became available.
|
||||
liveEdgeTimestampUs = Math.min(liveEdgeTimestampUs,
|
||||
nowUs - currentManifest.availabilityStartTime * 1000);
|
||||
}
|
||||
}
|
||||
return liveEdgeTimestampUs - liveEdgeLatencyUs;
|
||||
}
|
||||
|
@ -28,6 +28,11 @@ public interface DashSegmentIndex {
|
||||
|
||||
/**
|
||||
* Returns the segment number of the segment containing a given media time.
|
||||
* <p>
|
||||
* If the given media time is outside the range of the index, then the returned segment number is
|
||||
* clamped to {@link #getFirstSegmentNum()} (if the given media time is earlier the start of the
|
||||
* first segment) or {@link #getLastSegmentNum()} (if the given media time is later then the end
|
||||
* of the last segment).
|
||||
*
|
||||
* @param timeUs The time in microseconds.
|
||||
* @return The segment number of the corresponding segment.
|
||||
@ -78,4 +83,18 @@ public interface DashSegmentIndex {
|
||||
*/
|
||||
int getLastSegmentNum();
|
||||
|
||||
/**
|
||||
* Returns true if segments are defined explicitly by the index.
|
||||
* <p>
|
||||
* If true is returned, each segment is defined explicitly by the index data, and all of the
|
||||
* listed segments are guaranteed to be available at the time when the index was obtained.
|
||||
* <p>
|
||||
* If false is returned then segment information was derived from properties such as a fixed
|
||||
* segment duration. If the presentation is dynamic, it's possible that only a subset of the
|
||||
* segments are available.
|
||||
*
|
||||
* @return True if segments are defined explicitly by the index. False otherwise.
|
||||
*/
|
||||
boolean isExplicit();
|
||||
|
||||
}
|
||||
|
@ -74,4 +74,9 @@ public class DashWrappingSegmentIndex implements DashSegmentIndex {
|
||||
return Util.binarySearchFloor(segmentIndex.timesUs, timeUs, true, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExplicit() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -277,6 +277,11 @@ public abstract class Representation {
|
||||
return segmentBase.getLastSegmentNum();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExplicit() {
|
||||
return segmentBase.isExplicit();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -128,15 +128,22 @@ public abstract class SegmentBase {
|
||||
this.segmentTimeline = segmentTimeline;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DashSegmentIndex#getSegmentNum(long)
|
||||
*/
|
||||
public int getSegmentNum(long timeUs) {
|
||||
int lowIndex = getFirstSegmentNum();
|
||||
int highIndex = getLastSegmentNum();
|
||||
if (segmentTimeline == null) {
|
||||
// All segments are of equal duration (with the possible exception of the last one).
|
||||
long durationUs = (duration * C.MICROS_PER_SECOND) / timescale;
|
||||
return startNumber + (int) (timeUs / durationUs);
|
||||
int segmentNum = startNumber + (int) (timeUs / durationUs);
|
||||
// Ensure we stay within bounds.
|
||||
return segmentNum < lowIndex ? lowIndex
|
||||
: highIndex != DashSegmentIndex.INDEX_UNBOUNDED && segmentNum > highIndex ? highIndex
|
||||
: segmentNum;
|
||||
} else {
|
||||
// Identify the segment using binary search.
|
||||
int lowIndex = getFirstSegmentNum();
|
||||
int highIndex = getLastSegmentNum();
|
||||
// The high index cannot be unbounded. Identify the segment using binary search.
|
||||
while (lowIndex <= highIndex) {
|
||||
int midIndex = (lowIndex + highIndex) / 2;
|
||||
long midTimeUs = getSegmentTimeUs(midIndex);
|
||||
@ -152,6 +159,9 @@ public abstract class SegmentBase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DashSegmentIndex#getDurationUs(int)
|
||||
*/
|
||||
public final long getSegmentDurationUs(int sequenceNumber) {
|
||||
if (segmentTimeline != null) {
|
||||
long duration = segmentTimeline.get(sequenceNumber - startNumber).duration;
|
||||
@ -163,6 +173,9 @@ public abstract class SegmentBase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DashSegmentIndex#getTimeUs(int)
|
||||
*/
|
||||
public final long getSegmentTimeUs(int sequenceNumber) {
|
||||
long unscaledSegmentTime;
|
||||
if (segmentTimeline != null) {
|
||||
@ -174,14 +187,33 @@ public abstract class SegmentBase {
|
||||
return Util.scaleLargeTimestamp(unscaledSegmentTime, C.MICROS_PER_SECOND, timescale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link RangedUri} defining the location of a segment for the given index in the
|
||||
* given representation.
|
||||
*
|
||||
* @see DashSegmentIndex#getSegmentUrl(int)
|
||||
*/
|
||||
public abstract RangedUri getSegmentUrl(Representation representation, int index);
|
||||
|
||||
/**
|
||||
* @see DashSegmentIndex#getFirstSegmentNum()
|
||||
*/
|
||||
public int getFirstSegmentNum() {
|
||||
return startNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DashSegmentIndex#getLastSegmentNum()
|
||||
*/
|
||||
public abstract int getLastSegmentNum();
|
||||
|
||||
/**
|
||||
* @see DashSegmentIndex#isExplicit()
|
||||
*/
|
||||
public boolean isExplicit() {
|
||||
return segmentTimeline != null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -225,6 +257,11 @@ public abstract class SegmentBase {
|
||||
return startNumber + mediaSegments.size() - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExplicit() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user