gyorgy@0: package htmlelements gyorgy@0: { gyorgy@0: import flash.display.Sprite; gyorgy@0: import flash.events.*; gyorgy@0: import flash.net.NetConnection; gyorgy@0: import flash.net.NetStream; gyorgy@0: import flash.media.Video; gyorgy@0: import flash.media.SoundTransform; gyorgy@0: import flash.utils.Timer; gyorgy@0: gyorgy@0: import FlashMediaElement; gyorgy@0: import HtmlMediaEvent; gyorgy@0: gyorgy@0: public class VideoElement extends Sprite implements IMediaElement gyorgy@0: { gyorgy@0: private var _currentUrl:String = ""; gyorgy@0: private var _autoplay:Boolean = true; gyorgy@0: private var _preload:String = ""; gyorgy@0: gyorgy@0: private var _connection:NetConnection; gyorgy@0: private var _stream:NetStream; gyorgy@0: private var _video:Video; gyorgy@0: private var _element:FlashMediaElement; gyorgy@0: private var _soundTransform; gyorgy@0: private var _oldVolume:Number = 1; gyorgy@0: gyorgy@0: // event values gyorgy@0: private var _duration:Number = 0; gyorgy@0: private var _framerate:Number; gyorgy@0: private var _isPaused:Boolean = true; gyorgy@0: private var _isEnded:Boolean = false; gyorgy@0: private var _volume:Number = 1; gyorgy@0: private var _isMuted:Boolean = false; gyorgy@0: gyorgy@0: private var _bytesLoaded:Number = 0; gyorgy@0: private var _bytesTotal:Number = 0; gyorgy@0: private var _bufferedTime:Number = 0; gyorgy@0: private var _bufferEmpty:Boolean = false; gyorgy@0: gyorgy@0: private var _videoWidth:Number = -1; gyorgy@0: private var _videoHeight:Number = -1; gyorgy@0: gyorgy@0: private var _timer:Timer; gyorgy@0: gyorgy@0: private var _isRTMP:Boolean = false; gyorgy@0: private var _isConnected:Boolean = false; gyorgy@0: private var _playWhenConnected:Boolean = false; gyorgy@0: private var _hasStartedPlaying:Boolean = false; gyorgy@0: gyorgy@0: public function get video():Video { gyorgy@0: return _video; gyorgy@0: } gyorgy@0: gyorgy@0: public function get videoHeight():Number { gyorgy@0: return _videoHeight; gyorgy@0: } gyorgy@0: gyorgy@0: public function get videoWidth():Number { gyorgy@0: return _videoWidth; gyorgy@0: } gyorgy@0: gyorgy@0: gyorgy@0: public function duration():Number { gyorgy@0: return _duration; gyorgy@0: } gyorgy@0: gyorgy@0: public function currentTime():Number { gyorgy@0: if (_stream != null) { gyorgy@0: return _stream.time; gyorgy@0: } else { gyorgy@0: return 0; gyorgy@0: } gyorgy@0: } gyorgy@0: gyorgy@0: // (1) load() gyorgy@0: // calls _connection.connect(); gyorgy@0: // waits for NetConnection.Connect.Success gyorgy@0: // _stream gets created gyorgy@0: gyorgy@0: gyorgy@0: public function VideoElement(element:FlashMediaElement, autoplay:Boolean, preload:String, timerRate:Number, startVolume:Number) gyorgy@0: { gyorgy@0: _element = element; gyorgy@0: _autoplay = autoplay; gyorgy@0: _volume = startVolume; gyorgy@0: _preload = preload; gyorgy@0: gyorgy@0: _video = new Video(); gyorgy@0: addChild(_video); gyorgy@0: gyorgy@0: _connection = new NetConnection(); gyorgy@0: _connection.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler); gyorgy@0: _connection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler); gyorgy@0: //_connection.connect(null); gyorgy@0: gyorgy@0: _timer = new Timer(timerRate); gyorgy@0: _timer.addEventListener("timer", timerHandler); gyorgy@0: gyorgy@0: } gyorgy@0: gyorgy@0: private function timerHandler(e:TimerEvent) { gyorgy@0: gyorgy@0: _bytesLoaded = _stream.bytesLoaded; gyorgy@0: _bytesTotal = _stream.bytesTotal; gyorgy@0: gyorgy@0: sendEvent(HtmlMediaEvent.TIMEUPDATE); gyorgy@0: gyorgy@0: trace("bytes", _bytesLoaded, _bytesTotal); gyorgy@0: gyorgy@0: if (_bytesLoaded < _bytesTotal) gyorgy@0: sendEvent(HtmlMediaEvent.PROGRESS); gyorgy@0: } gyorgy@0: gyorgy@0: // internal events gyorgy@0: private function netStatusHandler(event:NetStatusEvent):void { gyorgy@0: trace("netStatus", event.info.code); gyorgy@0: gyorgy@0: switch (event.info.code) { gyorgy@0: gyorgy@0: case "NetStream.Buffer.Empty": gyorgy@0: _bufferEmpty = true; gyorgy@0: _isEnded ? sendEvent(HtmlMediaEvent.ENDED) : null; gyorgy@0: break; gyorgy@0: gyorgy@0: case "NetStream.Buffer.Full": gyorgy@0: _bytesLoaded = _stream.bytesLoaded; gyorgy@0: _bytesTotal = _stream.bytesTotal; gyorgy@0: _bufferEmpty = false; gyorgy@0: gyorgy@0: sendEvent(HtmlMediaEvent.PROGRESS); gyorgy@0: break; gyorgy@0: gyorgy@0: case "NetConnection.Connect.Success": gyorgy@0: connectStream(); gyorgy@0: break; gyorgy@0: case "NetStream.Play.StreamNotFound": gyorgy@0: trace("Unable to locate video"); gyorgy@0: break; gyorgy@0: gyorgy@0: // STREAM gyorgy@0: case "NetStream.Play.Start": gyorgy@0: _isPaused = false; gyorgy@0: sendEvent(HtmlMediaEvent.LOADEDDATA); gyorgy@0: sendEvent(HtmlMediaEvent.CANPLAY); gyorgy@0: sendEvent(HtmlMediaEvent.PLAY); gyorgy@0: sendEvent(HtmlMediaEvent.PLAYING); gyorgy@0: _timer.start(); gyorgy@0: break; gyorgy@0: gyorgy@0: case "NetStream.Seek.Notify": gyorgy@0: sendEvent(HtmlMediaEvent.SEEKED); gyorgy@0: break; gyorgy@0: gyorgy@0: case "NetStream.Pause.Notify": gyorgy@0: _isPaused = true; gyorgy@0: sendEvent(HtmlMediaEvent.PAUSE); gyorgy@0: break; gyorgy@0: gyorgy@0: case "NetStream.Play.Stop": gyorgy@0: _isEnded = true; gyorgy@0: _isPaused = false; gyorgy@0: _timer.stop(); gyorgy@0: _bufferEmpty ? sendEvent(HtmlMediaEvent.ENDED) : null; gyorgy@0: break; gyorgy@0: gyorgy@0: } gyorgy@0: } gyorgy@0: gyorgy@0: gyorgy@0: private function securityErrorHandler(event:SecurityErrorEvent):void { gyorgy@0: trace("securityErrorHandler: " + event); gyorgy@0: } gyorgy@0: gyorgy@0: private function asyncErrorHandler(event:AsyncErrorEvent):void { gyorgy@0: // ignore AsyncErrorEvent events. gyorgy@0: } gyorgy@0: gyorgy@0: gyorgy@0: private function onMetaDataHandler(info:Object):void { gyorgy@0: _duration = info.duration; gyorgy@0: _framerate = info.framerate; gyorgy@0: _videoWidth = info.width; gyorgy@0: _videoHeight = info.height; gyorgy@0: gyorgy@0: // set size? gyorgy@0: gyorgy@0: sendEvent(HtmlMediaEvent.LOADEDMETADATA); gyorgy@0: } gyorgy@0: gyorgy@0: gyorgy@0: gyorgy@0: gyorgy@0: // interface members gyorgy@0: public function setSrc(url:String):void { gyorgy@0: if (_isConnected && _stream) { gyorgy@0: // stop and restart gyorgy@0: _stream.pause(); gyorgy@0: } gyorgy@0: gyorgy@0: _currentUrl = url; gyorgy@0: _isRTMP = !!_currentUrl.match(/^rtmp(s|t|e|te)?\:\/\//); gyorgy@0: _isConnected = false; gyorgy@0: _hasStartedPlaying = false; gyorgy@0: } gyorgy@0: gyorgy@0: public function load():void { gyorgy@0: // disconnect existing stream and connection gyorgy@0: if (_isConnected && _stream) { gyorgy@0: _stream.pause(); gyorgy@0: _stream.close(); gyorgy@0: _connection.close(); gyorgy@0: } gyorgy@0: _isConnected = false; gyorgy@0: gyorgy@0: // start new connection gyorgy@0: if (_isRTMP) { gyorgy@0: _connection.connect(_currentUrl.replace(/\/[^\/]+$/,"/")); gyorgy@0: } else { gyorgy@0: _connection.connect(null); gyorgy@0: } gyorgy@0: gyorgy@0: // in a few moments the "NetConnection.Connect.Success" event will fire gyorgy@0: // and call createConnection which finishes the "load" sequence gyorgy@0: sendEvent(HtmlMediaEvent.LOADSTART); gyorgy@0: } gyorgy@0: gyorgy@0: gyorgy@0: private function connectStream():void { gyorgy@0: trace("connectStream"); gyorgy@0: _stream = new NetStream(_connection); gyorgy@0: gyorgy@0: // explicitly set the sound since it could have come before the connection was made gyorgy@0: _soundTransform = new SoundTransform(_volume); gyorgy@0: _stream.soundTransform = _soundTransform; gyorgy@0: gyorgy@0: _stream.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler); // same event as connection gyorgy@0: _stream.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler); gyorgy@0: gyorgy@0: var customClient:Object = new Object(); gyorgy@0: customClient.onMetaData = onMetaDataHandler; gyorgy@0: _stream.client = customClient; gyorgy@0: gyorgy@0: _video.attachNetStream(_stream); gyorgy@0: gyorgy@0: // start downloading without playing )based on preload and play() hasn't been called) gyorgy@0: // I wish flash had a load() command to make this less awkward gyorgy@0: if (_preload != "none" && !_playWhenConnected) { gyorgy@0: _stream.play(_currentUrl, 0, 0); gyorgy@0: gyorgy@0: _stream.pause(); gyorgy@0: _isPaused = true; gyorgy@0: sendEvent(HtmlMediaEvent.PAUSE); // have to send this because the "playing" event gets sent via event handlers gyorgy@0: } gyorgy@0: gyorgy@0: _isConnected = true; gyorgy@0: gyorgy@0: if (_playWhenConnected && !_hasStartedPlaying) { gyorgy@0: play(); gyorgy@0: _playWhenConnected = false; gyorgy@0: } gyorgy@0: gyorgy@0: } gyorgy@0: gyorgy@0: public function play():void { gyorgy@0: gyorgy@0: if (!_hasStartedPlaying && !_isConnected) { gyorgy@0: _playWhenConnected = true; gyorgy@0: load(); gyorgy@0: return; gyorgy@0: } gyorgy@0: gyorgy@0: if (_hasStartedPlaying) { gyorgy@0: if (_isPaused) { gyorgy@0: _stream.resume(); gyorgy@0: _timer.start(); gyorgy@0: _isPaused = false; gyorgy@0: sendEvent(HtmlMediaEvent.PLAY); gyorgy@0: sendEvent(HtmlMediaEvent.PLAYING); gyorgy@0: } gyorgy@0: } else { gyorgy@0: gyorgy@0: if (_isRTMP) { gyorgy@0: _stream.play(_currentUrl.split("/").pop()); gyorgy@0: } else { gyorgy@0: _stream.play(_currentUrl); gyorgy@0: } gyorgy@0: _timer.start(); gyorgy@0: _isPaused = false; gyorgy@0: _hasStartedPlaying = true; gyorgy@0: gyorgy@0: // don't toss play/playing events here, because we haven't sent a gyorgy@0: // canplay / loadeddata event yet. that'll be handled in the net gyorgy@0: // event listener gyorgy@0: } gyorgy@0: gyorgy@0: } gyorgy@0: gyorgy@0: public function pause():void { gyorgy@0: if (_stream == null) gyorgy@0: return; gyorgy@0: gyorgy@0: _stream.pause(); gyorgy@0: _isPaused = true; gyorgy@0: _timer.stop(); gyorgy@0: gyorgy@0: _isPaused = true; gyorgy@0: sendEvent(HtmlMediaEvent.PAUSE); gyorgy@0: } gyorgy@0: gyorgy@0: public function stop():void { gyorgy@0: if (_stream == null) gyorgy@0: return; gyorgy@0: gyorgy@0: _stream.close(); gyorgy@0: _isPaused = false; gyorgy@0: _timer.stop(); gyorgy@0: sendEvent(HtmlMediaEvent.STOP); gyorgy@0: } gyorgy@0: gyorgy@0: public function setCurrentTime(pos:Number):void { gyorgy@0: if (_stream == null) gyorgy@0: return; gyorgy@0: gyorgy@0: sendEvent(HtmlMediaEvent.SEEKING); gyorgy@0: _stream.seek(pos); gyorgy@0: sendEvent(HtmlMediaEvent.TIMEUPDATE); gyorgy@0: } gyorgy@0: gyorgy@0: public function setVolume(volume:Number):void { gyorgy@0: if (_stream != null) { gyorgy@0: _soundTransform = new SoundTransform(volume); gyorgy@0: _stream.soundTransform = _soundTransform; gyorgy@0: } gyorgy@0: gyorgy@0: _volume = volume; gyorgy@0: gyorgy@0: _isMuted = (_volume == 0); gyorgy@0: gyorgy@0: sendEvent(HtmlMediaEvent.VOLUMECHANGE); gyorgy@0: } gyorgy@0: gyorgy@0: gyorgy@0: public function setMuted(muted:Boolean):void { gyorgy@0: gyorgy@0: if (_isMuted == muted) gyorgy@0: return; gyorgy@0: gyorgy@0: if (muted) { gyorgy@0: _oldVolume = _stream.soundTransform.volume; gyorgy@0: setVolume(0); gyorgy@0: } else { gyorgy@0: setVolume(_oldVolume); gyorgy@0: } gyorgy@0: gyorgy@0: _isMuted = muted; gyorgy@0: } gyorgy@0: gyorgy@0: gyorgy@0: private function sendEvent(eventName:String) { gyorgy@0: gyorgy@0: // calculate this to mimic HTML5 gyorgy@0: _bufferedTime = _bytesLoaded / _bytesTotal * _duration; gyorgy@0: gyorgy@0: // build JSON gyorgy@0: var values:String = gyorgy@0: "duration:" + _duration + gyorgy@0: ",framerate:" + _framerate + gyorgy@0: ",currentTime:" + (_stream != null ? _stream.time : 0) + gyorgy@0: ",muted:" + _isMuted + gyorgy@0: ",paused:" + _isPaused + gyorgy@0: ",ended:" + _isEnded + gyorgy@0: ",volume:" + _volume + gyorgy@0: ",src:\"" + _currentUrl + "\"" + gyorgy@0: ",bytesTotal:" + _bytesTotal + gyorgy@0: ",bufferedBytes:" + _bytesLoaded + gyorgy@0: ",bufferedTime:" + _bufferedTime + gyorgy@0: ",videoWidth:" + _videoWidth + gyorgy@0: ",videoHeight:" + _videoHeight + gyorgy@0: ""; gyorgy@0: gyorgy@0: _element.sendEvent(eventName, values); gyorgy@0: } gyorgy@0: } gyorgy@0: }