/*****************************************************
*
* Copyright 2009 Adobe Systems Incorporated. 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.f4f
{
import __AS3__.vec.Vector;
CONFIG::LOGGING
{
import org.osmf.logging.Logger;
}
[ExcludeClass]
/**
* @private
*
* Fragment run table. Each entry in the table is the first fragment of a sequence of
* fragments that have the same duration.
*/
internal class AdobeFragmentRunTable extends FullBox
{
/**
* Constructor
*
* @param bi The box info that contains the size and type of the box
* @param parser The box parser to be used to assist constructing the box
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public function AdobeFragmentRunTable()
{
super();
_fragmentDurationPairs = new Vector.<FragmentDurationPair>();
}
/**
* The time scale for this run table.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public function get timeScale():uint
{
return _timeScale;
}
public function set timeScale(value:uint):void
{
_timeScale = value;
}
/**
* The quality segment URL modifiers.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public function get qualitySegmentURLModifiers():Vector.<String>
{
return _qualitySegmentURLModifiers;
}
public function set qualitySegmentURLModifiers(value:Vector.<String>):void
{
_qualitySegmentURLModifiers = value;
}
/**
* A list of <first fragment, duration> pairs.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public function get fragmentDurationPairs():Vector.<FragmentDurationPair>
{
return _fragmentDurationPairs;
}
/**
* Append a fragment duration pair to the list. The accrued duration for the newly appended
* fragment duration needed to be calculated. This is basically the total duration till the
* time spot that the newly appended fragment duration pair represents.
*
* @param fdp The <first fragment, duration> pair to be appended to the list.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public function addFragmentDurationPair(fdp:FragmentDurationPair):void
{
_fragmentDurationPairs.push(fdp);
}
/**
* Given a time spot in terms of the time scale used by the fragment table, returns the corresponding
* Id of the fragment that contains the time spot.
*
* @return the Id of the fragment that contains the time spot.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public function findFragmentIdByTime(time:Number, totalDuration:Number, live:Boolean=false):FragmentAccessInformation
{
if (_fragmentDurationPairs.length <= 0)
{
return null;
}
var fdp:FragmentDurationPair = null;
for (var i:uint = 1; i < _fragmentDurationPairs.length; i++)
{
fdp = _fragmentDurationPairs[i];
if (fdp.durationAccrued >= time)
{
return validateFragment(calculateFragmentId(_fragmentDurationPairs[i - 1], time), totalDuration, live);
}
}
return validateFragment(calculateFragmentId(_fragmentDurationPairs[_fragmentDurationPairs.length - 1], time), totalDuration, live);
}
/**
* Given a fragment id, check whether the current fragment is valid or a discontinuity.
* If the latter, skip to the nearest fragment and return the new fragment id.
*
* @return the Id of the fragment that is valid.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public function validateFragment(fragId:uint, totalDuration:Number, live:Boolean=false):FragmentAccessInformation
{
var size:uint = _fragmentDurationPairs.length - 1;
var fai:FragmentAccessInformation = null;
for (var i:uint = 0; i < size; i++)
{
var curFdp:FragmentDurationPair = _fragmentDurationPairs[i];
var nextFdp:FragmentDurationPair = _fragmentDurationPairs[i+1];
if ((curFdp.firstFragment <= fragId) && (fragId < nextFdp.firstFragment))
{
if (curFdp.duration <= 0)
{
fai = getNextValidFragment(i+1, totalDuration);
}
else
{
fai = new FragmentAccessInformation();
fai.fragId = fragId;
fai.fragDuration = curFdp.duration;
fai.fragmentEndTime = curFdp.durationAccrued + curFdp.duration * (fragId - curFdp.firstFragment + 1);
}
break;
}
else if ((curFdp.firstFragment <= fragId) && endOfStreamEntry(nextFdp))
{
if (curFdp.duration > 0)
{
var timeResidue:Number = totalDuration - curFdp.durationAccrued;
var timeDistance:Number = (fragId - curFdp.firstFragment + 1) * curFdp.duration;
var fragStartTime:Number = (fragId - curFdp.firstFragment) * curFdp.duration;
if (timeResidue > fragStartTime)
{
if (!live || ((fragStartTime + curFdp.duration + curFdp.durationAccrued) <= totalDuration))
{
fai = new FragmentAccessInformation();
fai.fragId = fragId;
fai.fragDuration = curFdp.duration;
if (timeResidue >= timeDistance)
{
fai.fragmentEndTime = curFdp.durationAccrued + timeDistance;
}
else
{
fai.fragmentEndTime = curFdp.durationAccrued + timeResidue;
}
break;
}
}
}
}
}
if (fai == null)
{
var lastFdp:FragmentDurationPair = _fragmentDurationPairs[size];
if (lastFdp.duration > 0 && fragId >= lastFdp.firstFragment)
{
timeResidue = totalDuration - lastFdp.durationAccrued;
timeDistance = (fragId - lastFdp.firstFragment + 1) * lastFdp.duration;
fragStartTime = (fragId - lastFdp.firstFragment) * lastFdp.duration;
if (timeResidue > fragStartTime)
{
if (!live || ((fragStartTime + lastFdp.duration + lastFdp.durationAccrued) <= totalDuration))
{
fai = new FragmentAccessInformation();
fai.fragId = fragId;
fai.fragDuration = lastFdp.duration;
if (timeResidue >= timeDistance)
{
fai.fragmentEndTime = lastFdp.durationAccrued + timeDistance;
}
else
{
fai.fragmentEndTime = lastFdp.durationAccrued + timeResidue;
}
}
}
}
}
return fai;
}
private function getNextValidFragment(startIdx:uint, totalDuration:Number):FragmentAccessInformation
{
var fai:FragmentAccessInformation = null;
for (var i:uint = startIdx; i < _fragmentDurationPairs.length; i++)
{
var fdp:FragmentDurationPair = _fragmentDurationPairs[i];
if (fdp.duration > 0)
{
fai = new FragmentAccessInformation();
fai.fragId = fdp.firstFragment;
fai.fragDuration = fdp.duration;
fai.fragmentEndTime = fdp.durationAccrued + fdp.duration;
break;
}
}
return fai;
}
private function endOfStreamEntry(fdp:FragmentDurationPair):Boolean
{
return (fdp.duration == 0 && fdp.discontinuityIndicator == 0);
}
/**
* Given a fragment id, return the number of fragments after the
* fragment with the id given.
*
* @return the number of fragments after the fragment with the id given.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public function fragmentsLeft(fragId:uint, currentMediaTime:Number):uint
{
if (_fragmentDurationPairs == null || _fragmentDurationPairs.length == 0)
{
return 0;
}
var fdp:FragmentDurationPair = _fragmentDurationPairs[fragmentDurationPairs.length - 1] as FragmentDurationPair;
var fragments:uint = (currentMediaTime - fdp.durationAccrued) / fdp.duration + fdp.firstFragment - fragId -1;
return fragments;
}
/**
* @return whether the fragment table is complete.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public function tableComplete():Boolean
{
if (_fragmentDurationPairs == null || _fragmentDurationPairs.length <= 0)
{
return false;
}
var fdp:FragmentDurationPair = _fragmentDurationPairs[fragmentDurationPairs.length - 1] as FragmentDurationPair;
return (fdp.duration == 0 && fdp.discontinuityIndicator == 0);
}
public function adjustEndEntryDurationAccrued(value:Number):void
{
var fdp:FragmentDurationPair = _fragmentDurationPairs[_fragmentDurationPairs.length - 1];
if (fdp.duration == 0)
{
fdp.durationAccrued = value;
}
}
// Internal
//
private function findValidFragmentDurationPair(index:uint):FragmentDurationPair
{
for (var i:uint = index; index < _fragmentDurationPairs.length; i++)
{
var fdp:FragmentDurationPair = _fragmentDurationPairs[i];
if (fdp.duration > 0)
{
return fdp;
}
}
return null;
}
private function calculateFragmentId(fdp:FragmentDurationPair, time:Number):uint
{
if (fdp.duration <= 0)
{
return fdp.firstFragment;
}
var deltaTime:Number = time - fdp.durationAccrued;
var count:uint = (deltaTime > 0)? deltaTime / fdp.duration : 1;
if ((deltaTime % fdp.duration) > 0)
{
count++;
}
return fdp.firstFragment + count - 1;
}
private var _timeScale:uint;
private var _qualitySegmentURLModifiers:Vector.<String>;
private var _fragmentDurationPairs:Vector.<FragmentDurationPair>;
CONFIG::LOGGING
{
private static const logger:org.osmf.logging.Logger = org.osmf.logging.Log.getLogger("org.osmf.net.httpstreaming.f4f.AdobeFragmentRunTable");
}
}
}