├── Makefile
├── README.md
├── build.bat
├── build.sh
├── docs
├── demo.html
├── jstest.html
└── vgaplayer.swf
├── src
├── ControlBar.as
├── FullscreenButton.as
├── Main.as
├── OverlayButton.as
├── PlayPauseButton.as
├── SeekBar.as
├── StatusDisplay.as
├── VolumeSlider.as
└── baseui
│ ├── Button.as
│ ├── Control.as
│ ├── MenuItem.as
│ ├── MenuItemEvent.as
│ ├── MenuPopup.as
│ ├── PopupMenuButton.as
│ ├── Slider.as
│ ├── Style.as
│ └── TextMenuItem.as
└── vgaplayer.as3proj
/Makefile:
--------------------------------------------------------------------------------
1 | # GNUMakefile
2 | RSYNC=/usr/bin/rsync \
3 | --exclude NOBACKUP/ \
4 | --exclude LOCAL/ \
5 | --exclude local/ \
6 | --exclude tmp/ \
7 | --exclude obj/ \
8 | --exclude Makefile \
9 | --exclude '.??*' \
10 | --exclude '*~'
11 |
12 | PROJECT=vgaplayer
13 | WWWBASE=../../euske.github.io/$(PROJECT)/
14 |
15 | all:
16 |
17 | clean:
18 |
19 | upload: bin
20 | -$(RSYNC) -rutv bin/ $(WWWBASE)/
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | VGA Player
2 | ==========
3 |
4 | VGAPlayer is an open source player for FLV videos (static or RTMP stream).
5 |
6 | Demo: http://euske.github.io/vgaplayer/demo.html
7 |
8 | Typical usage:
9 |
10 |
16 |
17 | FlashVars Parameters:
18 |
19 | It takes a form of `FlashVars="name=value&name=value&..."`
20 |
21 | * url: RTMP URL. (e.g. "rtmp://example.com/live" or "/app/live")
22 | * debug: Indicates if the debug console is displayed. (1: on, 0: off)
23 | * fullscreen: Indicates if the fullscreen button is shown. (1: on, 0: off)
24 | * smoothing: Indicates if the video smoothing is turned on. (1: on, 0: off)
25 | * start: Start position of the stream. (default: 0)
26 | * autoplay: Start playing automatically. (1: on, 0: off)
27 | * bufferTime: Stream buffering time. (default: 1.0 sec)
28 | * bufferTimeMax: Maximum stream buffering time. (default: 1.0 sec)
29 | * bgColor: Background color. (default: "#000000")
30 | * buttonBgColor: Button background color. The upper 8 bits are for alpha. (default: "#448888ff")
31 | * buttonFgColor: Button foreground color. (default: "#cc888888")
32 | * buttonHiFgColor: Button highlighted foreground color. (default: "#ffeeeeee")
33 | * buttonHiBgColor: Button highlighted background color. (default: "#ff444488")
34 | * buttonBorderColor: Button border color. (default: "#88ffffff")
35 | * volumeMutedColor: Color used when the volume is muted. (default: "#ffff0000")
36 | * imageUrl: Background image URL.
37 | * menu: Add a menu. (Explained below.) (1: on, 0: off)
38 | * pid: A string to identify a player in Javascript. (Explained below.)
39 |
40 | Adding a Menu
41 | -------------
42 |
43 | It is possible to add a menu. (Javascript required.)
44 | There are two Javascript callback functions:
45 | `VGAPlayerOnLoad` and `VGAPlayerOnMenuChoose`.
46 |
47 | Demo: http://euske.github.io/vgaplayer/jstest.html
48 |
49 |
60 | ...
61 |
68 |
69 |
70 | Terms and Conditions
71 | --------------------
72 |
73 | (This is so-called MIT/X License)
74 |
75 | Copyright (c) 2014 Yusuke Shinyama
76 |
77 | Permission is hereby granted, free of charge, to any person
78 | obtaining a copy of this software and associated documentation
79 | files (the "Software"), to deal in the Software without
80 | restriction, including without limitation the rights to use,
81 | copy, modify, merge, publish, distribute, sublicense, and/or
82 | sell copies of the Software, and to permit persons to whom the
83 | Software is furnished to do so, subject to the following
84 | conditions:
85 |
86 | The above copyright notice and this permission notice shall be
87 | included in all copies or substantial portions of the Software.
88 |
89 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
90 | KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
91 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
92 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
93 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
94 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
95 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
96 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
97 |
--------------------------------------------------------------------------------
/build.bat:
--------------------------------------------------------------------------------
1 | @set FLEX_HOME=%UserProfile%\flex_sdk_4.6
2 | java -jar %FLEX_HOME%\lib\mxmlc.jar +flexlib=%FLEX_HOME%\frameworks -static-rsls -o .\bin\vgaplayer.swf -compiler.source-path=./src .\src\Main.as
3 | @if errorlevel 1 (
4 | pause
5 | exit /b
6 | )
7 |
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | JAVA=java
3 | FLEX_HOME=${FLEX_HOME:-$HOME/flex_sdk_4.6}
4 | MXMLC="$JAVA -jar $FLEX_HOME/lib/mxmlc.jar +flexlib=$FLEX_HOME/frameworks"
5 |
6 | $MXMLC -static-rsls -o ./bin/vgaplayer.swf -compiler.source-path=./src/ ./src/Main.as
7 |
--------------------------------------------------------------------------------
/docs/demo.html:
--------------------------------------------------------------------------------
1 |
2 | VGAPlayer Test
3 |
4 |
15 |
16 |
--------------------------------------------------------------------------------
/docs/jstest.html:
--------------------------------------------------------------------------------
1 |
2 | VGAPlayer Javascript Test
3 |
4 |
17 |
24 |
25 |
--------------------------------------------------------------------------------
/docs/vgaplayer.swf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/euske/vgaplayer/90fc691370493291cafb5e0a6c39e2bb9fa0beee/docs/vgaplayer.swf
--------------------------------------------------------------------------------
/src/ControlBar.as:
--------------------------------------------------------------------------------
1 | package {
2 |
3 | import flash.display.Sprite;
4 | import flash.utils.getTimer;
5 | import baseui.Style;
6 | import baseui.PopupMenuButton;
7 |
8 | // ControlBar
9 | // Bar shown at the bottom of screen containing buttons, etc.
10 | //
11 | public class ControlBar extends Sprite
12 | {
13 | public var margin:int = 4;
14 | public var fadeDuration:int = 1000;
15 |
16 | public var playButton:PlayPauseButton;
17 | public var volumeSlider:VolumeSlider;
18 | public var seekBar:SeekBar;
19 | public var statusDisplay:StatusDisplay;
20 | public var popupMenu:PopupMenuButton;
21 | public var fsButton:FullscreenButton;
22 |
23 | private var _autohide:Boolean;
24 | private var _timeout:int;
25 |
26 | public function ControlBar(fullscreen:Boolean=false,
27 | menu:Boolean=false)
28 | {
29 | super();
30 | _timeout = -fadeDuration;
31 |
32 | playButton = new PlayPauseButton();
33 | addChild(playButton);
34 |
35 | volumeSlider = new VolumeSlider();
36 | volumeSlider.value = 1.0;
37 | addChild(volumeSlider);
38 |
39 | seekBar = new SeekBar();
40 | seekBar.visible = false;
41 | addChild(seekBar);
42 |
43 | statusDisplay = new StatusDisplay();
44 | addChild(statusDisplay);
45 |
46 | if (menu) {
47 | popupMenu = new PopupMenuButtonOfDoom();
48 | addChild(popupMenu);
49 | }
50 |
51 | if (fullscreen) {
52 | fsButton = new FullscreenButton();
53 | addChild(fsButton);
54 | }
55 | }
56 |
57 | public function get autohide():Boolean
58 | {
59 | return _autohide;
60 | }
61 |
62 | public function set autohide(value:Boolean):void
63 | {
64 | _autohide = value;
65 | }
66 |
67 | public function set style(value:Style):void
68 | {
69 | playButton.style = value;
70 | volumeSlider.style = value;
71 | seekBar.style = value;
72 | statusDisplay.style = value;
73 | if (popupMenu != null) {
74 | popupMenu.style = value;
75 | }
76 | if (fsButton != null) {
77 | fsButton.style = value;
78 | }
79 | }
80 |
81 | public function show(duration:int=2000):void
82 | {
83 | _timeout = getTimer()+duration;
84 | }
85 |
86 | public function resize(w:int, h:int):void
87 | {
88 | var size:int = h - margin*2;
89 | var x0:int = margin;
90 | var x1:int = w - margin;
91 |
92 | graphics.clear();
93 | graphics.beginFill(0, 0.5);
94 | graphics.drawRect(0, 0, w, h);
95 | graphics.endFill();
96 |
97 | playButton.resize(size, size);
98 | playButton.x = x0;
99 | playButton.y = margin;
100 | x0 += playButton.width + margin;
101 |
102 | if (fsButton != null) {
103 | fsButton.resize(size, size);
104 | fsButton.x = x1 - fsButton.width;
105 | fsButton.y = margin;
106 | x1 = fsButton.x - margin;
107 | }
108 |
109 | if (popupMenu != null) {
110 | popupMenu.resize(size, size);
111 | popupMenu.x = x1 - popupMenu.width;
112 | popupMenu.y = margin;
113 | x1 = popupMenu.x - margin;
114 | }
115 |
116 | volumeSlider.resize(size*2, size);
117 | volumeSlider.x = x1 - volumeSlider.width;
118 | volumeSlider.y = margin;
119 | x1 = volumeSlider.x - margin;
120 |
121 | seekBar.resize(x1-x0, size);
122 | seekBar.x = x0;
123 | seekBar.y = margin;
124 |
125 | statusDisplay.resize(x1-x0, size);
126 | statusDisplay.x = x0;
127 | statusDisplay.y = margin;
128 | }
129 |
130 | public function update():void
131 | {
132 | if (_autohide) {
133 | var a:Number = (_timeout - getTimer())/fadeDuration + 1.0;
134 | alpha = Math.min(Math.max(a, 0.0), 1.0);
135 | } else {
136 | alpha = 1.0;
137 | }
138 | playButton.update();
139 | volumeSlider.update();
140 | seekBar.update();
141 | statusDisplay.update();
142 | if (popupMenu != null) {
143 | popupMenu.update();
144 | }
145 | if (fsButton != null) {
146 | fsButton.update();
147 | }
148 | }
149 | }
150 |
151 | } // package
152 |
153 | import baseui.PopupMenuButton;
154 |
155 | class PopupMenuButtonOfDoom extends PopupMenuButton
156 | {
157 | public override function repaint():void
158 | {
159 | super.repaint();
160 |
161 | // draw a gear.
162 | var size:int = buttonSize/8;
163 | var color:uint = (highlit)? style.hiFgColor : style.fgColor;
164 | var cx:int = width/2 + ((pressed)? 1 : 0);
165 | var cy:int = height/2 + ((pressed)? 1 : 0);
166 |
167 | graphics.beginFill(color, (color>>>24)/255);
168 | graphics.moveTo(cx+size*3, cy);
169 | const T:Number = 2.0*Math.PI/32;
170 | for (var i:int = 0; i < 32; i++) {
171 | var r:Number = ((i % 4) < 2)? 3 : 2;
172 | graphics.lineTo(cx+Math.cos(T*i)*size*r, cy+Math.sin(T*i)*size*r);
173 | }
174 | graphics.lineTo(cx+size*3, cy);
175 | graphics.drawCircle(cx, cy, size*1);
176 | graphics.endFill();
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/src/FullscreenButton.as:
--------------------------------------------------------------------------------
1 | package {
2 |
3 | import baseui.Button;
4 |
5 | // FullscreenButton
6 | // Fullscreen/Windowed toggle button. (part of ControlBar)
7 | //
8 | public class FullscreenButton extends Button
9 | {
10 | private var _toFullscreen:Boolean = false;
11 |
12 | public function get toFullscreen():Boolean
13 | {
14 | return _toFullscreen;
15 | }
16 |
17 | public function set toFullscreen(value:Boolean):void
18 | {
19 | _toFullscreen = value;
20 | invalidate();
21 | }
22 |
23 | public override function repaint():void
24 | {
25 | super.repaint();
26 | var size:int = buttonSize/16;
27 | var color:uint = (highlit)? style.hiFgColor : style.fgColor;
28 | var cx:int = width/2 + ((pressed)? 1 : 0);
29 | var cy:int = height/2 + ((pressed)? 1 : 0);
30 |
31 | if (_toFullscreen) {
32 | graphics.beginFill(color, (color>>>24)/255);
33 | graphics.drawRect(cx-size*7, cy-size*4, size*14, size*8);
34 | graphics.endFill();
35 | } else {
36 | graphics.lineStyle(0, color, (color>>>24)/255);
37 | graphics.drawRect(cx-size*7, cy-size*5, size*10, size*6);
38 | graphics.drawRect(cx-size*2, cy-size*1, size*9, size*7);
39 | }
40 | }
41 | }
42 |
43 | } // package
44 |
--------------------------------------------------------------------------------
/src/Main.as:
--------------------------------------------------------------------------------
1 | // VGAPlayer
2 | //
3 |
4 | package {
5 |
6 | import flash.display.Sprite;
7 | import flash.display.DisplayObject;
8 | import flash.display.Loader;
9 | import flash.display.LoaderInfo;
10 | import flash.display.StageDisplayState;
11 | import flash.display.StageScaleMode;
12 | import flash.display.StageAlign;
13 | import flash.events.Event;
14 | import flash.events.KeyboardEvent;
15 | import flash.events.MouseEvent;
16 | import flash.events.FullScreenEvent;
17 | import flash.events.NetStatusEvent;
18 | import flash.events.AsyncErrorEvent;
19 | import flash.media.Video;
20 | import flash.media.SoundTransform;
21 | import flash.net.NetConnection;
22 | import flash.net.NetStream;
23 | import flash.net.URLRequest;
24 | import flash.ui.Keyboard;
25 | import flash.external.ExternalInterface;
26 | import flash.system.Security;
27 | import baseui.Style;
28 | import baseui.Slider;
29 | import baseui.MenuItemEvent;
30 |
31 | // Main
32 | //
33 | public class Main extends Sprite
34 | {
35 | private const STARTING:String = "STARTING";
36 | private const STARTED:String = "STARTED";
37 | private const STOPPING:String = "STOPPING";
38 | private const STOPPED:String = "STOPPED";
39 | private const PAUSED:String = "PAUSED"; // only used for non-remoting streams.
40 |
41 | private var _config:Config;
42 | private var _video:Video;
43 | private var _overlay:OverlayButton;
44 | private var _control:ControlBar;
45 | private var _debugdisp:DebugDisplay;
46 | private var _imageLoader:Loader;
47 |
48 | private var _url:String;
49 | private var _streamPath:String;
50 | private var _remoting:Boolean; // true if connected via RTMP or FMS.
51 | private var _connection:NetConnection;
52 | private var _stream:NetStream;
53 | private var _videoMetaData:Object;
54 | private var _state:String;
55 |
56 | // Main()
57 | public function Main()
58 | {
59 | var info:LoaderInfo = LoaderInfo(this.root.loaderInfo);
60 | _config = new Config(info.parameters);
61 |
62 | stage.color = _config.bgColor;
63 | stage.scaleMode = StageScaleMode.NO_SCALE;
64 | stage.align = StageAlign.TOP_LEFT;
65 |
66 | if (_config.imageUrl != null) {
67 | _imageLoader = new Loader();
68 | _imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageLoaded);
69 | _imageLoader.load(new URLRequest(_config.imageUrl));
70 | addChild(_imageLoader);
71 | }
72 |
73 | _video = new Video();
74 | _video.smoothing = _config.smoothing;
75 | addChild(_video);
76 |
77 | _overlay = new OverlayButton();
78 | _overlay.visible = false;
79 | _overlay.style = _config.style;
80 | _overlay.addEventListener(MouseEvent.CLICK, onOverlayClick);
81 | addChild(_overlay);
82 |
83 | _control = new ControlBar(_config.fullscreen, _config.menu);
84 | _control.style = _config.style;
85 | _control.playButton.addEventListener(MouseEvent.CLICK, onPlayPauseClick);
86 | _control.volumeSlider.addEventListener(Slider.CLICK, onVolumeSliderClick);
87 | _control.volumeSlider.addEventListener(Slider.CHANGED, onVolumeSliderChanged);
88 | _control.seekBar.addEventListener(Slider.CHANGED, onSeekBarChanged);
89 | if (_control.popupMenu != null) {
90 | _control.popupMenu.container = this;
91 | _control.popupMenu.addEventListener(MenuItemEvent.CHOOSE, onMenuItemChoose);
92 | }
93 | if (_control.fsButton != null) {
94 | _control.fsButton.toFullscreen = (stage.displayState == StageDisplayState.NORMAL);
95 | _control.fsButton.addEventListener(MouseEvent.CLICK, onFullscreenClick);
96 | }
97 | addChild(_control);
98 |
99 | _debugdisp = new DebugDisplay();
100 | _debugdisp.visible = _config.debug;
101 | addChild(_debugdisp);
102 |
103 | addEventListener(Event.ADDED_TO_STAGE, onAdded);
104 | addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
105 | addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
106 | addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
107 | stage.addEventListener(Event.RESIZE, onResize);
108 | stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
109 | stage.addEventListener(FullScreenEvent.FULL_SCREEN, onFullScreen);
110 | stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
111 | resize();
112 |
113 | log("FlashVars:", expandAttrs(info.parameters));
114 | log("url:", _config.url);
115 | log("fullscreen:", _config.fullscreen);
116 | log("bufferTime:", _config.bufferTime);
117 | log("bufferTimeMax:", _config.bufferTimeMax);
118 | log("maxPauseBufferTime:", _config.maxPauseBufferTime);
119 | log("backBufferTime:", _config.backBufferTime);
120 | log("inBufferSeek:", _config.inBufferSeek);
121 |
122 | _connection = new NetConnection();
123 | _connection.addEventListener(NetStatusEvent.NET_STATUS, onNetStatusEvent);
124 | _connection.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncErrorEvent);
125 |
126 | updateStatus(STOPPED);
127 |
128 | if (ExternalInterface.available) {
129 | // Allowing security domains.
130 | // Not sure if this is the right way...
131 | var domain:String = (Security.pageDomain == null)? "*" : Security.pageDomain;
132 | log("ExternalInterface: allowing: "+domain);
133 | Security.allowDomain(domain);
134 | ExternalInterface.addCallback("VGAPlayerAddMenuItem", externalAddMenuItem);
135 | ExternalInterface.addCallback("VGAPlayerConnect", externalConnect);
136 | }
137 | }
138 |
139 | private function log(... args):void
140 | {
141 | var x:String = "";
142 | for each (var a:Object in args) {
143 | if (x.length != 0) x += " ";
144 | x += a;
145 | }
146 | _debugdisp.writeLine(x);
147 | trace(x);
148 | }
149 |
150 | private function expandAttrs(obj:Object):String
151 | {
152 | var x:String = null;
153 | for (var key:Object in obj) {
154 | var value:Object = obj[key];
155 | if (x == null) {
156 | x = key+"="+value;
157 | } else {
158 | x += ", "+key+"="+value;
159 | }
160 | }
161 | return x;
162 | }
163 |
164 | private function getCanonicalizedURL(url:String, proto:String="rtmp"):String
165 | {
166 | if (url.indexOf("://") < 0) {
167 | // Resolve a relative url.
168 | var info:LoaderInfo = LoaderInfo(this.root.loaderInfo);
169 | var basehref:String = info.loaderURL;
170 | var i:int;
171 | if (url.substr(0, 1) == "/") {
172 | i = basehref.indexOf("://");
173 | if (0 < i) {
174 | basehref = basehref.substring(i+3);
175 | i = basehref.indexOf("/");
176 | if (i < 0) {
177 | i = basehref.length;
178 | }
179 | url = proto+"://"+basehref.substr(0, i)+url;
180 | }
181 | } else {
182 | i = basehref.lastIndexOf("/");
183 | url = basehref.substr(0, i+1)+url;
184 | }
185 | }
186 | return url;
187 | }
188 |
189 | private function onAdded(e:Event):void
190 | {
191 | init();
192 | }
193 |
194 | private function onResize(e:Event):void
195 | {
196 | resize();
197 | }
198 |
199 | private function onFullScreen(e:FullScreenEvent):void
200 | {
201 | _control.fsButton.toFullscreen = !e.fullScreen;
202 | }
203 |
204 | private function onEnterFrame(e:Event):void
205 | {
206 | update();
207 | }
208 |
209 | private function onMouseDown(e:MouseEvent):void
210 | {
211 | _control.show();
212 | }
213 | private function onMouseUp(e:MouseEvent):void
214 | {
215 | _control.show();
216 | }
217 | private function onMouseMove(e:MouseEvent):void
218 | {
219 | _control.show();
220 | }
221 |
222 | private function onKeyDown(e:KeyboardEvent):void
223 | {
224 | _control.show();
225 | switch (e.keyCode) {
226 | case Keyboard.ESCAPE: // Esc
227 | case 68: // D
228 | // Toggle the debug window if debug = 1.
229 | if (_config.debug) {
230 | _debugdisp.visible = !_debugdisp.visible;
231 | }
232 | break;
233 | case Keyboard.SPACE:
234 | // Toggle play/stop.
235 | setPlayState(_control.playButton.state == _control.playButton.PLAY);
236 | break;
237 | case Keyboard.LEFT:
238 | // Rewind for 10 sec.
239 | seekDelta(-10);
240 | break;
241 | case Keyboard.RIGHT:
242 | // Fast-forward for 10 sec.
243 | seekDelta(+10);
244 | break;
245 | case Keyboard.UP:
246 | // Rewind for 1 min.
247 | seekDelta(-60);
248 | break;
249 | case Keyboard.DOWN:
250 | // Fast-forward for 1 min.
251 | seekDelta(+60);
252 | break;
253 | case Keyboard.PAGE_UP:
254 | // Rewind for 10 min.
255 | seekDelta(-600);
256 | break;
257 | case Keyboard.PAGE_DOWN:
258 | // Fast-forward for 10 min.
259 | seekDelta(+600);
260 | break;
261 | case Keyboard.HOME:
262 | // Rewind to the beginning.
263 | seek(0);
264 | break;
265 | }
266 | }
267 |
268 | private function onNetStatusEvent(ev:NetStatusEvent):void
269 | {
270 | log("onNetStatusEvent:", expandAttrs(ev.info));
271 | switch (ev.info.code) {
272 | case "NetConnection.Connect.Failed":
273 | case "NetConnection.Connect.Rejected":
274 | case "NetConnection.Connect.InvalidApp":
275 | updateStatus(STOPPED, "Failed");
276 | break;
277 |
278 | case "NetConnection.Connect.Success":
279 | var nc:NetConnection = NetConnection(ev.target);
280 | _stream = new NetStream(nc);
281 | _stream.addEventListener(NetStatusEvent.NET_STATUS, onNetStatusEvent);
282 | _stream.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncErrorEvent);
283 | _stream.client = new Object();
284 | _stream.client.onMetaData = onMetaData;
285 | _stream.client.onCuePoint = onCuePoint;
286 | _stream.client.onPlayStatus = onPlayStatus;
287 | _stream.inBufferSeek = _config.inBufferSeek;
288 | _stream.bufferTime = _config.bufferTime;
289 | _stream.bufferTimeMax = _config.bufferTimeMax;
290 | _stream.maxPauseBufferTime = _config.maxPauseBufferTime;
291 | _stream.backBufferTime = _config.backBufferTime;
292 | _video.attachNetStream(_stream);
293 | _control.seekBar.isStatic = !_remoting;
294 | updateVolume(_control.volumeSlider);
295 | startPlaying(_config.start);
296 | break;
297 |
298 | case "NetConnection.Connect.Closed":
299 | stopPlaying();
300 | _video.attachNetStream(null);
301 | _stream.removeEventListener(NetStatusEvent.NET_STATUS, onNetStatusEvent);
302 | _stream.removeEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncErrorEvent);
303 | _stream.client = null;
304 | _stream = null;
305 | updateStatus(STOPPED, "Disconnected");
306 | break;
307 |
308 | case "NetStream.Play.Start":
309 | updateStatus(STARTING);
310 | break;
311 |
312 | case "NetStream.Play.Stop":
313 | case "NetStream.Play.Complete":
314 | case "NetStream.Buffer.Flush":
315 | updateStatus(STOPPED);
316 | break;
317 |
318 | case "NetStream.Buffer.Empty":
319 | updateStatus(STARTING);
320 | break;
321 |
322 | case "NetStream.Buffer.Full":
323 | if (_state == STOPPING) {
324 | stopPlaying();
325 | } else {
326 | updateStatus(STARTED);
327 | }
328 | break;
329 | }
330 | }
331 |
332 | private function onImageLoaded(event:Event):void
333 | {
334 | resize();
335 | }
336 |
337 | private function onMetaData(info:Object):void
338 | {
339 | log("onMetaData:", expandAttrs(info));
340 | _videoMetaData = info;
341 | if (0 < _videoMetaData.duration) {
342 | // Show the seek bar when the video duration is defined.
343 | _control.statusDisplay.visible = false;
344 | _control.seekBar.duration = _videoMetaData.duration;
345 | _control.seekBar.bytesTotal = _videoMetaData.filesize;
346 | _control.seekBar.visible = true;
347 | } else {
348 | _control.statusDisplay.visible = true;
349 | _control.seekBar.duration = 0;
350 | _control.seekBar.visible = false;
351 | }
352 | updateStatus(_state);
353 | resize();
354 | }
355 |
356 | private function onCuePoint(info:Object):void
357 | {
358 | log("onCuePoint:", expandAttrs(info));
359 | }
360 |
361 | private function onPlayStatus(info:Object):void
362 | {
363 | log("onPlayStatus:", expandAttrs(info));
364 | }
365 |
366 | private function onAsyncErrorEvent(ev:AsyncErrorEvent):void
367 | {
368 | log("onAsyncErrorEvent:", ev.error);
369 | }
370 |
371 | private function onOverlayClick(e:MouseEvent):void
372 | {
373 | var overlay:OverlayButton = OverlayButton(e.target);
374 | overlay.show();
375 | setPlayState(overlay.state == overlay.PLAY);
376 | }
377 |
378 | private function onPlayPauseClick(e:Event):void
379 | {
380 | var button:PlayPauseButton = PlayPauseButton(e.target);
381 | setPlayState(button.state == button.PLAY);
382 | }
383 |
384 | private function onVolumeSliderClick(e:Event):void
385 | {
386 | var slider:VolumeSlider = VolumeSlider(e.target);
387 | slider.muted = !slider.muted;
388 | updateVolume(slider);
389 | }
390 |
391 | private function onVolumeSliderChanged(e:Event):void
392 | {
393 | var slider:VolumeSlider = VolumeSlider(e.target);
394 | updateVolume(slider);
395 | }
396 |
397 | private function onSeekBarChanged(e:Event):void
398 | {
399 | var seekbar:SeekBar = SeekBar(e.target);
400 | seek(seekbar.time);
401 | }
402 |
403 | private function onFullscreenClick(e:Event):void
404 | {
405 | var button:FullscreenButton = FullscreenButton(e.target);
406 | stage.displayState = ((button.toFullscreen)?
407 | StageDisplayState.FULL_SCREEN :
408 | StageDisplayState.NORMAL);
409 | }
410 |
411 | private function onMenuItemChoose(e:MenuItemEvent):void
412 | {
413 | log("onMenuItemChoose:", e.item.value);
414 |
415 | if (ExternalInterface.available) {
416 | ExternalInterface.call("VGAPlayerOnMenuChoose", e.item.value);
417 | }
418 | }
419 |
420 | private function proportionalScaleToStage(obj:DisplayObject, w:int, h:int):void
421 | {
422 | var r:Number = Math.min((stage.stageWidth / w),
423 | (stage.stageHeight / h));
424 | obj.width = w*r;
425 | obj.height = h*r;
426 | obj.x = (stage.stageWidth - obj.width)/2;
427 | obj.y = (stage.stageHeight - obj.height)/2;
428 | }
429 |
430 | private function updateVolume(slider:VolumeSlider):void
431 | {
432 | if (_stream != null) {
433 | var transform:SoundTransform =
434 | new SoundTransform((slider.muted)? 0 : slider.value);
435 | _stream.soundTransform = transform;
436 | }
437 | }
438 |
439 | private function updateStatus(state:String, text:String=null):void
440 | {
441 | _state = state;
442 | switch (_state) {
443 | case STARTING:
444 | _overlay.state = _overlay.BUSY;
445 | _overlay.autohide = false;
446 | _control.playButton.state = _control.playButton.BUSY;
447 | if (text == null) {
448 | text = "Starting...";
449 | }
450 | break;
451 |
452 | case STARTED:
453 | _control.seekBar.unlock();
454 | _overlay.state = _overlay.PAUSE;
455 | _overlay.autohide = true;
456 | _control.playButton.state = _control.playButton.PAUSE;
457 | _control.autohide = true;
458 | if (text == null) {
459 | text = "Playing";
460 | }
461 | break;
462 |
463 | case STOPPING:
464 | _overlay.state = _overlay.BUSY;
465 | _overlay.autohide = false;
466 | _control.playButton.state = _control.playButton.BUSY;
467 | if (text == null) {
468 | text = "Stopping...";
469 | }
470 | break;
471 |
472 | case STOPPED:
473 | case PAUSED:
474 | _overlay.state = _overlay.PLAY;
475 | _overlay.autohide = false;
476 | _control.playButton.state = _control.playButton.PLAY;
477 | _control.autohide = false;
478 | if (text == null) {
479 | text = "Stopped";
480 | }
481 | break;
482 | }
483 |
484 | _control.statusDisplay.text = text;
485 | }
486 |
487 | private function startPlaying(start:Number):void
488 | {
489 | if (_streamPath != null && _stream != null) {
490 | updateStatus(STARTING);
491 | log("Starting:", _streamPath, start);
492 | _stream.play(_streamPath, start);
493 | }
494 | }
495 |
496 | private function stopPlaying():void
497 | {
498 | if (_stream != null) {
499 | if (_remoting) {
500 | log("Stopping");
501 | updateStatus(STOPPING);
502 | _stream.close();
503 | } else {
504 | updateStatus(PAUSED);
505 | _stream.pause();
506 | }
507 | }
508 | }
509 |
510 | private function init():void
511 | {
512 | log("init");
513 |
514 | // Notify the browser if possible.
515 | if (ExternalInterface.available) {
516 | ExternalInterface.call("VGAPlayerOnLoad", _config.pid);
517 | }
518 |
519 | if (_config.url != null) {
520 | _overlay.visible = true;
521 | _overlay.state = _overlay.PLAY;
522 | if (_config.autoplay) {
523 | _url = _config.url;
524 | connect();
525 | }
526 | }
527 | }
528 |
529 | private function resize():void
530 | {
531 | log("resize:", stage.stageWidth+","+stage.stageHeight);
532 |
533 | if (_videoMetaData != null) {
534 | proportionalScaleToStage(_video, _videoMetaData.width, _videoMetaData.height);
535 | }
536 | if (_imageLoader != null) {
537 | proportionalScaleToStage(_imageLoader, _imageLoader.width, _imageLoader.height);
538 | }
539 |
540 | _overlay.resize(stage.stageWidth, stage.stageHeight);
541 | _overlay.x = 0;
542 | _overlay.y = 0;
543 |
544 | _control.resize(stage.stageWidth, 28);
545 | _control.x = 0;
546 | _control.y = stage.stageHeight-_control.height;
547 |
548 | _debugdisp.resize(stage.stageWidth, stage.stageHeight-_control.height);
549 | _debugdisp.x = 0;
550 | _debugdisp.y = 0;
551 | }
552 |
553 | private function update():void
554 | {
555 | _overlay.update();
556 | _control.update();
557 | if (_stream != null) {
558 | if (_state == STARTED) {
559 | _control.seekBar.time = _stream.time;
560 | }
561 | if (0 < _stream.bytesTotal) {
562 | _control.seekBar.bytesLoaded = _stream.bytesLoaded;
563 | }
564 | if (_debugdisp.visible) {
565 | _debugdisp.update(_stream);
566 | }
567 | }
568 | }
569 |
570 | private function externalAddMenuItem(label:String, value:String=null):void
571 | {
572 | log("externalAddMenuItem: "+label+", "+value);
573 | if (_control.popupMenu != null) {
574 | _control.popupMenu.addTextItem(label, value);
575 | }
576 | }
577 |
578 | private function externalConnect(url:String):void
579 | {
580 | stopPlaying();
581 | _url = url;
582 | connect();
583 | }
584 |
585 | public function connect():void
586 | {
587 | if (_connection.connected) {
588 | _connection.close();
589 | }
590 | if (_url != null && !_connection.connected) {
591 | var url:String = getCanonicalizedURL(_url);
592 | if (url.substr(0, 5) == "rtmp:") {
593 | var i:int = url.lastIndexOf("/");
594 | _streamPath = url.substr(i+1);
595 | _remoting = true;
596 | url = url.substr(0, i);
597 | } else {
598 | _streamPath = url;
599 | _remoting = false;
600 | url = null;
601 | }
602 | log("Connecting:", url);
603 | _overlay.visible = true;
604 | _control.statusDisplay.text = "Connecting...";
605 | _connection.connect(url);
606 | }
607 | }
608 |
609 | public function setPlayState(playing:Boolean):void
610 | {
611 | log("setPlayState:", playing);
612 | switch (_state) {
613 | case STARTING:
614 | if (!playing) {
615 | updateStatus(STOPPING);
616 | }
617 | break;
618 | case STARTED:
619 | if (!playing) {
620 | stopPlaying();
621 | }
622 | break;
623 | case STOPPED:
624 | if (playing) {
625 | if (_connection.connected) {
626 | startPlaying(_control.seekBar.time);
627 | } else {
628 | connect();
629 | }
630 | }
631 | break;
632 | case PAUSED:
633 | if (playing) {
634 | if (_stream != null) {
635 | _stream.resume();
636 | }
637 | }
638 | break;
639 | }
640 | }
641 |
642 | public function seek(t:Number):void
643 | {
644 | log("seek:", t);
645 | if (_stream != null) {
646 | _stream.seek(t);
647 | }
648 | }
649 |
650 | public function seekDelta(dt:Number):void
651 | {
652 | log("seekDelta:", dt);
653 | if (_stream != null) {
654 | _stream.seek(_stream.time + dt);
655 | }
656 | }
657 |
658 | }
659 |
660 | } // package
661 |
662 | /// Private classes below.
663 |
664 | import flash.display.Sprite;
665 | import flash.text.TextField;
666 | import flash.text.TextFieldType;
667 | import flash.text.TextFormat;
668 | import flash.net.NetStream;
669 | import flash.net.NetStreamInfo;
670 | import baseui.Style;
671 |
672 | // Config
673 | // Object to hold the parameters given by FlashVars.
674 | //
675 | class Config extends Object
676 | {
677 | public var debug:Boolean = false;
678 | public var url:String = null;
679 | public var bufferTime:Number = 1.0;
680 | public var bufferTimeMax:Number = 0.0;
681 | public var maxPauseBufferTime:Number = 30.0;
682 | public var backBufferTime:Number = 30.0;
683 | public var inBufferSeek:Boolean = false;
684 | public var fullscreen:Boolean = false;
685 | public var menu:Boolean = false;
686 | public var smoothing:Boolean = false;
687 | public var start:Number = 0.0;
688 | public var autoplay:Boolean = true;
689 |
690 | public var bgColor:uint = 0x000000;
691 | public var style:Style = new Style();
692 | public var volumeMutedColor:uint = 0xffff0000;
693 | public var imageUrl:String = null;
694 | public var pid:String = null;
695 |
696 | public function Config(obj:Object)
697 | {
698 | super();
699 | if (obj != null) {
700 | // debug
701 | if (obj.debug) {
702 | debug = parseBoolean(obj.debug);
703 | }
704 | // url
705 | if (obj.url) {
706 | url = obj.url;
707 | }
708 | // bufferTime
709 | if (obj.bufferTime) {
710 | bufferTime = parseFloat(obj.bufferTime);
711 | bufferTimeMax = bufferTime;
712 | maxPauseBufferTime = bufferTime;
713 | }
714 | // bufferTimeMax
715 | if (obj.bufferTimeMax) {
716 | bufferTimeMax = parseFloat(obj.bufferTimeMax);
717 | }
718 | // maxPauseBufferTime
719 | if (obj.maxPauseBufferTime) {
720 | maxPauseBufferTime = parseFloat(obj.maxPauseBufferTime);
721 | }
722 | // backBufferTime
723 | if (obj.backBufferTime) {
724 | backBufferTime = parseFloat(obj.backBufferTime);
725 | }
726 | // inBufferSeek
727 | if (obj.inBufferSeek) {
728 | inBufferSeek = parseBoolean(obj.inBufferSeek);
729 | }
730 | // fullscreen
731 | if (obj.fullscreen) {
732 | fullscreen = parseBoolean(obj.fullscreen);
733 | }
734 | // menu
735 | if (obj.menu) {
736 | menu = parseBoolean(obj.menu);
737 | }
738 | // smoothing
739 | if (obj.smoothing) {
740 | smoothing = parseBoolean(obj.smoothing);
741 | }
742 | // start
743 | if (obj.start) {
744 | start = parseFloat(obj.start);
745 | }
746 | // autoplay
747 | if (obj.autoplay) {
748 | autoplay = parseBoolean(obj.autoplay);
749 | }
750 |
751 | // bgColor
752 | if (obj.bgColor) {
753 | bgColor = parseColor(obj.bgColor);
754 | }
755 | // buttonBgColor
756 | if (obj.buttonBgColor) {
757 | style.bgColor = parseColor(obj.buttonBgColor);
758 | }
759 | // buttonFgColor
760 | if (obj.buttonFgColor) {
761 | style.fgColor = parseColor(obj.buttonFgColor);
762 | }
763 | // buttonHiFgColor
764 | if (obj.buttonHiFgColor) {
765 | style.hiFgColor = parseColor(obj.buttonHiFgColor);
766 | }
767 | // buttonHiBgColor
768 | if (obj.buttonHiBgColor) {
769 | style.hiBgColor = parseColor(obj.buttonHiBgColor);
770 | }
771 | // buttonBorderColor
772 | if (obj.buttonBorderColor) {
773 | style.borderColor = parseColor(obj.buttonBorderColor);
774 | }
775 | // volumeMutedColor
776 | if (obj.volumeMutedColor) {
777 | volumeMutedColor = parseColor(obj.volumeMutedColor);
778 | }
779 | // imageUrl
780 | if (obj.imageUrl) {
781 | imageUrl = obj.imageUrl;
782 | }
783 | // pid
784 | if (obj.pid) {
785 | pid = obj.pid;
786 | }
787 | }
788 | }
789 |
790 | private function parseBoolean(v:String):Boolean
791 | {
792 | return (parseInt(v) != 0);
793 | }
794 |
795 | private function parseColor(v:String):uint
796 | {
797 | if (v.substr(0, 1) == "#") {
798 | v = v.substr(1);
799 | }
800 | return parseInt(v, 16);
801 | }
802 | }
803 |
804 |
805 | // DebugDisplay
806 | // Text areas showing the debug info.
807 | //
808 | class DebugDisplay extends Sprite
809 | {
810 | private var _logger:TextField;
811 | private var _playstat:TextField;
812 | private var _streaminfo:TextField;
813 |
814 | public function DebugDisplay()
815 | {
816 | super();
817 | _logger = new TextField();
818 | _logger.multiline = true;
819 | _logger.wordWrap = true;
820 | _logger.border = true;
821 | _logger.background = true;
822 | _logger.type = TextFieldType.DYNAMIC;
823 | _logger.width = 400;
824 | _logger.height = 100;
825 | addChild(_logger);
826 |
827 | _playstat = new TextField();
828 | _playstat.multiline = true;
829 | _playstat.textColor = 0xffffff;
830 | _playstat.type = TextFieldType.DYNAMIC;
831 | _playstat.text = "\n\n\n\n\n\n\n\n";
832 | _playstat.width = 200;
833 | _playstat.height = _playstat.textHeight+1;
834 | addChild(_playstat);
835 |
836 | _streaminfo = new TextField();
837 | _streaminfo.multiline = true;
838 | _streaminfo.textColor = 0xffff00;
839 | _streaminfo.type = TextFieldType.DYNAMIC;
840 | _streaminfo.text = "\n\n\n\n\n\n\n\n\n\n\n";
841 | _streaminfo.width = 200;
842 | _streaminfo.height = _streaminfo.textHeight+1;
843 | addChild(_streaminfo);
844 | }
845 |
846 | public function writeLine(x:String):void
847 | {
848 | _logger.appendText(x+"\n");
849 | _logger.scrollV = _logger.maxScrollV;
850 | }
851 |
852 | public function resize(w:int, h:int):void
853 | {
854 | _playstat.x = w - _playstat.width;
855 | _playstat.y = h - _playstat.height;
856 | _streaminfo.x = 0;
857 | _streaminfo.y = h - _streaminfo.height;
858 | }
859 |
860 | public function update(stream:NetStream):void
861 | {
862 | if (!visible) return;
863 |
864 | var text:String;
865 | text = ("time: "+stream.time+"\n"+
866 | "bufferLength: "+stream.bufferLength+"\n"+
867 | "backBufferLength: "+stream.backBufferLength+"\n"+
868 | "bytesLoaded: "+stream.bytesLoaded+"\n"+
869 | "bytesTotal: "+stream.bytesTotal+"\n"+
870 | "currentFPS: "+Math.floor(stream.currentFPS)+"\n"+
871 | "liveDelay: "+stream.liveDelay+"\n");
872 | _playstat.text = text;
873 |
874 | var info:NetStreamInfo = stream.info;
875 | text = ("isLive: "+info.isLive+"\n"+
876 | "byteCount: "+info.byteCount+"\n"+
877 | "audioBufferLength: "+info.audioBufferLength+"\n"+
878 | "videoBufferLength: "+info.videoBufferLength+"\n"+
879 | "currentBytesPerSecond: "+Math.floor(info.currentBytesPerSecond)+"\n"+
880 | "maxBytesPerSecond: "+Math.floor(info.maxBytesPerSecond)+"\n"+
881 | "audioBytesPerSecond: "+Math.floor(info.audioBytesPerSecond)+"\n"+
882 | "videoBytesPerSecond: "+Math.floor(info.videoBytesPerSecond)+"\n"+
883 | "playbackBytesPerSecond: "+Math.floor(info.playbackBytesPerSecond)+"\n"+
884 | "droppedFrames: "+info.droppedFrames+"\n");
885 | _streaminfo.text = text;
886 | }
887 | }
888 |
--------------------------------------------------------------------------------
/src/OverlayButton.as:
--------------------------------------------------------------------------------
1 | package {
2 |
3 | import flash.display.Sprite;
4 | import flash.events.MouseEvent;
5 | import flash.utils.getTimer;
6 | import baseui.Style;
7 |
8 | // OverlayButton
9 | // A transparent button shown over the video.
10 | //
11 | public class OverlayButton extends Sprite
12 | {
13 | public const PLAY:String = "PLAY";
14 | public const PAUSE:String = "PAUSE";
15 | public const BUSY:String = "BUSY";
16 |
17 | public var style:Style = new Style();
18 | public var buttonSize:int = 100;
19 | public var fadeDuration:int = 2000;
20 |
21 | private var _size:int;
22 | private var _width:int;
23 | private var _height:int;
24 | private var _invalidated:Boolean;
25 | private var _highlit:Boolean;
26 | private var _state:String;
27 | private var _autohide:Boolean;
28 | private var _timeout:int;
29 |
30 | public function OverlayButton()
31 | {
32 | super();
33 | _timeout = 0;
34 | alpha = 0;
35 | addEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
36 | addEventListener(MouseEvent.MOUSE_OUT, onMouseOut);
37 | addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
38 | }
39 |
40 | public function get state():String
41 | {
42 | return _state;
43 | }
44 |
45 | public function set state(value:String):void
46 | {
47 | _state = value;
48 | _invalidated = true;
49 | }
50 |
51 | public function get autohide():Boolean
52 | {
53 | return _autohide;
54 | }
55 |
56 | public function set autohide(value:Boolean):void
57 | {
58 | _autohide = value;
59 | }
60 |
61 | protected virtual function onMouseOver(e:MouseEvent):void
62 | {
63 | _highlit = true;
64 | _invalidated = true;
65 | }
66 |
67 | protected virtual function onMouseOut(e:MouseEvent):void
68 | {
69 | _highlit = false;
70 | _invalidated = true;
71 | }
72 |
73 | protected virtual function onMouseMove(e:MouseEvent):void
74 | {
75 | if (_timeout < getTimer()) {
76 | show();
77 | }
78 | }
79 |
80 | public function resize(w:int, h:int):void
81 | {
82 | _width = w;
83 | _height = h;
84 | _invalidated = true;
85 | }
86 |
87 | public function show():void
88 | {
89 | _timeout = getTimer()+fadeDuration;
90 | _invalidated = true;
91 | }
92 |
93 | public function repaint():void
94 | {
95 | graphics.clear();
96 | graphics.beginFill(0, 0);
97 | graphics.drawRect(0, 0, _width, _height);
98 | graphics.endFill();
99 |
100 | var size:int = buttonSize/16;
101 | var cx:int = width/2;
102 | var cy:int = height/2;
103 |
104 | var fgcolor:uint = (_highlit)? style.hiFgColor : style.fgColor;
105 | var bgcolor:uint = (_highlit)? style.hiBgColor : style.bgColor;
106 | graphics.beginFill(bgcolor, (bgcolor>>>24)/255);
107 | graphics.drawRect(cx-buttonSize/2, cy-buttonSize/2, buttonSize, buttonSize);
108 | graphics.endFill();
109 |
110 | switch (_state) {
111 | case PLAY:
112 | graphics.beginFill(fgcolor, (fgcolor>>>24)/255);
113 | graphics.moveTo(cx-size*3, cy-size*4);
114 | graphics.lineTo(cx-size*3, cy+size*4);
115 | graphics.lineTo(cx+size*4, cy);
116 | graphics.endFill();
117 | break;
118 | case PAUSE:
119 | graphics.beginFill(fgcolor, (fgcolor>>>24)/255);
120 | graphics.drawRect(cx-size*3, cy-size*4, size*2, size*8);
121 | graphics.drawRect(cx+size*1, cy-size*4, size*2, size*8);
122 | graphics.endFill();
123 | break;
124 | case BUSY:
125 | graphics.beginFill(fgcolor, (fgcolor>>>24)/255);
126 | graphics.drawCircle(cx-size*3, cy, size);
127 | graphics.drawCircle(cx, cy, size);
128 | graphics.drawCircle(cx+size*3, cy, size);
129 | graphics.endFill();
130 | break;
131 | }
132 | }
133 |
134 | public function update():void
135 | {
136 | if (_invalidated) {
137 | _invalidated = false;
138 | repaint();
139 | }
140 |
141 | if (_autohide) {
142 | var a:Number = (_timeout - getTimer())/fadeDuration;
143 | alpha = Math.min(Math.max(a, 0.0), 1.0);
144 | } else {
145 | alpha = 1.0;
146 | }
147 | }
148 | }
149 |
150 | } // package
151 |
--------------------------------------------------------------------------------
/src/PlayPauseButton.as:
--------------------------------------------------------------------------------
1 | package {
2 |
3 | import baseui.Button;
4 |
5 | // PlayPauseButton
6 | // Play/pause toggle button. (part of ControlBar)
7 | //
8 | public class PlayPauseButton extends Button
9 | {
10 | public const PLAY:String = "PLAY";
11 | public const PAUSE:String = "PAUSE";
12 | public const BUSY:String = "BUSY";
13 |
14 | private var _state:String;
15 |
16 | public function get state():String
17 | {
18 | return _state;
19 | }
20 |
21 | public function set state(value:String):void
22 | {
23 | _state = value;
24 | invalidate();
25 | }
26 |
27 | public override function repaint():void
28 | {
29 | super.repaint();
30 | var size:int = buttonSize/16;
31 | var color:uint = (highlit)? style.hiFgColor : style.fgColor;
32 | var cx:int = width/2 + ((pressed)? 1 : 0);
33 | var cy:int = height/2 + ((pressed)? 1 : 0);
34 |
35 | switch (_state) {
36 | case PLAY:
37 | graphics.beginFill(color, (color>>>24)/255);
38 | graphics.moveTo(cx-size*3, cy-size*4);
39 | graphics.lineTo(cx-size*3, cy+size*4);
40 | graphics.lineTo(cx+size*4, cy);
41 | graphics.endFill();
42 | break;
43 | case PAUSE:
44 | graphics.beginFill(color, (color>>>24)/255);
45 | graphics.drawRect(cx-size*3, cy-size*4, size*2, size*8);
46 | graphics.drawRect(cx+size*1, cy-size*4, size*2, size*8);
47 | graphics.endFill();
48 | break;
49 | }
50 | }
51 | }
52 |
53 | } // package
54 |
--------------------------------------------------------------------------------
/src/SeekBar.as:
--------------------------------------------------------------------------------
1 | package {
2 |
3 | import flash.text.TextField;
4 | import flash.text.TextFieldAutoSize;
5 | import flash.text.TextFormat;
6 | import flash.events.Event;
7 | import flash.events.MouseEvent;
8 | import baseui.Slider;
9 |
10 | // SeekBar
11 | // A horizontal seek bar. (part of ControlBar)
12 | //
13 | public class SeekBar extends Slider
14 | {
15 | public var margin:int = 4;
16 | public var barSize:int = 2;
17 |
18 | private var _text:TextField;
19 | private var _duration:Number = 0;
20 | private var _bytesTotal:uint = 0;
21 | private var _bytesLoaded:uint = 0;
22 | private var _static:Boolean = false;
23 | private var _locked:Boolean = false;
24 | private var _time:Number = 0;
25 | private var _goal:Number = 0;
26 |
27 | public function SeekBar()
28 | {
29 | super();
30 | _text = new TextField();
31 | _text.x = margin;
32 | _text.selectable = false;
33 | _text.autoSize = TextFieldAutoSize.LEFT;
34 | addChild(_text);
35 | }
36 |
37 | public function setTextFormat(textFormat:TextFormat, embedFonts:Boolean=false):void
38 | {
39 | _text.defaultTextFormat = textFormat;
40 | _text.embedFonts = embedFonts;
41 | invalidate();
42 | }
43 |
44 | protected override function onMouseDownLocal(e:MouseEvent):void
45 | {
46 | super.onMouseDownLocal(e);
47 | updateGoal(e.localX);
48 | }
49 |
50 | protected override function onMouseDrag(e:MouseEvent):void
51 | {
52 | super.onMouseDrag(e);
53 | updateGoal(e.localX);
54 | }
55 |
56 | protected override function onMouseUpLocal(e:MouseEvent):void
57 | {
58 | super.onMouseUpLocal(e);
59 | dispatchEvent(new Event(CHANGED));
60 | }
61 |
62 | private function updateGoal(x:int):void
63 | {
64 | var w:int = (width-margin-leftMargin);
65 | var v:Number = (x-leftMargin)/w;
66 | v = Math.min(v, availableRatio);
67 | _locked = true;
68 | _goal = Math.max(0, Math.min(1, v)) * _duration;
69 | invalidate();
70 | }
71 |
72 | public function get leftMargin():int
73 | {
74 | return (margin+_text.width);
75 | }
76 |
77 | public function get duration():Number
78 | {
79 | return _duration;
80 | }
81 |
82 | public function set duration(v:Number):void
83 | {
84 | _duration = v;
85 | invalidate();
86 | }
87 |
88 | public function set isStatic(v:Boolean):void
89 | {
90 | _static = v;
91 | invalidate();
92 | }
93 |
94 | public function set bytesTotal(v:uint):void
95 | {
96 | _bytesTotal = v;
97 | invalidate();
98 | }
99 |
100 | public function set bytesLoaded(v:uint):void
101 | {
102 | _bytesLoaded = v;
103 | invalidate();
104 | }
105 |
106 | public function get availableRatio():Number
107 | {
108 | if (_static && 0 < _bytesTotal) {
109 | return _bytesLoaded / Number(_bytesTotal);
110 | } else {
111 | return 1.0;
112 | }
113 | }
114 |
115 | public function set time(v:Number):void
116 | {
117 | _time = v;
118 | invalidate();
119 | }
120 |
121 | public function get time():Number
122 | {
123 | return (_locked)? _goal : _time;
124 | }
125 |
126 | public function unlock():void
127 | {
128 | _locked = false;
129 | invalidate();
130 | }
131 |
132 | public override function repaint():void
133 | {
134 | super.repaint();
135 | var size:int = barSize;
136 | var color:uint = (highlit)? style.hiFgColor : style.fgColor;
137 | var t:Number = (_locked)? _goal : _time;
138 |
139 | _text.text = (Math.floor(t/3600)+":"+
140 | format2(Math.floor(t/60)%60, "0")+":"+
141 | format2(t%60, "0")+" ");
142 | _text.textColor = color;
143 |
144 | var w:int = (width-margin-leftMargin);
145 | var h:int = (height-margin*2);
146 | graphics.beginFill(color, (color>>>24)/255);
147 | graphics.drawRect(leftMargin, (height-size)/2, w*availableRatio, size);
148 | if (0 < _duration) {
149 | graphics.drawRect(leftMargin+w*t/_duration-size, margin, size*2, h);
150 | }
151 | graphics.endFill();
152 | }
153 |
154 | private function format2(v:int, c:String=" "):String
155 | {
156 | return ((v < 10)? c+v : String(v));
157 | }
158 | }
159 |
160 | } // package
161 |
--------------------------------------------------------------------------------
/src/StatusDisplay.as:
--------------------------------------------------------------------------------
1 | package {
2 |
3 | import baseui.Control;
4 | import flash.text.TextField;
5 | import flash.text.TextFormat;
6 |
7 | // StatusDisplay
8 | // Shows a text status. (part of ControlBar)
9 | //
10 | public class StatusDisplay extends Control
11 | {
12 | private var _text:TextField;
13 |
14 | public function StatusDisplay()
15 | {
16 | super();
17 | _text = new TextField();
18 | _text.selectable = false;
19 | addChild(_text);
20 | }
21 |
22 | public function setTextFormat(textFormat:TextFormat, embedFonts:Boolean=false):void
23 | {
24 | _text.defaultTextFormat = textFormat;
25 | _text.embedFonts = embedFonts;
26 | invalidate();
27 | }
28 |
29 | public function get text():String
30 | {
31 | return _text.text;
32 | }
33 | public function set text(value:String):void
34 | {
35 | _text.text = value;
36 | }
37 |
38 | public override function resize(w:int, h:int):void
39 | {
40 | super.resize(w, h);
41 | _text.width = w;
42 | _text.height = h;
43 | }
44 |
45 | public override function repaint():void
46 | {
47 | super.repaint();
48 | var color:uint = (highlit)? style.hiFgColor : style.fgColor;
49 | _text.textColor = color;
50 | }
51 | }
52 |
53 | } // package
54 |
--------------------------------------------------------------------------------
/src/VolumeSlider.as:
--------------------------------------------------------------------------------
1 | package {
2 |
3 | import flash.events.Event;
4 | import flash.events.MouseEvent;
5 | import baseui.Slider;
6 |
7 | // VolumeSlider
8 | // A volume slider. (part of ControlBar)
9 | //
10 | public class VolumeSlider extends Slider
11 | {
12 | public var muteColor:uint = 0xffff0000;
13 |
14 | private var _value:Number = 0;
15 | private var _muted:Boolean = false;
16 |
17 | protected override function onMouseDrag(e:MouseEvent):void
18 | {
19 | super.onMouseDrag(e);
20 | var size:int = buttonSize/8;
21 | var w:int = (width-size*2);
22 | value = (e.localX-size)/w;
23 | dispatchEvent(new Event(CHANGED));
24 | }
25 |
26 | public function get value():Number
27 | {
28 | return _value;
29 | }
30 |
31 | public function set value(v:Number):void
32 | {
33 | v = Math.max(0, Math.min(1, v));
34 | if (_value != v) {
35 | _value = v;
36 | invalidate();
37 | }
38 | }
39 |
40 | public function get muted():Boolean
41 | {
42 | return _muted;
43 | }
44 |
45 | public function set muted(value:Boolean):void
46 | {
47 | _muted = value;
48 | invalidate();
49 | }
50 |
51 | public override function repaint():void
52 | {
53 | super.repaint();
54 | var size:int = buttonSize/4;
55 | var color:uint = (highlit)? style.hiFgColor : style.fgColor;
56 | var cx:int = width/2;
57 | var cy:int = height/2;
58 |
59 | graphics.lineStyle(0, color, (color>>>24)/255);
60 | graphics.moveTo(size, height-size);
61 | graphics.lineTo(width-size, size);
62 | graphics.lineTo(width-size, height-size);
63 | graphics.lineTo(size, height-size);
64 |
65 | var w:int = (width-size*2);
66 | var h:int = (height-size*2);
67 | graphics.beginFill(color, (color>>>24)/255);
68 | graphics.moveTo(size, height-size);
69 | graphics.lineTo(size+_value*w, height-size-_value*h);
70 | graphics.lineTo(size+_value*w, height-size);
71 | graphics.endFill();
72 |
73 | if (_muted) {
74 | graphics.lineStyle(2, muteColor, (muteColor>>>24)/255);
75 | graphics.moveTo(cx-size, cy-size);
76 | graphics.lineTo(cx+size, cy+size);
77 | }
78 | }
79 | }
80 |
81 | } // package
82 |
--------------------------------------------------------------------------------
/src/baseui/Button.as:
--------------------------------------------------------------------------------
1 | package baseui {
2 |
3 | // Button
4 | // Generic button class.
5 | //
6 | public class Button extends Control
7 | {
8 | public function get buttonSize():int
9 | {
10 | return Math.min(controlWidth, controlHeight);
11 | }
12 |
13 | public override function repaint():void
14 | {
15 | super.repaint();
16 |
17 | if (highlit) {
18 | graphics.lineStyle(0, style.borderColor, (style.borderColor>>>24)/255);
19 | graphics.drawRect(0, 0, controlWidth, controlHeight);
20 | }
21 | }
22 | }
23 |
24 | } // package
25 |
--------------------------------------------------------------------------------
/src/baseui/Control.as:
--------------------------------------------------------------------------------
1 | package baseui {
2 |
3 | import flash.events.Event;
4 | import flash.events.MouseEvent;
5 | import flash.display.Sprite;
6 |
7 | // Control
8 | // Base class for buttons/sliders.
9 | //
10 | public class Control extends Sprite
11 | {
12 | private var _style:Style = new Style();
13 | private var _width:int;
14 | private var _height:int;
15 |
16 | private var _mousedown:Boolean;
17 | private var _mouseover:Boolean;
18 | private var _invalidated:Boolean;
19 |
20 | public function Control()
21 | {
22 | super();
23 | addEventListener(Event.ADDED_TO_STAGE, onAdded);
24 | addEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
25 | addEventListener(MouseEvent.MOUSE_OUT, onMouseOut);
26 | }
27 |
28 | public function get pressed():Boolean
29 | {
30 | return _mouseover && _mousedown;
31 | }
32 |
33 | public function get highlit():Boolean
34 | {
35 | return _mouseover || _mousedown;
36 | }
37 |
38 | private function onAdded(e:Event):void
39 | {
40 | stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
41 | stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
42 | }
43 |
44 | public function get controlWidth():int
45 | {
46 | return _width;
47 | }
48 |
49 | public function get controlHeight():int
50 | {
51 | return _height;
52 | }
53 |
54 | public virtual function get style():Style
55 | {
56 | return _style;
57 | }
58 |
59 | public virtual function set style(value:Style):void
60 | {
61 | _style = value;
62 | }
63 |
64 | protected virtual function onMouseDown(e:MouseEvent):void
65 | {
66 | if (_mouseover) {
67 | _mousedown = true;
68 | _invalidated = true;
69 | onMouseDownLocal(e);
70 | }
71 | }
72 |
73 | protected virtual function onMouseUp(e:MouseEvent):void
74 | {
75 | if (_mousedown) {
76 | onMouseUpLocal(e);
77 | _mousedown = false;
78 | _invalidated = true;
79 | }
80 | }
81 |
82 | protected virtual function onMouseOver(e:MouseEvent):void
83 | {
84 | _mouseover = true;
85 | _invalidated = true;
86 | }
87 |
88 | protected virtual function onMouseOut(e:MouseEvent):void
89 | {
90 | _mouseover = false;
91 | _invalidated = true;
92 | }
93 |
94 | protected virtual function onMouseDownLocal(e:MouseEvent):void
95 | {
96 | }
97 |
98 | protected virtual function onMouseUpLocal(e:MouseEvent):void
99 | {
100 | }
101 |
102 | protected function invalidate():void
103 | {
104 | _invalidated = true;
105 | }
106 |
107 | public virtual function resize(w:int, h:int):void
108 | {
109 | _width = w;
110 | _height = h;
111 | repaint();
112 | }
113 |
114 | public virtual function repaint():void
115 | {
116 | var color:uint = (highlit)? style.hiBgColor : style.bgColor;
117 | graphics.clear();
118 | graphics.beginFill(color, (color>>>24)/255);
119 | graphics.drawRect(0, 0, controlWidth, controlHeight);
120 | graphics.endFill();
121 | }
122 |
123 | public virtual function update():void
124 | {
125 | if (_invalidated) {
126 | _invalidated = false;
127 | repaint();
128 | }
129 | }
130 |
131 | }
132 |
133 | } // package
134 |
--------------------------------------------------------------------------------
/src/baseui/MenuItem.as:
--------------------------------------------------------------------------------
1 | package baseui {
2 |
3 | import flash.events.MouseEvent;
4 |
5 | // MenuItem
6 | //
7 | public class MenuItem extends Control
8 | {
9 | public var value:Object;
10 |
11 | public override function toString():String
12 | {
13 | return ("