From 8a46320e953d49e9a858f63f00afa0cc1e412a00 Mon Sep 17 00:00:00 2001 From: Ray Greenwell Date: Wed, 28 Feb 2007 02:59:37 +0000 Subject: [PATCH] Re-enable video in the world. There are a few rough edges which'll eventually get ironed out. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4607 542714f4-19e9-0310-aa3c-eee0fc999fb1 --- src/as/com/threerings/util/MediaContainer.as | 92 ++------ src/as/com/threerings/util/VideoDisplayer.as | 225 +++++++++++++++++++ 2 files changed, 249 insertions(+), 68 deletions(-) create mode 100644 src/as/com/threerings/util/VideoDisplayer.as diff --git a/src/as/com/threerings/util/MediaContainer.as b/src/as/com/threerings/util/MediaContainer.as index 5aabef124..1b2593d42 100644 --- a/src/as/com/threerings/util/MediaContainer.as +++ b/src/as/com/threerings/util/MediaContainer.as @@ -44,8 +44,6 @@ import flash.events.TextEvent; import flash.geom.Point; -import flash.media.Video; - import flash.text.TextField; import flash.text.TextFieldAutoSize; @@ -145,26 +143,12 @@ public class MediaContainer extends Sprite */ protected function setupVideo (url :String) :void { - // TODO: re-implement video without flex - var tf :TextField = new TextField(); - tf.autoSize = TextFieldAutoSize.LEFT; - tf.text = "Video is temporarily disabled"; - tf.background = true; - tf.selectable = false; - tf.width = tf.textWidth + 5; - setMediaObject(tf); - -// var vid :VideoDisplay = new VideoDisplay(); -// vid.autoPlay = false; -// _media = vid; -// addChild(vid); -// vid.addEventListener(ProgressEvent.PROGRESS, loadVideoProgress); -// vid.addEventListener(VideoEvent.READY, loadVideoReady); -// vid.addEventListener(VideoEvent.REWIND, videoDidRewind); -// -// // start it loading -// vid.source = url; -// vid.load(); + var vid :VideoDisplayer = new VideoDisplayer(); + vid.addEventListener(VideoDisplayer.SIZE_KNOWN, handleVideoSizeKnown); + vid.addEventListener(VideoDisplayer.VIDEO_ERROR, handleVideoError); + _media = vid; + addChild(vid); + vid.setup(url); } /** @@ -256,25 +240,12 @@ public class MediaContainer extends Sprite removeChild(loader); -// } else if (_media is VideoDisplay) { -// var vid :VideoDisplay = (_media as VideoDisplay); -// // remove any listeners -// vid.removeEventListener(ProgressEvent.PROGRESS, -// loadVideoProgress); -// vid.removeEventListener(VideoEvent.READY, loadVideoReady); -// vid.removeEventListener(VideoEvent.REWIND, videoDidRewind); -// -// // dispose of media -// vid.pause(); -// try { -// vid.close(); -// } catch (ioe :IOError) { -// // ignore -// } -// vid.stop(); -// -// // remove from hierarchy -// removeChild(vid); + } else if (_media is VideoDisplayer) { + var vid :VideoDisplayer = (_media as VideoDisplayer); + vid.removeEventListener(VideoDisplayer.SIZE_KNOWN, handleVideoSizeKnown); + vid.removeEventListener(VideoDisplayer.VIDEO_ERROR, handleVideoError); + vid.shutdown(); + removeChild(vid); } else if (_media != null) { removeChild(_media); @@ -421,31 +392,24 @@ public class MediaContainer extends Sprite } /** - * A callback to receive PROGRESS events on the video. + * Handles video size known. */ - protected function loadVideoProgress (event :ProgressEvent) :void + protected function handleVideoSizeKnown (event :ValueEvent) :void { -// var vid :VideoDisplay = (event.currentTarget as VideoDisplay); -// updateContentDimensions(vid.videoWidth, vid.videoHeight); -// -// updateLoadingProgress(vid.bytesLoaded, vid.bytesTotal); + var args :Array = (event.value as Array); + updateContentDimensions(int(args[0]), int(args[1])); + trace("Video size is now known: " + _w + ", " + _h); } /** - * A callback to receive READY events for video. + * Handles an error loading a video. */ -// protected function loadVideoReady (event :VideoEvent) :void -// { -// var vid :VideoDisplay = (event.currentTarget as VideoDisplay); -// updateContentDimensions(vid.videoWidth, vid.videoHeight); -// updateLoadingProgress(1, 1); -// -// vid.play(); -// -// // remove the two listeners -// vid.removeEventListener(ProgressEvent.PROGRESS, loadVideoProgress); -// vid.removeEventListener(VideoEvent.READY, loadVideoReady); -// } + protected function handleVideoError (event :ValueEvent) :void + { + trace("Video load error: " + event.value); + stoppedLoading(); + setupBrokenImage(-1, -1); + } /** * Callback function to receive COMPLETE events for swfs or images. @@ -465,14 +429,6 @@ public class MediaContainer extends Sprite stoppedLoading(); } - /** - * Called when the video auto-rewinds. - */ -// protected function videoDidRewind (event :VideoEvent) :void -// { -// (_media as VideoDisplay).play(); -// } - /** * Configure the mask for this object. */ diff --git a/src/as/com/threerings/util/VideoDisplayer.as b/src/as/com/threerings/util/VideoDisplayer.as new file mode 100644 index 000000000..0fc739461 --- /dev/null +++ b/src/as/com/threerings/util/VideoDisplayer.as @@ -0,0 +1,225 @@ +package com.threerings.util { // TODO: Move. + +import flash.display.Sprite; + +import flash.events.AsyncErrorEvent; +import flash.events.IOErrorEvent; +import flash.events.Event; +import flash.events.MouseEvent; +import flash.events.NetStatusEvent; +import flash.events.SecurityErrorEvent; +import flash.events.TimerEvent; + +import flash.media.Video; + +import flash.net.NetConnection; +import flash.net.NetStream; + +import flash.utils.Timer; + +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"; + + /** 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 function VideoDisplayer () + { + _vid = new Video(); + addChild(_vid); + + addEventListener(MouseEvent.CLICK, handleClick); + _videoChecker.addEventListener(TimerEvent.TIMER, handleVideoCheck); + } + + public function setup (url :String) :void + { + _netCon = new NetConnection(); + _netCon.addEventListener(NetStatusEvent.NET_STATUS, handleNetStatus); + + // error handlers + _netCon.addEventListener(AsyncErrorEvent.ASYNC_ERROR, handleAsyncError); + _netCon.addEventListener(IOErrorEvent.IO_ERROR, handleIOError); + _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); + _vid.attachNetStream(_netStream); + _videoChecker.start(); + _netStream.play(url); + } + + public function shutdown () :void + { + _videoChecker.reset(); + _vid.attachNetStream(null); + + if (_netStream != null) { + _netStream.close(); + _netStream = null; + } + if (_netCon != null) { + _netCon.close(); + _netCon.removeEventListener(NetStatusEvent.NET_STATUS, handleNetStatus); + _netCon.removeEventListener(AsyncErrorEvent.ASYNC_ERROR, handleAsyncError); + _netCon.removeEventListener(IOErrorEvent.IO_ERROR, handleIOError); + _netCon.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, handleSecurityError); + _netCon = null; + } + } + + /** + * Check to see if we now know the dimensions of the video. + */ + protected function handleVideoCheck (event :TimerEvent) :void + { + if (_vid.videoWidth == 0 || _vid.videoHeight == 0) { + return; // not known yet! + } + + // stop the checker timer + _videoChecker.stop(); + + // set up the width/height + _vid.width = _vid.videoWidth; + _vid.height = _vid.videoHeight; + + // tell any interested parties + dispatchEvent(new ValueEvent(SIZE_KNOWN, [ _vid.videoWidth, _vid.videoHeight ])); + } + + protected function handleClick (event :MouseEvent) :void + { + trace("Click!"); + _netStream.togglePause(); + } + + protected function handleNetStatus (event :NetStatusEvent) :void + { + var info :Object = event.info; + if ("error" == info.level) { + trace("NetStatus error: " + info.code); + redispatchError(event); + return; + } + // else info.level == "status" + switch (info.code) { + case "NetConnection.Connect.Success": + trace("Connected!"); + break; + + default: + trace("NetStatus status: " + info.code); + break; + } + } + + protected function handleAsyncError (event :AsyncErrorEvent) :void + { + trace("AsyncError: " + event); + redispatchError(event); + } + + protected function handleIOError (event :IOErrorEvent) :void + { + trace("IOError: " + event); + redispatchError(event); + } + + protected function handleSecurityError (event :SecurityErrorEvent) :void + { + trace("SecurityError: " + event); + redispatchError(event); + } + + protected function redispatchError (event :Event) :void + { + dispatchEvent(new ValueEvent(VIDEO_ERROR, event)); + } + + public function callbackCalled (name :String, args :Array) :* + { + trace("Callback: " + name + ", args: " + args); + return undefined; + } + + public function onMetaData (obj :Object) :void + { + trace("Got metadata.."); + for (var n :String in obj) { + trace("name " + n + " -> " + obj[n]); + } + } + + protected var _vid :Video; + + protected var _netCon :NetConnection; + + protected var _netStream :NetStream; + + /** 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, + * and we want this to work in the general case. */ + 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; +}