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><s:VideoDisplay></code> tag inherits all of the tag 194 * attributes of its superclass and adds the following tag attributes:</p> 195 * 196 * <pre> 197 * <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 * /> 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