/*****************************************************
*
* 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 org.osmf.events.PlayEvent;
import org.osmf.media.MediaElement;
import org.osmf.traits.MediaTraitType;
import org.osmf.traits.PlayState;
import org.osmf.traits.PlayTrait;
/**
* @private
*
* Helper class for managing transitions from one child of
* a SerialElement to another.
*/
internal class SerialElementTransitionManager
{
/**
* Plays the next playable child that follows the current child,
* using the TraitAggregator as the source of available children.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public static function playNextPlayableChild
( traitAggregator:TraitAggregator
, noNextPlayableChildCallback:Function
):void
{
var currentChild:MediaElement = traitAggregator.listenedChild;
// Make a list of all children that follow the current
// child.
var nextChildren:Array = getNextChildren(traitAggregator, currentChild);
// Use a TraitLoader to find the next child that has the
// PlayTrait, loading along the way if necessary.
var traitLoader:TraitLoader = new TraitLoader();
traitLoader.addEventListener(TraitLoaderEvent.TRAIT_FOUND, onTraitFound);
traitLoader.findOrLoadMediaElementWithTrait(nextChildren, MediaTraitType.PLAY);
function onTraitFound(event:TraitLoaderEvent):void
{
traitLoader.removeEventListener(TraitLoaderEvent.TRAIT_FOUND, onTraitFound);
// If we do have a next playable child, then we play it.
// Otherwise we're done playing.
if (event.mediaElement)
{
var traitOfNextPlayableChild:PlayTrait = event.mediaElement.getTrait(MediaTraitType.PLAY) as PlayTrait;
// We want to set the new current child, and then initiate
// playback. However, it's possible that the act of setting
// the new current child will automatically trigger playback
// (for example if the child is placed in parallel to an
// already-playing element). In such a case, we obviously
// don't want to play it a second time.
//
// Unfortunately, it's not a simple matter of checking the
// PlayTrait.playState property, because it's possible that
// the play happened instantaneously (i.e. playState wouldn't
// be PLAYING). The most surefire way of detecting
// whether the play actually occurred is to listen for an
// event.
var playbackInitiated:Boolean = false;
traitOfNextPlayableChild.addEventListener(PlayEvent.PLAY_STATE_CHANGE, onPlayStateChange);
function onPlayStateChange(event:PlayEvent):void
{
if (event.playState == PlayState.PLAYING)
{
playbackInitiated = true;
}
}
// Set the current child, this is where the playback might
// get automatically triggered.
traitAggregator.listenedChild = event.mediaElement;
// No need to listen for the event anymore.
traitOfNextPlayableChild.removeEventListener(PlayEvent.PLAY_STATE_CHANGE, onPlayStateChange);
if (playbackInitiated == false)
{
traitOfNextPlayableChild.play();
}
else
{
// Just because playback was initiated doesn't mean
// we're done. It's possible that the playback
// occurred synchronously and already completed, in
// which case we should trigger playback of the next
// child (if it's not playing already). This should
// only happen if the child lacks the TimeTrait.
if (traitOfNextPlayableChild.playState == PlayState.STOPPED &&
traitAggregator.listenedChild.hasTrait(MediaTraitType.TIME) == false)
{
playNextPlayableChild
( traitAggregator
, noNextPlayableChildCallback
);
}
}
}
else
{
// There's no next playable child. However, we should still
// move to the next child (which isn't playable).
if (nextChildren.length > 0)
{
traitAggregator.listenedChild = nextChildren[0] as MediaElement;
}
if (noNextPlayableChildCallback != null)
{
noNextPlayableChildCallback();
}
}
}
}
/**
* Returns all children that follow the specified currentChild,
* using the specified TraitAggregator as the source of all
* children.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
private static function getNextChildren(traitAggregator:TraitAggregator, currentChild:MediaElement):Array
{
var nextChildren:Array = [];
var reachedCurrentChild:Boolean = false;
for (var i:int = 0; i < traitAggregator.numChildren; i++)
{
var child:MediaElement = traitAggregator.getChildAt(i);
if (currentChild == child)
{
reachedCurrentChild = true;
}
else if (reachedCurrentChild)
{
nextChildren.push(child);
}
}
return nextChildren;
}
}
}