annotate johndyer-mediaelement-13fa20a/src/flash/htmlelements/VideoElement.as @ 25:4a4bd554b4c1 tip

Closing this sub branch.
author Daniele Barchiesi <daniele.barchiesi@eecs.qmul.ac.uk>
date Mon, 25 Mar 2013 14:02:54 +0000
parents 032bc65ebafc
children
rev   line source
gyorgy@0 1 package htmlelements
gyorgy@0 2 {
gyorgy@0 3 import flash.display.Sprite;
gyorgy@0 4 import flash.events.*;
gyorgy@0 5 import flash.net.NetConnection;
gyorgy@0 6 import flash.net.NetStream;
gyorgy@0 7 import flash.media.Video;
gyorgy@0 8 import flash.media.SoundTransform;
gyorgy@0 9 import flash.utils.Timer;
gyorgy@0 10
gyorgy@0 11 import FlashMediaElement;
gyorgy@0 12 import HtmlMediaEvent;
gyorgy@0 13
gyorgy@0 14 public class VideoElement extends Sprite implements IMediaElement
gyorgy@0 15 {
gyorgy@0 16 private var _currentUrl:String = "";
gyorgy@0 17 private var _autoplay:Boolean = true;
gyorgy@0 18 private var _preload:String = "";
gyorgy@0 19
gyorgy@0 20 private var _connection:NetConnection;
gyorgy@0 21 private var _stream:NetStream;
gyorgy@0 22 private var _video:Video;
gyorgy@0 23 private var _element:FlashMediaElement;
gyorgy@0 24 private var _soundTransform;
gyorgy@0 25 private var _oldVolume:Number = 1;
gyorgy@0 26
gyorgy@0 27 // event values
gyorgy@0 28 private var _duration:Number = 0;
gyorgy@0 29 private var _framerate:Number;
gyorgy@0 30 private var _isPaused:Boolean = true;
gyorgy@0 31 private var _isEnded:Boolean = false;
gyorgy@0 32 private var _volume:Number = 1;
gyorgy@0 33 private var _isMuted:Boolean = false;
gyorgy@0 34
gyorgy@0 35 private var _bytesLoaded:Number = 0;
gyorgy@0 36 private var _bytesTotal:Number = 0;
gyorgy@0 37 private var _bufferedTime:Number = 0;
gyorgy@0 38 private var _bufferEmpty:Boolean = false;
gyorgy@0 39
gyorgy@0 40 private var _videoWidth:Number = -1;
gyorgy@0 41 private var _videoHeight:Number = -1;
gyorgy@0 42
gyorgy@0 43 private var _timer:Timer;
gyorgy@0 44
gyorgy@0 45 private var _isRTMP:Boolean = false;
gyorgy@0 46 private var _isConnected:Boolean = false;
gyorgy@0 47 private var _playWhenConnected:Boolean = false;
gyorgy@0 48 private var _hasStartedPlaying:Boolean = false;
gyorgy@0 49
gyorgy@0 50 public function get video():Video {
gyorgy@0 51 return _video;
gyorgy@0 52 }
gyorgy@0 53
gyorgy@0 54 public function get videoHeight():Number {
gyorgy@0 55 return _videoHeight;
gyorgy@0 56 }
gyorgy@0 57
gyorgy@0 58 public function get videoWidth():Number {
gyorgy@0 59 return _videoWidth;
gyorgy@0 60 }
gyorgy@0 61
gyorgy@0 62
gyorgy@0 63 public function duration():Number {
gyorgy@0 64 return _duration;
gyorgy@0 65 }
gyorgy@0 66
gyorgy@0 67 public function currentTime():Number {
gyorgy@0 68 if (_stream != null) {
gyorgy@0 69 return _stream.time;
gyorgy@0 70 } else {
gyorgy@0 71 return 0;
gyorgy@0 72 }
gyorgy@0 73 }
gyorgy@0 74
gyorgy@0 75 // (1) load()
gyorgy@0 76 // calls _connection.connect();
gyorgy@0 77 // waits for NetConnection.Connect.Success
gyorgy@0 78 // _stream gets created
gyorgy@0 79
gyorgy@0 80
gyorgy@0 81 public function VideoElement(element:FlashMediaElement, autoplay:Boolean, preload:String, timerRate:Number, startVolume:Number)
gyorgy@0 82 {
gyorgy@0 83 _element = element;
gyorgy@0 84 _autoplay = autoplay;
gyorgy@0 85 _volume = startVolume;
gyorgy@0 86 _preload = preload;
gyorgy@0 87
gyorgy@0 88 _video = new Video();
gyorgy@0 89 addChild(_video);
gyorgy@0 90
gyorgy@0 91 _connection = new NetConnection();
gyorgy@0 92 _connection.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
gyorgy@0 93 _connection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
gyorgy@0 94 //_connection.connect(null);
gyorgy@0 95
gyorgy@0 96 _timer = new Timer(timerRate);
gyorgy@0 97 _timer.addEventListener("timer", timerHandler);
gyorgy@0 98
gyorgy@0 99 }
gyorgy@0 100
gyorgy@0 101 private function timerHandler(e:TimerEvent) {
gyorgy@0 102
gyorgy@0 103 _bytesLoaded = _stream.bytesLoaded;
gyorgy@0 104 _bytesTotal = _stream.bytesTotal;
gyorgy@0 105
gyorgy@0 106 sendEvent(HtmlMediaEvent.TIMEUPDATE);
gyorgy@0 107
gyorgy@0 108 trace("bytes", _bytesLoaded, _bytesTotal);
gyorgy@0 109
gyorgy@0 110 if (_bytesLoaded < _bytesTotal)
gyorgy@0 111 sendEvent(HtmlMediaEvent.PROGRESS);
gyorgy@0 112 }
gyorgy@0 113
gyorgy@0 114 // internal events
gyorgy@0 115 private function netStatusHandler(event:NetStatusEvent):void {
gyorgy@0 116 trace("netStatus", event.info.code);
gyorgy@0 117
gyorgy@0 118 switch (event.info.code) {
gyorgy@0 119
gyorgy@0 120 case "NetStream.Buffer.Empty":
gyorgy@0 121 _bufferEmpty = true;
gyorgy@0 122 _isEnded ? sendEvent(HtmlMediaEvent.ENDED) : null;
gyorgy@0 123 break;
gyorgy@0 124
gyorgy@0 125 case "NetStream.Buffer.Full":
gyorgy@0 126 _bytesLoaded = _stream.bytesLoaded;
gyorgy@0 127 _bytesTotal = _stream.bytesTotal;
gyorgy@0 128 _bufferEmpty = false;
gyorgy@0 129
gyorgy@0 130 sendEvent(HtmlMediaEvent.PROGRESS);
gyorgy@0 131 break;
gyorgy@0 132
gyorgy@0 133 case "NetConnection.Connect.Success":
gyorgy@0 134 connectStream();
gyorgy@0 135 break;
gyorgy@0 136 case "NetStream.Play.StreamNotFound":
gyorgy@0 137 trace("Unable to locate video");
gyorgy@0 138 break;
gyorgy@0 139
gyorgy@0 140 // STREAM
gyorgy@0 141 case "NetStream.Play.Start":
gyorgy@0 142 _isPaused = false;
gyorgy@0 143 sendEvent(HtmlMediaEvent.LOADEDDATA);
gyorgy@0 144 sendEvent(HtmlMediaEvent.CANPLAY);
gyorgy@0 145 sendEvent(HtmlMediaEvent.PLAY);
gyorgy@0 146 sendEvent(HtmlMediaEvent.PLAYING);
gyorgy@0 147 _timer.start();
gyorgy@0 148 break;
gyorgy@0 149
gyorgy@0 150 case "NetStream.Seek.Notify":
gyorgy@0 151 sendEvent(HtmlMediaEvent.SEEKED);
gyorgy@0 152 break;
gyorgy@0 153
gyorgy@0 154 case "NetStream.Pause.Notify":
gyorgy@0 155 _isPaused = true;
gyorgy@0 156 sendEvent(HtmlMediaEvent.PAUSE);
gyorgy@0 157 break;
gyorgy@0 158
gyorgy@0 159 case "NetStream.Play.Stop":
gyorgy@0 160 _isEnded = true;
gyorgy@0 161 _isPaused = false;
gyorgy@0 162 _timer.stop();
gyorgy@0 163 _bufferEmpty ? sendEvent(HtmlMediaEvent.ENDED) : null;
gyorgy@0 164 break;
gyorgy@0 165
gyorgy@0 166 }
gyorgy@0 167 }
gyorgy@0 168
gyorgy@0 169
gyorgy@0 170 private function securityErrorHandler(event:SecurityErrorEvent):void {
gyorgy@0 171 trace("securityErrorHandler: " + event);
gyorgy@0 172 }
gyorgy@0 173
gyorgy@0 174 private function asyncErrorHandler(event:AsyncErrorEvent):void {
gyorgy@0 175 // ignore AsyncErrorEvent events.
gyorgy@0 176 }
gyorgy@0 177
gyorgy@0 178
gyorgy@0 179 private function onMetaDataHandler(info:Object):void {
gyorgy@0 180 _duration = info.duration;
gyorgy@0 181 _framerate = info.framerate;
gyorgy@0 182 _videoWidth = info.width;
gyorgy@0 183 _videoHeight = info.height;
gyorgy@0 184
gyorgy@0 185 // set size?
gyorgy@0 186
gyorgy@0 187 sendEvent(HtmlMediaEvent.LOADEDMETADATA);
gyorgy@0 188 }
gyorgy@0 189
gyorgy@0 190
gyorgy@0 191
gyorgy@0 192
gyorgy@0 193 // interface members
gyorgy@0 194 public function setSrc(url:String):void {
gyorgy@0 195 if (_isConnected && _stream) {
gyorgy@0 196 // stop and restart
gyorgy@0 197 _stream.pause();
gyorgy@0 198 }
gyorgy@0 199
gyorgy@0 200 _currentUrl = url;
gyorgy@0 201 _isRTMP = !!_currentUrl.match(/^rtmp(s|t|e|te)?\:\/\//);
gyorgy@0 202 _isConnected = false;
gyorgy@0 203 _hasStartedPlaying = false;
gyorgy@0 204 }
gyorgy@0 205
gyorgy@0 206 public function load():void {
gyorgy@0 207 // disconnect existing stream and connection
gyorgy@0 208 if (_isConnected && _stream) {
gyorgy@0 209 _stream.pause();
gyorgy@0 210 _stream.close();
gyorgy@0 211 _connection.close();
gyorgy@0 212 }
gyorgy@0 213 _isConnected = false;
gyorgy@0 214
gyorgy@0 215 // start new connection
gyorgy@0 216 if (_isRTMP) {
gyorgy@0 217 _connection.connect(_currentUrl.replace(/\/[^\/]+$/,"/"));
gyorgy@0 218 } else {
gyorgy@0 219 _connection.connect(null);
gyorgy@0 220 }
gyorgy@0 221
gyorgy@0 222 // in a few moments the "NetConnection.Connect.Success" event will fire
gyorgy@0 223 // and call createConnection which finishes the "load" sequence
gyorgy@0 224 sendEvent(HtmlMediaEvent.LOADSTART);
gyorgy@0 225 }
gyorgy@0 226
gyorgy@0 227
gyorgy@0 228 private function connectStream():void {
gyorgy@0 229 trace("connectStream");
gyorgy@0 230 _stream = new NetStream(_connection);
gyorgy@0 231
gyorgy@0 232 // explicitly set the sound since it could have come before the connection was made
gyorgy@0 233 _soundTransform = new SoundTransform(_volume);
gyorgy@0 234 _stream.soundTransform = _soundTransform;
gyorgy@0 235
gyorgy@0 236 _stream.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler); // same event as connection
gyorgy@0 237 _stream.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);
gyorgy@0 238
gyorgy@0 239 var customClient:Object = new Object();
gyorgy@0 240 customClient.onMetaData = onMetaDataHandler;
gyorgy@0 241 _stream.client = customClient;
gyorgy@0 242
gyorgy@0 243 _video.attachNetStream(_stream);
gyorgy@0 244
gyorgy@0 245 // start downloading without playing )based on preload and play() hasn't been called)
gyorgy@0 246 // I wish flash had a load() command to make this less awkward
gyorgy@0 247 if (_preload != "none" && !_playWhenConnected) {
gyorgy@0 248 _stream.play(_currentUrl, 0, 0);
gyorgy@0 249
gyorgy@0 250 _stream.pause();
gyorgy@0 251 _isPaused = true;
gyorgy@0 252 sendEvent(HtmlMediaEvent.PAUSE); // have to send this because the "playing" event gets sent via event handlers
gyorgy@0 253 }
gyorgy@0 254
gyorgy@0 255 _isConnected = true;
gyorgy@0 256
gyorgy@0 257 if (_playWhenConnected && !_hasStartedPlaying) {
gyorgy@0 258 play();
gyorgy@0 259 _playWhenConnected = false;
gyorgy@0 260 }
gyorgy@0 261
gyorgy@0 262 }
gyorgy@0 263
gyorgy@0 264 public function play():void {
gyorgy@0 265
gyorgy@0 266 if (!_hasStartedPlaying && !_isConnected) {
gyorgy@0 267 _playWhenConnected = true;
gyorgy@0 268 load();
gyorgy@0 269 return;
gyorgy@0 270 }
gyorgy@0 271
gyorgy@0 272 if (_hasStartedPlaying) {
gyorgy@0 273 if (_isPaused) {
gyorgy@0 274 _stream.resume();
gyorgy@0 275 _timer.start();
gyorgy@0 276 _isPaused = false;
gyorgy@0 277 sendEvent(HtmlMediaEvent.PLAY);
gyorgy@0 278 sendEvent(HtmlMediaEvent.PLAYING);
gyorgy@0 279 }
gyorgy@0 280 } else {
gyorgy@0 281
gyorgy@0 282 if (_isRTMP) {
gyorgy@0 283 _stream.play(_currentUrl.split("/").pop());
gyorgy@0 284 } else {
gyorgy@0 285 _stream.play(_currentUrl);
gyorgy@0 286 }
gyorgy@0 287 _timer.start();
gyorgy@0 288 _isPaused = false;
gyorgy@0 289 _hasStartedPlaying = true;
gyorgy@0 290
gyorgy@0 291 // don't toss play/playing events here, because we haven't sent a
gyorgy@0 292 // canplay / loadeddata event yet. that'll be handled in the net
gyorgy@0 293 // event listener
gyorgy@0 294 }
gyorgy@0 295
gyorgy@0 296 }
gyorgy@0 297
gyorgy@0 298 public function pause():void {
gyorgy@0 299 if (_stream == null)
gyorgy@0 300 return;
gyorgy@0 301
gyorgy@0 302 _stream.pause();
gyorgy@0 303 _isPaused = true;
gyorgy@0 304 _timer.stop();
gyorgy@0 305
gyorgy@0 306 _isPaused = true;
gyorgy@0 307 sendEvent(HtmlMediaEvent.PAUSE);
gyorgy@0 308 }
gyorgy@0 309
gyorgy@0 310 public function stop():void {
gyorgy@0 311 if (_stream == null)
gyorgy@0 312 return;
gyorgy@0 313
gyorgy@0 314 _stream.close();
gyorgy@0 315 _isPaused = false;
gyorgy@0 316 _timer.stop();
gyorgy@0 317 sendEvent(HtmlMediaEvent.STOP);
gyorgy@0 318 }
gyorgy@0 319
gyorgy@0 320 public function setCurrentTime(pos:Number):void {
gyorgy@0 321 if (_stream == null)
gyorgy@0 322 return;
gyorgy@0 323
gyorgy@0 324 sendEvent(HtmlMediaEvent.SEEKING);
gyorgy@0 325 _stream.seek(pos);
gyorgy@0 326 sendEvent(HtmlMediaEvent.TIMEUPDATE);
gyorgy@0 327 }
gyorgy@0 328
gyorgy@0 329 public function setVolume(volume:Number):void {
gyorgy@0 330 if (_stream != null) {
gyorgy@0 331 _soundTransform = new SoundTransform(volume);
gyorgy@0 332 _stream.soundTransform = _soundTransform;
gyorgy@0 333 }
gyorgy@0 334
gyorgy@0 335 _volume = volume;
gyorgy@0 336
gyorgy@0 337 _isMuted = (_volume == 0);
gyorgy@0 338
gyorgy@0 339 sendEvent(HtmlMediaEvent.VOLUMECHANGE);
gyorgy@0 340 }
gyorgy@0 341
gyorgy@0 342
gyorgy@0 343 public function setMuted(muted:Boolean):void {
gyorgy@0 344
gyorgy@0 345 if (_isMuted == muted)
gyorgy@0 346 return;
gyorgy@0 347
gyorgy@0 348 if (muted) {
gyorgy@0 349 _oldVolume = _stream.soundTransform.volume;
gyorgy@0 350 setVolume(0);
gyorgy@0 351 } else {
gyorgy@0 352 setVolume(_oldVolume);
gyorgy@0 353 }
gyorgy@0 354
gyorgy@0 355 _isMuted = muted;
gyorgy@0 356 }
gyorgy@0 357
gyorgy@0 358
gyorgy@0 359 private function sendEvent(eventName:String) {
gyorgy@0 360
gyorgy@0 361 // calculate this to mimic HTML5
gyorgy@0 362 _bufferedTime = _bytesLoaded / _bytesTotal * _duration;
gyorgy@0 363
gyorgy@0 364 // build JSON
gyorgy@0 365 var values:String =
gyorgy@0 366 "duration:" + _duration +
gyorgy@0 367 ",framerate:" + _framerate +
gyorgy@0 368 ",currentTime:" + (_stream != null ? _stream.time : 0) +
gyorgy@0 369 ",muted:" + _isMuted +
gyorgy@0 370 ",paused:" + _isPaused +
gyorgy@0 371 ",ended:" + _isEnded +
gyorgy@0 372 ",volume:" + _volume +
gyorgy@0 373 ",src:\"" + _currentUrl + "\"" +
gyorgy@0 374 ",bytesTotal:" + _bytesTotal +
gyorgy@0 375 ",bufferedBytes:" + _bytesLoaded +
gyorgy@0 376 ",bufferedTime:" + _bufferedTime +
gyorgy@0 377 ",videoWidth:" + _videoWidth +
gyorgy@0 378 ",videoHeight:" + _videoHeight +
gyorgy@0 379 "";
gyorgy@0 380
gyorgy@0 381 _element.sendEvent(eventName, values);
gyorgy@0 382 }
gyorgy@0 383 }
gyorgy@0 384 }