Commit 05833356 by Oliver Woodman

Use bits/sec instead of bytes/sec for format bandwidth.

Why: This was a bad initial choice. Manifests typically define bandwidth in
bits/sec. If you divide by 8 then you're throwing away information due to
rounding. Unfortunately it turns out that SegmentTemplate based manifests
require you to be able to recall the bitrate exactly (because it's substituted
in during segment URL construction).

Medium term: We should consider converting all our bandwidth estimation
over to bits/sec as well.

Note1: Also changed Period id to be a string, to match the mpd spec.
Note2: Made small optimization in FormatEvaluator to not consider discarding
the first chunk (durationBeforeThisSegmentUs will always be negative, and even
in the error case where it's not, removing the first thunk should be an error).
parent 4366afc2
...@@ -31,7 +31,7 @@ public class Format { ...@@ -31,7 +31,7 @@ public class Format {
@Override @Override
public int compare(Format a, Format b) { public int compare(Format a, Format b) {
return b.bandwidth - a.bandwidth; return b.bitrate - a.bitrate;
} }
} }
...@@ -67,26 +67,17 @@ public class Format { ...@@ -67,26 +67,17 @@ public class Format {
public final int audioSamplingRate; public final int audioSamplingRate;
/** /**
* The average bandwidth in bytes per second. * The average bandwidth in bits per second.
*/ */
public final int bandwidth; public final int bitrate;
/** /**
* @deprecated Format identifiers are now strings. * The average bandwidth in bytes per second.
* *
* @param id The format identifier. * @deprecated Use {@link #bitrate}. However note that the units of measurement are different.
* @param mimeType The format mime type.
* @param width The width of the video in pixels, or -1 for non-video formats.
* @param height The height of the video in pixels, or -1 for non-video formats.
* @param numChannels The number of audio channels, or -1 for non-audio formats.
* @param audioSamplingRate The audio sampling rate in Hz, or -1 for non-audio formats.
* @param bandwidth The average bandwidth of the format in bytes per second.
*/ */
@Deprecated @Deprecated
public Format(int id, String mimeType, int width, int height, int numChannels, public final int bandwidth;
int audioSamplingRate, int bandwidth) {
this(String.valueOf(id), mimeType, width, height, numChannels, audioSamplingRate, bandwidth);
}
/** /**
* @param id The format identifier. * @param id The format identifier.
...@@ -95,17 +86,18 @@ public class Format { ...@@ -95,17 +86,18 @@ public class Format {
* @param height The height of the video in pixels, or -1 for non-video formats. * @param height The height of the video in pixels, or -1 for non-video formats.
* @param numChannels The number of audio channels, or -1 for non-audio formats. * @param numChannels The number of audio channels, or -1 for non-audio formats.
* @param audioSamplingRate The audio sampling rate in Hz, or -1 for non-audio formats. * @param audioSamplingRate The audio sampling rate in Hz, or -1 for non-audio formats.
* @param bandwidth The average bandwidth of the format in bytes per second. * @param bitrate The average bandwidth of the format in bits per second.
*/ */
public Format(String id, String mimeType, int width, int height, int numChannels, public Format(String id, String mimeType, int width, int height, int numChannels,
int audioSamplingRate, int bandwidth) { int audioSamplingRate, int bitrate) {
this.id = Assertions.checkNotNull(id); this.id = Assertions.checkNotNull(id);
this.mimeType = mimeType; this.mimeType = mimeType;
this.width = width; this.width = width;
this.height = height; this.height = height;
this.numChannels = numChannels; this.numChannels = numChannels;
this.audioSamplingRate = audioSamplingRate; this.audioSamplingRate = audioSamplingRate;
this.bandwidth = bandwidth; this.bitrate = bitrate;
this.bandwidth = bitrate / 8;
} }
/** /**
......
...@@ -236,8 +236,8 @@ public interface FormatEvaluator { ...@@ -236,8 +236,8 @@ public interface FormatEvaluator {
: queue.get(queue.size() - 1).endTimeUs - playbackPositionUs; : queue.get(queue.size() - 1).endTimeUs - playbackPositionUs;
Format current = evaluation.format; Format current = evaluation.format;
Format ideal = determineIdealFormat(formats, bandwidthMeter.getEstimate()); Format ideal = determineIdealFormat(formats, bandwidthMeter.getEstimate());
boolean isHigher = ideal != null && current != null && ideal.bandwidth > current.bandwidth; boolean isHigher = ideal != null && current != null && ideal.bitrate > current.bitrate;
boolean isLower = ideal != null && current != null && ideal.bandwidth < current.bandwidth; boolean isLower = ideal != null && current != null && ideal.bitrate < current.bitrate;
if (isHigher) { if (isHigher) {
if (bufferedDurationUs < minDurationForQualityIncreaseUs) { if (bufferedDurationUs < minDurationForQualityIncreaseUs) {
// The ideal format is a higher quality, but we have insufficient buffer to // The ideal format is a higher quality, but we have insufficient buffer to
...@@ -247,11 +247,11 @@ public interface FormatEvaluator { ...@@ -247,11 +247,11 @@ public interface FormatEvaluator {
// We're switching from an SD stream to a stream of higher resolution. Consider // We're switching from an SD stream to a stream of higher resolution. Consider
// discarding already buffered media chunks. Specifically, discard media chunks starting // discarding already buffered media chunks. Specifically, discard media chunks starting
// from the first one that is of lower bandwidth, lower resolution and that is not HD. // from the first one that is of lower bandwidth, lower resolution and that is not HD.
for (int i = 0; i < queue.size(); i++) { for (int i = 1; i < queue.size(); i++) {
MediaChunk thisChunk = queue.get(i); MediaChunk thisChunk = queue.get(i);
long durationBeforeThisSegmentUs = thisChunk.startTimeUs - playbackPositionUs; long durationBeforeThisSegmentUs = thisChunk.startTimeUs - playbackPositionUs;
if (durationBeforeThisSegmentUs >= minDurationToRetainAfterDiscardUs if (durationBeforeThisSegmentUs >= minDurationToRetainAfterDiscardUs
&& thisChunk.format.bandwidth < ideal.bandwidth && thisChunk.format.bitrate < ideal.bitrate
&& thisChunk.format.height < ideal.height && thisChunk.format.height < ideal.height
&& thisChunk.format.height < 720 && thisChunk.format.height < 720
&& thisChunk.format.width < 1280) { && thisChunk.format.width < 1280) {
...@@ -280,7 +280,7 @@ public interface FormatEvaluator { ...@@ -280,7 +280,7 @@ public interface FormatEvaluator {
long effectiveBandwidth = computeEffectiveBandwidthEstimate(bandwidthEstimate); long effectiveBandwidth = computeEffectiveBandwidthEstimate(bandwidthEstimate);
for (int i = 0; i < formats.length; i++) { for (int i = 0; i < formats.length; i++) {
Format format = formats[i]; Format format = formats[i];
if (format.bandwidth <= effectiveBandwidth) { if ((format.bitrate / 8) <= effectiveBandwidth) {
return format; return format;
} }
} }
......
...@@ -111,7 +111,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler { ...@@ -111,7 +111,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler {
private Period parsePeriod(XmlPullParser xpp, String contentId, Uri parentBaseUrl, private Period parsePeriod(XmlPullParser xpp, String contentId, Uri parentBaseUrl,
long mediaPresentationDuration) throws XmlPullParserException, IOException { long mediaPresentationDuration) throws XmlPullParserException, IOException {
Uri baseUrl = parentBaseUrl; Uri baseUrl = parentBaseUrl;
int id = parseInt(xpp, "id"); String id = xpp.getAttributeValue(null, "id");
long start = parseDurationMs(xpp, "start", 0); long start = parseDurationMs(xpp, "start", 0);
long duration = parseDurationMs(xpp, "duration", mediaPresentationDuration); long duration = parseDurationMs(xpp, "duration", mediaPresentationDuration);
...@@ -214,7 +214,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler { ...@@ -214,7 +214,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler {
List<Segment.Timeline> segmentTimelineList) throws XmlPullParserException, IOException { List<Segment.Timeline> segmentTimelineList) throws XmlPullParserException, IOException {
Uri baseUrl = parentBaseUrl; Uri baseUrl = parentBaseUrl;
String id = xpp.getAttributeValue(null, "id"); String id = xpp.getAttributeValue(null, "id");
int bandwidth = parseInt(xpp, "bandwidth") / 8; int bandwidth = parseInt(xpp, "bandwidth");
int audioSamplingRate = parseInt(xpp, "audioSamplingRate"); int audioSamplingRate = parseInt(xpp, "audioSamplingRate");
int width = parseInt(xpp, "width"); int width = parseInt(xpp, "width");
int height = parseInt(xpp, "height"); int height = parseInt(xpp, "height");
......
...@@ -23,7 +23,7 @@ import java.util.List; ...@@ -23,7 +23,7 @@ import java.util.List;
*/ */
public final class Period { public final class Period {
public final int id; public final String id;
public final long start; public final long start;
...@@ -39,16 +39,16 @@ public final class Period { ...@@ -39,16 +39,16 @@ public final class Period {
public final long presentationTimeOffset; public final long presentationTimeOffset;
public Period(int id, long start, long duration, List<AdaptationSet> adaptationSets) { public Period(String id, long start, long duration, List<AdaptationSet> adaptationSets) {
this(id, start, duration, adaptationSets, null, 0, 0, 0); this(id, start, duration, adaptationSets, null, 0, 0, 0);
} }
public Period(int id, long start, long duration, List<AdaptationSet> adaptationSets, public Period(String id, long start, long duration, List<AdaptationSet> adaptationSets,
List<Segment.Timeline> segmentList, int segmentStartNumber, int segmentTimescale) { List<Segment.Timeline> segmentList, int segmentStartNumber, int segmentTimescale) {
this(id, start, duration, adaptationSets, segmentList, segmentStartNumber, segmentTimescale, 0); this(id, start, duration, adaptationSets, segmentList, segmentStartNumber, segmentTimescale, 0);
} }
public Period(int id, long start, long duration, List<AdaptationSet> adaptationSets, public Period(String id, long start, long duration, List<AdaptationSet> adaptationSets,
List<Segment.Timeline> segmentList, int segmentStartNumber, int segmentTimescale, List<Segment.Timeline> segmentList, int segmentStartNumber, int segmentTimescale,
long presentationTimeOffset) { long presentationTimeOffset) {
this.id = id; this.id = id;
......
...@@ -103,7 +103,7 @@ public class SmoothStreamingChunkSource implements ChunkSource { ...@@ -103,7 +103,7 @@ public class SmoothStreamingChunkSource implements ChunkSource {
TrackElement trackElement = streamElement.tracks[trackIndex]; TrackElement trackElement = streamElement.tracks[trackIndex];
formats[i] = new SmoothStreamingFormat(String.valueOf(trackIndex), trackElement.mimeType, formats[i] = new SmoothStreamingFormat(String.valueOf(trackIndex), trackElement.mimeType,
trackElement.maxWidth, trackElement.maxHeight, trackElement.numChannels, trackElement.maxWidth, trackElement.maxHeight, trackElement.numChannels,
trackElement.sampleRate, trackElement.bitrate / 8, trackIndex); trackElement.sampleRate, trackElement.bitrate, trackIndex);
maxWidth = Math.max(maxWidth, trackElement.maxWidth); maxWidth = Math.max(maxWidth, trackElement.maxWidth);
maxHeight = Math.max(maxHeight, trackElement.maxHeight); maxHeight = Math.max(maxHeight, trackElement.maxHeight);
...@@ -266,8 +266,8 @@ public class SmoothStreamingChunkSource implements ChunkSource { ...@@ -266,8 +266,8 @@ public class SmoothStreamingChunkSource implements ChunkSource {
public final int trackIndex; public final int trackIndex;
public SmoothStreamingFormat(String id, String mimeType, int width, int height, public SmoothStreamingFormat(String id, String mimeType, int width, int height,
int numChannels, int audioSamplingRate, int bandwidth, int trackIndex) { int numChannels, int audioSamplingRate, int bitrate, int trackIndex) {
super(id, mimeType, width, height, numChannels, audioSamplingRate, bandwidth); super(id, mimeType, width, height, numChannels, audioSamplingRate, bitrate);
this.trackIndex = trackIndex; this.trackIndex = trackIndex;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment