/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mangui.chromeless { import flash.display.*; import flash.events.*; import flash.external.ExternalInterface; import flash.geom.Rectangle; import flash.media.SoundTransform; import flash.media.StageVideo; import flash.media.StageVideoAvailability; import flash.media.Video; import flash.net.URLLoader; import flash.net.URLStream; import flash.system.Security; import flash.utils.getTimer; import flash.utils.setTimeout; import org.mangui.hls.event.HLSError; import org.mangui.hls.event.HLSEvent; import org.mangui.hls.HLS; import org.mangui.hls.HLSSettings; import org.mangui.hls.model.AudioTrack; import org.mangui.hls.model.Level; import org.mangui.hls.utils.JSURLLoader; import org.mangui.hls.utils.JSURLStream; import org.mangui.hls.utils.Log; import org.mangui.hls.utils.ScaleVideo; // import com.sociodox.theminer.*; public class ChromelessPlayer extends Sprite { /** reference to the framework. **/ protected var _hls : HLS; /** Sheet to place on top of the video. **/ protected var _sheet : Sprite; /** Reference to the stage video element. **/ protected var _stageVideo : StageVideo = null; /** Reference to the video element. **/ protected var _video : Video = null; /** Video size **/ protected var _videoWidth : int = 0; protected var _videoHeight : int = 0; /** current media position */ protected var _mediaPosition : Number; protected var _duration : Number; /** URL autoload feature */ protected var _autoLoad : Boolean = false; /* JS callback name */ protected var _callbackName : String; /* stats handler */ private var _statsHandler : StatsHandler; /** Initialization. **/ public function ChromelessPlayer() { Security.allowDomain("*"); Security.allowInsecureDomain("*"); ExternalInterface.marshallExceptions = true; _setupStage(); _setupSheet(); _setupExternalGetters(); _setupExternalCallers(); _setupExternalCallback(); setTimeout(_pingJavascript, 50); }; protected function _setupExternalGetters() : void { ExternalInterface.addCallback("getCurrentLevel", _getCurrentLevel); ExternalInterface.addCallback("getNextLevel", _getNextLevel); ExternalInterface.addCallback("getLoadLevel", _getLoadLevel); ExternalInterface.addCallback("getLevels", _getLevels); ExternalInterface.addCallback("getAutoLevel", _getAutoLevel); ExternalInterface.addCallback("getDuration", _getDuration); ExternalInterface.addCallback("getPosition", _getPosition); ExternalInterface.addCallback("getPlaybackState", _getPlaybackState); ExternalInterface.addCallback("getSeekState", _getSeekState); ExternalInterface.addCallback("getType", _getType); ExternalInterface.addCallback("getmaxBufferLength", _getmaxBufferLength); ExternalInterface.addCallback("getminBufferLength", _getminBufferLength); ExternalInterface.addCallback("getlowBufferLength", _getlowBufferLength); ExternalInterface.addCallback("getmaxBackBufferLength", _getmaxBackBufferLength); ExternalInterface.addCallback("getbufferLength", _getbufferLength); ExternalInterface.addCallback("getbackBufferLength", _getbackBufferLength); ExternalInterface.addCallback("getLogDebug", _getLogDebug); ExternalInterface.addCallback("getLogDebug2", _getLogDebug2); ExternalInterface.addCallback("getUseHardwareVideoDecoder", _getUseHardwareVideoDecoder); ExternalInterface.addCallback("getCapLeveltoStage", _getCapLeveltoStage); ExternalInterface.addCallback("getAutoLevelCapping", _getAutoLevelCapping); ExternalInterface.addCallback("getflushLiveURLCache", _getflushLiveURLCache); ExternalInterface.addCallback("getstartFromLevel", _getstartFromLevel); ExternalInterface.addCallback("getseekFromLowestLevel", _getseekFromLevel); ExternalInterface.addCallback("getJSURLStream", _getJSURLStream); ExternalInterface.addCallback("getPlayerVersion", _getPlayerVersion); ExternalInterface.addCallback("getAudioTrackList", _getAudioTrackList); ExternalInterface.addCallback("getAudioTrackId", _getAudioTrackId); ExternalInterface.addCallback("getStats", _getStats); }; protected function _setupExternalCallers() : void { ExternalInterface.addCallback("playerLoad", _load); ExternalInterface.addCallback("playerPlay", _play); ExternalInterface.addCallback("playerPause", _pause); ExternalInterface.addCallback("playerResume", _resume); ExternalInterface.addCallback("playerSeek", _seek); ExternalInterface.addCallback("playerStop", _stop); ExternalInterface.addCallback("playerVolume", _volume); ExternalInterface.addCallback("playerSetCurrentLevel", _setCurrentLevel); ExternalInterface.addCallback("playerSetNextLevel", _setNextLevel); ExternalInterface.addCallback("playerSetLoadLevel", _setLoadLevel); ExternalInterface.addCallback("playerSetmaxBufferLength", _setmaxBufferLength); ExternalInterface.addCallback("playerSetminBufferLength", _setminBufferLength); ExternalInterface.addCallback("playerSetlowBufferLength", _setlowBufferLength); ExternalInterface.addCallback("playerSetbackBufferLength", _setbackBufferLength); ExternalInterface.addCallback("playerSetflushLiveURLCache", _setflushLiveURLCache); ExternalInterface.addCallback("playerSetstartFromLevel", _setstartFromLevel); ExternalInterface.addCallback("playerSetseekFromLevel", _setseekFromLevel); ExternalInterface.addCallback("playerSetLogDebug", _setLogDebug); ExternalInterface.addCallback("playerSetLogDebug2", _setLogDebug2); ExternalInterface.addCallback("playerSetUseHardwareVideoDecoder", _setUseHardwareVideoDecoder); ExternalInterface.addCallback("playerSetAutoLevelCapping", _setAutoLevelCapping); ExternalInterface.addCallback("playerCapLeveltoStage", _setCapLeveltoStage); ExternalInterface.addCallback("playerSetAudioTrack", _setAudioTrack); ExternalInterface.addCallback("playerSetJSURLStream", _setJSURLStream); }; protected function _setupExternalCallback() : void { // Pass in the JavaScript callback name in the `callback` FlashVars parameter. _callbackName = LoaderInfo(this.root.loaderInfo).parameters.callback.toString(); }; protected function _setupStage() : void { stage.scaleMode = StageScaleMode.NO_SCALE; stage.align = StageAlign.TOP_LEFT; stage.fullScreenSourceRect = new Rectangle(0, 0, stage.stageWidth, stage.stageHeight); stage.addEventListener(StageVideoAvailabilityEvent.STAGE_VIDEO_AVAILABILITY, _onStageVideoState); stage.addEventListener(Event.RESIZE, _onStageResize); } protected function _setupSheet() : void { // Draw sheet for catching clicks _sheet = new Sprite(); _sheet.graphics.beginFill(0x000000, 0); _sheet.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight); _sheet.addEventListener(MouseEvent.CLICK, _clickHandler); _sheet.buttonMode = true; addChild(_sheet); } protected function _trigger(event : String, ...args) : void { if (ExternalInterface.available) { ExternalInterface.call(_callbackName, event, args); } }; /** Notify javascript the framework is ready. **/ protected function _pingJavascript() : void { _trigger("ready", getTimer()); }; /** Forward events from the framework. **/ protected function _completeHandler(event : HLSEvent) : void { _trigger("complete"); }; protected function _errorHandler(event : HLSEvent) : void { var hlsError : HLSError = event.error; _trigger("error", hlsError.code, hlsError.url, hlsError.msg); }; protected function _levelLoadedHandler(event : HLSEvent) : void { _trigger("levelLoaded", event.loadMetrics); }; protected function _levelEndlistHandler(event : HLSEvent) : void { _trigger("levelEndlist", event.level); }; protected function _audioLevelLoadedHandler(event : HLSEvent) : void { _trigger("audioLevelLoaded", event.loadMetrics); }; protected function _fragmentLoadedHandler(event : HLSEvent) : void { _trigger("fragmentLoaded", event.loadMetrics); }; protected function _fragmentPlayingHandler(event : HLSEvent) : void { _trigger("fragmentPlaying", event.playMetrics); }; protected function _manifestLoadedHandler(event : HLSEvent) : void { _duration = event.levels[_hls.startLevel].duration; if (_autoLoad) { _play(-1); } _trigger("manifest", _duration, event.levels, event.loadMetrics); }; protected function _mediaTimeHandler(event : HLSEvent) : void { _duration = event.mediatime.duration; _mediaPosition = event.mediatime.position; _trigger("position", event.mediatime); var videoWidth : int = _video ? _video.videoWidth : _stageVideo.videoWidth; var videoHeight : int = _video ? _video.videoHeight : _stageVideo.videoHeight; if (videoWidth && videoHeight) { var changed : Boolean = _videoWidth != videoWidth || _videoHeight != videoHeight; if (changed) { _videoHeight = videoHeight; _videoWidth = videoWidth; _resize(); _trigger("videoSize", _videoWidth, _videoHeight); } } }; protected function _playbackStateHandler(event : HLSEvent) : void { _trigger("state", event.state); }; protected function _seekStateHandler(event : HLSEvent) : void { _trigger("seekState", event.state); }; protected function _levelSwitchHandler(event : HLSEvent) : void { _trigger("switch", event.level); }; protected function _fpsDropHandler(event : HLSEvent) : void { _trigger("fpsDrop", event.level); }; protected function _fpsDropLevelCappingHandler(event : HLSEvent) : void { _trigger("fpsDropLevelCapping", event.level); }; protected function _fpsDropSmoothLevelSwitchHandler(event : HLSEvent) : void { _trigger("fpsDropSmoothLevelSwitch"); }; protected function _audioTracksListChange(event : HLSEvent) : void { _trigger("audioTracksListChange", _getAudioTrackList()); } protected function _audioTrackChange(event : HLSEvent) : void { _trigger("audioTrackChange", event.audioTrack); } protected function _id3Updated(event : HLSEvent) : void { _trigger("id3Updated", event.ID3Data); } protected function _liveLoadingStalledHandler(event : HLSEvent) : void { _trigger("liveLoadingStalled"); }; /** Javascript getters. **/ protected function _getCurrentLevel() : int { return _hls.currentLevel; }; protected function _getNextLevel() : int { return _hls.nextLevel; }; protected function _getLoadLevel() : int { return _hls.loadLevel; }; protected function _getLevels() : Vector. { return _hls.levels; }; protected function _getAutoLevel() : Boolean { return _hls.autoLevel; }; protected function _getDuration() : Number { return _duration; }; protected function _getPosition() : Number { return _hls.position; }; protected function _getPlaybackState() : String { return _hls.playbackState; }; protected function _getSeekState() : String { return _hls.seekState; }; protected function _getType() : String { return _hls.type; }; protected function _getbufferLength() : Number { return _hls.stream.bufferLength; }; protected function _getbackBufferLength() : Number { return _hls.stream.backBufferLength; }; protected function _getmaxBufferLength() : Number { return HLSSettings.maxBufferLength; }; protected function _getminBufferLength() : Number { return HLSSettings.minBufferLength; }; protected function _getlowBufferLength() : Number { return HLSSettings.lowBufferLength; }; protected function _getmaxBackBufferLength() : Number { return HLSSettings.maxBackBufferLength; }; protected function _getflushLiveURLCache() : Boolean { return HLSSettings.flushLiveURLCache; }; protected function _getstartFromLevel() : int { return HLSSettings.startFromLevel; }; protected function _getseekFromLevel() : int { return HLSSettings.seekFromLevel; }; protected function _getLogDebug() : Boolean { return HLSSettings.logDebug; }; protected function _getLogDebug2() : Boolean { return HLSSettings.logDebug2; }; protected function _getUseHardwareVideoDecoder() : Boolean { return HLSSettings.useHardwareVideoDecoder; }; protected function _getCapLeveltoStage() : Boolean { return HLSSettings.capLevelToStage; }; protected function _getAutoLevelCapping() : int { return _hls.autoLevelCapping; }; protected function _getJSURLStream() : Boolean { return (_hls.URLstream is JSURLStream); }; protected function _getPlayerVersion() : Number { return 3; }; protected function _getAudioTrackList() : Array { var list : Array = []; var vec : Vector. = _hls.audioTracks; for (var i : Object in vec) { list.push(vec[i]); } return list; }; protected function _getAudioTrackId() : int { return _hls.audioTrack; }; protected function _getStats() : Object { return _statsHandler.stats; }; /** Javascript calls. **/ protected function _load(url : String) : void { _hls.load(url); }; protected function _play(position : Number = -1) : void { _hls.stream.play(null, position); }; protected function _pause() : void { _hls.stream.pause(); }; protected function _resume() : void { _hls.stream.resume(); }; protected function _seek(position : Number) : void { _hls.stream.seek(position); }; protected function _stop() : void { _hls.stream.close(); }; protected function _volume(percent : Number) : void { _hls.stream.soundTransform = new SoundTransform(percent / 100); }; protected function _setCurrentLevel(level : int) : void { _hls.currentLevel = level; }; protected function _setNextLevel(level : int) : void { _hls.nextLevel = level; }; protected function _setLoadLevel(level : int) : void { _hls.loadLevel = level; }; protected function _setmaxBufferLength(newLen : Number) : void { HLSSettings.maxBufferLength = newLen; }; protected function _setminBufferLength(newLen : Number) : void { HLSSettings.minBufferLength = newLen; }; protected function _setlowBufferLength(newLen : Number) : void { HLSSettings.lowBufferLength = newLen; }; protected function _setbackBufferLength(newLen : Number) : void { HLSSettings.maxBackBufferLength = newLen; }; protected function _setflushLiveURLCache(flushLiveURLCache : Boolean) : void { HLSSettings.flushLiveURLCache = flushLiveURLCache; }; protected function _setstartFromLevel(startFromLevel : int) : void { HLSSettings.startFromLevel = startFromLevel; }; protected function _setseekFromLevel(seekFromLevel : int) : void { HLSSettings.seekFromLevel = seekFromLevel; }; protected function _setLogDebug(debug : Boolean) : void { HLSSettings.logDebug = debug; }; protected function _setLogDebug2(debug2 : Boolean) : void { HLSSettings.logDebug2 = debug2; }; protected function _setUseHardwareVideoDecoder(value : Boolean) : void { HLSSettings.useHardwareVideoDecoder = value; }; protected function _setCapLeveltoStage(value : Boolean) : void { HLSSettings.capLevelToStage = value; }; protected function _setAutoLevelCapping(value : int) : void { _hls.autoLevelCapping = value; }; protected function _setJSURLStream(jsURLstream : Boolean) : void { if (jsURLstream) { _hls.URLstream = JSURLStream as Class; _hls.URLloader = JSURLLoader as Class; if (_callbackName) { _hls.URLstream.externalCallback = _callbackName; _hls.URLloader.externalCallback = _callbackName; } } else { _hls.URLstream = URLStream as Class; _hls.URLloader = URLLoader as Class; } }; protected function _setAudioTrack(val : int) : void { if (val == _hls.audioTrack) return; _hls.audioTrack = val; if (!isNaN(_mediaPosition)) { _hls.stream.seek(_mediaPosition); } }; /** Mouse click handler. **/ protected function _clickHandler(event : MouseEvent) : void { if (stage.displayState == StageDisplayState.FULL_SCREEN_INTERACTIVE || stage.displayState == StageDisplayState.FULL_SCREEN) { stage.displayState = StageDisplayState.NORMAL; } else { stage.displayState = StageDisplayState.FULL_SCREEN; } }; /** StageVideo detector. **/ protected function _onStageVideoState(event : StageVideoAvailabilityEvent) : void { var available : Boolean = (event.availability == StageVideoAvailability.AVAILABLE); _hls = new HLS(); _hls.stage = stage; // set framerate to 60 fps stage.frameRate = 60; // set up stats handler _statsHandler = new StatsHandler(_hls); _hls.addEventListener(HLSEvent.PLAYBACK_COMPLETE, _completeHandler); _hls.addEventListener(HLSEvent.ERROR, _errorHandler); _hls.addEventListener(HLSEvent.FRAGMENT_LOADED, _fragmentLoadedHandler); _hls.addEventListener(HLSEvent.AUDIO_LEVEL_LOADED, _audioLevelLoadedHandler); _hls.addEventListener(HLSEvent.LEVEL_LOADED, _levelLoadedHandler); _hls.addEventListener(HLSEvent.LEVEL_ENDLIST, _levelEndlistHandler); _hls.addEventListener(HLSEvent.FRAGMENT_PLAYING, _fragmentPlayingHandler); _hls.addEventListener(HLSEvent.MANIFEST_LOADED, _manifestLoadedHandler); _hls.addEventListener(HLSEvent.MEDIA_TIME, _mediaTimeHandler); _hls.addEventListener(HLSEvent.PLAYBACK_STATE, _playbackStateHandler); _hls.addEventListener(HLSEvent.SEEK_STATE, _seekStateHandler); _hls.addEventListener(HLSEvent.LEVEL_SWITCH, _levelSwitchHandler); _hls.addEventListener(HLSEvent.AUDIO_TRACKS_LIST_CHANGE, _audioTracksListChange); _hls.addEventListener(HLSEvent.AUDIO_TRACK_SWITCH, _audioTrackChange); _hls.addEventListener(HLSEvent.ID3_UPDATED, _id3Updated); _hls.addEventListener(HLSEvent.FPS_DROP, _fpsDropHandler); _hls.addEventListener(HLSEvent.FPS_DROP_LEVEL_CAPPING, _fpsDropLevelCappingHandler); _hls.addEventListener(HLSEvent.FPS_DROP_SMOOTH_LEVEL_SWITCH, _fpsDropSmoothLevelSwitchHandler); _hls.addEventListener(HLSEvent.LIVE_LOADING_STALLED, _liveLoadingStalledHandler); if (available && stage.stageVideos.length > 0) { _stageVideo = stage.stageVideos[0]; _stageVideo.addEventListener(StageVideoEvent.RENDER_STATE, _onStageVideoStateChange); _stageVideo.viewPort = new Rectangle(0, 0, stage.stageWidth, stage.stageHeight); _stageVideo.attachNetStream(_hls.stream); } else { _video = new Video(stage.stageWidth, stage.stageHeight); _video.addEventListener(VideoEvent.RENDER_STATE, _onVideoStateChange); addChild(_video); _video.smoothing = true; _video.attachNetStream(_hls.stream); } stage.removeEventListener(StageVideoAvailabilityEvent.STAGE_VIDEO_AVAILABILITY, _onStageVideoState); var autoLoadUrl : String = root.loaderInfo.parameters.url as String; if (autoLoadUrl != null) { _autoLoad = true; _load(autoLoadUrl); } }; private function _onStageVideoStateChange(event : StageVideoEvent) : void { Log.info("Video decoding:" + event.status); } private function _onVideoStateChange(event : VideoEvent) : void { Log.info("Video decoding:" + event.status); } protected function _onStageResize(event : Event) : void { stage.fullScreenSourceRect = new Rectangle(0, 0, stage.stageWidth, stage.stageHeight); _sheet.width = stage.stageWidth; _sheet.height = stage.stageHeight; _resize(); }; protected function _resize() : void { var rect : Rectangle; rect = ScaleVideo.resizeRectangle(_videoWidth, _videoHeight, stage.stageWidth, stage.stageHeight); // resize video if (_video) { _video.width = rect.width; _video.height = rect.height; _video.x = rect.x; _video.y = rect.y; } else if (_stageVideo && rect.width > 0) { _stageVideo.viewPort = rect; } } } }