mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Support dynamic TimeRange for DASH live.
This commit is contained in:
parent
d5f8d1a1b1
commit
f8824ac390
4 changed files with 193 additions and 109 deletions
|
|
@ -173,8 +173,8 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener
|
||||||
@Override
|
@Override
|
||||||
public void onAvailableRangeChanged(TimeRange availableRange) {
|
public void onAvailableRangeChanged(TimeRange availableRange) {
|
||||||
availableRangeValuesUs = availableRange.getCurrentBoundsUs(availableRangeValuesUs);
|
availableRangeValuesUs = availableRange.getCurrentBoundsUs(availableRangeValuesUs);
|
||||||
Log.d(TAG, "availableRange [ " + availableRange.type + ", " + availableRangeValuesUs[0] + ", "
|
Log.d(TAG, "availableRange [" + availableRange.isStatic() + ", " + availableRangeValuesUs[0]
|
||||||
+ availableRangeValuesUs[1] + "]");
|
+ ", " + availableRangeValuesUs[1] + "]");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void printInternalError(String type, Exception e) {
|
private void printInternalError(String type, Exception e) {
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer;
|
package com.google.android.exoplayer;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer.TimeRange.StaticTimeRange;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -22,14 +24,14 @@ import junit.framework.TestCase;
|
||||||
*/
|
*/
|
||||||
public class TimeRangeTest extends TestCase {
|
public class TimeRangeTest extends TestCase {
|
||||||
|
|
||||||
public void testEquals() {
|
public void testStaticEquals() {
|
||||||
TimeRange timeRange1 = new TimeRange(TimeRange.TYPE_SNAPSHOT, 0, 30000000);
|
TimeRange timeRange1 = new StaticTimeRange(0, 30000000);
|
||||||
assertTrue(timeRange1.equals(timeRange1));
|
assertTrue(timeRange1.equals(timeRange1));
|
||||||
|
|
||||||
TimeRange timeRange2 = new TimeRange(TimeRange.TYPE_SNAPSHOT, 0, 30000000);
|
TimeRange timeRange2 = new StaticTimeRange(0, 30000000);
|
||||||
assertTrue(timeRange1.equals(timeRange2));
|
assertTrue(timeRange1.equals(timeRange2));
|
||||||
|
|
||||||
TimeRange timeRange3 = new TimeRange(TimeRange.TYPE_SNAPSHOT, 0, 60000000);
|
TimeRange timeRange3 = new StaticTimeRange(0, 60000000);
|
||||||
assertFalse(timeRange1.equals(timeRange3));
|
assertFalse(timeRange1.equals(timeRange3));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,37 +15,22 @@
|
||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer;
|
package com.google.android.exoplayer;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer.util.Clock;
|
||||||
|
|
||||||
|
import android.os.SystemClock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A container to store a start and end time in microseconds.
|
* A container to store a start and end time in microseconds.
|
||||||
*/
|
*/
|
||||||
public final class TimeRange {
|
public interface TimeRange {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a range of time whose bounds change in bulk increments rather than smoothly over
|
* Whether the range is static, meaning repeated calls to {@link #getCurrentBoundsMs(long[])}
|
||||||
* time.
|
* or {@link #getCurrentBoundsUs(long[])} will return identical results.
|
||||||
*/
|
|
||||||
public static final int TYPE_SNAPSHOT = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of this time range.
|
|
||||||
*/
|
|
||||||
public final int type;
|
|
||||||
|
|
||||||
private final long startTimeUs;
|
|
||||||
private final long endTimeUs;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new {@link TimeRange} of the appropriate type.
|
|
||||||
*
|
*
|
||||||
* @param type The type of the TimeRange.
|
* @return Whether the range is static.
|
||||||
* @param startTimeUs The beginning of the TimeRange.
|
|
||||||
* @param endTimeUs The end of the TimeRange.
|
|
||||||
*/
|
*/
|
||||||
public TimeRange(int type, long startTimeUs, long endTimeUs) {
|
public boolean isStatic();
|
||||||
this.type = type;
|
|
||||||
this.startTimeUs = startTimeUs;
|
|
||||||
this.endTimeUs = endTimeUs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the start and end times (in milliseconds) of the TimeRange in the provided array,
|
* Returns the start and end times (in milliseconds) of the TimeRange in the provided array,
|
||||||
|
|
@ -54,12 +39,7 @@ public final class TimeRange {
|
||||||
* @param out An array to store the start and end times; can be null.
|
* @param out An array to store the start and end times; can be null.
|
||||||
* @return An array containing the start time (index 0) and end time (index 1) in milliseconds.
|
* @return An array containing the start time (index 0) and end time (index 1) in milliseconds.
|
||||||
*/
|
*/
|
||||||
public long[] getCurrentBoundsMs(long[] out) {
|
public long[] getCurrentBoundsMs(long[] out);
|
||||||
out = getCurrentBoundsUs(out);
|
|
||||||
out[0] /= 1000;
|
|
||||||
out[1] /= 1000;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the start and end times (in microseconds) of the TimeRange in the provided array,
|
* Returns the start and end times (in microseconds) of the TimeRange in the provided array,
|
||||||
|
|
@ -68,35 +48,156 @@ public final class TimeRange {
|
||||||
* @param out An array to store the start and end times; can be null.
|
* @param out An array to store the start and end times; can be null.
|
||||||
* @return An array containing the start time (index 0) and end time (index 1) in microseconds.
|
* @return An array containing the start time (index 0) and end time (index 1) in microseconds.
|
||||||
*/
|
*/
|
||||||
public long[] getCurrentBoundsUs(long[] out) {
|
public long[] getCurrentBoundsUs(long[] out);
|
||||||
if (out == null || out.length < 2) {
|
|
||||||
out = new long[2];
|
/**
|
||||||
|
* A static {@link TimeRange}.
|
||||||
|
*/
|
||||||
|
public static final class StaticTimeRange implements TimeRange {
|
||||||
|
|
||||||
|
private final long startTimeUs;
|
||||||
|
private final long endTimeUs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param startTimeUs The beginning of the range.
|
||||||
|
* @param endTimeUs The end of the range.
|
||||||
|
*/
|
||||||
|
public StaticTimeRange(long startTimeUs, long endTimeUs) {
|
||||||
|
this.startTimeUs = startTimeUs;
|
||||||
|
this.endTimeUs = endTimeUs;
|
||||||
}
|
}
|
||||||
out[0] = startTimeUs;
|
|
||||||
out[1] = endTimeUs;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public boolean isStatic() {
|
||||||
int hashCode = 0;
|
|
||||||
hashCode |= type << 30;
|
|
||||||
hashCode |= (((startTimeUs + endTimeUs) / 1000) & 0x3FFFFFFF);
|
|
||||||
return hashCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object other) {
|
|
||||||
if (other == this) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (other instanceof TimeRange) {
|
|
||||||
TimeRange otherTimeRange = (TimeRange) other;
|
@Override
|
||||||
return (otherTimeRange.type == type) && (otherTimeRange.startTimeUs == startTimeUs)
|
public long[] getCurrentBoundsMs(long[] out) {
|
||||||
&& (otherTimeRange.endTimeUs == endTimeUs);
|
out = getCurrentBoundsUs(out);
|
||||||
} else {
|
out[0] /= 1000;
|
||||||
|
out[1] /= 1000;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long[] getCurrentBoundsUs(long[] out) {
|
||||||
|
if (out == null || out.length < 2) {
|
||||||
|
out = new long[2];
|
||||||
|
}
|
||||||
|
out[0] = startTimeUs;
|
||||||
|
out[1] = endTimeUs;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = 17;
|
||||||
|
result = 31 * result + (int) startTimeUs;
|
||||||
|
result = 31 * result + (int) endTimeUs;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj == this) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null || getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
StaticTimeRange other = (StaticTimeRange) obj;
|
||||||
|
return other.startTimeUs == startTimeUs
|
||||||
|
&& other.endTimeUs == endTimeUs;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A dynamic {@link TimeRange}.
|
||||||
|
*/
|
||||||
|
public static final class DynamicTimeRange implements TimeRange {
|
||||||
|
|
||||||
|
private final long minStartTimeUs;
|
||||||
|
private final long maxEndTimeUs;
|
||||||
|
private final long elapsedRealtimeAtStartUs;
|
||||||
|
private final long bufferDepthUs;
|
||||||
|
private final Clock systemClock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param minStartTimeUs A lower bound on the beginning of the range.
|
||||||
|
* @param maxEndTimeUs An upper bound on the end of the range.
|
||||||
|
* @param elapsedRealtimeAtStartUs The value of {@link SystemClock#elapsedRealtime()},
|
||||||
|
* multiplied by 1000, corresponding to a media time of zero.
|
||||||
|
* @param bufferDepthUs The buffer depth of the media, or -1.
|
||||||
|
* @param systemClock A system clock.
|
||||||
|
*/
|
||||||
|
public DynamicTimeRange(long minStartTimeUs, long maxEndTimeUs, long elapsedRealtimeAtStartUs,
|
||||||
|
long bufferDepthUs, Clock systemClock) {
|
||||||
|
this.minStartTimeUs = minStartTimeUs;
|
||||||
|
this.maxEndTimeUs = maxEndTimeUs;
|
||||||
|
this.elapsedRealtimeAtStartUs = elapsedRealtimeAtStartUs;
|
||||||
|
this.bufferDepthUs = bufferDepthUs;
|
||||||
|
this.systemClock = systemClock;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isStatic() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long[] getCurrentBoundsMs(long[] out) {
|
||||||
|
out = getCurrentBoundsUs(out);
|
||||||
|
out[0] /= 1000;
|
||||||
|
out[1] /= 1000;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long[] getCurrentBoundsUs(long[] out) {
|
||||||
|
if (out == null || out.length < 2) {
|
||||||
|
out = new long[2];
|
||||||
|
}
|
||||||
|
// Don't allow the end time to be greater than the total elapsed time.
|
||||||
|
long currentEndTimeUs = Math.min(maxEndTimeUs,
|
||||||
|
(systemClock.elapsedRealtime() * 1000) - elapsedRealtimeAtStartUs);
|
||||||
|
long currentStartTimeUs = minStartTimeUs;
|
||||||
|
if (bufferDepthUs != -1) {
|
||||||
|
// Don't allow the start time to be less than the current end time minus the buffer depth.
|
||||||
|
currentStartTimeUs = Math.max(currentStartTimeUs,
|
||||||
|
currentEndTimeUs - bufferDepthUs);
|
||||||
|
}
|
||||||
|
out[0] = currentStartTimeUs;
|
||||||
|
out[1] = currentEndTimeUs;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = 17;
|
||||||
|
result = 31 * result + (int) minStartTimeUs;
|
||||||
|
result = 31 * result + (int) maxEndTimeUs;
|
||||||
|
result = 31 * result + (int) elapsedRealtimeAtStartUs;
|
||||||
|
result = 31 * result + (int) bufferDepthUs;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj == this) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null || getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DynamicTimeRange other = (DynamicTimeRange) obj;
|
||||||
|
return other.minStartTimeUs == minStartTimeUs
|
||||||
|
&& other.maxEndTimeUs == maxEndTimeUs
|
||||||
|
&& other.elapsedRealtimeAtStartUs == elapsedRealtimeAtStartUs
|
||||||
|
&& other.bufferDepthUs == bufferDepthUs;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ package com.google.android.exoplayer.dash;
|
||||||
import com.google.android.exoplayer.BehindLiveWindowException;
|
import com.google.android.exoplayer.BehindLiveWindowException;
|
||||||
import com.google.android.exoplayer.MediaFormat;
|
import com.google.android.exoplayer.MediaFormat;
|
||||||
import com.google.android.exoplayer.TimeRange;
|
import com.google.android.exoplayer.TimeRange;
|
||||||
|
import com.google.android.exoplayer.TimeRange.DynamicTimeRange;
|
||||||
|
import com.google.android.exoplayer.TimeRange.StaticTimeRange;
|
||||||
import com.google.android.exoplayer.TrackRenderer;
|
import com.google.android.exoplayer.TrackRenderer;
|
||||||
import com.google.android.exoplayer.chunk.Chunk;
|
import com.google.android.exoplayer.chunk.Chunk;
|
||||||
import com.google.android.exoplayer.chunk.ChunkExtractorWrapper;
|
import com.google.android.exoplayer.chunk.ChunkExtractorWrapper;
|
||||||
|
|
@ -115,6 +117,7 @@ public class DashChunkSource implements ChunkSource {
|
||||||
private final long elapsedRealtimeOffsetUs;
|
private final long elapsedRealtimeOffsetUs;
|
||||||
private final int maxWidth;
|
private final int maxWidth;
|
||||||
private final int maxHeight;
|
private final int maxHeight;
|
||||||
|
private final long[] availableRangeValues;
|
||||||
|
|
||||||
private final SparseArray<PeriodHolder> periodHolders;
|
private final SparseArray<PeriodHolder> periodHolders;
|
||||||
|
|
||||||
|
|
@ -128,7 +131,6 @@ public class DashChunkSource implements ChunkSource {
|
||||||
|
|
||||||
private DrmInitData drmInitData;
|
private DrmInitData drmInitData;
|
||||||
private TimeRange availableRange;
|
private TimeRange availableRange;
|
||||||
private long[] availableRangeValues;
|
|
||||||
|
|
||||||
private boolean startAtLiveEdge;
|
private boolean startAtLiveEdge;
|
||||||
private boolean lastChunkWasInitialization;
|
private boolean lastChunkWasInitialization;
|
||||||
|
|
@ -327,7 +329,6 @@ public class DashChunkSource implements ChunkSource {
|
||||||
if (manifestFetcher != null) {
|
if (manifestFetcher != null) {
|
||||||
manifestFetcher.enable();
|
manifestFetcher.enable();
|
||||||
}
|
}
|
||||||
updateAvailableBounds(getNowUs());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -348,7 +349,6 @@ public class DashChunkSource implements ChunkSource {
|
||||||
MediaPresentationDescription newManifest = manifestFetcher.getManifest();
|
MediaPresentationDescription newManifest = manifestFetcher.getManifest();
|
||||||
if (currentManifest != newManifest && newManifest != null) {
|
if (currentManifest != newManifest && newManifest != null) {
|
||||||
processManifest(newManifest);
|
processManifest(newManifest);
|
||||||
updateAvailableBounds(getNowUs());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This is a temporary hack to avoid constantly refreshing the MPD in cases where
|
// TODO: This is a temporary hack to avoid constantly refreshing the MPD in cases where
|
||||||
|
|
@ -401,19 +401,12 @@ public class DashChunkSource implements ChunkSource {
|
||||||
// In all cases where we return before instantiating a new chunk, we want out.chunk to be null.
|
// In all cases where we return before instantiating a new chunk, we want out.chunk to be null.
|
||||||
out.chunk = null;
|
out.chunk = null;
|
||||||
|
|
||||||
if (currentManifest.dynamic
|
|
||||||
&& periodHolders.valueAt(periodHolders.size() - 1).isIndexUnbounded()) {
|
|
||||||
// Manifests with unbounded indexes aren't updated regularly, so we need to update the
|
|
||||||
// segment bounds before use to ensure that they are accurate to the current time
|
|
||||||
updateAvailableBounds(getNowUs());
|
|
||||||
}
|
|
||||||
availableRangeValues = availableRange.getCurrentBoundsUs(availableRangeValues);
|
|
||||||
|
|
||||||
long segmentStartTimeUs;
|
long segmentStartTimeUs;
|
||||||
int segmentNum = -1;
|
int segmentNum = -1;
|
||||||
boolean startingNewPeriod = false;
|
boolean startingNewPeriod = false;
|
||||||
PeriodHolder periodHolder;
|
PeriodHolder periodHolder;
|
||||||
|
|
||||||
|
availableRange.getCurrentBoundsUs(availableRangeValues);
|
||||||
if (queue.isEmpty()) {
|
if (queue.isEmpty()) {
|
||||||
if (currentManifest.dynamic) {
|
if (currentManifest.dynamic) {
|
||||||
if (startAtLiveEdge) {
|
if (startAtLiveEdge) {
|
||||||
|
|
@ -565,45 +558,6 @@ public class DashChunkSource implements ChunkSource {
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateAvailableBounds(long nowUs) {
|
|
||||||
PeriodHolder firstPeriod = periodHolders.valueAt(0);
|
|
||||||
long earliestAvailablePosition = firstPeriod.getAvailableStartTimeUs();
|
|
||||||
PeriodHolder lastPeriod = periodHolders.valueAt(periodHolders.size() - 1);
|
|
||||||
boolean isManifestUnbounded = lastPeriod.isIndexUnbounded();
|
|
||||||
long latestAvailablePosition;
|
|
||||||
if (!currentManifest.dynamic || !isManifestUnbounded) {
|
|
||||||
latestAvailablePosition = lastPeriod.getAvailableEndTimeUs();
|
|
||||||
} else {
|
|
||||||
latestAvailablePosition = TrackRenderer.UNKNOWN_TIME_US;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentManifest.dynamic) {
|
|
||||||
if (isManifestUnbounded) {
|
|
||||||
latestAvailablePosition = nowUs - currentManifest.availabilityStartTime * 1000;
|
|
||||||
} else if (!lastPeriod.isIndexExplicit()) {
|
|
||||||
// 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.
|
|
||||||
latestAvailablePosition = Math.min(latestAvailablePosition,
|
|
||||||
nowUs - currentManifest.availabilityStartTime * 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we have a limited timeshift buffer, we need to adjust the earliest seek position so
|
|
||||||
// that it doesn't start before the buffer
|
|
||||||
if (currentManifest.timeShiftBufferDepth != -1) {
|
|
||||||
long bufferDepthUs = currentManifest.timeShiftBufferDepth * 1000;
|
|
||||||
earliestAvailablePosition = Math.max(earliestAvailablePosition,
|
|
||||||
latestAvailablePosition - bufferDepthUs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TimeRange newAvailableRange = new TimeRange(TimeRange.TYPE_SNAPSHOT, earliestAvailablePosition,
|
|
||||||
latestAvailablePosition);
|
|
||||||
if (availableRange == null || !availableRange.equals(newAvailableRange)) {
|
|
||||||
availableRange = newAvailableRange;
|
|
||||||
notifyAvailableRangeChanged(availableRange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean mimeTypeIsWebm(String mimeType) {
|
private static boolean mimeTypeIsWebm(String mimeType) {
|
||||||
return mimeType.startsWith(MimeTypes.VIDEO_WEBM) || mimeType.startsWith(MimeTypes.AUDIO_WEBM);
|
return mimeType.startsWith(MimeTypes.VIDEO_WEBM) || mimeType.startsWith(MimeTypes.AUDIO_WEBM);
|
||||||
}
|
}
|
||||||
|
|
@ -755,9 +709,36 @@ public class DashChunkSource implements ChunkSource {
|
||||||
periodHolderNextIndex++;
|
periodHolderNextIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the available range.
|
||||||
|
TimeRange newAvailableRange = getAvailableRange(getNowUs());
|
||||||
|
if (availableRange == null || !availableRange.equals(newAvailableRange)) {
|
||||||
|
availableRange = newAvailableRange;
|
||||||
|
notifyAvailableRangeChanged(availableRange);
|
||||||
|
}
|
||||||
|
|
||||||
currentManifest = manifest;
|
currentManifest = manifest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private TimeRange getAvailableRange(long nowUs) {
|
||||||
|
PeriodHolder firstPeriod = periodHolders.valueAt(0);
|
||||||
|
PeriodHolder lastPeriod = periodHolders.valueAt(periodHolders.size() - 1);
|
||||||
|
|
||||||
|
if (!currentManifest.dynamic || lastPeriod.isIndexExplicit()) {
|
||||||
|
return new StaticTimeRange(firstPeriod.getAvailableStartTimeUs(),
|
||||||
|
lastPeriod.getAvailableEndTimeUs());
|
||||||
|
}
|
||||||
|
|
||||||
|
long minStartPositionUs = firstPeriod.getAvailableStartTimeUs();
|
||||||
|
long maxEndPositionUs = lastPeriod.isIndexUnbounded() ? Long.MAX_VALUE
|
||||||
|
: lastPeriod.getAvailableEndTimeUs();
|
||||||
|
long elapsedRealtimeAtZeroUs = (systemClock.elapsedRealtime() * 1000)
|
||||||
|
- (nowUs - currentManifest.availabilityStartTime * 1000);
|
||||||
|
long timeShiftBufferDepthUs = currentManifest.timeShiftBufferDepth == -1 ? -1
|
||||||
|
: currentManifest.timeShiftBufferDepth * 1000;
|
||||||
|
return new DynamicTimeRange(minStartPositionUs, maxEndPositionUs, elapsedRealtimeAtZeroUs,
|
||||||
|
timeShiftBufferDepthUs, systemClock);
|
||||||
|
}
|
||||||
|
|
||||||
private void notifyAvailableRangeChanged(final TimeRange seekRange) {
|
private void notifyAvailableRangeChanged(final TimeRange seekRange) {
|
||||||
if (eventHandler != null && eventListener != null) {
|
if (eventHandler != null && eventListener != null) {
|
||||||
eventHandler.post(new Runnable() {
|
eventHandler.post(new Runnable() {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue