add linux version of band check app; add web version of band check app
正在显示
35 个修改的文件
包含
1963 行增加
和
215 行删除
| @@ -6,8 +6,8 @@ srs is a simple, high-performance, running in single process, origin live server | @@ -6,8 +6,8 @@ srs is a simple, high-performance, running in single process, origin live server | ||
| 6 | srs supports vhost, rtmp, HLS, transcoding, forward, http hooks. <br/> | 6 | srs supports vhost, rtmp, HLS, transcoding, forward, http hooks. <br/> |
| 7 | blog: [http://blog.csdn.net/win_lin](http://blog.csdn.net/win_lin) <br/> | 7 | blog: [http://blog.csdn.net/win_lin](http://blog.csdn.net/win_lin) <br/> |
| 8 | see also: [https://github.com/winlinvip/simple-rtmp-server](https://github.com/winlinvip/simple-rtmp-server) <br/> | 8 | see also: [https://github.com/winlinvip/simple-rtmp-server](https://github.com/winlinvip/simple-rtmp-server) <br/> |
| 9 | -see also: [http://winlinvip.github.io/simple-rtmp-server](http://winlinvip.github.io/simple-rtmp-server) | ||
| 10 | -TencentQQ: http://url.cn/WAHICw (Group: 212189142) | 9 | +see also: [http://winlinvip.github.io/simple-rtmp-server](http://winlinvip.github.io/simple-rtmp-server) <br/> |
| 10 | +TencentQQ: [http://url.cn/WAHICw](http://url.cn/WAHICw) (Group: 212189142) | ||
| 11 | 11 | ||
| 12 | ### Contributors | 12 | ### Contributors |
| 13 | winlin([winterserver](#)): [http://blog.csdn.net/win_lin](http://blog.csdn.net/win_lin) <br/> | 13 | winlin([winterserver](#)): [http://blog.csdn.net/win_lin](http://blog.csdn.net/win_lin) <br/> |
| @@ -253,7 +253,8 @@ usr sys idl wai hiq siq| read writ| recv send| in out | int csw | @@ -253,7 +253,8 @@ usr sys idl wai hiq siq| read writ| recv send| in out | int csw | ||
| 253 | </pre> | 253 | </pre> |
| 254 | 254 | ||
| 255 | ### Releases | 255 | ### Releases |
| 256 | -* 2013-12-08, [release v0.8](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.8), support http hooks callback, update st-load. 19186 lines.<br/> | 256 | +* 2013-12-25, [release v0.9](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.9), support bandwidth test, add player/encoder/chat demos. 20926 lines.<br/> |
| 257 | +* 2013-12-08, [release v0.8](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.8), support http hooks callback, update [st_load](https://github.com/winlinvip/st-load). 19186 lines.<br/> | ||
| 257 | * 2013-12-03, [release v0.7](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.7), support live stream transcoding. 17605 lines.<br/> | 258 | * 2013-12-03, [release v0.7](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.7), support live stream transcoding. 17605 lines.<br/> |
| 258 | * 2013-11-29, [release v0.6](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.6), support forward stream to origin/edge. 16094 lines.<br/> | 259 | * 2013-11-29, [release v0.6](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.6), support forward stream to origin/edge. 16094 lines.<br/> |
| 259 | * 2013-11-26, [release v0.5](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.5), support HLS(m3u8), fragment and window. 14449 lines.<br/> | 260 | * 2013-11-26, [release v0.5](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.5), support HLS(m3u8), fragment and window. 14449 lines.<br/> |
| @@ -264,6 +265,7 @@ usr sys idl wai hiq siq| read writ| recv send| in out | int csw | @@ -264,6 +265,7 @@ usr sys idl wai hiq siq| read writ| recv send| in out | int csw | ||
| 264 | * 2013-10-17, created.<br/> | 265 | * 2013-10-17, created.<br/> |
| 265 | 266 | ||
| 266 | ### Compare | 267 | ### Compare |
| 268 | +* srs v0.9: 20926 lines. player/encoder/chat demos. bandwidth test for encoder/CDN.<br/> | ||
| 267 | * srs v0.8: 19186 lines. implements http hooks refer to [nginx-rtmp](https://github.com/arut/nginx-rtmp-module). <br/> | 269 | * srs v0.8: 19186 lines. implements http hooks refer to [nginx-rtmp](https://github.com/arut/nginx-rtmp-module). <br/> |
| 268 | * srs v0.7: 17605 lines. implements transcoding(FFMPEG) feature refer to [wowza](http://www.wowza.com). <br/> | 270 | * srs v0.7: 17605 lines. implements transcoding(FFMPEG) feature refer to [wowza](http://www.wowza.com). <br/> |
| 269 | * srs v0.6: 16094 lines. important feature forward for CDN. <br/> | 271 | * srs v0.6: 16094 lines. important feature forward for CDN. <br/> |
| @@ -276,6 +278,8 @@ usr sys idl wai hiq siq| read writ| recv send| in out | int csw | @@ -276,6 +278,8 @@ usr sys idl wai hiq siq| read writ| recv send| in out | int csw | ||
| 276 | * nginx v1.5.0: 139524 lines <br/> | 278 | * nginx v1.5.0: 139524 lines <br/> |
| 277 | 279 | ||
| 278 | ### History | 280 | ### History |
| 281 | +* v0.9, 2013-12-25, [v0.9](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.9) released. 20926 lines. | ||
| 282 | +* v0.9, 2013-12-25, fix the bitrate bug(in Bps), use enhanced microphone. | ||
| 279 | * v0.9, 2013-12-22, demo video meeting or chat(srs+cherrypy+jquery+bootstrap). | 283 | * v0.9, 2013-12-22, demo video meeting or chat(srs+cherrypy+jquery+bootstrap). |
| 280 | * v0.9, 2013-12-22, merge from wenjie, support banwidth test. | 284 | * v0.9, 2013-12-22, merge from wenjie, support banwidth test. |
| 281 | * v0.9, 2013-12-22, merge from wenjie: support set chunk size at vhost level | 285 | * v0.9, 2013-12-22, merge from wenjie: support set chunk size at vhost level |
| @@ -261,11 +261,8 @@ if [ $SRS_HLS = YES ]; then | @@ -261,11 +261,8 @@ if [ $SRS_HLS = YES ]; then | ||
| 261 | ln -sf `pwd`/research/players/crossdomain.xml ${SRS_OBJS}/nginx/html/crossdomain.xml | 261 | ln -sf `pwd`/research/players/crossdomain.xml ${SRS_OBJS}/nginx/html/crossdomain.xml |
| 262 | 262 | ||
| 263 | # override the default index. | 263 | # override the default index. |
| 264 | - cat <<END > ${SRS_OBJS}/nginx/html/index.html | ||
| 265 | - <script type="text/javascript"> | ||
| 266 | - window.location.href = "players/index.html"; | ||
| 267 | - </script> | ||
| 268 | -END | 264 | + rm -f ${SRS_OBJS}/nginx/html/index.html && |
| 265 | + ln -sf `pwd`/research/players/nginx_index.html ${SRS_OBJS}/nginx/html/index.html | ||
| 269 | fi | 266 | fi |
| 270 | 267 | ||
| 271 | if [ $SRS_HLS = YES ]; then | 268 | if [ $SRS_HLS = YES ]; then |
| @@ -300,6 +297,12 @@ else | @@ -300,6 +297,12 @@ else | ||
| 300 | echo "#undef SRS_HTTP" >> $SRS_AUTO_HEADERS_H | 297 | echo "#undef SRS_HTTP" >> $SRS_AUTO_HEADERS_H |
| 301 | fi | 298 | fi |
| 302 | 299 | ||
| 300 | +echo "link players to cherrypy static-dir" | ||
| 301 | +rm -f research/api-server/static-dir/players && | ||
| 302 | +ln -sf `pwd`/research/players research/api-server/static-dir/players && | ||
| 303 | +rm -f research/api-server/static-dir/crossdomain.xml && | ||
| 304 | +ln -sf `pwd`/research/players/crossdomain.xml research/api-server/static-dir/crossdomain.xml | ||
| 305 | + | ||
| 303 | ##################################################################################### | 306 | ##################################################################################### |
| 304 | # openssl, for rtmp complex handshake | 307 | # openssl, for rtmp complex handshake |
| 305 | ##################################################################################### | 308 | ##################################################################################### |
| @@ -343,7 +343,7 @@ class RESTChats(object): | @@ -343,7 +343,7 @@ class RESTChats(object): | ||
| 343 | self.__chat_lock = threading.Lock(); | 343 | self.__chat_lock = threading.Lock(); |
| 344 | 344 | ||
| 345 | # dead time in seconds, if exceed, remove the chat. | 345 | # dead time in seconds, if exceed, remove the chat. |
| 346 | - self.__dead_time = 30; | 346 | + self.__dead_time = 15; |
| 347 | 347 | ||
| 348 | def GET(self): | 348 | def GET(self): |
| 349 | enable_crossdomain() | 349 | enable_crossdomain() |
| @@ -474,7 +474,8 @@ if len(sys.argv) <= 1: | @@ -474,7 +474,8 @@ if len(sys.argv) <= 1: | ||
| 474 | 474 | ||
| 475 | # parse port from user options. | 475 | # parse port from user options. |
| 476 | port = int(sys.argv[1]) | 476 | port = int(sys.argv[1]) |
| 477 | -trace("api server listen at port: %s"%(port)) | 477 | +static_dir = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), "static-dir")) |
| 478 | +trace("api server listen at port: %s, static_dir: %s"%(port, static_dir)) | ||
| 478 | 479 | ||
| 479 | # cherrypy config. | 480 | # cherrypy config. |
| 480 | conf = { | 481 | conf = { |
| @@ -483,9 +484,12 @@ conf = { | @@ -483,9 +484,12 @@ conf = { | ||
| 483 | 'server.socket_host': '0.0.0.0', | 484 | 'server.socket_host': '0.0.0.0', |
| 484 | 'server.socket_port': port, | 485 | 'server.socket_port': port, |
| 485 | 'tools.encode.on': True, | 486 | 'tools.encode.on': True, |
| 487 | + 'tools.staticdir.on': True, | ||
| 486 | 'tools.encode.encoding': "utf-8" | 488 | 'tools.encode.encoding': "utf-8" |
| 487 | }, | 489 | }, |
| 488 | '/': { | 490 | '/': { |
| 491 | + 'tools.staticdir.dir': static_dir, | ||
| 492 | + 'tools.staticdir.index': "index.html", | ||
| 489 | # for cherrypy RESTful api support | 493 | # for cherrypy RESTful api support |
| 490 | 'request.dispatch': cherrypy.dispatch.MethodDispatcher() | 494 | 'request.dispatch': cherrypy.dispatch.MethodDispatcher() |
| 491 | } | 495 | } |
| 1 | +<!DOCTYPE html> | ||
| 2 | +<html> | ||
| 3 | +<head> | ||
| 4 | + <title>SRS</title> | ||
| 5 | + <meta charset="utf-8"> | ||
| 6 | + <link rel="stylesheet" type="text/css" href="players/css/bootstrap.min.css"/> | ||
| 7 | + <script type="text/javascript" src="players/js/jquery-1.10.2.min.js"></script> | ||
| 8 | + <script type="text/javascript" src="players/js/bootstrap.min.js"></script> | ||
| 9 | + <script type="text/javascript" src="players/js/swfobject.js"></script> | ||
| 10 | + <script type="text/javascript" src="players/js/srs.page.js"></script> | ||
| 11 | + <style> | ||
| 12 | + body{ | ||
| 13 | + padding-top: 55px; | ||
| 14 | + } | ||
| 15 | + </style> | ||
| 16 | + <script type="text/javascript"> | ||
| 17 | + $(function(){ | ||
| 18 | + update_nav(); | ||
| 19 | + | ||
| 20 | + // direct to the default vhost for players. | ||
| 21 | + window.location.href = "players/index.html" + window.location.search; | ||
| 22 | + }); | ||
| 23 | + </script> | ||
| 24 | +</head> | ||
| 25 | +<body> | ||
| 26 | +<div class="navbar navbar-fixed-top"> | ||
| 27 | + <div class="navbar-inner"> | ||
| 28 | + <div class="container"> | ||
| 29 | + <a class="brand" href="index.html">SRS</a> | ||
| 30 | + <div class="nav-collapse collapse"> | ||
| 31 | + <ul class="nav"> | ||
| 32 | + <li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li> | ||
| 33 | + <li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li> | ||
| 34 | + <li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li> | ||
| 35 | + <li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li> | ||
| 36 | + <li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li> | ||
| 37 | + <li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li> | ||
| 38 | + <li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li> | ||
| 39 | + </ul> | ||
| 40 | + </div> | ||
| 41 | + </div> | ||
| 42 | + </div> | ||
| 43 | +</div> | ||
| 44 | +<div class="container"> | ||
| 45 | + <hr> | ||
| 46 | + <footer> | ||
| 47 | + <p><a href="https://github.com/winlinvip/simple-rtmp-server">SRS Team © 2013</a></p> | ||
| 48 | + </footer> | ||
| 49 | +</div> | ||
| 50 | +</body> |
| @@ -7,7 +7,8 @@ | @@ -7,7 +7,8 @@ | ||
| 7 | <script type="text/javascript" src="js/jquery-1.10.2.min.js"></script> | 7 | <script type="text/javascript" src="js/jquery-1.10.2.min.js"></script> |
| 8 | <script type="text/javascript" src="js/bootstrap.min.js"></script> | 8 | <script type="text/javascript" src="js/bootstrap.min.js"></script> |
| 9 | <script type="text/javascript" src="js/swfobject.js"></script> | 9 | <script type="text/javascript" src="js/swfobject.js"></script> |
| 10 | - <script type="text/javascript" src="js/srs.js"></script> | 10 | + <script type="text/javascript" src="js/srs.page.js"></script> |
| 11 | + <script type="text/javascript" src="js/srs.utility.js"></script> | ||
| 11 | <style> | 12 | <style> |
| 12 | body{ | 13 | body{ |
| 13 | padding-top: 55px; | 14 | padding-top: 55px; |
| @@ -17,8 +18,19 @@ | @@ -17,8 +18,19 @@ | ||
| 17 | $(function(){ | 18 | $(function(){ |
| 18 | update_nav(); | 19 | update_nav(); |
| 19 | 20 | ||
| 20 | - // direct to the default vhost for players. | ||
| 21 | - window.location.href = "srs_player.html?vhost=" + srs_get_player_vhost(); | 21 | + var query = parse_query_string(); |
| 22 | + var url = "srs_chat.html?vhost=" + srs_get_player_vhost(); | ||
| 23 | + | ||
| 24 | + for (var key in query.user_query) { | ||
| 25 | + if (key == "vhost") { | ||
| 26 | + continue; | ||
| 27 | + } | ||
| 28 | + url += "&" + key + "=" + query[key]; | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + setTimeout(function(){ | ||
| 32 | + window.location.href = url; | ||
| 33 | + }, 100); | ||
| 22 | }); | 34 | }); |
| 23 | </script> | 35 | </script> |
| 24 | </head> | 36 | </head> |
| @@ -26,7 +38,7 @@ | @@ -26,7 +38,7 @@ | ||
| 26 | <div class="navbar navbar-fixed-top"> | 38 | <div class="navbar navbar-fixed-top"> |
| 27 | <div class="navbar-inner"> | 39 | <div class="navbar-inner"> |
| 28 | <div class="container"> | 40 | <div class="container"> |
| 29 | - <a class="brand" href="index.html">SRS</a> | 41 | + <a id="srs_index" class="brand" href="#">SRS</a> |
| 30 | <div class="nav-collapse collapse"> | 42 | <div class="nav-collapse collapse"> |
| 31 | <ul class="nav"> | 43 | <ul class="nav"> |
| 32 | <li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li> | 44 | <li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li> |
trunk/research/players/js/AdobeFlashPlayerInstall.swf
100755 → 100644
不能预览此文件类型
trunk/research/players/js/bootstrap.min.js
100755 → 100644
trunk/research/players/js/jquery-1.10.2.min.js
100755 → 100644
trunk/research/players/js/jquery-1.10.2.min.map
100755 → 100644
trunk/research/players/js/json2.js
0 → 100755
| 1 | +/* | ||
| 2 | + json2.js | ||
| 3 | + 2013-05-26 | ||
| 4 | + | ||
| 5 | + Public Domain. | ||
| 6 | + | ||
| 7 | + NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. | ||
| 8 | + | ||
| 9 | + See http://www.JSON.org/js.html | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + This code should be minified before deployment. | ||
| 13 | + See http://javascript.crockford.com/jsmin.html | ||
| 14 | + | ||
| 15 | + USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO | ||
| 16 | + NOT CONTROL. | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + This file creates a global JSON object containing two methods: stringify | ||
| 20 | + and parse. | ||
| 21 | + | ||
| 22 | + JSON.stringify(value, replacer, space) | ||
| 23 | + value any JavaScript value, usually an object or array. | ||
| 24 | + | ||
| 25 | + replacer an optional parameter that determines how object | ||
| 26 | + values are stringified for objects. It can be a | ||
| 27 | + function or an array of strings. | ||
| 28 | + | ||
| 29 | + space an optional parameter that specifies the indentation | ||
| 30 | + of nested structures. If it is omitted, the text will | ||
| 31 | + be packed without extra whitespace. If it is a number, | ||
| 32 | + it will specify the number of spaces to indent at each | ||
| 33 | + level. If it is a string (such as '\t' or ' '), | ||
| 34 | + it contains the characters used to indent at each level. | ||
| 35 | + | ||
| 36 | + This method produces a JSON text from a JavaScript value. | ||
| 37 | + | ||
| 38 | + When an object value is found, if the object contains a toJSON | ||
| 39 | + method, its toJSON method will be called and the result will be | ||
| 40 | + stringified. A toJSON method does not serialize: it returns the | ||
| 41 | + value represented by the name/value pair that should be serialized, | ||
| 42 | + or undefined if nothing should be serialized. The toJSON method | ||
| 43 | + will be passed the key associated with the value, and this will be | ||
| 44 | + bound to the value | ||
| 45 | + | ||
| 46 | + For example, this would serialize Dates as ISO strings. | ||
| 47 | + | ||
| 48 | + Date.prototype.toJSON = function (key) { | ||
| 49 | + function f(n) { | ||
| 50 | + // Format integers to have at least two digits. | ||
| 51 | + return n < 10 ? '0' + n : n; | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + return this.getUTCFullYear() + '-' + | ||
| 55 | + f(this.getUTCMonth() + 1) + '-' + | ||
| 56 | + f(this.getUTCDate()) + 'T' + | ||
| 57 | + f(this.getUTCHours()) + ':' + | ||
| 58 | + f(this.getUTCMinutes()) + ':' + | ||
| 59 | + f(this.getUTCSeconds()) + 'Z'; | ||
| 60 | + }; | ||
| 61 | + | ||
| 62 | + You can provide an optional replacer method. It will be passed the | ||
| 63 | + key and value of each member, with this bound to the containing | ||
| 64 | + object. The value that is returned from your method will be | ||
| 65 | + serialized. If your method returns undefined, then the member will | ||
| 66 | + be excluded from the serialization. | ||
| 67 | + | ||
| 68 | + If the replacer parameter is an array of strings, then it will be | ||
| 69 | + used to select the members to be serialized. It filters the results | ||
| 70 | + such that only members with keys listed in the replacer array are | ||
| 71 | + stringified. | ||
| 72 | + | ||
| 73 | + Values that do not have JSON representations, such as undefined or | ||
| 74 | + functions, will not be serialized. Such values in objects will be | ||
| 75 | + dropped; in arrays they will be replaced with null. You can use | ||
| 76 | + a replacer function to replace those with JSON values. | ||
| 77 | + JSON.stringify(undefined) returns undefined. | ||
| 78 | + | ||
| 79 | + The optional space parameter produces a stringification of the | ||
| 80 | + value that is filled with line breaks and indentation to make it | ||
| 81 | + easier to read. | ||
| 82 | + | ||
| 83 | + If the space parameter is a non-empty string, then that string will | ||
| 84 | + be used for indentation. If the space parameter is a number, then | ||
| 85 | + the indentation will be that many spaces. | ||
| 86 | + | ||
| 87 | + Example: | ||
| 88 | + | ||
| 89 | + text = JSON.stringify(['e', {pluribus: 'unum'}]); | ||
| 90 | + // text is '["e",{"pluribus":"unum"}]' | ||
| 91 | + | ||
| 92 | + | ||
| 93 | + text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); | ||
| 94 | + // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' | ||
| 95 | + | ||
| 96 | + text = JSON.stringify([new Date()], function (key, value) { | ||
| 97 | + return this[key] instanceof Date ? | ||
| 98 | + 'Date(' + this[key] + ')' : value; | ||
| 99 | + }); | ||
| 100 | + // text is '["Date(---current time---)"]' | ||
| 101 | + | ||
| 102 | + | ||
| 103 | + JSON.parse(text, reviver) | ||
| 104 | + This method parses a JSON text to produce an object or array. | ||
| 105 | + It can throw a SyntaxError exception. | ||
| 106 | + | ||
| 107 | + The optional reviver parameter is a function that can filter and | ||
| 108 | + transform the results. It receives each of the keys and values, | ||
| 109 | + and its return value is used instead of the original value. | ||
| 110 | + If it returns what it received, then the structure is not modified. | ||
| 111 | + If it returns undefined then the member is deleted. | ||
| 112 | + | ||
| 113 | + Example: | ||
| 114 | + | ||
| 115 | + // Parse the text. Values that look like ISO date strings will | ||
| 116 | + // be converted to Date objects. | ||
| 117 | + | ||
| 118 | + myData = JSON.parse(text, function (key, value) { | ||
| 119 | + var a; | ||
| 120 | + if (typeof value === 'string') { | ||
| 121 | + a = | ||
| 122 | +/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); | ||
| 123 | + if (a) { | ||
| 124 | + return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], | ||
| 125 | + +a[5], +a[6])); | ||
| 126 | + } | ||
| 127 | + } | ||
| 128 | + return value; | ||
| 129 | + }); | ||
| 130 | + | ||
| 131 | + myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { | ||
| 132 | + var d; | ||
| 133 | + if (typeof value === 'string' && | ||
| 134 | + value.slice(0, 5) === 'Date(' && | ||
| 135 | + value.slice(-1) === ')') { | ||
| 136 | + d = new Date(value.slice(5, -1)); | ||
| 137 | + if (d) { | ||
| 138 | + return d; | ||
| 139 | + } | ||
| 140 | + } | ||
| 141 | + return value; | ||
| 142 | + }); | ||
| 143 | + | ||
| 144 | + | ||
| 145 | + This is a reference implementation. You are free to copy, modify, or | ||
| 146 | + redistribute. | ||
| 147 | +*/ | ||
| 148 | + | ||
| 149 | +/*jslint evil: true, regexp: true */ | ||
| 150 | + | ||
| 151 | +/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, | ||
| 152 | + call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, | ||
| 153 | + getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, | ||
| 154 | + lastIndex, length, parse, prototype, push, replace, slice, stringify, | ||
| 155 | + test, toJSON, toString, valueOf | ||
| 156 | +*/ | ||
| 157 | + | ||
| 158 | + | ||
| 159 | +// Create a JSON object only if one does not already exist. We create the | ||
| 160 | +// methods in a closure to avoid creating global variables. | ||
| 161 | + | ||
| 162 | +if (typeof JSON !== 'object') { | ||
| 163 | + JSON = {}; | ||
| 164 | +} | ||
| 165 | + | ||
| 166 | +(function () { | ||
| 167 | + 'use strict'; | ||
| 168 | + | ||
| 169 | + function f(n) { | ||
| 170 | + // Format integers to have at least two digits. | ||
| 171 | + return n < 10 ? '0' + n : n; | ||
| 172 | + } | ||
| 173 | + | ||
| 174 | + if (typeof Date.prototype.toJSON !== 'function') { | ||
| 175 | + | ||
| 176 | + Date.prototype.toJSON = function () { | ||
| 177 | + | ||
| 178 | + return isFinite(this.valueOf()) | ||
| 179 | + ? this.getUTCFullYear() + '-' + | ||
| 180 | + f(this.getUTCMonth() + 1) + '-' + | ||
| 181 | + f(this.getUTCDate()) + 'T' + | ||
| 182 | + f(this.getUTCHours()) + ':' + | ||
| 183 | + f(this.getUTCMinutes()) + ':' + | ||
| 184 | + f(this.getUTCSeconds()) + 'Z' | ||
| 185 | + : null; | ||
| 186 | + }; | ||
| 187 | + | ||
| 188 | + String.prototype.toJSON = | ||
| 189 | + Number.prototype.toJSON = | ||
| 190 | + Boolean.prototype.toJSON = function () { | ||
| 191 | + return this.valueOf(); | ||
| 192 | + }; | ||
| 193 | + } | ||
| 194 | + | ||
| 195 | + var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, | ||
| 196 | + escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, | ||
| 197 | + gap, | ||
| 198 | + indent, | ||
| 199 | + meta = { // table of character substitutions | ||
| 200 | + '\b': '\\b', | ||
| 201 | + '\t': '\\t', | ||
| 202 | + '\n': '\\n', | ||
| 203 | + '\f': '\\f', | ||
| 204 | + '\r': '\\r', | ||
| 205 | + '"' : '\\"', | ||
| 206 | + '\\': '\\\\' | ||
| 207 | + }, | ||
| 208 | + rep; | ||
| 209 | + | ||
| 210 | + | ||
| 211 | + function quote(string) { | ||
| 212 | + | ||
| 213 | +// If the string contains no control characters, no quote characters, and no | ||
| 214 | +// backslash characters, then we can safely slap some quotes around it. | ||
| 215 | +// Otherwise we must also replace the offending characters with safe escape | ||
| 216 | +// sequences. | ||
| 217 | + | ||
| 218 | + escapable.lastIndex = 0; | ||
| 219 | + return escapable.test(string) ? '"' + string.replace(escapable, function (a) { | ||
| 220 | + var c = meta[a]; | ||
| 221 | + return typeof c === 'string' | ||
| 222 | + ? c | ||
| 223 | + : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); | ||
| 224 | + }) + '"' : '"' + string + '"'; | ||
| 225 | + } | ||
| 226 | + | ||
| 227 | + | ||
| 228 | + function str(key, holder) { | ||
| 229 | + | ||
| 230 | +// Produce a string from holder[key]. | ||
| 231 | + | ||
| 232 | + var i, // The loop counter. | ||
| 233 | + k, // The member key. | ||
| 234 | + v, // The member value. | ||
| 235 | + length, | ||
| 236 | + mind = gap, | ||
| 237 | + partial, | ||
| 238 | + value = holder[key]; | ||
| 239 | + | ||
| 240 | +// If the value has a toJSON method, call it to obtain a replacement value. | ||
| 241 | + | ||
| 242 | + if (value && typeof value === 'object' && | ||
| 243 | + typeof value.toJSON === 'function') { | ||
| 244 | + value = value.toJSON(key); | ||
| 245 | + } | ||
| 246 | + | ||
| 247 | +// If we were called with a replacer function, then call the replacer to | ||
| 248 | +// obtain a replacement value. | ||
| 249 | + | ||
| 250 | + if (typeof rep === 'function') { | ||
| 251 | + value = rep.call(holder, key, value); | ||
| 252 | + } | ||
| 253 | + | ||
| 254 | +// What happens next depends on the value's type. | ||
| 255 | + | ||
| 256 | + switch (typeof value) { | ||
| 257 | + case 'string': | ||
| 258 | + return quote(value); | ||
| 259 | + | ||
| 260 | + case 'number': | ||
| 261 | + | ||
| 262 | +// JSON numbers must be finite. Encode non-finite numbers as null. | ||
| 263 | + | ||
| 264 | + return isFinite(value) ? String(value) : 'null'; | ||
| 265 | + | ||
| 266 | + case 'boolean': | ||
| 267 | + case 'null': | ||
| 268 | + | ||
| 269 | +// If the value is a boolean or null, convert it to a string. Note: | ||
| 270 | +// typeof null does not produce 'null'. The case is included here in | ||
| 271 | +// the remote chance that this gets fixed someday. | ||
| 272 | + | ||
| 273 | + return String(value); | ||
| 274 | + | ||
| 275 | +// If the type is 'object', we might be dealing with an object or an array or | ||
| 276 | +// null. | ||
| 277 | + | ||
| 278 | + case 'object': | ||
| 279 | + | ||
| 280 | +// Due to a specification blunder in ECMAScript, typeof null is 'object', | ||
| 281 | +// so watch out for that case. | ||
| 282 | + | ||
| 283 | + if (!value) { | ||
| 284 | + return 'null'; | ||
| 285 | + } | ||
| 286 | + | ||
| 287 | +// Make an array to hold the partial results of stringifying this object value. | ||
| 288 | + | ||
| 289 | + gap += indent; | ||
| 290 | + partial = []; | ||
| 291 | + | ||
| 292 | +// Is the value an array? | ||
| 293 | + | ||
| 294 | + if (Object.prototype.toString.apply(value) === '[object Array]') { | ||
| 295 | + | ||
| 296 | +// The value is an array. Stringify every element. Use null as a placeholder | ||
| 297 | +// for non-JSON values. | ||
| 298 | + | ||
| 299 | + length = value.length; | ||
| 300 | + for (i = 0; i < length; i += 1) { | ||
| 301 | + partial[i] = str(i, value) || 'null'; | ||
| 302 | + } | ||
| 303 | + | ||
| 304 | +// Join all of the elements together, separated with commas, and wrap them in | ||
| 305 | +// brackets. | ||
| 306 | + | ||
| 307 | + v = partial.length === 0 | ||
| 308 | + ? '[]' | ||
| 309 | + : gap | ||
| 310 | + ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' | ||
| 311 | + : '[' + partial.join(',') + ']'; | ||
| 312 | + gap = mind; | ||
| 313 | + return v; | ||
| 314 | + } | ||
| 315 | + | ||
| 316 | +// If the replacer is an array, use it to select the members to be stringified. | ||
| 317 | + | ||
| 318 | + if (rep && typeof rep === 'object') { | ||
| 319 | + length = rep.length; | ||
| 320 | + for (i = 0; i < length; i += 1) { | ||
| 321 | + if (typeof rep[i] === 'string') { | ||
| 322 | + k = rep[i]; | ||
| 323 | + v = str(k, value); | ||
| 324 | + if (v) { | ||
| 325 | + partial.push(quote(k) + (gap ? ': ' : ':') + v); | ||
| 326 | + } | ||
| 327 | + } | ||
| 328 | + } | ||
| 329 | + } else { | ||
| 330 | + | ||
| 331 | +// Otherwise, iterate through all of the keys in the object. | ||
| 332 | + | ||
| 333 | + for (k in value) { | ||
| 334 | + if (Object.prototype.hasOwnProperty.call(value, k)) { | ||
| 335 | + v = str(k, value); | ||
| 336 | + if (v) { | ||
| 337 | + partial.push(quote(k) + (gap ? ': ' : ':') + v); | ||
| 338 | + } | ||
| 339 | + } | ||
| 340 | + } | ||
| 341 | + } | ||
| 342 | + | ||
| 343 | +// Join all of the member texts together, separated with commas, | ||
| 344 | +// and wrap them in braces. | ||
| 345 | + | ||
| 346 | + v = partial.length === 0 | ||
| 347 | + ? '{}' | ||
| 348 | + : gap | ||
| 349 | + ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' | ||
| 350 | + : '{' + partial.join(',') + '}'; | ||
| 351 | + gap = mind; | ||
| 352 | + return v; | ||
| 353 | + } | ||
| 354 | + } | ||
| 355 | + | ||
| 356 | +// If the JSON object does not yet have a stringify method, give it one. | ||
| 357 | + | ||
| 358 | + if (typeof JSON.stringify !== 'function') { | ||
| 359 | + JSON.stringify = function (value, replacer, space) { | ||
| 360 | + | ||
| 361 | +// The stringify method takes a value and an optional replacer, and an optional | ||
| 362 | +// space parameter, and returns a JSON text. The replacer can be a function | ||
| 363 | +// that can replace values, or an array of strings that will select the keys. | ||
| 364 | +// A default replacer method can be provided. Use of the space parameter can | ||
| 365 | +// produce text that is more easily readable. | ||
| 366 | + | ||
| 367 | + var i; | ||
| 368 | + gap = ''; | ||
| 369 | + indent = ''; | ||
| 370 | + | ||
| 371 | +// If the space parameter is a number, make an indent string containing that | ||
| 372 | +// many spaces. | ||
| 373 | + | ||
| 374 | + if (typeof space === 'number') { | ||
| 375 | + for (i = 0; i < space; i += 1) { | ||
| 376 | + indent += ' '; | ||
| 377 | + } | ||
| 378 | + | ||
| 379 | +// If the space parameter is a string, it will be used as the indent string. | ||
| 380 | + | ||
| 381 | + } else if (typeof space === 'string') { | ||
| 382 | + indent = space; | ||
| 383 | + } | ||
| 384 | + | ||
| 385 | +// If there is a replacer, it must be a function or an array. | ||
| 386 | +// Otherwise, throw an error. | ||
| 387 | + | ||
| 388 | + rep = replacer; | ||
| 389 | + if (replacer && typeof replacer !== 'function' && | ||
| 390 | + (typeof replacer !== 'object' || | ||
| 391 | + typeof replacer.length !== 'number')) { | ||
| 392 | + throw new Error('JSON.stringify'); | ||
| 393 | + } | ||
| 394 | + | ||
| 395 | +// Make a fake root object containing our value under the key of ''. | ||
| 396 | +// Return the result of stringifying the value. | ||
| 397 | + | ||
| 398 | + return str('', {'': value}); | ||
| 399 | + }; | ||
| 400 | + } | ||
| 401 | + | ||
| 402 | + | ||
| 403 | +// If the JSON object does not yet have a parse method, give it one. | ||
| 404 | + | ||
| 405 | + if (typeof JSON.parse !== 'function') { | ||
| 406 | + JSON.parse = function (text, reviver) { | ||
| 407 | + | ||
| 408 | +// The parse method takes a text and an optional reviver function, and returns | ||
| 409 | +// a JavaScript value if the text is a valid JSON text. | ||
| 410 | + | ||
| 411 | + var j; | ||
| 412 | + | ||
| 413 | + function walk(holder, key) { | ||
| 414 | + | ||
| 415 | +// The walk method is used to recursively walk the resulting structure so | ||
| 416 | +// that modifications can be made. | ||
| 417 | + | ||
| 418 | + var k, v, value = holder[key]; | ||
| 419 | + if (value && typeof value === 'object') { | ||
| 420 | + for (k in value) { | ||
| 421 | + if (Object.prototype.hasOwnProperty.call(value, k)) { | ||
| 422 | + v = walk(value, k); | ||
| 423 | + if (v !== undefined) { | ||
| 424 | + value[k] = v; | ||
| 425 | + } else { | ||
| 426 | + delete value[k]; | ||
| 427 | + } | ||
| 428 | + } | ||
| 429 | + } | ||
| 430 | + } | ||
| 431 | + return reviver.call(holder, key, value); | ||
| 432 | + } | ||
| 433 | + | ||
| 434 | + | ||
| 435 | +// Parsing happens in four stages. In the first stage, we replace certain | ||
| 436 | +// Unicode characters with escape sequences. JavaScript handles many characters | ||
| 437 | +// incorrectly, either silently deleting them, or treating them as line endings. | ||
| 438 | + | ||
| 439 | + text = String(text); | ||
| 440 | + cx.lastIndex = 0; | ||
| 441 | + if (cx.test(text)) { | ||
| 442 | + text = text.replace(cx, function (a) { | ||
| 443 | + return '\\u' + | ||
| 444 | + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); | ||
| 445 | + }); | ||
| 446 | + } | ||
| 447 | + | ||
| 448 | +// In the second stage, we run the text against regular expressions that look | ||
| 449 | +// for non-JSON patterns. We are especially concerned with '()' and 'new' | ||
| 450 | +// because they can cause invocation, and '=' because it can cause mutation. | ||
| 451 | +// But just to be safe, we want to reject all unexpected forms. | ||
| 452 | + | ||
| 453 | +// We split the second stage into 4 regexp operations in order to work around | ||
| 454 | +// crippling inefficiencies in IE's and Safari's regexp engines. First we | ||
| 455 | +// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we | ||
| 456 | +// replace all simple value tokens with ']' characters. Third, we delete all | ||
| 457 | +// open brackets that follow a colon or comma or that begin the text. Finally, | ||
| 458 | +// we look to see that the remaining characters are only whitespace or ']' or | ||
| 459 | +// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. | ||
| 460 | + | ||
| 461 | + if (/^[\],:{}\s]*$/ | ||
| 462 | + .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') | ||
| 463 | + .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') | ||
| 464 | + .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { | ||
| 465 | + | ||
| 466 | +// In the third stage we use the eval function to compile the text into a | ||
| 467 | +// JavaScript structure. The '{' operator is subject to a syntactic ambiguity | ||
| 468 | +// in JavaScript: it can begin a block or an object literal. We wrap the text | ||
| 469 | +// in parens to eliminate the ambiguity. | ||
| 470 | + | ||
| 471 | + j = eval('(' + text + ')'); | ||
| 472 | + | ||
| 473 | +// In the optional fourth stage, we recursively walk the new structure, passing | ||
| 474 | +// each name/value pair to a reviver function for possible transformation. | ||
| 475 | + | ||
| 476 | + return typeof reviver === 'function' | ||
| 477 | + ? walk({'': j}, '') | ||
| 478 | + : j; | ||
| 479 | + } | ||
| 480 | + | ||
| 481 | +// If the text is not JSON parseable, then a SyntaxError is thrown. | ||
| 482 | + | ||
| 483 | + throw new SyntaxError('JSON.parse'); | ||
| 484 | + }; | ||
| 485 | + } | ||
| 486 | +}()); |
trunk/research/players/js/srs.log.js
0 → 100755
| 1 | +/** | ||
| 2 | +* log specified, there must be a log element as: | ||
| 3 | + <!-- for the log --> | ||
| 4 | + <div class="alert alert-info fade in" id="txt_log"> | ||
| 5 | + <button type="button" class="close" data-dismiss="alert">×</button> | ||
| 6 | + <strong><span id="txt_log_title">标题:</span></strong> | ||
| 7 | + <span id="txt_log_msg">日志内容</span> | ||
| 8 | + </div> | ||
| 9 | +*/ | ||
| 10 | +var srs_log_disabled = false; | ||
| 11 | +function info(desc) { | ||
| 12 | + if (srs_log_disabled) { | ||
| 13 | + return; | ||
| 14 | + } | ||
| 15 | + | ||
| 16 | + $("#txt_log").addClass("alert-info").removeClass("alert-error").removeClass("alert-warn"); | ||
| 17 | + $("#txt_log_title").text("Info:"); | ||
| 18 | + $("#txt_log_msg").html(desc); | ||
| 19 | +} | ||
| 20 | +function warn(code, desc) { | ||
| 21 | + if (srs_log_disabled) { | ||
| 22 | + return; | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + $("#txt_log").removeClass("alert-info").removeClass("alert-error").addClass("alert-warn"); | ||
| 26 | + $("#txt_log_title").text("Warn:"); | ||
| 27 | + $("#txt_log_msg").html("code: " + code + ", " + desc); | ||
| 28 | +} | ||
| 29 | +function error(code, desc) { | ||
| 30 | + if (srs_log_disabled) { | ||
| 31 | + return; | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + $("#txt_log").removeClass("alert-info").addClass("alert-error").removeClass("alert-warn"); | ||
| 35 | + $("#txt_log_title").text("Error:"); | ||
| 36 | + $("#txt_log_msg").html("code: " + code + ", " + desc); | ||
| 37 | +} |
trunk/research/players/js/srs.page.js
0 → 100755
| 1 | +////////////////////////////////////////////////////////////////////////////////// | ||
| 2 | +////////////////////////////////////////////////////////////////////////////////// | ||
| 3 | +////////////////////////////////////////////////////////////////////////////////// | ||
| 4 | + | ||
| 5 | +/** | ||
| 6 | +* player specified size. | ||
| 7 | +*/ | ||
| 8 | +function srs_get_player_modal() { return 740; } | ||
| 9 | +function srs_get_player_width() { return srs_get_player_modal() - 30; } | ||
| 10 | +function srs_get_player_height() { return srs_get_player_width() * 9 / 19; } | ||
| 11 | + | ||
| 12 | +// to query the swf anti cache. | ||
| 13 | +function srs_get_version_code() { return "1.17"; } | ||
| 14 | +// get the default vhost for players. | ||
| 15 | +function srs_get_player_vhost() { return "players"; } | ||
| 16 | +// the api server port, for chat room. | ||
| 17 | +function srs_get_api_server_port() { return 8085; } | ||
| 18 | +// get the stream published to vhost, | ||
| 19 | +// generally we need to transcode the stream to support HLS and filters. | ||
| 20 | +// for example, src_vhost is "players", we transcode stream to vhost "players_pub". | ||
| 21 | +// if not equals to the player vhost, return the orignal vhost. | ||
| 22 | +function srs_get_player_publish_vhost(src_vhost) { return (src_vhost != srs_get_player_vhost())? src_vhost:(src_vhost + "_pub"); } | ||
| 23 | +// for chat, use rtmp only vhost, low latecy, without gop cache. | ||
| 24 | +function srs_get_player_chat_vhost(src_vhost) { return (src_vhost != srs_get_player_vhost())? src_vhost:(src_vhost + "_pub_rtmp"); } | ||
| 25 | + | ||
| 26 | +////////////////////////////////////////////////////////////////////////////////// | ||
| 27 | +////////////////////////////////////////////////////////////////////////////////// | ||
| 28 | +////////////////////////////////////////////////////////////////////////////////// | ||
| 29 | + | ||
| 30 | +/** | ||
| 31 | +* update the navigator, add same query string. | ||
| 32 | +*/ | ||
| 33 | +function update_nav() { | ||
| 34 | + $("#srs_index").attr("href", "index.html" + window.location.search); | ||
| 35 | + $("#nav_srs_player").attr("href", "srs_player.html" + window.location.search); | ||
| 36 | + $("#nav_srs_publisher").attr("href", "srs_publisher.html" + window.location.search); | ||
| 37 | + $("#nav_srs_chat").attr("href", "srs_chat.html" + window.location.search); | ||
| 38 | + $("#nav_srs_bwt").attr("href", "srs_bwt.html" + window.location.search); | ||
| 39 | + $("#nav_jwplayer6").attr("href", "jwplayer6.html" + window.location.search); | ||
| 40 | + $("#nav_osmf").attr("href", "osmf.html" + window.location.search); | ||
| 41 | + $("#nav_vlc").attr("href", "vlc.html" + window.location.search); | ||
| 42 | +} | ||
| 43 | + | ||
| 44 | +/** | ||
| 45 | +@param server the ip of server. default to window.location.hostname | ||
| 46 | +@param vhost the vhost of rtmp. default to window.location.hostname | ||
| 47 | +@param port the port of rtmp. default to 1935 | ||
| 48 | +@param app the app of rtmp. default to live. | ||
| 49 | +@param stream the stream of rtmp. default to livestream. | ||
| 50 | +*/ | ||
| 51 | +function build_default_rtmp_url() { | ||
| 52 | + var query = parse_query_string(); | ||
| 53 | + | ||
| 54 | + var server = (query.server == undefined)? window.location.hostname:query.server; | ||
| 55 | + var port = (query.port == undefined)? 1935:query.port; | ||
| 56 | + var vhost = (query.vhost == undefined)? window.location.hostname:query.vhost; | ||
| 57 | + var app = (query.app == undefined)? "live":query.app; | ||
| 58 | + var stream = (query.stream == undefined)? "livestream":query.stream; | ||
| 59 | + | ||
| 60 | + if (server == vhost || vhost == "") { | ||
| 61 | + return "rtmp://" + server + ":" + port + "/" + app + "/" + stream; | ||
| 62 | + } else { | ||
| 63 | + return "rtmp://" + server + ":" + port + "/" + app + "...vhost..." + vhost + "/" + stream; | ||
| 64 | + } | ||
| 65 | +} | ||
| 66 | +// for the chat to init the publish url. | ||
| 67 | +function build_default_publish_rtmp_url() { | ||
| 68 | + var query = parse_query_string(); | ||
| 69 | + | ||
| 70 | + var server = (query.server == undefined)? window.location.hostname:query.server; | ||
| 71 | + var port = (query.port == undefined)? 1935:query.port; | ||
| 72 | + var vhost = (query.vhost == undefined)? window.location.hostname:query.vhost; | ||
| 73 | + var app = (query.app == undefined)? "live":query.app; | ||
| 74 | + var stream = (query.stream == undefined)? "livestream":query.stream; | ||
| 75 | + | ||
| 76 | + if (server == vhost || vhost == "") { | ||
| 77 | + return "rtmp://" + server + ":" + port + "/" + app + "/" + stream; | ||
| 78 | + } else { | ||
| 79 | + vhost = srs_get_player_chat_vhost(vhost); | ||
| 80 | + return "rtmp://" + server + ":" + port + "/" + app + "...vhost..." + vhost + "/" + stream; | ||
| 81 | + } | ||
| 82 | +} | ||
| 83 | + | ||
| 84 | +/** | ||
| 85 | +@param server the ip of server. default to window.location.hostname | ||
| 86 | +@param vhost the vhost of hls. default to window.location.hostname | ||
| 87 | +@param hls_vhost the vhost of hls. override the server if specified. | ||
| 88 | +@param hls_port the port of hls. default to window.location.port | ||
| 89 | +@param app the app of hls. default to live. | ||
| 90 | +@param stream the stream of hls. default to livestream. | ||
| 91 | +*/ | ||
| 92 | +function build_default_hls_url() { | ||
| 93 | + var query = parse_query_string(); | ||
| 94 | + | ||
| 95 | + // for http, use hls_vhost to override server if specified. | ||
| 96 | + var server = window.location.hostname; | ||
| 97 | + if (query.server != undefined) { | ||
| 98 | + server = query.server; | ||
| 99 | + } else if (query.hls_vhost != undefined) { | ||
| 100 | + server = query.hls_vhost; | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + var port = (query.hls_port == undefined)? window.location.port:query.hls_port; | ||
| 104 | + var app = (query.app == undefined)? "live":query.app; | ||
| 105 | + var stream = (query.stream == undefined)? "livestream":query.stream; | ||
| 106 | + | ||
| 107 | + if (port == "" || port == null || port == undefined) { | ||
| 108 | + port = 80; | ||
| 109 | + } | ||
| 110 | + | ||
| 111 | + return "http://" + server + ":" + port + "/" + app + "/" + stream + ".m3u8"; | ||
| 112 | +} | ||
| 113 | + | ||
| 114 | +/** | ||
| 115 | +* initialize the page. | ||
| 116 | +* @param rtmp_url the div id contains the rtmp stream url to play | ||
| 117 | +* @param hls_url the div id contains the hls stream url to play | ||
| 118 | +* @param modal_player the div id contains the modal player | ||
| 119 | +*/ | ||
| 120 | +function srs_init(rtmp_url, hls_url, modal_player) { | ||
| 121 | + update_nav(); | ||
| 122 | + | ||
| 123 | + if (rtmp_url) { | ||
| 124 | + $(rtmp_url).val(build_default_rtmp_url()); | ||
| 125 | + } | ||
| 126 | + if (hls_url) { | ||
| 127 | + $(hls_url).val(build_default_hls_url()); | ||
| 128 | + } | ||
| 129 | + if (modal_player) { | ||
| 130 | + $(modal_player).width(srs_get_player_modal() + "px"); | ||
| 131 | + $(modal_player).css("margin-left", "-" + srs_get_player_modal() / 2 +"px"); | ||
| 132 | + } | ||
| 133 | +} | ||
| 134 | +// for the chat to init the publish url. | ||
| 135 | +function srs_init_publish(rtmp_url) { | ||
| 136 | + update_nav(); | ||
| 137 | + | ||
| 138 | + if (rtmp_url) { | ||
| 139 | + $(rtmp_url).val(build_default_publish_rtmp_url()); | ||
| 140 | + } | ||
| 141 | +} | ||
| 142 | + | ||
| 143 | +// check whether can republish | ||
| 144 | +function srs_can_republish() { | ||
| 145 | + var browser = get_browser_agents(); | ||
| 146 | + | ||
| 147 | + if (browser.Chrome || browser.Firefox) { | ||
| 148 | + return true; | ||
| 149 | + } | ||
| 150 | + | ||
| 151 | + if (browser.MSIE || browser.QQBrowser) { | ||
| 152 | + return false; | ||
| 153 | + } | ||
| 154 | + | ||
| 155 | + return false; | ||
| 156 | +} | ||
| 157 | + | ||
| 158 | +// without default values set. | ||
| 159 | +function srs_initialize_codec_page( | ||
| 160 | + cameras, microphones, | ||
| 161 | + sl_cameras, sl_microphones, sl_vcodec, sl_profile, sl_level, sl_gop, sl_size, sl_fps, sl_bitrate | ||
| 162 | +) { | ||
| 163 | + $(sl_cameras).empty(); | ||
| 164 | + for (var i = 0; i < cameras.length; i++) { | ||
| 165 | + $(sl_cameras).append("<option value='" + i + "'>" + cameras[i] + "</option"); | ||
| 166 | + } | ||
| 167 | + // optional: select the except matches | ||
| 168 | + matchs = ["virtual"]; | ||
| 169 | + for (var i = 0; i < cameras.length; i++) { | ||
| 170 | + for (var j = 0; j < matchs.length; j++) { | ||
| 171 | + if (cameras[i].toLowerCase().indexOf(matchs[j]) == -1) { | ||
| 172 | + $(sl_cameras + " option[value='" + i + "']").attr("selected", true); | ||
| 173 | + break; | ||
| 174 | + } | ||
| 175 | + } | ||
| 176 | + if (j < matchs.length) { | ||
| 177 | + break; | ||
| 178 | + } | ||
| 179 | + } | ||
| 180 | + // optional: select the first matched. | ||
| 181 | + matchs = ["truevision", "integrated"]; | ||
| 182 | + for (var i = 0; i < cameras.length; i++) { | ||
| 183 | + for (var j = 0; j < matchs.length; j++) { | ||
| 184 | + if (cameras[i].toLowerCase().indexOf(matchs[j]) >= 0) { | ||
| 185 | + $(sl_cameras + " option[value='" + i + "']").attr("selected", true); | ||
| 186 | + break; | ||
| 187 | + } | ||
| 188 | + } | ||
| 189 | + if (j < matchs.length) { | ||
| 190 | + break; | ||
| 191 | + } | ||
| 192 | + } | ||
| 193 | + | ||
| 194 | + $(sl_microphones).empty(); | ||
| 195 | + for (var i = 0; i < microphones.length; i++) { | ||
| 196 | + $(sl_microphones).append("<option value='" + i + "'>" + microphones[i] + "</option"); | ||
| 197 | + } | ||
| 198 | + // optional: select the except matches | ||
| 199 | + matchs = ["default"]; | ||
| 200 | + for (var i = 0; i < microphones.length; i++) { | ||
| 201 | + for (var j = 0; j < matchs.length; j++) { | ||
| 202 | + if (microphones[i].toLowerCase().indexOf(matchs[j]) == -1) { | ||
| 203 | + $(sl_microphones + " option[value='" + i + "']").attr("selected", true); | ||
| 204 | + break; | ||
| 205 | + } | ||
| 206 | + } | ||
| 207 | + if (j < matchs.length) { | ||
| 208 | + break; | ||
| 209 | + } | ||
| 210 | + } | ||
| 211 | + // optional: select the first matched. | ||
| 212 | + matchs = ["realtek", "内置式麦克风"]; | ||
| 213 | + for (var i = 0; i < microphones.length; i++) { | ||
| 214 | + for (var j = 0; j < matchs.length; j++) { | ||
| 215 | + if (microphones[i].toLowerCase().indexOf(matchs[j]) >= 0) { | ||
| 216 | + $(sl_microphones + " option[value='" + i + "']").attr("selected", true); | ||
| 217 | + break; | ||
| 218 | + } | ||
| 219 | + } | ||
| 220 | + if (j < matchs.length) { | ||
| 221 | + break; | ||
| 222 | + } | ||
| 223 | + } | ||
| 224 | + | ||
| 225 | + $(sl_vcodec).empty(); | ||
| 226 | + var vcodecs = ["h264", "vp6"]; | ||
| 227 | + vcodecs = ["h264"]; // h264 only. | ||
| 228 | + for (var i = 0; i < vcodecs.length; i++) { | ||
| 229 | + $(sl_vcodec).append("<option value='" + vcodecs[i] + "'>" + vcodecs[i] + "</option"); | ||
| 230 | + } | ||
| 231 | + | ||
| 232 | + $(sl_profile).empty(); | ||
| 233 | + var profiles = ["baseline", "main"]; | ||
| 234 | + for (var i = 0; i < profiles.length; i++) { | ||
| 235 | + $(sl_profile).append("<option value='" + profiles[i] + "'>" + profiles[i] + "</option"); | ||
| 236 | + } | ||
| 237 | + | ||
| 238 | + $(sl_level).empty(); | ||
| 239 | + var levels = ["1", "1b", "1.1", "1.2", "1.3", | ||
| 240 | + "2", "2.1", "2.2", "3", "3.1", "3.2", "4", "4.1", "4.2", "5", "5.1"]; | ||
| 241 | + for (var i = 0; i < levels.length; i++) { | ||
| 242 | + $(sl_level).append("<option value='" + levels[i] + "'>" + levels[i] + "</option"); | ||
| 243 | + } | ||
| 244 | + | ||
| 245 | + $(sl_gop).empty(); | ||
| 246 | + var gops = ["0.3", "0.5", "1", "2", "3", "4", | ||
| 247 | + "5", "6", "7", "8", "9", "10", "15", "20"]; | ||
| 248 | + for (var i = 0; i < gops.length; i++) { | ||
| 249 | + $(sl_gop).append("<option value='" + gops[i] + "'>" + gops[i] + "秒</option"); | ||
| 250 | + } | ||
| 251 | + | ||
| 252 | + $(sl_size).empty(); | ||
| 253 | + var sizes = ["176x144", "320x240", "352x240", | ||
| 254 | + "352x288", "480x360", "640x480", "720x480", "720x576", "800x600", | ||
| 255 | + "1024x768", "1280x720", "1360x768", "1920x1080"]; | ||
| 256 | + for (i = 0; i < sizes.length; i++) { | ||
| 257 | + $(sl_size).append("<option value='" + sizes[i] + "'>" + sizes[i] + "</option"); | ||
| 258 | + } | ||
| 259 | + | ||
| 260 | + $(sl_fps).empty(); | ||
| 261 | + var fpses = ["5", "10", "15", "20", "24", "25", "29.97", "30"]; | ||
| 262 | + for (i = 0; i < fpses.length; i++) { | ||
| 263 | + $(sl_fps).append("<option value='" + fpses[i] + "'>" + Number(fpses[i]).toFixed(2) + " 帧/秒</option"); | ||
| 264 | + } | ||
| 265 | + | ||
| 266 | + $(sl_bitrate).empty(); | ||
| 267 | + var bitrates = ["50", "200", "350", "500", "650", "800", | ||
| 268 | + "950", "1000", "1200", "1500", "1800", "2000", "3000", "5000"]; | ||
| 269 | + for (i = 0; i < bitrates.length; i++) { | ||
| 270 | + $(sl_bitrate).append("<option value='" + bitrates[i] + "'>" + bitrates[i] + " kbps</option"); | ||
| 271 | + } | ||
| 272 | +} | ||
| 273 | +/** | ||
| 274 | +* when publisher ready, init the page elements. | ||
| 275 | +*/ | ||
| 276 | +function srs_publisher_initialize_page( | ||
| 277 | + cameras, microphones, | ||
| 278 | + sl_cameras, sl_microphones, sl_vcodec, sl_profile, sl_level, sl_gop, sl_size, sl_fps, sl_bitrate | ||
| 279 | +) { | ||
| 280 | + srs_initialize_codec_page( | ||
| 281 | + cameras, microphones, | ||
| 282 | + sl_cameras, sl_microphones, sl_vcodec, sl_profile, sl_level, sl_gop, sl_size, sl_fps, sl_bitrate | ||
| 283 | + ); | ||
| 284 | + | ||
| 285 | + //var profiles = ["baseline", "main"]; | ||
| 286 | + $(sl_profile + " option[value='main']").attr("selected", true); | ||
| 287 | + | ||
| 288 | + //var levels = ["1", "1b", "1.1", "1.2", "1.3", | ||
| 289 | + // "2", "2.1", "2.2", "3", "3.1", "3.2", "4", "4.1", "4.2", "5", "5.1"]; | ||
| 290 | + $(sl_level + " option[value='4.1']").attr("selected", true); | ||
| 291 | + | ||
| 292 | + //var gops = ["0.3", "0.5", "1", "2", "3", "4", | ||
| 293 | + // "5", "6", "7", "8", "9", "10", "15", "20"]; | ||
| 294 | + $(sl_gop + " option[value='10']").attr("selected", true); | ||
| 295 | + | ||
| 296 | + //var sizes = ["176x144", "320x240", "352x240", | ||
| 297 | + // "352x288", "480x360", "640x480", "720x480", "720x576", "800x600", | ||
| 298 | + // "1024x768", "1280x720", "1360x768", "1920x1080"]; | ||
| 299 | + $(sl_size + " option[value='640x480']").attr("selected", true); | ||
| 300 | + | ||
| 301 | + //var fpses = ["5", "10", "15", "20", "24", "25", "29.97", "30"]; | ||
| 302 | + $(sl_fps + " option[value='20']").attr("selected", true); | ||
| 303 | + | ||
| 304 | + //var bitrates = ["50", "200", "350", "500", "650", "800", | ||
| 305 | + // "950", "1000", "1200", "1500", "1800", "2000", "3000", "5000"]; | ||
| 306 | + $(sl_bitrate + " option[value='500']").attr("selected", true); | ||
| 307 | +} | ||
| 308 | +/** | ||
| 309 | +* for chat, use low latecy settings. | ||
| 310 | +*/ | ||
| 311 | +function srs_chat_initialize_page( | ||
| 312 | + cameras, microphones, | ||
| 313 | + sl_cameras, sl_microphones, sl_vcodec, sl_profile, sl_level, sl_gop, sl_size, sl_fps, sl_bitrate | ||
| 314 | +) { | ||
| 315 | + srs_initialize_codec_page( | ||
| 316 | + cameras, microphones, | ||
| 317 | + sl_cameras, sl_microphones, sl_vcodec, sl_profile, sl_level, sl_gop, sl_size, sl_fps, sl_bitrate | ||
| 318 | + ); | ||
| 319 | + | ||
| 320 | + //var profiles = ["baseline", "main"]; | ||
| 321 | + $(sl_profile + " option[value='baseline']").attr("selected", true); | ||
| 322 | + | ||
| 323 | + //var levels = ["1", "1b", "1.1", "1.2", "1.3", | ||
| 324 | + // "2", "2.1", "2.2", "3", "3.1", "3.2", "4", "4.1", "4.2", "5", "5.1"]; | ||
| 325 | + $(sl_level + " option[value='3.1']").attr("selected", true); | ||
| 326 | + | ||
| 327 | + //var gops = ["0.3", "0.5", "1", "2", "3", "4", | ||
| 328 | + // "5", "6", "7", "8", "9", "10", "15", "20"]; | ||
| 329 | + $(sl_gop + " option[value='2']").attr("selected", true); | ||
| 330 | + | ||
| 331 | + //var sizes = ["176x144", "320x240", "352x240", | ||
| 332 | + // "352x288", "480x360", "640x480", "720x480", "720x576", "800x600", | ||
| 333 | + // "1024x768", "1280x720", "1360x768", "1920x1080"]; | ||
| 334 | + $(sl_size + " option[value='480x360']").attr("selected", true); | ||
| 335 | + | ||
| 336 | + //var fpses = ["5", "10", "15", "20", "24", "25", "29.97", "30"]; | ||
| 337 | + $(sl_fps + " option[value='15']").attr("selected", true); | ||
| 338 | + | ||
| 339 | + //var bitrates = ["50", "200", "350", "500", "650", "800", | ||
| 340 | + // "950", "1000", "1200", "1500", "1800", "2000", "3000", "5000"]; | ||
| 341 | + $(sl_bitrate + " option[value='350']").attr("selected", true); | ||
| 342 | +} | ||
| 343 | +/** | ||
| 344 | +* get the vcodec and acodec. | ||
| 345 | +*/ | ||
| 346 | +function srs_publiser_get_codec( | ||
| 347 | + vcodec, acodec, | ||
| 348 | + sl_cameras, sl_microphones, sl_vcodec, sl_profile, sl_level, sl_gop, sl_size, sl_fps, sl_bitrate | ||
| 349 | +) { | ||
| 350 | + acodec.device_code = $(sl_microphones).val(); | ||
| 351 | + acodec.device_name = $(sl_microphones).text(); | ||
| 352 | + | ||
| 353 | + vcodec.device_code = $(sl_cameras).find("option:selected").val(); | ||
| 354 | + vcodec.device_name = $(sl_cameras).find("option:selected").text(); | ||
| 355 | + | ||
| 356 | + vcodec.codec = $(sl_vcodec).find("option:selected").val(); | ||
| 357 | + vcodec.profile = $(sl_profile).find("option:selected").val(); | ||
| 358 | + vcodec.level = $(sl_level).find("option:selected").val(); | ||
| 359 | + vcodec.fps = $(sl_fps).find("option:selected").val(); | ||
| 360 | + vcodec.gop = $(sl_gop).find("option:selected").val(); | ||
| 361 | + vcodec.size = $(sl_size).find("option:selected").val(); | ||
| 362 | + vcodec.bitrate = $(sl_bitrate).find("option:selected").val(); | ||
| 363 | +} |
trunk/research/players/js/srs.player.js
0 → 100755
| 1 | +/** | ||
| 2 | +* the SrsPlayer object. | ||
| 3 | +* @param container the html container id. | ||
| 4 | +* @param width a float value specifies the width of player. | ||
| 5 | +* @param height a float value specifies the height of player. | ||
| 6 | +* @param private_object [optional] an object that used as private object, | ||
| 7 | +* for example, the logic chat object which owner this player. | ||
| 8 | +*/ | ||
| 9 | +function SrsPlayer(container, width, height, private_object) { | ||
| 10 | + if (!SrsPlayer.__id) { | ||
| 11 | + SrsPlayer.__id = 100; | ||
| 12 | + } | ||
| 13 | + if (!SrsPlayer.__players) { | ||
| 14 | + SrsPlayer.__players = []; | ||
| 15 | + } | ||
| 16 | + | ||
| 17 | + SrsPlayer.__players.push(this); | ||
| 18 | + | ||
| 19 | + this.private_object = private_object; | ||
| 20 | + this.container = container; | ||
| 21 | + this.width = width; | ||
| 22 | + this.height = height; | ||
| 23 | + this.id = SrsPlayer.__id++; | ||
| 24 | + this.stream_url = null; | ||
| 25 | + this.buffer_time = 0.8; // default to 0.8 | ||
| 26 | + this.volume = 1.0; // default to 100% | ||
| 27 | + this.callbackObj = null; | ||
| 28 | + | ||
| 29 | + // callback set the following values. | ||
| 30 | + this.meatadata = {}; // for on_player_metadata | ||
| 31 | + this.time = 0; // current stream time. | ||
| 32 | + this.buffer_length = 0; // current stream buffer length. | ||
| 33 | +} | ||
| 34 | +/** | ||
| 35 | +* user can set some callback, then start the player. | ||
| 36 | +* @param url the default url. | ||
| 37 | +* callbacks: | ||
| 38 | +* on_player_ready():int, when srs player ready, user can play. | ||
| 39 | +* on_player_metadata(metadata:Object):int, when srs player get metadata. | ||
| 40 | +*/ | ||
| 41 | +SrsPlayer.prototype.start = function(url) { | ||
| 42 | + if (url) { | ||
| 43 | + this.stream_url = url; | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + // embed the flash. | ||
| 47 | + var flashvars = {}; | ||
| 48 | + flashvars.id = this.id; | ||
| 49 | + flashvars.on_player_ready = "__srs_on_player_ready"; | ||
| 50 | + flashvars.on_player_metadata = "__srs_on_player_metadata"; | ||
| 51 | + flashvars.on_player_timer = "__srs_on_player_timer"; | ||
| 52 | + | ||
| 53 | + var params = {}; | ||
| 54 | + params.wmode = "opaque"; | ||
| 55 | + params.allowFullScreen = "true"; | ||
| 56 | + params.allowScriptAccess = "always"; | ||
| 57 | + | ||
| 58 | + var attributes = {}; | ||
| 59 | + | ||
| 60 | + var self = this; | ||
| 61 | + | ||
| 62 | + swfobject.embedSWF( | ||
| 63 | + "srs_player/release/srs_player.swf?_version="+srs_get_version_code(), | ||
| 64 | + this.container, | ||
| 65 | + this.width, this.height, | ||
| 66 | + "11.1", "js/AdobeFlashPlayerInstall.swf", | ||
| 67 | + flashvars, params, attributes, | ||
| 68 | + function(callbackObj){ | ||
| 69 | + self.callbackObj = callbackObj; | ||
| 70 | + } | ||
| 71 | + ); | ||
| 72 | + | ||
| 73 | + return this; | ||
| 74 | +} | ||
| 75 | +/** | ||
| 76 | +* play the stream. | ||
| 77 | +* @param stream_url the url of stream, rtmp or http. | ||
| 78 | +* @param volume the volume, 0 is mute, 1 is 100%, 2 is 200%. | ||
| 79 | +*/ | ||
| 80 | +SrsPlayer.prototype.play = function(url, volume) { | ||
| 81 | + this.stop(); | ||
| 82 | + SrsPlayer.__players.push(this); | ||
| 83 | + | ||
| 84 | + if (url) { | ||
| 85 | + this.stream_url = url; | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + // volume maybe 0, so never use if(volume) to check its value. | ||
| 89 | + if (volume != null && volume != undefined) { | ||
| 90 | + this.volume = volume; | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + this.callbackObj.ref.__play(this.stream_url, this.width, this.height, this.buffer_time, this.volume); | ||
| 94 | +} | ||
| 95 | +SrsPlayer.prototype.stop = function() { | ||
| 96 | + for (var i = 0; i < SrsPlayer.__players.length; i++) { | ||
| 97 | + var player = SrsPlayer.__players[i]; | ||
| 98 | + | ||
| 99 | + if (player.id != this.id) { | ||
| 100 | + continue; | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + SrsPlayer.__players.splice(i, 1); | ||
| 104 | + break; | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + this.callbackObj.ref.__stop(); | ||
| 108 | +} | ||
| 109 | +SrsPlayer.prototype.pause = function() { | ||
| 110 | + this.callbackObj.ref.__pause(); | ||
| 111 | +} | ||
| 112 | +SrsPlayer.prototype.resume = function() { | ||
| 113 | + this.callbackObj.ref.__resume(); | ||
| 114 | +} | ||
| 115 | +/** | ||
| 116 | +* to set the DAR, for example, DAR=16:9 where num=16,den=9. | ||
| 117 | +* @param num, for example, 16. | ||
| 118 | +* use metadata width if 0. | ||
| 119 | +* use user specified width if -1. | ||
| 120 | +* @param den, for example, 9. | ||
| 121 | +* use metadata height if 0. | ||
| 122 | +* use user specified height if -1. | ||
| 123 | +*/ | ||
| 124 | +SrsPlayer.prototype.set_dar = function(num, den) { | ||
| 125 | + this.callbackObj.ref.__set_dar(num, den); | ||
| 126 | +} | ||
| 127 | +/** | ||
| 128 | +* set the fullscreen size data. | ||
| 129 | +* @refer the refer fullscreen mode. it can be: | ||
| 130 | +* video: use video orignal size. | ||
| 131 | +* screen: use screen size to rescale video. | ||
| 132 | +* @param percent, the rescale percent, where | ||
| 133 | +* 100 means 100%. | ||
| 134 | +*/ | ||
| 135 | +SrsPlayer.prototype.set_fs = function(refer, percent) { | ||
| 136 | + this.callbackObj.ref.__set_fs(refer, percent); | ||
| 137 | +} | ||
| 138 | +/** | ||
| 139 | +* set the stream buffer time in seconds. | ||
| 140 | +* @buffer_time the buffer time in seconds. | ||
| 141 | +*/ | ||
| 142 | +SrsPlayer.prototype.set_bt = function(buffer_time) { | ||
| 143 | + this.buffer_time = buffer_time; | ||
| 144 | + this.callbackObj.ref.__set_bt(buffer_time); | ||
| 145 | +} | ||
| 146 | +SrsPlayer.prototype.on_player_ready = function() { | ||
| 147 | +} | ||
| 148 | +SrsPlayer.prototype.on_player_metadata = function(metadata) { | ||
| 149 | + // ignore. | ||
| 150 | +} | ||
| 151 | +SrsPlayer.prototype.on_player_timer = function(time, buffer_length) { | ||
| 152 | + // ignore. | ||
| 153 | +} | ||
| 154 | +function __srs_find_player(id) { | ||
| 155 | + for (var i = 0; i < SrsPlayer.__players.length; i++) { | ||
| 156 | + var player = SrsPlayer.__players[i]; | ||
| 157 | + | ||
| 158 | + if (player.id != id) { | ||
| 159 | + continue; | ||
| 160 | + } | ||
| 161 | + | ||
| 162 | + return player; | ||
| 163 | + } | ||
| 164 | + | ||
| 165 | + throw new Error("player not found. id=" + id); | ||
| 166 | +} | ||
| 167 | +function __srs_on_player_ready(id) { | ||
| 168 | + var player = __srs_find_player(id); | ||
| 169 | + player.on_player_ready(); | ||
| 170 | +} | ||
| 171 | +function __srs_on_player_metadata(id, metadata) { | ||
| 172 | + var player = __srs_find_player(id); | ||
| 173 | + | ||
| 174 | + // user may override the on_player_metadata, | ||
| 175 | + // so set the data before invoke it. | ||
| 176 | + player.metadata = metadata; | ||
| 177 | + | ||
| 178 | + player.on_player_metadata(metadata); | ||
| 179 | +} | ||
| 180 | +function __srs_on_player_timer(id, time, buffer_length) { | ||
| 181 | + var player = __srs_find_player(id); | ||
| 182 | + | ||
| 183 | + buffer_length = Math.max(0, buffer_length); | ||
| 184 | + buffer_length = Math.min(player.buffer_time, buffer_length); | ||
| 185 | + | ||
| 186 | + time = Math.max(0, time); | ||
| 187 | + | ||
| 188 | + // user may override the on_player_timer, | ||
| 189 | + // so set the data before invoke it. | ||
| 190 | + player.time = time; | ||
| 191 | + player.buffer_length = buffer_length; | ||
| 192 | + | ||
| 193 | + player.on_player_timer(time, buffer_length); | ||
| 194 | +} |
trunk/research/players/js/srs.publisher.js
0 → 100755
| 1 | +/** | ||
| 2 | +* the SrsPublisher object. | ||
| 3 | +* @param container the html container id. | ||
| 4 | +* @param width a float value specifies the width of publisher. | ||
| 5 | +* @param height a float value specifies the height of publisher. | ||
| 6 | +* @param private_object [optional] an object that used as private object, | ||
| 7 | +* for example, the logic chat object which owner this publisher. | ||
| 8 | +*/ | ||
| 9 | +function SrsPublisher(container, width, height, private_object) { | ||
| 10 | + if (!SrsPublisher.__id) { | ||
| 11 | + SrsPublisher.__id = 100; | ||
| 12 | + } | ||
| 13 | + if (!SrsPublisher.__publishers) { | ||
| 14 | + SrsPublisher.__publishers = []; | ||
| 15 | + } | ||
| 16 | + | ||
| 17 | + SrsPublisher.__publishers.push(this); | ||
| 18 | + | ||
| 19 | + this.private_object = private_object; | ||
| 20 | + this.container = container; | ||
| 21 | + this.width = width; | ||
| 22 | + this.height = height; | ||
| 23 | + this.id = SrsPublisher.__id++; | ||
| 24 | + this.callbackObj = null; | ||
| 25 | + | ||
| 26 | + // set the values when publish. | ||
| 27 | + this.url = null; | ||
| 28 | + this.vcodec = {}; | ||
| 29 | + this.acodec = {}; | ||
| 30 | + | ||
| 31 | + // callback set the following values. | ||
| 32 | + this.cameras = []; | ||
| 33 | + this.microphones = []; | ||
| 34 | + this.code = 0; | ||
| 35 | + | ||
| 36 | + // error code defines. | ||
| 37 | + this.errors = { | ||
| 38 | + "100": "无法获取指定的摄像头。", //error_camera_get | ||
| 39 | + "101": "无法获取指定的麦克风。", //error_microphone_get | ||
| 40 | + "102": "摄像头为禁用状态,推流时请允许flash访问摄像头。", //error_camera_muted | ||
| 41 | + "103": "服务器关闭了连接。", //error_connection_closed | ||
| 42 | + "104": "服务器连接失败。", //error_connection_failed | ||
| 43 | + "199": "未知错误。" | ||
| 44 | + }; | ||
| 45 | +} | ||
| 46 | +/** | ||
| 47 | +* user can set some callback, then start the publisher. | ||
| 48 | +* callbacks: | ||
| 49 | +* on_publisher_ready(cameras, microphones):int, when srs publisher ready, user can publish. | ||
| 50 | +* on_publisher_error(code, desc):int, when srs publisher error, callback this method. | ||
| 51 | +* on_publisher_warn(code, desc):int, when srs publisher warn, callback this method. | ||
| 52 | +*/ | ||
| 53 | +SrsPublisher.prototype.start = function() { | ||
| 54 | + // embed the flash. | ||
| 55 | + var flashvars = {}; | ||
| 56 | + flashvars.id = this.id; | ||
| 57 | + flashvars.width = this.width; | ||
| 58 | + flashvars.height = this.height; | ||
| 59 | + flashvars.on_publisher_ready = "__srs_on_publisher_ready"; | ||
| 60 | + flashvars.on_publisher_error = "__srs_on_publisher_error"; | ||
| 61 | + flashvars.on_publisher_warn = "__srs_on_publisher_warn"; | ||
| 62 | + | ||
| 63 | + var params = {}; | ||
| 64 | + params.wmode = "opaque"; | ||
| 65 | + params.allowFullScreen = "true"; | ||
| 66 | + params.allowScriptAccess = "always"; | ||
| 67 | + | ||
| 68 | + var attributes = {}; | ||
| 69 | + | ||
| 70 | + var self = this; | ||
| 71 | + | ||
| 72 | + swfobject.embedSWF( | ||
| 73 | + "srs_publisher/release/srs_publisher.swf?_version="+srs_get_version_code(), | ||
| 74 | + this.container, | ||
| 75 | + this.width, this.height, | ||
| 76 | + "11.1", "js/AdobeFlashPlayerInstall.swf", | ||
| 77 | + flashvars, params, attributes, | ||
| 78 | + function(callbackObj){ | ||
| 79 | + self.callbackObj = callbackObj; | ||
| 80 | + } | ||
| 81 | + ); | ||
| 82 | + | ||
| 83 | + return this; | ||
| 84 | +} | ||
| 85 | +/** | ||
| 86 | +* publish stream to server. | ||
| 87 | +* @param url a string indicates the rtmp url to publish. | ||
| 88 | +* @param vcodec an object contains the video codec info. | ||
| 89 | +* @param acodec an object contains the audio codec info. | ||
| 90 | +*/ | ||
| 91 | +SrsPublisher.prototype.publish = function(url, vcodec, acodec) { | ||
| 92 | + this.stop(); | ||
| 93 | + SrsPublisher.__publishers.push(this); | ||
| 94 | + | ||
| 95 | + if (url) { | ||
| 96 | + this.url = url; | ||
| 97 | + } | ||
| 98 | + if (vcodec) { | ||
| 99 | + this.vcodec = vcodec; | ||
| 100 | + } | ||
| 101 | + if (acodec) { | ||
| 102 | + this.acodec = acodec; | ||
| 103 | + } | ||
| 104 | + | ||
| 105 | + this.callbackObj.ref.__publish(this.url, this.width, this.height, this.vcodec, this.acodec); | ||
| 106 | +} | ||
| 107 | +SrsPublisher.prototype.stop = function() { | ||
| 108 | + for (var i = 0; i < SrsPublisher.__publishers.length; i++) { | ||
| 109 | + var player = SrsPublisher.__publishers[i]; | ||
| 110 | + | ||
| 111 | + if (player.id != this.id) { | ||
| 112 | + continue; | ||
| 113 | + } | ||
| 114 | + | ||
| 115 | + SrsPublisher.__publishers.splice(i, 1); | ||
| 116 | + break; | ||
| 117 | + } | ||
| 118 | + | ||
| 119 | + this.callbackObj.ref.__stop(); | ||
| 120 | +} | ||
| 121 | +/** | ||
| 122 | +* when publisher ready. | ||
| 123 | +* @param cameras a string array contains the names of cameras. | ||
| 124 | +* @param microphones a string array contains the names of microphones. | ||
| 125 | +*/ | ||
| 126 | +SrsPublisher.prototype.on_publisher_ready = function(cameras, microphones) { | ||
| 127 | +} | ||
| 128 | +/** | ||
| 129 | +* when publisher error. | ||
| 130 | +* @code the error code. | ||
| 131 | +* @desc the error desc message. | ||
| 132 | +*/ | ||
| 133 | +SrsPublisher.prototype.on_publisher_error = function(code, desc) { | ||
| 134 | + throw new Error("publisher error. code=" + code + ", desc=" + desc); | ||
| 135 | +} | ||
| 136 | +SrsPublisher.prototype.on_publisher_warn = function(code, desc) { | ||
| 137 | + throw new Error("publisher warn. code=" + code + ", desc=" + desc); | ||
| 138 | +} | ||
| 139 | +function __srs_find_publisher(id) { | ||
| 140 | + for (var i = 0; i < SrsPublisher.__publishers.length; i++) { | ||
| 141 | + var publisher = SrsPublisher.__publishers[i]; | ||
| 142 | + | ||
| 143 | + if (publisher.id != id) { | ||
| 144 | + continue; | ||
| 145 | + } | ||
| 146 | + | ||
| 147 | + return publisher; | ||
| 148 | + } | ||
| 149 | + | ||
| 150 | + throw new Error("publisher not found. id=" + id); | ||
| 151 | +} | ||
| 152 | +function __srs_on_publisher_ready(id, cameras, microphones) { | ||
| 153 | + var publisher = __srs_find_publisher(id); | ||
| 154 | + | ||
| 155 | + publisher.cameras = cameras; | ||
| 156 | + publisher.microphones = microphones; | ||
| 157 | + | ||
| 158 | + publisher.on_publisher_ready(cameras, microphones); | ||
| 159 | +} | ||
| 160 | +function __srs_on_publisher_error(id, code) { | ||
| 161 | + var publisher = __srs_find_publisher(id); | ||
| 162 | + | ||
| 163 | + publisher.code = code; | ||
| 164 | + | ||
| 165 | + publisher.on_publisher_error(code, publisher.errors[""+code]); | ||
| 166 | +} | ||
| 167 | +function __srs_on_publisher_warn(id, code) { | ||
| 168 | + var publisher = __srs_find_publisher(id); | ||
| 169 | + | ||
| 170 | + publisher.code = code; | ||
| 171 | + | ||
| 172 | + publisher.on_publisher_warn(code, publisher.errors[""+code]); | ||
| 173 | +} |
trunk/research/players/js/srs.utility.js
0 → 100755
| 1 | +/** | ||
| 2 | +* padding the output. | ||
| 3 | +* padding(3, 5, '0') is 00003 | ||
| 4 | +* padding(3, 5, 'x') is xxxx3 | ||
| 5 | +* @see http://blog.csdn.net/win_lin/article/details/12065413 | ||
| 6 | +*/ | ||
| 7 | +function padding(number, length, prefix) { | ||
| 8 | + if(String(number).length >= length){ | ||
| 9 | + return String(number); | ||
| 10 | + } | ||
| 11 | + return padding(prefix+number, length, prefix); | ||
| 12 | +} | ||
| 13 | + | ||
| 14 | +/** | ||
| 15 | +* parse the query string to object. | ||
| 16 | +*/ | ||
| 17 | +function parse_query_string(){ | ||
| 18 | + var obj = {}; | ||
| 19 | + | ||
| 20 | + // add the uri object. | ||
| 21 | + // parse the host(hostname:http_port), pathname(dir/filename) | ||
| 22 | + obj.host = window.location.host; | ||
| 23 | + obj.hostname = window.location.hostname; | ||
| 24 | + obj.http_port = (window.location.port == "")? 80:window.location.port; | ||
| 25 | + obj.pathname = window.location.pathname; | ||
| 26 | + if (obj.pathname.lastIndexOf("/") <= 0) { | ||
| 27 | + obj.dir = "/"; | ||
| 28 | + obj.filename = ""; | ||
| 29 | + } else { | ||
| 30 | + obj.dir = obj.pathname.substr(0, obj.pathname.lastIndexOf("/")); | ||
| 31 | + obj.filename = obj.pathname.substr(obj.pathname.lastIndexOf("/")); | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + // pure user query object. | ||
| 35 | + obj.user_query = {}; | ||
| 36 | + | ||
| 37 | + // parse the query string. | ||
| 38 | + var query_string = String(window.location.search).replace(" ", "").split("?")[1]; | ||
| 39 | + if(query_string == undefined){ | ||
| 40 | + return obj; | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + var queries = query_string.split("&"); | ||
| 44 | + $(queries).each(function(){ | ||
| 45 | + var query = this.split("="); | ||
| 46 | + obj[query[0]] = query[1]; | ||
| 47 | + obj.user_query[query[0]] = query[1]; | ||
| 48 | + }); | ||
| 49 | + | ||
| 50 | + return obj; | ||
| 51 | +} | ||
| 52 | + | ||
| 53 | +/** | ||
| 54 | +* parse the rtmp url, | ||
| 55 | +* for example: rtmp://demo.srs.com:1935/live...vhost...players/livestream | ||
| 56 | +* @return object {server, port, vhost, app, stream} | ||
| 57 | +*/ | ||
| 58 | +function srs_parse_rtmp_url(rtmp_url) { | ||
| 59 | + // @see: http://stackoverflow.com/questions/10469575/how-to-use-location-object-to-parse-url-without-redirecting-the-page-in-javascri | ||
| 60 | + var a = document.createElement("a"); | ||
| 61 | + a.href = rtmp_url.replace("rtmp://", "http://"); | ||
| 62 | + | ||
| 63 | + var vhost = a.hostname; | ||
| 64 | + var port = (a.port == "")? "1935":a.port; | ||
| 65 | + var app = a.pathname.substr(1, a.pathname.lastIndexOf("/") - 1); | ||
| 66 | + var stream = a.pathname.substr(a.pathname.lastIndexOf("/") + 1); | ||
| 67 | + | ||
| 68 | + // parse the vhost in the params of app, that srs supports. | ||
| 69 | + app = app.replace("...vhost...", "?vhost="); | ||
| 70 | + if (app.indexOf("?") >= 0) { | ||
| 71 | + var params = app.substr(app.indexOf("?")); | ||
| 72 | + app = app.substr(0, app.indexOf("?")); | ||
| 73 | + | ||
| 74 | + if (params.indexOf("vhost=") > 0) { | ||
| 75 | + vhost = params.substr(params.indexOf("vhost=") + "vhost=".length); | ||
| 76 | + if (vhost.indexOf("&") > 0) { | ||
| 77 | + vhost = vhost.substr(0, vhost.indexOf("&")); | ||
| 78 | + } | ||
| 79 | + } | ||
| 80 | + } | ||
| 81 | + | ||
| 82 | + var ret = { | ||
| 83 | + server: a.hostname, port: port, | ||
| 84 | + vhost: vhost, app: app, stream: stream | ||
| 85 | + }; | ||
| 86 | + | ||
| 87 | + return ret; | ||
| 88 | +} | ||
| 89 | + | ||
| 90 | +/** | ||
| 91 | +* get the agent. | ||
| 92 | +* @return an object specifies some browser. | ||
| 93 | +* for example, get_browser_agents().MSIE | ||
| 94 | +*/ | ||
| 95 | +function get_browser_agents() { | ||
| 96 | + var agent = navigator.userAgent; | ||
| 97 | + | ||
| 98 | + /** | ||
| 99 | + WindowsPC platform, Win7: | ||
| 100 | + chrome 31.0.1650.63: | ||
| 101 | + Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 | ||
| 102 | + (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36 | ||
| 103 | + firefox 23.0.1: | ||
| 104 | + Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20100101 | ||
| 105 | + Firefox/23.0 | ||
| 106 | + safari 5.1.7(7534.57.2): | ||
| 107 | + Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.57.2 | ||
| 108 | + (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2 | ||
| 109 | + opera 15.0.1147.153: | ||
| 110 | + Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 | ||
| 111 | + (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36 | ||
| 112 | + OPR/15.0.1147.153 | ||
| 113 | + 360 6.2.1.272: | ||
| 114 | + Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; | ||
| 115 | + Trident/6.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; | ||
| 116 | + .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; .NET4.0C; | ||
| 117 | + .NET4.0E) | ||
| 118 | + IE 10.0.9200.16750(update: 10.0.12): | ||
| 119 | + Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; | ||
| 120 | + Trident/6.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; | ||
| 121 | + .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; .NET4.0C; | ||
| 122 | + .NET4.0E) | ||
| 123 | + */ | ||
| 124 | + | ||
| 125 | + return { | ||
| 126 | + // platform | ||
| 127 | + Android: agent.indexOf("Android") != -1, | ||
| 128 | + Windows: agent.indexOf("Windows") != -1, | ||
| 129 | + iPhone: agent.indexOf("iPhone") != -1, | ||
| 130 | + // Windows Browsers | ||
| 131 | + Chrome: agent.indexOf("Chrome") != -1, | ||
| 132 | + Firefox: agent.indexOf("Firefox") != -1, | ||
| 133 | + QQBrowser: agent.indexOf("QQBrowser") != -1, | ||
| 134 | + MSIE: agent.indexOf("MSIE") != -1, | ||
| 135 | + // Android Browsers | ||
| 136 | + Opera: agent.indexOf("Presto") != -1, | ||
| 137 | + MQQBrowser: agent.indexOf("MQQBrowser") != -1 | ||
| 138 | + }; | ||
| 139 | +} |
| @@ -6,7 +6,11 @@ | @@ -6,7 +6,11 @@ | ||
| 6 | <link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/> | 6 | <link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/> |
| 7 | <script type="text/javascript" src="js/jquery-1.10.2.min.js"></script> | 7 | <script type="text/javascript" src="js/jquery-1.10.2.min.js"></script> |
| 8 | <script type="text/javascript" src="js/bootstrap.min.js"></script> | 8 | <script type="text/javascript" src="js/bootstrap.min.js"></script> |
| 9 | - <script type="text/javascript" src="js/srs.js"></script> | 9 | + <script type="text/javascript" src="js/srs.page.js"></script> |
| 10 | + <script type="text/javascript" src="js/srs.log.js"></script> | ||
| 11 | + <script type="text/javascript" src="js/srs.player.js"></script> | ||
| 12 | + <script type="text/javascript" src="js/srs.publisher.js"></script> | ||
| 13 | + <script type="text/javascript" src="js/srs.utility.js"></script> | ||
| 10 | <style> | 14 | <style> |
| 11 | body{ | 15 | body{ |
| 12 | padding-top: 55px; | 16 | padding-top: 55px; |
| @@ -78,7 +82,7 @@ | @@ -78,7 +82,7 @@ | ||
| 78 | <div class="navbar navbar-fixed-top"> | 82 | <div class="navbar navbar-fixed-top"> |
| 79 | <div class="navbar-inner"> | 83 | <div class="navbar-inner"> |
| 80 | <div class="container"> | 84 | <div class="container"> |
| 81 | - <a class="brand" href="index.html">SRS</a> | 85 | + <a id="srs_index" class="brand" href="#">SRS</a> |
| 82 | <div class="nav-collapse collapse"> | 86 | <div class="nav-collapse collapse"> |
| 83 | <ul class="nav"> | 87 | <ul class="nav"> |
| 84 | <li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li> | 88 | <li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li> |
trunk/research/players/nginx_index.html
0 → 100755
| 1 | +<!DOCTYPE html> | ||
| 2 | +<html> | ||
| 3 | +<head> | ||
| 4 | + <title>SRS</title> | ||
| 5 | + <meta charset="utf-8"> | ||
| 6 | + <script type="text/javascript" src="players/js/jquery-1.10.2.min.js"></script> | ||
| 7 | + <script type="text/javascript" src="players/js/srs.page.js"></script> | ||
| 8 | + <script type="text/javascript" src="players/js/srs.utility.js"></script> | ||
| 9 | +</head> | ||
| 10 | +<body> | ||
| 11 | + <script type="text/javascript"> | ||
| 12 | + var query = parse_query_string(); | ||
| 13 | + var url = window.location.protocol + "//" + query.hostname + ":" + srs_get_api_server_port() + "/index.html" + window.location.search; | ||
| 14 | + document.write("请确认api-server已经开启,跳转到api-server的页面(解决IE跨域问题)<br/>"); | ||
| 15 | + document.write("正在跳转,若您的浏览器没有跳转,请点击: <a href='" + url + "'>" + url + "</a>"); | ||
| 16 | + | ||
| 17 | + setTimeout(function(){ | ||
| 18 | + window.location.href = url; | ||
| 19 | + }, 3000); | ||
| 20 | + </script> | ||
| 21 | +</body> |
| @@ -7,7 +7,11 @@ | @@ -7,7 +7,11 @@ | ||
| 7 | <script type="text/javascript" src="js/jquery-1.10.2.min.js"></script> | 7 | <script type="text/javascript" src="js/jquery-1.10.2.min.js"></script> |
| 8 | <script type="text/javascript" src="js/bootstrap.min.js"></script> | 8 | <script type="text/javascript" src="js/bootstrap.min.js"></script> |
| 9 | <script type="text/javascript" src="js/swfobject.js"></script> | 9 | <script type="text/javascript" src="js/swfobject.js"></script> |
| 10 | - <script type="text/javascript" src="js/srs.js"></script> | 10 | + <script type="text/javascript" src="js/srs.page.js"></script> |
| 11 | + <script type="text/javascript" src="js/srs.log.js"></script> | ||
| 12 | + <script type="text/javascript" src="js/srs.player.js"></script> | ||
| 13 | + <script type="text/javascript" src="js/srs.publisher.js"></script> | ||
| 14 | + <script type="text/javascript" src="js/srs.utility.js"></script> | ||
| 11 | <style> | 15 | <style> |
| 12 | body{ | 16 | body{ |
| 13 | padding-top: 55px; | 17 | padding-top: 55px; |
| @@ -74,7 +78,7 @@ | @@ -74,7 +78,7 @@ | ||
| 74 | <div class="navbar navbar-fixed-top"> | 78 | <div class="navbar navbar-fixed-top"> |
| 75 | <div class="navbar-inner"> | 79 | <div class="navbar-inner"> |
| 76 | <div class="container"> | 80 | <div class="container"> |
| 77 | - <a class="brand" href="index.html">SRS</a> | 81 | + <a id="srs_index" class="brand" href="#">SRS</a> |
| 78 | <div class="nav-collapse collapse"> | 82 | <div class="nav-collapse collapse"> |
| 79 | <ul class="nav"> | 83 | <ul class="nav"> |
| 80 | <li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li> | 84 | <li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li> |
| @@ -7,7 +7,13 @@ | @@ -7,7 +7,13 @@ | ||
| 7 | <script type="text/javascript" src="js/jquery-1.10.2.min.js"></script> | 7 | <script type="text/javascript" src="js/jquery-1.10.2.min.js"></script> |
| 8 | <script type="text/javascript" src="js/bootstrap.min.js"></script> | 8 | <script type="text/javascript" src="js/bootstrap.min.js"></script> |
| 9 | <script type="text/javascript" src="js/swfobject.js"></script> | 9 | <script type="text/javascript" src="js/swfobject.js"></script> |
| 10 | - <script type="text/javascript" src="js/srs.js"></script> | 10 | + <script type="text/javascript" src="js/srs.page.js"></script> |
| 11 | + <script type="text/javascript" src="js/srs.log.js"></script> | ||
| 12 | + <script type="text/javascript" src="js/srs.player.js"></script> | ||
| 13 | + <script type="text/javascript" src="js/srs.publisher.js"></script> | ||
| 14 | + <script type="text/javascript" src="js/srs.utility.js"></script> | ||
| 15 | + <script type="text/javascript" src="js/srs.utility.js"></script> | ||
| 16 | + <script type="text/javascript" src="js/srs.bandwidth.js"></script> | ||
| 11 | <style> | 17 | <style> |
| 12 | body{ | 18 | body{ |
| 13 | padding-top: 55px; | 19 | padding-top: 55px; |
| @@ -119,7 +125,7 @@ | @@ -119,7 +125,7 @@ | ||
| 119 | <div class="navbar navbar-fixed-top"> | 125 | <div class="navbar navbar-fixed-top"> |
| 120 | <div class="navbar-inner"> | 126 | <div class="navbar-inner"> |
| 121 | <div class="container"> | 127 | <div class="container"> |
| 122 | - <a class="brand" href="index.html">SRS</a> | 128 | + <a id="srs_index" class="brand" href="#">SRS</a> |
| 123 | <div class="nav-collapse collapse"> | 129 | <div class="nav-collapse collapse"> |
| 124 | <ul class="nav"> | 130 | <ul class="nav"> |
| 125 | <li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li> | 131 | <li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li> |
| @@ -7,11 +7,19 @@ | @@ -7,11 +7,19 @@ | ||
| 7 | <script type="text/javascript" src="js/jquery-1.10.2.min.js"></script> | 7 | <script type="text/javascript" src="js/jquery-1.10.2.min.js"></script> |
| 8 | <script type="text/javascript" src="js/bootstrap.min.js"></script> | 8 | <script type="text/javascript" src="js/bootstrap.min.js"></script> |
| 9 | <script type="text/javascript" src="js/swfobject.js"></script> | 9 | <script type="text/javascript" src="js/swfobject.js"></script> |
| 10 | - <script type="text/javascript" src="js/srs.js"></script> | 10 | + <script type="text/javascript" src="js/json2.js"></script> |
| 11 | + <script type="text/javascript" src="js/srs.page.js"></script> | ||
| 12 | + <script type="text/javascript" src="js/srs.log.js"></script> | ||
| 13 | + <script type="text/javascript" src="js/srs.player.js"></script> | ||
| 14 | + <script type="text/javascript" src="js/srs.publisher.js"></script> | ||
| 15 | + <script type="text/javascript" src="js/srs.utility.js"></script> | ||
| 11 | <style> | 16 | <style> |
| 12 | body{ | 17 | body{ |
| 13 | padding-top: 55px; | 18 | padding-top: 55px; |
| 14 | } | 19 | } |
| 20 | + .accordion-group { | ||
| 21 | + width: 310px; | ||
| 22 | + } | ||
| 15 | </style> | 23 | </style> |
| 16 | <script type="text/javascript"> | 24 | <script type="text/javascript"> |
| 17 | var srs_publisher = null; | 25 | var srs_publisher = null; |
| @@ -22,18 +30,36 @@ | @@ -22,18 +30,36 @@ | ||
| 22 | var previous_chats = []; | 30 | var previous_chats = []; |
| 23 | var no_play = false; | 31 | var no_play = false; |
| 24 | 32 | ||
| 33 | + var query = parse_query_string(); | ||
| 25 | $(function(){ | 34 | $(function(){ |
| 26 | // get the vhost and port to set the default url. | 35 | // get the vhost and port to set the default url. |
| 27 | // for example: http://192.168.1.213/players/jwplayer6.html?port=1935&vhost=demo | 36 | // for example: http://192.168.1.213/players/jwplayer6.html?port=1935&vhost=demo |
| 28 | // url set to: rtmp://demo:1935/live/livestream | 37 | // url set to: rtmp://demo:1935/live/livestream |
| 29 | srs_init_publish("#txt_url"); | 38 | srs_init_publish("#txt_url"); |
| 30 | 39 | ||
| 40 | + // support 5x3+1 users | ||
| 41 | + for (var i = 0; i < 5; i++) { | ||
| 42 | + var tr = $("<tr></tr>").hide(); | ||
| 43 | + $("#lst_chats").append(tr); | ||
| 44 | + | ||
| 45 | + for (var j = 0; j < 3; j++) { | ||
| 46 | + tr.append($("<td></td>").attr("id", "td_" + ((i+1) * 8 + j))); | ||
| 47 | + } | ||
| 48 | + } | ||
| 49 | + // remove border of row. | ||
| 50 | + $("#lst_chats").find("td").css("border", "none").css("padding", "2px") | ||
| 51 | + .css("padding-left", "0px").css("width", "327px"); | ||
| 52 | + | ||
| 53 | + if (query.agent == "true") { | ||
| 54 | + document.write(navigator.userAgent); | ||
| 55 | + return; | ||
| 56 | + } | ||
| 57 | + | ||
| 31 | $("#realtime_player_url").tooltip({ | 58 | $("#realtime_player_url").tooltip({ |
| 32 | title: "右键复制RTMP地址" | 59 | title: "右键复制RTMP地址" |
| 33 | }); | 60 | }); |
| 34 | 61 | ||
| 35 | // if no play specified, donot show the player, for debug the publisher. | 62 | // if no play specified, donot show the player, for debug the publisher. |
| 36 | - var query = parse_query_string(); | ||
| 37 | if (query.no_play == "true") { | 63 | if (query.no_play == "true") { |
| 38 | no_play = true; | 64 | no_play = true; |
| 39 | } | 65 | } |
| @@ -50,7 +76,7 @@ | @@ -50,7 +76,7 @@ | ||
| 50 | $("#txt_url").val($("#txt_url").val() + "." + new Date().getTime()); | 76 | $("#txt_url").val($("#txt_url").val() + "." + new Date().getTime()); |
| 51 | 77 | ||
| 52 | // start the publisher. | 78 | // start the publisher. |
| 53 | - srs_publisher = new SrsPublisher("local_publisher", 430, 185); | 79 | + srs_publisher = new SrsPublisher("local_publisher", 280, 180); |
| 54 | srs_publisher.on_publisher_ready = function(cameras, microphones) { | 80 | srs_publisher.on_publisher_ready = function(cameras, microphones) { |
| 55 | srs_chat_initialize_page( | 81 | srs_chat_initialize_page( |
| 56 | cameras, microphones, | 82 | cameras, microphones, |
| @@ -60,7 +86,11 @@ | @@ -60,7 +86,11 @@ | ||
| 60 | ); | 86 | ); |
| 61 | }; | 87 | }; |
| 62 | srs_publisher.on_publisher_error = function(code, desc) { | 88 | srs_publisher.on_publisher_error = function(code, desc) { |
| 63 | - error(code, desc); | 89 | + if (!on_publish_stop()) { |
| 90 | + return; | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + error(code, desc + "请重试。"); | ||
| 64 | }; | 94 | }; |
| 65 | srs_publisher.on_publisher_warn = function(code, desc) { | 95 | srs_publisher.on_publisher_warn = function(code, desc) { |
| 66 | warn(code, desc); | 96 | warn(code, desc); |
| @@ -71,18 +101,37 @@ | @@ -71,18 +101,37 @@ | ||
| 71 | 101 | ||
| 72 | if (!no_play) { | 102 | if (!no_play) { |
| 73 | // start the realtime player. | 103 | // start the realtime player. |
| 74 | - realtime_player = new SrsPlayer("realtime_player", 430, 185); | 104 | + realtime_player = new SrsPlayer("realtime_player", 280, 180); |
| 75 | realtime_player.on_player_ready = function() { | 105 | realtime_player.on_player_ready = function() { |
| 76 | this.set_bt(0.5); | 106 | this.set_bt(0.5); |
| 77 | - this.set_fs("screen", 100); | ||
| 78 | }; | 107 | }; |
| 108 | + realtime_player.on_player_metadata = function(metadata) { | ||
| 109 | + this.set_dar(0, 0); | ||
| 110 | + this.set_fs("screen", 100); | ||
| 111 | + | ||
| 112 | + info("推流到服务器成功。请戴耳机聊天,否则音箱的声音会进入麦克风造成回声。"); | ||
| 113 | + } | ||
| 79 | realtime_player.start(); | 114 | realtime_player.start(); |
| 80 | } | 115 | } |
| 81 | 116 | ||
| 117 | + $("#txt_name").focus(); | ||
| 118 | + | ||
| 82 | api_server = "http://" + query.hostname + ":" + srs_get_api_server_port() + "/api/v1/chats"; | 119 | api_server = "http://" + query.hostname + ":" + srs_get_api_server_port() + "/api/v1/chats"; |
| 83 | refresh(); | 120 | refresh(); |
| 84 | }); | 121 | }); |
| 85 | 122 | ||
| 123 | + function on_publish_stop() { | ||
| 124 | + if (!srs_can_republish()) { | ||
| 125 | + $("#btn_join").attr("disabled", true); | ||
| 126 | + error(0, "您使用的浏览器很弱,请关闭页面后重新打开页面(刷新也不管用)。<br/>推荐使用Chrome/Firefox/Safari/Opera浏览器,支持重试"); | ||
| 127 | + | ||
| 128 | + srs_log_disabled = true; | ||
| 129 | + return false; | ||
| 130 | + } | ||
| 131 | + | ||
| 132 | + return true; | ||
| 133 | + } | ||
| 134 | + | ||
| 86 | function update_play_url() { | 135 | function update_play_url() { |
| 87 | var url = $("#txt_url").val(); | 136 | var url = $("#txt_url").val(); |
| 88 | 137 | ||
| @@ -180,86 +229,44 @@ | @@ -180,86 +229,44 @@ | ||
| 180 | } | 229 | } |
| 181 | }); | 230 | }); |
| 182 | } | 231 | } |
| 183 | - function render_chat_room(chats) { | 232 | + function render_chat_room(original_chats) { |
| 184 | if (!self_chat) { | 233 | if (!self_chat) { |
| 185 | return; | 234 | return; |
| 186 | } | 235 | } |
| 187 | 236 | ||
| 188 | - // new added chat | ||
| 189 | - for (var i = 0; i < chats.length; i++) { | ||
| 190 | - var chat = chats[i]; | 237 | + var chats = []; |
| 238 | + for (var i = 0; i < original_chats.length; i++) { | ||
| 239 | + var chat = original_chats[i]; | ||
| 240 | + | ||
| 191 | // ignore the self. | 241 | // ignore the self. |
| 192 | if (self_chat && self_chat.id == chat.id) { | 242 | if (self_chat && self_chat.id == chat.id) { |
| 193 | continue; | 243 | continue; |
| 194 | } | 244 | } |
| 195 | 245 | ||
| 246 | + chats.push(chat); | ||
| 247 | + } | ||
| 248 | + | ||
| 249 | + // new added chat | ||
| 250 | + for (var i = 0; i < chats.length; i++) { | ||
| 251 | + var chat = chats[i]; | ||
| 252 | + | ||
| 196 | // if previous exists, ignore, only add new here. | 253 | // if previous exists, ignore, only add new here. |
| 197 | var previous_chat = get_previous_chat_user(previous_chats, chat.id); | 254 | var previous_chat = get_previous_chat_user(previous_chats, chat.id); |
| 198 | if (previous_chat) { | 255 | if (previous_chat) { |
| 199 | // update reference. | 256 | // update reference. |
| 257 | + chat.ui = previous_chat.ui; | ||
| 258 | + chat.parent = previous_chat.parent; | ||
| 200 | chat.player = previous_chat.player; | 259 | chat.player = previous_chat.player; |
| 201 | - chat.player.private_object = chat; | 260 | + if (chat.player) { |
| 261 | + chat.player.private_object = chat; | ||
| 262 | + } | ||
| 202 | continue; | 263 | continue; |
| 203 | } | 264 | } |
| 204 | - | ||
| 205 | - global_chat_user_id++; | ||
| 206 | - | ||
| 207 | - // username: a str indicates the user name. | ||
| 208 | - // url: a str indicates the url of user stream. | ||
| 209 | - // join_date: a str indicates the join timestamp in seconds. | ||
| 210 | - // join_date_str: friendly formated time. | ||
| 211 | - var obj = $("<div/>").html($("#template").html()); | ||
| 212 | - $(obj).attr("chat_id", chat.id); | ||
| 213 | - $(obj).attr("id", "div_" + chat.id); // for specifed chat: $("#div_" + chat_id) | ||
| 214 | - $(obj).attr("class", "div_chat"); // for all chats: $(".div_chat") | ||
| 215 | - $(obj).find("#chat_player").attr("id", "rp_" + chat.id); // for specifed player: $("#rp_" + chat_id) | ||
| 216 | - $(obj).find("#chat_player_raw").attr("id", "rp_raw_" + chat.id); // for specifed player: $("#rp_raw_" + chat_id) | ||
| 217 | - $(obj).find("#user_name").text(chat.username); | ||
| 218 | - $(obj).find("#join_date").text(chat.join_date_str); | ||
| 219 | - $(obj).find("#collapseM").attr("id", "collapse_" + global_chat_user_id); | ||
| 220 | - $(obj).find("#headerN").attr("href", "#collapse_" + global_chat_user_id); | ||
| 221 | - | ||
| 222 | - $("#lst_chats").append(obj); | ||
| 223 | - | ||
| 224 | - if (!no_play) { | ||
| 225 | - // start the realtime player. | ||
| 226 | - var _player = new SrsPlayer("rp_raw_" + chat.id, 600, 300, chat); | ||
| 227 | - _player.on_player_ready = function() { | ||
| 228 | - this.set_bt(0.5); | ||
| 229 | - this.set_fs("screen", 100); | ||
| 230 | - }; | ||
| 231 | - _player.start(chat.url); | ||
| 232 | - | ||
| 233 | - chat.player = _player; | ||
| 234 | - } else { | ||
| 235 | - chat.player = null; | ||
| 236 | - } | ||
| 237 | - | ||
| 238 | - $(obj).find("#collapse_main").on('hidden', function(){ | ||
| 239 | - var id = $(this).parent().attr("chat_id"); | ||
| 240 | - var chat = get_previous_chat_user(previous_chats, id); | ||
| 241 | - if (!chat || !chat.player) { | ||
| 242 | - return; | ||
| 243 | - } | ||
| 244 | - chat.player.stop(); | ||
| 245 | - }); | ||
| 246 | - $(obj).find("#collapse_main").on('shown', function(){ | ||
| 247 | - var id = $(this).parent().attr("chat_id"); | ||
| 248 | - var chat = get_previous_chat_user(previous_chats, id); | ||
| 249 | - if (!chat || !chat.player) { | ||
| 250 | - return; | ||
| 251 | - } | ||
| 252 | - chat.player.play(); | ||
| 253 | - }); | ||
| 254 | } | 265 | } |
| 255 | 266 | ||
| 256 | // removed chat | 267 | // removed chat |
| 257 | for (var i = 0; i < previous_chats.length; i++) { | 268 | for (var i = 0; i < previous_chats.length; i++) { |
| 258 | var chat = previous_chats[i]; | 269 | var chat = previous_chats[i]; |
| 259 | - // ignore the self. | ||
| 260 | - if (self_chat && self_chat.id == chat.id) { | ||
| 261 | - continue; | ||
| 262 | - } | ||
| 263 | 270 | ||
| 264 | var new_chat = get_previous_chat_user(chats, chat.id); | 271 | var new_chat = get_previous_chat_user(chats, chat.id); |
| 265 | if (new_chat) { | 272 | if (new_chat) { |
| @@ -272,6 +279,118 @@ | @@ -272,6 +279,118 @@ | ||
| 272 | $("#div_" + chat.id).remove(); | 279 | $("#div_" + chat.id).remove(); |
| 273 | } | 280 | } |
| 274 | 281 | ||
| 282 | + // hide empty rows. | ||
| 283 | + $("#lst_chats").find("tr").each(function(){ | ||
| 284 | + var empty = true; | ||
| 285 | + | ||
| 286 | + $(this).find("td").each(function(){ | ||
| 287 | + if ($(this).html() != "") { | ||
| 288 | + empty = false; | ||
| 289 | + return false; // abort each | ||
| 290 | + } | ||
| 291 | + return true; | ||
| 292 | + }); | ||
| 293 | + | ||
| 294 | + if (empty) { | ||
| 295 | + $(this).hide(); | ||
| 296 | + } | ||
| 297 | + }); | ||
| 298 | + | ||
| 299 | + // render the chat | ||
| 300 | + for (var i = 0; i < chats.length; i++) { | ||
| 301 | + var chat = chats[i]; | ||
| 302 | + | ||
| 303 | + // if redered, ignore. | ||
| 304 | + if (chat.parent) { | ||
| 305 | + continue; | ||
| 306 | + } | ||
| 307 | + | ||
| 308 | + // generate the ui of chat | ||
| 309 | + if (!chat.ui) { | ||
| 310 | + global_chat_user_id++; | ||
| 311 | + | ||
| 312 | + // username: a str indicates the user name. | ||
| 313 | + // url: a str indicates the url of user stream. | ||
| 314 | + // join_date: a str indicates the join timestamp in seconds. | ||
| 315 | + // join_date_str: friendly formated time. | ||
| 316 | + var obj = $("<div/>").html($("#template").html()); | ||
| 317 | + if (true) { | ||
| 318 | + // save current ui object to the chat. | ||
| 319 | + chat.ui = obj; | ||
| 320 | + | ||
| 321 | + $(obj).attr("chat_id", chat.id); | ||
| 322 | + $(obj).attr("id", "div_" + chat.id); // for specifed chat: $("#div_" + chat_id) | ||
| 323 | + $(obj).attr("class", "div_chat"); // for all chats: $(".div_chat") | ||
| 324 | + $(obj).find("#chat_player").attr("id", "rp_" + chat.id); // for specifed player: $("#rp_" + chat_id) | ||
| 325 | + $(obj).find("#chat_player_raw").attr("id", "rp_raw_" + chat.id); // for specifed player: $("#rp_raw_" + chat_id) | ||
| 326 | + $(obj).find("#user_name").text(chat.username); | ||
| 327 | + $(obj).find("#user_player_url").attr("href", chat.url); | ||
| 328 | + $(obj).find("#join_date").text(chat.join_date_str.split(" ")[1]); | ||
| 329 | + $(obj).find("#collapseM").attr("id", "collapse_" + global_chat_user_id); | ||
| 330 | + $(obj).find("#headerN").attr("href", "#collapse_" + global_chat_user_id); | ||
| 331 | + } | ||
| 332 | + } | ||
| 333 | + | ||
| 334 | + // find a idle td to render the chat. | ||
| 335 | + var parent = null; | ||
| 336 | + $("#lst_chats").find("td").each(function(){ | ||
| 337 | + if ($(this).html() != "") { | ||
| 338 | + return true; | ||
| 339 | + } | ||
| 340 | + | ||
| 341 | + parent = $(this); | ||
| 342 | + return false; // abort each | ||
| 343 | + }); | ||
| 344 | + | ||
| 345 | + if (!parent) { | ||
| 346 | + warn("没有可用的位置展示流。"); | ||
| 347 | + break; | ||
| 348 | + } | ||
| 349 | + $(parent).append(chat.ui); | ||
| 350 | + $(parent).parent().show(); | ||
| 351 | + | ||
| 352 | + // when ui elements appent to the page, | ||
| 353 | + // create the player, or flash will failed. | ||
| 354 | + if (!chat.parent) { | ||
| 355 | + chat.parent = $(parent); | ||
| 356 | + | ||
| 357 | + if (!no_play) { | ||
| 358 | + // start the realtime player. | ||
| 359 | + var _player = new SrsPlayer("rp_raw_" + chat.id, 240, 180, chat); | ||
| 360 | + _player.on_player_ready = function() { | ||
| 361 | + this.set_bt(0.5); | ||
| 362 | + this.play(); | ||
| 363 | + }; | ||
| 364 | + _player.on_player_metadata = function(metadata) { | ||
| 365 | + this.set_dar(0, 0); | ||
| 366 | + this.set_fs("screen", 100); | ||
| 367 | + } | ||
| 368 | + _player.start(chat.url); | ||
| 369 | + | ||
| 370 | + chat.player = _player; | ||
| 371 | + } else { | ||
| 372 | + chat.player = null; | ||
| 373 | + } | ||
| 374 | + | ||
| 375 | + $(obj).find("#collapse_main").on('hidden', function(){ | ||
| 376 | + var id = $(this).parent().attr("chat_id"); | ||
| 377 | + var chat = get_previous_chat_user(previous_chats, id); | ||
| 378 | + if (!chat || !chat.player) { | ||
| 379 | + return; | ||
| 380 | + } | ||
| 381 | + chat.player.stop(); | ||
| 382 | + }); | ||
| 383 | + $(obj).find("#collapse_main").on('shown', function(){ | ||
| 384 | + var id = $(this).parent().attr("chat_id"); | ||
| 385 | + var chat = get_previous_chat_user(previous_chats, id); | ||
| 386 | + if (!chat || !chat.player) { | ||
| 387 | + return; | ||
| 388 | + } | ||
| 389 | + chat.player.play(); | ||
| 390 | + }); | ||
| 391 | + } | ||
| 392 | + } | ||
| 393 | + | ||
| 275 | previous_chats = chats; | 394 | previous_chats = chats; |
| 276 | } | 395 | } |
| 277 | function get_previous_chat_user(arr, id) { | 396 | function get_previous_chat_user(arr, id) { |
| @@ -285,7 +404,7 @@ | @@ -285,7 +404,7 @@ | ||
| 285 | } | 404 | } |
| 286 | 405 | ||
| 287 | function on_user_publish() { | 406 | function on_user_publish() { |
| 288 | - if ($("#txt_name").val().trim() == "") { | 407 | + if ($("#txt_name").val() == "") { |
| 289 | $("#txt_name").focus().parent().parent().addClass("error"); | 408 | $("#txt_name").focus().parent().parent().addClass("error"); |
| 290 | warn(100, "请输入您的名字"); | 409 | warn(100, "请输入您的名字"); |
| 291 | return; | 410 | return; |
| @@ -315,10 +434,6 @@ | @@ -315,10 +434,6 @@ | ||
| 315 | // removed chat | 434 | // removed chat |
| 316 | for (var i = 0; i < previous_chats.length; i++) { | 435 | for (var i = 0; i < previous_chats.length; i++) { |
| 317 | var chat = previous_chats[i]; | 436 | var chat = previous_chats[i]; |
| 318 | - // ignore the self. | ||
| 319 | - if (self_chat && self_chat.id == chat.id) { | ||
| 320 | - continue; | ||
| 321 | - } | ||
| 322 | 437 | ||
| 323 | if (chat.player) { | 438 | if (chat.player) { |
| 324 | chat.player.stop(); | 439 | chat.player.stop(); |
| @@ -342,6 +457,10 @@ | @@ -342,6 +457,10 @@ | ||
| 342 | data : "", | 457 | data : "", |
| 343 | dataType : "json", | 458 | dataType : "json", |
| 344 | complete : function() { | 459 | complete : function() { |
| 460 | + if (!on_publish_stop()) { | ||
| 461 | + return; | ||
| 462 | + } | ||
| 463 | + | ||
| 345 | $("#btn_join").attr("disabled", false); | 464 | $("#btn_join").attr("disabled", false); |
| 346 | if (complete_pfn) { | 465 | if (complete_pfn) { |
| 347 | complete_pfn(); | 466 | complete_pfn(); |
| @@ -376,7 +495,7 @@ | @@ -376,7 +495,7 @@ | ||
| 376 | 495 | ||
| 377 | var chat = {}; | 496 | var chat = {}; |
| 378 | chat.id = -1; | 497 | chat.id = -1; |
| 379 | - chat.username = $("#txt_name").val().trim(); | 498 | + chat.username = $("#txt_name").val(); |
| 380 | chat.agent = navigator.userAgent; | 499 | chat.agent = navigator.userAgent; |
| 381 | chat.url = url; | 500 | chat.url = url; |
| 382 | chat.vcodec = vcodec; | 501 | chat.vcodec = vcodec; |
| @@ -414,13 +533,13 @@ | @@ -414,13 +533,13 @@ | ||
| 414 | 533 | ||
| 415 | $("#btn_join").text("退出会议"); | 534 | $("#btn_join").text("退出会议"); |
| 416 | 535 | ||
| 417 | - info("开始推流到服务器"); | 536 | + info("开始推流到服务器。请戴耳机聊天,否则音箱的声音会进入麦克风造成回声。"); |
| 418 | srs_publisher.publish(url, vcodec, acodec); | 537 | srs_publisher.publish(url, vcodec, acodec); |
| 419 | 538 | ||
| 420 | if (realtime_player) { | 539 | if (realtime_player) { |
| 421 | // directly play the url for the realtime player. | 540 | // directly play the url for the realtime player. |
| 422 | realtime_player.stop(); | 541 | realtime_player.stop(); |
| 423 | - realtime_player.play(url); | 542 | + realtime_player.play(url, 0); |
| 424 | } | 543 | } |
| 425 | } | 544 | } |
| 426 | }); | 545 | }); |
| @@ -431,7 +550,7 @@ | @@ -431,7 +550,7 @@ | ||
| 431 | <div class="navbar navbar-fixed-top"> | 550 | <div class="navbar navbar-fixed-top"> |
| 432 | <div class="navbar-inner"> | 551 | <div class="navbar-inner"> |
| 433 | <div class="container"> | 552 | <div class="container"> |
| 434 | - <a class="brand" href="index.html">SRS</a> | 553 | + <a id="srs_index" class="brand" href="#">SRS</a> |
| 435 | <div class="nav-collapse collapse"> | 554 | <div class="nav-collapse collapse"> |
| 436 | <ul class="nav"> | 555 | <ul class="nav"> |
| 437 | <li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li> | 556 | <li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li> |
| @@ -451,21 +570,21 @@ | @@ -451,21 +570,21 @@ | ||
| 451 | <div class="alert alert-info fade in" id="txt_log"> | 570 | <div class="alert alert-info fade in" id="txt_log"> |
| 452 | <button type="button" class="close" data-dismiss="alert">×</button> | 571 | <button type="button" class="close" data-dismiss="alert">×</button> |
| 453 | <strong><span id="txt_log_title">Usage:</span></strong> | 572 | <strong><span id="txt_log_title">Usage:</span></strong> |
| 454 | - <span id="txt_log_msg">输入名字,设置编码参数后,加入会议室</span> | 573 | + <span id="txt_log_msg">输入名字,设点“加入会议”按钮</span> |
| 455 | </div> | 574 | </div> |
| 456 | 575 | ||
| 457 | <div class="control-group"> | 576 | <div class="control-group"> |
| 458 | <div class="form-inline"> | 577 | <div class="form-inline"> |
| 459 | - <input type="text" id="txt_name" class="input-small" placeholder="您的名字..." value=""></input> | ||
| 460 | - <button class="btn input-medium" id="btn_video_settings">视频编码配置</button> | ||
| 461 | - <button class="btn input-medium" id="btn_audio_settings">音频编码配置</button> | ||
| 462 | - <button class="btn input-medium btn-primary" id="btn_join">加入会议</button> | 578 | + <button class="btn input-small" id="btn_video_settings">摄像头</button> |
| 579 | + <button class="btn input-small" id="btn_audio_settings">麦克风</button> | ||
| 580 | + <input type="text" id="txt_name" class="input-large" placeholder="请输入您的名字..." value=""></input> | ||
| 581 | + <button class="btn input-small" id="btn_join">加入会议</button> | ||
| 463 | <input type="text" id="txt_url" class="input-mini hide" value=""></input> | 582 | <input type="text" id="txt_url" class="input-mini hide" value=""></input> |
| 464 | </div> | 583 | </div> |
| 465 | </div> | 584 | </div> |
| 466 | - <div class="container"> | ||
| 467 | - <div class="row-fluid"> | ||
| 468 | - <div class="span6"> | 585 | + <table id="lst_chats" class="table"> |
| 586 | + <tr> | ||
| 587 | + <td id="td_0"> | ||
| 469 | <div class="accordion-group"> | 588 | <div class="accordion-group"> |
| 470 | <div class="accordion-heading"> | 589 | <div class="accordion-heading"> |
| 471 | <span class="accordion-toggle"> | 590 | <span class="accordion-toggle"> |
| @@ -478,8 +597,8 @@ | @@ -478,8 +597,8 @@ | ||
| 478 | </div> | 597 | </div> |
| 479 | </div> | 598 | </div> |
| 480 | </div> | 599 | </div> |
| 481 | - </div> | ||
| 482 | - <div class="span6"> | 600 | + </td> |
| 601 | + <td id="td_1"> | ||
| 483 | <div class="accordion-group"> | 602 | <div class="accordion-group"> |
| 484 | <div class="accordion-heading"> | 603 | <div class="accordion-heading"> |
| 485 | <span class="accordion-toggle"> | 604 | <span class="accordion-toggle"> |
| @@ -495,38 +614,33 @@ | @@ -495,38 +614,33 @@ | ||
| 495 | </div> | 614 | </div> |
| 496 | </div> | 615 | </div> |
| 497 | </div> | 616 | </div> |
| 498 | - </div> | ||
| 499 | - </div> | ||
| 500 | - </div> | 617 | + </td> |
| 618 | + <td id="td_2"></td> | ||
| 619 | + </tr> | ||
| 620 | + </table> | ||
| 501 | <div class="container hide" id="template"> | 621 | <div class="container hide" id="template"> |
| 502 | <div class="accordion-group" id="collapse_main"> | 622 | <div class="accordion-group" id="collapse_main"> |
| 503 | <div class="accordion-heading" title="点击展开或收起,收起后停止播放流,展开时从服务器请求流"> | 623 | <div class="accordion-heading" title="点击展开或收起,收起后停止播放流,展开时从服务器请求流"> |
| 504 | <span id="headerN" class="accordion-toggle" data-toggle="collapse" href="#collapseN"> | 624 | <span id="headerN" class="accordion-toggle" data-toggle="collapse" href="#collapseN"> |
| 505 | <strong>[<a href="#"><span id="user_name">XX</span></a>]</strong> | 625 | <strong>[<a href="#"><span id="user_name">XX</span></a>]</strong> |
| 506 | <strong>加入时间</strong>[<span id="join_date"></span>] | 626 | <strong>加入时间</strong>[<span id="join_date"></span>] |
| 507 | - <img src="img/tooltip.png"/> | 627 | + <a id="user_player_url" href="#" data-toggle="tooltip" data-placement="top" title=""> |
| 628 | + 播放地址<img src="img/tooltip.png"/> | ||
| 629 | + </a> | ||
| 508 | </span> | 630 | </span> |
| 509 | </div> | 631 | </div> |
| 510 | - <div id="collapseM" class="accordion-body collapse"> | 632 | + <div id="collapseM" class="accordion-body collapse in"> |
| 511 | <div class="accordion-inner"> | 633 | <div class="accordion-inner"> |
| 512 | - <div class="row-fluid"> | ||
| 513 | - <div class="span2"> | ||
| 514 | - </div> | ||
| 515 | - <div class="span8"> | ||
| 516 | - <div id="chat_player"> | ||
| 517 | - <div id="chat_player_raw"> | ||
| 518 | - </div> | ||
| 519 | - </div> | ||
| 520 | - </div> | ||
| 521 | - <div class="span2"> | 634 | + <div id="chat_player"> |
| 635 | + <div id="chat_player_raw"> | ||
| 522 | </div> | 636 | </div> |
| 523 | </div> | 637 | </div> |
| 524 | </div> | 638 | </div> |
| 525 | </div> | 639 | </div> |
| 526 | </div> | 640 | </div> |
| 527 | </div> | 641 | </div> |
| 528 | - <div class="container" id="lst_chats"> | ||
| 529 | - </div> | 642 | + <table class="table"> |
| 643 | + </table> | ||
| 530 | <div id="video_modal" class="modal hide fade"> | 644 | <div id="video_modal" class="modal hide fade"> |
| 531 | <div class="modal-header"> | 645 | <div class="modal-header"> |
| 532 | <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> | 646 | <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> |
| @@ -7,7 +7,12 @@ | @@ -7,7 +7,12 @@ | ||
| 7 | <script type="text/javascript" src="js/jquery-1.10.2.min.js"></script> | 7 | <script type="text/javascript" src="js/jquery-1.10.2.min.js"></script> |
| 8 | <script type="text/javascript" src="js/bootstrap.min.js"></script> | 8 | <script type="text/javascript" src="js/bootstrap.min.js"></script> |
| 9 | <script type="text/javascript" src="js/swfobject.js"></script> | 9 | <script type="text/javascript" src="js/swfobject.js"></script> |
| 10 | - <script type="text/javascript" src="js/srs.js"></script> | 10 | + <script type="text/javascript" src="js/json2.js"></script> |
| 11 | + <script type="text/javascript" src="js/srs.page.js"></script> | ||
| 12 | + <script type="text/javascript" src="js/srs.log.js"></script> | ||
| 13 | + <script type="text/javascript" src="js/srs.player.js"></script> | ||
| 14 | + <script type="text/javascript" src="js/srs.publisher.js"></script> | ||
| 15 | + <script type="text/javascript" src="js/srs.utility.js"></script> | ||
| 11 | <style> | 16 | <style> |
| 12 | body{ | 17 | body{ |
| 13 | padding-top: 55px; | 18 | padding-top: 55px; |
| @@ -30,7 +35,7 @@ | @@ -30,7 +35,7 @@ | ||
| 30 | 35 | ||
| 31 | var __active_dar = null; | 36 | var __active_dar = null; |
| 32 | function select_dar(dar_id, num, den) { | 37 | function select_dar(dar_id, num, den) { |
| 33 | - srs_player.dar(num, den); | 38 | + srs_player.set_dar(num, den); |
| 34 | 39 | ||
| 35 | if (__active_dar) { | 40 | if (__active_dar) { |
| 36 | __active_dar.removeClass("active"); | 41 | __active_dar.removeClass("active"); |
| @@ -191,13 +196,13 @@ | @@ -191,13 +196,13 @@ | ||
| 191 | select_dar("#btn_dar_original", 0, 0); | 196 | select_dar("#btn_dar_original", 0, 0); |
| 192 | }); | 197 | }); |
| 193 | $("#btn_dar_21_9").click(function(){ | 198 | $("#btn_dar_21_9").click(function(){ |
| 194 | - select_dar("#btn_dar_21_9", 9, 21); | 199 | + select_dar("#btn_dar_21_9", 21, 9); |
| 195 | }); | 200 | }); |
| 196 | $("#btn_dar_16_9").click(function(){ | 201 | $("#btn_dar_16_9").click(function(){ |
| 197 | - select_dar("#btn_dar_16_9", 9, 16); | 202 | + select_dar("#btn_dar_16_9", 16, 9); |
| 198 | }); | 203 | }); |
| 199 | $("#btn_dar_4_3").click(function(){ | 204 | $("#btn_dar_4_3").click(function(){ |
| 200 | - select_dar("#btn_dar_4_3", 3, 4); | 205 | + select_dar("#btn_dar_4_3", 4, 3); |
| 201 | }); | 206 | }); |
| 202 | $("#btn_dar_fill").click(function(){ | 207 | $("#btn_dar_fill").click(function(){ |
| 203 | select_dar("#btn_dar_fill", -1, -1); | 208 | select_dar("#btn_dar_fill", -1, -1); |
| @@ -264,7 +269,7 @@ | @@ -264,7 +269,7 @@ | ||
| 264 | <div class="navbar navbar-fixed-top"> | 269 | <div class="navbar navbar-fixed-top"> |
| 265 | <div class="navbar-inner"> | 270 | <div class="navbar-inner"> |
| 266 | <div class="container"> | 271 | <div class="container"> |
| 267 | - <a class="brand" href="index.html">SRS</a> | 272 | + <a id="srs_index" class="brand" href="#">SRS</a> |
| 268 | <div class="nav-collapse collapse"> | 273 | <div class="nav-collapse collapse"> |
| 269 | <ul class="nav"> | 274 | <ul class="nav"> |
| 270 | <li class="active"><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li> | 275 | <li class="active"><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li> |
| @@ -10,6 +10,7 @@ package | @@ -10,6 +10,7 @@ package | ||
| 10 | import flash.events.NetStatusEvent; | 10 | import flash.events.NetStatusEvent; |
| 11 | import flash.events.TimerEvent; | 11 | import flash.events.TimerEvent; |
| 12 | import flash.external.ExternalInterface; | 12 | import flash.external.ExternalInterface; |
| 13 | + import flash.media.SoundTransform; | ||
| 13 | import flash.media.Video; | 14 | import flash.media.Video; |
| 14 | import flash.net.NetConnection; | 15 | import flash.net.NetConnection; |
| 15 | import flash.net.NetStream; | 16 | import flash.net.NetStream; |
| @@ -19,6 +20,8 @@ package | @@ -19,6 +20,8 @@ package | ||
| 19 | import flash.utils.Timer; | 20 | import flash.utils.Timer; |
| 20 | import flash.utils.setTimeout; | 21 | import flash.utils.setTimeout; |
| 21 | 22 | ||
| 23 | + import flashx.textLayout.formats.Float; | ||
| 24 | + | ||
| 22 | public class srs_player extends Sprite | 25 | public class srs_player extends Sprite |
| 23 | { | 26 | { |
| 24 | // user set id. | 27 | // user set id. |
| @@ -34,8 +37,8 @@ package | @@ -34,8 +37,8 @@ package | ||
| 34 | private var user_w:int = 0; | 37 | private var user_w:int = 0; |
| 35 | private var user_h:int = 0; | 38 | private var user_h:int = 0; |
| 36 | // user set dar den:num | 39 | // user set dar den:num |
| 37 | - private var user_dar_num:int = 0; | ||
| 38 | private var user_dar_den:int = 0; | 40 | private var user_dar_den:int = 0; |
| 41 | + private var user_dar_num:int = 0; | ||
| 39 | // user set fs(fullscreen) refer and percent. | 42 | // user set fs(fullscreen) refer and percent. |
| 40 | private var user_fs_refer:String = null; | 43 | private var user_fs_refer:String = null; |
| 41 | private var user_fs_percent:int = 0; | 44 | private var user_fs_percent:int = 0; |
| @@ -112,7 +115,7 @@ package | @@ -112,7 +115,7 @@ package | ||
| 112 | flash.external.ExternalInterface.addCallback("__stop", this.js_call_stop); | 115 | flash.external.ExternalInterface.addCallback("__stop", this.js_call_stop); |
| 113 | flash.external.ExternalInterface.addCallback("__pause", this.js_call_pause); | 116 | flash.external.ExternalInterface.addCallback("__pause", this.js_call_pause); |
| 114 | flash.external.ExternalInterface.addCallback("__resume", this.js_call_resume); | 117 | flash.external.ExternalInterface.addCallback("__resume", this.js_call_resume); |
| 115 | - flash.external.ExternalInterface.addCallback("__dar", this.js_call_dar); | 118 | + flash.external.ExternalInterface.addCallback("__set_dar", this.js_call_set_dar); |
| 116 | flash.external.ExternalInterface.addCallback("__set_fs", this.js_call_set_fs_size); | 119 | flash.external.ExternalInterface.addCallback("__set_fs", this.js_call_set_fs_size); |
| 117 | flash.external.ExternalInterface.addCallback("__set_bt", this.js_call_set_bt); | 120 | flash.external.ExternalInterface.addCallback("__set_bt", this.js_call_set_bt); |
| 118 | 121 | ||
| @@ -218,15 +221,15 @@ package | @@ -218,15 +221,15 @@ package | ||
| 218 | } | 221 | } |
| 219 | 222 | ||
| 220 | /** | 223 | /** |
| 221 | - * to set the DAR, for example, DAR=16:9 | ||
| 222 | - * @param num, for example, 9. | ||
| 223 | - * use metadata height if 0. | ||
| 224 | - * use user specified height if -1. | ||
| 225 | - * @param den, for example, 16. | ||
| 226 | - * use metadata width if 0. | ||
| 227 | - * use user specified width if -1. | 224 | + * to set the DAR, for example, DAR=16:9 where num=16,den=9. |
| 225 | + * @param num, for example, 16. | ||
| 226 | + * use metadata width if 0. | ||
| 227 | + * use user specified width if -1. | ||
| 228 | + * @param den, for example, 9. | ||
| 229 | + * use metadata height if 0. | ||
| 230 | + * use user specified height if -1. | ||
| 228 | */ | 231 | */ |
| 229 | - private function js_call_dar(num:int, den:int):void { | 232 | + private function js_call_set_dar(num:int, den:int):void { |
| 230 | user_dar_num = num; | 233 | user_dar_num = num; |
| 231 | user_dar_den = den; | 234 | user_dar_den = den; |
| 232 | 235 | ||
| @@ -280,8 +283,9 @@ package | @@ -280,8 +283,9 @@ package | ||
| 280 | * @param _width, the player width. | 283 | * @param _width, the player width. |
| 281 | * @param _height, the player height. | 284 | * @param _height, the player height. |
| 282 | * @param buffer_time, the buffer time in seconds. recommend to >=0.5 | 285 | * @param buffer_time, the buffer time in seconds. recommend to >=0.5 |
| 286 | + * @param volume, the volume, 0 is mute, 1 is 100%, 2 is 200%. | ||
| 283 | */ | 287 | */ |
| 284 | - private function js_call_play(url:String, _width:int, _height:int, buffer_time:Number):void { | 288 | + private function js_call_play(url:String, _width:int, _height:int, buffer_time:Number, volume:Number):void { |
| 285 | this.user_url = url; | 289 | this.user_url = url; |
| 286 | this.user_w = _width; | 290 | this.user_w = _width; |
| 287 | this.user_h = _height; | 291 | this.user_h = _height; |
| @@ -313,6 +317,7 @@ package | @@ -313,6 +317,7 @@ package | ||
| 313 | } | 317 | } |
| 314 | 318 | ||
| 315 | media_stream = new NetStream(media_conn); | 319 | media_stream = new NetStream(media_conn); |
| 320 | + media_stream.soundTransform = new SoundTransform(volume); | ||
| 316 | media_stream.bufferTime = buffer_time; | 321 | media_stream.bufferTime = buffer_time; |
| 317 | media_stream.client = {}; | 322 | media_stream.client = {}; |
| 318 | media_stream.client.onMetaData = system_on_metadata; | 323 | media_stream.client.onMetaData = system_on_metadata; |
| @@ -397,21 +402,21 @@ package | @@ -397,21 +402,21 @@ package | ||
| 397 | var obj:Object = __get_video_size_object(); | 402 | var obj:Object = __get_video_size_object(); |
| 398 | 403 | ||
| 399 | // get the DAR | 404 | // get the DAR |
| 400 | - var num:int = user_dar_num; | ||
| 401 | var den:int = user_dar_den; | 405 | var den:int = user_dar_den; |
| 406 | + var num:int = user_dar_num; | ||
| 402 | 407 | ||
| 403 | - if (num == 0) { | ||
| 404 | - num = obj.height; | 408 | + if (den == 0) { |
| 409 | + den = obj.height; | ||
| 405 | } | 410 | } |
| 406 | - if (num == -1) { | ||
| 407 | - num = this.stage.fullScreenHeight; | 411 | + if (den == -1) { |
| 412 | + den = this.stage.fullScreenHeight; | ||
| 408 | } | 413 | } |
| 409 | 414 | ||
| 410 | - if (den == 0) { | ||
| 411 | - den = obj.width; | 415 | + if (num == 0) { |
| 416 | + num = obj.width; | ||
| 412 | } | 417 | } |
| 413 | - if (den == -1) { | ||
| 414 | - den = this.stage.fullScreenWidth; | 418 | + if (num == -1) { |
| 419 | + num = this.stage.fullScreenWidth; | ||
| 415 | } | 420 | } |
| 416 | 421 | ||
| 417 | // for refer is screen. | 422 | // for refer is screen. |
| @@ -431,23 +436,23 @@ package | @@ -431,23 +436,23 @@ package | ||
| 431 | */ | 436 | */ |
| 432 | private function __execute_user_set_dar():void { | 437 | private function __execute_user_set_dar():void { |
| 433 | // get the DAR | 438 | // get the DAR |
| 434 | - var num:int = user_dar_num; | ||
| 435 | var den:int = user_dar_den; | 439 | var den:int = user_dar_den; |
| 440 | + var num:int = user_dar_num; | ||
| 436 | 441 | ||
| 437 | var obj:Object = __get_video_size_object(); | 442 | var obj:Object = __get_video_size_object(); |
| 438 | 443 | ||
| 439 | - if (num == 0) { | ||
| 440 | - num = obj.height; | 444 | + if (den == 0) { |
| 445 | + den = obj.height; | ||
| 441 | } | 446 | } |
| 442 | - if (num == -1) { | ||
| 443 | - num = this.user_h; | 447 | + if (den == -1) { |
| 448 | + den = this.user_h; | ||
| 444 | } | 449 | } |
| 445 | 450 | ||
| 446 | - if (den == 0) { | ||
| 447 | - den = obj.width; | 451 | + if (num == 0) { |
| 452 | + num = obj.width; | ||
| 448 | } | 453 | } |
| 449 | - if (den == -1) { | ||
| 450 | - den = this.user_w; | 454 | + if (num == -1) { |
| 455 | + num = this.user_w; | ||
| 451 | } | 456 | } |
| 452 | 457 | ||
| 453 | __update_video_size(num, den, this.user_w, this.user_h, this.user_w, this.user_h); | 458 | __update_video_size(num, den, this.user_w, this.user_h, this.user_w, this.user_h); |
| @@ -463,19 +468,19 @@ package | @@ -463,19 +468,19 @@ package | ||
| 463 | * @param _sw/_wh the stage size, >= paper size. used to center the player. | 468 | * @param _sw/_wh the stage size, >= paper size. used to center the player. |
| 464 | */ | 469 | */ |
| 465 | private function __update_video_size(_num:int, _den:int, _w:int, _h:int, _sw:int, _sh:int):void { | 470 | private function __update_video_size(_num:int, _den:int, _w:int, _h:int, _sw:int, _sh:int):void { |
| 466 | - if (!this.media_video || _num <= 0 || _den <= 0) { | 471 | + if (!this.media_video || _den <= 0 || _num <= 0) { |
| 467 | return; | 472 | return; |
| 468 | } | 473 | } |
| 469 | 474 | ||
| 470 | // set DAR. | 475 | // set DAR. |
| 471 | // calc the height by DAR | 476 | // calc the height by DAR |
| 472 | - var _height:int = _w * _num / _den; | 477 | + var _height:int = _w * _den / _num; |
| 473 | if (_height <= _h) { | 478 | if (_height <= _h) { |
| 474 | this.media_video.width = _w; | 479 | this.media_video.width = _w; |
| 475 | this.media_video.height = _height; | 480 | this.media_video.height = _height; |
| 476 | } else { | 481 | } else { |
| 477 | // height overflow, calc the width by DAR | 482 | // height overflow, calc the width by DAR |
| 478 | - var _width:int = _h * _den / _num; | 483 | + var _width:int = _h * _num / _den; |
| 479 | 484 | ||
| 480 | this.media_video.width = _width; | 485 | this.media_video.width = _width; |
| 481 | this.media_video.height = _h; | 486 | this.media_video.height = _h; |
| @@ -7,7 +7,12 @@ | @@ -7,7 +7,12 @@ | ||
| 7 | <script type="text/javascript" src="js/jquery-1.10.2.min.js"></script> | 7 | <script type="text/javascript" src="js/jquery-1.10.2.min.js"></script> |
| 8 | <script type="text/javascript" src="js/bootstrap.min.js"></script> | 8 | <script type="text/javascript" src="js/bootstrap.min.js"></script> |
| 9 | <script type="text/javascript" src="js/swfobject.js"></script> | 9 | <script type="text/javascript" src="js/swfobject.js"></script> |
| 10 | - <script type="text/javascript" src="js/srs.js"></script> | 10 | + <script type="text/javascript" src="js/json2.js"></script> |
| 11 | + <script type="text/javascript" src="js/srs.page.js"></script> | ||
| 12 | + <script type="text/javascript" src="js/srs.log.js"></script> | ||
| 13 | + <script type="text/javascript" src="js/srs.player.js"></script> | ||
| 14 | + <script type="text/javascript" src="js/srs.publisher.js"></script> | ||
| 15 | + <script type="text/javascript" src="js/srs.utility.js"></script> | ||
| 11 | <style> | 16 | <style> |
| 12 | body{ | 17 | body{ |
| 13 | padding-top: 55px; | 18 | padding-top: 55px; |
| @@ -18,12 +23,18 @@ | @@ -18,12 +23,18 @@ | ||
| 18 | var remote_player = null; | 23 | var remote_player = null; |
| 19 | var realtime_player = null; | 24 | var realtime_player = null; |
| 20 | 25 | ||
| 26 | + var query = parse_query_string(); | ||
| 21 | $(function(){ | 27 | $(function(){ |
| 22 | // get the vhost and port to set the default url. | 28 | // get the vhost and port to set the default url. |
| 23 | // for example: http://192.168.1.213/players/jwplayer6.html?port=1935&vhost=demo | 29 | // for example: http://192.168.1.213/players/jwplayer6.html?port=1935&vhost=demo |
| 24 | // url set to: rtmp://demo:1935/live/livestream | 30 | // url set to: rtmp://demo:1935/live/livestream |
| 25 | srs_init("#txt_url", null, null); | 31 | srs_init("#txt_url", null, null); |
| 26 | 32 | ||
| 33 | + if (query.agent == "true") { | ||
| 34 | + document.write(navigator.userAgent); | ||
| 35 | + return; | ||
| 36 | + } | ||
| 37 | + | ||
| 27 | $("#btn_video_settings").click(function(){ | 38 | $("#btn_video_settings").click(function(){ |
| 28 | $("#video_modal").modal({show:true}); | 39 | $("#video_modal").modal({show:true}); |
| 29 | }); | 40 | }); |
| @@ -60,7 +71,10 @@ | @@ -60,7 +71,10 @@ | ||
| 60 | ); | 71 | ); |
| 61 | }; | 72 | }; |
| 62 | srs_publisher.on_publisher_error = function(code, desc) { | 73 | srs_publisher.on_publisher_error = function(code, desc) { |
| 63 | - error(code, desc); | 74 | + if (!on_publish_stop()) { |
| 75 | + return; | ||
| 76 | + } | ||
| 77 | + error(code, desc + "请重试。"); | ||
| 64 | }; | 78 | }; |
| 65 | srs_publisher.on_publisher_warn = function(code, desc) { | 79 | srs_publisher.on_publisher_warn = function(code, desc) { |
| 66 | warn(code, desc); | 80 | warn(code, desc); |
| @@ -70,26 +84,43 @@ | @@ -70,26 +84,43 @@ | ||
| 70 | update_play_url(); | 84 | update_play_url(); |
| 71 | 85 | ||
| 72 | // if no play specified, donot show the player, for debug the publisher. | 86 | // if no play specified, donot show the player, for debug the publisher. |
| 73 | - var query = parse_query_string(); | ||
| 74 | if (query.no_play != "true") { | 87 | if (query.no_play != "true") { |
| 75 | // start the normal player with HLS supported. | 88 | // start the normal player with HLS supported. |
| 76 | remote_player = new SrsPlayer("remote_player", 430, 185); | 89 | remote_player = new SrsPlayer("remote_player", 430, 185); |
| 77 | remote_player.on_player_ready = function() { | 90 | remote_player.on_player_ready = function() { |
| 78 | this.set_bt(0.8); | 91 | this.set_bt(0.8); |
| 79 | - this.set_fs("screen", 100); | ||
| 80 | }; | 92 | }; |
| 93 | + remote_player.on_player_metadata = function(metadata) { | ||
| 94 | + this.set_dar(0, 0); | ||
| 95 | + this.set_fs("screen", 100); | ||
| 96 | + } | ||
| 81 | remote_player.start(); | 97 | remote_player.start(); |
| 82 | 98 | ||
| 83 | // start the realtime player. | 99 | // start the realtime player. |
| 84 | realtime_player = new SrsPlayer("realtime_player", 430, 185); | 100 | realtime_player = new SrsPlayer("realtime_player", 430, 185); |
| 85 | realtime_player.on_player_ready = function() { | 101 | realtime_player.on_player_ready = function() { |
| 86 | this.set_bt(0.8); | 102 | this.set_bt(0.8); |
| 87 | - this.set_fs("screen", 100); | ||
| 88 | }; | 103 | }; |
| 104 | + realtime_player.on_player_metadata = function(metadata) { | ||
| 105 | + this.set_dar(0, 0); | ||
| 106 | + this.set_fs("screen", 100); | ||
| 107 | + } | ||
| 89 | realtime_player.start(); | 108 | realtime_player.start(); |
| 90 | } | 109 | } |
| 91 | }); | 110 | }); |
| 92 | 111 | ||
| 112 | + function on_publish_stop() { | ||
| 113 | + if (!srs_can_republish()) { | ||
| 114 | + $("#btn_join").attr("disabled", true); | ||
| 115 | + error(0, "您使用的浏览器很弱,请关闭页面后重新打开页面(刷新也不管用)。<br/>推荐使用Chrome/Firefox/Safari/Opera浏览器,支持重试"); | ||
| 116 | + | ||
| 117 | + srs_log_disabled = true; | ||
| 118 | + return false; | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + return true; | ||
| 122 | + } | ||
| 123 | + | ||
| 93 | /** | 124 | /** |
| 94 | * we generate the transcoded stream url for flash publish donot support HLS | 125 | * we generate the transcoded stream url for flash publish donot support HLS |
| 95 | * which requires aac, so the publish vhost maybe players for example, we | 126 | * which requires aac, so the publish vhost maybe players for example, we |
| @@ -101,7 +132,6 @@ | @@ -101,7 +132,6 @@ | ||
| 101 | function update_play_url() { | 132 | function update_play_url() { |
| 102 | var url = $("#txt_url").val(); | 133 | var url = $("#txt_url").val(); |
| 103 | var ret = srs_parse_rtmp_url(url); | 134 | var ret = srs_parse_rtmp_url(url); |
| 104 | - var query = parse_query_string(); | ||
| 105 | 135 | ||
| 106 | var remote_url = "rtmp://" + ret.server + ":" + ret.port + "/" + ret.app + "...vhost..." + srs_get_player_publish_vhost(ret.vhost) + "/" + ret.stream; | 136 | var remote_url = "rtmp://" + ret.server + ":" + ret.port + "/" + ret.app + "...vhost..." + srs_get_player_publish_vhost(ret.vhost) + "/" + ret.stream; |
| 107 | $("#realtime_player_url").attr("href", url).attr("target", "_blank"); | 137 | $("#realtime_player_url").attr("href", url).attr("target", "_blank"); |
| @@ -136,6 +166,10 @@ | @@ -136,6 +166,10 @@ | ||
| 136 | //$("#remote_player_url").attr("href", "#").attr("target", "_self"); | 166 | //$("#remote_player_url").attr("href", "#").attr("target", "_self"); |
| 137 | //$("#txt_play_hls").text("HLS-m3u8(请发布视频)").attr("href", "#").attr("target", "_self"); | 167 | //$("#txt_play_hls").text("HLS-m3u8(请发布视频)").attr("href", "#").attr("target", "_self"); |
| 138 | //$("#txt_play_jwplayer").text("HLS-JWPlayer(请发布视频)").attr("href", "#").attr("target", "_self"); | 168 | //$("#txt_play_jwplayer").text("HLS-JWPlayer(请发布视频)").attr("href", "#").attr("target", "_self"); |
| 169 | + | ||
| 170 | + if (!on_publish_stop()) { | ||
| 171 | + return; | ||
| 172 | + } | ||
| 139 | return; | 173 | return; |
| 140 | } | 174 | } |
| 141 | 175 | ||
| @@ -180,7 +214,7 @@ | @@ -180,7 +214,7 @@ | ||
| 180 | <div class="navbar navbar-fixed-top"> | 214 | <div class="navbar navbar-fixed-top"> |
| 181 | <div class="navbar-inner"> | 215 | <div class="navbar-inner"> |
| 182 | <div class="container"> | 216 | <div class="container"> |
| 183 | - <a class="brand" href="index.html">SRS</a> | 217 | + <a id="srs_index" class="brand" href="#">SRS</a> |
| 184 | <div class="nav-collapse collapse"> | 218 | <div class="nav-collapse collapse"> |
| 185 | <ul class="nav"> | 219 | <ul class="nav"> |
| 186 | <li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li> | 220 | <li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li> |
| @@ -10,9 +10,14 @@ package | @@ -10,9 +10,14 @@ package | ||
| 10 | import flash.media.H264Profile; | 10 | import flash.media.H264Profile; |
| 11 | import flash.media.H264VideoStreamSettings; | 11 | import flash.media.H264VideoStreamSettings; |
| 12 | import flash.media.Microphone; | 12 | import flash.media.Microphone; |
| 13 | + import flash.media.MicrophoneEnhancedMode; | ||
| 14 | + import flash.media.MicrophoneEnhancedOptions; | ||
| 15 | + import flash.media.SoundCodec; | ||
| 13 | import flash.media.Video; | 16 | import flash.media.Video; |
| 14 | import flash.net.NetConnection; | 17 | import flash.net.NetConnection; |
| 15 | import flash.net.NetStream; | 18 | import flash.net.NetStream; |
| 19 | + import flash.system.Security; | ||
| 20 | + import flash.system.SecurityPanel; | ||
| 16 | import flash.ui.ContextMenu; | 21 | import flash.ui.ContextMenu; |
| 17 | import flash.ui.ContextMenuItem; | 22 | import flash.ui.ContextMenuItem; |
| 18 | import flash.utils.setTimeout; | 23 | import flash.utils.setTimeout; |
| @@ -45,6 +50,8 @@ package | @@ -45,6 +50,8 @@ package | ||
| 45 | private const error_camera_get:int = 100; | 50 | private const error_camera_get:int = 100; |
| 46 | private const error_microphone_get:int = 101; | 51 | private const error_microphone_get:int = 101; |
| 47 | private const error_camera_muted:int = 102; | 52 | private const error_camera_muted:int = 102; |
| 53 | + private const error_connection_closed:int = 103; | ||
| 54 | + private const error_connection_failed:int = 104; | ||
| 48 | 55 | ||
| 49 | public function srs_publisher() | 56 | public function srs_publisher() |
| 50 | { | 57 | { |
| @@ -79,6 +86,18 @@ package | @@ -79,6 +86,18 @@ package | ||
| 79 | this.js_on_publisher_error = flashvars.on_publisher_error; | 86 | this.js_on_publisher_error = flashvars.on_publisher_error; |
| 80 | this.js_on_publisher_warn = flashvars.on_publisher_warn; | 87 | this.js_on_publisher_warn = flashvars.on_publisher_warn; |
| 81 | 88 | ||
| 89 | + // initialized size. | ||
| 90 | + this.user_w = flashvars.width; | ||
| 91 | + this.user_h = flashvars.height; | ||
| 92 | + | ||
| 93 | + // try to get the camera, if muted, alert the security and requires user to open it. | ||
| 94 | + var c:Camera = Camera.getCamera(); | ||
| 95 | + if (c.muted) { | ||
| 96 | + Security.showSettings(SecurityPanel.PRIVACY); | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + __show_local_camera(c); | ||
| 100 | + | ||
| 82 | flash.utils.setTimeout(this.system_on_js_ready, 0); | 101 | flash.utils.setTimeout(this.system_on_js_ready, 0); |
| 83 | } | 102 | } |
| 84 | 103 | ||
| @@ -135,27 +154,31 @@ package | @@ -135,27 +154,31 @@ package | ||
| 135 | this.js_call_stop(); | 154 | this.js_call_stop(); |
| 136 | 155 | ||
| 137 | // microphone and camera | 156 | // microphone and camera |
| 138 | - var m:Microphone = Microphone.getMicrophone(acodec.device_code); | ||
| 139 | - if(m == null){ | 157 | + var microphone:Microphone = null; |
| 158 | + //microphone = Microphone.getEnhancedMicrophone(acodec.device_code); | ||
| 159 | + if (!microphone) { | ||
| 160 | + microphone = Microphone.getMicrophone(acodec.device_code); | ||
| 161 | + } | ||
| 162 | + if(microphone == null){ | ||
| 140 | this.system_error(this.error_microphone_get, "failed to open microphone " + acodec.device_code + "(" + acodec.device_name + ")"); | 163 | this.system_error(this.error_microphone_get, "failed to open microphone " + acodec.device_code + "(" + acodec.device_name + ")"); |
| 141 | return; | 164 | return; |
| 142 | } | 165 | } |
| 143 | // ignore muted, for flash will require user to access it. | 166 | // ignore muted, for flash will require user to access it. |
| 144 | 167 | ||
| 145 | // Remark: the name is the index! | 168 | // Remark: the name is the index! |
| 146 | - var c:Camera = Camera.getCamera(vcodec.device_code); | ||
| 147 | - if(c == null){ | 169 | + var camera:Camera = Camera.getCamera(vcodec.device_code); |
| 170 | + if(camera == null){ | ||
| 148 | this.system_error(this.error_camera_get, "failed to open camera " + vcodec.device_code + "(" + vcodec.device_name + ")"); | 171 | this.system_error(this.error_camera_get, "failed to open camera " + vcodec.device_code + "(" + vcodec.device_name + ")"); |
| 149 | return; | 172 | return; |
| 150 | } | 173 | } |
| 151 | // ignore muted, for flash will require user to access it. | 174 | // ignore muted, for flash will require user to access it. |
| 152 | // but we still warn user. | 175 | // but we still warn user. |
| 153 | - if(c && c.muted){ | 176 | + if(camera && camera.muted){ |
| 154 | this.system_warn(this.error_camera_muted, "Access Denied, camera " + vcodec.device_code + "(" + vcodec.device_name + ") is muted"); | 177 | this.system_warn(this.error_camera_muted, "Access Denied, camera " + vcodec.device_code + "(" + vcodec.device_name + ") is muted"); |
| 155 | } | 178 | } |
| 156 | 179 | ||
| 157 | - this.media_camera = c; | ||
| 158 | - this.media_microphone = m; | 180 | + this.media_camera = camera; |
| 181 | + this.media_microphone = microphone; | ||
| 159 | 182 | ||
| 160 | this.media_conn = new NetConnection(); | 183 | this.media_conn = new NetConnection(); |
| 161 | this.media_conn.client = {}; | 184 | this.media_conn.client = {}; |
| @@ -174,6 +197,17 @@ package | @@ -174,6 +197,17 @@ package | ||
| 174 | } | 197 | } |
| 175 | contextMenu.customItems = customItems; | 198 | contextMenu.customItems = customItems; |
| 176 | } | 199 | } |
| 200 | + | ||
| 201 | + if (evt.info.code == "NetConnection.Connect.Closed") { | ||
| 202 | + js_call_stop(); | ||
| 203 | + system_error(error_connection_closed, "server closed the connection"); | ||
| 204 | + return; | ||
| 205 | + } | ||
| 206 | + if (evt.info.code == "NetConnection.Connect.Failed") { | ||
| 207 | + js_call_stop(); | ||
| 208 | + system_error(error_connection_failed, "connect to server failed"); | ||
| 209 | + return; | ||
| 210 | + } | ||
| 177 | 211 | ||
| 178 | // TODO: FIXME: failed event. | 212 | // TODO: FIXME: failed event. |
| 179 | if (evt.info.code != "NetConnection.Connect.Success") { | 213 | if (evt.info.code != "NetConnection.Connect.Success") { |
| @@ -188,30 +222,20 @@ package | @@ -188,30 +222,20 @@ package | ||
| 188 | // TODO: FIXME: failed event. | 222 | // TODO: FIXME: failed event. |
| 189 | }); | 223 | }); |
| 190 | 224 | ||
| 191 | - __build_video_codec(media_stream, c, vcodec); | ||
| 192 | - __build_audio_codec(media_stream, m, acodec); | 225 | + __build_video_codec(media_stream, camera, vcodec); |
| 226 | + __build_audio_codec(media_stream, microphone, acodec); | ||
| 193 | 227 | ||
| 194 | if (media_microphone) { | 228 | if (media_microphone) { |
| 195 | - media_stream.attachAudio(m); | 229 | + media_stream.attachAudio(microphone); |
| 196 | } | 230 | } |
| 197 | if (media_camera) { | 231 | if (media_camera) { |
| 198 | - media_stream.attachCamera(c); | 232 | + media_stream.attachCamera(camera); |
| 199 | } | 233 | } |
| 200 | 234 | ||
| 201 | var streamName:String = url.substr(url.lastIndexOf("/")); | 235 | var streamName:String = url.substr(url.lastIndexOf("/")); |
| 202 | media_stream.publish(streamName); | 236 | media_stream.publish(streamName); |
| 203 | 237 | ||
| 204 | - media_video = new Video(); | ||
| 205 | - media_video.width = _width; | ||
| 206 | - media_video.height = _height; | ||
| 207 | - media_video.attachCamera(media_camera); | ||
| 208 | - media_video.smoothing = true; | ||
| 209 | - addChild(media_video); | ||
| 210 | - | ||
| 211 | - //__draw_black_background(_width, _height); | ||
| 212 | - | ||
| 213 | - // lowest layer, for mask to cover it. | ||
| 214 | - setChildIndex(media_video, 0); | 238 | + __show_local_camera(media_camera); |
| 215 | }); | 239 | }); |
| 216 | 240 | ||
| 217 | var tcUrl:String = this.user_url.substr(0, this.user_url.lastIndexOf("/")); | 241 | var tcUrl:String = this.user_url.substr(0, this.user_url.lastIndexOf("/")); |
| @@ -222,11 +246,9 @@ package | @@ -222,11 +246,9 @@ package | ||
| 222 | * function for js to call: to stop the stream. ignore if not publish. | 246 | * function for js to call: to stop the stream. ignore if not publish. |
| 223 | */ | 247 | */ |
| 224 | private function js_call_stop():void { | 248 | private function js_call_stop():void { |
| 225 | - if (this.media_video) { | ||
| 226 | - this.removeChild(this.media_video); | ||
| 227 | - this.media_video = null; | ||
| 228 | - } | ||
| 229 | if (this.media_stream) { | 249 | if (this.media_stream) { |
| 250 | + this.media_stream.attachAudio(null); | ||
| 251 | + this.media_stream.attachCamera(null); | ||
| 230 | this.media_stream.close(); | 252 | this.media_stream.close(); |
| 231 | this.media_stream = null; | 253 | this.media_stream = null; |
| 232 | } | 254 | } |
| @@ -264,6 +286,10 @@ package | @@ -264,6 +286,10 @@ package | ||
| 264 | // if your sound capture device supports this value. Otherwise, the default value is the next available capture level above 8 kHz that | 286 | // if your sound capture device supports this value. Otherwise, the default value is the next available capture level above 8 kHz that |
| 265 | // your sound capture device supports, usually 11 kHz. | 287 | // your sound capture device supports, usually 11 kHz. |
| 266 | m.rate = microRate; | 288 | m.rate = microRate; |
| 289 | + | ||
| 290 | + // see: http://www.adobe.com/cn/devnet/flashplayer/articles/acoustic-echo-cancellation.html | ||
| 291 | + m.codec = SoundCodec.SPEEX; | ||
| 292 | + m.framesPerPacket = 1; | ||
| 267 | } | 293 | } |
| 268 | private function __build_video_codec(stream:NetStream, c:Camera, vcodec:Object):void { | 294 | private function __build_video_codec(stream:NetStream, c:Camera, vcodec:Object):void { |
| 269 | if (!c) { | 295 | if (!c) { |
| @@ -332,9 +358,56 @@ package | @@ -332,9 +358,56 @@ package | ||
| 332 | // (highest quality, no compression). To specify that picture quality can vary as needed to avoid exceeding bandwidth, | 358 | // (highest quality, no compression). To specify that picture quality can vary as needed to avoid exceeding bandwidth, |
| 333 | // pass 0 for quality. | 359 | // pass 0 for quality. |
| 334 | // winlin: | 360 | // winlin: |
| 335 | - // bandwidth is in bps not kbps. 500*1000 = 500kbps. | 361 | + // bandwidth is in Bps not kbps. |
| 336 | // quality=1 is lowest quality, 100 is highest quality. | 362 | // quality=1 is lowest quality, 100 is highest quality. |
| 337 | - c.setQuality(cameraBitrate * 1000, cameraQuality); | 363 | + c.setQuality(cameraBitrate / 8.0 * 1000, cameraQuality); |
| 364 | + } | ||
| 365 | + | ||
| 366 | + private function __show_local_camera(c:Camera):void { | ||
| 367 | + if (this.media_video) { | ||
| 368 | + this.media_video.attachCamera(null); | ||
| 369 | + this.removeChild(this.media_video); | ||
| 370 | + this.media_video = null; | ||
| 371 | + } | ||
| 372 | + | ||
| 373 | + // show local camera | ||
| 374 | + media_video = new Video(); | ||
| 375 | + media_video.attachCamera(c); | ||
| 376 | + media_video.smoothing = true; | ||
| 377 | + addChild(media_video); | ||
| 378 | + | ||
| 379 | + // rescale the local camera. | ||
| 380 | + var cw:Number = user_h * c.width / c.height; | ||
| 381 | + if (cw > user_w) { | ||
| 382 | + var ch:Number = user_w * c.height / c.width; | ||
| 383 | + media_video.width = user_w; | ||
| 384 | + media_video.height = ch; | ||
| 385 | + } else { | ||
| 386 | + media_video.width = cw; | ||
| 387 | + media_video.height = user_h; | ||
| 388 | + } | ||
| 389 | + media_video.x = (user_w - media_video.width) / 2; | ||
| 390 | + media_video.y = (user_h - media_video.height) / 2; | ||
| 391 | + | ||
| 392 | + __draw_black_background(user_w, user_h); | ||
| 393 | + | ||
| 394 | + // lowest layer, for mask to cover it. | ||
| 395 | + setChildIndex(media_video, 0); | ||
| 396 | + } | ||
| 397 | + | ||
| 398 | + /** | ||
| 399 | + * draw black background and draw the fullscreen mask. | ||
| 400 | + */ | ||
| 401 | + private function __draw_black_background(_width:int, _height:int):void { | ||
| 402 | + // draw black bg. | ||
| 403 | + this.graphics.beginFill(0x00, 1.0); | ||
| 404 | + this.graphics.drawRect(0, 0, _width, _height); | ||
| 405 | + this.graphics.endFill(); | ||
| 406 | + | ||
| 407 | + // draw the fs mask. | ||
| 408 | + //this.control_fs_mask.graphics.beginFill(0xff0000, 0); | ||
| 409 | + //this.control_fs_mask.graphics.drawRect(0, 0, _width, _height); | ||
| 410 | + //this.control_fs_mask.graphics.endFill(); | ||
| 338 | } | 411 | } |
| 339 | } | 412 | } |
| 340 | } | 413 | } |
| @@ -7,7 +7,7 @@ | @@ -7,7 +7,7 @@ | ||
| 7 | <script type="text/javascript" src="js/jquery-1.10.2.min.js"></script> | 7 | <script type="text/javascript" src="js/jquery-1.10.2.min.js"></script> |
| 8 | <script type="text/javascript" src="js/bootstrap.min.js"></script> | 8 | <script type="text/javascript" src="js/bootstrap.min.js"></script> |
| 9 | <script type="text/javascript" src="js/swfobject.js"></script> | 9 | <script type="text/javascript" src="js/swfobject.js"></script> |
| 10 | - <script type="text/javascript" src="js/srs.js"></script> | 10 | + <script type="text/javascript" src="js/srs.page.js"></script> |
| 11 | <style> | 11 | <style> |
| 12 | body{ | 12 | body{ |
| 13 | padding-top: 55px; | 13 | padding-top: 55px; |
| @@ -24,7 +24,7 @@ | @@ -24,7 +24,7 @@ | ||
| 24 | <div class="navbar navbar-fixed-top"> | 24 | <div class="navbar navbar-fixed-top"> |
| 25 | <div class="navbar-inner"> | 25 | <div class="navbar-inner"> |
| 26 | <div class="container"> | 26 | <div class="container"> |
| 27 | - <a class="brand" href="index.html">SRS</a> | 27 | + <a id="srs_index" class="brand" href="#">SRS</a> |
| 28 | <div class="nav-collapse collapse"> | 28 | <div class="nav-collapse collapse"> |
| 29 | <ul class="nav"> | 29 | <ul class="nav"> |
| 30 | <li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li> | 30 | <li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li> |
| @@ -4,7 +4,9 @@ if [[ ! -d $src_dir ]]; then echo "错误:必须在src同目录执行脚本"; | @@ -4,7 +4,9 @@ if [[ ! -d $src_dir ]]; then echo "错误:必须在src同目录执行脚本"; | ||
| 4 | 4 | ||
| 5 | cmd="sudo ./objs/nginx/sbin/nginx" | 5 | cmd="sudo ./objs/nginx/sbin/nginx" |
| 6 | echo "启动NGINX(HLS服务):$cmd" | 6 | echo "启动NGINX(HLS服务):$cmd" |
| 7 | -pids=`ps aux|grep nginx|grep process|awk '{print $2}'`; for pid in $pids; do echo "结束现有进程:$pid"; sudo kill -s SIGKILL $pid; done | 7 | +if [[ -f ./objs/nginx/logs/nginx.pid ]]; then |
| 8 | + pids=`cat ./objs/nginx/logs/nginx.pid`; for pid in $pids; do echo "结束现有进程:$pid"; sudo kill -s SIGTERM $pid; done | ||
| 9 | +fi | ||
| 8 | sudo ./objs/nginx/sbin/nginx | 10 | sudo ./objs/nginx/sbin/nginx |
| 9 | ret=$?; if [[ 0 -ne $ret ]]; then echo "错误:启动NGINX(HLS服务)失败"; exit $ret; fi | 11 | ret=$?; if [[ 0 -ne $ret ]]; then echo "错误:启动NGINX(HLS服务)失败"; exit $ret; fi |
| 10 | 12 |
| @@ -31,12 +31,6 @@ bash scripts/_step.start.api.server.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $ | @@ -31,12 +31,6 @@ bash scripts/_step.start.api.server.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $ | ||
| 31 | 31 | ||
| 32 | # step 8: add server ip to client hosts as demo. | 32 | # step 8: add server ip to client hosts as demo. |
| 33 | ip=`ifconfig|grep "inet"|grep "addr"|grep "Mask"|grep -v "127.0.0.1"|awk 'NR==1 {print $2}'|awk -F ':' '{print $2}'` | 33 | ip=`ifconfig|grep "inet"|grep "addr"|grep "Mask"|grep -v "127.0.0.1"|awk 'NR==1 {print $2}'|awk -F ':' '{print $2}'` |
| 34 | -echo -e "${GREEN}SRS系统开发环境启动成功${BLACK}" | ||
| 35 | -echo -e "${BLACK}播放器演示:${BLACK}" | ||
| 36 | -echo -e "${RED} http://$ip/players/srs_player.html?vhost=players${BLACK}" | ||
| 37 | -echo -e "${BLACK}编码器演示:${BLACK}" | ||
| 38 | -echo -e "${RED} http://$ip/players/srs_publisher.html?vhost=players${BLACK}" | ||
| 39 | -echo -e "${BLACK}视频会议演示:${BLACK}" | ||
| 40 | -echo -e "${RED} http://$ip/players/srs_chat.html?vhost=players${BLACK}" | ||
| 41 | -echo -e "${BLACK}服务器测速演示:${BLACK}" | ||
| 42 | -echo -e "${RED} http://$ip/players/srs_bwt.html?vhost=players${BLACK}" | 34 | +echo -e "${GREEN}SRS系统开发环境启动成功。演示:${BLACK}" |
| 35 | +echo -e "${RED} http://$ip${BLACK}" | ||
| 36 | +echo -e "${RED} http://$ip:8085/players/index.html${BLACK}" |
| @@ -11,7 +11,9 @@ pids=`ps aux|grep srs|grep "./objs"|grep "srs.19350.conf"|awk '{print $2}'`; for | @@ -11,7 +11,9 @@ pids=`ps aux|grep srs|grep "./objs"|grep "srs.19350.conf"|awk '{print $2}'`; for | ||
| 11 | # step 4(optinal): start nginx for HLS | 11 | # step 4(optinal): start nginx for HLS |
| 12 | echo "停止NGINX(HLS服务)" | 12 | echo "停止NGINX(HLS服务)" |
| 13 | ps aux|grep nginx|grep process | 13 | ps aux|grep nginx|grep process |
| 14 | -pids=`ps aux|grep nginx|grep process|awk '{print $2}'`; for pid in $pids; do echo "结束现有进程:$pid"; sudo kill -s SIGKILL $pid; done | 14 | +if [[ -f ./objs/nginx/logs/nginx.pid ]]; then |
| 15 | + pids=`cat ./objs/nginx/logs/nginx.pid`; for pid in $pids; do echo "结束现有进程:$pid"; sudo kill -s SIGTERM $pid; done | ||
| 16 | +fi | ||
| 15 | 17 | ||
| 16 | # step 5(optinal): start http hooks for srs callback | 18 | # step 5(optinal): start http hooks for srs callback |
| 17 | echo "停止API服务器" | 19 | echo "停止API服务器" |
| @@ -24,6 +24,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -24,6 +24,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 24 | #include <srs_core_bandwidth.hpp> | 24 | #include <srs_core_bandwidth.hpp> |
| 25 | 25 | ||
| 26 | #include <arpa/inet.h> | 26 | #include <arpa/inet.h> |
| 27 | +#include <sstream> | ||
| 27 | 28 | ||
| 28 | using namespace std; | 29 | using namespace std; |
| 29 | 30 | ||
| @@ -260,21 +261,24 @@ int SrsBandwidth::check_play( | @@ -260,21 +261,24 @@ int SrsBandwidth::check_play( | ||
| 260 | int64_t current_time = srs_get_system_time_ms(); | 261 | int64_t current_time = srs_get_system_time_ms(); |
| 261 | int size = 1024; // TODO: FIXME: magic number | 262 | int size = 1024; // TODO: FIXME: magic number |
| 262 | char random_data[size]; | 263 | char random_data[size]; |
| 263 | - memset(random_data, 0x01, size); | 264 | + memset(random_data, 'A', size); |
| 264 | 265 | ||
| 265 | int interval = 0; | 266 | int interval = 0; |
| 267 | + int data_count = 1; | ||
| 266 | while ( (srs_get_system_time_ms() - current_time) < duration_ms ) { | 268 | while ( (srs_get_system_time_ms() - current_time) < duration_ms ) { |
| 267 | st_usleep(interval); | 269 | st_usleep(interval); |
| 268 | 270 | ||
| 269 | // TODO: FIXME: use shared ptr message. | 271 | // TODO: FIXME: use shared ptr message. |
| 270 | SrsBandwidthPacket* pkt = SrsBandwidthPacket::create_playing(); | 272 | SrsBandwidthPacket* pkt = SrsBandwidthPacket::create_playing(); |
| 271 | 273 | ||
| 272 | - // TODO: FIXME: magic number | ||
| 273 | - for (int i = 0; i < 100; ++i) { | ||
| 274 | - char buf[32]; // TODO: FIXME: magic number | ||
| 275 | - sprintf(buf, "%d", i); | ||
| 276 | - pkt->data->set(buf, new SrsAmf0String(random_data)); | 274 | + // TODO: FIXME: magic number |
| 275 | + for (int i = 0; i < data_count; ++i) { | ||
| 276 | + std::stringstream seq; | ||
| 277 | + seq << i; | ||
| 278 | + std::string play_data = "SrS band check data from server's playing......"; | ||
| 279 | + pkt->data->set(seq.str(), new SrsAmf0String(play_data.c_str())); | ||
| 277 | } | 280 | } |
| 281 | + data_count += 2; | ||
| 278 | 282 | ||
| 279 | // TODO: FIXME: get length from the rtmp protocol stack. | 283 | // TODO: FIXME: get length from the rtmp protocol stack. |
| 280 | play_bytes += pkt->get_payload_length(); | 284 | play_bytes += pkt->get_payload_length(); |
| @@ -2789,7 +2789,7 @@ SrsBandwidthPacket* SrsBandwidthPacket::create_start_play() | @@ -2789,7 +2789,7 @@ SrsBandwidthPacket* SrsBandwidthPacket::create_start_play() | ||
| 2789 | SrsBandwidthPacket* SrsBandwidthPacket::create_playing() | 2789 | SrsBandwidthPacket* SrsBandwidthPacket::create_playing() |
| 2790 | { | 2790 | { |
| 2791 | SrsBandwidthPacket* pkt = new SrsBandwidthPacket(); | 2791 | SrsBandwidthPacket* pkt = new SrsBandwidthPacket(); |
| 2792 | - return pkt->set_command(SRS_BW_CHECK_STARTING_PLAY); | 2792 | + return pkt->set_command(SRS_BW_CHECK_PLAYING); |
| 2793 | } | 2793 | } |
| 2794 | 2794 | ||
| 2795 | SrsBandwidthPacket* SrsBandwidthPacket::create_stop_play() | 2795 | SrsBandwidthPacket* SrsBandwidthPacket::create_stop_play() |
| @@ -414,7 +414,17 @@ int SrsRtmpClient::publish(string stream, int stream_id) | @@ -414,7 +414,17 @@ int SrsRtmpClient::publish(string stream, int stream_id) | ||
| 414 | } | 414 | } |
| 415 | } | 415 | } |
| 416 | 416 | ||
| 417 | - return ret; | 417 | + return ret; |
| 418 | +} | ||
| 419 | + | ||
| 420 | +SrsProtocol *SrsRtmpClient::get_protocol() | ||
| 421 | +{ | ||
| 422 | + return protocol; | ||
| 423 | +} | ||
| 424 | + | ||
| 425 | +st_netfd_t SrsRtmpClient::get_st_fd() | ||
| 426 | +{ | ||
| 427 | + return stfd; | ||
| 418 | } | 428 | } |
| 419 | 429 | ||
| 420 | SrsRtmp::SrsRtmp(st_netfd_t client_stfd) | 430 | SrsRtmp::SrsRtmp(st_netfd_t client_stfd) |
| @@ -131,6 +131,10 @@ public: | @@ -131,6 +131,10 @@ public: | ||
| 131 | virtual int create_stream(int& stream_id); | 131 | virtual int create_stream(int& stream_id); |
| 132 | virtual int play(std::string stream, int stream_id); | 132 | virtual int play(std::string stream, int stream_id); |
| 133 | virtual int publish(std::string stream, int stream_id); | 133 | virtual int publish(std::string stream, int stream_id); |
| 134 | + | ||
| 135 | +protected: | ||
| 136 | + SrsProtocol* get_protocol(); | ||
| 137 | + st_netfd_t get_st_fd(); | ||
| 134 | }; | 138 | }; |
| 135 | 139 | ||
| 136 | /** | 140 | /** |
-
请 注册 或 登录 后发表评论