/*****************************************************
*
* 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.elements.compositeClasses
{
import __AS3__.vec.Vector;
import org.osmf.events.DynamicStreamEvent;
import org.osmf.traits.DynamicStreamTrait;
import org.osmf.traits.MediaTraitBase;
import org.osmf.traits.MediaTraitType;
import org.osmf.utils.OSMFStrings;
/**
* ParallelDynamicStreamTrait is the composite trait for DynamicStreamTrait.
* If a child doesn't have the same bitrate as an another, the closest match will be chosen
* when switching between bitrates.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
internal class ParallelDynamicStreamTrait extends DynamicStreamTrait
{
/**
* Constructor.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public function ParallelDynamicStreamTrait(traitAggregator:TraitAggregator)
{
super();
this.traitAggregator = traitAggregator;
traitAggregationHelper = new TraitAggregationHelper
( traitType
, traitAggregator
, processAggregatedChild
, processUnaggregatedChild
);
}
/**
* @private
*/
override public function getBitrateForIndex(index:int):Number
{
if (index >= bitrates.length || index < 0)
{
throw new RangeError(OSMFStrings.getString(OSMFStrings.STREAMSWITCH_INVALID_INDEX));
}
return bitrates[index];
}
/**
* @private
*/
override protected function autoSwitchChangeStart(value:Boolean):void
{
if (autoSwitchIsChanging == false)
{
autoSwitchIsChanging = true;
traitAggregator.forEachChildTrait(
function(mediaTrait:DynamicStreamTrait):void
{
mediaTrait.autoSwitch = value;
}
, MediaTraitType.DYNAMIC_STREAM
);
autoSwitchIsChanging = false;
}
}
override protected function autoSwitchChangeEnd():void
{
if (autoSwitchIsChanging == false)
{
super.autoSwitchChangeEnd();
}
}
/**
* @private
*/
override protected function switchingChangeStart(newSwitching:Boolean, index:int):void
{
if (newSwitching)
{
traitAggregator.forEachChildTrait(
function(mediaTrait:DynamicStreamTrait):void
{
var desiredBitRate:Number = bitrates[index];
var childIndex:Number;
for (childIndex = 0; childIndex <= mediaTrait.maxAllowedIndex; childIndex++)
{
var childBitRate:Number = mediaTrait.getBitrateForIndex(childIndex);
if (childBitRate == desiredBitRate)
{
break;
}
else if (childBitRate > desiredBitRate)
{
childIndex--;
break;
}
}
// If we made it here, the last item is the correct stream
var targetIndex:int = Math.max(0, Math.min(childIndex, mediaTrait.maxAllowedIndex));
if (mediaTrait.currentIndex != targetIndex)
{
numChildrenSwitching++;
mediaTrait.switchTo(targetIndex);
}
}
, MediaTraitType.DYNAMIC_STREAM);
setCurrentIndex(index);
}
}
/**
* @private
*/
override protected function switchingChangeEnd(index:int):void
{
super.switchingChangeEnd(index);
// If a switch didn't trigger the switch of any children, or if all
// children finished their switches immediately, then we can
// terminate the switch now.
if (switching == true && numChildrenSwitching == 0)
{
setSwitching(false, index);
}
}
/**
* @private
*/
private function processAggregatedChild(child:MediaTraitBase):void
{
var aggregatedBR:int = 0;
var childTrait:DynamicStreamTrait = DynamicStreamTrait(child);
if (traitAggregator.getNumTraits(MediaTraitType.DYNAMIC_STREAM) == 1)
{
autoSwitch = childTrait.autoSwitch;
}
else
{
childTrait.autoSwitch = autoSwitch;
}
mergeChildRates(childTrait);
child.addEventListener(DynamicStreamEvent.NUM_DYNAMIC_STREAMS_CHANGE, recomputeIndices);
child.addEventListener(DynamicStreamEvent.SWITCHING_CHANGE, onSwitchingChange);
child.addEventListener(DynamicStreamEvent.AUTO_SWITCH_CHANGE, onAutoSwitchChange);
setNumDynamicStreams(bitrates.length);
maxAllowedIndex = bitrates.length - 1;
}
/**
* @private
*/
private function processUnaggregatedChild(child:MediaTraitBase):void
{
child.removeEventListener(DynamicStreamEvent.NUM_DYNAMIC_STREAMS_CHANGE, recomputeIndices);
child.removeEventListener(DynamicStreamEvent.SWITCHING_CHANGE, onSwitchingChange);
child.removeEventListener(DynamicStreamEvent.AUTO_SWITCH_CHANGE, onAutoSwitchChange);
recomputeIndices();
}
/**
* Rebuilds the child bitrates from the children's traits.
* Updates the max index based on the children's max indices.
*
* @returns True if there were changes to the bitrate table.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
private function rebuildBitRateTable():Boolean
{
// Rebuild bitrate table.
var oldBitrates:Vector.<Number> = bitrates;
bitrates = new Vector.<Number>;
traitAggregator.forEachChildTrait(
function(mediaTrait:DynamicStreamTrait):void
{
mergeChildRates(mediaTrait);
}
, MediaTraitType.DYNAMIC_STREAM
);
if (bitrates.length > 0)
{
setNumDynamicStreams(bitrates.length);
maxAllowedIndex = bitrates.length -1;
}
// Currently doesn't detect in place changes.
// since this is never called after an in place change, this check is good enough.
return oldBitrates.length != bitrates.length;
}
/**
* Add a new child to the bitrate table. Insertions are made in
* sorted order.
*
* @returns True if the indices changed.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
private function mergeChildRates(child:DynamicStreamTrait):Boolean
{
var aggregatedIndex:int = 0;
var indicesChanged:Boolean = false;
for (var childBR:int = 0; childBR <= child.maxAllowedIndex; ++childBR)
{
var rate:Number = child.getBitrateForIndex(childBR);
if (bitrates.length <= aggregatedIndex) // Add it to the end
{
indicesChanged = true;
bitrates.push(rate);
aggregatedIndex++;
}
else if (bitrates[aggregatedIndex] == rate)
{
continue; // No operation for rates that already are in the list.
}
else if (bitrates[aggregatedIndex] < rate)
{
aggregatedIndex++;
childBR--; // backup one, we need to keep going through the aggregatedBR's until we find a spot.
}
else // bitrate is smaller than the current bitrate.
{
indicesChanged = true;
bitrates.splice(aggregatedIndex, 0, rate);
}
}
return indicesChanged;
}
/**
* Rebuilds the bitrate table and switches to the appropriate bit rate.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
private function recomputeIndices(event:DynamicStreamEvent = null):void
{
var oldBitRate:Number = bitrates[currentIndex];
if (rebuildBitRateTable()) // Update current index, and dispatch event if indices changed.
{
if (!autoSwitch)
{
var highestBitRate:Number = 0;
traitAggregator.forEachChildTrait(
function(mediaTrait:DynamicStreamTrait):void
{
highestBitRate = Math.max(mediaTrait.getBitrateForIndex(mediaTrait.currentIndex), highestBitRate);
}
, MediaTraitType.DYNAMIC_STREAM);
var newBIndex:Number = 0;
while (highestBitRate != bitrates[newBIndex])
{
newBIndex++;
}
setCurrentIndex(newBIndex);
}
}
}
/**
* Handle the child trait changing. Collapse multiple events into a
* single event when switching multiple children simultaneously.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
private function onSwitchingChange(event:DynamicStreamEvent):void
{
if (event.switching == false && numChildrenSwitching > 0)
{
numChildrenSwitching--;
if (numChildrenSwitching == 0 && switching == true)
{
setSwitching(false, currentIndex);
}
}
}
private function onAutoSwitchChange(event:DynamicStreamEvent):void
{
// Propagate to the composite trait (and thus all children too).
autoSwitch = event.autoSwitch;
}
private var traitAggregator:TraitAggregator;
private var traitAggregationHelper:TraitAggregationHelper;
private var bitrates:Vector.<Number> = new Vector.<Number>;
private var autoSwitchIsChanging:Boolean = false;
private var numChildrenSwitching:int = 0;
}
}