/*****************************************************
*
* 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
{
import flash.utils.Dictionary;
import org.osmf.elements.compositeClasses.CompositeMetadata;
import org.osmf.elements.compositeClasses.CompositionMode;
import org.osmf.elements.compositeClasses.IReusable;
import org.osmf.elements.compositeClasses.TraitAggregatorEvent;
import org.osmf.events.SerialElementEvent;
import org.osmf.media.MediaElement;
import org.osmf.media.MediaResourceBase;
import org.osmf.metadata.Metadata;
import org.osmf.traits.MediaTraitBase;
/**
* SerialElement is a media composition whose elements are presented
* serially (i.e. in sequence).
*
* <p>The media elements that make up a SerialElement are treated as a
* single, unified media element. For example, if a SerialElement
* encapsulates a sequence of videos, the SerialElement will behave as
* if it's a single VideoElement, but one which plays several videos in
* sequence.</p>
*
* <p>Typically, a trait on a SerialElement is a reflection of the "current"
* child of the composition. A SerialElement plays through its children
* in serial order. As the current child completes its execution, the next
* child in the sequence becomes the "current" child. To a client of the
* class, the changes from one current child to the next are hidden. They
* are only noticeable through changes to the traits of this class.</p>
*
* <p>A childless SerialElement has no notion of a "current" child, so
* it reflects no traits. The first child that
* is added to a SerialElement immediately becomes the current child
* of the composition. If the current child is removed, the next
* child in the sequence becomes the new current
* child, if there is a next child. If there is no next child,
* the first child in the sequence becomes the current child.</p>
* <p>The only way that the "current" status can pass from one
* child to another is when the state of one of the current child's
* traits changes in such a way that the
* SerialElement knows that it needs to change its current child. For
* example, if each child in the sequence has the PlayTrait,
* the "current" status advances from one child to the next when a
* child finishes playing and its PlayTrait's <code>PlayState</code>
* property changes from <code>PLAYING</code> to <code>STOPPED</code>.
* Another example: if the client of a SerialElement with a SeekTrait
* seeks from one point to another, the "current"
* status is likely to change from one child to another.</p>
*
* <p>Here is how each trait is expressed when in serial:</p>
* <ul>
* <li>
* AudioTrait - The composite trait keeps the audible properties of all
* children in sync. When the volume of a child element (or the composite
* element) is changed, the volume is similarly changed for all audible
* children (and for the composite trait).
* </li>
* <li>
* BufferTrait - The composite trait represents the bufferable trait of
* the current child in the sequence. Any changes apply only to the
* current child.
* </li>
* <li>
* DisplayObjectTrait - The composite trait represents the DisplayObjectTrait of the
* current child in the sequence.
* </li>
* <li>
* DRMTrait - The composite trait represents the DRMTrait of the
* current child in the sequence.
* </li>
* <li>
* DVRTrait - The composite trait represents the DVRTrait of the
* current child in the sequence.
* </li>
* <li>
* DynamicStreamTrait - The composite trait represents the DynamicStreamTrait of the
* current child in the sequence. Any changes apply only to the current
* child.
* </li>
* <li>
* LoadTrait - The composite trait represents the LoadTrait of the
* current child in the sequence. Any changes apply only to the current
* child.
* </li>
* <li>
* PlayTrait - The composite trait represents the PlayTrait of the
* current child in the sequence. Any changes apply only to the current
* child.
* </li>
* <li>
* SeekTrait - The composite trait represents the SeekTrait of the
* current child in the sequence. A seek operation can change the current
* child.
* </li>
* <li>
* TimeTrait - The composite trait represents a timeline that encapsulates
* the timeline of all children. Its duration is the sum of the durations
* of all children. Its position is the sum of the positions of the first
* N fully complete children, plus the position of the next child.
* </li>
* </ul>
*
* @includeExample SerialElementExample.as -noswf
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public class SerialElement extends CompositeElement
{
/**
* Constructor.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public function SerialElement()
{
super();
traitAggregator.addEventListener
( TraitAggregatorEvent.LISTENED_CHILD_CHANGE
, onListenedChildChanged
);
reusableTraits = new Dictionary();
}
/**
* The currently active child of this SerialElement.
**/
public function get currentChild():MediaElement
{
return traitAggregator.listenedChild;
}
/**
* @private
**/
override public function get resource():MediaResourceBase
{
return traitAggregator.listenedChild != null
? traitAggregator.listenedChild.resource
: null;
}
// Protected
//
/**
* @private
*
* Deprecated method.
*
* Method invoked after the currentChild property has changed. The base implementation
* does nothing.
*
* Clients can override this to do custom processing when the current child changes.
**/
protected function currentChildChange():void
{
}
// Overrides
//
/**
* @private
*/
override protected function createMetadata():Metadata
{
var result:Metadata = super.createMetadata();
CompositeMetadata(result).mode = CompositionMode.SERIAL;
return result;
}
/**
* @private
*/
override protected function processAddedChild(child:MediaElement, index:int):void
{
super.processAddedChild(child, index);
// The first added child of a SerialElement becomes the "current"
// child (i.e. the child from which all traits of the composite
// element come).
if (traitAggregator.listenedChild == null)
{
traitAggregator.listenedChild = child;
}
updateListenedChildIndex();
}
/**
* @private
*/
override protected function processRemovedChild(child:MediaElement):void
{
super.processRemovedChild(child);
// If we remove the current child, then we should set a new
// current child (if one is available).
if (traitAggregator.listenedChild == child)
{
// Our first choice for the new current child is the next
// child.
var newListenedChild:MediaElement = getChildAt(listenedChildIndex);
// If there is no next child, then we pick the first child.
if (newListenedChild == null)
{
listenedChildIndex = (numChildren > 0) ? 0 : -1;
newListenedChild = getChildAt(listenedChildIndex);
}
traitAggregator.listenedChild = newListenedChild;
}
}
/**
* @private
*/
override protected function processAggregatedTrait(traitType:String, trait:MediaTraitBase):void
{
super.processAggregatedTrait(traitType, trait);
var compositeTrait:MediaTraitBase = getTrait(traitType);
// Create the composite trait if the aggregated trait is for the
// current child, the reason being that aggregating a new trait on
// a non-current child shouldn't cause a new trait to be reflected.
if ( compositeTrait == null
&& traitAggregator.listenedChild != null
&& traitAggregator.listenedChild.getTrait(traitType) == trait
)
{
compositeTrait = reusableTraits[traitType] as MediaTraitBase;
if (compositeTrait == null)
{
compositeTrait = traitFactory.createTrait
( traitType
, traitAggregator
, CompositionMode.SERIAL
, this
);
}
else
{
(compositeTrait as IReusable).attach();
reusableTraits[traitType] = null;
}
if (compositeTrait != null)
{
addTrait(traitType, compositeTrait);
}
}
}
/**
* @private
*/
override protected function processUnaggregatedTrait(traitType:String, trait:MediaTraitBase):void
{
super.processUnaggregatedTrait(traitType, trait);
// Remove the composite trait if the unaggregated trait comes from
// the current child, the reason being that the composition should
// not reflect a trait that doesn't exist on the current child.
if ( traitAggregator.listenedChild != null
&& traitAggregator.listenedChild.getTrait(traitType) == trait
)
{
var trait:MediaTraitBase = removeTrait(traitType);
if (trait != null && trait is IReusable)
{
(trait as IReusable).detach();
reusableTraits[traitType] = trait;
}
}
}
// Internals
//
private function onListenedChildChanged(event:TraitAggregatorEvent):void
{
compositeMetadata.activeChild
= event.newListenedChild
? event.newListenedChild.metadata
: null;
// Update the index of the current child.
updateListenedChildIndex();
// Inform any interested subclasses of the change.
currentChildChange();
// Dispatch the change event.
dispatchEvent(new SerialElementEvent(SerialElementEvent.CURRENT_CHILD_CHANGE, false, false, currentChild));
}
private function updateListenedChildIndex():void
{
listenedChildIndex = traitAggregator.getChildIndex(traitAggregator.listenedChild);
}
private var listenedChildIndex:int = -1;
private var reusableTraits:Dictionary;
}
}