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:
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user