All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scaffold.libs_as.feathers.media.VideoPlayer.as Maven / Gradle / Ivy

/*
Feathers
Copyright 2012-2015 Bowler Hat LLC. All Rights Reserved.

This program is free software. You can redistribute and/or modify it in
accordance with the terms of the accompanying license agreement.
*/
package feathers.media
{
	import feathers.controls.LayoutGroup;
	import feathers.core.PopUpManager;
	import feathers.events.FeathersEventType;
	import feathers.events.MediaPlayerEventType;
	import feathers.skins.IStyleProvider;
	import feathers.utils.display.stageToStarling;

	import flash.display.Stage;
	import flash.display.StageDisplayState;
	import flash.errors.IllegalOperationError;
	import flash.events.IOErrorEvent;
	import flash.events.NetStatusEvent;
	import flash.media.SoundTransform;
	import flash.net.NetConnection;
	import flash.net.NetStream;

	import starling.core.Starling;
	import starling.display.DisplayObject;
	import starling.events.Event;
	import starling.rendering.Painter;
	import starling.textures.Texture;

	/**
	 * Dispatched when the original native width or height of the video content
	 * is calculated.
	 *
	 * 

The properties of the event object have the following values:

* * * * * * *
PropertyValue
bubblesfalse
currentTargetThe Object that defines the * event listener that handles the event. For example, if you use * myButton.addEventListener() to register an event listener, * myButton is the value of the currentTarget.
datanull
targetThe Object that dispatched the event; * it is not always the Object listening for the event. Use the * currentTarget property to always access the Object * listening for the event.
* * @see #nativeWidth * @see #nativeHeight * * @eventType feathers.events.MediaPlayerEventType.DIMENSIONS_CHANGE */ [Event(name="dimensionsChange",type="starling.events.Event")] /** * Dispatched when the media player changes to the full-screen display mode * or back to the normal display mode. The value of the * isFullScreen property indicates if the media player is * displayed in full screen mode or normally. * *

The properties of the event object have the following values:

* * * * * * *
PropertyValue
bubblesfalse
currentTargetThe Object that defines the * event listener that handles the event. For example, if you use * myButton.addEventListener() to register an event listener, * myButton is the value of the currentTarget.
datanull
targetThe Object that dispatched the event; * it is not always the Object listening for the event. Use the * currentTarget property to always access the Object * listening for the event.
* * @see #isFullScreen * @see #toggleFullScreen() * * @eventType feathers.events.MediaPlayerEventType.DISPLAY_STATE_CHANGE */ [Event(name="displayStateChange",type="starling.events.Event")] /** * Dispatched when the media player's sound transform changes. * *

The properties of the event object have the following values:

* * * * * * *
PropertyValue
bubblesfalse
currentTargetThe Object that defines the * event listener that handles the event. For example, if you use * myButton.addEventListener() to register an event listener, * myButton is the value of the currentTarget.
datanull
targetThe Object that dispatched the event; * it is not always the Object listening for the event. Use the * currentTarget property to always access the Object * listening for the event.
* * @see #soundTransform * * @eventType feathers.events.MediaPlayerEventType.SOUND_TRANSFORM_CHANGE */ [Event(name="soundTransformChange",type="starling.events.Event")] /** * Dispatched when the video metadata becomes available with the * onMetaData callback from the NetStream instance. * *

The properties of the event object have the following values:

* * * * * * *
PropertyValue
bubblesfalse
currentTargetThe Object that defines the * event listener that handles the event. For example, if you use * myButton.addEventListener() to register an event listener, * myButton is the value of the currentTarget.
dataThe metadata object passed with the * onMetaData callback from the NetStream.
targetThe Object that dispatched the event; * it is not always the Object listening for the event. Use the * currentTarget property to always access the Object * listening for the event.
* * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetStream.html#event:onMetaData flash.net.NetStream onMetaData callback * * @eventType feathers.events.MediaPlayerEventType.METADATA_RECEIVED */ [Event(name="metadataReceived",type="starling.events.Event")] /** * Dispatched when the video texture is ready to be rendered. Indicates that * the texture property will return a * starling.textures.Texture that may be displayed in an * ImageLoader or another component. * *

The properties of the event object have the following values:

* * * * * * *
PropertyValue
bubblesfalse
currentTargetThe Object that defines the * event listener that handles the event. For example, if you use * myButton.addEventListener() to register an event listener, * myButton is the value of the currentTarget.
datanull
targetThe Object that dispatched the event; * it is not always the Object listening for the event. Use the * currentTarget property to always access the Object * listening for the event.
* * @see #texture * * @eventType starling.events.Event.READY */ [Event(name="ready",type="starling.events.Event")] /** * Dispatched when the video texture is no longer valid. Indicates that * the texture property will return a null value, * and that that the previous texture has been disposed and should not be * rendered any more. * *

The properties of the event object have the following values:

* * * * * * *
PropertyValue
bubblesfalse
currentTargetThe Object that defines the * event listener that handles the event. For example, if you use * myButton.addEventListener() to register an event listener, * myButton is the value of the currentTarget.
datanull
targetThe Object that dispatched the event; * it is not always the Object listening for the event. Use the * currentTarget property to always access the Object * listening for the event.
* * @see #texture * * @eventType feathers.events.FeathersEventType.CLEAR */ [Event(name="clear",type="starling.events.Event")] /** * Dispatched periodically when a media player's content is loading to * indicate the current progress. The bytesLoaded and * bytesTotal properties may be accessed to determine the * exact number of bytes loaded. * *

The properties of the event object have the following values:

* * * * * * *
PropertyValue
bubblesfalse
currentTargetThe Object that defines the * event listener that handles the event. For example, if you use * myButton.addEventListener() to register an event listener, * myButton is the value of the currentTarget.
dataA numeric value between 0 * and 1 that indicates how much of the media has loaded so far.
targetThe Object that dispatched the event; * it is not always the Object listening for the event. Use the * currentTarget property to always access the Object * listening for the event.
* * @see #bytesLoaded * @see #bytesTotal * * @eventType feathers.events.MediaPlayerEventType.LOAD_PROGRESS */ [Event(name="loadProgress",type="starling.events.Event")] /** * Dispatched when the flash.net.NetStream object dispatches * flash.events.IOErrorEvent.IO_ERROR. * *

The properties of the event object have the following values:

* * * * * * *
PropertyValue
bubblesfalse
currentTargetThe Object that defines the * event listener that handles the event. For example, if you use * myButton.addEventListener() to register an event listener, * myButton is the value of the currentTarget.
dataThe flash.events.IOErrorEvent * dispatched by the flash.net.NetStream.
targetThe Object that dispatched the event; * it is not always the Object listening for the event. Use the * currentTarget property to always access the Object * listening for the event.
* * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetStream.html#event:ioError flash.net.NetStream: flash.events.IOErrorEvent.IO_ERROR * * @eventType starling.events.Event.IO_ERROR */ [Event(name="ioError",type="starling.events.Event")] /** * Controls playback of video with a flash.net.NetStream object. * *

Beta Component: This is a new component, and its APIs * may need some changes between now and the next version of Feathers to * account for overlooked requirements or other issues. Upgrading to future * versions of Feathers may involve manual changes to your code that uses * this component. The * Feathers deprecation policy * will not go into effect until this component's status is upgraded from * beta to stable.

* * @see ../../../help/video-player.html How to use the Feathers VideoPlayer component */ public class VideoPlayer extends BaseTimedMediaPlayer implements IVideoPlayer, IProgressiveMediaPlayer { /** * @private */ protected static const NET_STATUS_CODE_NETSTREAM_PLAY_START:String = "NetStream.Play.Start"; /** * @private */ protected static const NET_STATUS_CODE_NETSTREAM_PLAY_STOP:String = "NetStream.Play.Stop"; /** * @private */ protected static const NET_STATUS_CODE_NETSTREAM_PLAY_STREAMNOTFOUND:String = "NetStream.Play.StreamNotFound"; /** * @private */ protected static const NO_VIDEO_SOURCE_PLAY_ERROR:String = "Cannot play media when videoSource property has not been set."; /** * @private */ protected static const NO_VIDEO_SOURCE_PAUSE_ERROR:String = "Cannot pause media when videoSource property has not been set."; /** * @private */ protected static const NO_VIDEO_SOURCE_SEEK_ERROR:String = "Cannot seek media when videoSource property has not been set."; /** * The default IStyleProvider for all * VideoPlayer components. * * @default null * @see feathers.core.FeathersControl#styleProvider */ public static var globalStyleProvider:IStyleProvider; /** * @private */ protected static function defaultNetConnectionFactory():NetConnection { var connection:NetConnection = new NetConnection(); connection.connect(null); return connection; } /** * Constructor. */ public function VideoPlayer() { super(); } /** * @private */ override protected function get defaultStyleProvider():IStyleProvider { return VideoPlayer.globalStyleProvider; } /** * @private */ protected var _fullScreenContainer:LayoutGroup; /** * @private */ protected var _ignoreDisplayListEvents:Boolean = false; /** * @private */ protected var _soundTransform:SoundTransform; /** * @inheritDoc * *

In the following example, the audio is muted:

* * * videoPlayer.soundTransform = new SoundTransform(0); * * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/media/SoundTransform.html flash.media.SoundTransform * @see #event:soundTransformChange feathers.events.MediaPlayerEventType.SOUND_TRANSFORM_CHANGE */ public function get soundTransform():SoundTransform { if(!this._soundTransform) { this._soundTransform = new SoundTransform(); } return this._soundTransform; } /** * @private */ public function set soundTransform(value:SoundTransform):void { this._soundTransform = value; if(this._netStream) { this._netStream.soundTransform = this._soundTransform; } this.dispatchEventWith(MediaPlayerEventType.SOUND_TRANSFORM_CHANGE); } /** * @private */ protected var _isWaitingForTextureReady:Boolean = false; /** * @private */ protected var _texture:Texture; /** * The texture used to display the video. This texture is not * automatically rendered by the VideoPlayer component. A * component like an ImageLoader should be added as a child * of the VideoPlayer to display the texture when it is * ready. * *

The texture property will initially return * null. Listen for Event.READY to know when * a valid texture is available to render.

* *

In the following example, a listener is added for * Event.READY, and the texture is displayed by an * ImageLoader component:

* * * function videoPlayer_readyHandler( event:Event ):void * { * var view:ImageLoader = new ImageLoader(); * view.source = videoPlayer.texture; * videoPlayer.addChildAt(view, 0); * } * * videoPlayer.addEventListener( Event.READY, videoPlayer_readyHandler ); * * @see #event:ready starling.events.Event.READY * @see feathers.controls.ImageLoader */ public function get texture():Texture { //there can be runtime errors if the texture is rendered before it //is ready, so we must return null until we're sure it's safe if(this._isWaitingForTextureReady) { return null; } return this._texture; } /** * @inheritDoc * * @see #event:dimensionsChange feathers.events.MediaPlayerEventType.DIMENSIONS_CHANGE */ public function get nativeWidth():Number { if(this._texture) { return this._texture.nativeWidth; } return 0; } /** * @inheritDoc * * @see #event:dimensionsChange feathers.events.MediaPlayerEventType.DIMENSIONS_CHANGE */ public function get nativeHeight():Number { if(this._texture) { return this._texture.nativeHeight; } return 0; } /** * @private */ protected var _netConnection:NetConnection; /** * @private */ protected var _netStream:NetStream; /** * The flash.net.NetStream object used to play the video. * * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetStream.html flash.net.NetStream */ public function get netStream():NetStream { return this._netStream; } /** * @private */ protected var _hasPlayedToEnd:Boolean = false; /** * @private */ protected var _videoSource:String; /** * A string representing the video URL or any other accepted value that * may be passed to the play() function of a * NetStream object. * *

In the following example, a video file URL is passed in:

* * * videoPlayer.videoSource = "http://example.com/video.m4v"; * * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetStream.html#play() Full description of flash.net.NetStream.play() in Adobe's Flash Platform API Reference */ public function get videoSource():String { return this._videoSource; } /** * @private */ public function set videoSource(value:String):void { if(this._videoSource === value) { return; } if(this._isPlaying) { this.stop(); } if(!value) { //if we're not playing anything, we shouldn't keep the NetStream //around in memory. if we're switching to something else, then //the NetStream can be reused. this.disposeNetStream(); } if(this._texture) { this._texture.dispose(); this._texture = null; this.dispatchEventWith(FeathersEventType.CLEAR); } this._videoSource = value; //reset the current and total time if we were playing a different //video previously if(this._currentTime !== 0) { this._currentTime = 0; this.dispatchEventWith(MediaPlayerEventType.CURRENT_TIME_CHANGE); } if(this._totalTime !== 0) { this._totalTime = 0; this.dispatchEventWith(MediaPlayerEventType.TOTAL_TIME_CHANGE); } //same with the bytes loaded and total this._bytesLoaded = 0; this._bytesTotal = 0; if(this._autoPlay && this._videoSource) { this.play(); } } /** * @private */ protected var _autoPlay:Boolean = true; /** * Determines if the video starts playing immediately when the * videoSource property is set. * *

In the following example, automatic playback is disabled:

* * * videoPlayer.autoPlay = false; * * @see #videoSource */ public function get autoPlay():Boolean { return this._autoPlay; } /** * @private */ public function set autoPlay(value:Boolean):void { this._autoPlay = value; } /** * @private */ protected var _isFullScreen:Boolean = false; /** * Indicates if the video player is currently full screen or not. When * the player is full screen, it will be displayed as a modal pop-up * that fills the entire Starling stage. Depending on the value of * fullScreenDisplayState, it may also change the value of * the native stage's displayState property. * * @see #toggleFullScreen() * @see #event:displayStateChange feathers.events.MediaPlayerEventType.DISPLAY_STATE_CHANGE */ public function get isFullScreen():Boolean { return this._isFullScreen; } /** * @private */ protected var _normalDisplayState:String = StageDisplayState.NORMAL; [Inspectable(type="String",enumeration="fullScreenInteractive,fullScreen,normal")] /** * When the video player is displayed normally (in other words, when it * isn't full-screen), determines the value of the native stage's * displayState property. * *

Using this property, it is possible to set the native stage's * displayState property to * StageDisplayState.FULL_SCREEN_INTERACTIVE or * StageDisplayState.FULL_SCREEN when the video player * is not in full screen mode. This might be useful for mobile apps that * should always display in full screen, while allowing a video player * to toggle between filling the entire stage and displaying at a * smaller size within its parent's layout.

* *

In the following example, the display state for normal mode * is changed:

* * * videoPlayer.fullScreenDisplayState = StageDisplayState.FULL_SCREEN_INTERACTIVE; * * @default StageDisplayState.NORMAL * * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/StageDisplayState.html#FULL_SCREEN_INTERACTIVE StageDisplayState.FULL_SCREEN_INTERACTIVE * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/StageDisplayState.html#FULL_SCREEN StageDisplayState.FULL_SCREEN * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/StageDisplayState.html#NORMAL StageDisplayState.NORMAL * @see #fullScreenDisplayState */ public function get normalDisplayState():String { return this._normalDisplayState; } /** * @private */ public function set normalDisplayState(value:String):void { if(this._normalDisplayState == value) { return; } this._normalDisplayState = value; if(!this._isFullScreen && this.stage) { var starling:Starling = stageToStarling(this.stage); var nativeStage:Stage = starling.nativeStage; nativeStage.displayState = this._normalDisplayState; } } /** * @private */ protected var _fullScreenDisplayState:String = StageDisplayState.FULL_SCREEN_INTERACTIVE; [Inspectable(type="String",enumeration="fullScreenInteractive,fullScreen,normal")] /** * When the video player is displayed full-screen, determines the value * of the native stage's displayState property. * *

Using this property, it is possible to set the native stage's * displayState property to * StageDisplayState.NORMAL when the video player is in * full screen mode. The video player will still be displayed as a modal * pop-up that fills the entire Starling stage, in this situation.

* *

In the following example, the display state for full-screen mode * is changed:

* * * videoPlayer.fullScreenDisplayState = StageDisplayState.FULL_SCREEN; * * @default StageDisplayState.FULL_SCREEN_INTERACTIVE * * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/StageDisplayState.html#FULL_SCREEN_INTERACTIVE StageDisplayState.FULL_SCREEN_INTERACTIVE * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/StageDisplayState.html#FULL_SCREEN StageDisplayState.FULL_SCREEN * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/StageDisplayState.html#NORMAL StageDisplayState.NORMAL * @see #normalDisplayState */ public function get fullScreenDisplayState():String { return this._fullScreenDisplayState; } /** * @private */ public function set fullScreenDisplayState(value:String):void { if(this._fullScreenDisplayState == value) { return; } this._fullScreenDisplayState = value; if(this._isFullScreen && this.stage) { var starling:Starling = stageToStarling(this.stage); var nativeStage:Stage = starling.nativeStage; nativeStage.displayState = this._fullScreenDisplayState; } } /** * @private */ protected var _hideRootWhenFullScreen:Boolean = true; /** * Determines if the Starling root display object is hidden when the * video player switches to full screen mode. By hiding the root display * object, rendering performance is optimized because Starling skips a * portion of the display list that is obscured by the video player. * *

In the following example, the root display object isn't hidden * when the video player is displayed in full screen:

* * * videoPlayer.hideRootWhenFullScreen = false; * * @default true */ public function get hideRootWhenFullScreen():Boolean { return this._hideRootWhenFullScreen; } /** * @private */ public function set hideRootWhenFullScreen(value:Boolean):void { this._hideRootWhenFullScreen = value; } /** * @private */ protected var _netConnectionFactory:Function = defaultNetConnectionFactory; /** * Creates the flash.net.NetConnection object used to play * the video, and calls the connect() method. By default, a * value of null is passed to the connect() * method. To pass different parameters, you may use a custom * netConnectionFactory. * *

The function is expected to have the following signature:

*
function():NetConnection
* *

In the following example, a custom factory for the * NetConnection is passed to the video player:

* * * videoPlayer.netConnectionFactory = function():NetConnection * { * var connection:NetConnection = new NetConnection(); * connection.connect( command ); * return connection; * }; * * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetConnection.html flash.net.NetConnection * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetConnection.html#connect() flash.net.NetConnection.connect() */ public function get netConnectionFactory():Function { return this._netConnectionFactory; } /** * @private */ public function set netConnectionFactory(value:Function):void { if(this._netConnectionFactory === value) { return; } this._netConnectionFactory = value; this.stop(); } /** * @private */ protected var _bytesLoaded:uint = 0; /** * @copy feathers.media.IProgressiveMediaPlayer#bytesLoaded * * @see #bytesTotal * @see #event:loadProgress feathers.events.MediaPlayerEventType.LOAD_PROGRESS */ public function get bytesLoaded():uint { return this._bytesLoaded; } /** * @private */ protected var _bytesTotal:uint = 0; /** * @copy feathers.media.IProgressiveMediaPlayer#bytesTotal * * @see #bytesLoaded * @see #event:loadProgress feathers.events.MediaPlayerEventType.LOAD_PROGRESS */ public function get bytesTotal():uint { return this._bytesTotal; } /** * @private */ override public function dispose():void { this.videoSource = null; super.dispose(); } /** * @private */ override public function play():void { if(this._videoSource === null) { return; } super.play(); } /** * @private */ override public function render(painter:Painter):void { if(this._isFullScreen) { return; } super.render(painter); } /** * Goes to full screen or returns to normal display. * *

When the player is full screen, it will be displayed as a modal * pop-up that fills the entire Starling stage. Depending on the value * of fullScreenDisplayState, it may also change the value * of the native stage's displayState property.

* *

When the player is displaying normally (in other words, when it is * not full screen), it will be displayed in its parent's layout like * any other Feathers component.

* * @see #isFullScreen * @see #fullScreenDisplayState * @see #normalDisplayState * @see #event:displayStateChange feathers.events.MediaPlayerEventType.DISPLAY_STATE_CHANGE */ public function toggleFullScreen():void { if(!this.stage) { throw new IllegalOperationError("Cannot enter full screen mode if the video player does not have access to the Starling stage.") } var starling:Starling = stageToStarling(this.stage); var nativeStage:Stage = starling.nativeStage; var oldIgnoreDisplayListEvents:Boolean = this._ignoreDisplayListEvents; this._ignoreDisplayListEvents = true; if(this._isFullScreen) { this.root.visible = true; PopUpManager.removePopUp(this._fullScreenContainer, false); var childCount:int = this._fullScreenContainer.numChildren; for(var i:int = 0; i < childCount; i++) { var child:DisplayObject = this._fullScreenContainer.getChildAt(0); this.addChild(child); } nativeStage.displayState = this._normalDisplayState; } else { if(this._hideRootWhenFullScreen) { this.root.visible = false; } nativeStage.displayState = this._fullScreenDisplayState; if(!this._fullScreenContainer) { this._fullScreenContainer = new LayoutGroup(); this._fullScreenContainer.autoSizeMode = LayoutGroup.AUTO_SIZE_MODE_STAGE; } this._fullScreenContainer.layout = this._layout; childCount = this.numChildren; for(i = 0; i < childCount; i++) { child = this.getChildAt(0); this._fullScreenContainer.addChild(child); } PopUpManager.addPopUp(this._fullScreenContainer, true, false); } this._ignoreDisplayListEvents = oldIgnoreDisplayListEvents; this._isFullScreen = !this._isFullScreen; this.dispatchEventWith(MediaPlayerEventType.DISPLAY_STATE_CHANGE); } /** * @private */ override protected function playMedia():void { if(!this._videoSource) { throw new IllegalOperationError(NO_VIDEO_SOURCE_PLAY_ERROR); } if(!this._netStream) { var netConnectionFactory:Function = this._netConnectionFactory !== null ? this._netConnectionFactory : defaultNetConnectionFactory; this._netConnection = netConnectionFactory(); this._netStream = new NetStream(this._netConnection); this._netStream.client = new VideoPlayerNetStreamClient(this.netStream_onMetaData); this._netStream.addEventListener(NetStatusEvent.NET_STATUS, netStream_netStatusHandler); this._netStream.addEventListener(IOErrorEvent.IO_ERROR, netStream_ioErrorHandler); } if(!this._soundTransform) { this._soundTransform = new SoundTransform(); } this._netStream.soundTransform = this._soundTransform; var onCompleteCallback:Function = videoTexture_onComplete; if(this._texture && this._hasPlayedToEnd) { //after playing the media until the end, if we restart from the //beginning, the audio plays, but we cannot see the video. //however, we can ask the NetStream to play the video source //again from the beginning, and the video displays. if we need //to restore the time, we can do it after the NetStream //dispatches NetStream.Play.Start this._netStream.play(this._videoSource); } else if(this._texture) { //this case happens if the video is paused and resumed without //reaching the end. //NetStream.Play.Start will not be dispatched after calling //resume(), so we need to manually add the ENTER_FRAME listener. //there's no need to seek. we're resuming from where the video //was paused. this.addEventListener(Event.ENTER_FRAME, videoPlayer_enterFrameHandler); this._netStream.resume(); } else { //in the final case, the texture hasn't been created yet. this._isWaitingForTextureReady = true; this._texture = Texture.fromNetStream(this._netStream, Starling.current.contentScaleFactor, onCompleteCallback); this._texture.root.onRestore = videoTexture_onRestore; //don't call play() until after Texture.fromNetStream() because //the texture needs to be created first. //however, we need to call play() even though a video texture //isn't ready to be rendered yet. this._netStream.play(this._videoSource); } this._hasPlayedToEnd = false; } /** * @private */ override protected function pauseMedia():void { if(!this._videoSource) { throw new IllegalOperationError(NO_VIDEO_SOURCE_PAUSE_ERROR); } this.removeEventListener(Event.ENTER_FRAME, videoPlayer_enterFrameHandler); this._netStream.pause(); } /** * @private */ override protected function seekMedia(seconds:Number):void { if(!this._videoSource) { throw new IllegalOperationError(NO_VIDEO_SOURCE_SEEK_ERROR); } this._currentTime = seconds; if(this._hasPlayedToEnd) { //the video played until the end, which means that the current //texture cannot seek properly without reloading the media. this.playMedia(); return; } this._netStream.seek(seconds); } /** * @private */ protected function disposeNetStream():void { if(!this._netStream) { return; } this._netStream.removeEventListener(NetStatusEvent.NET_STATUS, netStream_netStatusHandler); this._netStream.removeEventListener(IOErrorEvent.IO_ERROR, netStream_ioErrorHandler); this._netStream.close(); this._netStream = null; this._netConnection = null; } /** * @private */ protected function videoPlayer_enterFrameHandler(event:Event):void { this._currentTime = this._netStream.time; this.dispatchEventWith(MediaPlayerEventType.CURRENT_TIME_CHANGE); } /** * @private */ protected function videoPlayer_progress_enterFrameHandler(event:Event):void { var newBytesTotal:Number = this._netStream.bytesTotal; if(newBytesTotal > 0) { var needsDispatch:Boolean = false; var newBytesLoaded:Number = this._netStream.bytesLoaded; if(this._bytesTotal !== newBytesTotal) { this._bytesTotal = newBytesTotal; needsDispatch = true; } if(this._bytesLoaded !== newBytesLoaded) { this._bytesLoaded = newBytesLoaded; needsDispatch = true; } if(needsDispatch) { this.dispatchEventWith(MediaPlayerEventType.LOAD_PROGRESS, false, newBytesLoaded / newBytesTotal); } if(newBytesLoaded === newBytesTotal) { this.removeEventListener(Event.ENTER_FRAME, videoPlayer_progress_enterFrameHandler); } } } /** * @private */ protected function videoTexture_onRestore():void { this.pauseMedia(); this._isWaitingForTextureReady = true; this._texture.root.attachNetStream(this._netStream, videoTexture_onComplete); //this will start playback from the beginning again, but we can seek //back to the current time once the video texture is ready. this._netStream.play(this._videoSource); } /** * @private */ protected function videoTexture_onComplete():void { this._isWaitingForTextureReady = false; //the texture is ready to be displayed this.dispatchEventWith(Event.READY); var bytesTotal:Number = this._netStream.bytesTotal; if(this._bytesTotal === 0 && bytesTotal > 0) { this._bytesLoaded = this._netStream.bytesLoaded; this._bytesTotal = bytesTotal; this.dispatchEventWith(MediaPlayerEventType.LOAD_PROGRESS, false, this._bytesLoaded / bytesTotal); if(this._bytesLoaded !== this._bytesTotal) { this.addEventListener(Event.ENTER_FRAME, videoPlayer_progress_enterFrameHandler); } } } /** * @private */ protected function netStream_onMetaData(metadata:Object):void { this.dispatchEventWith(MediaPlayerEventType.DIMENSIONS_CHANGE); this._totalTime = metadata.duration; this.dispatchEventWith(MediaPlayerEventType.TOTAL_TIME_CHANGE); this.dispatchEventWith(MediaPlayerEventType.METADATA_RECEIVED, false, metadata); } /** * @private */ protected function netStream_ioErrorHandler(event:IOErrorEvent):void { this.dispatchEventWith(event.type, false, event); } /** * @private */ protected function netStream_netStatusHandler(event:NetStatusEvent):void { var code:String = event.info.code; switch(code) { case NET_STATUS_CODE_NETSTREAM_PLAY_STREAMNOTFOUND: { this.dispatchEventWith(FeathersEventType.ERROR, false, code); break; } case NET_STATUS_CODE_NETSTREAM_PLAY_START: { if(this._netStream.time !== this._currentTime) { //if we're restoring from a lost context, or we've //restarted the video after it had reached the end, the //NetStream may have restarted from the beginning. in //that case, we can manually seek to the last known good //position. this._netStream.seek(this._currentTime); } if(this._isPlaying) { //only add the listener if a video is playing. it may be //paused when restoring lost context, but we need to //temporarily start playing the NetStream in order to //restore the video texture. this.addEventListener(Event.ENTER_FRAME, videoPlayer_enterFrameHandler); } else { //the only way to prepare the video texture is to start //playing the NetStream. the media player is not in a //playing state, though, so we should pause the //NetStream now. this.pauseMedia(); } break; } case NET_STATUS_CODE_NETSTREAM_PLAY_STOP: { if(this._hasPlayedToEnd) { //we haven't cleared this flag yet after calling play() //on the NetStream. it is safe to ignore this case. return; } //any time that the NetStream stops, we want to remove the //Event.ENTER_FRAME listener. in most cases, we don't want //a listener being called every frame for no reason. on iOS, //context loss resets the NetStream time to 0, and we need //to keep the current time so that we can seek back. this.removeEventListener(Event.ENTER_FRAME, videoPlayer_enterFrameHandler); //on iOS, when context is lost, the NetStream will stop //automatically, and the time property will be reset to 0. //on other platforms, the NetStream will continue playing on //context loss, and the time will be correct. //we need to check if the context is lost or not to decide //if we've reached the end of the video or not. if(Starling.context.driverInfo !== "Disposed") { this.stop(); //set this flag after calling stop() because stopping //will seek to the beginning and may check for the flag. this._hasPlayedToEnd = true; this.dispatchEventWith(Event.COMPLETE); } break; } } } /** * @private */ override protected function mediaPlayer_addedHandler(event:Event):void { if(this._ignoreDisplayListEvents) { return; } super.mediaPlayer_addedHandler(event); } /** * @private */ override protected function mediaPlayer_removedHandler(event:Event):void { if(this._ignoreDisplayListEvents) { return; } super.mediaPlayer_removedHandler(event); } } } dynamic class VideoPlayerNetStreamClient { public function VideoPlayerNetStreamClient(onMetaDataCallback:Function) { this.onMetaDataCallback = onMetaDataCallback; } public var onMetaDataCallback:Function; public function onMetaData(metadata:Object):void { this.onMetaDataCallback(metadata); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy