Checkpoint: seeking, tweaking... no peeking!
git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@684 ed5b42cb-e716-0410-a449-f6a68f950b19
This commit is contained in:
@@ -16,6 +16,7 @@ import flash.events.TimerEvent;
|
||||
|
||||
import flash.geom.Point;
|
||||
|
||||
import flash.media.SoundTransform;
|
||||
import flash.media.Video;
|
||||
|
||||
import flash.net.NetConnection;
|
||||
@@ -31,8 +32,11 @@ public class FlvVideoPlayer extends EventDispatcher
|
||||
{
|
||||
public function FlvVideoPlayer ()
|
||||
{
|
||||
_videoChecker = new Timer(100);
|
||||
_videoChecker.addEventListener(TimerEvent.TIMER, handleVideoCheck);
|
||||
_sizeChecker = new Timer(100);
|
||||
_sizeChecker.addEventListener(TimerEvent.TIMER, handleSizeCheck);
|
||||
|
||||
_positionChecker = new Timer(250);
|
||||
_positionChecker.addEventListener(TimerEvent.TIMER, handlePositionCheck);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,10 +58,11 @@ public class FlvVideoPlayer extends EventDispatcher
|
||||
_netStream.client = {
|
||||
onMetaData: handleMetaData
|
||||
};
|
||||
_netStream.soundTransform = new SoundTransform(_volume);
|
||||
_netStream.addEventListener(NetStatusEvent.NET_STATUS, handleStreamNetStatus);
|
||||
|
||||
_vid.attachNetStream(_netStream);
|
||||
_videoChecker.start();
|
||||
_sizeChecker.start();
|
||||
|
||||
_netStream.play(url);
|
||||
_netStream.pause(); // TODO Does this work?
|
||||
@@ -104,37 +109,45 @@ public class FlvVideoPlayer extends EventDispatcher
|
||||
// from VideoPlayer
|
||||
public function getDuration () :Number
|
||||
{
|
||||
return NaN; // TODO
|
||||
return _duration;
|
||||
}
|
||||
|
||||
// from VideoPlayer
|
||||
public function getPosition () :Number
|
||||
{
|
||||
return NaN; // TODO
|
||||
if (_netStream != null) {
|
||||
return _netStream.time;
|
||||
}
|
||||
return NaN;
|
||||
}
|
||||
|
||||
// from VideoPlayer
|
||||
public function seek (position :Number) :void
|
||||
{
|
||||
// TODO
|
||||
if (_netStream != null) {
|
||||
_netStream.seek(position);
|
||||
}
|
||||
}
|
||||
|
||||
// from VideoPlayer
|
||||
public function getVolume () :Number
|
||||
{
|
||||
return 1; // TODO
|
||||
return _volume;
|
||||
}
|
||||
|
||||
// from VideoPlayer
|
||||
public function setVolume (volume :Number) :void
|
||||
{
|
||||
// TODO
|
||||
_volume = Math.max(0, Math.min(1, volume));
|
||||
if (_netStream != null) {
|
||||
_netStream.soundTransform = new SoundTransform(_volume);
|
||||
}
|
||||
}
|
||||
|
||||
// from VideoPlayer
|
||||
public function unload () :void
|
||||
{
|
||||
_videoChecker.reset();
|
||||
_sizeChecker.reset();
|
||||
_vid.attachNetStream(null);
|
||||
|
||||
if (_netStream != null) {
|
||||
@@ -155,24 +168,29 @@ public class FlvVideoPlayer extends EventDispatcher
|
||||
/**
|
||||
* Check to see if we now know the dimensions of the video.
|
||||
*/
|
||||
protected function handleVideoCheck (event :TimerEvent) :void
|
||||
protected function handleSizeCheck (event :TimerEvent) :void
|
||||
{
|
||||
if (_vid.videoWidth == 0 || _vid.videoHeight == 0) {
|
||||
return; // not known yet!
|
||||
}
|
||||
|
||||
// stop the checker timer
|
||||
_videoChecker.stop();
|
||||
_sizeChecker.stop();
|
||||
_size = new Point(_vid.videoWidth, _vid.videoHeight);
|
||||
|
||||
// set up the width/height
|
||||
_vid.width = _size.x;
|
||||
_vid.height = _size.y;
|
||||
|
||||
log.info("=================> size known: " + _size);
|
||||
log.debug("====> size known: " + _size);
|
||||
dispatchEvent(new ValueEvent(VideoPlayerCodes.SIZE, _size.clone()));
|
||||
}
|
||||
|
||||
protected function handlePositionCheck (event :TimerEvent) :void
|
||||
{
|
||||
dispatchEvent(new ValueEvent(VideoPlayerCodes.POSITION, getPosition()));
|
||||
}
|
||||
|
||||
protected function handleNetStatus (event :NetStatusEvent) :void
|
||||
{
|
||||
var info :Object = event.info;
|
||||
@@ -196,10 +214,19 @@ public class FlvVideoPlayer extends EventDispatcher
|
||||
|
||||
protected function handleStreamNetStatus (event :NetStatusEvent) :void
|
||||
{
|
||||
if (event.info.code == "NetStream.Play.Stop") {
|
||||
log.debug("NetStatus", "level", event.info.level, "code", event.info.code);
|
||||
|
||||
switch (event.info.code) {
|
||||
case "NetStream.Play.Stop":
|
||||
// rewind to the beginning
|
||||
_netStream.seek(0);
|
||||
_netStream.pause();
|
||||
updateState(VideoPlayerCodes.STATE_PAUSED);
|
||||
break;
|
||||
|
||||
case "NetStream.Seek.Notify":
|
||||
handlePositionCheck(null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,12 +265,31 @@ public class FlvVideoPlayer extends EventDispatcher
|
||||
}
|
||||
|
||||
// TODO: total duration is info.duration
|
||||
|
||||
if ("duration" in info) {
|
||||
_duration = Number(info.duration);
|
||||
if (!isNaN(_duration)) {
|
||||
dispatchEvent(new ValueEvent(VideoPlayerCodes.DURATION, _duration));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function updateState (newState :int) :void
|
||||
{
|
||||
const oldState :int = _state;
|
||||
|
||||
_state = newState;
|
||||
dispatchEvent(new ValueEvent(VideoPlayerCodes.STATE, newState));
|
||||
|
||||
if (_state == VideoPlayerCodes.STATE_PLAYING) {
|
||||
_positionChecker.start();
|
||||
|
||||
} else {
|
||||
_positionChecker.reset();
|
||||
if (oldState == VideoPlayerCodes.STATE_PLAYING) {
|
||||
handlePositionCheck(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected const log :Log = Log.getLog(this);
|
||||
@@ -259,10 +305,16 @@ public class FlvVideoPlayer extends EventDispatcher
|
||||
/** Our size, null until known. */
|
||||
protected var _size :Point;
|
||||
|
||||
protected var _duration :Number = NaN;
|
||||
|
||||
protected var _volume :Number = 1;
|
||||
|
||||
/** 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;
|
||||
protected var _sizeChecker :Timer;
|
||||
|
||||
protected var _positionChecker :Timer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,8 @@ public class SimpleVideoDisplay extends Sprite
|
||||
_player = player;
|
||||
_player.addEventListener(VideoPlayerCodes.STATE, handlePlayerState);
|
||||
_player.addEventListener(VideoPlayerCodes.SIZE, handlePlayerSize);
|
||||
_player.addEventListener(VideoPlayerCodes.DURATION, handlePlayerDuration);
|
||||
_player.addEventListener(VideoPlayerCodes.POSITION, handlePlayerPosition);
|
||||
_player.addEventListener(VideoPlayerCodes.ERROR, handlePlayerError);
|
||||
|
||||
addChild(_player.getDisplay());
|
||||
@@ -68,6 +70,36 @@ public class SimpleVideoDisplay extends Sprite
|
||||
// and update the HUD location (even if not currently showing)
|
||||
_hud.x = NATIVE_WIDTH/2;
|
||||
_hud.y = NATIVE_HEIGHT/2;
|
||||
|
||||
_track = new Sprite();
|
||||
_track.x = PAD - _hud.x;
|
||||
_track.y = NATIVE_HEIGHT - PAD - TRACK_HEIGHT - _hud.y;
|
||||
g = _track.graphics;
|
||||
g.beginFill(0x000000, .7);
|
||||
g.lineStyle(1, 0xFFFFFF);
|
||||
g.drawRect(0, 0, TRACK_WIDTH, TRACK_HEIGHT);
|
||||
g.endFill();
|
||||
//g.drawRect(0, 0, TRACK_WIDTH - 2, TRACK_HEIGHT - 2);
|
||||
// _track is not added to _hud until we know the duration
|
||||
|
||||
const trackMask :Shape = new Shape();
|
||||
g = trackMask.graphics;
|
||||
g.beginFill(0xFFFFFF);
|
||||
g.drawRect(0, 0, TRACK_WIDTH, TRACK_HEIGHT);
|
||||
_track.addChild(trackMask);
|
||||
_track.mask = trackMask;
|
||||
|
||||
_knob = new Sprite();
|
||||
_knob.y = TRACK_HEIGHT / 2;
|
||||
g = _knob.graphics;
|
||||
g.lineStyle(1, 0xFFFFFF);
|
||||
g.beginFill(0x000099);
|
||||
g.drawCircle(0, 0, TRACK_HEIGHT/2 - 1);
|
||||
// _knob is not added to _track until we know the position
|
||||
|
||||
_track.addEventListener(MouseEvent.CLICK, handleTrackClick);
|
||||
_knob.addEventListener(MouseEvent.MOUSE_DOWN, handleKnobDown);
|
||||
_knob.addEventListener(MouseEvent.MOUSE_UP, handleKnobUp);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,6 +141,36 @@ public class SimpleVideoDisplay extends Sprite
|
||||
}
|
||||
}
|
||||
|
||||
protected function handleTrackClick (event :MouseEvent) :void
|
||||
{
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
// see if we can seek to a position
|
||||
const dur :Number = _player.getDuration();
|
||||
if (isNaN(dur)) {
|
||||
log.debug("Duration is NaN, track click dropped.");
|
||||
return;
|
||||
}
|
||||
|
||||
var perc :Number = event.localX / TRACK_WIDTH;
|
||||
perc = Math.max(0, Math.min(1, perc));
|
||||
log.debug("Seek", "x", event.localX, "perc", perc, "pos", (perc * dur));
|
||||
|
||||
_player.seek(perc * dur);
|
||||
}
|
||||
|
||||
protected function handleKnobDown (event :MouseEvent) :void
|
||||
{
|
||||
event.stopImmediatePropagation();
|
||||
// TODO
|
||||
}
|
||||
|
||||
protected function handleKnobUp (event :MouseEvent) :void
|
||||
{
|
||||
event.stopImmediatePropagation();
|
||||
// TODO
|
||||
}
|
||||
|
||||
protected function handlePlayerState (event :ValueEvent) :void
|
||||
{
|
||||
redrawHUD();
|
||||
@@ -129,6 +191,20 @@ public class SimpleVideoDisplay extends Sprite
|
||||
dispatchEvent(event);
|
||||
}
|
||||
|
||||
protected function handlePlayerDuration (event :ValueEvent) :void
|
||||
{
|
||||
_hud.addChild(_track);
|
||||
}
|
||||
|
||||
protected function handlePlayerPosition (event :ValueEvent) :void
|
||||
{
|
||||
const pos :Number = Number(event.value);
|
||||
_knob.x = (pos / _player.getDuration()) * TRACK_WIDTH;
|
||||
if (_knob.parent == null) {
|
||||
_track.addChild(_knob);
|
||||
}
|
||||
}
|
||||
|
||||
protected function handlePlayerError (event :ValueEvent) :void
|
||||
{
|
||||
// TODO.. maybe just redispatch
|
||||
@@ -153,41 +229,42 @@ public class SimpleVideoDisplay extends Sprite
|
||||
const g :Graphics = _hud.graphics;
|
||||
g.clear();
|
||||
|
||||
if (state == VideoPlayerCodes.STATE_READY ||
|
||||
state == VideoPlayerCodes.STATE_PLAYING ||
|
||||
state == VideoPlayerCodes.STATE_PAUSED) {
|
||||
// draw a nice circle
|
||||
g.beginFill(0x333333);
|
||||
g.drawCircle(0, 0, 20);
|
||||
g.endFill();
|
||||
g.lineStyle(2, 0xFFFFFF);
|
||||
g.drawCircle(0, 0, 20);
|
||||
|
||||
if (state == VideoPlayerCodes.STATE_PLAYING) {
|
||||
g.lineStyle(2, 0x00FF00);
|
||||
g.moveTo(-4, -10);
|
||||
g.lineTo(-4, 10);
|
||||
g.moveTo(4, -10);
|
||||
g.lineTo(4, 10);
|
||||
|
||||
} else {
|
||||
g.lineStyle(0, 0, 0);
|
||||
g.beginFill(0x00FF00);
|
||||
g.moveTo(-4, -10);
|
||||
g.lineTo(4, 0);
|
||||
g.lineTo(-4, 10);
|
||||
g.lineTo(-4, -10);
|
||||
g.endFill();
|
||||
}
|
||||
|
||||
} else {
|
||||
if ((state != VideoPlayerCodes.STATE_READY) &&
|
||||
(state != VideoPlayerCodes.STATE_PLAYING) &&
|
||||
(state != VideoPlayerCodes.STATE_PAUSED)) {
|
||||
// draw something loading-like
|
||||
// TODO animated swf
|
||||
g.beginFill(0x000033);
|
||||
g.drawCircle(5, 5, 10);
|
||||
g.drawCircle(-5, 5, 10);
|
||||
g.drawCircle(-5, -5, 10);
|
||||
g.drawCircle(5, -5, 10);
|
||||
g.endFill();
|
||||
return;
|
||||
}
|
||||
|
||||
// draw a nice circle
|
||||
g.beginFill(0x000000, .7);
|
||||
g.drawCircle(0, 0, 20);
|
||||
g.endFill();
|
||||
g.lineStyle(2, 0xFFFFFF);
|
||||
g.drawCircle(0, 0, 20);
|
||||
|
||||
if (state == VideoPlayerCodes.STATE_PLAYING) {
|
||||
g.lineStyle(2, 0x00FF00);
|
||||
g.moveTo(-4, -10);
|
||||
g.lineTo(-4, 10);
|
||||
g.moveTo(4, -10);
|
||||
g.lineTo(4, 10);
|
||||
|
||||
} else { // READY or PAUSED
|
||||
g.lineStyle(0, 0, 0);
|
||||
g.beginFill(0x00FF00);
|
||||
g.moveTo(-4, -10);
|
||||
g.lineTo(4, 0);
|
||||
g.lineTo(-4, 10);
|
||||
g.lineTo(-4, -10);
|
||||
g.endFill();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,8 +273,17 @@ public class SimpleVideoDisplay extends Sprite
|
||||
protected var _player :VideoPlayer;
|
||||
|
||||
protected var _hud :Sprite;
|
||||
|
||||
protected var _track :Sprite;
|
||||
|
||||
/** The seek selector knob, positioned on the hud. */
|
||||
protected var _knob :Sprite;
|
||||
|
||||
/** Our mask, also defines our boundaries for clicking. */
|
||||
protected var _mask :Shape;
|
||||
|
||||
protected static const PAD :int = 10;
|
||||
protected static const TRACK_HEIGHT :int = 20;
|
||||
protected static const TRACK_WIDTH :int = NATIVE_WIDTH - (PAD * 2);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user