From 2bff3befde4e130821849c0a38a3a1029babc4f3 Mon Sep 17 00:00:00 2001 From: Ray Greenwell Date: Wed, 28 Feb 2007 04:57:30 +0000 Subject: [PATCH] A reasonable alpha-level video displayer. When you mouse-over it, the controls appear, which are currently only a pause/play button. When it reaches the end, it autorewinds and pauses. Things: - We may want all videos to start paused. However, since we're not using a streaming media server, there's no way to show the first frame without loading the video unless we screenshot it on the server and then include a secondary media ident, which we don't do. So: as long as the user has to load the FLV, we may as well play it and not make it just look like an image. - We'll probably want to adapt this into a standalone video displayer that can be used to view FLVs using our UI from inside a web page. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4610 542714f4-19e9-0310-aa3c-eee0fc999fb1 --- src/as/com/threerings/util/VideoDisplayer.as | 181 +++++++++++-------- 1 file changed, 110 insertions(+), 71 deletions(-) diff --git a/src/as/com/threerings/util/VideoDisplayer.as b/src/as/com/threerings/util/VideoDisplayer.as index 0fc739461..2966e441e 100644 --- a/src/as/com/threerings/util/VideoDisplayer.as +++ b/src/as/com/threerings/util/VideoDisplayer.as @@ -21,21 +21,33 @@ public class VideoDisplayer extends Sprite { /** A value event dispatched when the size of the video is known. * Value: [ width, height ]. */ - public static const SIZE_KNOWN :String = "SIZE_KNOWN"; + public static const SIZE_KNOWN :String = "videoDisplayerSizeKnown"; /** A value event sent when there's an error loading the video. * Value: original error event. */ - public static const VIDEO_ERROR :String = "VIDEO_ERROR"; + public static const VIDEO_ERROR :String = "videoDisplayerError"; + /** + * Create a video displayer. + */ public function VideoDisplayer () { _vid = new Video(); addChild(_vid); - addEventListener(MouseEvent.CLICK, handleClick); + addEventListener(MouseEvent.ROLL_OVER, handleRollOver); + addEventListener(MouseEvent.ROLL_OUT, handleRollOut); + _videoChecker.addEventListener(TimerEvent.TIMER, handleVideoCheck); + + _pauser = new Sprite(); + _pauser.addEventListener(MouseEvent.CLICK, handleClick); + redrawPauser(); } + /** + * Start playing a video! + */ public function setup (url :String) :void { _netCon = new NetConnection(); @@ -47,15 +59,22 @@ public class VideoDisplayer extends Sprite _netCon.addEventListener(SecurityErrorEvent.SECURITY_ERROR, handleSecurityError); _netCon.connect(null); - trace("Not waiting, just using!"); _netStream = new NetStream(_netCon); - _netStream.client = this; - //_netStream.client = new Callbacker(this); + // pass in refs to some of our protected methods + _netStream.client = { + onMetaData: metaDataReceived + }; + _netStream.addEventListener(NetStatusEvent.NET_STATUS, handleStreamNetStatus); + _vid.attachNetStream(_netStream); _videoChecker.start(); _netStream.play(url); + _paused = false; } + /** + * Stop playing our video. + */ public function shutdown () :void { _videoChecker.reset(); @@ -63,6 +82,7 @@ public class VideoDisplayer extends Sprite if (_netStream != null) { _netStream.close(); + _netStream.removeEventListener(NetStatusEvent.NET_STATUS, handleStreamNetStatus); _netStream = null; } if (_netCon != null) { @@ -90,15 +110,71 @@ public class VideoDisplayer extends Sprite // set up the width/height _vid.width = _vid.videoWidth; _vid.height = _vid.videoHeight; + redrawPauser(); // tell any interested parties dispatchEvent(new ValueEvent(SIZE_KNOWN, [ _vid.videoWidth, _vid.videoHeight ])); } + protected function handleRollOver (event :MouseEvent) :void + { + addChild(_pauser); + } + + protected function handleRollOut (event :MouseEvent) :void + { + removeChild(_pauser); + } + protected function handleClick (event :MouseEvent) :void { - trace("Click!"); - _netStream.togglePause(); + if (_paused) { + _netStream.resume(); + _paused = false; + + } else { + _netStream.pause(); + _paused = true; + } + redrawPauser(); + } + + /** + * Draw the pauser sprite, update it's location. + * This will become something artistic, etc. + */ + protected function redrawPauser () :void + { + with (_pauser.graphics) { + clear(); + // draw a nice circle + beginFill(0x333333); + drawCircle(0, 0, 20); + endFill(); + lineStyle(2, 0); + drawCircle(0, 0, 20); + + if (_paused) { + lineStyle(0, 0, 0); + beginFill(0x00FF00); + moveTo(-4, -10); + lineTo(4, 0); + lineTo(-4, 10); + lineTo(-4, -10); + endFill(); + + } else { + lineStyle(2, 0x00FF00); + moveTo(-4, -10); + lineTo(-4, 10); + moveTo(4, -10); + lineTo(4, 10); + } + } + + // and update the location + _pauser.x = _vid.width/2; + _pauser.y = _vid.height/2; } protected function handleNetStatus (event :NetStatusEvent) :void @@ -112,7 +188,8 @@ public class VideoDisplayer extends Sprite // else info.level == "status" switch (info.code) { case "NetConnection.Connect.Success": - trace("Connected!"); + case "NetConnection.Connect.Closed": + // these status events we ignore break; default: @@ -121,6 +198,16 @@ public class VideoDisplayer extends Sprite } } + protected function handleStreamNetStatus (event :NetStatusEvent) :void + { + if (event.info.code == "NetStream.Play.Stop") { + _netStream.seek(0); + _netStream.pause(); + _paused = true; + redrawPauser(); + } + } + protected function handleAsyncError (event :AsyncErrorEvent) :void { trace("AsyncError: " + event); @@ -139,31 +226,35 @@ public class VideoDisplayer extends Sprite redispatchError(event); } + /** + * Redispatch some error we received to our listeners. + */ protected function redispatchError (event :Event) :void { dispatchEvent(new ValueEvent(VIDEO_ERROR, event)); } - public function callbackCalled (name :String, args :Array) :* + /** + * Called when metadata (if any) is found in the video stream. + */ + protected function metaDataReceived (obj :Object) :void { - trace("Callback: " + name + ", args: " + args); - return undefined; - } - - public function onMetaData (obj :Object) :void - { - trace("Got metadata.."); + trace("Got video metadata:"); for (var n :String in obj) { - trace("name " + n + " -> " + obj[n]); + trace(" " + n + ": " + obj[n]); } } - + protected var _vid :Video; + protected var _paused :Boolean = false; + protected var _netCon :NetConnection; protected var _netStream :NetStream; + protected var _pauser :Sprite; + /** Checks the video every 100ms to see if the dimensions are now know. * Yes, this is how to do it. We could trigger on ENTER_FRAME, but then * we may not know the dimensions unless we're added on the display list, @@ -171,55 +262,3 @@ public class VideoDisplayer extends Sprite protected var _videoChecker :Timer = new Timer(100); } } - -import flash.utils.Proxy; - -import flash.utils.flash_proxy; - -import com.threerings.util.VideoDisplayer; - -use namespace flash_proxy; - -/** - */ -internal class Callbacker extends Proxy -{ - public function Callbacker (displayer :VideoDisplayer) - { - _displayer = displayer; - - _props - } - - override flash_proxy function hasProperty (name :*) :Boolean - { - return (_props[name] !== undefined); - } - - override flash_proxy function getProperty (name :*) :* - { - trace("Damn thing asked about '" + name + "'."); - return _props[name]; - } - - override flash_proxy function setProperty (name :*, value :*) :void - { - trace("Damn thing tried to set '" + name + "' to '" + value + "'."); - _props[name] = value; - } - - override flash_proxy function deleteProperty (name :*) :Boolean - { - trace("Damn thing tried to delete '" + name + "'."); - return (delete _props[name]); - } - - override flash_proxy function callProperty (name :*, ... args) :* - { - return _displayer.callbackCalled(name, args); - } - - protected var _props :Object = {}; - - protected var _displayer :VideoDisplayer; -}