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
This commit is contained in:
Ray Greenwell
2007-02-28 04:57:30 +00:00
parent e4d7b86d2b
commit 2bff3befde
+110 -71
View File
@@ -21,21 +21,33 @@ public class VideoDisplayer extends Sprite
{ {
/** A value event dispatched when the size of the video is known. /** A value event dispatched when the size of the video is known.
* Value: [ width, height ]. */ * 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. /** A value event sent when there's an error loading the video.
* Value: original error event. */ * 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 () public function VideoDisplayer ()
{ {
_vid = new Video(); _vid = new Video();
addChild(_vid); addChild(_vid);
addEventListener(MouseEvent.CLICK, handleClick); addEventListener(MouseEvent.ROLL_OVER, handleRollOver);
addEventListener(MouseEvent.ROLL_OUT, handleRollOut);
_videoChecker.addEventListener(TimerEvent.TIMER, handleVideoCheck); _videoChecker.addEventListener(TimerEvent.TIMER, handleVideoCheck);
_pauser = new Sprite();
_pauser.addEventListener(MouseEvent.CLICK, handleClick);
redrawPauser();
} }
/**
* Start playing a video!
*/
public function setup (url :String) :void public function setup (url :String) :void
{ {
_netCon = new NetConnection(); _netCon = new NetConnection();
@@ -47,15 +59,22 @@ public class VideoDisplayer extends Sprite
_netCon.addEventListener(SecurityErrorEvent.SECURITY_ERROR, handleSecurityError); _netCon.addEventListener(SecurityErrorEvent.SECURITY_ERROR, handleSecurityError);
_netCon.connect(null); _netCon.connect(null);
trace("Not waiting, just using!");
_netStream = new NetStream(_netCon); _netStream = new NetStream(_netCon);
_netStream.client = this; // pass in refs to some of our protected methods
//_netStream.client = new Callbacker(this); _netStream.client = {
onMetaData: metaDataReceived
};
_netStream.addEventListener(NetStatusEvent.NET_STATUS, handleStreamNetStatus);
_vid.attachNetStream(_netStream); _vid.attachNetStream(_netStream);
_videoChecker.start(); _videoChecker.start();
_netStream.play(url); _netStream.play(url);
_paused = false;
} }
/**
* Stop playing our video.
*/
public function shutdown () :void public function shutdown () :void
{ {
_videoChecker.reset(); _videoChecker.reset();
@@ -63,6 +82,7 @@ public class VideoDisplayer extends Sprite
if (_netStream != null) { if (_netStream != null) {
_netStream.close(); _netStream.close();
_netStream.removeEventListener(NetStatusEvent.NET_STATUS, handleStreamNetStatus);
_netStream = null; _netStream = null;
} }
if (_netCon != null) { if (_netCon != null) {
@@ -90,15 +110,71 @@ public class VideoDisplayer extends Sprite
// set up the width/height // set up the width/height
_vid.width = _vid.videoWidth; _vid.width = _vid.videoWidth;
_vid.height = _vid.videoHeight; _vid.height = _vid.videoHeight;
redrawPauser();
// tell any interested parties // tell any interested parties
dispatchEvent(new ValueEvent(SIZE_KNOWN, [ _vid.videoWidth, _vid.videoHeight ])); 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 protected function handleClick (event :MouseEvent) :void
{ {
trace("Click!"); if (_paused) {
_netStream.togglePause(); _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 protected function handleNetStatus (event :NetStatusEvent) :void
@@ -112,7 +188,8 @@ public class VideoDisplayer extends Sprite
// else info.level == "status" // else info.level == "status"
switch (info.code) { switch (info.code) {
case "NetConnection.Connect.Success": case "NetConnection.Connect.Success":
trace("Connected!"); case "NetConnection.Connect.Closed":
// these status events we ignore
break; break;
default: 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 protected function handleAsyncError (event :AsyncErrorEvent) :void
{ {
trace("AsyncError: " + event); trace("AsyncError: " + event);
@@ -139,31 +226,35 @@ public class VideoDisplayer extends Sprite
redispatchError(event); redispatchError(event);
} }
/**
* Redispatch some error we received to our listeners.
*/
protected function redispatchError (event :Event) :void protected function redispatchError (event :Event) :void
{ {
dispatchEvent(new ValueEvent(VIDEO_ERROR, event)); 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); trace("Got video metadata:");
return undefined;
}
public function onMetaData (obj :Object) :void
{
trace("Got metadata..");
for (var n :String in obj) { for (var n :String in obj) {
trace("name " + n + " -> " + obj[n]); trace(" " + n + ": " + obj[n]);
} }
} }
protected var _vid :Video; protected var _vid :Video;
protected var _paused :Boolean = false;
protected var _netCon :NetConnection; protected var _netCon :NetConnection;
protected var _netStream :NetStream; protected var _netStream :NetStream;
protected var _pauser :Sprite;
/** Checks the video every 100ms to see if the dimensions are now know. /** 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 * 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, * 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); 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;
}