1////////////////////////////////////////////////////////////////////////////////
2//
3//  ADOBE SYSTEMS INCORPORATED
4//  Copyright 2009 Adobe Systems Incorporated
5//  All Rights Reserved.
6//
7//  NOTICE: Adobe permits you to use, modify, and distribute this file
8//  in accordance with the terms of the license agreement accompanying it.
9//
10///////////////////////////////////////////////////////////////////////////////
11
12package spark.components
13{
14
15import flash.events.Event;
16import flash.geom.Point;
17import flash.geom.Rectangle;
18import flash.media.Video;
19
20import mx.core.IUIComponent;
21import mx.core.IVisualElement;
22import mx.core.UIComponent;
23import mx.core.mx_internal;
24import mx.events.FlexEvent;
25import mx.events.PropertyChangeEvent;
26import mx.resources.IResourceManager;
27import mx.resources.ResourceManager;
28
29
30import org.osmf.containers.MediaContainer;
31import org.osmf.elements.VideoElement;
32import org.osmf.events.AudioEvent;
33import org.osmf.events.DisplayObjectEvent;
34import org.osmf.events.LoadEvent;
35import org.osmf.events.MediaPlayerCapabilityChangeEvent;
36import org.osmf.events.MediaPlayerStateChangeEvent;
37import org.osmf.events.SeekEvent;
38import org.osmf.events.TimeEvent;
39import org.osmf.layout.HorizontalAlign;
40import org.osmf.layout.LayoutMetadata;
41import org.osmf.layout.ScaleMode;
42import org.osmf.layout.VerticalAlign;
43import org.osmf.media.DefaultMediaFactory;
44import org.osmf.media.MediaElement;
45import org.osmf.media.MediaFactory;
46import org.osmf.media.MediaFactoryItem;
47import org.osmf.media.MediaPlayer;
48import org.osmf.media.MediaPlayerState;
49import org.osmf.media.MediaResourceBase;
50import org.osmf.media.URLResource;
51import org.osmf.media.MediaType;
52import org.osmf.net.NetLoader;
53import org.osmf.net.DynamicStreamingItem;
54import org.osmf.net.rtmpstreaming.RTMPDynamicStreamingNetLoader;
55import org.osmf.net.DynamicStreamingResource;
56import org.osmf.net.FMSURL;
57import org.osmf.utils.OSMFStrings;
58import org.osmf.utils.URL;
59
60import spark.components.mediaClasses.DynamicStreamingVideoItem;
61import spark.components.mediaClasses.DynamicStreamingVideoSource;
62import spark.primitives.BitmapImage;
63
64use namespace mx_internal;
65
66//--------------------------------------
67//  Events
68//--------------------------------------
69
70/**
71 *  Dispatched when the data is received as a download operation progresses.
72 *  This event is only dispatched when playing a video by downloading it
73 *  directly from a server, typically by issuing an HTTP request.
74 *  It is not displatched when playing a video from a special media server,
75 *  such as Flash Media Server.
76 *
77 *  <p>This event may not be dispatched when the source is set to null or a playback
78 *  error occurs.</p>
79 *
80 *  @eventType org.osmf.events.LoadEvent.BYTES_LOADED_CHANGE
81 *
82 *  @langversion 3.0
83 *  @playerversion Flash 10
84 *  @playerversion AIR 1.0
85 *  @productversion Flex 4
86 */
87[Event(name="bytesLoadedChange",type="org.osmf.events.LoadEvent")]
88
89/**
90 *  Dispatched when the playhead reaches the duration for playable media.
91 *
92 *  @eventType org.osmf.events.TimeEvent.COMPLETE
93 *
94 *  @langversion 3.0
95 *  @playerversion Flash 10
96 *  @playerversion AIR 1.0
97 *  @productversion Flex 4
98 */
99[Event(name="complete", type="org.osmf.events.TimeEvent")]
100
101/**
102 *  Dispatched when the <code>currentTime</code> property of the MediaPlayer has changed.
103 *
104 *  <p>This event may not be dispatched when the source is set to null or a playback
105 *  error occurs.</p>
106 *
107 *  @eventType org.osmf.events.TimeEvent.CURRENT_TIME_CHANGE
108 *
109 *  @langversion 3.0
110 *  @playerversion Flash 10
111 *  @playerversion AIR 1.0
112 *  @productversion Flex 4
113 */
114[Event(name="currentTimeChange",type="org.osmf.events.TimeEvent")]
115
116/**
117 *  Dispatched when the <code>duration</code> property of the media has changed.
118 *
119 *  <p>This event may not be dispatched when the source is set to null or a playback
120 *  error occurs.</p>
121 *
122 *  @eventType org.osmf.events.TimeEvent.DURATION_CHANGE
123 *
124 *  @langversion 3.0
125 *  @playerversion Flash 10
126 *  @playerversion AIR 1.0
127 *  @productversion Flex 4
128 */
129[Event(name="durationChange", type="org.osmf.events.TimeEvent")]
130
131/**
132 *  Dispatched when the MediaPlayer's state has changed.
133 *
134 *  @eventType org.osmf.events.MediaPlayerStateChangeEvent.MEDIA_PLAYER_STATE_CHANGE
135 *
136 *  @langversion 3.0
137 *  @playerversion Flash 10
138 *  @playerversion AIR 1.0
139 *  @productversion Flex 4
140 */
141[Event(name="mediaPlayerStateChange", type="org.osmf.events.MediaPlayerStateChangeEvent")]
142
143//--------------------------------------
144//  Other metadata
145//--------------------------------------
146
147[DefaultProperty("source")]
148
149[ResourceBundle("osmf")]
150
151[IconFile("VideoDisplay.png")]
152
153/**
154 * Because this component does not define a skin for the mobile theme, Adobe
155 * recommends that you not use it in a mobile application. Alternatively, you
156 * can define your own mobile skin for the component. For more information,
157 * see <a href="http://help.adobe.com/en_US/flex/mobileapps/WS19f279b149e7481c698e85712b3011fe73-8000.html">Basics of mobile skinning</a>.
158 */
159[DiscouragedForProfile("mobileDevice")]
160
161/**
162 *  The VideoDisplay class is chromeless video player that supports
163 *  progressive download, multi-bitrate, and streaming video.
164 *
165 *  <p><code>VideoDisplay</code> is the chromeless version that does not support skinning.
166 *  It is useful when you do not want the user to interact with the control.</p>
167 *
168 *  <p><code>VideoPlayer</code> is the skinnable version.</p>
169 *
170 *  <p>The VideoDisplay control has the following default characteristics:</p>
171 *     <table class="innertable">
172 *        <tr>
173 *           <th>Characteristic</th>
174 *           <th>Description</th>
175 *        </tr>
176 *        <tr>
177 *           <td>Default size</td>
178 *           <td>0 pixels wide by 0 pixels high with no content,
179 *             and the width and height of the video with content</td>
180 *        </tr>
181 *        <tr>
182 *           <td>Minimum size</td>
183 *           <td>0</td>
184 *        </tr>
185 *        <tr>
186 *           <td>Maximum size</td>
187 *           <td>10000 pixels wide and 10000 pixels high</td>
188 *        </tr>
189 *     </table>
190 *
191 *  @mxml
192 *
193 *  <p>The <code>&lt;s:VideoDisplay&gt;</code> tag inherits all of the tag
194 *  attributes of its superclass and adds the following tag attributes:</p>
195 *
196 *  <pre>
197 *  &lt;s:VideoDisplay
198 *    <strong>Properties</strong>
199 *    autoDisplayFirstFrame="true"
200 *    autoPlay="true"
201 *    autoRewind="true"
202 *    loop="false"
203 *    muted="false"
204 *    pauseWhenHidden="true"
205 *    scaleMode="letterbox"
206 *    source=""
207 *    volume="1"
208 *
209 *    <strong>Events</strong>
210 *    bytesLoadedChange="<i>No default</i>"
211 *    complete="<i>No default</i>"
212 *    currentTimeChange="<i>No default</i>"
213 *    durationChange="<i>No default</i>"
214 *    mediaPlayerStateChange="<i>No default</i>"
215 *
216 *  /&gt;
217 *  </pre>
218 *
219 *  @see spark.components.VideoPlayer
220 *
221 *  @includeExample examples/VideoDisplayExample.mxml
222 *
223 *  @langversion 3.0
224 *  @playerversion Flash 10
225 *  @playerversion AIR 1.5
226 *  @productversion Flex 4
227 */
228public class VideoDisplay extends UIComponent
229{
230    include "../core/Version.as";
231
232    //--------------------------------------------------------------------------
233    //
234    //  Class methods
235    //
236    //--------------------------------------------------------------------------
237
238    /**
239     *  @private
240     *  Set as the OSMF.resourceBundleFunction and used to look up
241     *  strings so the OSMF RTEs are localized in Flex.
242     */
243    private static function getResourceString(resourceName:String,
244                                              args:Array = null):String
245    {
246        var resourceManager:IResourceManager = ResourceManager.getInstance();
247        return resourceManager.getString("osmf", resourceName, args);
248    }
249
250    /**
251     * Copied from OSMF ScaleModeUtils.getScaledSize. ScaleModeUtils became
252     * an internal OSMF class in OSMF 1.0 so it is copied here.
253     *
254     * Calculates the scaled size based on the scaling algorithm.
255     * The available width and height are the width and height of the container.
256     * The intrinsic width and height are the width and height of the content.
257     *
258     *  @langversion 3.0
259     *  @playerversion Flash 10
260     *  @playerversion AIR 1.5
261     *  @productversion OSMF 1.0
262     */
263    private static function getScaledSize
264        ( scaleMode:String
265          , availableWidth:Number, availableHeight:Number
266            , intrinsicWidth:Number, intrinsicHeight:Number
267        ):Point
268    {
269        var result:Point;
270
271        switch (scaleMode)
272        {
273            case ScaleMode.ZOOM:
274            case ScaleMode.LETTERBOX:
275
276                var availableRatio:Number
277                = availableWidth
278                / availableHeight;
279
280                var componentRatio:Number
281                = (intrinsicWidth || availableWidth)
282                / (intrinsicHeight || availableHeight);
283
284                if  (   (scaleMode == ScaleMode.ZOOM && componentRatio < availableRatio)
285                    ||  (scaleMode == ScaleMode.LETTERBOX && componentRatio > availableRatio)
286                )
287                {
288                    result
289                    = new Point
290                        ( availableWidth
291                            , availableWidth / componentRatio
292                        );
293                }
294                else
295                {
296                    result
297                    = new Point
298                        ( availableHeight * componentRatio
299                            , availableHeight
300                        );
301                }
302
303                break;
304
305            case ScaleMode.STRETCH:
306
307                result
308                = new Point
309                ( availableWidth
310                    , availableHeight
311                );
312                break;
313
314            case ScaleMode.NONE:
315
316                result
317                = new Point
318                ( intrinsicWidth    || availableWidth
319                    , intrinsicHeight   || availableHeight
320                );
321
322                break;
323        }
324
325        return result;
326    }
327
328    //--------------------------------------------------------------------------
329    //
330    //  Constructor
331    //
332    //--------------------------------------------------------------------------
333
334    /**
335     *  Constructor.
336     *
337     *  @langversion 3.0
338     *  @playerversion Flash 10
339     *  @playerversion AIR 1.5
340     *  @productversion Flex 4
341     */
342    public function VideoDisplay()
343    {
344        super();
345
346        // create the underlying MediaPlayer class first.
347        createUnderlyingVideoPlayer();
348
349        // added and removed event listeners to see whether we should
350        // start or stop the video
351        addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
352        addEventListener(Event.REMOVED_FROM_STAGE, removedFromStageHandler);
353
354        // Set the OSMF hook used for localizing runtime error messages.
355        // OSMF itself has English-only messages,
356        // but higher layers like Flex can provide localized versions.
357        OSMFStrings.resourceStringFunction = getResourceString;
358    }
359
360    //--------------------------------------------------------------------------
361    //
362    //  Variables
363    //
364    //--------------------------------------------------------------------------
365
366    /**
367     *  @private
368     *  This is the underlying VideoPlayer object. At some point in the
369     *  future, we may change to a new implementation.
370     */
371    mx_internal var videoPlayer:MediaPlayer;
372
373    /**
374     *  @private
375     *  This is the underlying container used to display
376     *  the underlying videoPlayer.
377     */
378    mx_internal var videoContainer:MediaContainer;
379
380    /**
381     *  @private
382     *  How the correct media elements are created based on the url of
383     *  the resource.
384     */
385    mx_internal var mediaFactory:MediaFactory;
386
387    /**
388     *  @private
389     *  Whether the video is on the display list or not
390     */
391    private var _isOnDisplayList:Boolean = false;
392
393    /**
394     *  @private
395     *  Whether the we should play the video when the video
396     *  becomes playable again (visible, on display list, and enabled).
397     *  This starts out as true, but when we pause the video is changePlayback(),
398     *  we set it to false.  Also, when a user action occurs, like pause() or play()
399     *  or stop() is called, we set it to false as well.
400     */
401    private var playTheVideoOnVisible:Boolean = true;
402
403    /**
404     *  @private
405     */
406    private var effectiveVisibility:Boolean = false;
407
408    /**
409     *  @private
410     */
411    private var effectiveVisibilityChanged:Boolean = false;
412
413    /**
414     *  @private
415     */
416    private var effectiveEnabled:Boolean = false;
417
418    /**
419     *  @private
420     */
421    private var effectiveEnabledChanged:Boolean = false;
422
423    /**
424     *  @private
425     *  We do different things in the source setter based on if we
426     *  are initialized or not.
427     */
428    private var initializedOnce:Boolean = false;
429
430    /**
431     *  @private
432     *  Keeps track of the muted property while loading up a
433     *  video because of autoDisplayFirstFrame.
434     */
435    private var beforeLoadMuted:Boolean;
436
437    /**
438     *  @private
439     *  Keeps track whether we are loading up the
440     *  video because of autoDisplayFirstFrame.
441     *
442     *  <p>In this case we are in "state1" of loading,
443     *  which means we are waiting for the READY
444     *  MediaPlayerStateChangeEvent and haven't done anything yet.</p>
445     */
446    private var inLoadingState1:Boolean;
447
448    /**
449     *  @private
450     *  Keeps track whether we are loading up the
451     *  video because of autoDisplayFirstFrame.
452     *
453     *  <p>In this case we are in "state2" of loading,
454     *  which means have set videoPlayer.displayObject.visible=false
455     *  and videoPlayer.muted=true.  We've also called play() and are
456     *  waiting for the DimensionChangeEvent.</p>
457     *
458     *  <p>Note: At this point, inLoadingState1 = true as well.</p>
459     */
460    private var inLoadingState2:Boolean;
461
462    /**
463     *  @private
464     *  Keeps track whether we are loading up the
465     *  video because of autoDisplayFirstFrame.
466     *
467     *  <p>In this case we are in "state3" of loading,
468     *  which means have received the DimensionChangeEvent and have called
469     *  pause() and seek(0).  We are currently waiting for the
470     *  SEEK_END event, at which point we will be completely loaded up.</p>
471     *
472     *  <p>Note: At this point, inLoadingState1 = inLoadingState2 = true.</p>
473     */
474    private var inLoadingState3:Boolean;
475
476    /**
477     *  @private
478     *
479     *  Old value of videoPlayer.currentTimeUpdateInterval. We store the old
480     *  value when we are taken off the stage and pauseWhenHidden is true
481     *  (removedFromStageHandler). We restore the old value when we are added
482     *  to the stage (addedToStageHandler). The value is used in setupSource()
483     *  to turn the timers back on if they were disabled. The value if also used
484     *  to check if we should turn off the timers in videoPlayer_seekChangeHandler
485     *  and videoPlayer_currentTimeChangeHandler.
486     *  This is done to keep this component from being pinned in memory by the timer
487     *  associated the currentTimeUpdateInterval property.
488     */
489    private var oldCurrentTimeUpdateInterval:Number = NaN;
490
491    /**
492     *  @private
493     *
494     *  Old value of videoPlayer.bytesLoadedUpdateInterval. We store the old
495     *  value when we are taken off the stage and pauseWhenHidden is true
496     *  (removedFromStageHandler). We restore the old value when we are added
497     *  to the stage (addedToStageHandler). The value is used in setupSource()
498     *  to turn the timers back on if they were disabled. The value if also used
499     *  to check if we should turn off the timers in videoPlayer_seekChangeHandler
500     *  and videoPlayer_currentTimeChangeHandler.
501     *  This is done to keep this component from being pinned in memory by the timer
502     *  associated the bytesLoadedUpdateInterval property.
503     */
504    private var oldBytesLoadedUpdateInterval:Number = NaN;
505
506    //--------------------------------------------------------------------------
507    //
508    //  Properties
509    //
510    //--------------------------------------------------------------------------
511
512    //----------------------------------
513    //  autoDisplayFirstFrame
514    //----------------------------------
515
516    /**
517     *  @private
518     */
519    private var _autoDisplayFirstFrame:Boolean = true;
520
521    [Inspectable(category="General", defaultValue="true")]
522
523    /**
524     *  If <code>autoPlay = false</code>, then
525     *  <code>autoDisplayFirstFrame</code> controls whether the video
526     *  is loaded when the <code>source</code> is set.
527     *  If <code>autoDisplayFirstFrame</code>
528     *  is set to <code>true</code>, then the first frame of the video is
529     *  loaded and the video is sized correctly.
530     *  If <code>autoDisplayFirstFrame</code> is set to <code>false</code>, then no
531     *  connection to the source is made, the first frame is not shown,
532     *  and the video's size is not determined until someone tries to play
533     *  the video.
534     *
535     *  <p>If <code>autoPlay = true</code>, then this flag is ignored.</p>
536     *
537     *  @default true
538     *
539     *  @langversion 3.0
540     *  @playerversion Flash 10
541     *  @playerversion AIR 1.5
542     *  @productversion Flex 4
543     */
544    public function get autoDisplayFirstFrame():Boolean
545    {
546        return _autoDisplayFirstFrame;
547    }
548
549    /**
550     * @private
551     */
552    public function set autoDisplayFirstFrame(value:Boolean):void
553    {
554        _autoDisplayFirstFrame = value;
555    }
556
557    //----------------------------------
558    //  autoPlay
559    //----------------------------------
560
561    /**
562     * @private
563     */
564    private var _autoPlay:Boolean = true;
565
566    [Inspectable(category="General", defaultValue="true")]
567
568    /**
569     *  Specifies whether the video starts playing immediately when the
570     *  <code>source</code> property is set.
571     *  If <code>true</code>, the video file immediately begins to buffer and
572     *  play.
573     *
574     *  <p>Even if <code>autoPlay</code> is set to <code>false</code>, Flex
575     *  starts loading the video after the <code>initialize()</code> method is
576     *  called.
577     *  For Flash Media Server, this means creating the stream and loading
578     *  the first frame to display.
579     *  In the case of an HTTP download, Flex starts downloading the stream
580     *  and shows the first frame.</p>
581     *
582     *  <p>If <code>playWhenHidden</code> is set to <code>false</code>, then
583     *  <code>autoPlay</code> also affects what happens when the video
584     *  comes back on stage and is enabled and visible.</p>
585     *
586     *  @default true
587     *
588     *  @langversion 3.0
589     *  @playerversion Flash 10
590     *  @playerversion AIR 1.5
591     *  @productversion Flex 4
592     */
593    public function get autoPlay():Boolean
594    {
595        return _autoPlay;
596    }
597
598    /**
599     * @private (setter)
600     */
601    public function set autoPlay(value:Boolean):void
602    {
603        if (autoPlay == value)
604            return;
605
606        _autoPlay = value;
607
608        // call changePlayback() but don't immediately play or pause
609        // based on this change to autoPlay
610        changePlayback(false, false);
611    }
612
613    //----------------------------------
614    //  autoRewind
615    //----------------------------------
616
617    [Inspectable(category="General", defaultValue="true")]
618
619    /**
620     *  Specifies whether the FLV file should rewind to the first frame
621     *  when play stops, either by calling the <code>stop()</code> method or by
622     *  reaching the end of the stream.
623     *
624     *  <p>This property has no effect for live streaming video.</p>
625     *
626     *  @default true
627     *
628     *  @langversion 3.0
629     *  @playerversion Flash 10
630     *  @playerversion AIR 1.5
631     *  @productversion Flex 4
632     */
633    public function get autoRewind():Boolean
634    {
635        return videoPlayer.autoRewind;
636    }
637
638    public function set autoRewind(value:Boolean):void
639    {
640        videoPlayer.autoRewind = value;
641    }
642
643    //----------------------------------
644    //  bytesLoaded
645    //----------------------------------
646
647    [Inspectable(Category="General", defaultValue="0")]
648    [Bindable("bytesLoadedChange")]
649    [Bindable("mediaPlayerStateChange")]
650
651    /**
652     *  The number of bytes of data that have been downloaded into the application.
653     *
654     *  @return The number of bytes of data that have been downloaded into the application.
655     *  @default 0
656     *
657     *  @langversion 3.0
658     *  @playerversion Flash 10
659     *  @playerversion AIR 1.5
660     *  @productversion Flex 4
661     */
662    public function get bytesLoaded():Number
663    {
664        return videoPlayer.bytesLoaded;
665    }
666
667    //----------------------------------
668    //  bytesTotal
669    //----------------------------------
670
671    [Inspectable(Category="General", defaultValue="0")]
672    [Bindable("mediaPlayerStateChange")]
673
674    /**
675     *  The total size in bytes of the data being downloaded into the application.
676     *
677     *  @return The total size in bytes of the data being downloaded into the application.
678     *  @default 0
679     *
680     *  @langversion 3.0
681     *  @playerversion Flash 10
682     *  @playerversion AIR 1.5
683     *  @productversion Flex 4
684     */
685    public function get bytesTotal():Number
686    {
687        return videoPlayer.bytesTotal;
688    }
689
690    //----------------------------------
691    //  currentTime
692    //----------------------------------
693
694    [Inspectable(Category="General", defaultValue="0")]
695    [Bindable("currentTimeChange")]
696    [Bindable("mediaPlayerStateChange")]
697
698    /**
699     *  Current time of the playhead, measured in seconds,
700     *  since the video starting playing.
701     *
702     *  @default 0
703     *
704     *  @langversion 3.0
705     *  @playerversion Flash 10
706     *  @playerversion AIR 1.5
707     *  @productversion Flex 4
708     */
709    public function get currentTime():Number
710    {
711        return videoPlayer.currentTime;
712    }
713
714    //----------------------------------
715    //  duration
716    //----------------------------------
717
718    [Inspectable(Category="General", defaultValue="0")]
719    [Bindable("durationChange")]
720    [Bindable("mediaPlayerStateChange")]
721
722    /**
723     *  Duration of the video's playback, in seconds
724     *
725     *  @return The total running time of the video in seconds
726     *  @default 0
727     *
728     *  @langversion 3.0
729     *  @playerversion Flash 10
730     *  @playerversion AIR 1.5
731     *  @productversion Flex 4
732     */
733    public function get duration():Number
734    {
735        return videoPlayer.duration;
736    }
737
738    //----------------------------------
739    //  loop
740    //----------------------------------
741
742    [Inspectable(Category="General", defaultValue="false")]
743
744    /**
745     *  Indicates whether the media should play again after playback has completed.
746     *  The <code>loop</code> property takes precedence over the <code>autoRewind</code>
747     *  property, so if loop is set to <code>true</code>, the <code>autoRewind</code>
748     *  property is ignored.
749     *
750     *  @default false
751     *
752     *  @langversion 3.0
753     *  @playerversion Flash 10
754     *  @playerversion AIR 1.5
755     *  @productversion Flex 4
756     */
757    public function get loop():Boolean
758    {
759        return videoPlayer.loop;
760    }
761
762    /**
763     *  @private
764     */
765    public function set loop(value:Boolean):void
766    {
767        if (loop == value)
768            return;
769
770        videoPlayer.loop = value;
771    }
772
773    //----------------------------------
774    //  mediaPlayerState
775    //----------------------------------
776
777    [Inspectable(category="General", defaultValue="uninitialized")]
778    [Bindable("mediaPlayerStateChange")]
779
780    /**
781     *  The current state of the video.  See
782     *  org.osmf.media.MediaPlayerState for available values.
783     *
784     *  @default uninitialized
785     *
786     *  @see org.osmf.media.MediaPlayerState
787     *
788     *  @langversion 3.0
789     *  @playerversion Flash 10
790     *  @playerversion AIR 1.5
791     *  @productversion Flex 4
792     */
793    public function get mediaPlayerState():String
794    {
795        return videoPlayer.state;
796    }
797
798    //----------------------------------
799    //  muted
800    //----------------------------------
801
802    [Inspectable(category="General", defaultValue="false")]
803    [Bindable("volumeChanged")]
804
805    /**
806     *  Set to <code>true</code> to mute the video, <code>false</code>
807     *  to unmute the video.
808     *
809     *  @langversion 3.0
810     *  @playerversion Flash 10
811     *  @playerversion AIR 1.5
812     *  @productversion Flex 4
813     */
814    public function get muted():Boolean
815    {
816        // if inLoadingState2, we've got to
817        // fake the muted value
818        if (inLoadingState2)
819            return beforeLoadMuted;
820
821        return videoPlayer.muted;
822    }
823
824    /**
825     *  @private
826     */
827    public function set muted(value:Boolean):void
828    {
829        if (muted == value)
830            return;
831
832        // if inLoadingState2, don't change muted...just fake it
833        if (inLoadingState2)
834        {
835            beforeLoadMuted = value;
836            return;
837        }
838
839        videoPlayer.muted = value;
840    }
841
842    //----------------------------------
843    //  pauseWhenHidden
844    //----------------------------------
845
846    /**
847     *  @private
848     *  Storage variable for pauseWhenHidden
849     */
850    private var _pauseWhenHidden:Boolean = true;
851
852    [Inspectable(category="General", defaultValue="true")]
853
854    /**
855     *  Controls whether the video continues to play when it is
856     *  "hidden".  The video is "hidden" when either <code>visible</code>
857     *  is set to <code>false</code> on it or one of its ancestors,
858     *  or when the video is taken off
859     *  of the display list.  If set to <code>true</code>, the video
860     *  pauses playback until the video is visible again.  If set to
861     *  <code>false</code> the video continues to play when it is hidden.
862     *
863     *  <p>If the video is disabled (or one of the video's parents are
864     *  disabled), the video pauses as well, but when it is re-enabled,
865     *  the video does not play again.  This behavior is not controlled through
866     *  <code>pauseWhenHidden</code>; it is defined in the VideoDisplay component.</p>
867     *
868     *  @default true
869     *
870     *  @langversion 3.0
871     *  @playerversion Flash 10
872     *  @playerversion AIR 1.5
873     *  @productversion Flex 4
874     */
875    public function get pauseWhenHidden():Boolean
876    {
877        return _pauseWhenHidden;
878    }
879
880    /**
881     *  @private
882     */
883    public function set pauseWhenHidden(value:Boolean):void
884    {
885        if (_pauseWhenHidden == value)
886            return;
887
888        _pauseWhenHidden = value;
889
890        if (_pauseWhenHidden)
891        {
892            addVisibilityListeners();
893            computeEffectiveVisibilityAndEnabled();
894        }
895        else
896        {
897            removeVisibilityListeners();
898        }
899
900        // call changePlayback().  If we're invisible or off the stage,
901        // setting this to true can pause the video.  However, setting it
902        // to false should have no immediate impact.
903        changePlayback(value, false);
904    }
905
906    //----------------------------------
907    //  playing
908    //----------------------------------
909
910    [Inspectable(category="General")]
911    [Bindable("mediaPlayerStateChange")]
912
913    /**
914     *  Contains <code>true</code> if the video is playing or is attempting to play.
915     *
916     *  <p>The video may not be currently playing, as it may be seeking
917     *  or buffering, but the video is attempting to play.</p>
918     *
919     *  @see #play()
920     *  @see #pause()
921     *  @see #stop()
922     *  @see #autoPlay
923     *
924     *  @langversion 3.0
925     *  @playerversion Flash 10
926     *  @playerversion AIR 1.5
927     *  @productversion Flex 4
928     */
929    public function get playing():Boolean
930    {
931        return videoPlayer.playing;
932    }
933
934    //----------------------------------
935    //  scaleMode
936    //----------------------------------
937
938    /**
939     *  @private
940     */
941    private var _scaleMode:String = ScaleMode.LETTERBOX;
942
943    [Inspectable(Category="General", enumeration="none,stretch,letterbox,zoom", defaultValue="letterbox")]
944
945    /**
946     *  The <code>scaleMode</code> property describes different ways of
947     *  sizing the video content.
948     *  You can set <code>scaleMode</code> to
949     *  <code>"none"</code>, <code>"stretch"</code>,
950     *  <code>"letterbox"</code>, or <code>"zoom"</code>.
951     *
952     *  <p>If no width, height, or constraints are specified on the component,
953     *  this property has no effect.</p>
954     *
955     *  @default "letterbox"
956     *
957     *  @see org.osmf.display.ScaleMode
958     *
959     *  @langversion 3.0
960     *  @playerversion Flash 10
961     *  @playerversion AIR 1.5
962     *  @productversion Flex 4
963     */
964    public function get scaleMode():String
965    {
966        return _scaleMode;
967    }
968
969    /**
970     *  @private
971     */
972    public function set scaleMode(value:String):void
973    {
974        if (scaleMode == value)
975            return;
976
977        switch(value)
978        {
979            case ScaleMode.NONE:
980                _scaleMode = ScaleMode.NONE;
981                break;
982            case ScaleMode.STRETCH:
983                _scaleMode = ScaleMode.STRETCH;
984                break;
985            case ScaleMode.LETTERBOX:
986                _scaleMode = ScaleMode.LETTERBOX;
987                break;
988            case ScaleMode.ZOOM:
989                _scaleMode = ScaleMode.ZOOM;
990                break;
991            default:
992                _scaleMode = ScaleMode.LETTERBOX;
993                break;
994        }
995
996        // set scaleMode on the videoElement object
997        if (videoPlayer.media)
998        {
999            var layout:LayoutMetadata = videoPlayer.media.
1000                                getMetadata(LayoutMetadata.LAYOUT_NAMESPACE) as LayoutMetadata;
1001            if (layout)
1002                layout.scaleMode = _scaleMode;
1003        }
1004
1005        invalidateSize();
1006        invalidateDisplayList();
1007    }
1008
1009    //----------------------------------
1010    //  source
1011    //----------------------------------
1012
1013    private var _source:Object;
1014    private var sourceChanged:Boolean;
1015
1016    [Inspectable(category="General", defaultValue="null")]
1017    [Bindable("sourceChanged")]
1018
1019    /**
1020     *  The video source.
1021     *
1022     *  <p>For progressive download, the source is just a path or URL pointing
1023     *  to the video file to play.</p>
1024     *
1025     *  <p>For streaming (recorded streaming, live streaming,
1026     *  or multi-bitrate streaming), the source property is a
1027     *  DynamicStreamingVideoSource object.  If you just want to play
1028     *  a recorded or live streaming video with no multi-bitrate support,
1029     *  you can just pass in a String URL pointing to the video
1030     *  stream.  However, if you do this, the streamType is assumed to be "any,"
1031     *  and you don't have as much control over the streaming as you would if
1032     *  you used the DynamicStreamingVideoSource object.</p>
1033     *
1034     *  <p>Note: Setting the source on a MediaPlayerStateChangeEvent.LOADING or a
1035     *  MediaPlayerStateChangeEvent.READY is not recommended if the source was
1036     *  previously set.  This could cause an infinite loop or an RTE.
1037     *  If you must do an operation like that, wait an additional frame to
1038     *  set the source.</p>
1039     *
1040     *  @see spark.components.mediaClasses.DynamicStreamingVideoSource
1041     *
1042     *  @langversion 3.0
1043     *  @playerversion Flash 10
1044     *  @playerversion AIR 1.5
1045     *  @productversion Flex 4
1046     */
1047    public function get source():Object
1048    {
1049        return _source;
1050    }
1051
1052    /**
1053     * @private (setter)
1054     */
1055    public function set source(value:Object):void
1056    {
1057        _source = value;
1058
1059        // if we haven't initialized, let's wait to set up the
1060        // source in commitProperties() as it is dependent on other
1061        // properties, like autoPlay and enabled, and those may not
1062        // be set yet, especially if they are set via MXML.
1063        // Otherwise, if we have initialized, let's just set up the
1064        // source immediately.  This way people can change the source
1065        // and immediately call methods like seek().
1066        if (!initializedOnce)
1067        {
1068            sourceChanged = true;
1069            invalidateProperties();
1070        }
1071        else
1072        {
1073            setUpSource();
1074        }
1075
1076        dispatchEvent(new Event("sourceChanged"));
1077    }
1078
1079    //----------------------------------
1080    //  thumbnailSource
1081    //----------------------------------
1082
1083    /**
1084     *  @private
1085     */
1086    private var _thumbnailSource:Object;
1087
1088    /**
1089     *  @private
1090     *  Group that holds the BitmapImage for the thumbnail
1091     */
1092    private var thumbnailGroup:Group;
1093
1094    /**
1095     *  @private
1096     *  BitmapImage for the thumbnail
1097     */
1098    private var thumbnailBitmapImage:BitmapImage;
1099
1100    [Inspectable(Category="General")]
1101
1102    /**
1103     *  @private
1104     *  Thumbnail source is an internal property used to replace the video with a thumbnail.
1105     *  This is for places where we just want to load in a placeholder object for the video
1106     *  and don't want to incur the extra load-time or memory of loading up the video.
1107     *
1108     *  <p>Thumbnail source can take any valid source that can be passed in to
1109     *  <code>spark.primitivies.BitmapImage#source</code>.</p>
1110     *
1111     *  @langversion 3.0
1112     *  @playerversion Flash 10
1113     *  @playerversion AIR 1.5
1114     *  @productversion Flex 4
1115     */
1116    mx_internal function get thumbnailSource():Object
1117    {
1118        return _thumbnailSource;
1119    }
1120
1121    /**
1122     *  @private
1123     */
1124    mx_internal function set thumbnailSource(value:Object):void
1125    {
1126        if (_thumbnailSource == value)
1127            return;
1128
1129        _thumbnailSource = value;
1130
1131        // if we haven't initialized, let's wait to set up the
1132        // source in commitProperties() as it is dependent on other
1133        // properties, like autoPlay and enabled, and those may not
1134        // be set yet, especially if they are set via MXML.
1135        // Otherwise, if we have initialized, let's just set up the
1136        // source immediately.  This way people can change the source
1137        // and immediately call methods like seek().
1138        if (!initializedOnce)
1139        {
1140            sourceChanged = true;
1141            invalidateProperties();
1142        }
1143        else
1144        {
1145            setUpThumbnailSource();
1146        }
1147    }
1148
1149    /**
1150     *  @private
1151     *  Sets up the thumbnail source for use.
1152     */
1153    private function setUpThumbnailSource():void
1154    {
1155        if (thumbnailSource)
1156        {
1157            // create thumbnail group if there isn't one
1158            if (!thumbnailGroup)
1159            {
1160                thumbnailBitmapImage = new BitmapImage();
1161                thumbnailBitmapImage.includeInLayout = false;
1162
1163                thumbnailGroup = new Group();
1164                // add thumbnailGroup to the display list first in case
1165                // bitmap needs to check its layoutDirection.
1166                addChild(thumbnailGroup);
1167                thumbnailGroup.clipAndEnableScrolling = true;
1168                thumbnailGroup.addElement(thumbnailBitmapImage);
1169            }
1170
1171            // if thumbnailGroup isn't on the display list, then add it.
1172            if (!this.contains(thumbnailGroup))
1173                addChild(thumbnailGroup);
1174
1175            thumbnailBitmapImage.source = thumbnailSource;
1176            invalidateSize();
1177            invalidateDisplayList();
1178        }
1179        else
1180        {
1181            if (thumbnailGroup)
1182            {
1183                // null out the source and remove the thumbnail group
1184                if (thumbnailBitmapImage)
1185                    thumbnailBitmapImage.source = null;
1186                if (this.contains(thumbnailGroup))
1187                    removeChild(thumbnailGroup);
1188                invalidateSize();
1189            }
1190        }
1191    }
1192
1193    //----------------------------------
1194    //  videoObject
1195    //----------------------------------
1196
1197    [Inspectable(category="General", defaultValue="null")]
1198
1199    /**
1200     *  The underlying flash player <code>flash.media.Video</code> object.
1201     *
1202     *  <p>If the source is <code>null</code>, then there may be no
1203     *  underlying <code>flash.media.Video object</code> yet.  In that
1204     *  case, <code>videoObject</code> returns <code>null</code>.</p>
1205     *
1206     *  @default null
1207     *
1208     *  @langversion 3.0
1209     *  @playerversion Flash 10
1210     *  @playerversion AIR 1.5
1211     *  @productversion Flex 4
1212     */
1213    public function get videoObject():Video
1214    {
1215        return videoPlayer.displayObject as Video;
1216    }
1217
1218    //----------------------------------
1219    //  volume
1220    //----------------------------------
1221
1222    [Inspectable(category="General", defaultValue="1.0", minValue="0.0", maxValue="1.0")]
1223    [Bindable("volumeChanged")]
1224
1225    /**
1226     *  The volume level, specified as a value between 0 and 1.
1227     *
1228     *  @default 1
1229     *
1230     *  @langversion 3.0
1231     *  @playerversion Flash 10
1232     *  @playerversion AIR 1.5
1233     *  @productversion Flex 4
1234     */
1235    public function get volume():Number
1236    {
1237        return videoPlayer.volume;
1238    }
1239
1240    /**
1241     *  @private
1242     */
1243    public function set volume(value:Number):void
1244    {
1245        if (volume == value)
1246            return;
1247
1248        videoPlayer.volume = value;
1249    }
1250
1251    //--------------------------------------------------------------------------
1252    //
1253    //  Overridden methods
1254    //
1255    //--------------------------------------------------------------------------
1256
1257    /**
1258     *  @private
1259     */
1260    override protected function commitProperties():void
1261    {
1262        super.commitProperties();
1263
1264        initializedOnce = true;
1265
1266        if (effectiveVisibilityChanged || effectiveEnabledChanged)
1267        {
1268            // if either visibility of enabled changed, re-compute them here
1269            computeEffectiveVisibilityAndEnabled();
1270
1271            // if visibility changed and we care about it, we can
1272            // cause a play or a pause depending on our visibility
1273            var causePause:Boolean = false;
1274            var causePlay:Boolean = false;
1275            if (effectiveVisibilityChanged && pauseWhenHidden)
1276            {
1277                causePause = !effectiveVisibility;
1278                causePlay = effectiveVisibility;
1279            }
1280
1281            // if enabled changed, we can only cause a pause.
1282            // Re-enabling a component doesn't cause a play.
1283            if (effectiveEnabledChanged)
1284            {
1285                if (!effectiveEnabled)
1286                    causePause = true;
1287            }
1288
1289            changePlayback(causePause, causePlay);
1290
1291            effectiveVisibilityChanged = false;
1292            effectiveEnabledChanged = false;
1293        }
1294
1295        if (sourceChanged)
1296        {
1297            sourceChanged = false;
1298
1299            if (thumbnailSource)
1300                setUpThumbnailSource();
1301            else
1302                setUpSource();
1303        }
1304    }
1305
1306    /**
1307     *  @private
1308     */
1309    override protected function measure() : void
1310    {
1311        super.measure();
1312
1313        var intrinsicWidth:Number;
1314        var intrinsicHeight:Number;
1315
1316        // if showing the thumbnail, just use the thumbnail's size
1317        if (thumbnailSource && thumbnailGroup)
1318        {
1319            intrinsicWidth = thumbnailBitmapImage.getPreferredBoundsWidth();
1320            intrinsicHeight = thumbnailBitmapImage.getPreferredBoundsHeight();
1321        }
1322        else
1323        {
1324            // If there is no media the width/height will be NaN.
1325            // Convert it to zero for our purposes.
1326            intrinsicWidth = videoPlayer.mediaWidth;
1327            if (isNaN(intrinsicWidth))
1328                intrinsicWidth = 0;
1329
1330            intrinsicHeight = videoPlayer.mediaHeight;
1331            if (isNaN(intrinsicHeight))
1332                intrinsicHeight = 0;
1333        }
1334
1335        measuredWidth = intrinsicWidth;
1336        measuredHeight = intrinsicHeight;
1337
1338        // Determine whether 'width' and 'height' have been set.
1339        var bExplicitWidth:Boolean = !isNaN(explicitWidth);
1340        var bExplicitHeight:Boolean = !isNaN(explicitHeight);
1341
1342        // If only one has been set, calculate the other based on aspect ratio.
1343        if (bExplicitWidth && !bExplicitHeight && intrinsicWidth > 0)
1344            measuredHeight = explicitWidth * intrinsicHeight / intrinsicWidth;
1345        else if (bExplicitHeight && !bExplicitWidth && intrinsicHeight > 0)
1346            measuredWidth = explicitHeight * intrinsicWidth / intrinsicHeight;
1347    }
1348
1349    /**
1350     *  @private
1351     */
1352    override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number) : void
1353    {
1354        super.updateDisplayList(unscaledWidth, unscaledHeight);
1355
1356        // if just showing the thumbnail, push this width/height in to the thumbnail
1357        // otherwise we'll push it in to the video object
1358        if (thumbnailSource && thumbnailGroup)
1359        {
1360            // get what the size of our image should be
1361            var newSize:Point = getScaledSize(scaleMode, unscaledWidth, unscaledHeight,
1362                thumbnailBitmapImage.getPreferredBoundsWidth(), thumbnailBitmapImage.getPreferredBoundsHeight());
1363
1364            // set the thumbnailGroup to be the size of the component.
1365            // set the bitmap image to be the size it should be according to OSMF
1366            thumbnailGroup.setLayoutBoundsSize(unscaledWidth, unscaledHeight);
1367            thumbnailBitmapImage.setLayoutBoundsSize(newSize.x, newSize.y);
1368
1369            // center the thumnail image within the thumbnail group.
1370            // if it's too big to fit, the thumbnail group will crop it
1371            thumbnailBitmapImage.x = (unscaledWidth - newSize.x)/2;
1372            thumbnailBitmapImage.y = (unscaledHeight - newSize.y)/2;
1373
1374            return;
1375        }
1376
1377        videoContainer.width = Math.floor(unscaledWidth);
1378        videoContainer.height = Math.floor(unscaledHeight);
1379
1380        // need to validate the gateway immediately--otherwise we may run out of synch
1381        // as they may wait a frame by default before validating (see SDK-24880)
1382        videoContainer.validateNow();
1383    }
1384
1385    //--------------------------------------------------------------------------
1386    //
1387    //  Methods
1388    //
1389    //--------------------------------------------------------------------------
1390
1391    /**
1392     *  Pauses playback without moving the playhead.
1393     *  If playback is already is paused or is stopped, this method has no
1394     *  effect.
1395     *
1396     *  <p>To start playback again, call the <code>play()</code> method.</p>
1397     *
1398     *  @langversion 3.0
1399     *  @playerversion Flash 10
1400     *  @playerversion AIR 1.5
1401     *  @productversion Flex 4
1402     */
1403    public function pause():void
1404    {
1405        // check to see if we can call methods on the video player object yet
1406        if (!videoPlayerResponsive())
1407            return;
1408
1409        playTheVideoOnVisible = false;
1410
1411        // if we're loading up, then we will pause automatically, so let's
1412        // not interrupt this process
1413        // if inLoadingState1 && pausable, then let loading state handle it
1414        // if inLoadingState1 && !pausable, then let the loading state handle it
1415        // if !inLoadingState1 && pausable, then just pause
1416        // if !inLoadingState1 && !pausable, then load (if needed to show first frame)
1417        if (!inLoadingState1 && videoPlayer.canPause)
1418            videoPlayer.pause();
1419        else if (!videoPlayer.canPause && autoDisplayFirstFrame)
1420            load();
1421    }
1422
1423    /**
1424     *  Causes the video to play.  Can be called while the video is
1425     *  paused, stopped, or while the video is already playing.
1426     *
1427     *  @langversion 3.0
1428     *  @playerversion Flash 10
1429     *  @playerversion AIR 1.5
1430     *  @productversion Flex 4
1431     */
1432    public function play():void
1433    {
1434        // check to see if we can call methods on the video player object yet
1435        if (!videoPlayerResponsive())
1436            return;
1437
1438        playTheVideoOnVisible = false;
1439
1440        // if we're loading up, use a special method to cancel the load
1441        // and to start playing again.  Otherwise, go ahead and play
1442        if (inLoadingState1)
1443            cancelLoadAndPlay();
1444        else if (videoPlayer.canPlay)
1445            videoPlayer.play();
1446    }
1447
1448    /**
1449     *  Seeks to given time in the video. If the video is playing,
1450     *  continue playing from that point. If the video is paused, seek to
1451     *  that point and remain paused. If the video is stopped, seek to
1452     *  that point and enters paused state.
1453     *  This method has no effect with live video streams.
1454     *
1455     *  <p>If time is less than 0 or NaN, throws exception. If time
1456     *  is past the end of the stream, or past the amount of file
1457     *  downloaded so far, then attempts to seek and, if it fails, it then recovers.</p>
1458     *
1459     *  <p>The <code>currentTime</code> property might not have the expected value
1460     *  immediately after you call <code>seek()</code>.
1461     *  For a progressive download,
1462     *  you can seek only to a keyframe; therefore, a seek takes you to the
1463     *  time of the first keyframe after the specified time.</p>
1464     *
1465     *  <p><strong>Note</strong>: When streaming, a seek always goes to the precise specified
1466     *  time even if the source FLV file doesn't have a keyframe there.</p>
1467     *
1468     *  <p>Seeking is asynchronous, so if you call the <code>seek()</code> method,
1469     *  <code>currentTime</code> does not update immediately. </p>
1470     *
1471     *  @param time The seek time, in seconds.
1472     *
1473     *  @langversion 3.0
1474     *  @playerversion Flash 10
1475     *  @playerversion AIR 1.5
1476     *  @productversion Flex 4
1477     */
1478    public function seek(time:Number):void
1479    {
1480        // check to see if we can call methods on the video player object yet
1481        if (!videoPlayerResponsive())
1482            return;
1483
1484        // TODO (rfrishbe): could handle what to do if this gets called when loading() better.
1485        // Need to store where we want to seek to.
1486        if (videoPlayer.canSeek)
1487            videoPlayer.seek(time);
1488    }
1489
1490    /**
1491     *  Stops video playback.  If <code>autoRewind</code> is set to
1492     *  <code>true</code>, rewinds to first frame.  If video is already
1493     *  stopped, has no effect.  To start playback again, call
1494     *  <code>play()</code>.
1495     *
1496     *  @see #autoRewind
1497     *  @see #play()
1498     *
1499     *  @langversion 3.0
1500     *  @playerversion Flash 10
1501     *  @playerversion AIR 1.5
1502     *  @productversion Flex 4
1503     */
1504    public function stop():void
1505    {
1506        // check to see if we can call methods on the video player object yet
1507        if (!videoPlayerResponsive())
1508            return;
1509
1510        playTheVideoOnVisible = false;
1511
1512        // if we're loading up, then we will stop automatically, so let's
1513        // not interrupt this process
1514        // if inLoadingState1 && pausable, then let loading state handle it
1515        // if inLoadingState1 && !pausable, then let the loading state handle it
1516        // if !inLoadingState1 && pausable, then just pause
1517        // if !inLoadingState1 && !pausable, then load (if needed to show first frame)
1518        if (!inLoadingState1 && videoPlayer.canPause)
1519            videoPlayer.stop();
1520        else if (!videoPlayer.canPause && autoDisplayFirstFrame)
1521            load();
1522    }
1523
1524    //--------------------------------------------------------------------------
1525    //
1526    //  Private Methods
1527    //
1528    //--------------------------------------------------------------------------
1529
1530    /**
1531     *  @private
1532     *  If the video player is responsive, then methods can be called on the underlying
1533     *  video player.
1534     */
1535    private function videoPlayerResponsive():Boolean
1536    {
1537        // can't call any methods before we've initialized
1538        if (!initializedOnce)
1539            return false;
1540
1541        // if displaying a thumbnail, no methods can be called b/c there's no video
1542        // loaded up
1543        if (thumbnailSource)
1544            return false;
1545
1546        // if the video player's in a bad state, we can't do anything
1547        if (videoPlayer.state == MediaPlayerState.PLAYBACK_ERROR ||
1548            videoPlayer.state == MediaPlayerState.UNINITIALIZED ||
1549            videoPlayer.state == MediaPlayerState.LOADING)
1550        {
1551            return false;
1552        }
1553
1554        // if no source, return false as well
1555        if (!source)
1556            return false;
1557
1558        // otherwise, we are in a good state and have a source, so let's go
1559        return true;
1560    }
1561
1562    /**
1563     *  @private
1564     */
1565    private function createUnderlyingVideoPlayer():void
1566    {
1567        // create new video player
1568        videoPlayer = new MediaPlayer();
1569        videoContainer = new MediaContainer();
1570        videoContainer.clipChildren = true;
1571
1572        mediaFactory = new DefaultMediaFactory();
1573
1574        // remove unsupport media types
1575        var unsupportedMediaTypes:Array = ["org.osmf.elements.video.dvr.dvrcast",
1576                                           "org.osmf.elements.image",
1577                                           "org.osmf.elements.swf"];
1578
1579        for each (var mediaType:String in unsupportedMediaTypes)
1580        {
1581            var mediaFactoryItem:MediaFactoryItem = mediaFactory.getItemById(mediaType);
1582            if (mediaFactoryItem)
1583                mediaFactory.removeItem(mediaFactoryItem);
1584        }
1585
1586        // internal events
1587        videoPlayer.addEventListener(DisplayObjectEvent.MEDIA_SIZE_CHANGE, videoPlayer_mediaSizeChangeHandler);
1588        videoPlayer.addEventListener(AudioEvent.VOLUME_CHANGE, videoPlayer_volumeChangeHandler);
1589        videoPlayer.addEventListener(AudioEvent.MUTED_CHANGE, videoPlayer_mutedChangeHandler);
1590
1591        // public events
1592        videoPlayer.addEventListener(MediaPlayerStateChangeEvent.MEDIA_PLAYER_STATE_CHANGE, videoPlayer_mediaPlayerStateChangeHandler);
1593        videoPlayer.addEventListener(TimeEvent.CURRENT_TIME_CHANGE, dispatchEvent);
1594        videoPlayer.addEventListener(LoadEvent.BYTES_LOADED_CHANGE, dispatchEvent);
1595        videoPlayer.addEventListener(TimeEvent.DURATION_CHANGE, videoPlayer_durationChangeHandler);
1596        videoPlayer.addEventListener(TimeEvent.COMPLETE, dispatchEvent);
1597
1598        addChild(videoContainer);
1599    }
1600
1601    /**
1602     *  @private
1603     *  Sets up the source for use.
1604     */
1605    private function setUpSource():void
1606    {
1607        // clean up any listeners from the old source, especially if we
1608        // are in the processing of loading that video file up
1609        cleanUpSource()
1610
1611        // if was playing a previous video, let's remove it now
1612        if (videoPlayer.media && videoContainer.containsMediaElement(videoPlayer.media))
1613        {
1614            videoContainer.removeMediaElement(videoPlayer.media);
1615        }
1616
1617        var videoElement:org.osmf.media.MediaElement = null;
1618
1619        // check for 4 cases: streaming video, progressive download,
1620        // an IMediaResource, or a VideoElement.
1621        // The latter 2 are undocumented but allowed for flexibility until we
1622        // can support OSMF better after they ship OSMF 1.0.  At that point, support
1623        // for a source as an IMediaResource or a VideoElement may be removed.
1624        if (source is DynamicStreamingVideoSource)
1625        {
1626            // the streaming video case.
1627            // build up a DynamicStreamingResource to pass in to OSMF
1628            var streamingSource:DynamicStreamingVideoSource = source as DynamicStreamingVideoSource;
1629            var dsr:DynamicStreamingResource;
1630
1631            // check for two cases for host: String and URL.
1632            // Technically, we only support URL, but we secretly allow
1633            // them to send in an OSMF URL or FMSURL here to help resolve any ambiguity
1634            // around serverName vs. streamName.
1635            if (streamingSource.host is String)
1636            {
1637                dsr = new DynamicStreamingResource(streamingSource.host as String,
1638                                                   streamingSource.streamType);
1639            }
1640            else if (streamingSource.host is URL)
1641            {
1642                dsr = new DynamicStreamingResource(URL(streamingSource.host).host,
1643                                                   streamingSource.streamType);
1644            }
1645
1646            if (dsr)
1647            {
1648                var n:int = streamingSource.streamItems.length;
1649                var item:DynamicStreamingVideoItem;
1650                var dsi:DynamicStreamingItem;
1651                var streamItems:Vector.<DynamicStreamingItem> = new Vector.<DynamicStreamingItem>(n);
1652
1653                for (var i:int = 0; i < n; i++)
1654                {
1655                    item = streamingSource.streamItems[i];
1656                    dsi = new DynamicStreamingItem(item.streamName, item.bitrate);
1657                    streamItems[i] = dsi;
1658                }
1659                dsr.streamItems = streamItems;
1660
1661                dsr.initialIndex = streamingSource.initialIndex;
1662
1663                // add video type metadata so if the URL is ambiguous, OSMF will
1664                // know what type of file we're trying to connect to
1665                dsr.mediaType = MediaType.VIDEO;
1666
1667                videoElement = new org.osmf.elements.VideoElement(dsr, new RTMPDynamicStreamingNetLoader());
1668            }
1669        }
1670        else if (source is String && source != "")
1671        {
1672            var urlResource:URLResource = new URLResource(source as String);
1673            videoElement = mediaFactory.createMediaElement(urlResource);
1674
1675            // If the url could not be resolved to a media element then try
1676            // telling osmf the media is a video and try again.
1677            // We do not specify the media type as video the first time,
1678            // so we can have the chance to play audio.
1679            if (videoElement == null)
1680            {
1681                urlResource.mediaType = MediaType.VIDEO;
1682                videoElement = mediaFactory.createMediaElement(urlResource);
1683            }
1684        }
1685        else if (source is MediaResourceBase)
1686        {
1687            videoElement = mediaFactory.createMediaElement(MediaResourceBase(source));
1688        }
1689        else if (source is org.osmf.elements.VideoElement)
1690        {
1691            videoElement = source as org.osmf.elements.VideoElement;
1692        }
1693
1694        // reset the visibilityPausedTheVideo flag
1695        playTheVideoOnVisible = true;
1696        // set up videoPlayer.autoPlay based on whether this.autoPlay is
1697        // set and whether we are visible and the other typical conditions.
1698        changePlayback(false, false);
1699
1700        // if we're not going to autoPlay (or couldn't autoPlay because
1701        // we're hidden or for some other reason), but we need to seek
1702        // to the first frame, then we have to do this on our own
1703        // by using our load() method.
1704        if (videoElement && (!autoPlay || !shouldBePlaying) && autoDisplayFirstFrame)
1705            load();
1706
1707        // set videoPlayer's element to the newly constructed VideoElement
1708        // set the newly constructed videoElement's gateway to be the videoGateway
1709        videoPlayer.media = videoElement;
1710
1711        if (videoElement)
1712        {
1713            // If we are loading a video, make sure the timers are restored in case
1714            // they had been disabled. The timers will be disabled again if we are
1715            // only loading the first frame.
1716            if (!isNaN(oldCurrentTimeUpdateInterval))
1717            {
1718                videoPlayer.currentTimeUpdateInterval = oldCurrentTimeUpdateInterval;
1719                videoPlayer.bytesLoadedUpdateInterval = oldBytesLoadedUpdateInterval;
1720            }
1721
1722            if (videoElement.getMetadata(LayoutMetadata.LAYOUT_NAMESPACE) == null)
1723            {
1724                var layout:LayoutMetadata = new LayoutMetadata();
1725                layout.scaleMode = scaleMode;
1726                layout.verticalAlign = VerticalAlign.MIDDLE;
1727                layout.horizontalAlign = HorizontalAlign.CENTER;
1728                layout.percentWidth = 100;
1729                layout.percentHeight = 100;
1730                videoElement.addMetadata(LayoutMetadata.LAYOUT_NAMESPACE, layout);
1731            }
1732
1733            if (videoElement && !videoContainer.containsMediaElement(videoElement) )
1734            {
1735                videoContainer.addMediaElement(videoElement);
1736            }
1737        }
1738        else
1739        {
1740            // if our source is null, let's invalidateSize() here.
1741            // if it's a bad source, we'll get a playbackError and invalidate
1742            // the size down there.  If it's a good source, we'll get a
1743            // dimensionChange event and invalidate the size in there.
1744            invalidateSize();
1745        }
1746    }
1747
1748    /**
1749     *  @private
1750     *  Our own internal load() method to handle the case
1751     *  where autoPlay = false and autoDisplayFirstFrame = true
1752     *  so that we can load up the video, figure out its size,
1753     *  and show the first frame
1754     */
1755    private function load():void
1756    {
1757        inLoadingState1 = true;
1758
1759        // wait until we can mute, play(), pause(), and seek() before doing anything.
1760        // We should be able to do all of these operations on the READY state change event.
1761        videoPlayer.addEventListener(MediaPlayerStateChangeEvent.MEDIA_PLAYER_STATE_CHANGE, videoPlayer_mediaPlayerStateChangeHandlerForLoading);
1762    }
1763
1764    //--------------------------------------------------------------------------
1765    //
1766    //  pauseWhenHidden: Event handlers and Private Methods
1767    //
1768    //--------------------------------------------------------------------------
1769
1770    /**
1771     *  @private
1772     *  Whether the video should be playing based on enabled,
1773     *  pauseWhenHidden, whether it's on the display list, and its
1774     *  effective visibility.
1775     */
1776    private function get shouldBePlaying():Boolean
1777    {
1778        // if disabled, return false
1779        if (!effectiveEnabled)
1780            return false;
1781
1782        // if we want to look at visibility, check to
1783        // see if we are on the display list and check out
1784        // effectiveVisibility (which looks up our parent chain
1785        // to make sure us and all of our ancestors are visible)
1786        if (pauseWhenHidden)
1787        {
1788            if (!_isOnDisplayList)
1789                return false;
1790
1791            if (!effectiveVisibility)
1792                return false;
1793        }
1794
1795        return true;
1796    }
1797
1798    /**
1799     *  @private
1800     *  This method will pause or play the video by looking at the state of
1801     *  the component and determining whether it should play or pause.  This
1802     *  method gets called when an important event occurs, such as
1803     *  the component being added/removed from the stage, the component's
1804     *  effective visibility changing, or when autoPlay is set.
1805     *
1806     *  <p>Only certain events are "action events" which can cause the video
1807     *  to pause or play immediately.  For example, when autoPlay is set to
1808     *  true/false, that shouldn't cause any immediate action, but changePlayback()
1809     *  is still called so that autoPlay can be set on the underlying media player.</p>
1810     *
1811     *  <p>Actions that can pause the video are:
1812     *  <ul>
1813     *      <li>Changes in effective enablement</li>
1814     *      <li>Changes in effective visibility</li>
1815     *      <li>Changes in staging (added or removed from display list)</li>
1816     *      <li>Setting pauseWhenHidden = true</li>
1817     *  </ul></p>
1818     *
1819     *  <p>Actions that can play the video are:
1820     *  <ul>
1821     *      <li>Changes in effective visibility</li>
1822     *      <li>Changes in staging (added or removed from display list)</li>
1823     *  </ul></p>
1824     *
1825     *  @param causePause Whether this action can cause a currently playing video to pause
1826     *  @param causePlay Whether this action can cause a currently paused video to play
1827     */
1828    private function changePlayback(causePause:Boolean, causePlay:Boolean):void
1829    {
1830        // if we shouldn't be playing, we pause the video.
1831        // if we come back up and should be playing, we will
1832        // start playing the video again if the video wasn't paused
1833        // by the user or developer and autoPlay is true.
1834        if (shouldBePlaying)
1835        {
1836            videoPlayer.autoPlay = autoPlay;
1837
1838            // only play the video if visibility caused it to pause
1839            // (instead of a user or developer calling video.pause()).
1840            // Also, only play if autoPlay is true.  Otherwise when
1841            // the visibility changes, we won't automatically
1842            // play the video
1843            if (causePlay && (playTheVideoOnVisible && autoPlay))
1844            {
1845                playTheVideoOnVisible = false;
1846
1847                // set autoplay and call play() if the
1848                // source has loaded up and it's playable
1849                if (inLoadingState1)
1850                    cancelLoadAndPlay();
1851                else if (videoPlayer.canPlay)
1852                    videoPlayer.play();
1853            }
1854        }
1855        else
1856        {
1857            // there are really three states the video player can
1858            // be in with respect to play vs. paused:
1859            // 1) playing
1860            // 2) paused
1861            // 3) loading
1862            // Here we are checking if we are playing or loading
1863            // and going to play soon (autoPlay = true)
1864            if (causePause && (playing || (videoPlayer.state == MediaPlayerState.LOADING && autoPlay)))
1865                playTheVideoOnVisible = true;
1866
1867            // always set autoPlay to false here and
1868            // if pausable, pause the video
1869            videoPlayer.autoPlay = false;
1870            if (causePause)
1871            {
1872                // if we're loading up, then we will pause automatically, so let's
1873                // not interrupt this process
1874                // if inLoadingState1 && pausable, then let loading state handle it
1875                // if inLoadingState1 && !pausable, then let the loading state handle it
1876                // if !inLoadingState1 && pausable, then just pause
1877                // if !inLoadingState1 && !pausable, then load (if needed to show first frame)
1878                if (!inLoadingState1 && videoPlayer.canPause)
1879                    videoPlayer.pause();
1880                else if (!videoPlayer.canPause && autoDisplayFirstFrame)
1881                    load();
1882            }
1883        }
1884    }
1885
1886    /**
1887     *  @private
1888     *  Cancels the load, no matter what state it's in, and starts to play().
1889     */
1890    private function cancelLoadAndPlay():void
1891    {
1892        if (inLoadingState1)
1893        {
1894            if (!inLoadingState2)
1895            {
1896                // first step
1897
1898                // Don't need to do anything but set inLoadingState1 = false (done down below).
1899                // This is handled in videoPlayer_mediaPlayerStateChangeHandlerForLoading which will still
1900                // be fired and will handle calling videoPlayer.play() without the rest of the loading
1901                // junk because inLoadingState1 = false now
1902            }
1903            else if (!inLoadingState3)
1904            {
1905                // second step
1906                videoPlayer.muted = beforeLoadMuted;
1907
1908                if (videoPlayer.displayObject)
1909                    videoPlayer.displayObject.visible = true;
1910
1911                videoPlayer.removeEventListener(TimeEvent.CURRENT_TIME_CHANGE, videoPlayer_currentTimeChangeHandler);
1912                videoPlayer.removeEventListener(MediaPlayerCapabilityChangeEvent.CAN_SEEK_CHANGE, videoPlayer_canSeekChangeHandler);
1913            }
1914            else
1915            {
1916                // third step
1917                videoPlayer.removeEventListener(SeekEvent.SEEKING_CHANGE, videoPlayer_seekChangeHandler);
1918                videoPlayer.muted = beforeLoadMuted;
1919                if (videoPlayer.displayObject)
1920                    videoPlayer.displayObject.visible = true;
1921
1922                // wasn't playing
1923                if (videoPlayer.canPlay)
1924                    videoPlayer.play();
1925            }
1926
1927            inLoadingState1 = false;
1928            inLoadingState2 = false;
1929            inLoadingState3 = false;
1930        }
1931    }
1932
1933    /**
1934     *  @private
1935     *  Cancels the load, no matter what state it's in.  This is used when changing the source.
1936     */
1937    private function cleanUpSource():void
1938    {
1939        // TODO (rfrishbe): very similar to cancelLoadAndPlay(). Should collapse it down.
1940
1941        // always remove listener as we could be out of loadState1 but still "loading to play"
1942        videoPlayer.removeEventListener(MediaPlayerStateChangeEvent.MEDIA_PLAYER_STATE_CHANGE, videoPlayer_mediaPlayerStateChangeHandlerForLoading);
1943
1944        if (inLoadingState1)
1945        {
1946            if (!inLoadingState2)
1947            {
1948                // first step
1949
1950                // Just need to remove event listeners as we did above
1951            }
1952            else if (!inLoadingState3)
1953            {
1954                // second step
1955                videoPlayer.muted = beforeLoadMuted;
1956                videoPlayer.displayObject.visible = true;
1957
1958                // going to call pause() now to stop immediately
1959                videoPlayer.pause();
1960            }
1961            else
1962            {
1963                // third step
1964                videoPlayer.removeEventListener(SeekEvent.SEEKING_CHANGE, videoPlayer_seekChangeHandler);
1965                videoPlayer.muted = beforeLoadMuted;
1966                videoPlayer.displayObject.visible = true;
1967
1968                // already called pause(), so don't do anything
1969            }
1970
1971            inLoadingState1 = false;
1972            inLoadingState2 = false;
1973            inLoadingState3 = false;
1974        }
1975    }
1976
1977    /**
1978     *  @private
1979     */
1980    private function addedToStageHandler(event:Event):void
1981    {
1982        _isOnDisplayList = true;
1983
1984        // add listeners to current parents to see if their visibility has changed
1985        if (pauseWhenHidden)
1986            addVisibilityListeners();
1987
1988        addEnabledListeners();
1989
1990        computeEffectiveVisibilityAndEnabled();
1991
1992        // When added to the stage, restore some videoPlayer timers that we had
1993        // disabled when we went offstage.
1994        if (!isNaN(oldCurrentTimeUpdateInterval))
1995        {
1996            videoPlayer.currentTimeUpdateInterval = oldCurrentTimeUpdateInterval;
1997            videoPlayer.bytesLoadedUpdateInterval = oldBytesLoadedUpdateInterval;
1998
1999            oldCurrentTimeUpdateInterval = NaN;
2000            oldBytesLoadedUpdateInterval = NaN;
2001        }
2002
2003        // being added to the display list will not pause the video, but
2004        // it may play the video if pauseWhenHidden = true
2005        changePlayback(false, pauseWhenHidden);
2006    }
2007
2008    /**
2009     *  @private
2010     */
2011    private function removedFromStageHandler(event:Event):void
2012    {
2013        _isOnDisplayList = false;
2014
2015        // remove listeners from old parents
2016        if (pauseWhenHidden)
2017            removeVisibilityListeners();
2018
2019        removeEnabledListeners();
2020
2021        // Stop the timers associated with these intervals when we go
2022        // offscreen so we are not pinned in memory. Save the old
2023        // values of the timers so we can restore them when we come
2024        // back on stage.
2025        if (pauseWhenHidden)
2026        {
2027            oldCurrentTimeUpdateInterval = videoPlayer.currentTimeUpdateInterval;
2028            oldBytesLoadedUpdateInterval = videoPlayer.bytesLoadedUpdateInterval;
2029            videoPlayer.currentTimeUpdateInterval = -1;
2030            videoPlayer.bytesLoadedUpdateInterval = -1;
2031        }
2032
2033        // being removed from the display list will pause the video if
2034        // pauseWhenHidden = true
2035        changePlayback(pauseWhenHidden, false);
2036    }
2037
2038    /**
2039     *  @private
2040     *  Add event listeners for SHOW and HIDE on all the ancestors up the parent chain.
2041     *  Adding weak event listeners just to be safe.
2042     */
2043    private function addVisibilityListeners():void
2044    {
2045        var current:IVisualElement = this;
2046        while (current)
2047        {
2048            // add visibility listeners to the parent
2049            current.addEventListener(FlexEvent.HIDE, visibilityChangedHandler, false, 0, true);
2050            current.addEventListener(FlexEvent.SHOW, visibilityChangedHandler, false, 0, true);
2051
2052            // add listeners to the design layer too
2053            if (current.designLayer)
2054            {
2055                current.designLayer.addEventListener("layerPropertyChange",
2056                    designLayer_layerPropertyChangeHandler, false, 0, true);
2057            }
2058
2059            current = current.parent as IVisualElement;
2060        }
2061    }
2062
2063    /**
2064     *  @private
2065     *  Add event listeners for "enabledChanged" event on all ancestors up the parent chain.
2066     *  Adding weak event listeners just to be safe.
2067     */
2068    private function addEnabledListeners():void
2069    {
2070        var current:IVisualElement = this;
2071        while (current)
2072        {
2073            current.addEventListener("enabledChanged", enabledChangedHandler, false, 0, true);
2074            current.addEventListener("enabledChanged", enabledChangedHandler, false, 0, true);
2075
2076            current = current.parent as IVisualElement;
2077        }
2078    }
2079
2080    /**
2081     *  @private
2082     *  Remove event listeners for SHOW and HIDE on all the ancestors up the parent chain.
2083     */
2084    private function removeVisibilityListeners():void
2085    {
2086        var current:IVisualElement = this;
2087        while (current)
2088        {
2089            current.removeEventListener(FlexEvent.HIDE, visibilityChangedHandler, false);
2090            current.removeEventListener(FlexEvent.SHOW, visibilityChangedHandler, false);
2091
2092            if (current.designLayer)
2093            {
2094                current.designLayer.removeEventListener("layerPropertyChange",
2095                    designLayer_layerPropertyChangeHandler, false);
2096            }
2097
2098            current = current.parent as IVisualElement;
2099        }
2100    }
2101
2102    /**
2103     *  @private
2104     *  Remove event listeners for "enabledChanged" event on all ancestors up the parent chain.
2105     */
2106    private function removeEnabledListeners():void
2107    {
2108        var current:IVisualElement = this;
2109        while (current)
2110        {
2111            current.removeEventListener("enabledChanged", enabledChangedHandler, false);
2112            current.removeEventListener("enabledChanged", enabledChangedHandler, false);
2113
2114            current = current.parent as IVisualElement;
2115        }
2116    }
2117
2118    /**
2119     *  @private
2120     *  Event call back whenever the visibility of us or one of our ancestors
2121     *  changes
2122     */
2123    private function visibilityChangedHandler(event:FlexEvent):void
2124    {
2125        effectiveVisibilityChanged = true;
2126        invalidateProperties();
2127    }
2128
2129    /**
2130     *  @private
2131     *  Event call back whenever the visibility of our designLayer or one of our parent's
2132     *  designLayers change.
2133     */
2134    private function designLayer_layerPropertyChangeHandler(event:PropertyChangeEvent):void
2135    {
2136        if (event.property == "effectiveVisibility")
2137        {
2138            effectiveVisibilityChanged = true;
2139            invalidateProperties();
2140        }
2141    }
2142
2143    /**
2144     *  @private
2145     *  Event call back whenever the enablement of us or one of our ancestors
2146     *  changes
2147     */
2148    private function enabledChangedHandler(event:Event):void
2149    {
2150        effectiveEnabledChanged = true;
2151        invalidateProperties();
2152    }
2153
2154    /**
2155     *  @private
2156     */
2157    private function computeEffectiveVisibilityAndEnabled():void
2158    {
2159        // start out with true visibility and enablement
2160        // then loop up parent-chain to see if any of them are false
2161        effectiveVisibility = true;
2162        effectiveEnabled = true;
2163        var current:IVisualElement = this;
2164
2165        while (current)
2166        {
2167            if (!current.visible ||
2168                (current.designLayer && !current.designLayer.effectiveVisibility))
2169            {
2170                effectiveVisibility = false;
2171                if (!effectiveEnabled)
2172                    break;
2173            }
2174
2175            if (current is IUIComponent && !IUIComponent(current).enabled)
2176            {
2177                effectiveEnabled = false;
2178                if (!effectiveVisibility)
2179                    break;
2180            }
2181
2182            current = current.parent as IVisualElement;
2183        }
2184    }
2185
2186    //--------------------------------------------------------------------------
2187    //
2188    //  Event handlers
2189    //
2190    //--------------------------------------------------------------------------
2191
2192    /**
2193     *  @private
2194     */
2195    private function videoPlayer_volumeChangeHandler(event:AudioEvent):void
2196    {
2197        dispatchEvent(new Event("volumeChanged"));
2198    }
2199
2200    /**
2201     *  @private
2202     */
2203    private function videoPlayer_mutedChangeHandler(event:AudioEvent):void
2204    {
2205        dispatchEvent(new Event("volumeChanged"));
2206    }
2207
2208    /**
2209     *  @private
2210     *  Event handler for mediaPlayerStateChange event.
2211     */
2212    private function videoPlayer_mediaPlayerStateChangeHandler(event:MediaPlayerStateChangeEvent):void
2213    {
2214        // if the event change caused us to go in to a state where
2215        // nothing is loaded up and we've no chance of getting a
2216        // dimensionChangeEvent, then let's invalidate our size here
2217        if (event.state == MediaPlayerState.PLAYBACK_ERROR)
2218            invalidateSize();
2219
2220        // this is a public event, so let's re-dispatch it
2221        dispatchEvent(event);
2222    }
2223
2224    /**
2225     *  @private
2226     *  Event handler for mediaPlayerStateChange event--used only
2227     *  when trying to load up the video without playing it.
2228     */
2229    private function videoPlayer_mediaPlayerStateChangeHandlerForLoading(event:MediaPlayerStateChangeEvent):void
2230    {
2231        // only come in here when we want to load the video without playing it.
2232        //trace("videoPlayer_mediaPlayerStateChangeHandlerForLoading: mediaPlayerState = " + event.state);
2233
2234        // wait until we are ready so that we can set mute, play, pause, and seek
2235        if (event.state == MediaPlayerState.READY)
2236        {
2237            // now that we are loading up, let's remove the event listener:
2238            videoPlayer.removeEventListener(MediaPlayerStateChangeEvent.MEDIA_PLAYER_STATE_CHANGE, videoPlayer_mediaPlayerStateChangeHandlerForLoading);
2239
2240            // if we are already playing() for some reason because someone called play(), then
2241            // we don't need to do anything.
2242            if (videoPlayer.playing)
2243                return;
2244
2245            // if this load wasn't cancelled, then we'll do the load stuff.
2246            // otherwise, we'll just cause play().
2247            if (inLoadingState1)
2248            {
2249                //trace("videoPlayer_mediaPlayerStateChangeHandlerForLoading: inLoadingState1");
2250
2251                beforeLoadMuted = videoPlayer.muted;
2252                videoPlayer.muted = true;
2253
2254                if (videoPlayer.displayObject)
2255                    videoPlayer.displayObject.visible = false;
2256
2257                inLoadingState2 = true;
2258            }
2259
2260            // call play(), here, then wait to call pause() and seek(0) in the
2261            // mediaSizeChangeHandler
2262            //trace("videoPlayer_mediaPlayerStateChangeHandlerForLoading: call videoPlayer.play()");
2263            videoPlayer.play();
2264        }
2265    }
2266
2267    /**
2268     *  @private
2269     */
2270    private function videoPlayer_mediaSizeChangeHandler(event:DisplayObjectEvent):void
2271    {
2272        //trace("videoPlayer_mediaSizeChangeHandler");
2273        invalidateSize();
2274
2275        // if we're loading up the video, then let's finish the load in here
2276        if (inLoadingState2)
2277        {
2278            //trace("videoPlayer_mediaSizeChangeHandler: inLoadingState2");
2279
2280            if (videoPlayer.canSeek && videoPlayer.canSeekTo(0))
2281            {
2282                //trace("videoPlayer_mediaSizeChangeHandler: canSeek to first frame");
2283                inLoadingState3 = true;
2284
2285                // Don't call pause and seek inside this handler because OSMF is
2286                // not expecting us to change its HTTPStreamingState value in
2287                // HTTPNetStream.onMainTimer as a result of dispatching this
2288                // event (see SDK-27028).
2289                callLater(pauseAndSeekCallBack);
2290            }
2291            else if (duration < 0)
2292            {
2293                // Work around for negative durations - FM-1009
2294                // We want to seek to the first frame but we can't because the
2295                // duration of the video is reported as negative. As a work around,
2296                // listen for the first time change event and then pause the video.
2297                //trace("videoPlayer_mediaSizeChangeHandler: negative duration - wait for first current time change event");
2298                videoPlayer.addEventListener(TimeEvent.CURRENT_TIME_CHANGE, videoPlayer_currentTimeChangeHandler);
2299            }
2300            else
2301            {
2302                //trace("videoPlayer_mediaSizeChangeHandler: waiting for media to become seekable");
2303
2304                // wait for the media to become seekable.
2305                videoPlayer.addEventListener(MediaPlayerCapabilityChangeEvent.CAN_SEEK_CHANGE, videoPlayer_canSeekChangeHandler);
2306            }
2307        }
2308    }
2309
2310    private function pauseAndSeekCallBack():void
2311    {
2312        // the seek(0) is asynchronous so let's add an event listener to see when it's finsished:
2313        videoPlayer.addEventListener(SeekEvent.SEEKING_CHANGE, videoPlayer_seekChangeHandler);
2314
2315        // called play(), now call pause() and seek(0);
2316        videoPlayer.pause();
2317        videoPlayer.seek(0);
2318
2319    }
2320
2321    /**
2322     *  @private
2323     *  Wait until the media is seekable before we call pause() and seek().
2324     */
2325    private function videoPlayer_canSeekChangeHandler(event:Event):void
2326    {
2327        //trace("videoPlayer_canSeekChangeHandler: seeking = " + videoPlayer.canSeek);
2328
2329        videoPlayer.removeEventListener(MediaPlayerCapabilityChangeEvent.CAN_SEEK_CHANGE, videoPlayer_canSeekChangeHandler);
2330
2331        if (inLoadingState2)
2332        {
2333            if (videoPlayer.canSeek && videoPlayer.canSeekTo(0))
2334            {
2335                inLoadingState3 = true;
2336
2337                // Don't call pause and seek inside this handler because OSMF is
2338                // not expecting us to change its HTTPStreamingState value in
2339                // HTTPNetStream.onMainTimer as a result of dispatching this
2340                // event (see SDK-27028).
2341                callLater(pauseAndSeekCallBack);
2342            }
2343        }
2344    }
2345
2346    /**
2347     *  @private
2348     *  Event handler for seekEnd events.  We only use this
2349     *  when trying to load up the video without playing it.
2350     *  This will be called after the video has loaded up and
2351     *  we have finished seeking back to the first frame.
2352     */
2353    private function videoPlayer_seekChangeHandler(event:SeekEvent):void
2354    {
2355        if (!event.seeking)
2356        {
2357            inLoadingState1 = false;
2358            inLoadingState2 = false;
2359            inLoadingState3 = false;
2360
2361            videoPlayer.removeEventListener(SeekEvent.SEEKING_CHANGE, videoPlayer_seekChangeHandler);
2362            videoPlayer.muted = beforeLoadMuted;
2363            if (videoPlayer.displayObject)
2364                videoPlayer.displayObject.visible = true;
2365
2366            // Disable the TimeEvents again that we had
2367            // enabled for loading a video while offstage.
2368            if (!isNaN(oldCurrentTimeUpdateInterval))
2369            {
2370                videoPlayer.currentTimeUpdateInterval = -1;
2371                videoPlayer.bytesLoadedUpdateInterval = -1;
2372            }
2373        }
2374    }
2375
2376
2377    /**
2378     *  @private
2379     *
2380     *  Work around for negative durations - see FM-1009.
2381     *  See want to seek to the first frame but can't because
2382     *  the video has a negative duration. So we listen to the
2383     *  current time. When we get a time change so we must be at
2384     *  least the first frame so pause the video now and clean
2385     *  up the load state variables.
2386     */
2387    private function videoPlayer_currentTimeChangeHandler(event:TimeEvent):void
2388    {
2389        //trace("videoPlayer_currentTimeChangeHandler: time = " + event.time);
2390
2391        videoPlayer.removeEventListener(TimeEvent.CURRENT_TIME_CHANGE, videoPlayer_currentTimeChangeHandler);
2392        videoPlayer.removeEventListener(MediaPlayerCapabilityChangeEvent.CAN_SEEK_CHANGE, videoPlayer_canSeekChangeHandler);
2393
2394        videoPlayer.pause();
2395        videoPlayer.muted = beforeLoadMuted;
2396
2397        if (videoPlayer.displayObject)
2398            videoPlayer.displayObject.visible = true;
2399
2400        inLoadingState1 = false;
2401        inLoadingState2 = false;
2402        inLoadingState3 = false;
2403
2404        // Disable the TimeEvents again that we had
2405        // enabled for loading a video while offstage.
2406        if (!isNaN(oldCurrentTimeUpdateInterval))
2407        {
2408            videoPlayer.currentTimeUpdateInterval = -1;
2409            videoPlayer.bytesLoadedUpdateInterval = -1;
2410        }
2411    }
2412
2413    /**
2414     *  @private
2415     *
2416     *  Work around for negative durations - see FM-1009.
2417     *
2418     *  If we get a duration event that is negative while in
2419     *  inLoadingState2 is true, then listen for the first time
2420     *  change event so we can pause the video.
2421     */
2422    private function videoPlayer_durationChangeHandler(event:TimeEvent):void
2423    {
2424        //trace("videoPlayer_durationChangeHandler: time = " + event.time);
2425        dispatchEvent(event);
2426
2427        if (inLoadingState2)
2428        {
2429            if (event.time < 0)
2430            {
2431                // Work around for negative durations - FM-1009
2432                // We want to seek to the first frame but we can't because the
2433                // duration of the video is reported as negative. As a work around,
2434                // listen for the first time change event and then pause the video.
2435                //trace("videoPlayer_durationChangeHandler: negative duration - wait for first current time change event");
2436                videoPlayer.addEventListener(TimeEvent.CURRENT_TIME_CHANGE, videoPlayer_currentTimeChangeHandler);
2437            }
2438        }
2439    }
2440}
2441}
2442