blob: eed63579f9dbf41844cb435d7627bdca81a83208 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (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.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package spark.components
{
import flash.display.DisplayObject;
import flash.display.StageDisplayState;
import flash.events.Event;
import flash.events.FullScreenEvent;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.events.TimerEvent;
import flash.geom.Rectangle;
import flash.media.Video;
import flash.system.ApplicationDomain;
import flash.utils.Timer;
import mx.core.FlexGlobals;
import mx.core.IVisualElementContainer;
import mx.core.mx_internal;
import mx.events.FlexEvent;
import mx.managers.PopUpManager;
import mx.utils.BitFlagUtil;
import org.osmf.events.LoadEvent;
import org.osmf.events.MediaPlayerStateChangeEvent;
import org.osmf.events.TimeEvent;
import org.osmf.media.MediaPlayerState;
import spark.components.mediaClasses.MuteButton;
import spark.components.mediaClasses.ScrubBar;
import spark.components.mediaClasses.VolumeBar;
import spark.components.supportClasses.ButtonBase;
import spark.components.supportClasses.SkinnableComponent;
import spark.components.supportClasses.ToggleButtonBase;
import spark.core.IDisplayText;
import spark.events.TrackBaseEvent;
use namespace mx_internal;
//--------------------------------------
// Events
//--------------------------------------
/**
* Dispatched when the data is received as a download operation progresses.
* This event is only dispatched when playing a video by downloading it
* directly from a server, typically by issuing an HTTP request.
* It is not displatched when playing a video from a special media server,
* such as Flash Media Server.
*
* <p>This event may not be dispatched when the source is set to null or a playback
* error occurs.</p>
*
* @eventType org.osmf.events.LoadEvent.BYTES_LOADED_CHANGE
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.0
* @productversion Flex 4
*/
[Event(name="bytesLoadedChange",type="org.osmf.events.LoadEvent")]
/**
* Dispatched when the playhead reaches the duration for playable media.
*
* @eventType org.osmf.events.TimeEvent.COMPLETE
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.0
* @productversion Flex 4
*/
[Event(name="complete", type="org.osmf.events.TimeEvent")]
/**
* Dispatched when the <code>currentTime</code> property of the MediaPlayer has changed.
*
* <p>This event may not be dispatched when the source is set to null or a playback
* error occurs.</p>
*
* @eventType org.osmf.events.TimeEvent.CURRENT_TIME_CHANGE
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.0
* @productversion Flex 4
*/
[Event(name="currentTimeChange",type="org.osmf.events.TimeEvent")]
/**
* Dispatched when the <code>duration</code> property of the media has changed.
*
* <p>This event may not be dispatched when the source is set to null or a playback
* error occurs.</p>
*
* @eventType org.osmf.events.TimeEvent.DURATION_CHANGE
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.0
* @productversion Flex 4
*/
[Event(name="durationChange", type="org.osmf.events.TimeEvent")]
/**
* Dispatched when the MediaPlayer's state has changed.
*
* @eventType org.osmf.events.MediaPlayerStateChangeEvent.MEDIA_PLAYER_STATE_CHANGE
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.0
* @productversion Flex 4
*/
[Event(name="mediaPlayerStateChange", type="org.osmf.events.MediaPlayerStateChangeEvent")]
//--------------------------------------
// Styles
//--------------------------------------
include "../styles/metadata/BasicInheritingTextStyles.as";
/**
* Controls the visibility of the drop shadow for this component.
*
* @default true
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[Style(name="dropShadowVisible", type="Boolean", inherit="no", theme="spark")]
/**
* The time, in milli-seconds, to wait in fullscreen mode with no user-interaction
* before hiding the video playback controls.
*
* <p>If set to <code>Infinity</code>, then the playback controls will not
* be hidden in fullscreen mode. Changing this value while already in
* fullscreen mode has no effect.</p>
*
* @default 3000
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[Style(name="fullScreenHideControlsDelay", type="Number", format="Time", inherit="no")]
/**
* @copy spark.components.supportClasses.GroupBase#style:symbolColor
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[Style(name="symbolColor", type="uint", format="Color", inherit="yes", theme="spark")]
//--------------------------------------
// SkinStates
//--------------------------------------
/**
* Uninitialized state of the VideoPlayer.
* The Video Player has been constructed at this point,
* but the source has not been set and no connection
* attempt is in progress.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[SkinState("uninitialized")]
/**
* Loading state of the VideoPlayer.
* The VideoPlayer is loading or connecting to the source.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[SkinState("loading")]
/**
* Ready state of the VideoPlayer.
* The video is ready to be played.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[SkinState("ready")]
/**
* Playing state of the VideoPlayer
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[SkinState("playing")]
/**
* Paused state of the VideoPlayer
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[SkinState("paused")]
/**
* Buffering state of the VideoPlayer
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[SkinState("buffering")]
/**
* Playback Error state of the VideoPlayer.
* An error was encountered while trying to play the video.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[SkinState("playbackError")]
/**
* Disabled state of the VideoPlayer
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[SkinState("disabled")]
/**
* Uninitialized state of the VideoPlayer when
* in full screen mode.
* The Video Player has been constructed at this point,
* but the source has not been set and no connection
* attempt is in progress.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[SkinState("uninitializedAndFullScreen")]
/**
* Loading state of the VideoPlayer when
* in full screen mode.
* The VideoPlayer is loading or connecting to the source.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[SkinState("loadingAndFullScreen")]
/**
* Ready state of the VideoPlayer when
* in full screen mode. The video is ready to be played.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[SkinState("readyAndFullScreen")]
/**
* Playing state of the VideoPlayer when
* in full screen mode.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[SkinState("playingAndFullScreen")]
/**
* Paused state of the VideoPlayer when
* in full screen mode.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[SkinState("pausedAndFullScreen")]
/**
* Buffering state of the VideoPlayer when
* in full screen mode.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[SkinState("bufferingAndFullScreen")]
/**
* Playback Error state of the VideoPlayer when
* in full screen mode.
* An error was encountered while trying to play the video.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[SkinState("playbackErrorAndFullScreen")]
/**
* Disabled state of the VideoPlayer when
* in full screen mode.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[SkinState("disabledAndFullScreen")]
//--------------------------------------
// Excluded APIs
//--------------------------------------
[Exclude(name="focusBlendMode", kind="style")]
[Exclude(name="focusThickness", kind="style")]
//--------------------------------------
// Other metadata
//--------------------------------------
[AccessibilityClass(implementation="spark.accessibility.VideoPlayerAccImpl")]
[DefaultProperty("source")]
[IconFile("VideoPlayer.png")]
/**
* Because this component does not define a skin for the mobile theme, Adobe
* recommends that you not use it in a mobile application. Alternatively, you
* can define your own mobile skin for the component. For more information,
* see <a href="http://help.adobe.com/en_US/flex/mobileapps/WS19f279b149e7481c698e85712b3011fe73-8000.html">Basics of mobile skinning</a>.
*/
[DiscouragedForProfile("mobileDevice")]
/**
* The VideoPlayer control is a skinnable video player that supports
* progressive download, multi-bitrate streaming, and streaming video.
* It supports playback of FLV and F4v files. The VideoPlayer control
* contains a full-featured UI for controlling video playback.
*
* <p><code>VideoDisplay</code> is the chromeless version that does not support skinning.
* It is useful when you do not want the user to interact with the control.</p>
*
* <p>The VideoPlayer control has the following default characteristics:</p>
* <table class="innertable">
* <tr>
* <th>Characteristic</th>
* <th>Description</th>
* </tr>
* <tr>
* <td>Default size</td>
* <td>263 pixels wide by 184 pixels high</td>
* </tr>
* <tr>
* <td>Minimum size</td>
* <td>0</td>
* </tr>
* <tr>
* <td>Maximum size</td>
* <td>10000 pixels wide and 10000 pixels high</td>
* </tr>
* <tr>
* <td>Default skin class</td>
* <td>spark.skins.spark.VideoPlayerSkin</td>
* </tr>
* </table>
*
* @see spark.components.VideoDisplay
* @see spark.skins.spark.VideoPlayerSkin
* @see spark.skins.spark.mediaClasses.fullScreen.FullScreenButtonSkin
* @see spark.skins.spark.mediaClasses.fullScreen.MuteButtonSkin
* @see spark.skins.spark.mediaClasses.fullScreen.PlayPauseButtonSkin
* @see spark.skins.spark.mediaClasses.fullScreen.ScrubBarSkin
* @see spark.skins.spark.mediaClasses.fullScreen.ScrubBarThumbSkin
* @see spark.skins.spark.mediaClasses.fullScreen.ScrubBarTrackSkin
* @see spark.skins.spark.mediaClasses.fullScreen.VolumeBarSkin
* @see spark.skins.spark.mediaClasses.fullScreen.VolumeBarThumbSkin
* @see spark.skins.spark.mediaClasses.fullScreen.VolumeBarTrackSkin
* @see spark.skins.spark.mediaClasses.normal.FullScreenButtonSkin
* @see spark.skins.spark.mediaClasses.normal.MuteButtonSkin
* @see spark.skins.spark.mediaClasses.normal.PlayPauseButtonSkin
* @see spark.skins.spark.mediaClasses.normal.ScrubBarSkin
* @see spark.skins.spark.mediaClasses.normal.ScrubBarThumbSkin
* @see spark.skins.spark.mediaClasses.normal.ScrubBarTrackSkin
* @see spark.skins.spark.mediaClasses.normal.VolumeBarSkin
* @see spark.skins.spark.mediaClasses.normal.VolumeBarThumbSkin
* @see spark.skins.spark.mediaClasses.normal.VolumeBarTrackSkin
*
* @mxml
*
* <p>The <code>&lt;s:VideoPlayer&gt;</code> tag inherits all of the tag
* attributes of its superclass and adds the following tag attributes:</p>
*
* <pre>
* &lt;s:VideoPlayer
* <strong>Properties</strong>
* autoDisplayFirstFrame="true"
* autoPlay="true"
* autoRewind="true"
* loop="false"
* muted="false"
* pauseWhenHidden="true"
* scaleMode="letterbox"
* source=""
* volume="1"
*
* <strong>Events</strong>
* bytesLoadedChange="<i>No default</i>"
* complete="<i>No default</i>"
* currentTimeChange="<i>No default</i>"
* durationChange="<i>No default</i>"
* mediaPlayerStateChange="<i>No default</i>"
*
*
* <strong>Styles</strong>
* alignmentBaseline="baseline"
* baselineShift="0"
* cffHinting="0.0"
* color="0x000000"
* digitCase="default"
* digitWidth="default"
* direction="ltr"
* dominantBaseline="auto"
* dropShadowVisible="true"
* fontFamily="Arial"
* fontLookup="device"
* fontSize="12"
* fontStyle="normal"
* fontWeight="normal"
* justificationRule="auto"
* justificationStyle="auto"
* kerning="false"
* ligatureLevel="common"
* lineHeight="120%"
* lineThrough="false%"
* locale="en"
* renderingMode="cff"
* textAlign="start"
* textAlignLast="start"
* textAlpha="1"
* textDecoration="start"
* textJustify="interWord"
* trackingLeft="0"
* trackingRight="00"
* typographicCase="default"
* /&gt;
* </pre>
*
* @includeExample examples/VideoPlayerExample.mxml
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public class VideoPlayer extends SkinnableComponent
{
include "../core/Version.as";
//--------------------------------------------------------------------------
//
// Class constants
//
//--------------------------------------------------------------------------
/**
* @private
*/
private static const AUTO_DISPLAY_FIRST_FRAME_PROPERTY_FLAG:uint = 1 << 0;
/**
* @private
*/
private static const AUTO_PLAY_PROPERTY_FLAG:uint = 1 << 1;
/**
* @private
*/
private static const AUTO_REWIND_PROPERTY_FLAG:uint = 1 << 2;
/**
* @private
*/
private static const LOOP_PROPERTY_FLAG:uint = 1 << 3;
/**
* @private
*/
private static const SCALE_MODE_PROPERTY_FLAG:uint = 1 << 4;
/**
* @private
*/
private static const MUTED_PROPERTY_FLAG:uint = 1 << 5;
/**
* @private
*/
private static const SOURCE_PROPERTY_FLAG:uint = 1 << 6;
/**
* @private
*/
private static const VOLUME_PROPERTY_FLAG:uint = 1 << 7;
/**
* @private
*/
private static const PAUSE_WHEN_HIDDEN_PROPERTY_FLAG:uint = 1 << 8;
/**
* @private
*/
private static const THUMBNAIL_SOURCE_PROPERTY_FLAG:uint = 1 << 9;
//--------------------------------------------------------------------------
//
// Class properties
//
//--------------------------------------------------------------------------
/**
* @private
*/
private static var _screenClass:Class;
/**
* @private
*/
private static var checkedForScreenClass:Boolean;
/**
* @private
*/
private static function get screenClass():Class
{
if (!checkedForScreenClass)
{
checkedForScreenClass = true;
if (ApplicationDomain.currentDomain.
hasDefinition("flash.display::Screen"))
{
_screenClass = Class(ApplicationDomain.currentDomain.
getDefinition("flash.display::Screen"));
}
}
return _screenClass;
}
//--------------------------------------------------------------------------
//
// Class mixins
//
//--------------------------------------------------------------------------
/**
* @private
* Placeholder for mixin by VideoPlayerAccImpl.
*/
mx_internal static var createAccessibilityImplementation:Function;
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function VideoPlayer()
{
super();
}
//--------------------------------------------------------------------------
//
// Skin Parts
//
//--------------------------------------------------------------------------
[SkinPart(required="true")]
/**
* A required skin part that defines the VideoDisplay.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public var videoDisplay:VideoDisplay;
[SkinPart(required="false")]
/**
* An optional skin part to display the current value of <code>codecurrentTime</code>.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public var currentTimeDisplay:IDisplayText;
[SkinPart(required="false")]
/**
* An optional skin part for a button to toggle fullscreen mode.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public var fullScreenButton:ButtonBase;
[SkinPart(required="false")]
/**
* An optional skin part for the mute button. The mute
* button has both a <code>muted</code> property and a
* <code>volume</code> property.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public var muteButton:MuteButton;
[SkinPart(required="false")]
/**
* An optional skin part for the pause button.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public var pauseButton:ButtonBase;
[SkinPart(required="false")]
/**
* An optional skin part for the play button.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public var playButton:ButtonBase;
[SkinPart(required="false")]
/**
* An optional skin part for all of the player controls.
* This skin is used to determine what to hide when the player is in full screen
* mode and there has been no user interaction.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public var playerControls:DisplayObject;
[SkinPart(required="false")]
/**
* An optional skin part for a play/pause button. When the
* video is playing, the <code>selected</code> property is set to
* <code>true</code>. When the video is paused or stopped,
* the <code>selected</code> property is set to <code>false</code>.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public var playPauseButton:ToggleButtonBase;
[SkinPart(required="false")]
/**
* An optional skin part for the scrub bar (the
* timeline).
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public var scrubBar:ScrubBar;
[SkinPart(required="false")]
/**
* An optional skin part for the stop button.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public var stopButton:ButtonBase;
[SkinPart(required="false")]
/**
* An optional skin part to display the duration.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public var durationDisplay:IDisplayText;
[SkinPart(required="false")]
/**
* An optional skin part for the volume control.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public var volumeBar:VolumeBar;
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* @private
* Several properties are proxied to videoDisplay. However, when videoDisplay
* is not around, we need to store values set on VideoPlayer. This object
* stores those values. If videoDisplay is around, the values are stored
* on the videoDisplay directly. However, we need to know what values
* have been set by the developer on the VideoPlayer (versus set on
* the videoDisplay or defaults of the videoDisplay) as those are values
* we want to carry around if the videoDisplay changes (via a new skin).
* In order to store this info effeciently, videoDisplayProperties becomes
* a uint to store a series of BitFlags. These bits represent whether a
* property has been explicitely set on this VideoPlayer. When the
* contentGroup is not around, videoDisplayProperties is a typeless
* object to store these proxied properties. When videoDisplay is around,
* videoDisplayProperties stores booleans as to whether these properties
* have been explicitely set or not.
*/
private var videoDisplayProperties:Object = {};
/**
* @private
* The value of the pauseWhenHidden property before exiting
* fullScreen. We need to store it away here so we can
* restore it at commitProperties() time because of an AIR
* Mac bug.
*/
private var exitingFullScreenPauseWhenHidden:Boolean;
/**
* @private
* Whether the pauseWhenHidden property needs to be updated.
*/
private var needsToUpdatePauseWhenHidden:Boolean = false;
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// autoDisplayFirstFrame
//----------------------------------
[Inspectable(category="General", defaultValue="true")]
/**
* @copy spark.components.VideoDisplay#autoDisplayFirstFrame
*
* @default true
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function get autoDisplayFirstFrame():Boolean
{
if (videoDisplay)
{
return videoDisplay.autoDisplayFirstFrame;
}
else
{
var v:* = videoDisplayProperties.autoDisplayFirstFrame;
return (v === undefined) ? true : v;
}
}
/**
* @private
*/
public function set autoDisplayFirstFrame(value:Boolean):void
{
if (videoDisplay)
{
videoDisplay.autoDisplayFirstFrame = value;
videoDisplayProperties = BitFlagUtil.update(videoDisplayProperties as uint,
AUTO_DISPLAY_FIRST_FRAME_PROPERTY_FLAG, true);
}
else
{
videoDisplayProperties.autoDisplayFirstFrame = value;
}
}
//----------------------------------
// autoPlay
//----------------------------------
[Inspectable(category="General", defaultValue="true")]
/**
* @copy spark.components.VideoDisplay#autoPlay
*
* @default true
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function get autoPlay():Boolean
{
if (videoDisplay)
{
return videoDisplay.autoPlay;
}
else
{
var v:* = videoDisplayProperties.autoPlay;
return (v === undefined) ? true : v;
}
}
/**
* @private
*/
public function set autoPlay(value:Boolean):void
{
if (videoDisplay)
{
videoDisplay.autoPlay = value;
videoDisplayProperties = BitFlagUtil.update(videoDisplayProperties as uint,
AUTO_PLAY_PROPERTY_FLAG, true);
}
else
{
videoDisplayProperties.autoPlay = value;
}
}
//----------------------------------
// autoRewind
//----------------------------------
[Inspectable(category="General", defaultValue="true")]
/**
* @copy spark.components.VideoDisplay#autoRewind
*
* @default true
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function get autoRewind():Boolean
{
if (videoDisplay)
{
return videoDisplay.autoRewind;
}
else
{
var v:* = videoDisplayProperties.autoRewind;
return (v === undefined) ? true : v;
}
}
/**
* @private
*/
public function set autoRewind(value:Boolean):void
{
if (videoDisplay)
{
videoDisplay.autoRewind = value;
videoDisplayProperties = BitFlagUtil.update(videoDisplayProperties as uint,
AUTO_REWIND_PROPERTY_FLAG, true);
}
else
{
videoDisplayProperties.autoRewind = value;
}
}
//----------------------------------
// bytesLoaded
//----------------------------------
[Inspectable(Category="General", defaultValue="0")]
[Bindable("bytesLoadedChange")]
[Bindable("mediaPlayerStateChange")]
/**
* @copy spark.components.VideoDisplay#bytesLoaded
*
* @default 0
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function get bytesLoaded():Number
{
if (videoDisplay)
return videoDisplay.bytesLoaded;
else
return 0;
}
//----------------------------------
// bytesTotal
//----------------------------------
[Inspectable(Category="General", defaultValue="0")]
[Bindable("mediaPlayerStateChange")]
/**
* @copy spark.components.VideoDisplay#bytesTotal
*
* @default 0
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function get bytesTotal():Number
{
if (videoDisplay)
return videoDisplay.bytesTotal;
else
return 0;
}
//----------------------------------
// currentTime
//----------------------------------
[Inspectable(Category="General", defaultValue="0")]
[Bindable("currentTimeChange")]
[Bindable("mediaPlayerStateChange")]
/**
* @copy spark.components.VideoDisplay#currentTime
*
* @default 0
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function get currentTime():Number
{
if (videoDisplay)
return videoDisplay.currentTime;
else
return 0;
}
//----------------------------------
// duration
//----------------------------------
[Inspectable(Category="General", defaultValue="0")]
[Bindable("durationChange")]
[Bindable("mediaPlayerStateChange")]
/**
* @copy spark.components.VideoDisplay#duration
*
* @default 0
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function get duration():Number
{
if (videoDisplay)
return videoDisplay.duration;
else
return 0;
}
//----------------------------------
// loop
//----------------------------------
[Inspectable(Category="General", defaultValue="false")]
/**
* @copy spark.components.VideoDisplay#loop
*
* @default false
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function get loop():Boolean
{
if (videoDisplay)
{
return videoDisplay.loop;
}
else
{
var v:* = videoDisplayProperties.loop;
return (v === undefined) ? false : v;
}
}
/**
* @private
*/
public function set loop(value:Boolean):void
{
if (videoDisplay)
{
videoDisplay.loop = value;
videoDisplayProperties = BitFlagUtil.update(videoDisplayProperties as uint,
LOOP_PROPERTY_FLAG, true);
}
else
{
videoDisplayProperties.loop = value;
}
}
//----------------------------------
// mediaPlayerState
//----------------------------------
[Inspectable(category="General", defaultValue="uninitialized")]
[Bindable("mediaPlayerStateChange")]
/**
* @copy spark.components.VideoDisplay#mediaPlayerState
*
* @default uninitialized
*
* @see org.osmf.media.MediaPlayerState
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function get mediaPlayerState():String
{
if (videoDisplay)
return videoDisplay.mediaPlayerState;
else
return MediaPlayerState.UNINITIALIZED;
}
//----------------------------------
// muted
//----------------------------------
[Inspectable(category="General", defaultValue="false")]
[Bindable("volumeChanged")]
/**
* @copy spark.components.VideoDisplay#muted
*
* @default false
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function get muted():Boolean
{
if (videoDisplay)
{
return videoDisplay.muted;
}
else
{
var v:* = videoDisplayProperties.muted;
return (v === undefined) ? false : v;
}
}
/**
* @private
*/
public function set muted(value:Boolean):void
{
if (videoDisplay)
{
videoDisplay.muted = value;
videoDisplayProperties = BitFlagUtil.update(videoDisplayProperties as uint,
MUTED_PROPERTY_FLAG, true);
}
else
{
videoDisplayProperties.muted = value;
}
if (volumeBar)
volumeBar.muted = value;
if (muteButton)
muteButton.muted = value;
}
//----------------------------------
// pauseWhenHidden
//----------------------------------
[Inspectable(category="General", defaultValue="true")]
/**
* @copy spark.components.VideoDisplay#pauseWhenHidden
*
* @default true
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function get pauseWhenHidden():Boolean
{
if (needsToUpdatePauseWhenHidden)
{
return exitingFullScreenPauseWhenHidden;
}
else if (videoDisplay)
{
return videoDisplay.pauseWhenHidden;
}
else
{
var v:* = videoDisplayProperties.pauseWhenHidden;
return (v === undefined) ? false : v;
}
}
/**
* @private
*/
public function set pauseWhenHidden(value:Boolean):void
{
if (needsToUpdatePauseWhenHidden)
{
exitingFullScreenPauseWhenHidden = value;
}
else if (videoDisplay)
{
videoDisplay.pauseWhenHidden = value;
videoDisplayProperties = BitFlagUtil.update(videoDisplayProperties as uint,
PAUSE_WHEN_HIDDEN_PROPERTY_FLAG, true);
}
else
{
videoDisplayProperties.pauseWhenHidden = value;
}
}
//----------------------------------
// playing
//----------------------------------
[Inspectable(category="General")]
[Bindable("mediaPlayerStateChange")]
/**
* @copy spark.components.VideoDisplay#playing
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function get playing():Boolean
{
if (videoDisplay)
return videoDisplay.playing;
else
return false;
}
//----------------------------------
// scaleMode
//----------------------------------
[Inspectable(Category="General", enumeration="none,stretch,letterbox,zoom", defaultValue="letterbox")]
/**
* @copy spark.components.VideoDisplay#scaleMode
*
* @default "letterbox"
*
* @see org.osmf.display.ScaleMode
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function get scaleMode():String
{
if (videoDisplay)
{
return videoDisplay.scaleMode;
}
else
{
var v:* = videoDisplayProperties.scaleMode;
return (v === undefined) ? "letterbox" : v;
}
}
/**
* @private
*/
public function set scaleMode(value:String):void
{
if (videoDisplay)
{
videoDisplay.scaleMode = value;
videoDisplayProperties = BitFlagUtil.update(videoDisplayProperties as uint,
SCALE_MODE_PROPERTY_FLAG, true);
}
else
{
videoDisplayProperties.scaleMode = value;
}
}
//----------------------------------
// source
//----------------------------------
[Inspectable(category="General", defaultValue="null")]
[Bindable("sourceChanged")]
/**
* @copy spark.components.VideoDisplay#source
*
* @default null
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function get source():Object
{
if (videoDisplay)
{
return videoDisplay.source;
}
else
{
var v:* = videoDisplayProperties.source;
return (v === undefined) ? null : v;
}
}
/**
* @private
*/
public function set source(value:Object):void
{
if (videoDisplay)
{
videoDisplay.source = value;
videoDisplayProperties = BitFlagUtil.update(videoDisplayProperties as uint,
SOURCE_PROPERTY_FLAG, true);
}
else
{
videoDisplayProperties.source = value;
}
}
//----------------------------------
// thumbnailSource
//----------------------------------
[Inspectable(category="General")]
/**
* @private
* @copy spark.components.VideoDisplay#thumbnailSource
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
mx_internal function get thumbnailSource():Object
{
if (videoDisplay)
{
return videoDisplay.thumbnailSource;
}
else
{
var v:* = videoDisplayProperties.thumbnailSource;
return (v === undefined) ? null : v;
}
}
/**
* @private
*/
mx_internal function set thumbnailSource(value:Object):void
{
if (videoDisplay)
{
videoDisplay.thumbnailSource = value;
videoDisplayProperties = BitFlagUtil.update(videoDisplayProperties as uint,
THUMBNAIL_SOURCE_PROPERTY_FLAG, true);
}
else
{
videoDisplayProperties.thumbnailSource = value;
}
}
//----------------------------------
// videoObject
//----------------------------------
[Inspectable(category="General", defaultValue="null")]
/**
* @copy spark.components.VideoDisplay#videoObject
*
* @default null
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function get videoObject():Video
{
if (videoDisplay)
return videoDisplay.videoObject;
else
return null;
}
//----------------------------------
// volume
//----------------------------------
[Inspectable(category="General", defaultValue="1.0", minValue="0.0", maxValue="1.0")]
[Bindable("volumeChanged")]
/**
* @copy spark.components.VideoDisplay#volume
*
* @default 1
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function get volume():Number
{
if (videoDisplay)
{
return videoDisplay.volume;
}
else
{
var v:* = videoDisplayProperties.volume;
return (v === undefined) ? 1 : v;
}
}
/**
* @private
*/
public function set volume(value:Number):void
{
if (videoDisplay)
{
videoDisplay.volume = value;
videoDisplayProperties = BitFlagUtil.update(videoDisplayProperties as uint,
VOLUME_PROPERTY_FLAG, true);
}
else
{
videoDisplayProperties.volume = value;
}
if (volumeBar)
volumeBar.value = value;
}
//--------------------------------------------------------------------------
//
// Overridden methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
override protected function initializeAccessibility():void
{
if (VideoPlayer.createAccessibilityImplementation != null)
VideoPlayer.createAccessibilityImplementation(this);
}
/**
* @private
*/
override protected function getCurrentSkinState():String
{
if (!videoDisplay || !videoDisplay.videoPlayer)
return null;
var state:String = videoDisplay.videoPlayer.state;
// now that we have our video player's current state (atleast the one we care about)
// and that we've set the previous state to something we care about, let's figure
// out our skin's state
if (!enabled)
state="disabled"
if (fullScreen)
return state + "AndFullScreen";
return state;
}
/**
* @private
*/
override protected function partAdded(partName:String, instance:Object):void
{
super.partAdded(partName, instance);
if (instance == videoDisplay)
{
videoDisplay.addEventListener(TimeEvent.CURRENT_TIME_CHANGE, videoDisplay_currentTimeChangeHandler);
videoDisplay.addEventListener(LoadEvent.BYTES_LOADED_CHANGE, videoDisplay_bytesLoadedChangeHandler);
videoDisplay.addEventListener(MediaPlayerStateChangeEvent.MEDIA_PLAYER_STATE_CHANGE, videoDisplay_mediaPlayerStateChangeHandler);
videoDisplay.addEventListener(TimeEvent.DURATION_CHANGE, videoDisplay_durationChangeHandler);
videoDisplay.addEventListener(TimeEvent.COMPLETE, dispatchEvent);
// just strictly for binding purposes
videoDisplay.addEventListener("sourceChanged", dispatchEvent);
videoDisplay.addEventListener("volumeChanged", videoDisplay_volumeChangedHandler);
// copy proxied values from videoProperties (if set) to video
var newVideoProperties:uint = 0;
if (videoDisplayProperties.source !== undefined)
{
videoDisplay.source = videoDisplayProperties.source;
newVideoProperties = BitFlagUtil.update(newVideoProperties as uint,
SOURCE_PROPERTY_FLAG, true);
}
if (videoDisplayProperties.autoPlay !== undefined)
{
videoDisplay.autoPlay = videoDisplayProperties.autoPlay;
newVideoProperties = BitFlagUtil.update(newVideoProperties as uint,
AUTO_PLAY_PROPERTY_FLAG, true);
}
if (videoDisplayProperties.volume !== undefined)
{
videoDisplay.volume = videoDisplayProperties.volume;
newVideoProperties = BitFlagUtil.update(newVideoProperties as uint,
VOLUME_PROPERTY_FLAG, true);
}
if (videoDisplayProperties.autoRewind !== undefined)
{
videoDisplay.autoRewind = videoDisplayProperties.autoRewind;
newVideoProperties = BitFlagUtil.update(newVideoProperties as uint,
AUTO_REWIND_PROPERTY_FLAG, true);
}
if (videoDisplayProperties.loop !== undefined)
{
videoDisplay.loop = videoDisplayProperties.loop;
newVideoProperties = BitFlagUtil.update(newVideoProperties as uint,
LOOP_PROPERTY_FLAG, true);
}
if (videoDisplayProperties.scaleMode !== undefined)
{
videoDisplay.scaleMode = videoDisplayProperties.scaleMode;
newVideoProperties = BitFlagUtil.update(newVideoProperties as uint,
SCALE_MODE_PROPERTY_FLAG, true);
}
if (videoDisplayProperties.muted !== undefined)
{
videoDisplay.muted = videoDisplayProperties.muted;
newVideoProperties = BitFlagUtil.update(newVideoProperties as uint,
MUTED_PROPERTY_FLAG, true);
}
if (videoDisplayProperties.pauseWhenHidden !== undefined)
{
videoDisplay.pauseWhenHidden = videoDisplayProperties.pauseWhenHidden;
newVideoProperties = BitFlagUtil.update(newVideoProperties as uint,
PAUSE_WHEN_HIDDEN_PROPERTY_FLAG, true);
}
if (videoDisplayProperties.autoDisplayFirstFrame !== undefined)
{
videoDisplay.autoDisplayFirstFrame = videoDisplayProperties.autoDisplayFirstFrame;
newVideoProperties = BitFlagUtil.update(newVideoProperties as uint,
AUTO_DISPLAY_FIRST_FRAME_PROPERTY_FLAG, true);
}
if (videoDisplayProperties.thumbnailSource !== undefined)
{
videoDisplay.thumbnailSource = videoDisplayProperties.thumbnailSource;
newVideoProperties = BitFlagUtil.update(newVideoProperties as uint,
THUMBNAIL_SOURCE_PROPERTY_FLAG, true);
}
// these are state properties just carried over from an old video element
if (videoDisplayProperties.currentTime !== undefined ||
videoDisplayProperties.playing !== undefined)
{
videoDisplay_updateCompleteHandlerProperties = {
autoPlay: videoDisplay.autoPlay,
playing: videoDisplayProperties.playing,
currentTime: videoDisplayProperties.currentTime};
// so the videoDisplay doesn't start playing...we'll handle it instead in
// videoDisplay_updateCompleteHandler
videoDisplay.autoPlay = false;
videoDisplay.addEventListener(FlexEvent.UPDATE_COMPLETE, videoDisplay_updateCompleteHandler);
}
videoDisplayProperties = newVideoProperties;
if (volumeBar)
{
volumeBar.value = videoDisplay.volume;
volumeBar.muted = videoDisplay.muted;
}
if (muteButton)
{
muteButton.volume = videoDisplay.volume;
muteButton.muted = videoDisplay.muted;
}
if (scrubBar)
updateScrubBar();
if (currentTimeDisplay)
updateCurrentTime();
if (durationDisplay)
updateDuration();
}
else if (instance == playButton)
{
playButton.addEventListener(MouseEvent.CLICK, playButton_clickHandler);
}
else if (instance == pauseButton)
{
pauseButton.addEventListener(MouseEvent.CLICK, pauseButton_clickHandler);
}
else if (instance == playPauseButton)
{
playPauseButton.addEventListener(MouseEvent.CLICK, playPauseButton_clickHandler);
}
else if (instance == stopButton)
{
stopButton.addEventListener(MouseEvent.CLICK, stopButton_clickHandler);
}
else if (instance == muteButton)
{
if (videoDisplay)
{
muteButton.muted = muted;
muteButton.volume = volume;
}
muteButton.addEventListener(FlexEvent.MUTED_CHANGE, muteButton_mutedChangeHandler);
}
else if (instance == volumeBar)
{
volumeBar.minimum = 0;
volumeBar.maximum = 1;
if (videoDisplay)
{
volumeBar.value = volume;
volumeBar.muted = muted;
}
volumeBar.addEventListener(Event.CHANGE, volumeBar_changeHandler);
volumeBar.addEventListener(FlexEvent.MUTED_CHANGE, volumeBar_mutedChangeHandler);
}
else if (instance == scrubBar)
{
if (videoDisplay)
updateScrubBar();
// add thumbPress and thumbRelease so we pause the video while dragging
scrubBar.addEventListener(TrackBaseEvent.THUMB_PRESS, scrubBar_thumbPressHandler);
scrubBar.addEventListener(TrackBaseEvent.THUMB_RELEASE, scrubBar_thumbReleaseHandler);
// add change to actually seek() when the change is complete
scrubBar.addEventListener(Event.CHANGE, scrubBar_changeHandler);
// add changeEnd and changeStart so we don't update the scrubbar's value
// while the scrubbar is moving around due to an animation
scrubBar.addEventListener(FlexEvent.CHANGE_END, scrubBar_changeEndHandler);
scrubBar.addEventListener(FlexEvent.CHANGE_START, scrubBar_changeStartHandler);
}
else if (instance == fullScreenButton)
{
fullScreenButton.addEventListener(MouseEvent.CLICK, fullScreenButton_clickHandler);
}
else if (instance == currentTimeDisplay)
{
if (videoDisplay)
updateCurrentTime();
}
else if (instance == durationDisplay)
{
if (videoDisplay)
updateDuration();
}
}
/**
* @private
* Holds the state of the video element when the skin is being swapped out.
* This is so the new videoDisplay can load up and start playing
* where it left off.
*/
private var videoDisplay_updateCompleteHandlerProperties:Object;
/**
* @private
* We only listen for the updateComplete event on the videoDisplay when
* a skin has been swapped. This is so we can push the old videoDisplay's
* state in to the new object, and we can start playing the video
* where it left off.
*/
private function videoDisplay_updateCompleteHandler(event:FlexEvent):void
{
if (videoDisplay_updateCompleteHandlerProperties.autoPlay)
videoDisplay.autoPlay = true;
if (videoDisplay_updateCompleteHandlerProperties.currentTime !== undefined)
videoDisplay.seek(videoDisplay_updateCompleteHandlerProperties.currentTime);
if (videoDisplay_updateCompleteHandlerProperties.playing)
videoDisplay.play();
videoDisplay_updateCompleteHandlerProperties = null;
videoDisplay.removeEventListener(FlexEvent.UPDATE_COMPLETE, videoDisplay_updateCompleteHandler);
}
/**
* @private
*/
override protected function partRemoved(partName:String, instance:Object):void
{
super.partRemoved(partName, instance);
if (instance == videoDisplay)
{
// validate before doing anything with the videoDisplay.
// This is so if the video element hasn't been validated, it won't start playing.
// plus this way we'll get a valid currentTime and all those other properties
// we are interested in.
videoDisplay.validateNow();
// copy proxied values from video (if explicitely set) to videoProperties
var newVideoProperties:Object = {};
if (BitFlagUtil.isSet(videoDisplayProperties as uint, SOURCE_PROPERTY_FLAG))
newVideoProperties.source = videoDisplay.source;
if (BitFlagUtil.isSet(videoDisplayProperties as uint, AUTO_PLAY_PROPERTY_FLAG))
newVideoProperties.autoPlay = videoDisplay.autoPlay;
if (BitFlagUtil.isSet(videoDisplayProperties as uint, VOLUME_PROPERTY_FLAG))
newVideoProperties.volume = videoDisplay.volume;
if (BitFlagUtil.isSet(videoDisplayProperties as uint, AUTO_REWIND_PROPERTY_FLAG))
newVideoProperties.autoRewind = videoDisplay.autoRewind;
if (BitFlagUtil.isSet(videoDisplayProperties as uint, LOOP_PROPERTY_FLAG))
newVideoProperties.loop = videoDisplay.loop;
if (BitFlagUtil.isSet(videoDisplayProperties as uint, SCALE_MODE_PROPERTY_FLAG))
newVideoProperties.scaleMode = videoDisplay.scaleMode;
if (BitFlagUtil.isSet(videoDisplayProperties as uint, MUTED_PROPERTY_FLAG))
newVideoProperties.muted = videoDisplay.muted;
if (BitFlagUtil.isSet(videoDisplayProperties as uint, PAUSE_WHEN_HIDDEN_PROPERTY_FLAG))
newVideoProperties.pauseWhenHidden = videoDisplay.pauseWhenHidden;
if (BitFlagUtil.isSet(videoDisplayProperties as uint, AUTO_DISPLAY_FIRST_FRAME_PROPERTY_FLAG))
newVideoProperties.autoDisplayFirstFrame = videoDisplay.autoDisplayFirstFrame;
if (BitFlagUtil.isSet(videoDisplayProperties as uint, THUMBNAIL_SOURCE_PROPERTY_FLAG))
newVideoProperties.thumbnailSource = videoDisplay.thumbnailSource;
// push our current state (where we were in the video and whether we were playing)
// so that the new skin gets these as well
newVideoProperties.currentTime = videoDisplay.currentTime;
newVideoProperties.playing = videoDisplay.playing;
videoDisplay.stop();
videoDisplayProperties = newVideoProperties;
videoDisplay.removeEventListener(TimeEvent.CURRENT_TIME_CHANGE, videoDisplay_currentTimeChangeHandler);
videoDisplay.removeEventListener(LoadEvent.BYTES_LOADED_CHANGE, videoDisplay_bytesLoadedChangeHandler);
videoDisplay.removeEventListener(MediaPlayerStateChangeEvent.MEDIA_PLAYER_STATE_CHANGE, videoDisplay_mediaPlayerStateChangeHandler);
videoDisplay.removeEventListener(TimeEvent.DURATION_CHANGE, videoDisplay_durationChangeHandler);
videoDisplay.removeEventListener(TimeEvent.COMPLETE, dispatchEvent);
// just strictly for binding purposes
videoDisplay.removeEventListener("sourceChanged", dispatchEvent);
videoDisplay.removeEventListener("volumeChanged", videoDisplay_volumeChangedHandler);
}
else if (instance == playButton)
{
playButton.removeEventListener(MouseEvent.CLICK, playButton_clickHandler);
}
else if (instance == pauseButton)
{
pauseButton.removeEventListener(MouseEvent.CLICK, pauseButton_clickHandler);
}
else if (instance == playPauseButton)
{
playPauseButton.removeEventListener(MouseEvent.CLICK, playPauseButton_clickHandler);
}
else if (instance == stopButton)
{
stopButton.removeEventListener(MouseEvent.CLICK, stopButton_clickHandler);
}
else if (instance == muteButton)
{
muteButton.removeEventListener(FlexEvent.MUTED_CHANGE, muteButton_mutedChangeHandler);
}
else if (instance == volumeBar)
{
volumeBar.removeEventListener(Event.CHANGE, volumeBar_changeHandler);
volumeBar.removeEventListener(FlexEvent.MUTED_CHANGE, volumeBar_mutedChangeHandler);
}
else if (instance == scrubBar)
{
scrubBar.removeEventListener(TrackBaseEvent.THUMB_PRESS, scrubBar_thumbPressHandler);
scrubBar.removeEventListener(TrackBaseEvent.THUMB_RELEASE, scrubBar_thumbReleaseHandler);
scrubBar.removeEventListener(Event.CHANGE, scrubBar_changeHandler);
scrubBar.removeEventListener(FlexEvent.CHANGE_END, scrubBar_changeEndHandler);
scrubBar.removeEventListener(FlexEvent.CHANGE_START, scrubBar_changeStartHandler);
}
else if (instance == fullScreenButton)
{
fullScreenButton.removeEventListener(MouseEvent.CLICK, fullScreenButton_clickHandler);
}
}
/**
* @private
*/
override protected function commitProperties():void
{
super.commitProperties();
// if coming from full screen mode, we reset the
// pauseWhenHidden property here because of an AIR bug
// that requires us to defer it.
if (needsToUpdatePauseWhenHidden)
{
needsToUpdatePauseWhenHidden = false;
pauseWhenHidden = exitingFullScreenPauseWhenHidden;
}
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* @throws TypeError If the skin hasn't been loaded and there is no videoDisplay.
*
* @copy spark.components.VideoDisplay#pause()
*
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function pause():void
{
videoDisplay.pause();
}
/**
* @copy spark.components.VideoDisplay#play()
*
* @throws TypeError if the skin hasn't been loaded up yet
* and there's no videoDisplay.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function play():void
{
//trace("play");
videoDisplay.play();
}
/**
* @copy spark.components.VideoDisplay#seek()
*
* @throws TypeError if the skin hasn't been loaded up yet
* and there's no videoDisplay.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function seek(time:Number):void
{
videoDisplay.seek(time);
}
/**
* @copy spark.components.VideoDisplay#stop()
*
* @throws TypeError if the skin hasn't been loaded up yet
* and there's no videoDisplay.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function stop():void
{
videoDisplay.stop();
}
/**
* @private
*/
private function updateScrubBar():void
{
if (!videoDisplay)
return;
if (!scrubBarMouseCaptured && !scrubBarChanging)
{
scrubBar.minimum = 0;
scrubBar.maximum = videoDisplay.duration;
scrubBar.value = videoDisplay.currentTime;
}
// if streaming, then we pretend to have everything in view
// if progressive, then look at the bytesLoaded and bytesTotal
if (!videoDisplay.videoPlayer.canLoad)
scrubBar.loadedRangeEnd = videoDisplay.duration;
else if (videoDisplay.bytesTotal == 0)
scrubBar.loadedRangeEnd = 0;
else
scrubBar.loadedRangeEnd = (videoDisplay.bytesLoaded/videoDisplay.bytesTotal)*videoDisplay.duration;
}
/**
* @private
*/
private function updateDuration():void
{
durationDisplay.text = formatTimeValue(duration);
}
/**
* Formats a time value, specified in seconds, into a String that
* gets used for <code>currentTime</code> and the <code>duration</code>.
*
* @param value Value in seconds of the time to format.
*
* @return Formatted time value.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
protected function formatTimeValue(value:Number):String
{
// default format: hours:minutes:seconds
value = Math.round(value);
var hours:uint = Math.floor(value/3600) % 24;
var minutes:uint = Math.floor(value/60) % 60;
var seconds:uint = value % 60;
var result:String = "";
if (hours != 0)
result = hours + ":";
if (result && minutes < 10)
result += "0" + minutes + ":";
else
result += minutes + ":";
if (seconds < 10)
result += "0" + seconds;
else
result += seconds;
return result;
}
/**
* @private
*/
private function updateCurrentTime():void
{
currentTimeDisplay.text = formatTimeValue(currentTime);
}
/**
* @private
* Returns the screen bounds.
* If we are on the AIR Player, we need to work around AIR Player bug #2503351
* We check if the flash.display.Screen class is defined. If so, then
* we are running on the AIR Player and can access this API.
*/
mx_internal function getScreenBounds():Rectangle
{
var resultRect:Rectangle = new Rectangle(0, 0, stage.fullScreenWidth, stage.fullScreenHeight);
if (screenClass)
{
// Get the screen where the application resides
try
{
var nativeWindowBounds:Rectangle = stage["nativeWindow"]["bounds"];
var currentScreen:Object = screenClass["getScreensForRectangle"](nativeWindowBounds)[0];
// Return the bounds of that screen
resultRect = currentScreen["bounds"];
}
catch (e:Error)
{
}
}
return resultRect;
}
//--------------------------------------------------------------------------
//
// Event handlers
//
//--------------------------------------------------------------------------
/**
* @private
*/
private function videoDisplay_currentTimeChangeHandler(event:TimeEvent):void
{
if (scrubBar)
updateScrubBar();
if (currentTimeDisplay)
updateCurrentTime();
dispatchEvent(event);
}
/**
* @private
*/
private function videoDisplay_bytesLoadedChangeHandler(event:LoadEvent):void
{
if (scrubBar)
updateScrubBar();
dispatchEvent(event);
}
/**
* @private
*/
private function videoDisplay_mediaPlayerStateChangeHandler(event:MediaPlayerStateChangeEvent):void
{
invalidateSkinState();
if (scrubBar)
updateScrubBar();
if (durationDisplay)
updateDuration();
if (currentTimeDisplay)
updateCurrentTime();
if (playPauseButton)
playPauseButton.selected = playing;
//trace("mediaPlayerStateChangeHandler " + event + " state = " + event.state + " playing = " + playing);
dispatchEvent(event);
}
/**
* @private
*/
private function videoDisplay_durationChangeHandler(event:TimeEvent):void
{
if (scrubBar)
updateScrubBar();
if (durationDisplay)
updateDuration();
dispatchEvent(event);
}
/**
* @private
*/
private function videoDisplay_volumeChangedHandler(event:Event):void
{
if (volumeBar)
{
volumeBar.value = volume;
volumeBar.muted = muted;
}
if (muteButton)
{
muteButton.muted = muted;
muteButton.volume = volume;
}
dispatchEvent(event);
}
/**
* @private
* Indicates whether we are in the full screen state or not.
* We use this when determining our current skin state.
*/
private var fullScreen:Boolean = false;
/**
* @private
* Holds a list of properties for the "video player state" that will
* be restored when going out of fullScreen mode.
*/
private var beforeFullScreenInfo:Object;
/**
* @private
* Timer, which waits for 3 seconds by default to hide the
* playback controls. If there's interaction by the user, then
* these playback controls are show again, and the timer will reset
* and start the countdown.
*/
private var fullScreenHideControlTimer:Timer;
/**
* @private
*/
private function fullScreenButton_clickHandler(event:MouseEvent):void
{
if (!fullScreen)
{
// check to make sure we can go into fullscreen mode
if (!systemManager.getTopLevelRoot())
return;
var screenBounds:Rectangle = getScreenBounds();
fullScreen = true;
// need it to go into full screen state for the skin
invalidateSkinState();
// keep track of pauseWhenHidden b/c we will set it to false temporarily
// so that the video does not pause when we reparent it to the top
// level application
var oldPauseWhenHidden:Boolean = pauseWhenHidden;
// let's get it off of our layout system so it doesn't interfere with
// the sizing and positioning. Then let's resize it to be
// the full size of our screen. Then let's position it off-screen so
// there are no other elements in the way.
beforeFullScreenInfo = {parent: this.parent,
x: this.x,
y: this.y,
explicitWidth: this.explicitWidth,
explicitHeight: this.explicitHeight,
percentWidth: this.percentWidth,
percentHeight: this.percentHeight,
isPopUp: this.isPopUp};
pauseWhenHidden = false;
if (!isPopUp)
{
// remove from old parent
if (parent is IVisualElementContainer)
{
var ivec:IVisualElementContainer = IVisualElementContainer(parent);
beforeFullScreenInfo.childIndex = ivec.getElementIndex(this);
ivec.removeElement(this);
}
else
{
beforeFullScreenInfo.childIndex = parent.getChildIndex(this);
parent.removeChild(this);
}
// add as a popup
PopUpManager.addPopUp(this, FlexGlobals.topLevelApplication as DisplayObject, false, null, moduleFactory);
}
// Resize the component to be the full screen of the stage.
// Push the component at (0,0). It should be on top of everything
// at this point because it was added as a popup
setLayoutBoundsSize(screenBounds.width, screenBounds.height, true);
// set the explicit width/height to make sure this value sticks regardless
// of any other code or layout passes. Calling setLayoutBoundsSize() before hand
// allows us to use postLayout width/height.
// Setting explictWidth/Height sets percentWidth/Height to NaN.
this.explicitWidth = width;
this.explicitHeight = height;
setLayoutBoundsPosition(0, 0, true);
// this is for video performance reasons, but sometimes the videoObject isn't there
// if the source is null
if (videoDisplay.videoObject)
{
beforeFullScreenInfo.smoothing = videoDisplay.videoObject.smoothing;
beforeFullScreenInfo.deblocking = videoDisplay.videoObject.deblocking;
videoDisplay.videoObject.smoothing = false;
videoDisplay.videoObject.deblocking = 0;
}
this.validateNow();
systemManager.stage.addEventListener(FullScreenEvent.FULL_SCREEN, fullScreenEventHandler);
// TODO (rfrishbe): Should we make this FULL_SCREEN_INTERACTIVE if in AIR?
systemManager.stage.displayState = StageDisplayState.FULL_SCREEN;
pauseWhenHidden = oldPauseWhenHidden;
var fullScreenHideControlsDelay:Number = getStyle("fullScreenHideControlsDelay");
if (fullScreenHideControlsDelay == 0)
{
playerControls.visible = false;
if (volumeBar)
volumeBar.closeDropDown(true);
}
else if (fullScreenHideControlsDelay < Infinity)
{
// start timer for detecting for mouse movements/clicks to hide the controls
fullScreenHideControlTimer = new Timer(fullScreenHideControlsDelay, 1);
fullScreenHideControlTimer.addEventListener(TimerEvent.TIMER_COMPLETE,
fullScreenHideControlTimer_timerCompleteHandler, false, 0, true);
// use stage or systemManager?
systemManager.getSandboxRoot().addEventListener(MouseEvent.MOUSE_DOWN, resetFullScreenHideControlTimer);
systemManager.getSandboxRoot().addEventListener(MouseEvent.MOUSE_MOVE, resetFullScreenHideControlTimer);
systemManager.getSandboxRoot().addEventListener(MouseEvent.MOUSE_WHEEL, resetFullScreenHideControlTimer);
// keyboard events don't happen when in fullScreen mode, but could be in fullScreen and interactive mode
systemManager.getSandboxRoot().addEventListener(KeyboardEvent.KEY_DOWN, resetFullScreenHideControlTimer);
fullScreenHideControlTimer.start();
}
}
else
{
systemManager.stage.displayState = StageDisplayState.NORMAL;
}
}
/**
* @private
* After waiting a certain time perdiod, we hide the controls if no
* user-interaction has occurred on-screen.
*/
private function fullScreenHideControlTimer_timerCompleteHandler(event:TimerEvent):void
{
playerControls.visible = false;
if (volumeBar)
volumeBar.closeDropDown(true);
}
/**
* @private
* Handles when mouse interaction happens, and we are in the fullscreen mode. This
* resets the fullScreenHideControlTimer.
*/
private function resetFullScreenHideControlTimer(event:Event):void
{
playerControls.visible = true;
if (fullScreenHideControlTimer)
{
fullScreenHideControlTimer.reset();
fullScreenHideControlTimer.start();
}
else
{
fullScreenHideControlTimer = new Timer(getStyle("fullScreenHideControlsDelay"), 1);
fullScreenHideControlTimer.addEventListener(TimerEvent.TIMER_COMPLETE,
fullScreenHideControlTimer_timerCompleteHandler, false, 0, true);
}
}
/**
* @private
* Handles when coming out the full screen mode
*/
private function fullScreenEventHandler(event:FullScreenEvent):void
{
// going in to full screen is handled by the
// fullScreenButton_clickHandler
if (event.fullScreen)
return;
// keep track of pauseWhenHidden b/c we will set it to false temporarily
// so that the video does not pause when we reparent it to the top
// level application
exitingFullScreenPauseWhenHidden = pauseWhenHidden;
pauseWhenHidden = false;
// set the fullScreen variable back to false and remove this event listener
fullScreen = false;
systemManager.stage.removeEventListener(FullScreenEvent.FULL_SCREEN, fullScreenEventHandler);
// remove the event listeners to hide the controls
systemManager.getSandboxRoot().removeEventListener(MouseEvent.MOUSE_DOWN, resetFullScreenHideControlTimer);
systemManager.getSandboxRoot().removeEventListener(MouseEvent.MOUSE_MOVE, resetFullScreenHideControlTimer);
systemManager.getSandboxRoot().removeEventListener(MouseEvent.MOUSE_WHEEL, resetFullScreenHideControlTimer);
systemManager.getSandboxRoot().removeEventListener(KeyboardEvent.KEY_DOWN, resetFullScreenHideControlTimer);
if (fullScreenHideControlTimer)
{
fullScreenHideControlTimer.stop();
fullScreenHideControlTimer = null;
}
// make the controls visible no matter what
playerControls.visible = true;
// reset it so we're re-included in the layout
this.x = beforeFullScreenInfo.x;
this.y = beforeFullScreenInfo.y;
this.explicitWidth = beforeFullScreenInfo.explicitWidth;
this.explicitHeight = beforeFullScreenInfo.explicitHeight;
this.percentWidth = beforeFullScreenInfo.percentWidth;
this.percentHeight = beforeFullScreenInfo.percentHeight;
// sometimes there's no video object currently or there might not've been a
// video object when we went in to fullScreen mode. There may be no videoObject
// if the source hasn't been set.
if (videoDisplay.videoObject && beforeFullScreenInfo.smoothing !== undefined)
{
videoDisplay.videoObject.smoothing = beforeFullScreenInfo.smoothing;
videoDisplay.videoObject.deblocking = beforeFullScreenInfo.deblocking;
}
if (!beforeFullScreenInfo.isPopUp)
{
// remove from top level application:
PopUpManager.removePopUp(this);
// add back to original parent
if (beforeFullScreenInfo.parent is IVisualElementContainer)
beforeFullScreenInfo.parent.addElementAt(this, beforeFullScreenInfo.childIndex);
else
beforeFullScreenInfo.parent.addChildAt(this, beforeFullScreenInfo.childIndex);
}
// want to update pauseWhenHidden, but can't do it here
// b/c the AIR window thinks it's invisible at this point
// if we're on a Mac (a bug), so let's just defer this check
// to commitProperties().
if (exitingFullScreenPauseWhenHidden)
{
// if we need to set it back to true
needsToUpdatePauseWhenHidden = true;
invalidateProperties();
}
beforeFullScreenInfo = null;
invalidateSkinState();
invalidateSize();
invalidateDisplayList();
}
/**
* @private
*/
private function playButton_clickHandler(event:MouseEvent):void
{
if (!playing)
play();
}
/**
* @private
*/
private function pauseButton_clickHandler(event:MouseEvent):void
{
pause();
}
/**
* @private
*/
private function stopButton_clickHandler(event:MouseEvent):void
{
stop();
}
/**
* @private
*/
private function playPauseButton_clickHandler(event:MouseEvent):void
{
if (playing)
pause();
else
play();
// need to synch up to what we've actually got because sometimes
// the play() didn't actually play() because there's no source
// or we're in an error state
playPauseButton.selected = playing;
}
/**
* @private
*/
private function muteButton_mutedChangeHandler(event:FlexEvent):void
{
muted = muteButton.muted;
}
/**
* @private
*/
private function volumeBar_changeHandler(event:Event):void
{
if (volume != volumeBar.value)
volume = volumeBar.value;
}
/**
* @private
*/
private function volumeBar_mutedChangeHandler(event:FlexEvent):void
{
if (muted != volumeBar.muted)
muted = volumeBar.muted;
}
/**
* @private
* When someone is holding the scrubBar, we don't want to update the
* range's value--for this time period, we'll let the user completely
* control the range.
*/
private var scrubBarMouseCaptured:Boolean;
/**
* @private
* We pause the video when dragging the thumb for the scrub bar. This
* stores whether we were paused or not.
*/
private var wasPlayingBeforeSeeking:Boolean;
/**
* @private
* We are in the process of changing the timestamp
*/
private var scrubBarChanging:Boolean;
/**
* @private
*/
private function scrubBar_changeStartHandler(event:Event):void
{
scrubBarChanging = true;
}
/**
* @private
*/
private function scrubBar_thumbPressHandler(event:TrackBaseEvent):void
{
scrubBarMouseCaptured = true;
if (playing)
{
pause();
wasPlayingBeforeSeeking = true;
}
}
/**
* @private
*/
private function scrubBar_thumbReleaseHandler(event:TrackBaseEvent):void
{
scrubBarMouseCaptured = false;
if (wasPlayingBeforeSeeking)
{
play();
wasPlayingBeforeSeeking = false;
}
}
/**
* @private
*/
private function scrubBar_changeHandler(event:Event):void
{
seek(scrubBar.value);
}
/**
* @private
*/
private function scrubBar_changeEndHandler(event:Event):void
{
scrubBarChanging = false;
}
}
}