/*****************************************************
*
* Copyright 2009 Akamai Technologies, Inc. All Rights Reserved.
*
*****************************************************
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
*
*
* The Initial Developer of the Original Code is Adobe Systems Incorporated.
* Portions created by Adobe Systems Incorporated are Copyright (C) 2009 Adobe Systems
* Incorporated. All Rights Reserved.
*
*****************************************************/
package org.osmf.net.httpstreaming
{
import org.osmf.net.SwitchingRuleBase;
[ExcludeClass]
/**
* @private
*
* Switching rule that makes decisions based on the download ratio.
**/
public class DownloadRatioRule extends SwitchingRuleBase
{
/**
* Constructor
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public function DownloadRatioRule(metrics:HTTPNetStreamMetrics, aggressiveUpswitch:Boolean=true)
{
super(metrics);
this.aggressiveUpswitch = aggressiveUpswitch;
}
/**
* The new bitrate index to which this rule recommends switching. If the rule has no change request it will
* return a value of -1.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
override public function getNewIndex():int
{
// XXX IMPORTANT NOTE: WE AREN'T YET SETTING BUFFER TIME UPWARDS IN
// NON-SEEK SITUATIONS. NEED TO DO THIS SO RUNNING DRY IS LESS PAINFUL.
// We work in ratios so that we can cancel kbps out of all the equations.
//
// The downloadRatio is
// "playback time of last segment downloaded" /
// "amount of time it took to download that whole segment, from
// request to finished"
// The switchRatio[proposed] is
// "claimed rate of proposed quality" /
// "claimed rate of current quality"
//
// There are exactly four cases we need to deal with:
// 1. The downloadRatio is < 1 and < switchRatio[current-1]:
// Bandwidth is way down, switch to lowest rate immediately
// (even if there's an intermediate that might work).
// 2. The downloadRatio is < 1 but >= switchRatio[current-1]:
// We should be able to keep going if we go down one level.
// 3. The lastDownloadRatio is >= 1 but < switchRatio[current+1]
// OR no available rate is higher than current:
// Steady state where we like to be. Don't touch any knobs.
// 4. The lastDownloadRatio is >= 1 and > switchRatio[current+1]:
// We can go up to rate N where N is the highest N for which
// downloadRatio is still > switchRatio[N] (but see caution
// about high downloadRatio caused by cached response).
//
// XXX Note: We don't currently do this, but we can hold off loading
// for a bit if and only if we are in state 3 AND the downloadRatio
// is significantly >= 1 (in addition to holding off loading if
// bufferLength is growing too far).
//
// Note: There is a danger that downloadRatio is absurdly high
// because it is reflecting cached data. If that is detected, then
// in case 4 the switch up should only be a single quality level
// upwards rather than switching to the top rate instantly, just in
// case even one level up is actually too high a rate in reality.
//
var proposedIndex:int = -1;
var switchRatio:Number;
if (httpMetrics.downloadRatio < 1.0)
{
// Cases #1 and #2
// First check to see if we are even able to switch down.
if (httpMetrics.currentIndex > 0)
{
switchRatio = getSwitchRatio(httpMetrics.currentIndex - 1);
if (httpMetrics.downloadRatio < switchRatio)
{
// Case #1, switch to the lowest index.
proposedIndex = 0;
}
else
{
// Case #2, down by one.
proposedIndex = httpMetrics.currentIndex - 1;
}
}
}
else
{
// Cases #3 and #4
// First check to see if we are able to switch up.
if (httpMetrics.currentIndex < httpMetrics.maxAllowedIndex)
{
switchRatio = getSwitchRatio(httpMetrics.currentIndex + 1);
if (httpMetrics.downloadRatio < switchRatio)
{
// Case #3, don't touch anything, we're where we want to be.
}
else
{
// Is the last download ratio suspiciously high (cached data),
// or has aggressive upswitch been turned off?
// XXX 100.0 s/b constant value
if (httpMetrics.downloadRatio > 100.0 || !aggressiveUpswitch)
{
// Switch up one.
proposedIndex = httpMetrics.currentIndex + 1
}
else
{
// Find the most appropriate stream to upswitch to.
while (++proposedIndex < httpMetrics.maxAllowedIndex + 1)
{
switchRatio = getSwitchRatio(proposedIndex)
if (httpMetrics.downloadRatio < switchRatio)
{
// Found one that's too high.
break;
}
}
--proposedIndex;
}
}
}
}
return proposedIndex;
}
private function getSwitchRatio(index:int):Number
{
return httpMetrics.getBitrateForIndex(index) / httpMetrics.getBitrateForIndex(metrics.currentIndex);
}
private function get httpMetrics():HTTPNetStreamMetrics
{
return metrics as HTTPNetStreamMetrics;
}
private var aggressiveUpswitch:Boolean = false;
}
}