diff --git a/trunk/research/players/img/tooltip.png b/trunk/research/players/img/tooltip.png new file mode 100644 index 000000000..eaee2aebc Binary files /dev/null and b/trunk/research/players/img/tooltip.png differ diff --git a/trunk/research/players/js/srs.js b/trunk/research/players/js/srs.js index 47b9bcf7c..13b3acc0e 100755 --- a/trunk/research/players/js/srs.js +++ b/trunk/research/players/js/srs.js @@ -1,3 +1,6 @@ +/** +* update the navigator, add same query string. +*/ function update_nav() { $("#nav_srs_player").attr("href", "srs_player.html" + window.location.search); $("#nav_srs_publisher").attr("href", "srs_publisher.html" + window.location.search); @@ -7,6 +10,9 @@ function update_nav() { $("#nav_vlc").attr("href", "vlc.html" + window.location.search); } +/** +* parse the query string to object. +*/ function parse_query_string(){ var query_string = String(window.location.search).replace(" ", "").split("?")[1]; if(query_string == undefined){ @@ -60,6 +66,11 @@ function build_default_hls_url() { return "http://" + vhost + ":" + port + "/" + app + "/" + stream + ".m3u8"; } +/** +* initialize the page. +* @param rtmp_url the rtmp stream url to play +* @param hls_url the hls stream url to play +*/ function srs_init(rtmp_url, hls_url) { update_nav(); @@ -70,3 +81,146 @@ function srs_init(rtmp_url, hls_url) { $(hls_url).val(build_default_hls_url()); } } + +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// +/** +* the SrsPlayer object. +*/ +function SrsPlayer(container, stream_url, width, height, buffer_time) { + if (!SrsPlayer.__id) { + SrsPlayer.__id = 100; + } + if (!SrsPlayer.__players) { + SrsPlayer.__players = []; + } + + SrsPlayer.__players.push(this); + + this.container = container; + this.stream_url = stream_url; + this.width = width; + this.height = height; + this.buffer_time = buffer_time; + this.id = SrsPlayer.__id++; + this.callbackObj = null; + + // callback set the following values. + this.meatadata = {}; // for on_player_metadata +} +/** +* user can set some callback, then start the player. +* callbacks: +* on_player_ready():int, when srs player ready, user can play. +* on_player_metadata(metadata:Object):int, when srs player get metadata. +*/ +SrsPlayer.prototype.start = function() { + // embed the flash. + var flashvars = {}; + flashvars.id = this.id; + flashvars.on_player_ready = "__srs_on_player_ready"; + flashvars.on_player_metadata = "__srs_on_player_metadata"; + + var params = {}; + params.wmode = "opaque"; + params.allowFullScreen = "true"; + params.allowScriptAccess = "always"; + + var attributes = {}; + + var self = this; + + swfobject.embedSWF( + "srs_player/release/srs_player.swf", this.container, + this.width, this.height, + "11.1", "js/AdobeFlashPlayerInstall.swf", + flashvars, params, attributes, + function(callbackObj){ + self.callbackObj = callbackObj; + } + ); + + return this; +} +SrsPlayer.prototype.play = function() { + return this.callbackObj.ref.__play(this.stream_url, this.width, this.height, this.buffer_time); +} +SrsPlayer.prototype.stop = function() { + for (var i = 0; i < SrsPlayer.__players.length; i++) { + var player = SrsPlayer.__players[i]; + + if (player.id != this.id) { + continue; + } + + SrsPlayer.__players.splice(i, 1); + break; + } + return this.callbackObj.ref.__stop(); +} +SrsPlayer.prototype.pause = function() { + return this.callbackObj.ref.__pause(); +} +SrsPlayer.prototype.resume = function() { + return this.callbackObj.ref.__resume(); +} +/** +* to set the DAR, for example, DAR=16:9 +* @param num, for example, 9. +* use metadata height if 0. +* use user specified height if -1. +* @param den, for example, 16. +* use metadata width if 0. +* use user specified width if -1. +*/ +SrsPlayer.prototype.dar = function(num, den) { + return this.callbackObj.ref.__dar(num, den); +} +/** +* set the fullscreen size data. +* @refer the refer fullscreen mode. it can be: +* video: use video orignal size. +* screen: use screen size to rescale video. +* @param percent, the rescale percent, where +* 100 means 100%. +*/ +SrsPlayer.prototype.set_fs = function(refer, percent) { + return this.callbackObj.ref.__set_fs(refer, percent); +} +SrsPlayer.prototype.on_player_ready = function() { + return this.play(); +} +SrsPlayer.prototype.on_player_metadata = function(metadata) { + return 0; +} +function __srs_on_player_ready(id) { + for (var i = 0; i < SrsPlayer.__players.length; i++) { + var player = SrsPlayer.__players[i]; + + if (player.id != id) { + continue; + } + + return player.on_player_ready(); + } + + throw new Error("player not found. id=" + id); +} +function __srs_on_player_metadata(id, metadata) { + for (var i = 0; i < SrsPlayer.__players.length; i++) { + var player = SrsPlayer.__players[i]; + + if (player.id != id) { + continue; + } + + // user may override the on_player_metadata, + // so set the data before invoke it. + player.metadata = metadata; + + return player.on_player_metadata(metadata); + } + + throw new Error("player not found. id=" + id); +} diff --git a/trunk/research/players/srs_player.html b/trunk/research/players/srs_player.html index 988ae3ba4..2ec9c81ed 100755 --- a/trunk/research/players/srs_player.html +++ b/trunk/research/players/srs_player.html @@ -14,13 +14,30 @@ } @@ -278,17 +184,37 @@
diff --git a/trunk/research/players/srs_player/release/srs_player.swf b/trunk/research/players/srs_player/release/srs_player.swf index 69e20cfb1..02f1ae84c 100755 Binary files a/trunk/research/players/srs_player/release/srs_player.swf and b/trunk/research/players/srs_player/release/srs_player.swf differ diff --git a/trunk/research/players/srs_player/src/srs_player.as b/trunk/research/players/srs_player/src/srs_player.as index 54ddcd651..1f4b3691a 100755 --- a/trunk/research/players/srs_player/src/srs_player.as +++ b/trunk/research/players/srs_player/src/srs_player.as @@ -2,13 +2,17 @@ package { import flash.display.Sprite; import flash.display.StageAlign; + import flash.display.StageDisplayState; import flash.display.StageScaleMode; import flash.events.Event; + import flash.events.FullScreenEvent; + import flash.events.MouseEvent; import flash.events.NetStatusEvent; import flash.external.ExternalInterface; import flash.media.Video; import flash.net.NetConnection; import flash.net.NetStream; + import flash.system.Security; import flash.ui.ContextMenu; import flash.ui.ContextMenuItem; import flash.utils.setTimeout; @@ -23,18 +27,29 @@ package // play param url. private var url:String = null; - // play param, user set width + // play param, user set width and height private var w:int = 0; - // play param, user set height. private var h:int = 0; + // user set dar den:num + private var dar_num:int = 0; + private var dar_den:int = 0; + // user set fs(fullscreen) refer and percent. + private var fs_refer:String = null; + private var fs_percent:int = 0; private var conn:NetConnection = null; private var stream:NetStream = null; private var video:Video = null; private var metadata:Object = {}; + // flash donot allow js to set to fullscreen, + // only allow user click to enter fullscreen. + private var fs_mask:Sprite = new Sprite(); + public function srs_player() { + flash.system.Security.allowDomain("*"); + if (!this.stage) { this.addEventListener(Event.ADDED_TO_STAGE, this.onAddToStage); } else { @@ -46,6 +61,12 @@ package this.stage.align = StageAlign.TOP_LEFT; this.stage.scaleMode = StageScaleMode.NO_SCALE; + this.stage.addEventListener(FullScreenEvent.FULL_SCREEN, this.on_stage_fullscreen); + + this.addChild(this.fs_mask); + this.fs_mask.buttonMode = true; + this.fs_mask.addEventListener(MouseEvent.CLICK, on_user_click_video); + this.contextMenu = new ContextMenu(); this.contextMenu.hideBuiltInItems(); @@ -62,13 +83,21 @@ package this.on_player_ready = flashvars.on_player_ready; this.on_player_metadata = flashvars.on_player_metadata; - flash.utils.setTimeout(this.onJsReady, 0); + flash.utils.setTimeout(this.on_js_ready, 0); } - private function onJsReady():void { + private function on_stage_fullscreen(evt:FullScreenEvent):void { + if (!evt.fullScreen) { + execute_user_set_dar(); + } else { + execute_user_enter_fullscreen(); + } + } + + private function on_js_ready():void { if (!flash.external.ExternalInterface.available) { trace("js not ready, try later."); - flash.utils.setTimeout(this.onJsReady, 100); + flash.utils.setTimeout(this.on_js_ready, 100); return; } @@ -77,6 +106,7 @@ package flash.external.ExternalInterface.addCallback("__pause", this.js_call_pause); flash.external.ExternalInterface.addCallback("__resume", this.js_call_resume); flash.external.ExternalInterface.addCallback("__dar", this.js_call_dar); + flash.external.ExternalInterface.addCallback("__set_fs", this.js_call_set_fs_size); var code:int = flash.external.ExternalInterface.call(this.on_player_ready, this.id); if (code != 0) { @@ -84,14 +114,14 @@ package } } - public function js_call_pause():int { + private function js_call_pause():int { if (this.stream) { this.stream.pause(); } return 0; } - public function js_call_resume():int { + private function js_call_resume():int { if (this.stream) { this.stream.resume(); } @@ -99,34 +129,54 @@ package } /** - * to set the DAR, for example, DAR=16:9 - * @param num, for example, 9. - * @param den, for example, 16. + * to set the DAR, for example, DAR=16:9 + * @param num, for example, 9. + * use metadata height if 0. + * use user specified height if -1. + * @param den, for example, 16. + * use metadata width if 0. + * use user specified width if -1. + */ + private function js_call_dar(num:int, den:int):int { + dar_num = num; + dar_den = den; + + flash.utils.setTimeout(execute_user_set_dar, 0); + return 0; + } + + /** + * set the fullscreen size data. + * @refer the refer fullscreen mode. it can be: + * video: use video orignal size. + * screen: use screen size to rescale video. + * @param percent, the rescale percent, where + * 100 means 100%. + */ + private function js_call_set_fs_size(refer:String, percent:int):int { + fs_refer = refer; + fs_percent = percent; + + return 0; + } + /** + * js cannot enter the fullscreen mode, user must click to. */ - public function js_call_dar(num:int, den:int):int { - if (this.video && num > 0 && den > 0 && this.video.width > 0) { - // set DAR. - // calc the height by DAR - var _height:int = this.w * num / den; - if (_height <= this.h) { - this.video.width = this.w; - this.video.height = _height; - } else { - // height overflow, calc the width by DAR - var _width:int = this.h * den / num; - - this.video.width = _width; - this.video.height = this.h; - } - - // align center. - this.video.y = (this.h - this.video.height) / 2; - this.video.x = (this.w - this.video.width) / 2; + private function on_user_click_video(evt:MouseEvent):void { + if (!this.stage.allowsFullScreen) { + trace("donot allow fullscreen."); + return; + } + + // enter fullscreen to get the fullscreen size correctly. + if (this.stage.displayState == StageDisplayState.FULL_SCREEN) { + this.stage.displayState = StageDisplayState.NORMAL; + } else { + this.stage.displayState = StageDisplayState.FULL_SCREEN; } - return 0; } - public function js_call_stop():int { + private function js_call_stop():int { if (this.stream) { this.stream.close(); this.stream = null; @@ -143,17 +193,24 @@ package return 0; } - public function js_call_play(url:String, _width:int, _height:int, _buffer_time:Number):int { + private function draw_black_background(_width:int, _height:int):void { + // draw black bg. + this.graphics.beginFill(0x00, 1.0); + this.graphics.drawRect(0, 0, _width, _height); + this.graphics.endFill(); + + // draw the fs mask. + this.fs_mask.graphics.beginFill(0xff0000, 0); + this.fs_mask.graphics.drawRect(0, 0, _width, _height); + this.fs_mask.graphics.endFill(); + } + + private function js_call_play(url:String, _width:int, _height:int, _buffer_time:Number):int { this.url = url; this.w = _width; this.h = _height; trace("start to play url: " + this.url + ", w=" + this.w + ", h=" + this.h); - // draw black bg. - this.graphics.beginFill(0x00, 1.0); - this.graphics.drawRect(0, 0, this.w, this.h); - this.graphics.endFill(); - js_call_stop(); this.conn = new NetConnection(); @@ -184,6 +241,11 @@ package video.attachNetStream(stream); video.smoothing = true; addChild(video); + + draw_black_background(_width, _height); + + // lowest layer, for mask to cover it. + setChildIndex(video, 0); }); this.conn.connect(this.url.substr(0, this.url.lastIndexOf("/"))); @@ -204,13 +266,37 @@ package contextMenu.customItems = customItems; // for js. + var obj:Object = get_video_size_object(); + + obj.server = 'srs'; + obj.contributor = 'winlin'; + + if (metadata.hasOwnProperty("server")) { + obj.server = metadata.server; + } + if (metadata.hasOwnProperty("contributor")) { + obj.contributor = metadata.contributor; + } + + var code:int = flash.external.ExternalInterface.call(on_player_metadata, id, obj); + if (code != 0) { + throw new Error("callback on_player_metadata failed. code=" + code); + } + } + + /** + * get the "right" size of video, + * 1. initialize with the original video object size. + * 2. override with metadata size if specified. + * 3. override with codec size if specified. + */ + private function get_video_size_object():Object { var obj:Object = { width: video.width, - height: video.height, - server: 'srs', - contributor: 'winlin' + height: video.height }; + // override with metadata size. if (metadata.hasOwnProperty("width")) { obj.width = metadata.width; } @@ -218,6 +304,7 @@ package obj.height = metadata.height; } + // override with codec size. if (video.videoWidth > 0) { obj.width = video.videoWidth; } @@ -225,17 +312,110 @@ package obj.height = video.videoHeight; } - if (metadata.hasOwnProperty("server")) { - obj.server = metadata.server; + return obj; + } + + /** + * execute the enter fullscreen action. + */ + private function execute_user_enter_fullscreen():void { + if (!fs_refer || fs_percent <= 0) { + return; } - if (metadata.hasOwnProperty("contributor")) { - obj.contributor = metadata.contributor; + + // change to video size if refer to video. + var obj:Object = get_video_size_object(); + + // get the DAR + var num:int = dar_num; + var den:int = dar_den; + + if (num == 0) { + num = obj.height; + } + if (num == -1) { + num = this.stage.fullScreenHeight; } - var code:int = flash.external.ExternalInterface.call(on_player_metadata, id, obj); - if (code != 0) { - throw new Error("callback on_player_metadata failed. code=" + code); + if (den == 0) { + den = obj.width; + } + if (den == -1) { + den = this.stage.fullScreenWidth; + } + + // for refer is screen. + if (fs_refer == "screen") { + obj = { + width: this.stage.fullScreenWidth, + height: this.stage.fullScreenHeight + }; } + + // rescale to fs + update_video_size(num, den, obj.width * fs_percent / 100, obj.height * fs_percent / 100, this.stage.fullScreenWidth, this.stage.fullScreenHeight); + } + + /** + * for user set dar, or leave fullscreen to recover the dar. + */ + private function execute_user_set_dar():void { + // get the DAR + var num:int = dar_num; + var den:int = dar_den; + + var obj:Object = get_video_size_object(); + + if (num == 0) { + num = obj.height; + } + if (num == -1) { + num = this.h; + } + + if (den == 0) { + den = obj.width; + } + if (den == -1) { + den = this.w; + } + + update_video_size(num, den, this.w, this.h, this.w, this.h); + } + + /** + * update the video width and height, + * according to the specifies DAR(den:num) and max size(w:h). + * set the position of video(x,y) specifies by size(sw:sh), + * and update the bg to size(sw:sh). + * @param _num/_den the DAR. use to rescale the player together with paper size. + * @param _w/_h the video draw paper size. used to rescale the player together with DAR. + * @param _sw/_wh the stage size, >= paper size. used to center the player. + */ + private function update_video_size(_num:int, _den:int, _w:int, _h:int, _sw:int, _sh:int):void { + if (!this.video || _num <= 0 || _den <= 0) { + return; + } + + // set DAR. + // calc the height by DAR + var _height:int = _w * _num / _den; + if (_height <= _h) { + this.video.width = _w; + this.video.height = _height; + } else { + // height overflow, calc the width by DAR + var _width:int = _h * _den / _num; + + this.video.width = _width; + this.video.height = _h; + } + + // align center. + this.video.x = (_sw - this.video.width) / 2; + this.video.y = (_sh - this.video.height) / 2; + + draw_black_background(_sw, _sh); } } } \ No newline at end of file