VideoPlayer.as
22 KB
package com.gotenzing.videoplayer{
import fl.controls.*;
import fl.events.SliderEvent;
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.NetStatusEvent;
import flash.events.TimerEvent;
import flash.events.FullScreenEvent;
import flash.events.AsyncErrorEvent;
import flash.media.SoundTransform;
import flash.media.Video;
import flash.net.NetConnection;
import flash.net.NetStream;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.utils.Timer;
import flash.display.Stage;
import flash.display.StageDisplayState;
import flash.geom.*;
import flash.display.Loader;
import flash.display.DisplayObjectContainer;
import flash.events.IOErrorEvent;
import flash.events.EventDispatcher;
// fullscreen extra documentation re scaling and backgrounds, etc:
//http://www.adobe.com/devnet/flashplayer/articles/full_screen_mode.html
/* STRUCTURE
VideoPlayer_mc
posterPlay
controller
playpause, volume, etc
video (actual video inserted dynamically)
poster_mc (actual imagemc inserted dynamically)
mask (mask for size and shape)
*/
public class VideoPlayer extends Sprite {
// This class creates a video player complete with controls. It is essentially a skin control
public var autoPlay:Boolean = true;// if false, it will try a .jpg of the same file url and place a play button over vid
public var vid_scale:String = "none";// possible values: actual_size, fit_xy, fit_x, fit_y
public var vid_x:int=0;
public var vid_y:int=0;
public var autoHideController:Boolean=true;
public var fullscreenRect:Rectangle;//=new Rectangle(0,0,320,240);
public var isConnected:Boolean=false;
public var isReady:Boolean=false;
private const PLAYHEAD_UPDATE_INTERVAL_MS:uint=10;// milliseconds between updates to playhead & timer
private const DEFAULT_VOLUME:Number=0.8;
private const BUFFER_TIME:Number=1;// seconds in buffer at a time
private const SMOOTHING:Boolean = true;
public const PLAYERREADY:String = "VideoPlayerReady";
private var client:Object; // client object to use for the NetStream object
private var meta:Object;
private var nc:NetConnection;
private var ns:NetStream;
private var t:Timer;
private var vid:Video;
private var vidURL:String;
private var poster_sprite:Sprite;
private var volumeTransform:SoundTransform;// used to set the volume
private var lastVolume:Number = DEFAULT_VOLUME;
private var moviePlayer_mc:MovieClip;
private var vidCompleteEvent:Event;// video get to the end
private var vidState:int;// -1=empty; 0=pause; 1=play
private var bolVolumeScrub:Boolean = false;
private var rectVolumeScrub:Rectangle;
private var bolProgressScrub:Boolean = false;
private var rectProgressScrub:Rectangle;
public function VideoPlayer(mymoviePlayer_mc:MovieClip, connection:String) {
moviePlayer_mc=mymoviePlayer_mc;
moviePlayer_mc.controller_mc.position_txt.text = "";
// Create the client object for the NetStream, and set up a callback
// handler for the onMetaData event.
client = new Object();
client.onMetaData=onMetadata;
NetConnection.prototype.onBWDone = function(p_bw) {
trace("onBWDone: "+p_bw);
};
nc = new NetConnection();
nc.addEventListener(NetStatusEvent.NET_STATUS, onNetConnectionStatus);
nc.connect(connection);
//moviePlayer_mc.debug_txt.appendText("just testing\n");
}
/**
* Set scale property.
*/
public function scale(val:String):void {
switch(val){
case "none":
case "fill":
case "fit":
vid_scale=val;
break;
default :
trace("invalid value");
}
}
/**
* Pass the currently selected video.
*/
public function setVideo(url:String, posterURL:String=""):void {
trace(this+"setVideo:"+url+" posterURL"+posterURL);
stateLoading();
vidURL=url;
ns.play(vidURL);
/**/
// load poster image
//var posterURLRequest:URLRequest = new URLRequest(vidURL.substr(0,(vidURL.length-4))+".jpg");
if(posterURL!="" && posterURL!=null){
var posterURLRequest:URLRequest = new URLRequest(posterURL);
var posterLoader:Loader = new Loader();
posterLoader.load(posterURLRequest);
posterLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadPosterComplete);
posterLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onLoadPosterIOError);
}
if(autoPlay){
// show spinning wheel; but when do we hide it? some netstream event?
moviePlayer_mc.loadingIndicator.visible=true;
statePlay();
}else{
statePoster();
}
}
//////////////////////// PRIVATE FUNCTIONS ////////////////////////////////
//Event Handlers ////////////////////////////////
private function onNetConnectionStatus(e:NetStatusEvent){
trace(this+" onNetConnectionStatus called: "+e.info.code);
// Initialize the NetSteam object, add a listener for the netStatus
// event, and set the client for the NetStream.
if( e.info.code == "NetConnection.Connect.Success"){
ns=new NetStream(nc);
ns.addEventListener(NetStatusEvent.NET_STATUS, onNetStreamStatus);
ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError);
ns.client=client;
ns.bufferTime = BUFFER_TIME;
volumeTransform = new SoundTransform();
volumeTransform.volume=DEFAULT_VOLUME;
ns.soundTransform=volumeTransform;// default sound volume set
// Initialize the Video object, attach the NetStram, and add the Video
// object to the display list.
vid = new Video();
vid.attachNetStream(ns);
vid.smoothing = SMOOTHING;
moviePlayer_mc.myVideo=vid;
moviePlayer_mc.addChild(vid);
vid.x=vid_x;
vid.y=vid_y;
vidCompleteEvent = new Event(Event.COMPLETE);
fullscreenRect= moviePlayer_mc.vidMask_mc.getRect(this);//new Rectangle(0,0,320,240);
// controllers /////////
var cntrl:MovieClip = moviePlayer_mc.controller_mc;
moviePlayer_mc.stage.addEventListener( FullScreenEvent.FULL_SCREEN, onFullScrn );
moviePlayer_mc.stage.addEventListener( MouseEvent.MOUSE_UP, onMouseRelease);
// bring the controller to the front
moviePlayer_mc.setChildIndex(moviePlayer_mc.controller_mc, moviePlayer_mc.getChildIndex(moviePlayer_mc.myVideo));
//controllers
var mc:MovieClip = new MovieClip();
moviePlayer_mc.poster_mc=mc;
moviePlayer_mc.addChild(mc);
var errortxt:String = this+" skin is missing ";
cntrl.playPauseButton.playButton.addEventListener(MouseEvent.CLICK, onPLAYPauseClick);
cntrl.playPauseButton.pauseButton.addEventListener(MouseEvent.CLICK, onPlayPAUSEClick);
cntrl.muteButton.muteButton.addEventListener(MouseEvent.CLICK, onMuteClick);
cntrl.muteButton.unMuteButton.addEventListener(MouseEvent.CLICK, onUnMuteClick);
cntrl.muteButton.muteButton.visible = true;
cntrl.muteButton.unMuteButton.visible = false;
cntrl.fullscreenButton.fullscreenButton.addEventListener(MouseEvent.CLICK, onFullscreenClick);
cntrl.fullscreenButton.unFullscreenButton.addEventListener(MouseEvent.CLICK, onUnFullscreenClick);
cntrl.fullscreenButton.fullscreenButton.visible = true;
cntrl.fullscreenButton.unFullscreenButton.visible = false;
moviePlayer_mc.posterPlay.addEventListener(MouseEvent.CLICK, onPosterPlayClick);
cntrl.volumeSlider.minimum=0;
cntrl.volumeSlider.maximum=1;
cntrl.volumeSlider.snapInterval=0.1;
cntrl.volumeSlider.value=volumeTransform.volume;
cntrl.volumeSlider.tickInterval=cntrl.volumeSlider.snapInterval;
cntrl.volumeSlider.liveDragging=true;
cntrl.volumeSlider.addEventListener(SliderEvent.CHANGE, onVolumeChange);
cntrl.progressBar.progressBar.width=0.1;
cntrl.progressBar.bufferBar.width=0.1;
var s = moviePlayer_mc.controller_mc.progressBar.progressScrubber;
var sw = moviePlayer_mc.controller_mc.progressBar.dragArea_mc.width;
rectProgressScrub = new Rectangle(moviePlayer_mc.controller_mc.progressBar.dragArea_mc.x, s.y, sw, 0);
moviePlayer_mc.controller_mc.progressBar.progressScrubber.addEventListener(MouseEvent.MOUSE_DOWN, onProgressScrubberDown);
if(autoHideController){
// moviePlayer_mc.addEventListener(MouseEvent.ROLL_OVER, onMouseOverVideo);
// moviePlayer_mc.addEventListener(MouseEvent.ROLL_OUT, onMouseOutVideo);
moviePlayer_mc.addEventListener(MouseEvent.MOUSE_OVER, onMouseOverVideo);
moviePlayer_mc.addEventListener(MouseEvent.MOUSE_OUT, onMouseOutVideo);
}
stateEmpty();
isReady=true;
dispatchEvent(new Event(PLAYERREADY));
//trace(this+" Event dispatched: (PLAYERREADY)");
}
}
/**
* Event listener for the ns object. Called when the net stream's status
* changes.
*/
private function onNetStreamStatus(e:NetStatusEvent):void {
trace(this+" onNetStreamStatus: "+e.info.code);
try {
switch (e.info.code) {
case "NetStream.Play.Start" :
// If the current code is Start, start the timer object.
moviePlayer_mc.loadingIndicator.visible=false;
break;
case "NetStream.Play.StreamNotFound" :
case "NetStream.Play.Stop" :
// If the current code is Stop or StreamNotFound, stop
// the timer object and play the next video in the playlist.
if(!bolProgressScrub){
stateComplete();
}
break;
case "NetStream.Buffer.Empty" :
moviePlayer_mc.loadingIndicator.visible=true;
break;
case "NetStream.Buffer.Full" :
moviePlayer_mc.loadingIndicator.visible=false;
break;
}
//} catch (error:TypeError) {
} catch (error:TypeError) {
// Ignore any errors.
trace(this + " : onNetStatus error: "+error);
}
}
private function onAsyncError(event:AsyncErrorEvent):void {
trace(this+" onAsyncError: "+event.text);
}
/**
* Event listener for the ns object's client property. This method is called
* when the net stream object receives metadata information for a video.
*/
private function onMetadata(metadataObj:Object):void {
// Store the metadata information in the meta object.
meta=metadataObj;
// Resize the Video instance on the display list with the video's width
// and height from the metadata object.
vid.width=meta.width;
vid.height=meta.height;
mScale(moviePlayer_mc.myVideo, moviePlayer_mc.vidMask_mc, vid_scale);
vid.mask=moviePlayer_mc.vidMask_mc;
meta.timeString=formatTime(meta.duration);
}
private function onLoadPosterIOError(e:IOErrorEvent){
trace(this+" setVideo couldn't find a poster image.");
/*
var posterURLRequest:URLRequest = new URLRequest("video.jpg");
var posterLoader:Loader = new Loader();
posterLoader.load(posterURLRequest);
posterLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadPosterComplete);
*/
}
/**
* Event listener for the volumeSlider instance. Called when the user
* changes the value of the volume slider.
*/
private function onVolumeChange(e:SliderEvent):void {
// Set the volumeTransform's volume property to the current value of the
// Slider and set the NetStream object's soundTransform property.
lastVolume=e.value;
volumeTransform.volume=e.value;
ns.soundTransform=volumeTransform;
}
/**
* Event handler for the timer object. This method is called every
* PLAYHEAD_UPDATE_INTERVAL_MS milliseconds as long as the timer is running.
*/
private function onTimeInterval(e:TimerEvent):void {
// Update the progress bar and field based on the amount of video that has played back.
if(meta!=null){
var w:Number = rectProgressScrub.width;
var pb:MovieClip = moviePlayer_mc.controller_mc.progressBar;
if(bolProgressScrub){
// seeking jumps to the closest keyframe in the video
ns.seek(Math.round((pb.progressScrubber.x-rectProgressScrub.left)*meta.duration/w));
}else{
var playheadx=ns.time/meta.duration*w;
pb.progressScrubber.x=playheadx;
pb.progressBar.width = ns.time/meta.duration*w;
/*
//if you want to show the buffer, uncomment this.
pb.bufferBar.x=playheadx;
pb.bufferBar.width=ns.bufferLength/meta.duration*w;
var r:Rectangle = pb.bufferBar.getRect(pb);
if(r.right > rectProgressScrub.right){
pb.bufferBar.width = pb.bufferBar.width+rectProgressScrub.right-r.right;
}*/
}
var time:String = formatTime(ns.time);
moviePlayer_mc.controller_mc.position_txt.text = time+"/"+meta.timeString;
/*
// update volume and the red fill width when user is scrubbing
if(bolVolumeScrub) {
setVolume((mcVideoControls.mcVolumeScrubber.x - 341) / 53);
mcVideoControls.mcVolumeFill.mcFillRed.width = mcVideoControls.mcVolumeScrubber.x - 394 + 53;
}*/
}
}
private function onFullScrn( e:FullScreenEvent ):void {
//trace(this+" toggle full screen");
if(moviePlayer_mc.stage.displayState==StageDisplayState.FULL_SCREEN){
moviePlayer_mc.controller_mc.fullscreenButton.fullscreenButton.visible = false;
moviePlayer_mc.controller_mc.fullscreenButton.unFullscreenButton.visible = true;
}else{
moviePlayer_mc.controller_mc.fullscreenButton.fullscreenButton.visible = true;
moviePlayer_mc.controller_mc.fullscreenButton.unFullscreenButton.visible = false;
}
}
private function onLoadPosterComplete(e:Event):void {
//trace(this+" onLoadPosterComplete moviePlayer_mc.poster_mc.numChildren:" + moviePlayer_mc.poster_mc.numChildren);
try {
// kill the posterLoader
if(moviePlayer_mc.poster_mc.numChildren>0){
moviePlayer_mc.poster_mc.removeChildAt(0);
}
moviePlayer_mc.poster_mc.addChild(e.target.content);
mScale(moviePlayer_mc.poster_mc, moviePlayer_mc.vidMask_mc, vid_scale);
moviePlayer_mc.poster_mc.mask=moviePlayer_mc.posterMask_mc;
moviePlayer_mc.setChildIndex(moviePlayer_mc.poster_mc, moviePlayer_mc.getChildIndex(moviePlayer_mc.posterPlay));
} catch (error:Error) {trace("expected skin assets missing or misnamed:"+error);}
}
/**
* Click handler for each of the video playback buttons.
*/
private function onPLAYPauseClick(e:MouseEvent):void {
statePlay();
}
private function onPlayPAUSEClick(e:MouseEvent):void {
statePause();
}
private function onMuteClick(e:MouseEvent):void {
trace(this+"onMuteClick");
volumeTransform.volume=0;
ns.soundTransform=volumeTransform;
moviePlayer_mc.controller_mc.muteButton.muteButton.visible = false;
moviePlayer_mc.controller_mc.muteButton.unMuteButton.visible = true;
}
private function onUnMuteClick(e:MouseEvent):void {
trace(this+"onUnMuteClick");
volumeTransform.volume=lastVolume;
ns.soundTransform=volumeTransform;
var errortxt:String = this+"onMuteClick: skin is missing ";
moviePlayer_mc.controller_mc.muteButton.muteButton.visible = true;
moviePlayer_mc.controller_mc.muteButton.unMuteButton.visible = false;
}
private function onFullscreenClick(e:MouseEvent):void {
trace("going full screen");
moviePlayer_mc.stage.fullScreenSourceRect=fullscreenRect;
moviePlayer_mc.stage.displayState=StageDisplayState.FULL_SCREEN;
//moviePlayer_mc.controller_mc.fullscreenButton.fullscreenButton.visible = false;
//moviePlayer_mc.controller_mc.fullscreenButton.unFullscreenButton.visible = true;
}
private function onUnFullscreenClick(e:MouseEvent):void {
moviePlayer_mc.stage.displayState=StageDisplayState.NORMAL;
//moviePlayer_mc.controller_mc.fullscreenButton.fullscreenButton.visible = true;
//moviePlayer_mc.controller_mc.fullscreenButton.unFullscreenButton.visible = false;
}
private function onPosterPlayClick(e:MouseEvent):void {
statePlay();
}
private function onPlayClick(e:MouseEvent):void {
statePlay();
}
private function onPauseClick(e:MouseEvent):void {
statePause();
}
private function onProgressScrubberDown(e:MouseEvent):void {
trace(this+" onProgressScrubberDown");
bolProgressScrub = true;
moviePlayer_mc.controller_mc.progressBar.progressScrubber.startDrag(false, rectProgressScrub);
}
private function onVolumeScrubberDown(e:MouseEvent):void {
trace(this+" onVolumeScrubberDown");
bolVolumeScrub = true;
//moviePlayer_mc.controller_mc.progressBar.progressScrubber.startDrag(false, rectProgressScrub);
}
private function onMouseRelease(e:MouseEvent):void {
// set progress/volume scrub to false
bolVolumeScrub = false;
bolProgressScrub = false;
// stop all dragging actions
moviePlayer_mc.controller_mc.progressBar.progressScrubber.stopDrag();
//moviePlayer_mc.controller_mc.volumeControl.volumeScrubber.stopDrag();
/*
// save the volume if it's greater than zero
if((mcVideoControls.mcVolumeScrubber.x - 341) / 53 > 0)
intLastVolume = (mcVideoControls.mcVolumeScrubber.x - 341) / 53;
}*/
}
private function onMouseOverVideo(e:MouseEvent):void {
trace(this+" onMouseOverVideo() videState: "+vidState);
if(vidState>-1){
moviePlayer_mc.controller_mc.visible=true;
}
}
private function onMouseOutVideo(e:MouseEvent):void {
trace(this+" onMouseOutVideo()");
if(autoHideController){
trace(this+" onMouseOutVideo()");
moviePlayer_mc.controller_mc.visible = false;
}
}
private function mScale(scaleObj, sizeObj, method):void{
//trace(this+" mScale() "+scaleObj+sizeObj+method);
var fillH:Number; var fillW:Number; var fitH:Number; var fitW:Number;
if(sizeObj.width*scaleObj.width/sizeObj.height>scaleObj.height){
// true: video is proportionately wider than the target size
fillH = sizeObj.height;
fillW = scaleObj.width/scaleObj.height*sizeObj.height;
fitH = scaleObj.height/scaleObj.width*sizeObj.width;
fitW = sizeObj.width;
}else{
fitH = sizeObj.height;
fitW = scaleObj.width/scaleObj.height*sizeObj.height;
fillH = scaleObj.height/scaleObj.width*sizeObj.width;
fillW = sizeObj.width;
}
switch(method){
case "none":
break;
case "fill":
scaleObj.width=fillW;
scaleObj.height=fillH;
break;
case "fit":
scaleObj.width=fitW;
scaleObj.height=fitH;
break;
default:
trace(this+" mScale: not a valid scale method: "+method);
}
}
private function formatTime(tm:Number):String {
// returns mm:ss; if more than an hour, it returns hh:mm:ss
var s:int = Math.round(tm);
if (s > 0) {
var m:int = Math.floor(s/60);
s = s % 60;
var h:int = Math.floor(m/60);
m = m % 60;
var hr:String="";
if(h>0){hr= (h < 10 ? "0" : "")+h+":";}
return String(hr+(m < 10 ? "0" : "") + m + ":" + (s < 10 ? "0" : "") + s);
} else {
return "00:00";
}
}
// state control functions ///////////////
/*NOTE
moviePlayer_mc.controller_mc.positionBar;
moviePlayer_mc.controller_mc.volumeSlider;
moviePlayer_mc.controller_mc.playPauseButton;
moviePlayer_mc.controller_mc.pauseButton;
moviePlayer_mc.controller_mc.stopButton;
moviePlayer_mc.controller_mc.muteButton;
moviePlayer_mc.controller_mc.fullscreenButton;
moviePlayer_mc.loadingIndicator;
moviePlayer_mc.posterPlay;
moviePlayer_mc.poster_mc;
moviePlayer_mc.controller_mc.playPauseButton.playButton;
moviePlayer_mc.controller_mc.playPauseButton.pauseButton;
*/
private function stateEmpty(){
//trace(this+"stateEmpty");
moviePlayer_mc.myVideo.visible = false;
if(!autoHideController){moviePlayer_mc.controller_mc.visible = false;}
moviePlayer_mc.loadingIndicator.visible = false;
moviePlayer_mc.posterPlay.visible = false;
moviePlayer_mc.poster_mc.visible = false;
vidState=-1;// -1=empty; 0=pause; 1=play
}
private function statePoster(){
//trace(this+"statePoster");
ns.pause();
ns.seek(0);
moviePlayer_mc.myVideo.visible = false;
if(!autoHideController){moviePlayer_mc.controller_mc.visible = false;}
moviePlayer_mc.loadingIndicator.visible = false;
moviePlayer_mc.posterPlay.visible = true;
moviePlayer_mc.poster_mc.visible = true;
moviePlayer_mc.controller_mc.playPauseButton.playButton.visible = true;
moviePlayer_mc.controller_mc.playPauseButton.pauseButton.visible = false;
moviePlayer_mc.controller_mc.position_txt.text = "";
vidState=0;// -1=empty; 0=pause; 1=play
}
private function stateLoading(){
moviePlayer_mc.setChildIndex(moviePlayer_mc.loadingIndicator,moviePlayer_mc.numChildren - 1);
//moviePlayer_mc.debug_txt.appendText("stateLoading\n");
moviePlayer_mc.loadingIndicator.visible = true;
trace(this+"stateLoading");
moviePlayer_mc.posterPlay.visible = false;
vidState=-1;// -1=empty; 0=pause; 1=play
}
public function statePlay(){
//trace(this+"statePlay");
//ns.play(vidURL);
ns.resume();
moviePlayer_mc.myVideo.visible = true;
moviePlayer_mc.controller_mc.visible = true;
//moviePlayer_mc.loadingIndicator.visible = true;
moviePlayer_mc.posterPlay.visible = false;
moviePlayer_mc.poster_mc.visible = false;
moviePlayer_mc.controller_mc.playPauseButton.playButton.visible = false;
moviePlayer_mc.controller_mc.playPauseButton.pauseButton.visible = true;
t = new Timer(PLAYHEAD_UPDATE_INTERVAL_MS);
t.addEventListener(TimerEvent.TIMER, onTimeInterval);
t.start();
if (moviePlayer_mc.stage.displayState==StageDisplayState.FULL_SCREEN) {
} else {
}
vidState=1;// -1=empty; 0=pause; 1=play
}
public function statePause(){
//trace(this+"statePause");
ns.pause();
t.stop();
moviePlayer_mc.controller_mc.visible = true;
moviePlayer_mc.loadingIndicator.visible = false;
moviePlayer_mc.posterPlay.visible = false;
moviePlayer_mc.poster_mc.visible = false;
moviePlayer_mc.controller_mc.playPauseButton.playButton.visible = true;
moviePlayer_mc.controller_mc.playPauseButton.pauseButton.visible = false;
vidState=0;// -1=empty; 0=pause; 1=play
}
private function stateComplete(){
//trace(this+"stateComplete");
t.stop();
statePoster();
dispatchEvent(vidCompleteEvent);
}
// fullscreen and normalscreen states are independant of the other states.
public function stateFullScreen(){
trace("stateFullScreen");
moviePlayer_mc.stage.fullScreenSourceRect=new Rectangle(0,0,320,240);
moviePlayer_mc.stage.displayState=StageDisplayState.FULL_SCREEN;
}
public function stateNormalScreen(){
trace("stateNormalScreen");
moviePlayer_mc.stage.displayState=StageDisplayState.NORMAL;
}
//////////// END state control functions ///////////////
}
/**/
}