Finish painful bytes/sec -> bits/sec conversion.

This commit is contained in:
Oliver Woodman 2014-08-01 15:51:21 +01:00
parent 0f57a5f1cf
commit ad26085e5c
5 changed files with 38 additions and 49 deletions

View File

@ -80,9 +80,9 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener
// DemoPlayer.InfoListener // DemoPlayer.InfoListener
@Override @Override
public void onBandwidthSample(int elapsedMs, long bytes, long bandwidthEstimate) { public void onBandwidthSample(int elapsedMs, long bytes, long bitrateEstimate) {
Log.d(TAG, "bandwidth [" + getSessionTimeString() + ", " + bytes + Log.d(TAG, "bandwidth [" + getSessionTimeString() + ", " + bytes +
", " + getTimeString(elapsedMs) + ", " + bandwidthEstimate + "]"); ", " + getTimeString(elapsedMs) + ", " + bitrateEstimate + "]");
} }
@Override @Override

View File

@ -121,7 +121,7 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi
void onVideoFormatEnabled(String formatId, int trigger, int mediaTimeMs); void onVideoFormatEnabled(String formatId, int trigger, int mediaTimeMs);
void onAudioFormatEnabled(String formatId, int trigger, int mediaTimeMs); void onAudioFormatEnabled(String formatId, int trigger, int mediaTimeMs);
void onDroppedFrames(int count, long elapsed); void onDroppedFrames(int count, long elapsed);
void onBandwidthSample(int elapsedMs, long bytes, long bandwidthEstimate); void onBandwidthSample(int elapsedMs, long bytes, long bitrateEstimate);
void onLoadStarted(int sourceId, String formatId, int trigger, boolean isInitialization, void onLoadStarted(int sourceId, String formatId, int trigger, boolean isInitialization,
int mediaStartTimeMs, int mediaEndTimeMs, long totalBytes); int mediaStartTimeMs, int mediaEndTimeMs, long totalBytes);
void onLoadCompleted(int sourceId); void onLoadCompleted(int sourceId);
@ -391,9 +391,9 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi
} }
@Override @Override
public void onBandwidthSample(int elapsedMs, long bytes, long bandwidthEstimate) { public void onBandwidthSample(int elapsedMs, long bytes, long bitrateEstimate) {
if (infoListener != null) { if (infoListener != null) {
infoListener.onBandwidthSample(elapsedMs, bytes, bandwidthEstimate); infoListener.onBandwidthSample(elapsedMs, bytes, bitrateEstimate);
} }
} }

View File

@ -164,7 +164,7 @@ public interface FormatEvaluator {
*/ */
public static class AdaptiveEvaluator implements FormatEvaluator { public static class AdaptiveEvaluator implements FormatEvaluator {
public static final int DEFAULT_MAX_INITIAL_BYTE_RATE = 100000; public static final int DEFAULT_MAX_INITIAL_BITRATE = 800000;
public static final int DEFAULT_MIN_DURATION_FOR_QUALITY_INCREASE_MS = 10000; public static final int DEFAULT_MIN_DURATION_FOR_QUALITY_INCREASE_MS = 10000;
public static final int DEFAULT_MAX_DURATION_FOR_QUALITY_DECREASE_MS = 25000; public static final int DEFAULT_MAX_DURATION_FOR_QUALITY_DECREASE_MS = 25000;
@ -173,7 +173,7 @@ public interface FormatEvaluator {
private final BandwidthMeter bandwidthMeter; private final BandwidthMeter bandwidthMeter;
private final int maxInitialByteRate; private final int maxInitialBitrate;
private final long minDurationForQualityIncreaseUs; private final long minDurationForQualityIncreaseUs;
private final long maxDurationForQualityDecreaseUs; private final long maxDurationForQualityDecreaseUs;
private final long minDurationToRetainAfterDiscardUs; private final long minDurationToRetainAfterDiscardUs;
@ -183,7 +183,7 @@ public interface FormatEvaluator {
* @param bandwidthMeter Provides an estimate of the currently available bandwidth. * @param bandwidthMeter Provides an estimate of the currently available bandwidth.
*/ */
public AdaptiveEvaluator(BandwidthMeter bandwidthMeter) { public AdaptiveEvaluator(BandwidthMeter bandwidthMeter) {
this (bandwidthMeter, DEFAULT_MAX_INITIAL_BYTE_RATE, this (bandwidthMeter, DEFAULT_MAX_INITIAL_BITRATE,
DEFAULT_MIN_DURATION_FOR_QUALITY_INCREASE_MS, DEFAULT_MIN_DURATION_FOR_QUALITY_INCREASE_MS,
DEFAULT_MAX_DURATION_FOR_QUALITY_DECREASE_MS, DEFAULT_MAX_DURATION_FOR_QUALITY_DECREASE_MS,
DEFAULT_MIN_DURATION_TO_RETAIN_AFTER_DISCARD_MS, DEFAULT_BANDWIDTH_FRACTION); DEFAULT_MIN_DURATION_TO_RETAIN_AFTER_DISCARD_MS, DEFAULT_BANDWIDTH_FRACTION);
@ -191,7 +191,7 @@ public interface FormatEvaluator {
/** /**
* @param bandwidthMeter Provides an estimate of the currently available bandwidth. * @param bandwidthMeter Provides an estimate of the currently available bandwidth.
* @param maxInitialByteRate The maximum bandwidth in bytes per second that should be assumed * @param maxInitialBitrate The maximum bitrate in bits per second that should be assumed
* when bandwidthMeter cannot provide an estimate due to playback having only just started. * when bandwidthMeter cannot provide an estimate due to playback having only just started.
* @param minDurationForQualityIncreaseMs The minimum duration of buffered data required for * @param minDurationForQualityIncreaseMs The minimum duration of buffered data required for
* the evaluator to consider switching to a higher quality format. * the evaluator to consider switching to a higher quality format.
@ -206,13 +206,13 @@ public interface FormatEvaluator {
* for inaccuracies in the bandwidth estimator. * for inaccuracies in the bandwidth estimator.
*/ */
public AdaptiveEvaluator(BandwidthMeter bandwidthMeter, public AdaptiveEvaluator(BandwidthMeter bandwidthMeter,
int maxInitialByteRate, int maxInitialBitrate,
int minDurationForQualityIncreaseMs, int minDurationForQualityIncreaseMs,
int maxDurationForQualityDecreaseMs, int maxDurationForQualityDecreaseMs,
int minDurationToRetainAfterDiscardMs, int minDurationToRetainAfterDiscardMs,
float bandwidthFraction) { float bandwidthFraction) {
this.bandwidthMeter = bandwidthMeter; this.bandwidthMeter = bandwidthMeter;
this.maxInitialByteRate = maxInitialByteRate; this.maxInitialBitrate = maxInitialBitrate;
this.minDurationForQualityIncreaseUs = minDurationForQualityIncreaseMs * 1000L; this.minDurationForQualityIncreaseUs = minDurationForQualityIncreaseMs * 1000L;
this.maxDurationForQualityDecreaseUs = maxDurationForQualityDecreaseMs * 1000L; this.maxDurationForQualityDecreaseUs = maxDurationForQualityDecreaseMs * 1000L;
this.minDurationToRetainAfterDiscardUs = minDurationToRetainAfterDiscardMs * 1000L; this.minDurationToRetainAfterDiscardUs = minDurationToRetainAfterDiscardMs * 1000L;
@ -235,7 +235,7 @@ public interface FormatEvaluator {
long bufferedDurationUs = queue.isEmpty() ? 0 long bufferedDurationUs = queue.isEmpty() ? 0
: 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.getBitrateEstimate());
boolean isHigher = ideal != null && current != null && ideal.bitrate > current.bitrate; boolean isHigher = ideal != null && current != null && ideal.bitrate > current.bitrate;
boolean isLower = ideal != null && current != null && ideal.bitrate < current.bitrate; boolean isLower = ideal != null && current != null && ideal.bitrate < current.bitrate;
if (isHigher) { if (isHigher) {
@ -276,11 +276,11 @@ public interface FormatEvaluator {
/** /**
* Compute the ideal format ignoring buffer health. * Compute the ideal format ignoring buffer health.
*/ */
protected Format determineIdealFormat(Format[] formats, long bandwidthEstimate) { protected Format determineIdealFormat(Format[] formats, long bitrateEstimate) {
long effectiveBandwidth = computeEffectiveBandwidthEstimate(bandwidthEstimate); long effectiveBitrate = computeEffectiveBitrateEstimate(bitrateEstimate);
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.bitrate / 8) <= effectiveBandwidth) { if (format.bitrate <= effectiveBitrate) {
return format; return format;
} }
} }
@ -291,9 +291,9 @@ public interface FormatEvaluator {
/** /**
* Apply overhead factor, or default value in absence of estimate. * Apply overhead factor, or default value in absence of estimate.
*/ */
protected long computeEffectiveBandwidthEstimate(long bandwidthEstimate) { protected long computeEffectiveBitrateEstimate(long bitrateEstimate) {
return bandwidthEstimate == BandwidthMeter.NO_ESTIMATE return bitrateEstimate == BandwidthMeter.NO_ESTIMATE
? maxInitialByteRate : (long) (bandwidthEstimate * bandwidthFraction); ? maxInitialBitrate : (long) (bitrateEstimate * bandwidthFraction);
} }
} }

View File

@ -26,10 +26,10 @@ public interface BandwidthMeter {
final long NO_ESTIMATE = -1; final long NO_ESTIMATE = -1;
/** /**
* Gets the estimated bandwidth. * Gets the estimated bandwidth, in bits/sec.
* *
* @return Estimated bandwidth in bytes/sec, or {@link #NO_ESTIMATE} if no estimate is available. * @return Estimated bandwidth in bits/sec, or {@link #NO_ESTIMATE} if no estimate is available.
*/ */
long getEstimate(); long getBitrateEstimate();
} }

View File

@ -38,11 +38,11 @@ public class DefaultBandwidthMeter implements BandwidthMeter, TransferListener {
* *
* @param elapsedMs The time taken to transfer the bytes, in milliseconds. * @param elapsedMs The time taken to transfer the bytes, in milliseconds.
* @param bytes The number of bytes transferred. * @param bytes The number of bytes transferred.
* @param bandwidthEstimate The estimated bandwidth in bytes/sec, or {@link #NO_ESTIMATE} if no * @param bitrate The estimated bitrate in bits/sec, or {@link #NO_ESTIMATE} if no estimate
* estimate is available. Note that this estimate is typically derived from more information * is available. Note that this estimate is typically derived from more information than
* than {@code bytes} and {@code elapsedMs}. * {@code bytes} and {@code elapsedMs}.
*/ */
void onBandwidthSample(int elapsedMs, long bytes, long bandwidthEstimate); void onBandwidthSample(int elapsedMs, long bytes, long bitrate);
} }
@ -53,9 +53,9 @@ public class DefaultBandwidthMeter implements BandwidthMeter, TransferListener {
private final Clock clock; private final Clock clock;
private final SlidingPercentile slidingPercentile; private final SlidingPercentile slidingPercentile;
private long accumulator; private long bytesAccumulator;
private long startTimeMs; private long startTimeMs;
private long bandwidthEstimate; private long bitrateEstimate;
private int streamCount; private int streamCount;
public DefaultBandwidthMeter() { public DefaultBandwidthMeter() {
@ -80,17 +80,12 @@ public class DefaultBandwidthMeter implements BandwidthMeter, TransferListener {
this.eventListener = eventListener; this.eventListener = eventListener;
this.clock = clock; this.clock = clock;
this.slidingPercentile = new SlidingPercentile(maxWeight); this.slidingPercentile = new SlidingPercentile(maxWeight);
bandwidthEstimate = NO_ESTIMATE; bitrateEstimate = NO_ESTIMATE;
} }
/**
* Gets the estimated bandwidth.
*
* @return Estimated bandwidth in bytes/sec, or {@link #NO_ESTIMATE} if no estimate is available.
*/
@Override @Override
public synchronized long getEstimate() { public synchronized long getBitrateEstimate() {
return bandwidthEstimate; return bitrateEstimate;
} }
@Override @Override
@ -103,7 +98,7 @@ public class DefaultBandwidthMeter implements BandwidthMeter, TransferListener {
@Override @Override
public synchronized void onBytesTransferred(int bytes) { public synchronized void onBytesTransferred(int bytes) {
accumulator += bytes; bytesAccumulator += bytes;
} }
@Override @Override
@ -112,32 +107,26 @@ public class DefaultBandwidthMeter implements BandwidthMeter, TransferListener {
long nowMs = clock.elapsedRealtime(); long nowMs = clock.elapsedRealtime();
int elapsedMs = (int) (nowMs - startTimeMs); int elapsedMs = (int) (nowMs - startTimeMs);
if (elapsedMs > 0) { if (elapsedMs > 0) {
float bytesPerSecond = accumulator * 1000 / elapsedMs; float bitsPerSecond = (bytesAccumulator * 8000) / elapsedMs;
slidingPercentile.addSample(computeWeight(accumulator), bytesPerSecond); slidingPercentile.addSample((int) Math.sqrt(bytesAccumulator), bitsPerSecond);
float bandwidthEstimateFloat = slidingPercentile.getPercentile(0.5f); float bandwidthEstimateFloat = slidingPercentile.getPercentile(0.5f);
bandwidthEstimate = Float.isNaN(bandwidthEstimateFloat) ? NO_ESTIMATE bitrateEstimate = Float.isNaN(bandwidthEstimateFloat) ? NO_ESTIMATE
: (long) bandwidthEstimateFloat; : (long) bandwidthEstimateFloat;
notifyBandwidthSample(elapsedMs, accumulator, bandwidthEstimate); notifyBandwidthSample(elapsedMs, bytesAccumulator, bitrateEstimate);
} }
streamCount--; streamCount--;
if (streamCount > 0) { if (streamCount > 0) {
startTimeMs = nowMs; startTimeMs = nowMs;
} }
accumulator = 0; bytesAccumulator = 0;
} }
// TODO: Use media time (bytes / mediaRate) as weight. private void notifyBandwidthSample(final int elapsedMs, final long bytes, final long bitrate) {
private int computeWeight(long mediaBytes) {
return (int) Math.sqrt(mediaBytes);
}
private void notifyBandwidthSample(final int elapsedMs, final long bytes,
final long bandwidthEstimate) {
if (eventHandler != null && eventListener != null) { if (eventHandler != null && eventListener != null) {
eventHandler.post(new Runnable() { eventHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
eventListener.onBandwidthSample(elapsedMs, bytes, bandwidthEstimate); eventListener.onBandwidthSample(elapsedMs, bytes, bitrate);
} }
}); });
} }