正在显示
9 个修改的文件
包含
825 行增加
和
35 行删除
| @@ -116,11 +116,10 @@ function srs_init(rtmp_url, hls_url, modal_player) { | @@ -116,11 +116,10 @@ function srs_init(rtmp_url, hls_url, modal_player) { | ||
| 116 | /** | 116 | /** |
| 117 | * the SrsPlayer object. | 117 | * the SrsPlayer object. |
| 118 | * @param container the html container id. | 118 | * @param container the html container id. |
| 119 | -* @param stream_url the url of stream, rtmp or http. | ||
| 120 | * @param width a float value specifies the width of player. | 119 | * @param width a float value specifies the width of player. |
| 121 | * @param height a float value specifies the height of player. | 120 | * @param height a float value specifies the height of player. |
| 122 | */ | 121 | */ |
| 123 | -function SrsPlayer(container, stream_url, width, height) { | 122 | +function SrsPlayer(container, width, height) { |
| 124 | if (!SrsPlayer.__id) { | 123 | if (!SrsPlayer.__id) { |
| 125 | SrsPlayer.__id = 100; | 124 | SrsPlayer.__id = 100; |
| 126 | } | 125 | } |
| @@ -131,12 +130,12 @@ function SrsPlayer(container, stream_url, width, height) { | @@ -131,12 +130,12 @@ function SrsPlayer(container, stream_url, width, height) { | ||
| 131 | SrsPlayer.__players.push(this); | 130 | SrsPlayer.__players.push(this); |
| 132 | 131 | ||
| 133 | this.container = container; | 132 | this.container = container; |
| 134 | - this.stream_url = stream_url; | ||
| 135 | this.width = width; | 133 | this.width = width; |
| 136 | this.height = height; | 134 | this.height = height; |
| 137 | this.id = SrsPlayer.__id++; | 135 | this.id = SrsPlayer.__id++; |
| 138 | - this.callbackObj = null; | 136 | + this.stream_url = null; |
| 139 | this.buffer_time = 0.8; // default to 0.8 | 137 | this.buffer_time = 0.8; // default to 0.8 |
| 138 | + this.callbackObj = null; | ||
| 140 | 139 | ||
| 141 | // callback set the following values. | 140 | // callback set the following values. |
| 142 | this.meatadata = {}; // for on_player_metadata | 141 | this.meatadata = {}; // for on_player_metadata |
| @@ -178,7 +177,12 @@ SrsPlayer.prototype.start = function() { | @@ -178,7 +177,12 @@ SrsPlayer.prototype.start = function() { | ||
| 178 | 177 | ||
| 179 | return this; | 178 | return this; |
| 180 | } | 179 | } |
| 181 | -SrsPlayer.prototype.play = function() { | 180 | +/** |
| 181 | +* play the stream. | ||
| 182 | +* @param stream_url the url of stream, rtmp or http. | ||
| 183 | +*/ | ||
| 184 | +SrsPlayer.prototype.play = function(url) { | ||
| 185 | + this.stream_url = url; | ||
| 182 | this.callbackObj.ref.__play(this.stream_url, this.width, this.height, this.buffer_time); | 186 | this.callbackObj.ref.__play(this.stream_url, this.width, this.height, this.buffer_time); |
| 183 | } | 187 | } |
| 184 | SrsPlayer.prototype.stop = function() { | 188 | SrsPlayer.prototype.stop = function() { |
| @@ -233,7 +237,6 @@ SrsPlayer.prototype.set_bt = function(buffer_time) { | @@ -233,7 +237,6 @@ SrsPlayer.prototype.set_bt = function(buffer_time) { | ||
| 233 | this.callbackObj.ref.__set_bt(buffer_time); | 237 | this.callbackObj.ref.__set_bt(buffer_time); |
| 234 | } | 238 | } |
| 235 | SrsPlayer.prototype.on_player_ready = function() { | 239 | SrsPlayer.prototype.on_player_ready = function() { |
| 236 | - this.play(); | ||
| 237 | } | 240 | } |
| 238 | SrsPlayer.prototype.on_player_metadata = function(metadata) { | 241 | SrsPlayer.prototype.on_player_metadata = function(metadata) { |
| 239 | // ignore. | 242 | // ignore. |
| @@ -286,4 +289,131 @@ function __srs_on_player_timer(id, time, buffer_length) { | @@ -286,4 +289,131 @@ function __srs_on_player_timer(id, time, buffer_length) { | ||
| 286 | ////////////////////////////////////////////////////////////////////////////////// | 289 | ////////////////////////////////////////////////////////////////////////////////// |
| 287 | ////////////////////////////////////////////////////////////////////////////////// | 290 | ////////////////////////////////////////////////////////////////////////////////// |
| 288 | ////////////////////////////////////////////////////////////////////////////////// | 291 | ////////////////////////////////////////////////////////////////////////////////// |
| 292 | +/** | ||
| 293 | +* the SrsPublisher object. | ||
| 294 | +* @param container the html container id. | ||
| 295 | +* @param width a float value specifies the width of publisher. | ||
| 296 | +* @param height a float value specifies the height of publisher. | ||
| 297 | +*/ | ||
| 298 | +function SrsPublisher(container, width, height) { | ||
| 299 | + if (!SrsPublisher.__id) { | ||
| 300 | + SrsPublisher.__id = 100; | ||
| 301 | + } | ||
| 302 | + if (!SrsPublisher.__publishers) { | ||
| 303 | + SrsPublisher.__publishers = []; | ||
| 304 | + } | ||
| 305 | + | ||
| 306 | + SrsPublisher.__publishers.push(this); | ||
| 307 | + | ||
| 308 | + this.container = container; | ||
| 309 | + this.width = width; | ||
| 310 | + this.height = height; | ||
| 311 | + this.id = SrsPublisher.__id++; | ||
| 312 | + this.callbackObj = null; | ||
| 313 | + | ||
| 314 | + // set the values when publish. | ||
| 315 | + this.url = null; | ||
| 316 | + this.vcodec = {}; | ||
| 317 | + this.acodec = {}; | ||
| 318 | + | ||
| 319 | + // callback set the following values. | ||
| 320 | + this.cameras = []; | ||
| 321 | + this.microphones = []; | ||
| 322 | + this.code = 0; | ||
| 323 | + | ||
| 324 | + // error code defines. | ||
| 325 | + this.error_device_muted = 100; | ||
| 326 | +} | ||
| 327 | +/** | ||
| 328 | +* user can set some callback, then start the publisher. | ||
| 329 | +* callbacks: | ||
| 330 | +* on_publisher_ready(cameras, microphones):int, when srs publisher ready, user can publish. | ||
| 331 | +* on_publisher_error(code):int, when srs publisher error, callback this method. | ||
| 332 | +*/ | ||
| 333 | +SrsPublisher.prototype.start = function() { | ||
| 334 | + // embed the flash. | ||
| 335 | + var flashvars = {}; | ||
| 336 | + flashvars.id = this.id; | ||
| 337 | + flashvars.on_publisher_ready = "__srs_on_publisher_ready"; | ||
| 338 | + flashvars.on_publisher_error = "__srs_on_publisher_error"; | ||
| 339 | + | ||
| 340 | + var params = {}; | ||
| 341 | + params.wmode = "opaque"; | ||
| 342 | + params.allowFullScreen = "true"; | ||
| 343 | + params.allowScriptAccess = "always"; | ||
| 344 | + | ||
| 345 | + var attributes = {}; | ||
| 346 | + | ||
| 347 | + var self = this; | ||
| 348 | + | ||
| 349 | + swfobject.embedSWF( | ||
| 350 | + "srs_publisher/release/srs_publisher.swf", this.container, | ||
| 351 | + this.width, this.height, | ||
| 352 | + "11.1", "js/AdobeFlashPlayerInstall.swf", | ||
| 353 | + flashvars, params, attributes, | ||
| 354 | + function(callbackObj){ | ||
| 355 | + self.callbackObj = callbackObj; | ||
| 356 | + } | ||
| 357 | + ); | ||
| 358 | + | ||
| 359 | + return this; | ||
| 360 | +} | ||
| 361 | +/** | ||
| 362 | +* publish stream to server. | ||
| 363 | +* @param url a string indicates the rtmp url to publish. | ||
| 364 | +* @param vcodec an object contains the video codec info. | ||
| 365 | +* @param acodec an object contains the audio codec info. | ||
| 366 | +*/ | ||
| 367 | +SrsPublisher.prototype.publish = function(url, vcodec, acodec) { | ||
| 368 | + this.url = url; | ||
| 369 | + this.vcodec = vcodec; | ||
| 370 | + this.acodec = acodec; | ||
| 371 | + | ||
| 372 | + this.callbackObj.ref.__publish(url, this.width, this.height, vcodec, acodec); | ||
| 373 | +} | ||
| 374 | +SrsPublisher.prototype.stop = function() { | ||
| 375 | + this.callbackObj.ref.__stop(); | ||
| 376 | +} | ||
| 377 | +/** | ||
| 378 | +* when publisher ready. | ||
| 379 | +* @param cameras a string array contains the names of cameras. | ||
| 380 | +* @param microphones a string array contains the names of microphones. | ||
| 381 | +*/ | ||
| 382 | +SrsPublisher.prototype.on_publisher_ready = function(cameras, microphones) { | ||
| 383 | +} | ||
| 384 | +/** | ||
| 385 | +* when publisher error. | ||
| 386 | +* @code the error code. | ||
| 387 | +*/ | ||
| 388 | +SrsPublisher.prototype.on_publisher_error = function(code) { | ||
| 389 | + throw new Error("publisher error. code=" + code); | ||
| 390 | +} | ||
| 391 | +function __srs_find_publisher(id) { | ||
| 392 | + for (var i = 0; i < SrsPublisher.__publishers.length; i++) { | ||
| 393 | + var publisher = SrsPublisher.__publishers[i]; | ||
| 394 | + | ||
| 395 | + if (publisher.id != id) { | ||
| 396 | + continue; | ||
| 397 | + } | ||
| 398 | + | ||
| 399 | + return publisher; | ||
| 400 | + } | ||
| 401 | + | ||
| 402 | + throw new Error("publisher not found. id=" + id); | ||
| 403 | +} | ||
| 404 | +function __srs_on_publisher_ready(id, cameras, microphones) { | ||
| 405 | + var publisher = __srs_find_publisher(id); | ||
| 406 | + | ||
| 407 | + publisher.cameras = cameras; | ||
| 408 | + publisher.microphones = microphones; | ||
| 409 | + | ||
| 410 | + publisher.on_publisher_ready(cameras, microphones); | ||
| 411 | +} | ||
| 412 | +function __srs_on_publisher_error(id, code) { | ||
| 413 | + var publisher = __srs_find_publisher(id); | ||
| 414 | + | ||
| 415 | + publisher.code = code; | ||
| 416 | + | ||
| 417 | + publisher.on_publisher_error(code); | ||
| 418 | +} | ||
| 289 | 419 |
| @@ -35,14 +35,13 @@ | @@ -35,14 +35,13 @@ | ||
| 35 | $("#main_modal").on("show", function(){ | 35 | $("#main_modal").on("show", function(){ |
| 36 | $("#div_container").remove(); | 36 | $("#div_container").remove(); |
| 37 | 37 | ||
| 38 | - var obj = $("<div/>"); | ||
| 39 | - $(obj).attr("id", "div_container"); | 38 | + var div_container = $("<div/>"); |
| 39 | + $(div_container).attr("id", "div_container"); | ||
| 40 | + $("#player").append(div_container); | ||
| 40 | 41 | ||
| 41 | var player = $("<div/>"); | 42 | var player = $("<div/>"); |
| 42 | - $(obj).append(player); | ||
| 43 | - $(obj).attr("id", "player_id"); | ||
| 44 | - | ||
| 45 | - $("#player").append(obj); | 43 | + $(player).attr("id", "player_id"); |
| 44 | + $(div_container).append(player); | ||
| 46 | 45 | ||
| 47 | var conf = { | 46 | var conf = { |
| 48 | file: _url, | 47 | file: _url, |
| @@ -21,14 +21,13 @@ | @@ -21,14 +21,13 @@ | ||
| 21 | function osmf_play(url) { | 21 | function osmf_play(url) { |
| 22 | $("#div_container").remove(); | 22 | $("#div_container").remove(); |
| 23 | 23 | ||
| 24 | - var obj = $("<div/>"); | ||
| 25 | - $(obj).attr("id", "div_container"); | 24 | + var div_container = $("<div/>"); |
| 25 | + $(div_container).attr("id", "div_container"); | ||
| 26 | + $("#player").append(div_container); | ||
| 26 | 27 | ||
| 27 | var player = $("<div/>"); | 28 | var player = $("<div/>"); |
| 28 | - $(obj).append(player); | ||
| 29 | - $(obj).attr("id", "player_id"); | ||
| 30 | - | ||
| 31 | - $("#player").append(obj); | 29 | + $(player).attr("id", "player_id"); |
| 30 | + $(div_container).append(player); | ||
| 32 | 31 | ||
| 33 | var flashvars = {}; | 32 | var flashvars = {}; |
| 34 | flashvars.src = url; | 33 | flashvars.src = url; |
| @@ -80,28 +80,26 @@ | @@ -80,28 +80,26 @@ | ||
| 80 | 80 | ||
| 81 | $("#div_container").remove(); | 81 | $("#div_container").remove(); |
| 82 | 82 | ||
| 83 | - var obj = $("<div/>"); | ||
| 84 | - $(obj).attr("id", "div_container"); | 83 | + var div_container = $("<div/>"); |
| 84 | + $(div_container).attr("id", "div_container"); | ||
| 85 | + $("#player").append(div_container); | ||
| 85 | 86 | ||
| 86 | var player = $("<div/>"); | 87 | var player = $("<div/>"); |
| 87 | - $(obj).append(player); | ||
| 88 | - $(obj).attr("id", "player_id"); | ||
| 89 | - | ||
| 90 | - $("#player").append(obj); | 88 | + $(player).attr("id", "player_id"); |
| 89 | + $(div_container).append(player); | ||
| 91 | 90 | ||
| 92 | var url = $("#txt_url").val(); | 91 | var url = $("#txt_url").val(); |
| 93 | 92 | ||
| 94 | - srs_player = new SrsPlayer("player_id", url, | ||
| 95 | - srs_get_player_width(), srs_get_player_height()); | 93 | + srs_player = new SrsPlayer("player_id", srs_get_player_width(), srs_get_player_height()); |
| 96 | srs_player.on_player_ready = function() { | 94 | srs_player.on_player_ready = function() { |
| 97 | select_buffer_time("#btn_bt_0_8", 0.8); | 95 | select_buffer_time("#btn_bt_0_8", 0.8); |
| 98 | - srs_player.play(); | ||
| 99 | - } | 96 | + srs_player.play(url); |
| 97 | + }; | ||
| 100 | srs_player.on_player_metadata = function(metadata) { | 98 | srs_player.on_player_metadata = function(metadata) { |
| 101 | $("#btn_dar_original").text("视频原始比例" + "(" + metadata.width + ":" + metadata.height + ")"); | 99 | $("#btn_dar_original").text("视频原始比例" + "(" + metadata.width + ":" + metadata.height + ")"); |
| 102 | select_dar("#btn_dar_original", 0, 0); | 100 | select_dar("#btn_dar_original", 0, 0); |
| 103 | select_fs_size("#btn_fs_size_screen_100", "screen", 100); | 101 | select_fs_size("#btn_fs_size_screen_100", "screen", 100); |
| 104 | - } | 102 | + }; |
| 105 | srs_player.on_player_timer = function(time, buffer_length) { | 103 | srs_player.on_player_timer = function(time, buffer_length) { |
| 106 | var buffer = buffer_length / srs_player.buffer_time * 100; | 104 | var buffer = buffer_length / srs_player.buffer_time * 100; |
| 107 | $("#pb_buffer").width(Number(buffer).toFixed(1) + "%"); | 105 | $("#pb_buffer").width(Number(buffer).toFixed(1) + "%"); |
| @@ -124,7 +122,7 @@ | @@ -124,7 +122,7 @@ | ||
| 124 | time_str += padding(parseInt(time), 2, '0'); | 122 | time_str += padding(parseInt(time), 2, '0'); |
| 125 | // show | 123 | // show |
| 126 | $("#txt_time").val(time_str); | 124 | $("#txt_time").val(time_str); |
| 127 | - } | 125 | + }; |
| 128 | srs_player.start(); | 126 | srs_player.start(); |
| 129 | }); | 127 | }); |
| 130 | 128 | ||
| @@ -218,6 +216,11 @@ | @@ -218,6 +216,11 @@ | ||
| 218 | select_buffer_time("#btn_bt_30", 30); | 216 | select_buffer_time("#btn_bt_30", 30); |
| 219 | }); | 217 | }); |
| 220 | } | 218 | } |
| 219 | + | ||
| 220 | + var query = parse_query_string(); | ||
| 221 | + if (query.autostart == "true") { | ||
| 222 | + $("#main_modal").modal({show:true, keyboard:false}); | ||
| 223 | + } | ||
| 221 | }); | 224 | }); |
| 222 | </script> | 225 | </script> |
| 223 | </head> | 226 | </head> |
| @@ -258,6 +258,10 @@ package | @@ -258,6 +258,10 @@ package | ||
| 258 | * function for js to call: to stop the stream. ignore if not play. | 258 | * function for js to call: to stop the stream. ignore if not play. |
| 259 | */ | 259 | */ |
| 260 | private function js_call_stop():void { | 260 | private function js_call_stop():void { |
| 261 | + if (this.media_video) { | ||
| 262 | + this.removeChild(this.media_video); | ||
| 263 | + this.media_video = null; | ||
| 264 | + } | ||
| 261 | if (this.media_stream) { | 265 | if (this.media_stream) { |
| 262 | this.media_stream.close(); | 266 | this.media_stream.close(); |
| 263 | this.media_stream = null; | 267 | this.media_stream = null; |
| @@ -266,10 +270,6 @@ package | @@ -266,10 +270,6 @@ package | ||
| 266 | this.media_conn.close(); | 270 | this.media_conn.close(); |
| 267 | this.media_conn = null; | 271 | this.media_conn = null; |
| 268 | } | 272 | } |
| 269 | - if (this.media_video) { | ||
| 270 | - this.removeChild(this.media_video); | ||
| 271 | - this.media_video = null; | ||
| 272 | - } | ||
| 273 | } | 273 | } |
| 274 | 274 | ||
| 275 | /** | 275 | /** |
| @@ -14,9 +14,174 @@ | @@ -14,9 +14,174 @@ | ||
| 14 | } | 14 | } |
| 15 | </style> | 15 | </style> |
| 16 | <script type="text/javascript"> | 16 | <script type="text/javascript"> |
| 17 | + var srs_publisher = null; | ||
| 18 | + var remote_player = null; | ||
| 19 | + | ||
| 17 | $(function(){ | 20 | $(function(){ |
| 18 | - update_nav(); | 21 | + // get the vhost and port to set the default url. |
| 22 | + // for example: http://192.168.1.213/players/jwplayer6.html?port=1935&vhost=demo | ||
| 23 | + // url set to: rtmp://demo:1935/live/livestream | ||
| 24 | + srs_init("#txt_url", null, null); | ||
| 25 | + | ||
| 26 | + $("#btn_video_settings").click(function(){ | ||
| 27 | + $("#video_modal").modal({show:true}); | ||
| 28 | + }); | ||
| 29 | + $("#btn_audio_settings").click(function(){ | ||
| 30 | + $("#audio_modal").modal({show:true}); | ||
| 31 | + }); | ||
| 32 | + | ||
| 33 | + $("#btn_publish").click(on_user_publish); | ||
| 34 | + | ||
| 35 | + update_play_url(); | ||
| 36 | + | ||
| 37 | + // start the publisher. | ||
| 38 | + srs_publisher = new SrsPublisher("local_publisher", 430, 185); | ||
| 39 | + srs_publisher.on_publisher_ready = function(cameras, microphones) { | ||
| 40 | + $("#sl_cameras").empty(); | ||
| 41 | + for (var i = 0; i < cameras.length; i++) { | ||
| 42 | + $("#sl_cameras").append("<option value='" + i + "'>" + cameras[i] + "</option"); | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + $("#sl_microphones").empty(); | ||
| 46 | + for (var i = 0; i < microphones.length; i++) { | ||
| 47 | + $("#sl_microphones").append("<option value='" + i + "'>" + microphones[i] + "</option"); | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + $("#sl_vcodec").empty(); | ||
| 51 | + var vcodecs = ["h264", "vp6"]; | ||
| 52 | + for (var i = 0; i < vcodecs.length; i++) { | ||
| 53 | + $("#sl_vcodec").append("<option value='" + vcodecs[i] + "'>" + vcodecs[i] + "</option"); | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + $("#sl_profile").empty(); | ||
| 57 | + var profiles = ["baseline", "main"]; | ||
| 58 | + for (var i = 0; i < profiles.length; i++) { | ||
| 59 | + $("#sl_profile").append("<option value='" + profiles[i] + "'>" + profiles[i] + "</option"); | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + $("#sl_level").empty(); | ||
| 63 | + var levels = ["1", "1b", "1.1", "1.2", "1.3", | ||
| 64 | + "2", "2.1", "2.2", "3", "3.1", "3.2", "4", "4.1", "4.2", "5", "5.1"]; | ||
| 65 | + for (var i = 0; i < levels.length; i++) { | ||
| 66 | + $("#sl_level").append("<option value='" + levels[i] + "'>" + levels[i] + "</option"); | ||
| 67 | + } | ||
| 68 | + $("#sl_level option[value='4.1']").attr("selected", true); | ||
| 69 | + | ||
| 70 | + $("#sl_gop").empty(); | ||
| 71 | + var gops = ["0.3", "0.5", "1", "2", "3", "4", | ||
| 72 | + "5", "6", "7", "8", "9", "10", "15", "20"]; | ||
| 73 | + for (var i = 0; i < gops.length; i++) { | ||
| 74 | + $("#sl_gop").append("<option value='" + gops[i] + "'>" + gops[i] + "秒</option"); | ||
| 75 | + } | ||
| 76 | + $("#sl_gop option[value='5']").attr("selected", true); | ||
| 77 | + | ||
| 78 | + $("#sl_size").empty(); | ||
| 79 | + var sizes = ["176x144", "320x240", "352x240", | ||
| 80 | + "352x288", "460x240", "640x480", "720x480", "720x576", "800x600", | ||
| 81 | + "1024x768", "1280x720", "1360x768", "1920x1080"]; | ||
| 82 | + for (i = 0; i < sizes.length; i++) { | ||
| 83 | + $("#sl_size").append("<option value='" + sizes[i] + "'>" + sizes[i] + "</option"); | ||
| 84 | + } | ||
| 85 | + $("#sl_size option[value='460x240']").attr("selected", true); | ||
| 86 | + | ||
| 87 | + $("#sl_fps").empty(); | ||
| 88 | + var fpses = ["5", "10", "15", "20", "24", "25", "29.97", "30"]; | ||
| 89 | + for (i = 0; i < fpses.length; i++) { | ||
| 90 | + $("#sl_fps").append("<option value='" + fpses[i] + "'>" + Number(fpses[i]).toFixed(2) + " 帧/秒</option"); | ||
| 91 | + } | ||
| 92 | + $("#sl_fps option[value='15']").attr("selected", true); | ||
| 93 | + | ||
| 94 | + $("#sl_bitrate").empty(); | ||
| 95 | + var bitrates = ["50", "200", "350", "500", "650", "800", | ||
| 96 | + "950", "1000", "1200", "1500", "1800", "2000", "3000", "5000"]; | ||
| 97 | + for (i = 0; i < bitrates.length; i++) { | ||
| 98 | + $("#sl_bitrate").append("<option value='" + bitrates[i] + "'>" + bitrates[i] + " kbps</option"); | ||
| 99 | + } | ||
| 100 | + $("#sl_bitrate option[value='350']").attr("selected", true); | ||
| 101 | + }; | ||
| 102 | + srs_publisher.on_publisher_error = function(code) { | ||
| 103 | + if (code == srs_publisher.error_device_muted) { | ||
| 104 | + error(code, "摄像头和麦克风被禁用,请右键flash播放器启用。"); | ||
| 105 | + } else { | ||
| 106 | + error(code, "未知系统错误"); | ||
| 107 | + } | ||
| 108 | + }; | ||
| 109 | + srs_publisher.start(); | ||
| 110 | + | ||
| 111 | + // start the player. | ||
| 112 | + remote_player = new SrsPlayer("remote_player", 430, 185); | ||
| 113 | + remote_player.on_player_ready = function() { | ||
| 114 | + }; | ||
| 115 | + remote_player.start(); | ||
| 19 | }); | 116 | }); |
| 117 | + | ||
| 118 | + function update_play_url() { | ||
| 119 | + // @see: http://stackoverflow.com/questions/10469575/how-to-use-location-object-to-parse-url-without-redirecting-the-page-in-javascri | ||
| 120 | + var a = document.createElement("a"); | ||
| 121 | + a.href = $("#txt_url").val().replace("rtmp://", "http://"); | ||
| 122 | + | ||
| 123 | + var url = "http://" + window.location.host; | ||
| 124 | + url += window.location.pathname.substr(0, window.location.pathname.lastIndexOf("/")); | ||
| 125 | + url += "/srs_player.html?"; | ||
| 126 | + | ||
| 127 | + url += "vhost=" + a.hostname; | ||
| 128 | + url += "&port=" + a.port; | ||
| 129 | + url += "&app=" + a.pathname.substr(1, a.pathname.lastIndexOf("/") - 1); | ||
| 130 | + url += "&stream=" + a.pathname.substr(a.pathname.lastIndexOf("/") + 1); | ||
| 131 | + | ||
| 132 | + // autostart | ||
| 133 | + url += "&autostart=true"; | ||
| 134 | + | ||
| 135 | + $("#txt_play_url").text(url); | ||
| 136 | + $("#txt_play_url").attr("href", url); | ||
| 137 | + } | ||
| 138 | + function on_user_publish() { | ||
| 139 | + if ($("#btn_publish").text() == "停止发布") { | ||
| 140 | + srs_publisher.stop(); | ||
| 141 | + $("#btn_publish").text("发布视频"); | ||
| 142 | + return; | ||
| 143 | + } | ||
| 144 | + | ||
| 145 | + $("#btn_publish").text("停止发布"); | ||
| 146 | + | ||
| 147 | + update_play_url(); | ||
| 148 | + | ||
| 149 | + var url = $("#txt_url").val(); | ||
| 150 | + var vcodec = {}; | ||
| 151 | + var acodec = {}; | ||
| 152 | + | ||
| 153 | + acodec.device_code = $("#sl_microphones").val(); | ||
| 154 | + acodec.device_name = $("#sl_microphones").text(); | ||
| 155 | + | ||
| 156 | + vcodec.device_code = $("#sl_cameras").find("option:selected").val(); | ||
| 157 | + vcodec.device_name = $("#sl_cameras").find("option:selected").text(); | ||
| 158 | + | ||
| 159 | + vcodec.codec = $("#sl_vcodec").find("option:selected").val(); | ||
| 160 | + vcodec.profile = $("#sl_profile").find("option:selected").val(); | ||
| 161 | + vcodec.level = $("#sl_level").find("option:selected").val(); | ||
| 162 | + vcodec.fps = $("#sl_fps").find("option:selected").val(); | ||
| 163 | + vcodec.gop = $("#sl_gop").find("option:selected").val(); | ||
| 164 | + vcodec.size = $("#sl_size").find("option:selected").val(); | ||
| 165 | + vcodec.bitrate = $("#sl_bitrate").find("option:selected").val(); | ||
| 166 | + | ||
| 167 | + info("开始推流到服务器"); | ||
| 168 | + srs_publisher.publish(url, vcodec, acodec); | ||
| 169 | + | ||
| 170 | + // replay the url. | ||
| 171 | + remote_player.stop(); | ||
| 172 | + remote_player.play(url); | ||
| 173 | + } | ||
| 174 | + | ||
| 175 | + function info(desc) { | ||
| 176 | + $("#txt_log").removeClass("alert-error").addClass("alert-info"); | ||
| 177 | + $("#txt_log_title").text("Info:"); | ||
| 178 | + $("#txt_log_msg").text(desc); | ||
| 179 | + } | ||
| 180 | + function error(code, desc) { | ||
| 181 | + $("#txt_log").removeClass("alert-info").addClass("alert-error"); | ||
| 182 | + $("#txt_log_title").text("Error:"); | ||
| 183 | + $("#txt_log_msg").text("code: " + code + ", " + desc); | ||
| 184 | + } | ||
| 20 | </script> | 185 | </script> |
| 21 | </head> | 186 | </head> |
| 22 | <body> | 187 | <body> |
| @@ -38,6 +203,187 @@ | @@ -38,6 +203,187 @@ | ||
| 38 | </div> | 203 | </div> |
| 39 | </div> | 204 | </div> |
| 40 | <div class="container"> | 205 | <div class="container"> |
| 206 | + <div class="alert alert-info fade in" id="txt_log"> | ||
| 207 | + <button type="button" class="close" data-dismiss="alert">×</button> | ||
| 208 | + <strong><span id="txt_log_title">Usage:</span></strong> | ||
| 209 | + <span id="txt_log_msg">输入地址后点击发布按钮</span> | ||
| 210 | + </div> | ||
| 211 | + <div class="control-group"> | ||
| 212 | + <div class="form-inline"> | ||
| 213 | + <button class="btn" id="btn_video_settings">视频编码配置</button> | ||
| 214 | + <button class="btn" id="btn_audio_settings">音频编码配置</button> | ||
| 215 | + </div> | ||
| 216 | + </div> | ||
| 217 | + <div class="control-group"> | ||
| 218 | + <div class="form-inline"> | ||
| 219 | + 发布地址: | ||
| 220 | + <input type="text" id="txt_url" class="input-xxlarge" value=""></input> | ||
| 221 | + <button class="btn" id="btn_publish">发布视频</button> | ||
| 222 | + </div> | ||
| 223 | + </div> | ||
| 224 | + <div class="control-group"> | ||
| 225 | + <div class="form-inline"> | ||
| 226 | + 观看地址: | ||
| 227 | + <a id="txt_play_url" class="input-xxlarge" href="srs_player.html">srs_player.html</a> | ||
| 228 | + </div> | ||
| 229 | + </div> | ||
| 230 | + <div id="video_modal" class="modal hide fade"> | ||
| 231 | + <div class="modal-header"> | ||
| 232 | + <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> | ||
| 233 | + <h3>视频编码</h3> | ||
| 234 | + </div> | ||
| 235 | + <div class="modal-body"> | ||
| 236 | + <div class="form-horizontal"> | ||
| 237 | + <div class="control-group"> | ||
| 238 | + <label class="control-label" for="sl_cameras"> | ||
| 239 | + 摄像头 | ||
| 240 | + <a id="sl_cameras_tips" href="#" data-toggle="tooltip" data-placement="right" title=""> | ||
| 241 | + <img src="img/tooltip.png"/> | ||
| 242 | + </a> | ||
| 243 | + </label> | ||
| 244 | + <div class="controls"> | ||
| 245 | + <select class="span4" id="sl_cameras"></select> | ||
| 246 | + </div> | ||
| 247 | + </div> | ||
| 248 | + <div class="control-group"> | ||
| 249 | + <label class="control-label" for="sl_vcodec"> | ||
| 250 | + Codec | ||
| 251 | + <a id="sl_cameras_tips" href="#" data-toggle="tooltip" data-placement="right" title=""> | ||
| 252 | + <img src="img/tooltip.png"/> | ||
| 253 | + </a> | ||
| 254 | + </label> | ||
| 255 | + <div class="controls"> | ||
| 256 | + <select class="span2" id="sl_vcodec"></select> | ||
| 257 | + </div> | ||
| 258 | + </div> | ||
| 259 | + <div class="control-group"> | ||
| 260 | + <label class="control-label" for="sl_profile"> | ||
| 261 | + Profile | ||
| 262 | + <a id="sl_profile_tips" href="#" data-toggle="tooltip" data-placement="right" title=""> | ||
| 263 | + <img src="img/tooltip.png"/> | ||
| 264 | + </a> | ||
| 265 | + </label> | ||
| 266 | + <div class="controls"> | ||
| 267 | + <select class="span2" id="sl_profile"></select> | ||
| 268 | + </div> | ||
| 269 | + </div> | ||
| 270 | + <div class="control-group"> | ||
| 271 | + <label class="control-label" for="sl_level"> | ||
| 272 | + Level | ||
| 273 | + <a id="sl_level_tips" href="#" data-toggle="tooltip" data-placement="right" title=""> | ||
| 274 | + <img src="img/tooltip.png"/> | ||
| 275 | + </a> | ||
| 276 | + </label> | ||
| 277 | + <div class="controls"> | ||
| 278 | + <select class="span2" id="sl_level"></select> | ||
| 279 | + </div> | ||
| 280 | + </div> | ||
| 281 | + <div class="control-group"> | ||
| 282 | + <label class="control-label" for="sl_gop"> | ||
| 283 | + GOP | ||
| 284 | + <a id="sl_gop_tips" href="#" data-toggle="tooltip" data-placement="right" title=""> | ||
| 285 | + <img src="img/tooltip.png"/> | ||
| 286 | + </a> | ||
| 287 | + </label> | ||
| 288 | + <div class="controls"> | ||
| 289 | + <select class="span2" id="sl_gop"></select> | ||
| 290 | + </div> | ||
| 291 | + </div> | ||
| 292 | + <div class="control-group"> | ||
| 293 | + <label class="control-label" for="sl_size"> | ||
| 294 | + 尺寸 | ||
| 295 | + <a id="sl_size_tips" href="#" data-toggle="tooltip" data-placement="right" title=""> | ||
| 296 | + <img src="img/tooltip.png"/> | ||
| 297 | + </a> | ||
| 298 | + </label> | ||
| 299 | + <div class="controls"> | ||
| 300 | + <select class="span2" id="sl_size"></select> | ||
| 301 | + </div> | ||
| 302 | + </div> | ||
| 303 | + <div class="control-group"> | ||
| 304 | + <label class="control-label" for="sl_fps"> | ||
| 305 | + 帧率 | ||
| 306 | + <a id="sl_fps_tips" href="#" data-toggle="tooltip" data-placement="right" title=""> | ||
| 307 | + <img src="img/tooltip.png"/> | ||
| 308 | + </a> | ||
| 309 | + </label> | ||
| 310 | + <div class="controls"> | ||
| 311 | + <select class="span2" id="sl_fps"></select> | ||
| 312 | + </div> | ||
| 313 | + </div> | ||
| 314 | + <div class="control-group"> | ||
| 315 | + <label class="control-label" for="sl_bitrate"> | ||
| 316 | + 码率 | ||
| 317 | + <a id="sl_bitrate_tips" href="#" data-toggle="tooltip" data-placement="right" title=""> | ||
| 318 | + <img src="img/tooltip.png"/> | ||
| 319 | + </a> | ||
| 320 | + </label> | ||
| 321 | + <div class="controls"> | ||
| 322 | + <select class="span2" id="sl_bitrate"></select> | ||
| 323 | + </div> | ||
| 324 | + </div> | ||
| 325 | + </div> | ||
| 326 | + </div> | ||
| 327 | + <div class="modal-footer"> | ||
| 328 | + <button class="btn btn-primary" data-dismiss="modal" aria-hidden="true">设置</button> | ||
| 329 | + </div> | ||
| 330 | + </div> | ||
| 331 | + <div id="audio_modal" class="modal hide fade"> | ||
| 332 | + <div class="modal-header"> | ||
| 333 | + <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> | ||
| 334 | + <h3>音频编码</h3> | ||
| 335 | + </div> | ||
| 336 | + <div class="modal-body"> | ||
| 337 | + <div class="form-horizontal"> | ||
| 338 | + <div class="control-group"> | ||
| 339 | + <label class="control-label" for="sl_microphones"> | ||
| 340 | + 麦克风 | ||
| 341 | + <a id="worker_id_tips" href="#" data-toggle="tooltip" data-placement="right" title=""> | ||
| 342 | + <img src="img/tooltip.png"/> | ||
| 343 | + </a> | ||
| 344 | + </label> | ||
| 345 | + <div class="controls"> | ||
| 346 | + <select class="span4" id="sl_microphones"></select> | ||
| 347 | + </div> | ||
| 348 | + </div> | ||
| 349 | + </div> | ||
| 350 | + </div> | ||
| 351 | + <div class="modal-footer"> | ||
| 352 | + <button class="btn btn-primary" data-dismiss="modal" aria-hidden="true">设置</button> | ||
| 353 | + </div> | ||
| 354 | + </div> | ||
| 355 | + <div class="container"> | ||
| 356 | + <div class="row-fluid"> | ||
| 357 | + <div class="span6"> | ||
| 358 | + <div class="accordion-group"> | ||
| 359 | + <div class="accordion-heading"> | ||
| 360 | + <span class="accordion-toggle" data-toggle="collapse" href="#collapseOne"> | ||
| 361 | + <strong>本地摄像头</strong> | ||
| 362 | + </span> | ||
| 363 | + </div> | ||
| 364 | + <div id="collapseOne" class="accordion-body collapse in"> | ||
| 365 | + <div class="accordion-inner"> | ||
| 366 | + <div id="local_publisher"></div> | ||
| 367 | + </div> | ||
| 368 | + </div> | ||
| 369 | + </div> | ||
| 370 | + </div> | ||
| 371 | + <div class="span6"> | ||
| 372 | + <div class="accordion-group"> | ||
| 373 | + <div class="accordion-heading"> | ||
| 374 | + <span class="accordion-toggle" data-toggle="collapse" href="#collapseTwo"> | ||
| 375 | + <strong>远程服务器</strong> | ||
| 376 | + </span> | ||
| 377 | + </div> | ||
| 378 | + <div id="collapseTwo" class="accordion-body collapse in"> | ||
| 379 | + <div class="accordion-inner"> | ||
| 380 | + <div id="remote_player"></div> | ||
| 381 | + </div> | ||
| 382 | + </div> | ||
| 383 | + </div> | ||
| 384 | + </div> | ||
| 385 | + </div> | ||
| 386 | + </div> | ||
| 41 | <hr> | 387 | <hr> |
| 42 | <footer> | 388 | <footer> |
| 43 | <p><a href="https://github.com/winlinvip/simple-rtmp-server">SRS Team © 2013</a></p> | 389 | <p><a href="https://github.com/winlinvip/simple-rtmp-server">SRS Team © 2013</a></p> |
| 1 | package | 1 | package |
| 2 | { | 2 | { |
| 3 | import flash.display.Sprite; | 3 | import flash.display.Sprite; |
| 4 | + import flash.display.StageAlign; | ||
| 5 | + import flash.display.StageScaleMode; | ||
| 6 | + import flash.events.Event; | ||
| 7 | + import flash.events.NetStatusEvent; | ||
| 8 | + import flash.external.ExternalInterface; | ||
| 9 | + import flash.media.Camera; | ||
| 10 | + import flash.media.H264Profile; | ||
| 11 | + import flash.media.H264VideoStreamSettings; | ||
| 12 | + import flash.media.Microphone; | ||
| 13 | + import flash.media.Video; | ||
| 14 | + import flash.net.NetConnection; | ||
| 15 | + import flash.net.NetStream; | ||
| 16 | + import flash.ui.ContextMenu; | ||
| 17 | + import flash.utils.setTimeout; | ||
| 4 | 18 | ||
| 5 | public class srs_publisher extends Sprite | 19 | public class srs_publisher extends Sprite |
| 6 | { | 20 | { |
| 21 | + // user set id. | ||
| 22 | + private var js_id:String = null; | ||
| 23 | + // user set callback | ||
| 24 | + private var js_on_publisher_ready:String = null; | ||
| 25 | + private var js_on_publisher_error:String = null; | ||
| 26 | + | ||
| 27 | + // publish param url. | ||
| 28 | + private var user_url:String = null; | ||
| 29 | + // play param, user set width and height | ||
| 30 | + private var user_w:int = 0; | ||
| 31 | + private var user_h:int = 0; | ||
| 32 | + private var user_vcodec:Object = {}; | ||
| 33 | + private var user_acodec:Object = {}; | ||
| 34 | + | ||
| 35 | + // media specified. | ||
| 36 | + private var media_conn:NetConnection = null; | ||
| 37 | + private var media_stream:NetStream = null; | ||
| 38 | + private var media_video:Video = null; | ||
| 39 | + private var media_camera:Camera = null; | ||
| 40 | + private var media_microphone:Microphone = null; | ||
| 41 | + | ||
| 42 | + // error code. | ||
| 43 | + private const error_device_muted:int = 100; | ||
| 44 | + | ||
| 7 | public function srs_publisher() | 45 | public function srs_publisher() |
| 8 | { | 46 | { |
| 47 | + if (!this.stage) { | ||
| 48 | + this.addEventListener(Event.ADDED_TO_STAGE, this.system_on_add_to_stage); | ||
| 49 | + } else { | ||
| 50 | + this.system_on_add_to_stage(null); | ||
| 51 | + } | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + /** | ||
| 55 | + * system event callback, when this control added to stage. | ||
| 56 | + * the main function. | ||
| 57 | + */ | ||
| 58 | + private function system_on_add_to_stage(evt:Event):void { | ||
| 59 | + this.removeEventListener(Event.ADDED_TO_STAGE, this.system_on_add_to_stage); | ||
| 60 | + | ||
| 61 | + this.stage.align = StageAlign.TOP_LEFT; | ||
| 62 | + this.stage.scaleMode = StageScaleMode.NO_SCALE; | ||
| 63 | + | ||
| 64 | + this.contextMenu = new ContextMenu(); | ||
| 65 | + this.contextMenu.hideBuiltInItems(); | ||
| 66 | + | ||
| 67 | + var flashvars:Object = this.root.loaderInfo.parameters; | ||
| 68 | + | ||
| 69 | + if (!flashvars.hasOwnProperty("id")) { | ||
| 70 | + throw new Error("must specifies the id"); | ||
| 71 | + } | ||
| 72 | + | ||
| 73 | + this.js_id = flashvars.id; | ||
| 74 | + this.js_on_publisher_ready = flashvars.on_publisher_ready; | ||
| 75 | + this.js_on_publisher_error = flashvars.on_publisher_error; | ||
| 76 | + | ||
| 77 | + flash.utils.setTimeout(this.system_on_js_ready, 0); | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + /** | ||
| 81 | + * system callack event, when js ready, register callback for js. | ||
| 82 | + * the actual main function. | ||
| 83 | + */ | ||
| 84 | + private function system_on_js_ready():void { | ||
| 85 | + if (!flash.external.ExternalInterface.available) { | ||
| 86 | + trace("js not ready, try later."); | ||
| 87 | + flash.utils.setTimeout(this.system_on_js_ready, 100); | ||
| 88 | + return; | ||
| 89 | + } | ||
| 90 | + | ||
| 91 | + flash.external.ExternalInterface.addCallback("__publish", this.js_call_publish); | ||
| 92 | + flash.external.ExternalInterface.addCallback("__stop", this.js_call_stop); | ||
| 93 | + | ||
| 94 | + var cameras:Array = Camera.names; | ||
| 95 | + var microphones:Array = Microphone.names; | ||
| 96 | + trace("retrieve system cameras(" + cameras + ") and microphones(" + microphones + ")"); | ||
| 97 | + | ||
| 98 | + flash.external.ExternalInterface.call(this.js_on_publisher_ready, this.js_id, cameras, microphones); | ||
| 99 | + } | ||
| 100 | + | ||
| 101 | + /** | ||
| 102 | + * notify the js an error occur. | ||
| 103 | + */ | ||
| 104 | + private function system_error(code:int, desc:String):void { | ||
| 105 | + trace("system error, code=" + code + ", error=" + desc); | ||
| 106 | + flash.external.ExternalInterface.call(this.js_on_publisher_error, this.js_id, code); | ||
| 107 | + } | ||
| 108 | + | ||
| 109 | + /** | ||
| 110 | + * publish stream to server. | ||
| 111 | + * @param url a string indicates the rtmp url to publish. | ||
| 112 | + * @param _width, the player width. | ||
| 113 | + * @param _height, the player height. | ||
| 114 | + * @param vcodec an object contains the video codec info. | ||
| 115 | + * @param acodec an object contains the audio codec info. | ||
| 116 | + */ | ||
| 117 | + private function js_call_publish(url:String, _width:int, _height:int, vcodec:Object, acodec:Object):void { | ||
| 118 | + trace("start to publish to " + url + ", vcodec " + JSON.stringify(vcodec) + ", acodec " + JSON.stringify(acodec)); | ||
| 119 | + | ||
| 120 | + this.user_url = url; | ||
| 121 | + this.user_w = _width; | ||
| 122 | + this.user_h = _height; | ||
| 123 | + this.user_vcodec = vcodec; | ||
| 124 | + this.user_acodec = acodec; | ||
| 125 | + | ||
| 126 | + this.js_call_stop(); | ||
| 127 | + | ||
| 128 | + // microphone and camera | ||
| 129 | + var m:Microphone = Microphone.getMicrophone(acodec.device_code); | ||
| 130 | + if(m == null){ | ||
| 131 | + trace("failed to open microphone " + acodec.device_code + "(" + acodec.device_name + ")"); | ||
| 132 | + } | ||
| 133 | + if(m.muted){ | ||
| 134 | + trace("Access Denied, microphone " + acodec.device_code + "(" + acodec.device_name + ") is muted"); | ||
| 135 | + m = null; | ||
| 136 | + } | ||
| 137 | + | ||
| 138 | + // Remark: the name is the index! | ||
| 139 | + var c:Camera = Camera.getCamera(vcodec.device_code); | ||
| 140 | + if(c == null){ | ||
| 141 | + trace("failed to open camera " + vcodec.device_code + "(" + vcodec.device_name + ")"); | ||
| 142 | + } | ||
| 143 | + if(c.muted){ | ||
| 144 | + trace("Access Denied, camera " + vcodec.device_code + "(" + vcodec.device_name + ") is muted"); | ||
| 145 | + c = null; | ||
| 146 | + } | ||
| 147 | + | ||
| 148 | + if (m == null && c == null) { | ||
| 149 | + system_error(error_device_muted, "failed to publish, for neither camera or microphone is ok."); | ||
| 150 | + return; | ||
| 151 | + } | ||
| 152 | + | ||
| 153 | + this.media_camera = c; | ||
| 154 | + this.media_microphone = m; | ||
| 155 | + | ||
| 156 | + this.media_conn = new NetConnection(); | ||
| 157 | + this.media_conn.client = {}; | ||
| 158 | + this.media_conn.client.onBWDone = function():void {}; | ||
| 159 | + this.media_conn.addEventListener(NetStatusEvent.NET_STATUS, function(evt:NetStatusEvent):void { | ||
| 160 | + trace ("NetConnection: code=" + evt.info.code); | ||
| 161 | + | ||
| 162 | + // TODO: FIXME: failed event. | ||
| 163 | + if (evt.info.code != "NetConnection.Connect.Success") { | ||
| 164 | + return; | ||
| 165 | + } | ||
| 166 | + | ||
| 167 | + media_stream = new NetStream(media_conn); | ||
| 168 | + media_stream.client = {}; | ||
| 169 | + media_stream.addEventListener(NetStatusEvent.NET_STATUS, function(evt:NetStatusEvent):void { | ||
| 170 | + trace ("NetStream: code=" + evt.info.code); | ||
| 171 | + | ||
| 172 | + // TODO: FIXME: failed event. | ||
| 173 | + }); | ||
| 174 | + | ||
| 175 | + __build_video_codec(media_stream, c, vcodec); | ||
| 176 | + __build_audio_codec(media_stream, m, acodec); | ||
| 177 | + | ||
| 178 | + if (media_microphone) { | ||
| 179 | + media_stream.attachAudio(m); | ||
| 180 | + } | ||
| 181 | + if (media_camera) { | ||
| 182 | + media_stream.attachCamera(c); | ||
| 183 | + } | ||
| 184 | + | ||
| 185 | + var streamName:String = url.substr(url.lastIndexOf("/")); | ||
| 186 | + media_stream.publish(streamName); | ||
| 187 | + | ||
| 188 | + media_video = new Video(); | ||
| 189 | + media_video.width = _width; | ||
| 190 | + media_video.height = _height; | ||
| 191 | + media_video.attachCamera(media_camera); | ||
| 192 | + media_video.smoothing = true; | ||
| 193 | + addChild(media_video); | ||
| 194 | + | ||
| 195 | + //__draw_black_background(_width, _height); | ||
| 196 | + | ||
| 197 | + // lowest layer, for mask to cover it. | ||
| 198 | + setChildIndex(media_video, 0); | ||
| 199 | + }); | ||
| 200 | + | ||
| 201 | + var tcUrl:String = this.user_url.substr(0, this.user_url.lastIndexOf("/")); | ||
| 202 | + this.media_conn.connect(tcUrl); | ||
| 203 | + } | ||
| 204 | + | ||
| 205 | + /** | ||
| 206 | + * function for js to call: to stop the stream. ignore if not publish. | ||
| 207 | + */ | ||
| 208 | + private function js_call_stop():void { | ||
| 209 | + if (this.media_video) { | ||
| 210 | + this.removeChild(this.media_video); | ||
| 211 | + this.media_video = null; | ||
| 212 | + } | ||
| 213 | + if (this.media_stream) { | ||
| 214 | + this.media_stream.close(); | ||
| 215 | + this.media_stream = null; | ||
| 216 | + } | ||
| 217 | + if (this.media_conn) { | ||
| 218 | + this.media_conn.close(); | ||
| 219 | + this.media_conn = null; | ||
| 220 | + } | ||
| 221 | + } | ||
| 222 | + | ||
| 223 | + private function __build_audio_codec(stream:NetStream, m:Microphone, acodec:Object):void { | ||
| 224 | + if (!m) { | ||
| 225 | + return; | ||
| 226 | + } | ||
| 227 | + | ||
| 228 | + // if no microphone, donot set the params. | ||
| 229 | + if(m == null){ | ||
| 230 | + return; | ||
| 231 | + } | ||
| 232 | + | ||
| 233 | + // use default values. | ||
| 234 | + var microEncodeQuality:int = 8; | ||
| 235 | + var microRate:int = 22; // 22 === 22050 Hz | ||
| 236 | + | ||
| 237 | + trace("[Publish] audio encoding parameters: " | ||
| 238 | + + "audio(microphone) encodeQuality=" + microEncodeQuality | ||
| 239 | + + ", rate=" + microRate + "(22050Hz)" | ||
| 240 | + ); | ||
| 241 | + | ||
| 242 | + // The encoded speech quality when using the Speex codec. Possible values are from 0 to 10. The default value is 6. Higher numbers | ||
| 243 | + // represent higher quality but require more bandwidth, as shown in the following table. The bit rate values that are listed represent | ||
| 244 | + // net bit rates and do not include packetization overhead. | ||
| 245 | + m.encodeQuality = microEncodeQuality; | ||
| 246 | + | ||
| 247 | + // The rate at which the microphone is capturing sound, in kHz. Acceptable values are 5, 8, 11, 22, and 44. The default value is 8 kHz | ||
| 248 | + // if your sound capture device supports this value. Otherwise, the default value is the next available capture level above 8 kHz that | ||
| 249 | + // your sound capture device supports, usually 11 kHz. | ||
| 250 | + m.rate = microRate; | ||
| 251 | + } | ||
| 252 | + private function __build_video_codec(stream:NetStream, c:Camera, vcodec:Object):void { | ||
| 253 | + if (!c) { | ||
| 254 | + return; | ||
| 255 | + } | ||
| 256 | + | ||
| 257 | + if(vcodec.codec == "vp6"){ | ||
| 258 | + trace("use VP6, donot use H.264 publish encoding."); | ||
| 259 | + return; | ||
| 260 | + } | ||
| 261 | + | ||
| 262 | + var x264profile:String = (vcodec.profile == "main") ? H264Profile.MAIN : H264Profile.BASELINE; | ||
| 263 | + var x264level:String = vcodec.level; | ||
| 264 | + var cameraFps:Number = Number(vcodec.fps); | ||
| 265 | + var x264KeyFrameInterval:int = int(vcodec.gop * cameraFps); | ||
| 266 | + var cameraWidth:int = String(vcodec.size).split("x")[0]; | ||
| 267 | + var cameraHeight:int = String(vcodec.size).split("x")[1]; | ||
| 268 | + var cameraBitrate:int = int(vcodec.bitrate); | ||
| 269 | + | ||
| 270 | + // use default values. | ||
| 271 | + var cameraQuality:int = 85; | ||
| 272 | + | ||
| 273 | + trace("[Publish] video h.264(x264) encoding parameters: " | ||
| 274 | + + "profile=" + x264profile | ||
| 275 | + + ", level=" + x264level | ||
| 276 | + + ", keyFrameInterval(gop)=" + x264KeyFrameInterval | ||
| 277 | + + "; video(camera) width=" + cameraWidth | ||
| 278 | + + ", height=" + cameraHeight | ||
| 279 | + + ", fps=" + cameraFps | ||
| 280 | + + ", bitrate=" + cameraBitrate | ||
| 281 | + + ", quality=" + cameraQuality | ||
| 282 | + ); | ||
| 283 | + | ||
| 284 | + var h264Settings:H264VideoStreamSettings = new H264VideoStreamSettings(); | ||
| 285 | + // we MUST set its values first, then set the NetStream.videoStreamSettings, or it will keep the origin values. | ||
| 286 | + h264Settings.setProfileLevel(x264profile, x264level); | ||
| 287 | + stream.videoStreamSettings = h264Settings; | ||
| 288 | + // the setKeyFrameInterval/setMode/setQuality use the camera settings. | ||
| 289 | + // http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/media/VideoStreamSettings.html | ||
| 290 | + // Note This feature will be supported in future releases of Flash Player and AIR, for now, Camera parameters are used. | ||
| 291 | + // | ||
| 292 | + //h264Settings.setKeyFrameInterval(4); | ||
| 293 | + //h264Settings.setMode(800, 600, 15); | ||
| 294 | + //h264Settings.setQuality(500, 0); | ||
| 295 | + | ||
| 296 | + // set the camera and microphone. | ||
| 297 | + | ||
| 298 | + // setKeyFrameInterval(keyFrameInterval:int):void | ||
| 299 | + // keyFrameInterval:int — A value that specifies which video frames are transmitted in full (as keyframes) instead of being | ||
| 300 | + // interpolated by the video compression algorithm. A value of 1 means that every frame is a keyframe, a value of 3 means | ||
| 301 | + // that every third frame is a keyframe, and so on. Acceptable values are 1 through 48. | ||
| 302 | + c.setKeyFrameInterval(x264KeyFrameInterval); | ||
| 303 | + | ||
| 304 | + // setMode(width:int, height:int, fps:Number, favorArea:Boolean = true):void | ||
| 305 | + // width:int — The requested capture width, in pixels. The default value is 160. | ||
| 306 | + // height:int — The requested capture height, in pixels. The default value is 120. | ||
| 307 | + // fps:Number — The requested rate at which the camera should capture data, in frames per second. The default value is 15. | ||
| 308 | + c.setMode(cameraWidth, cameraHeight, cameraFps); | ||
| 309 | + | ||
| 310 | + // setQuality(bandwidth:int, quality:int):void | ||
| 311 | + // bandwidth:int — Specifies the maximum amount of bandwidth that the current outgoing video feed can use, in bytes per second. | ||
| 312 | + // To specify that the video can use as much bandwidth as needed to maintain the value of quality, pass 0 for bandwidth. | ||
| 313 | + // The default value is 16384. | ||
| 314 | + // quality:int — An integer that specifies the required level of picture quality, as determined by the amount of compression | ||
| 315 | + // being applied to each video frame. Acceptable values range from 1 (lowest quality, maximum compression) to 100 | ||
| 316 | + // (highest quality, no compression). To specify that picture quality can vary as needed to avoid exceeding bandwidth, | ||
| 317 | + // pass 0 for quality. | ||
| 318 | + // winlin: | ||
| 319 | + // bandwidth is in bps not kbps. 500*1000 = 500kbps. | ||
| 320 | + // quality=1 is lowest quality, 100 is highest quality. | ||
| 321 | + c.setQuality(cameraBitrate * 1000, cameraQuality); | ||
| 9 | } | 322 | } |
| 10 | } | 323 | } |
| 11 | } | 324 | } |
-
请 注册 或 登录 后发表评论