Fix timestamp rollover issue for DASH live.

The timestamp scaling in SegmentBase.getSegmentTimeUs was
overflowing for some streams. Apply a similar trick to that
applied in the SmoothStreaming case to fix it.
This commit is contained in:
Oliver Woodman 2014-11-27 18:14:19 +00:00
parent c534263032
commit 2969bba60f
3 changed files with 62 additions and 34 deletions

View file

@ -15,6 +15,8 @@
*/
package com.google.android.exoplayer.dash.mpd;
import com.google.android.exoplayer.util.Util;
import android.net.Uri;
import java.util.List;
@ -155,7 +157,7 @@ public abstract class SegmentBase {
} else {
unscaledSegmentTime = (sequenceNumber - startNumber) * duration;
}
return (unscaledSegmentTime * 1000000) / timescale;
return Util.scaleLargeTimestamp(unscaledSegmentTime, 1000000, timescale);
}
public abstract RangedUri getSegmentUrl(Representation representation, int index);

View file

@ -53,19 +53,8 @@ public class SmoothStreamingManifest {
this.isLive = isLive;
this.protectionElement = protectionElement;
this.streamElements = streamElements;
if (timescale >= MICROS_PER_SECOND && (timescale % MICROS_PER_SECOND) == 0) {
long divisionFactor = timescale / MICROS_PER_SECOND;
dvrWindowLengthUs = dvrWindowLength / divisionFactor;
durationUs = duration / divisionFactor;
} else if (timescale < MICROS_PER_SECOND && (MICROS_PER_SECOND % timescale) == 0) {
long multiplicationFactor = MICROS_PER_SECOND / timescale;
dvrWindowLengthUs = dvrWindowLength * multiplicationFactor;
durationUs = duration * multiplicationFactor;
} else {
double multiplicationFactor = (double) MICROS_PER_SECOND / timescale;
dvrWindowLengthUs = (long) (dvrWindowLength * multiplicationFactor);
durationUs = (long) (duration * multiplicationFactor);
}
dvrWindowLengthUs = Util.scaleLargeTimestamp(dvrWindowLength, MICROS_PER_SECOND, timescale);
durationUs = Util.scaleLargeTimestamp(duration, MICROS_PER_SECOND, timescale);
}
/**
@ -186,26 +175,10 @@ public class SmoothStreamingManifest {
this.tracks = tracks;
this.chunkCount = chunkStartTimes.size();
this.chunkStartTimes = chunkStartTimes;
chunkStartTimesUs = new long[chunkStartTimes.size()];
if (timescale >= MICROS_PER_SECOND && (timescale % MICROS_PER_SECOND) == 0) {
long divisionFactor = timescale / MICROS_PER_SECOND;
for (int i = 0; i < chunkStartTimesUs.length; i++) {
chunkStartTimesUs[i] = chunkStartTimes.get(i) / divisionFactor;
}
lastChunkDurationUs = lastChunkDuration / divisionFactor;
} else if (timescale < MICROS_PER_SECOND && (MICROS_PER_SECOND % timescale) == 0) {
long multiplicationFactor = MICROS_PER_SECOND / timescale;
for (int i = 0; i < chunkStartTimesUs.length; i++) {
chunkStartTimesUs[i] = chunkStartTimes.get(i) * multiplicationFactor;
}
lastChunkDurationUs = lastChunkDuration * multiplicationFactor;
} else {
double multiplicationFactor = (double) MICROS_PER_SECOND / timescale;
for (int i = 0; i < chunkStartTimesUs.length; i++) {
chunkStartTimesUs[i] = (long) (chunkStartTimes.get(i) * multiplicationFactor);
}
lastChunkDurationUs = (long) (lastChunkDuration * multiplicationFactor);
}
lastChunkDurationUs =
Util.scaleLargeTimestamp(lastChunkDuration, MICROS_PER_SECOND, timescale);
chunkStartTimesUs =
Util.scaleLargeTimestamps(chunkStartTimes, MICROS_PER_SECOND, timescale);
}
/**

View file

@ -346,4 +346,57 @@ public final class Util {
return time;
}
/**
* Scales a large timestamp.
* <p>
* Logically, scaling consists of a multiplication followed by a division. The actual operations
* performed are designed to minimize the probability of overflow.
*
* @param timestamp The timestamp to scale.
* @param multiplier The multiplier.
* @param divisor The divisor.
* @return The scaled timestamp.
*/
public static long scaleLargeTimestamp(long timestamp, long multiplier, long divisor) {
if (divisor >= multiplier && (divisor % multiplier) == 0) {
long divisionFactor = divisor / multiplier;
return timestamp / divisionFactor;
} else if (divisor < multiplier && (multiplier % divisor) == 0) {
long multiplicationFactor = multiplier / divisor;
return timestamp * multiplicationFactor;
} else {
double multiplicationFactor = (double) multiplier / divisor;
return (long) (timestamp * multiplicationFactor);
}
}
/**
* Applies {@link #scaleLargeTimestamp(long, long, long)} to a list of unscaled timestamps.
*
* @param timestamps The timestamps to scale.
* @param multiplier The multiplier.
* @param divisor The divisor.
* @return The scaled timestamps.
*/
public static long[] scaleLargeTimestamps(List<Long> timestamps, long multiplier, long divisor) {
long[] scaledTimestamps = new long[timestamps.size()];
if (divisor >= multiplier && (divisor % multiplier) == 0) {
long divisionFactor = divisor / multiplier;
for (int i = 0; i < scaledTimestamps.length; i++) {
scaledTimestamps[i] = timestamps.get(i) / divisionFactor;
}
} else if (divisor < multiplier && (multiplier % divisor) == 0) {
long multiplicationFactor = multiplier / divisor;
for (int i = 0; i < scaledTimestamps.length; i++) {
scaledTimestamps[i] = timestamps.get(i) * multiplicationFactor;
}
} else {
double multiplicationFactor = (double) multiplier / divisor;
for (int i = 0; i < scaledTimestamps.length; i++) {
scaledTimestamps[i] = (long) (timestamps.get(i) * multiplicationFactor);
}
}
return scaledTimestamps;
}
}