mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
add linux version of band check app; add web version of band check app
This commit is contained in:
commit
237fb13e1b
35 changed files with 1966 additions and 218 deletions
10
README.md
10
README.md
|
@ -6,8 +6,8 @@ srs is a simple, high-performance, running in single process, origin live server
|
|||
srs supports vhost, rtmp, HLS, transcoding, forward, http hooks. <br/>
|
||||
blog: [http://blog.csdn.net/win_lin](http://blog.csdn.net/win_lin) <br/>
|
||||
see also: [https://github.com/winlinvip/simple-rtmp-server](https://github.com/winlinvip/simple-rtmp-server) <br/>
|
||||
see also: [http://winlinvip.github.io/simple-rtmp-server](http://winlinvip.github.io/simple-rtmp-server)
|
||||
TencentQQ: http://url.cn/WAHICw (Group: 212189142)
|
||||
see also: [http://winlinvip.github.io/simple-rtmp-server](http://winlinvip.github.io/simple-rtmp-server) <br/>
|
||||
TencentQQ: [http://url.cn/WAHICw](http://url.cn/WAHICw) (Group: 212189142)
|
||||
|
||||
### Contributors
|
||||
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
|
|||
</pre>
|
||||
|
||||
### Releases
|
||||
* 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/>
|
||||
* 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/>
|
||||
* 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/>
|
||||
* 2013-12-03, [release v0.7](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.7), support live stream transcoding. 17605 lines.<br/>
|
||||
* 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/>
|
||||
* 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
|
|||
* 2013-10-17, created.<br/>
|
||||
|
||||
### Compare
|
||||
* srs v0.9: 20926 lines. player/encoder/chat demos. bandwidth test for encoder/CDN.<br/>
|
||||
* srs v0.8: 19186 lines. implements http hooks refer to [nginx-rtmp](https://github.com/arut/nginx-rtmp-module). <br/>
|
||||
* srs v0.7: 17605 lines. implements transcoding(FFMPEG) feature refer to [wowza](http://www.wowza.com). <br/>
|
||||
* 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
|
|||
* nginx v1.5.0: 139524 lines <br/>
|
||||
|
||||
### History
|
||||
* v0.9, 2013-12-25, [v0.9](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.9) released. 20926 lines.
|
||||
* v0.9, 2013-12-25, fix the bitrate bug(in Bps), use enhanced microphone.
|
||||
* v0.9, 2013-12-22, demo video meeting or chat(srs+cherrypy+jquery+bootstrap).
|
||||
* v0.9, 2013-12-22, merge from wenjie, support banwidth test.
|
||||
* v0.9, 2013-12-22, merge from wenjie: support set chunk size at vhost level
|
||||
|
|
|
@ -261,11 +261,8 @@ if [ $SRS_HLS = YES ]; then
|
|||
ln -sf `pwd`/research/players/crossdomain.xml ${SRS_OBJS}/nginx/html/crossdomain.xml
|
||||
|
||||
# override the default index.
|
||||
cat <<END > ${SRS_OBJS}/nginx/html/index.html
|
||||
<script type="text/javascript">
|
||||
window.location.href = "players/index.html";
|
||||
</script>
|
||||
END
|
||||
rm -f ${SRS_OBJS}/nginx/html/index.html &&
|
||||
ln -sf `pwd`/research/players/nginx_index.html ${SRS_OBJS}/nginx/html/index.html
|
||||
fi
|
||||
|
||||
if [ $SRS_HLS = YES ]; then
|
||||
|
@ -300,6 +297,12 @@ else
|
|||
echo "#undef SRS_HTTP" >> $SRS_AUTO_HEADERS_H
|
||||
fi
|
||||
|
||||
echo "link players to cherrypy static-dir"
|
||||
rm -f research/api-server/static-dir/players &&
|
||||
ln -sf `pwd`/research/players research/api-server/static-dir/players &&
|
||||
rm -f research/api-server/static-dir/crossdomain.xml &&
|
||||
ln -sf `pwd`/research/players/crossdomain.xml research/api-server/static-dir/crossdomain.xml
|
||||
|
||||
#####################################################################################
|
||||
# openssl, for rtmp complex handshake
|
||||
#####################################################################################
|
||||
|
|
|
@ -343,7 +343,7 @@ class RESTChats(object):
|
|||
self.__chat_lock = threading.Lock();
|
||||
|
||||
# dead time in seconds, if exceed, remove the chat.
|
||||
self.__dead_time = 30;
|
||||
self.__dead_time = 15;
|
||||
|
||||
def GET(self):
|
||||
enable_crossdomain()
|
||||
|
@ -474,7 +474,8 @@ if len(sys.argv) <= 1:
|
|||
|
||||
# parse port from user options.
|
||||
port = int(sys.argv[1])
|
||||
trace("api server listen at port: %s"%(port))
|
||||
static_dir = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), "static-dir"))
|
||||
trace("api server listen at port: %s, static_dir: %s"%(port, static_dir))
|
||||
|
||||
# cherrypy config.
|
||||
conf = {
|
||||
|
@ -483,9 +484,12 @@ conf = {
|
|||
'server.socket_host': '0.0.0.0',
|
||||
'server.socket_port': port,
|
||||
'tools.encode.on': True,
|
||||
'tools.staticdir.on': True,
|
||||
'tools.encode.encoding': "utf-8"
|
||||
},
|
||||
'/': {
|
||||
'tools.staticdir.dir': static_dir,
|
||||
'tools.staticdir.index': "index.html",
|
||||
# for cherrypy RESTful api support
|
||||
'request.dispatch': cherrypy.dispatch.MethodDispatcher()
|
||||
}
|
||||
|
|
50
trunk/research/api-server/static-dir/index.html
Executable file
50
trunk/research/api-server/static-dir/index.html
Executable file
|
@ -0,0 +1,50 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>SRS</title>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" type="text/css" href="players/css/bootstrap.min.css"/>
|
||||
<script type="text/javascript" src="players/js/jquery-1.10.2.min.js"></script>
|
||||
<script type="text/javascript" src="players/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="players/js/swfobject.js"></script>
|
||||
<script type="text/javascript" src="players/js/srs.page.js"></script>
|
||||
<style>
|
||||
body{
|
||||
padding-top: 55px;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
$(function(){
|
||||
update_nav();
|
||||
|
||||
// direct to the default vhost for players.
|
||||
window.location.href = "players/index.html" + window.location.search;
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<a class="brand" href="index.html">SRS</a>
|
||||
<div class="nav-collapse collapse">
|
||||
<ul class="nav">
|
||||
<li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
|
||||
<li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
|
||||
<li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>
|
||||
<li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
|
||||
<li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>
|
||||
<li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>
|
||||
<li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<hr>
|
||||
<footer>
|
||||
<p><a href="https://github.com/winlinvip/simple-rtmp-server">SRS Team © 2013</a></p>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
|
@ -7,7 +7,8 @@
|
|||
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
|
||||
<script type="text/javascript" src="js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="js/swfobject.js"></script>
|
||||
<script type="text/javascript" src="js/srs.js"></script>
|
||||
<script type="text/javascript" src="js/srs.page.js"></script>
|
||||
<script type="text/javascript" src="js/srs.utility.js"></script>
|
||||
<style>
|
||||
body{
|
||||
padding-top: 55px;
|
||||
|
@ -17,8 +18,19 @@
|
|||
$(function(){
|
||||
update_nav();
|
||||
|
||||
// direct to the default vhost for players.
|
||||
window.location.href = "srs_player.html?vhost=" + srs_get_player_vhost();
|
||||
var query = parse_query_string();
|
||||
var url = "srs_chat.html?vhost=" + srs_get_player_vhost();
|
||||
|
||||
for (var key in query.user_query) {
|
||||
if (key == "vhost") {
|
||||
continue;
|
||||
}
|
||||
url += "&" + key + "=" + query[key];
|
||||
}
|
||||
|
||||
setTimeout(function(){
|
||||
window.location.href = url;
|
||||
}, 100);
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
@ -26,7 +38,7 @@
|
|||
<div class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<a class="brand" href="index.html">SRS</a>
|
||||
<a id="srs_index" class="brand" href="#">SRS</a>
|
||||
<div class="nav-collapse collapse">
|
||||
<ul class="nav">
|
||||
<li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
|
||||
|
|
0
trunk/research/players/js/AdobeFlashPlayerInstall.swf
Executable file → Normal file
0
trunk/research/players/js/AdobeFlashPlayerInstall.swf
Executable file → Normal file
0
trunk/research/players/js/bootstrap.min.js
vendored
Executable file → Normal file
0
trunk/research/players/js/bootstrap.min.js
vendored
Executable file → Normal file
0
trunk/research/players/js/jquery-1.10.2.min.js
vendored
Executable file → Normal file
0
trunk/research/players/js/jquery-1.10.2.min.js
vendored
Executable file → Normal file
0
trunk/research/players/js/jquery-1.10.2.min.map
Executable file → Normal file
0
trunk/research/players/js/jquery-1.10.2.min.map
Executable file → Normal file
486
trunk/research/players/js/json2.js
Executable file
486
trunk/research/players/js/json2.js
Executable file
|
@ -0,0 +1,486 @@
|
|||
/*
|
||||
json2.js
|
||||
2013-05-26
|
||||
|
||||
Public Domain.
|
||||
|
||||
NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
|
||||
|
||||
See http://www.JSON.org/js.html
|
||||
|
||||
|
||||
This code should be minified before deployment.
|
||||
See http://javascript.crockford.com/jsmin.html
|
||||
|
||||
USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
|
||||
NOT CONTROL.
|
||||
|
||||
|
||||
This file creates a global JSON object containing two methods: stringify
|
||||
and parse.
|
||||
|
||||
JSON.stringify(value, replacer, space)
|
||||
value any JavaScript value, usually an object or array.
|
||||
|
||||
replacer an optional parameter that determines how object
|
||||
values are stringified for objects. It can be a
|
||||
function or an array of strings.
|
||||
|
||||
space an optional parameter that specifies the indentation
|
||||
of nested structures. If it is omitted, the text will
|
||||
be packed without extra whitespace. If it is a number,
|
||||
it will specify the number of spaces to indent at each
|
||||
level. If it is a string (such as '\t' or ' '),
|
||||
it contains the characters used to indent at each level.
|
||||
|
||||
This method produces a JSON text from a JavaScript value.
|
||||
|
||||
When an object value is found, if the object contains a toJSON
|
||||
method, its toJSON method will be called and the result will be
|
||||
stringified. A toJSON method does not serialize: it returns the
|
||||
value represented by the name/value pair that should be serialized,
|
||||
or undefined if nothing should be serialized. The toJSON method
|
||||
will be passed the key associated with the value, and this will be
|
||||
bound to the value
|
||||
|
||||
For example, this would serialize Dates as ISO strings.
|
||||
|
||||
Date.prototype.toJSON = function (key) {
|
||||
function f(n) {
|
||||
// Format integers to have at least two digits.
|
||||
return n < 10 ? '0' + n : n;
|
||||
}
|
||||
|
||||
return this.getUTCFullYear() + '-' +
|
||||
f(this.getUTCMonth() + 1) + '-' +
|
||||
f(this.getUTCDate()) + 'T' +
|
||||
f(this.getUTCHours()) + ':' +
|
||||
f(this.getUTCMinutes()) + ':' +
|
||||
f(this.getUTCSeconds()) + 'Z';
|
||||
};
|
||||
|
||||
You can provide an optional replacer method. It will be passed the
|
||||
key and value of each member, with this bound to the containing
|
||||
object. The value that is returned from your method will be
|
||||
serialized. If your method returns undefined, then the member will
|
||||
be excluded from the serialization.
|
||||
|
||||
If the replacer parameter is an array of strings, then it will be
|
||||
used to select the members to be serialized. It filters the results
|
||||
such that only members with keys listed in the replacer array are
|
||||
stringified.
|
||||
|
||||
Values that do not have JSON representations, such as undefined or
|
||||
functions, will not be serialized. Such values in objects will be
|
||||
dropped; in arrays they will be replaced with null. You can use
|
||||
a replacer function to replace those with JSON values.
|
||||
JSON.stringify(undefined) returns undefined.
|
||||
|
||||
The optional space parameter produces a stringification of the
|
||||
value that is filled with line breaks and indentation to make it
|
||||
easier to read.
|
||||
|
||||
If the space parameter is a non-empty string, then that string will
|
||||
be used for indentation. If the space parameter is a number, then
|
||||
the indentation will be that many spaces.
|
||||
|
||||
Example:
|
||||
|
||||
text = JSON.stringify(['e', {pluribus: 'unum'}]);
|
||||
// text is '["e",{"pluribus":"unum"}]'
|
||||
|
||||
|
||||
text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
|
||||
// text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
|
||||
|
||||
text = JSON.stringify([new Date()], function (key, value) {
|
||||
return this[key] instanceof Date ?
|
||||
'Date(' + this[key] + ')' : value;
|
||||
});
|
||||
// text is '["Date(---current time---)"]'
|
||||
|
||||
|
||||
JSON.parse(text, reviver)
|
||||
This method parses a JSON text to produce an object or array.
|
||||
It can throw a SyntaxError exception.
|
||||
|
||||
The optional reviver parameter is a function that can filter and
|
||||
transform the results. It receives each of the keys and values,
|
||||
and its return value is used instead of the original value.
|
||||
If it returns what it received, then the structure is not modified.
|
||||
If it returns undefined then the member is deleted.
|
||||
|
||||
Example:
|
||||
|
||||
// Parse the text. Values that look like ISO date strings will
|
||||
// be converted to Date objects.
|
||||
|
||||
myData = JSON.parse(text, function (key, value) {
|
||||
var a;
|
||||
if (typeof value === 'string') {
|
||||
a =
|
||||
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
|
||||
if (a) {
|
||||
return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
|
||||
+a[5], +a[6]));
|
||||
}
|
||||
}
|
||||
return value;
|
||||
});
|
||||
|
||||
myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
|
||||
var d;
|
||||
if (typeof value === 'string' &&
|
||||
value.slice(0, 5) === 'Date(' &&
|
||||
value.slice(-1) === ')') {
|
||||
d = new Date(value.slice(5, -1));
|
||||
if (d) {
|
||||
return d;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
});
|
||||
|
||||
|
||||
This is a reference implementation. You are free to copy, modify, or
|
||||
redistribute.
|
||||
*/
|
||||
|
||||
/*jslint evil: true, regexp: true */
|
||||
|
||||
/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
|
||||
call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
|
||||
getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
|
||||
lastIndex, length, parse, prototype, push, replace, slice, stringify,
|
||||
test, toJSON, toString, valueOf
|
||||
*/
|
||||
|
||||
|
||||
// Create a JSON object only if one does not already exist. We create the
|
||||
// methods in a closure to avoid creating global variables.
|
||||
|
||||
if (typeof JSON !== 'object') {
|
||||
JSON = {};
|
||||
}
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function f(n) {
|
||||
// Format integers to have at least two digits.
|
||||
return n < 10 ? '0' + n : n;
|
||||
}
|
||||
|
||||
if (typeof Date.prototype.toJSON !== 'function') {
|
||||
|
||||
Date.prototype.toJSON = function () {
|
||||
|
||||
return isFinite(this.valueOf())
|
||||
? this.getUTCFullYear() + '-' +
|
||||
f(this.getUTCMonth() + 1) + '-' +
|
||||
f(this.getUTCDate()) + 'T' +
|
||||
f(this.getUTCHours()) + ':' +
|
||||
f(this.getUTCMinutes()) + ':' +
|
||||
f(this.getUTCSeconds()) + 'Z'
|
||||
: null;
|
||||
};
|
||||
|
||||
String.prototype.toJSON =
|
||||
Number.prototype.toJSON =
|
||||
Boolean.prototype.toJSON = function () {
|
||||
return this.valueOf();
|
||||
};
|
||||
}
|
||||
|
||||
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
|
||||
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
|
||||
gap,
|
||||
indent,
|
||||
meta = { // table of character substitutions
|
||||
'\b': '\\b',
|
||||
'\t': '\\t',
|
||||
'\n': '\\n',
|
||||
'\f': '\\f',
|
||||
'\r': '\\r',
|
||||
'"' : '\\"',
|
||||
'\\': '\\\\'
|
||||
},
|
||||
rep;
|
||||
|
||||
|
||||
function quote(string) {
|
||||
|
||||
// If the string contains no control characters, no quote characters, and no
|
||||
// backslash characters, then we can safely slap some quotes around it.
|
||||
// Otherwise we must also replace the offending characters with safe escape
|
||||
// sequences.
|
||||
|
||||
escapable.lastIndex = 0;
|
||||
return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
|
||||
var c = meta[a];
|
||||
return typeof c === 'string'
|
||||
? c
|
||||
: '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
||||
}) + '"' : '"' + string + '"';
|
||||
}
|
||||
|
||||
|
||||
function str(key, holder) {
|
||||
|
||||
// Produce a string from holder[key].
|
||||
|
||||
var i, // The loop counter.
|
||||
k, // The member key.
|
||||
v, // The member value.
|
||||
length,
|
||||
mind = gap,
|
||||
partial,
|
||||
value = holder[key];
|
||||
|
||||
// If the value has a toJSON method, call it to obtain a replacement value.
|
||||
|
||||
if (value && typeof value === 'object' &&
|
||||
typeof value.toJSON === 'function') {
|
||||
value = value.toJSON(key);
|
||||
}
|
||||
|
||||
// If we were called with a replacer function, then call the replacer to
|
||||
// obtain a replacement value.
|
||||
|
||||
if (typeof rep === 'function') {
|
||||
value = rep.call(holder, key, value);
|
||||
}
|
||||
|
||||
// What happens next depends on the value's type.
|
||||
|
||||
switch (typeof value) {
|
||||
case 'string':
|
||||
return quote(value);
|
||||
|
||||
case 'number':
|
||||
|
||||
// JSON numbers must be finite. Encode non-finite numbers as null.
|
||||
|
||||
return isFinite(value) ? String(value) : 'null';
|
||||
|
||||
case 'boolean':
|
||||
case 'null':
|
||||
|
||||
// If the value is a boolean or null, convert it to a string. Note:
|
||||
// typeof null does not produce 'null'. The case is included here in
|
||||
// the remote chance that this gets fixed someday.
|
||||
|
||||
return String(value);
|
||||
|
||||
// If the type is 'object', we might be dealing with an object or an array or
|
||||
// null.
|
||||
|
||||
case 'object':
|
||||
|
||||
// Due to a specification blunder in ECMAScript, typeof null is 'object',
|
||||
// so watch out for that case.
|
||||
|
||||
if (!value) {
|
||||
return 'null';
|
||||
}
|
||||
|
||||
// Make an array to hold the partial results of stringifying this object value.
|
||||
|
||||
gap += indent;
|
||||
partial = [];
|
||||
|
||||
// Is the value an array?
|
||||
|
||||
if (Object.prototype.toString.apply(value) === '[object Array]') {
|
||||
|
||||
// The value is an array. Stringify every element. Use null as a placeholder
|
||||
// for non-JSON values.
|
||||
|
||||
length = value.length;
|
||||
for (i = 0; i < length; i += 1) {
|
||||
partial[i] = str(i, value) || 'null';
|
||||
}
|
||||
|
||||
// Join all of the elements together, separated with commas, and wrap them in
|
||||
// brackets.
|
||||
|
||||
v = partial.length === 0
|
||||
? '[]'
|
||||
: gap
|
||||
? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
|
||||
: '[' + partial.join(',') + ']';
|
||||
gap = mind;
|
||||
return v;
|
||||
}
|
||||
|
||||
// If the replacer is an array, use it to select the members to be stringified.
|
||||
|
||||
if (rep && typeof rep === 'object') {
|
||||
length = rep.length;
|
||||
for (i = 0; i < length; i += 1) {
|
||||
if (typeof rep[i] === 'string') {
|
||||
k = rep[i];
|
||||
v = str(k, value);
|
||||
if (v) {
|
||||
partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
// Otherwise, iterate through all of the keys in the object.
|
||||
|
||||
for (k in value) {
|
||||
if (Object.prototype.hasOwnProperty.call(value, k)) {
|
||||
v = str(k, value);
|
||||
if (v) {
|
||||
partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Join all of the member texts together, separated with commas,
|
||||
// and wrap them in braces.
|
||||
|
||||
v = partial.length === 0
|
||||
? '{}'
|
||||
: gap
|
||||
? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
|
||||
: '{' + partial.join(',') + '}';
|
||||
gap = mind;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
// If the JSON object does not yet have a stringify method, give it one.
|
||||
|
||||
if (typeof JSON.stringify !== 'function') {
|
||||
JSON.stringify = function (value, replacer, space) {
|
||||
|
||||
// The stringify method takes a value and an optional replacer, and an optional
|
||||
// space parameter, and returns a JSON text. The replacer can be a function
|
||||
// that can replace values, or an array of strings that will select the keys.
|
||||
// A default replacer method can be provided. Use of the space parameter can
|
||||
// produce text that is more easily readable.
|
||||
|
||||
var i;
|
||||
gap = '';
|
||||
indent = '';
|
||||
|
||||
// If the space parameter is a number, make an indent string containing that
|
||||
// many spaces.
|
||||
|
||||
if (typeof space === 'number') {
|
||||
for (i = 0; i < space; i += 1) {
|
||||
indent += ' ';
|
||||
}
|
||||
|
||||
// If the space parameter is a string, it will be used as the indent string.
|
||||
|
||||
} else if (typeof space === 'string') {
|
||||
indent = space;
|
||||
}
|
||||
|
||||
// If there is a replacer, it must be a function or an array.
|
||||
// Otherwise, throw an error.
|
||||
|
||||
rep = replacer;
|
||||
if (replacer && typeof replacer !== 'function' &&
|
||||
(typeof replacer !== 'object' ||
|
||||
typeof replacer.length !== 'number')) {
|
||||
throw new Error('JSON.stringify');
|
||||
}
|
||||
|
||||
// Make a fake root object containing our value under the key of ''.
|
||||
// Return the result of stringifying the value.
|
||||
|
||||
return str('', {'': value});
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// If the JSON object does not yet have a parse method, give it one.
|
||||
|
||||
if (typeof JSON.parse !== 'function') {
|
||||
JSON.parse = function (text, reviver) {
|
||||
|
||||
// The parse method takes a text and an optional reviver function, and returns
|
||||
// a JavaScript value if the text is a valid JSON text.
|
||||
|
||||
var j;
|
||||
|
||||
function walk(holder, key) {
|
||||
|
||||
// The walk method is used to recursively walk the resulting structure so
|
||||
// that modifications can be made.
|
||||
|
||||
var k, v, value = holder[key];
|
||||
if (value && typeof value === 'object') {
|
||||
for (k in value) {
|
||||
if (Object.prototype.hasOwnProperty.call(value, k)) {
|
||||
v = walk(value, k);
|
||||
if (v !== undefined) {
|
||||
value[k] = v;
|
||||
} else {
|
||||
delete value[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return reviver.call(holder, key, value);
|
||||
}
|
||||
|
||||
|
||||
// Parsing happens in four stages. In the first stage, we replace certain
|
||||
// Unicode characters with escape sequences. JavaScript handles many characters
|
||||
// incorrectly, either silently deleting them, or treating them as line endings.
|
||||
|
||||
text = String(text);
|
||||
cx.lastIndex = 0;
|
||||
if (cx.test(text)) {
|
||||
text = text.replace(cx, function (a) {
|
||||
return '\\u' +
|
||||
('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
||||
});
|
||||
}
|
||||
|
||||
// In the second stage, we run the text against regular expressions that look
|
||||
// for non-JSON patterns. We are especially concerned with '()' and 'new'
|
||||
// because they can cause invocation, and '=' because it can cause mutation.
|
||||
// But just to be safe, we want to reject all unexpected forms.
|
||||
|
||||
// We split the second stage into 4 regexp operations in order to work around
|
||||
// crippling inefficiencies in IE's and Safari's regexp engines. First we
|
||||
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
|
||||
// replace all simple value tokens with ']' characters. Third, we delete all
|
||||
// open brackets that follow a colon or comma or that begin the text. Finally,
|
||||
// we look to see that the remaining characters are only whitespace or ']' or
|
||||
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
|
||||
|
||||
if (/^[\],:{}\s]*$/
|
||||
.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
|
||||
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
|
||||
.replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
|
||||
|
||||
// In the third stage we use the eval function to compile the text into a
|
||||
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
|
||||
// in JavaScript: it can begin a block or an object literal. We wrap the text
|
||||
// in parens to eliminate the ambiguity.
|
||||
|
||||
j = eval('(' + text + ')');
|
||||
|
||||
// In the optional fourth stage, we recursively walk the new structure, passing
|
||||
// each name/value pair to a reviver function for possible transformation.
|
||||
|
||||
return typeof reviver === 'function'
|
||||
? walk({'': j}, '')
|
||||
: j;
|
||||
}
|
||||
|
||||
// If the text is not JSON parseable, then a SyntaxError is thrown.
|
||||
|
||||
throw new SyntaxError('JSON.parse');
|
||||
};
|
||||
}
|
||||
}());
|
37
trunk/research/players/js/srs.log.js
Executable file
37
trunk/research/players/js/srs.log.js
Executable file
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
* log specified, there must be a log element as:
|
||||
<!-- for the log -->
|
||||
<div class="alert alert-info fade in" id="txt_log">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<strong><span id="txt_log_title">标题:</span></strong>
|
||||
<span id="txt_log_msg">日志内容</span>
|
||||
</div>
|
||||
*/
|
||||
var srs_log_disabled = false;
|
||||
function info(desc) {
|
||||
if (srs_log_disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
$("#txt_log").addClass("alert-info").removeClass("alert-error").removeClass("alert-warn");
|
||||
$("#txt_log_title").text("Info:");
|
||||
$("#txt_log_msg").html(desc);
|
||||
}
|
||||
function warn(code, desc) {
|
||||
if (srs_log_disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
$("#txt_log").removeClass("alert-info").removeClass("alert-error").addClass("alert-warn");
|
||||
$("#txt_log_title").text("Warn:");
|
||||
$("#txt_log_msg").html("code: " + code + ", " + desc);
|
||||
}
|
||||
function error(code, desc) {
|
||||
if (srs_log_disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
$("#txt_log").removeClass("alert-info").addClass("alert-error").removeClass("alert-warn");
|
||||
$("#txt_log_title").text("Error:");
|
||||
$("#txt_log_msg").html("code: " + code + ", " + desc);
|
||||
}
|
363
trunk/research/players/js/srs.page.js
Executable file
363
trunk/research/players/js/srs.page.js
Executable file
|
@ -0,0 +1,363 @@
|
|||
//////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* player specified size.
|
||||
*/
|
||||
function srs_get_player_modal() { return 740; }
|
||||
function srs_get_player_width() { return srs_get_player_modal() - 30; }
|
||||
function srs_get_player_height() { return srs_get_player_width() * 9 / 19; }
|
||||
|
||||
// to query the swf anti cache.
|
||||
function srs_get_version_code() { return "1.17"; }
|
||||
// get the default vhost for players.
|
||||
function srs_get_player_vhost() { return "players"; }
|
||||
// the api server port, for chat room.
|
||||
function srs_get_api_server_port() { return 8085; }
|
||||
// get the stream published to vhost,
|
||||
// generally we need to transcode the stream to support HLS and filters.
|
||||
// for example, src_vhost is "players", we transcode stream to vhost "players_pub".
|
||||
// if not equals to the player vhost, return the orignal vhost.
|
||||
function srs_get_player_publish_vhost(src_vhost) { return (src_vhost != srs_get_player_vhost())? src_vhost:(src_vhost + "_pub"); }
|
||||
// for chat, use rtmp only vhost, low latecy, without gop cache.
|
||||
function srs_get_player_chat_vhost(src_vhost) { return (src_vhost != srs_get_player_vhost())? src_vhost:(src_vhost + "_pub_rtmp"); }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* update the navigator, add same query string.
|
||||
*/
|
||||
function update_nav() {
|
||||
$("#srs_index").attr("href", "index.html" + window.location.search);
|
||||
$("#nav_srs_player").attr("href", "srs_player.html" + window.location.search);
|
||||
$("#nav_srs_publisher").attr("href", "srs_publisher.html" + window.location.search);
|
||||
$("#nav_srs_chat").attr("href", "srs_chat.html" + window.location.search);
|
||||
$("#nav_srs_bwt").attr("href", "srs_bwt.html" + window.location.search);
|
||||
$("#nav_jwplayer6").attr("href", "jwplayer6.html" + window.location.search);
|
||||
$("#nav_osmf").attr("href", "osmf.html" + window.location.search);
|
||||
$("#nav_vlc").attr("href", "vlc.html" + window.location.search);
|
||||
}
|
||||
|
||||
/**
|
||||
@param server the ip of server. default to window.location.hostname
|
||||
@param vhost the vhost of rtmp. default to window.location.hostname
|
||||
@param port the port of rtmp. default to 1935
|
||||
@param app the app of rtmp. default to live.
|
||||
@param stream the stream of rtmp. default to livestream.
|
||||
*/
|
||||
function build_default_rtmp_url() {
|
||||
var query = parse_query_string();
|
||||
|
||||
var server = (query.server == undefined)? window.location.hostname:query.server;
|
||||
var port = (query.port == undefined)? 1935:query.port;
|
||||
var vhost = (query.vhost == undefined)? window.location.hostname:query.vhost;
|
||||
var app = (query.app == undefined)? "live":query.app;
|
||||
var stream = (query.stream == undefined)? "livestream":query.stream;
|
||||
|
||||
if (server == vhost || vhost == "") {
|
||||
return "rtmp://" + server + ":" + port + "/" + app + "/" + stream;
|
||||
} else {
|
||||
return "rtmp://" + server + ":" + port + "/" + app + "...vhost..." + vhost + "/" + stream;
|
||||
}
|
||||
}
|
||||
// for the chat to init the publish url.
|
||||
function build_default_publish_rtmp_url() {
|
||||
var query = parse_query_string();
|
||||
|
||||
var server = (query.server == undefined)? window.location.hostname:query.server;
|
||||
var port = (query.port == undefined)? 1935:query.port;
|
||||
var vhost = (query.vhost == undefined)? window.location.hostname:query.vhost;
|
||||
var app = (query.app == undefined)? "live":query.app;
|
||||
var stream = (query.stream == undefined)? "livestream":query.stream;
|
||||
|
||||
if (server == vhost || vhost == "") {
|
||||
return "rtmp://" + server + ":" + port + "/" + app + "/" + stream;
|
||||
} else {
|
||||
vhost = srs_get_player_chat_vhost(vhost);
|
||||
return "rtmp://" + server + ":" + port + "/" + app + "...vhost..." + vhost + "/" + stream;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@param server the ip of server. default to window.location.hostname
|
||||
@param vhost the vhost of hls. default to window.location.hostname
|
||||
@param hls_vhost the vhost of hls. override the server if specified.
|
||||
@param hls_port the port of hls. default to window.location.port
|
||||
@param app the app of hls. default to live.
|
||||
@param stream the stream of hls. default to livestream.
|
||||
*/
|
||||
function build_default_hls_url() {
|
||||
var query = parse_query_string();
|
||||
|
||||
// for http, use hls_vhost to override server if specified.
|
||||
var server = window.location.hostname;
|
||||
if (query.server != undefined) {
|
||||
server = query.server;
|
||||
} else if (query.hls_vhost != undefined) {
|
||||
server = query.hls_vhost;
|
||||
}
|
||||
|
||||
var port = (query.hls_port == undefined)? window.location.port:query.hls_port;
|
||||
var app = (query.app == undefined)? "live":query.app;
|
||||
var stream = (query.stream == undefined)? "livestream":query.stream;
|
||||
|
||||
if (port == "" || port == null || port == undefined) {
|
||||
port = 80;
|
||||
}
|
||||
|
||||
return "http://" + server + ":" + port + "/" + app + "/" + stream + ".m3u8";
|
||||
}
|
||||
|
||||
/**
|
||||
* initialize the page.
|
||||
* @param rtmp_url the div id contains the rtmp stream url to play
|
||||
* @param hls_url the div id contains the hls stream url to play
|
||||
* @param modal_player the div id contains the modal player
|
||||
*/
|
||||
function srs_init(rtmp_url, hls_url, modal_player) {
|
||||
update_nav();
|
||||
|
||||
if (rtmp_url) {
|
||||
$(rtmp_url).val(build_default_rtmp_url());
|
||||
}
|
||||
if (hls_url) {
|
||||
$(hls_url).val(build_default_hls_url());
|
||||
}
|
||||
if (modal_player) {
|
||||
$(modal_player).width(srs_get_player_modal() + "px");
|
||||
$(modal_player).css("margin-left", "-" + srs_get_player_modal() / 2 +"px");
|
||||
}
|
||||
}
|
||||
// for the chat to init the publish url.
|
||||
function srs_init_publish(rtmp_url) {
|
||||
update_nav();
|
||||
|
||||
if (rtmp_url) {
|
||||
$(rtmp_url).val(build_default_publish_rtmp_url());
|
||||
}
|
||||
}
|
||||
|
||||
// check whether can republish
|
||||
function srs_can_republish() {
|
||||
var browser = get_browser_agents();
|
||||
|
||||
if (browser.Chrome || browser.Firefox) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (browser.MSIE || browser.QQBrowser) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// without default values set.
|
||||
function srs_initialize_codec_page(
|
||||
cameras, microphones,
|
||||
sl_cameras, sl_microphones, sl_vcodec, sl_profile, sl_level, sl_gop, sl_size, sl_fps, sl_bitrate
|
||||
) {
|
||||
$(sl_cameras).empty();
|
||||
for (var i = 0; i < cameras.length; i++) {
|
||||
$(sl_cameras).append("<option value='" + i + "'>" + cameras[i] + "</option");
|
||||
}
|
||||
// optional: select the except matches
|
||||
matchs = ["virtual"];
|
||||
for (var i = 0; i < cameras.length; i++) {
|
||||
for (var j = 0; j < matchs.length; j++) {
|
||||
if (cameras[i].toLowerCase().indexOf(matchs[j]) == -1) {
|
||||
$(sl_cameras + " option[value='" + i + "']").attr("selected", true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j < matchs.length) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// optional: select the first matched.
|
||||
matchs = ["truevision", "integrated"];
|
||||
for (var i = 0; i < cameras.length; i++) {
|
||||
for (var j = 0; j < matchs.length; j++) {
|
||||
if (cameras[i].toLowerCase().indexOf(matchs[j]) >= 0) {
|
||||
$(sl_cameras + " option[value='" + i + "']").attr("selected", true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j < matchs.length) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$(sl_microphones).empty();
|
||||
for (var i = 0; i < microphones.length; i++) {
|
||||
$(sl_microphones).append("<option value='" + i + "'>" + microphones[i] + "</option");
|
||||
}
|
||||
// optional: select the except matches
|
||||
matchs = ["default"];
|
||||
for (var i = 0; i < microphones.length; i++) {
|
||||
for (var j = 0; j < matchs.length; j++) {
|
||||
if (microphones[i].toLowerCase().indexOf(matchs[j]) == -1) {
|
||||
$(sl_microphones + " option[value='" + i + "']").attr("selected", true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j < matchs.length) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// optional: select the first matched.
|
||||
matchs = ["realtek", "内置式麦克风"];
|
||||
for (var i = 0; i < microphones.length; i++) {
|
||||
for (var j = 0; j < matchs.length; j++) {
|
||||
if (microphones[i].toLowerCase().indexOf(matchs[j]) >= 0) {
|
||||
$(sl_microphones + " option[value='" + i + "']").attr("selected", true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j < matchs.length) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$(sl_vcodec).empty();
|
||||
var vcodecs = ["h264", "vp6"];
|
||||
vcodecs = ["h264"]; // h264 only.
|
||||
for (var i = 0; i < vcodecs.length; i++) {
|
||||
$(sl_vcodec).append("<option value='" + vcodecs[i] + "'>" + vcodecs[i] + "</option");
|
||||
}
|
||||
|
||||
$(sl_profile).empty();
|
||||
var profiles = ["baseline", "main"];
|
||||
for (var i = 0; i < profiles.length; i++) {
|
||||
$(sl_profile).append("<option value='" + profiles[i] + "'>" + profiles[i] + "</option");
|
||||
}
|
||||
|
||||
$(sl_level).empty();
|
||||
var levels = ["1", "1b", "1.1", "1.2", "1.3",
|
||||
"2", "2.1", "2.2", "3", "3.1", "3.2", "4", "4.1", "4.2", "5", "5.1"];
|
||||
for (var i = 0; i < levels.length; i++) {
|
||||
$(sl_level).append("<option value='" + levels[i] + "'>" + levels[i] + "</option");
|
||||
}
|
||||
|
||||
$(sl_gop).empty();
|
||||
var gops = ["0.3", "0.5", "1", "2", "3", "4",
|
||||
"5", "6", "7", "8", "9", "10", "15", "20"];
|
||||
for (var i = 0; i < gops.length; i++) {
|
||||
$(sl_gop).append("<option value='" + gops[i] + "'>" + gops[i] + "秒</option");
|
||||
}
|
||||
|
||||
$(sl_size).empty();
|
||||
var sizes = ["176x144", "320x240", "352x240",
|
||||
"352x288", "480x360", "640x480", "720x480", "720x576", "800x600",
|
||||
"1024x768", "1280x720", "1360x768", "1920x1080"];
|
||||
for (i = 0; i < sizes.length; i++) {
|
||||
$(sl_size).append("<option value='" + sizes[i] + "'>" + sizes[i] + "</option");
|
||||
}
|
||||
|
||||
$(sl_fps).empty();
|
||||
var fpses = ["5", "10", "15", "20", "24", "25", "29.97", "30"];
|
||||
for (i = 0; i < fpses.length; i++) {
|
||||
$(sl_fps).append("<option value='" + fpses[i] + "'>" + Number(fpses[i]).toFixed(2) + " 帧/秒</option");
|
||||
}
|
||||
|
||||
$(sl_bitrate).empty();
|
||||
var bitrates = ["50", "200", "350", "500", "650", "800",
|
||||
"950", "1000", "1200", "1500", "1800", "2000", "3000", "5000"];
|
||||
for (i = 0; i < bitrates.length; i++) {
|
||||
$(sl_bitrate).append("<option value='" + bitrates[i] + "'>" + bitrates[i] + " kbps</option");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* when publisher ready, init the page elements.
|
||||
*/
|
||||
function srs_publisher_initialize_page(
|
||||
cameras, microphones,
|
||||
sl_cameras, sl_microphones, sl_vcodec, sl_profile, sl_level, sl_gop, sl_size, sl_fps, sl_bitrate
|
||||
) {
|
||||
srs_initialize_codec_page(
|
||||
cameras, microphones,
|
||||
sl_cameras, sl_microphones, sl_vcodec, sl_profile, sl_level, sl_gop, sl_size, sl_fps, sl_bitrate
|
||||
);
|
||||
|
||||
//var profiles = ["baseline", "main"];
|
||||
$(sl_profile + " option[value='main']").attr("selected", true);
|
||||
|
||||
//var levels = ["1", "1b", "1.1", "1.2", "1.3",
|
||||
// "2", "2.1", "2.2", "3", "3.1", "3.2", "4", "4.1", "4.2", "5", "5.1"];
|
||||
$(sl_level + " option[value='4.1']").attr("selected", true);
|
||||
|
||||
//var gops = ["0.3", "0.5", "1", "2", "3", "4",
|
||||
// "5", "6", "7", "8", "9", "10", "15", "20"];
|
||||
$(sl_gop + " option[value='10']").attr("selected", true);
|
||||
|
||||
//var sizes = ["176x144", "320x240", "352x240",
|
||||
// "352x288", "480x360", "640x480", "720x480", "720x576", "800x600",
|
||||
// "1024x768", "1280x720", "1360x768", "1920x1080"];
|
||||
$(sl_size + " option[value='640x480']").attr("selected", true);
|
||||
|
||||
//var fpses = ["5", "10", "15", "20", "24", "25", "29.97", "30"];
|
||||
$(sl_fps + " option[value='20']").attr("selected", true);
|
||||
|
||||
//var bitrates = ["50", "200", "350", "500", "650", "800",
|
||||
// "950", "1000", "1200", "1500", "1800", "2000", "3000", "5000"];
|
||||
$(sl_bitrate + " option[value='500']").attr("selected", true);
|
||||
}
|
||||
/**
|
||||
* for chat, use low latecy settings.
|
||||
*/
|
||||
function srs_chat_initialize_page(
|
||||
cameras, microphones,
|
||||
sl_cameras, sl_microphones, sl_vcodec, sl_profile, sl_level, sl_gop, sl_size, sl_fps, sl_bitrate
|
||||
) {
|
||||
srs_initialize_codec_page(
|
||||
cameras, microphones,
|
||||
sl_cameras, sl_microphones, sl_vcodec, sl_profile, sl_level, sl_gop, sl_size, sl_fps, sl_bitrate
|
||||
);
|
||||
|
||||
//var profiles = ["baseline", "main"];
|
||||
$(sl_profile + " option[value='baseline']").attr("selected", true);
|
||||
|
||||
//var levels = ["1", "1b", "1.1", "1.2", "1.3",
|
||||
// "2", "2.1", "2.2", "3", "3.1", "3.2", "4", "4.1", "4.2", "5", "5.1"];
|
||||
$(sl_level + " option[value='3.1']").attr("selected", true);
|
||||
|
||||
//var gops = ["0.3", "0.5", "1", "2", "3", "4",
|
||||
// "5", "6", "7", "8", "9", "10", "15", "20"];
|
||||
$(sl_gop + " option[value='2']").attr("selected", true);
|
||||
|
||||
//var sizes = ["176x144", "320x240", "352x240",
|
||||
// "352x288", "480x360", "640x480", "720x480", "720x576", "800x600",
|
||||
// "1024x768", "1280x720", "1360x768", "1920x1080"];
|
||||
$(sl_size + " option[value='480x360']").attr("selected", true);
|
||||
|
||||
//var fpses = ["5", "10", "15", "20", "24", "25", "29.97", "30"];
|
||||
$(sl_fps + " option[value='15']").attr("selected", true);
|
||||
|
||||
//var bitrates = ["50", "200", "350", "500", "650", "800",
|
||||
// "950", "1000", "1200", "1500", "1800", "2000", "3000", "5000"];
|
||||
$(sl_bitrate + " option[value='350']").attr("selected", true);
|
||||
}
|
||||
/**
|
||||
* get the vcodec and acodec.
|
||||
*/
|
||||
function srs_publiser_get_codec(
|
||||
vcodec, acodec,
|
||||
sl_cameras, sl_microphones, sl_vcodec, sl_profile, sl_level, sl_gop, sl_size, sl_fps, sl_bitrate
|
||||
) {
|
||||
acodec.device_code = $(sl_microphones).val();
|
||||
acodec.device_name = $(sl_microphones).text();
|
||||
|
||||
vcodec.device_code = $(sl_cameras).find("option:selected").val();
|
||||
vcodec.device_name = $(sl_cameras).find("option:selected").text();
|
||||
|
||||
vcodec.codec = $(sl_vcodec).find("option:selected").val();
|
||||
vcodec.profile = $(sl_profile).find("option:selected").val();
|
||||
vcodec.level = $(sl_level).find("option:selected").val();
|
||||
vcodec.fps = $(sl_fps).find("option:selected").val();
|
||||
vcodec.gop = $(sl_gop).find("option:selected").val();
|
||||
vcodec.size = $(sl_size).find("option:selected").val();
|
||||
vcodec.bitrate = $(sl_bitrate).find("option:selected").val();
|
||||
}
|
194
trunk/research/players/js/srs.player.js
Executable file
194
trunk/research/players/js/srs.player.js
Executable file
|
@ -0,0 +1,194 @@
|
|||
/**
|
||||
* the SrsPlayer object.
|
||||
* @param container the html container id.
|
||||
* @param width a float value specifies the width of player.
|
||||
* @param height a float value specifies the height of player.
|
||||
* @param private_object [optional] an object that used as private object,
|
||||
* for example, the logic chat object which owner this player.
|
||||
*/
|
||||
function SrsPlayer(container, width, height, private_object) {
|
||||
if (!SrsPlayer.__id) {
|
||||
SrsPlayer.__id = 100;
|
||||
}
|
||||
if (!SrsPlayer.__players) {
|
||||
SrsPlayer.__players = [];
|
||||
}
|
||||
|
||||
SrsPlayer.__players.push(this);
|
||||
|
||||
this.private_object = private_object;
|
||||
this.container = container;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.id = SrsPlayer.__id++;
|
||||
this.stream_url = null;
|
||||
this.buffer_time = 0.8; // default to 0.8
|
||||
this.volume = 1.0; // default to 100%
|
||||
this.callbackObj = null;
|
||||
|
||||
// callback set the following values.
|
||||
this.meatadata = {}; // for on_player_metadata
|
||||
this.time = 0; // current stream time.
|
||||
this.buffer_length = 0; // current stream buffer length.
|
||||
}
|
||||
/**
|
||||
* user can set some callback, then start the player.
|
||||
* @param url the default url.
|
||||
* callbacks:
|
||||
* on_player_ready():int, when srs player ready, user can play.
|
||||
* on_player_metadata(metadata:Object):int, when srs player get metadata.
|
||||
*/
|
||||
SrsPlayer.prototype.start = function(url) {
|
||||
if (url) {
|
||||
this.stream_url = url;
|
||||
}
|
||||
|
||||
// embed the flash.
|
||||
var flashvars = {};
|
||||
flashvars.id = this.id;
|
||||
flashvars.on_player_ready = "__srs_on_player_ready";
|
||||
flashvars.on_player_metadata = "__srs_on_player_metadata";
|
||||
flashvars.on_player_timer = "__srs_on_player_timer";
|
||||
|
||||
var params = {};
|
||||
params.wmode = "opaque";
|
||||
params.allowFullScreen = "true";
|
||||
params.allowScriptAccess = "always";
|
||||
|
||||
var attributes = {};
|
||||
|
||||
var self = this;
|
||||
|
||||
swfobject.embedSWF(
|
||||
"srs_player/release/srs_player.swf?_version="+srs_get_version_code(),
|
||||
this.container,
|
||||
this.width, this.height,
|
||||
"11.1", "js/AdobeFlashPlayerInstall.swf",
|
||||
flashvars, params, attributes,
|
||||
function(callbackObj){
|
||||
self.callbackObj = callbackObj;
|
||||
}
|
||||
);
|
||||
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* play the stream.
|
||||
* @param stream_url the url of stream, rtmp or http.
|
||||
* @param volume the volume, 0 is mute, 1 is 100%, 2 is 200%.
|
||||
*/
|
||||
SrsPlayer.prototype.play = function(url, volume) {
|
||||
this.stop();
|
||||
SrsPlayer.__players.push(this);
|
||||
|
||||
if (url) {
|
||||
this.stream_url = url;
|
||||
}
|
||||
|
||||
// volume maybe 0, so never use if(volume) to check its value.
|
||||
if (volume != null && volume != undefined) {
|
||||
this.volume = volume;
|
||||
}
|
||||
|
||||
this.callbackObj.ref.__play(this.stream_url, this.width, this.height, this.buffer_time, this.volume);
|
||||
}
|
||||
SrsPlayer.prototype.stop = function() {
|
||||
for (var i = 0; i < SrsPlayer.__players.length; i++) {
|
||||
var player = SrsPlayer.__players[i];
|
||||
|
||||
if (player.id != this.id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SrsPlayer.__players.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
this.callbackObj.ref.__stop();
|
||||
}
|
||||
SrsPlayer.prototype.pause = function() {
|
||||
this.callbackObj.ref.__pause();
|
||||
}
|
||||
SrsPlayer.prototype.resume = function() {
|
||||
this.callbackObj.ref.__resume();
|
||||
}
|
||||
/**
|
||||
* to set the DAR, for example, DAR=16:9 where num=16,den=9.
|
||||
* @param num, for example, 16.
|
||||
* use metadata width if 0.
|
||||
* use user specified width if -1.
|
||||
* @param den, for example, 9.
|
||||
* use metadata height if 0.
|
||||
* use user specified height if -1.
|
||||
*/
|
||||
SrsPlayer.prototype.set_dar = function(num, den) {
|
||||
this.callbackObj.ref.__set_dar(num, den);
|
||||
}
|
||||
/**
|
||||
* set the fullscreen size data.
|
||||
* @refer the refer fullscreen mode. it can be:
|
||||
* video: use video orignal size.
|
||||
* screen: use screen size to rescale video.
|
||||
* @param percent, the rescale percent, where
|
||||
* 100 means 100%.
|
||||
*/
|
||||
SrsPlayer.prototype.set_fs = function(refer, percent) {
|
||||
this.callbackObj.ref.__set_fs(refer, percent);
|
||||
}
|
||||
/**
|
||||
* set the stream buffer time in seconds.
|
||||
* @buffer_time the buffer time in seconds.
|
||||
*/
|
||||
SrsPlayer.prototype.set_bt = function(buffer_time) {
|
||||
this.buffer_time = buffer_time;
|
||||
this.callbackObj.ref.__set_bt(buffer_time);
|
||||
}
|
||||
SrsPlayer.prototype.on_player_ready = function() {
|
||||
}
|
||||
SrsPlayer.prototype.on_player_metadata = function(metadata) {
|
||||
// ignore.
|
||||
}
|
||||
SrsPlayer.prototype.on_player_timer = function(time, buffer_length) {
|
||||
// ignore.
|
||||
}
|
||||
function __srs_find_player(id) {
|
||||
for (var i = 0; i < SrsPlayer.__players.length; i++) {
|
||||
var player = SrsPlayer.__players[i];
|
||||
|
||||
if (player.id != id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
throw new Error("player not found. id=" + id);
|
||||
}
|
||||
function __srs_on_player_ready(id) {
|
||||
var player = __srs_find_player(id);
|
||||
player.on_player_ready();
|
||||
}
|
||||
function __srs_on_player_metadata(id, metadata) {
|
||||
var player = __srs_find_player(id);
|
||||
|
||||
// user may override the on_player_metadata,
|
||||
// so set the data before invoke it.
|
||||
player.metadata = metadata;
|
||||
|
||||
player.on_player_metadata(metadata);
|
||||
}
|
||||
function __srs_on_player_timer(id, time, buffer_length) {
|
||||
var player = __srs_find_player(id);
|
||||
|
||||
buffer_length = Math.max(0, buffer_length);
|
||||
buffer_length = Math.min(player.buffer_time, buffer_length);
|
||||
|
||||
time = Math.max(0, time);
|
||||
|
||||
// user may override the on_player_timer,
|
||||
// so set the data before invoke it.
|
||||
player.time = time;
|
||||
player.buffer_length = buffer_length;
|
||||
|
||||
player.on_player_timer(time, buffer_length);
|
||||
}
|
173
trunk/research/players/js/srs.publisher.js
Executable file
173
trunk/research/players/js/srs.publisher.js
Executable file
|
@ -0,0 +1,173 @@
|
|||
/**
|
||||
* the SrsPublisher object.
|
||||
* @param container the html container id.
|
||||
* @param width a float value specifies the width of publisher.
|
||||
* @param height a float value specifies the height of publisher.
|
||||
* @param private_object [optional] an object that used as private object,
|
||||
* for example, the logic chat object which owner this publisher.
|
||||
*/
|
||||
function SrsPublisher(container, width, height, private_object) {
|
||||
if (!SrsPublisher.__id) {
|
||||
SrsPublisher.__id = 100;
|
||||
}
|
||||
if (!SrsPublisher.__publishers) {
|
||||
SrsPublisher.__publishers = [];
|
||||
}
|
||||
|
||||
SrsPublisher.__publishers.push(this);
|
||||
|
||||
this.private_object = private_object;
|
||||
this.container = container;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.id = SrsPublisher.__id++;
|
||||
this.callbackObj = null;
|
||||
|
||||
// set the values when publish.
|
||||
this.url = null;
|
||||
this.vcodec = {};
|
||||
this.acodec = {};
|
||||
|
||||
// callback set the following values.
|
||||
this.cameras = [];
|
||||
this.microphones = [];
|
||||
this.code = 0;
|
||||
|
||||
// error code defines.
|
||||
this.errors = {
|
||||
"100": "无法获取指定的摄像头。", //error_camera_get
|
||||
"101": "无法获取指定的麦克风。", //error_microphone_get
|
||||
"102": "摄像头为禁用状态,推流时请允许flash访问摄像头。", //error_camera_muted
|
||||
"103": "服务器关闭了连接。", //error_connection_closed
|
||||
"104": "服务器连接失败。", //error_connection_failed
|
||||
"199": "未知错误。"
|
||||
};
|
||||
}
|
||||
/**
|
||||
* user can set some callback, then start the publisher.
|
||||
* callbacks:
|
||||
* on_publisher_ready(cameras, microphones):int, when srs publisher ready, user can publish.
|
||||
* on_publisher_error(code, desc):int, when srs publisher error, callback this method.
|
||||
* on_publisher_warn(code, desc):int, when srs publisher warn, callback this method.
|
||||
*/
|
||||
SrsPublisher.prototype.start = function() {
|
||||
// embed the flash.
|
||||
var flashvars = {};
|
||||
flashvars.id = this.id;
|
||||
flashvars.width = this.width;
|
||||
flashvars.height = this.height;
|
||||
flashvars.on_publisher_ready = "__srs_on_publisher_ready";
|
||||
flashvars.on_publisher_error = "__srs_on_publisher_error";
|
||||
flashvars.on_publisher_warn = "__srs_on_publisher_warn";
|
||||
|
||||
var params = {};
|
||||
params.wmode = "opaque";
|
||||
params.allowFullScreen = "true";
|
||||
params.allowScriptAccess = "always";
|
||||
|
||||
var attributes = {};
|
||||
|
||||
var self = this;
|
||||
|
||||
swfobject.embedSWF(
|
||||
"srs_publisher/release/srs_publisher.swf?_version="+srs_get_version_code(),
|
||||
this.container,
|
||||
this.width, this.height,
|
||||
"11.1", "js/AdobeFlashPlayerInstall.swf",
|
||||
flashvars, params, attributes,
|
||||
function(callbackObj){
|
||||
self.callbackObj = callbackObj;
|
||||
}
|
||||
);
|
||||
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* publish stream to server.
|
||||
* @param url a string indicates the rtmp url to publish.
|
||||
* @param vcodec an object contains the video codec info.
|
||||
* @param acodec an object contains the audio codec info.
|
||||
*/
|
||||
SrsPublisher.prototype.publish = function(url, vcodec, acodec) {
|
||||
this.stop();
|
||||
SrsPublisher.__publishers.push(this);
|
||||
|
||||
if (url) {
|
||||
this.url = url;
|
||||
}
|
||||
if (vcodec) {
|
||||
this.vcodec = vcodec;
|
||||
}
|
||||
if (acodec) {
|
||||
this.acodec = acodec;
|
||||
}
|
||||
|
||||
this.callbackObj.ref.__publish(this.url, this.width, this.height, this.vcodec, this.acodec);
|
||||
}
|
||||
SrsPublisher.prototype.stop = function() {
|
||||
for (var i = 0; i < SrsPublisher.__publishers.length; i++) {
|
||||
var player = SrsPublisher.__publishers[i];
|
||||
|
||||
if (player.id != this.id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SrsPublisher.__publishers.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
this.callbackObj.ref.__stop();
|
||||
}
|
||||
/**
|
||||
* when publisher ready.
|
||||
* @param cameras a string array contains the names of cameras.
|
||||
* @param microphones a string array contains the names of microphones.
|
||||
*/
|
||||
SrsPublisher.prototype.on_publisher_ready = function(cameras, microphones) {
|
||||
}
|
||||
/**
|
||||
* when publisher error.
|
||||
* @code the error code.
|
||||
* @desc the error desc message.
|
||||
*/
|
||||
SrsPublisher.prototype.on_publisher_error = function(code, desc) {
|
||||
throw new Error("publisher error. code=" + code + ", desc=" + desc);
|
||||
}
|
||||
SrsPublisher.prototype.on_publisher_warn = function(code, desc) {
|
||||
throw new Error("publisher warn. code=" + code + ", desc=" + desc);
|
||||
}
|
||||
function __srs_find_publisher(id) {
|
||||
for (var i = 0; i < SrsPublisher.__publishers.length; i++) {
|
||||
var publisher = SrsPublisher.__publishers[i];
|
||||
|
||||
if (publisher.id != id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return publisher;
|
||||
}
|
||||
|
||||
throw new Error("publisher not found. id=" + id);
|
||||
}
|
||||
function __srs_on_publisher_ready(id, cameras, microphones) {
|
||||
var publisher = __srs_find_publisher(id);
|
||||
|
||||
publisher.cameras = cameras;
|
||||
publisher.microphones = microphones;
|
||||
|
||||
publisher.on_publisher_ready(cameras, microphones);
|
||||
}
|
||||
function __srs_on_publisher_error(id, code) {
|
||||
var publisher = __srs_find_publisher(id);
|
||||
|
||||
publisher.code = code;
|
||||
|
||||
publisher.on_publisher_error(code, publisher.errors[""+code]);
|
||||
}
|
||||
function __srs_on_publisher_warn(id, code) {
|
||||
var publisher = __srs_find_publisher(id);
|
||||
|
||||
publisher.code = code;
|
||||
|
||||
publisher.on_publisher_warn(code, publisher.errors[""+code]);
|
||||
}
|
139
trunk/research/players/js/srs.utility.js
Executable file
139
trunk/research/players/js/srs.utility.js
Executable file
|
@ -0,0 +1,139 @@
|
|||
/**
|
||||
* padding the output.
|
||||
* padding(3, 5, '0') is 00003
|
||||
* padding(3, 5, 'x') is xxxx3
|
||||
* @see http://blog.csdn.net/win_lin/article/details/12065413
|
||||
*/
|
||||
function padding(number, length, prefix) {
|
||||
if(String(number).length >= length){
|
||||
return String(number);
|
||||
}
|
||||
return padding(prefix+number, length, prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* parse the query string to object.
|
||||
*/
|
||||
function parse_query_string(){
|
||||
var obj = {};
|
||||
|
||||
// add the uri object.
|
||||
// parse the host(hostname:http_port), pathname(dir/filename)
|
||||
obj.host = window.location.host;
|
||||
obj.hostname = window.location.hostname;
|
||||
obj.http_port = (window.location.port == "")? 80:window.location.port;
|
||||
obj.pathname = window.location.pathname;
|
||||
if (obj.pathname.lastIndexOf("/") <= 0) {
|
||||
obj.dir = "/";
|
||||
obj.filename = "";
|
||||
} else {
|
||||
obj.dir = obj.pathname.substr(0, obj.pathname.lastIndexOf("/"));
|
||||
obj.filename = obj.pathname.substr(obj.pathname.lastIndexOf("/"));
|
||||
}
|
||||
|
||||
// pure user query object.
|
||||
obj.user_query = {};
|
||||
|
||||
// parse the query string.
|
||||
var query_string = String(window.location.search).replace(" ", "").split("?")[1];
|
||||
if(query_string == undefined){
|
||||
return obj;
|
||||
}
|
||||
|
||||
var queries = query_string.split("&");
|
||||
$(queries).each(function(){
|
||||
var query = this.split("=");
|
||||
obj[query[0]] = query[1];
|
||||
obj.user_query[query[0]] = query[1];
|
||||
});
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* parse the rtmp url,
|
||||
* for example: rtmp://demo.srs.com:1935/live...vhost...players/livestream
|
||||
* @return object {server, port, vhost, app, stream}
|
||||
*/
|
||||
function srs_parse_rtmp_url(rtmp_url) {
|
||||
// @see: http://stackoverflow.com/questions/10469575/how-to-use-location-object-to-parse-url-without-redirecting-the-page-in-javascri
|
||||
var a = document.createElement("a");
|
||||
a.href = rtmp_url.replace("rtmp://", "http://");
|
||||
|
||||
var vhost = a.hostname;
|
||||
var port = (a.port == "")? "1935":a.port;
|
||||
var app = a.pathname.substr(1, a.pathname.lastIndexOf("/") - 1);
|
||||
var stream = a.pathname.substr(a.pathname.lastIndexOf("/") + 1);
|
||||
|
||||
// parse the vhost in the params of app, that srs supports.
|
||||
app = app.replace("...vhost...", "?vhost=");
|
||||
if (app.indexOf("?") >= 0) {
|
||||
var params = app.substr(app.indexOf("?"));
|
||||
app = app.substr(0, app.indexOf("?"));
|
||||
|
||||
if (params.indexOf("vhost=") > 0) {
|
||||
vhost = params.substr(params.indexOf("vhost=") + "vhost=".length);
|
||||
if (vhost.indexOf("&") > 0) {
|
||||
vhost = vhost.substr(0, vhost.indexOf("&"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var ret = {
|
||||
server: a.hostname, port: port,
|
||||
vhost: vhost, app: app, stream: stream
|
||||
};
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the agent.
|
||||
* @return an object specifies some browser.
|
||||
* for example, get_browser_agents().MSIE
|
||||
*/
|
||||
function get_browser_agents() {
|
||||
var agent = navigator.userAgent;
|
||||
|
||||
/**
|
||||
WindowsPC platform, Win7:
|
||||
chrome 31.0.1650.63:
|
||||
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36
|
||||
(KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36
|
||||
firefox 23.0.1:
|
||||
Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20100101
|
||||
Firefox/23.0
|
||||
safari 5.1.7(7534.57.2):
|
||||
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.57.2
|
||||
(KHTML, like Gecko) Version/5.1.7 Safari/534.57.2
|
||||
opera 15.0.1147.153:
|
||||
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36
|
||||
(KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36
|
||||
OPR/15.0.1147.153
|
||||
360 6.2.1.272:
|
||||
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64;
|
||||
Trident/6.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729;
|
||||
.NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; .NET4.0C;
|
||||
.NET4.0E)
|
||||
IE 10.0.9200.16750(update: 10.0.12):
|
||||
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64;
|
||||
Trident/6.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729;
|
||||
.NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; .NET4.0C;
|
||||
.NET4.0E)
|
||||
*/
|
||||
|
||||
return {
|
||||
// platform
|
||||
Android: agent.indexOf("Android") != -1,
|
||||
Windows: agent.indexOf("Windows") != -1,
|
||||
iPhone: agent.indexOf("iPhone") != -1,
|
||||
// Windows Browsers
|
||||
Chrome: agent.indexOf("Chrome") != -1,
|
||||
Firefox: agent.indexOf("Firefox") != -1,
|
||||
QQBrowser: agent.indexOf("QQBrowser") != -1,
|
||||
MSIE: agent.indexOf("MSIE") != -1,
|
||||
// Android Browsers
|
||||
Opera: agent.indexOf("Presto") != -1,
|
||||
MQQBrowser: agent.indexOf("MQQBrowser") != -1
|
||||
};
|
||||
}
|
|
@ -6,7 +6,11 @@
|
|||
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/>
|
||||
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
|
||||
<script type="text/javascript" src="js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="js/srs.js"></script>
|
||||
<script type="text/javascript" src="js/srs.page.js"></script>
|
||||
<script type="text/javascript" src="js/srs.log.js"></script>
|
||||
<script type="text/javascript" src="js/srs.player.js"></script>
|
||||
<script type="text/javascript" src="js/srs.publisher.js"></script>
|
||||
<script type="text/javascript" src="js/srs.utility.js"></script>
|
||||
<style>
|
||||
body{
|
||||
padding-top: 55px;
|
||||
|
@ -78,7 +82,7 @@
|
|||
<div class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<a class="brand" href="index.html">SRS</a>
|
||||
<a id="srs_index" class="brand" href="#">SRS</a>
|
||||
<div class="nav-collapse collapse">
|
||||
<ul class="nav">
|
||||
<li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
|
||||
|
|
21
trunk/research/players/nginx_index.html
Executable file
21
trunk/research/players/nginx_index.html
Executable file
|
@ -0,0 +1,21 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>SRS</title>
|
||||
<meta charset="utf-8">
|
||||
<script type="text/javascript" src="players/js/jquery-1.10.2.min.js"></script>
|
||||
<script type="text/javascript" src="players/js/srs.page.js"></script>
|
||||
<script type="text/javascript" src="players/js/srs.utility.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
var query = parse_query_string();
|
||||
var url = window.location.protocol + "//" + query.hostname + ":" + srs_get_api_server_port() + "/index.html" + window.location.search;
|
||||
document.write("请确认api-server已经开启,跳转到api-server的页面(解决IE跨域问题)<br/>");
|
||||
document.write("正在跳转,若您的浏览器没有跳转,请点击: <a href='" + url + "'>" + url + "</a>");
|
||||
|
||||
setTimeout(function(){
|
||||
window.location.href = url;
|
||||
}, 3000);
|
||||
</script>
|
||||
</body>
|
|
@ -7,7 +7,11 @@
|
|||
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
|
||||
<script type="text/javascript" src="js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="js/swfobject.js"></script>
|
||||
<script type="text/javascript" src="js/srs.js"></script>
|
||||
<script type="text/javascript" src="js/srs.page.js"></script>
|
||||
<script type="text/javascript" src="js/srs.log.js"></script>
|
||||
<script type="text/javascript" src="js/srs.player.js"></script>
|
||||
<script type="text/javascript" src="js/srs.publisher.js"></script>
|
||||
<script type="text/javascript" src="js/srs.utility.js"></script>
|
||||
<style>
|
||||
body{
|
||||
padding-top: 55px;
|
||||
|
@ -74,7 +78,7 @@
|
|||
<div class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<a class="brand" href="index.html">SRS</a>
|
||||
<a id="srs_index" class="brand" href="#">SRS</a>
|
||||
<div class="nav-collapse collapse">
|
||||
<ul class="nav">
|
||||
<li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
|
||||
|
|
|
@ -7,7 +7,13 @@
|
|||
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
|
||||
<script type="text/javascript" src="js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="js/swfobject.js"></script>
|
||||
<script type="text/javascript" src="js/srs.js"></script>
|
||||
<script type="text/javascript" src="js/srs.page.js"></script>
|
||||
<script type="text/javascript" src="js/srs.log.js"></script>
|
||||
<script type="text/javascript" src="js/srs.player.js"></script>
|
||||
<script type="text/javascript" src="js/srs.publisher.js"></script>
|
||||
<script type="text/javascript" src="js/srs.utility.js"></script>
|
||||
<script type="text/javascript" src="js/srs.utility.js"></script>
|
||||
<script type="text/javascript" src="js/srs.bandwidth.js"></script>
|
||||
<style>
|
||||
body{
|
||||
padding-top: 55px;
|
||||
|
@ -119,7 +125,7 @@
|
|||
<div class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<a class="brand" href="index.html">SRS</a>
|
||||
<a id="srs_index" class="brand" href="#">SRS</a>
|
||||
<div class="nav-collapse collapse">
|
||||
<ul class="nav">
|
||||
<li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
|
||||
|
|
|
@ -7,11 +7,19 @@
|
|||
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
|
||||
<script type="text/javascript" src="js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="js/swfobject.js"></script>
|
||||
<script type="text/javascript" src="js/srs.js"></script>
|
||||
<script type="text/javascript" src="js/json2.js"></script>
|
||||
<script type="text/javascript" src="js/srs.page.js"></script>
|
||||
<script type="text/javascript" src="js/srs.log.js"></script>
|
||||
<script type="text/javascript" src="js/srs.player.js"></script>
|
||||
<script type="text/javascript" src="js/srs.publisher.js"></script>
|
||||
<script type="text/javascript" src="js/srs.utility.js"></script>
|
||||
<style>
|
||||
body{
|
||||
padding-top: 55px;
|
||||
}
|
||||
.accordion-group {
|
||||
width: 310px;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
var srs_publisher = null;
|
||||
|
@ -22,18 +30,36 @@
|
|||
var previous_chats = [];
|
||||
var no_play = false;
|
||||
|
||||
var query = parse_query_string();
|
||||
$(function(){
|
||||
// get the vhost and port to set the default url.
|
||||
// for example: http://192.168.1.213/players/jwplayer6.html?port=1935&vhost=demo
|
||||
// url set to: rtmp://demo:1935/live/livestream
|
||||
srs_init_publish("#txt_url");
|
||||
|
||||
// support 5x3+1 users
|
||||
for (var i = 0; i < 5; i++) {
|
||||
var tr = $("<tr></tr>").hide();
|
||||
$("#lst_chats").append(tr);
|
||||
|
||||
for (var j = 0; j < 3; j++) {
|
||||
tr.append($("<td></td>").attr("id", "td_" + ((i+1) * 8 + j)));
|
||||
}
|
||||
}
|
||||
// remove border of row.
|
||||
$("#lst_chats").find("td").css("border", "none").css("padding", "2px")
|
||||
.css("padding-left", "0px").css("width", "327px");
|
||||
|
||||
if (query.agent == "true") {
|
||||
document.write(navigator.userAgent);
|
||||
return;
|
||||
}
|
||||
|
||||
$("#realtime_player_url").tooltip({
|
||||
title: "右键复制RTMP地址"
|
||||
});
|
||||
|
||||
// if no play specified, donot show the player, for debug the publisher.
|
||||
var query = parse_query_string();
|
||||
if (query.no_play == "true") {
|
||||
no_play = true;
|
||||
}
|
||||
|
@ -50,7 +76,7 @@
|
|||
$("#txt_url").val($("#txt_url").val() + "." + new Date().getTime());
|
||||
|
||||
// start the publisher.
|
||||
srs_publisher = new SrsPublisher("local_publisher", 430, 185);
|
||||
srs_publisher = new SrsPublisher("local_publisher", 280, 180);
|
||||
srs_publisher.on_publisher_ready = function(cameras, microphones) {
|
||||
srs_chat_initialize_page(
|
||||
cameras, microphones,
|
||||
|
@ -60,7 +86,11 @@
|
|||
);
|
||||
};
|
||||
srs_publisher.on_publisher_error = function(code, desc) {
|
||||
error(code, desc);
|
||||
if (!on_publish_stop()) {
|
||||
return;
|
||||
}
|
||||
|
||||
error(code, desc + "请重试。");
|
||||
};
|
||||
srs_publisher.on_publisher_warn = function(code, desc) {
|
||||
warn(code, desc);
|
||||
|
@ -71,18 +101,37 @@
|
|||
|
||||
if (!no_play) {
|
||||
// start the realtime player.
|
||||
realtime_player = new SrsPlayer("realtime_player", 430, 185);
|
||||
realtime_player = new SrsPlayer("realtime_player", 280, 180);
|
||||
realtime_player.on_player_ready = function() {
|
||||
this.set_bt(0.5);
|
||||
this.set_fs("screen", 100);
|
||||
};
|
||||
realtime_player.on_player_metadata = function(metadata) {
|
||||
this.set_dar(0, 0);
|
||||
this.set_fs("screen", 100);
|
||||
|
||||
info("推流到服务器成功。请戴耳机聊天,否则音箱的声音会进入麦克风造成回声。");
|
||||
}
|
||||
realtime_player.start();
|
||||
}
|
||||
|
||||
$("#txt_name").focus();
|
||||
|
||||
api_server = "http://" + query.hostname + ":" + srs_get_api_server_port() + "/api/v1/chats";
|
||||
refresh();
|
||||
});
|
||||
|
||||
function on_publish_stop() {
|
||||
if (!srs_can_republish()) {
|
||||
$("#btn_join").attr("disabled", true);
|
||||
error(0, "您使用的浏览器很弱,请关闭页面后重新打开页面(刷新也不管用)。<br/>推荐使用Chrome/Firefox/Safari/Opera浏览器,支持重试");
|
||||
|
||||
srs_log_disabled = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function update_play_url() {
|
||||
var url = $("#txt_url").val();
|
||||
|
||||
|
@ -180,86 +229,44 @@
|
|||
}
|
||||
});
|
||||
}
|
||||
function render_chat_room(chats) {
|
||||
function render_chat_room(original_chats) {
|
||||
if (!self_chat) {
|
||||
return;
|
||||
}
|
||||
|
||||
var chats = [];
|
||||
for (var i = 0; i < original_chats.length; i++) {
|
||||
var chat = original_chats[i];
|
||||
|
||||
// ignore the self.
|
||||
if (self_chat && self_chat.id == chat.id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
chats.push(chat);
|
||||
}
|
||||
|
||||
// new added chat
|
||||
for (var i = 0; i < chats.length; i++) {
|
||||
var chat = chats[i];
|
||||
// ignore the self.
|
||||
if (self_chat && self_chat.id == chat.id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if previous exists, ignore, only add new here.
|
||||
var previous_chat = get_previous_chat_user(previous_chats, chat.id);
|
||||
if (previous_chat) {
|
||||
// update reference.
|
||||
chat.ui = previous_chat.ui;
|
||||
chat.parent = previous_chat.parent;
|
||||
chat.player = previous_chat.player;
|
||||
chat.player.private_object = chat;
|
||||
if (chat.player) {
|
||||
chat.player.private_object = chat;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
global_chat_user_id++;
|
||||
|
||||
// username: a str indicates the user name.
|
||||
// url: a str indicates the url of user stream.
|
||||
// join_date: a str indicates the join timestamp in seconds.
|
||||
// join_date_str: friendly formated time.
|
||||
var obj = $("<div/>").html($("#template").html());
|
||||
$(obj).attr("chat_id", chat.id);
|
||||
$(obj).attr("id", "div_" + chat.id); // for specifed chat: $("#div_" + chat_id)
|
||||
$(obj).attr("class", "div_chat"); // for all chats: $(".div_chat")
|
||||
$(obj).find("#chat_player").attr("id", "rp_" + chat.id); // for specifed player: $("#rp_" + chat_id)
|
||||
$(obj).find("#chat_player_raw").attr("id", "rp_raw_" + chat.id); // for specifed player: $("#rp_raw_" + chat_id)
|
||||
$(obj).find("#user_name").text(chat.username);
|
||||
$(obj).find("#join_date").text(chat.join_date_str);
|
||||
$(obj).find("#collapseM").attr("id", "collapse_" + global_chat_user_id);
|
||||
$(obj).find("#headerN").attr("href", "#collapse_" + global_chat_user_id);
|
||||
|
||||
$("#lst_chats").append(obj);
|
||||
|
||||
if (!no_play) {
|
||||
// start the realtime player.
|
||||
var _player = new SrsPlayer("rp_raw_" + chat.id, 600, 300, chat);
|
||||
_player.on_player_ready = function() {
|
||||
this.set_bt(0.5);
|
||||
this.set_fs("screen", 100);
|
||||
};
|
||||
_player.start(chat.url);
|
||||
|
||||
chat.player = _player;
|
||||
} else {
|
||||
chat.player = null;
|
||||
}
|
||||
|
||||
$(obj).find("#collapse_main").on('hidden', function(){
|
||||
var id = $(this).parent().attr("chat_id");
|
||||
var chat = get_previous_chat_user(previous_chats, id);
|
||||
if (!chat || !chat.player) {
|
||||
return;
|
||||
}
|
||||
chat.player.stop();
|
||||
});
|
||||
$(obj).find("#collapse_main").on('shown', function(){
|
||||
var id = $(this).parent().attr("chat_id");
|
||||
var chat = get_previous_chat_user(previous_chats, id);
|
||||
if (!chat || !chat.player) {
|
||||
return;
|
||||
}
|
||||
chat.player.play();
|
||||
});
|
||||
}
|
||||
|
||||
// removed chat
|
||||
for (var i = 0; i < previous_chats.length; i++) {
|
||||
var chat = previous_chats[i];
|
||||
// ignore the self.
|
||||
if (self_chat && self_chat.id == chat.id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var new_chat = get_previous_chat_user(chats, chat.id);
|
||||
if (new_chat) {
|
||||
|
@ -272,6 +279,118 @@
|
|||
$("#div_" + chat.id).remove();
|
||||
}
|
||||
|
||||
// hide empty rows.
|
||||
$("#lst_chats").find("tr").each(function(){
|
||||
var empty = true;
|
||||
|
||||
$(this).find("td").each(function(){
|
||||
if ($(this).html() != "") {
|
||||
empty = false;
|
||||
return false; // abort each
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (empty) {
|
||||
$(this).hide();
|
||||
}
|
||||
});
|
||||
|
||||
// render the chat
|
||||
for (var i = 0; i < chats.length; i++) {
|
||||
var chat = chats[i];
|
||||
|
||||
// if redered, ignore.
|
||||
if (chat.parent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// generate the ui of chat
|
||||
if (!chat.ui) {
|
||||
global_chat_user_id++;
|
||||
|
||||
// username: a str indicates the user name.
|
||||
// url: a str indicates the url of user stream.
|
||||
// join_date: a str indicates the join timestamp in seconds.
|
||||
// join_date_str: friendly formated time.
|
||||
var obj = $("<div/>").html($("#template").html());
|
||||
if (true) {
|
||||
// save current ui object to the chat.
|
||||
chat.ui = obj;
|
||||
|
||||
$(obj).attr("chat_id", chat.id);
|
||||
$(obj).attr("id", "div_" + chat.id); // for specifed chat: $("#div_" + chat_id)
|
||||
$(obj).attr("class", "div_chat"); // for all chats: $(".div_chat")
|
||||
$(obj).find("#chat_player").attr("id", "rp_" + chat.id); // for specifed player: $("#rp_" + chat_id)
|
||||
$(obj).find("#chat_player_raw").attr("id", "rp_raw_" + chat.id); // for specifed player: $("#rp_raw_" + chat_id)
|
||||
$(obj).find("#user_name").text(chat.username);
|
||||
$(obj).find("#user_player_url").attr("href", chat.url);
|
||||
$(obj).find("#join_date").text(chat.join_date_str.split(" ")[1]);
|
||||
$(obj).find("#collapseM").attr("id", "collapse_" + global_chat_user_id);
|
||||
$(obj).find("#headerN").attr("href", "#collapse_" + global_chat_user_id);
|
||||
}
|
||||
}
|
||||
|
||||
// find a idle td to render the chat.
|
||||
var parent = null;
|
||||
$("#lst_chats").find("td").each(function(){
|
||||
if ($(this).html() != "") {
|
||||
return true;
|
||||
}
|
||||
|
||||
parent = $(this);
|
||||
return false; // abort each
|
||||
});
|
||||
|
||||
if (!parent) {
|
||||
warn("没有可用的位置展示流。");
|
||||
break;
|
||||
}
|
||||
$(parent).append(chat.ui);
|
||||
$(parent).parent().show();
|
||||
|
||||
// when ui elements appent to the page,
|
||||
// create the player, or flash will failed.
|
||||
if (!chat.parent) {
|
||||
chat.parent = $(parent);
|
||||
|
||||
if (!no_play) {
|
||||
// start the realtime player.
|
||||
var _player = new SrsPlayer("rp_raw_" + chat.id, 240, 180, chat);
|
||||
_player.on_player_ready = function() {
|
||||
this.set_bt(0.5);
|
||||
this.play();
|
||||
};
|
||||
_player.on_player_metadata = function(metadata) {
|
||||
this.set_dar(0, 0);
|
||||
this.set_fs("screen", 100);
|
||||
}
|
||||
_player.start(chat.url);
|
||||
|
||||
chat.player = _player;
|
||||
} else {
|
||||
chat.player = null;
|
||||
}
|
||||
|
||||
$(obj).find("#collapse_main").on('hidden', function(){
|
||||
var id = $(this).parent().attr("chat_id");
|
||||
var chat = get_previous_chat_user(previous_chats, id);
|
||||
if (!chat || !chat.player) {
|
||||
return;
|
||||
}
|
||||
chat.player.stop();
|
||||
});
|
||||
$(obj).find("#collapse_main").on('shown', function(){
|
||||
var id = $(this).parent().attr("chat_id");
|
||||
var chat = get_previous_chat_user(previous_chats, id);
|
||||
if (!chat || !chat.player) {
|
||||
return;
|
||||
}
|
||||
chat.player.play();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
previous_chats = chats;
|
||||
}
|
||||
function get_previous_chat_user(arr, id) {
|
||||
|
@ -285,7 +404,7 @@
|
|||
}
|
||||
|
||||
function on_user_publish() {
|
||||
if ($("#txt_name").val().trim() == "") {
|
||||
if ($("#txt_name").val() == "") {
|
||||
$("#txt_name").focus().parent().parent().addClass("error");
|
||||
warn(100, "请输入您的名字");
|
||||
return;
|
||||
|
@ -315,10 +434,6 @@
|
|||
// removed chat
|
||||
for (var i = 0; i < previous_chats.length; i++) {
|
||||
var chat = previous_chats[i];
|
||||
// ignore the self.
|
||||
if (self_chat && self_chat.id == chat.id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (chat.player) {
|
||||
chat.player.stop();
|
||||
|
@ -342,6 +457,10 @@
|
|||
data : "",
|
||||
dataType : "json",
|
||||
complete : function() {
|
||||
if (!on_publish_stop()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$("#btn_join").attr("disabled", false);
|
||||
if (complete_pfn) {
|
||||
complete_pfn();
|
||||
|
@ -376,7 +495,7 @@
|
|||
|
||||
var chat = {};
|
||||
chat.id = -1;
|
||||
chat.username = $("#txt_name").val().trim();
|
||||
chat.username = $("#txt_name").val();
|
||||
chat.agent = navigator.userAgent;
|
||||
chat.url = url;
|
||||
chat.vcodec = vcodec;
|
||||
|
@ -414,13 +533,13 @@
|
|||
|
||||
$("#btn_join").text("退出会议");
|
||||
|
||||
info("开始推流到服务器");
|
||||
info("开始推流到服务器。请戴耳机聊天,否则音箱的声音会进入麦克风造成回声。");
|
||||
srs_publisher.publish(url, vcodec, acodec);
|
||||
|
||||
if (realtime_player) {
|
||||
// directly play the url for the realtime player.
|
||||
realtime_player.stop();
|
||||
realtime_player.play(url);
|
||||
realtime_player.play(url, 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -431,7 +550,7 @@
|
|||
<div class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<a class="brand" href="index.html">SRS</a>
|
||||
<a id="srs_index" class="brand" href="#">SRS</a>
|
||||
<div class="nav-collapse collapse">
|
||||
<ul class="nav">
|
||||
<li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
|
||||
|
@ -451,21 +570,21 @@
|
|||
<div class="alert alert-info fade in" id="txt_log">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<strong><span id="txt_log_title">Usage:</span></strong>
|
||||
<span id="txt_log_msg">输入名字,设置编码参数后,加入会议室</span>
|
||||
<span id="txt_log_msg">输入名字,设点“加入会议”按钮</span>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<div class="form-inline">
|
||||
<input type="text" id="txt_name" class="input-small" placeholder="您的名字..." value=""></input>
|
||||
<button class="btn input-medium" id="btn_video_settings">视频编码配置</button>
|
||||
<button class="btn input-medium" id="btn_audio_settings">音频编码配置</button>
|
||||
<button class="btn input-medium btn-primary" id="btn_join">加入会议</button>
|
||||
<button class="btn input-small" id="btn_video_settings">摄像头</button>
|
||||
<button class="btn input-small" id="btn_audio_settings">麦克风</button>
|
||||
<input type="text" id="txt_name" class="input-large" placeholder="请输入您的名字..." value=""></input>
|
||||
<button class="btn input-small" id="btn_join">加入会议</button>
|
||||
<input type="text" id="txt_url" class="input-mini hide" value=""></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="row-fluid">
|
||||
<div class="span6">
|
||||
<table id="lst_chats" class="table">
|
||||
<tr>
|
||||
<td id="td_0">
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle">
|
||||
|
@ -478,8 +597,8 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="span6">
|
||||
</td>
|
||||
<td id="td_1">
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle">
|
||||
|
@ -495,38 +614,33 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td id="td_2"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="container hide" id="template">
|
||||
<div class="accordion-group" id="collapse_main">
|
||||
<div class="accordion-heading" title="点击展开或收起,收起后停止播放流,展开时从服务器请求流">
|
||||
<span id="headerN" class="accordion-toggle" data-toggle="collapse" href="#collapseN">
|
||||
<strong>[<a href="#"><span id="user_name">XX</span></a>]</strong>
|
||||
<strong>加入时间</strong>[<span id="join_date"></span>]
|
||||
<img src="img/tooltip.png"/>
|
||||
<a id="user_player_url" href="#" data-toggle="tooltip" data-placement="top" title="">
|
||||
播放地址<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<div id="collapseM" class="accordion-body collapse">
|
||||
<div id="collapseM" class="accordion-body collapse in">
|
||||
<div class="accordion-inner">
|
||||
<div class="row-fluid">
|
||||
<div class="span2">
|
||||
</div>
|
||||
<div class="span8">
|
||||
<div id="chat_player">
|
||||
<div id="chat_player_raw">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="span2">
|
||||
<div id="chat_player">
|
||||
<div id="chat_player_raw">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container" id="lst_chats">
|
||||
</div>
|
||||
<table class="table">
|
||||
</table>
|
||||
<div id="video_modal" class="modal hide fade">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
|
|
|
@ -7,7 +7,12 @@
|
|||
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
|
||||
<script type="text/javascript" src="js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="js/swfobject.js"></script>
|
||||
<script type="text/javascript" src="js/srs.js"></script>
|
||||
<script type="text/javascript" src="js/json2.js"></script>
|
||||
<script type="text/javascript" src="js/srs.page.js"></script>
|
||||
<script type="text/javascript" src="js/srs.log.js"></script>
|
||||
<script type="text/javascript" src="js/srs.player.js"></script>
|
||||
<script type="text/javascript" src="js/srs.publisher.js"></script>
|
||||
<script type="text/javascript" src="js/srs.utility.js"></script>
|
||||
<style>
|
||||
body{
|
||||
padding-top: 55px;
|
||||
|
@ -30,7 +35,7 @@
|
|||
|
||||
var __active_dar = null;
|
||||
function select_dar(dar_id, num, den) {
|
||||
srs_player.dar(num, den);
|
||||
srs_player.set_dar(num, den);
|
||||
|
||||
if (__active_dar) {
|
||||
__active_dar.removeClass("active");
|
||||
|
@ -191,13 +196,13 @@
|
|||
select_dar("#btn_dar_original", 0, 0);
|
||||
});
|
||||
$("#btn_dar_21_9").click(function(){
|
||||
select_dar("#btn_dar_21_9", 9, 21);
|
||||
select_dar("#btn_dar_21_9", 21, 9);
|
||||
});
|
||||
$("#btn_dar_16_9").click(function(){
|
||||
select_dar("#btn_dar_16_9", 9, 16);
|
||||
select_dar("#btn_dar_16_9", 16, 9);
|
||||
});
|
||||
$("#btn_dar_4_3").click(function(){
|
||||
select_dar("#btn_dar_4_3", 3, 4);
|
||||
select_dar("#btn_dar_4_3", 4, 3);
|
||||
});
|
||||
$("#btn_dar_fill").click(function(){
|
||||
select_dar("#btn_dar_fill", -1, -1);
|
||||
|
@ -264,7 +269,7 @@
|
|||
<div class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<a class="brand" href="index.html">SRS</a>
|
||||
<a id="srs_index" class="brand" href="#">SRS</a>
|
||||
<div class="nav-collapse collapse">
|
||||
<ul class="nav">
|
||||
<li class="active"><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
|
||||
|
|
Binary file not shown.
|
@ -10,6 +10,7 @@ package
|
|||
import flash.events.NetStatusEvent;
|
||||
import flash.events.TimerEvent;
|
||||
import flash.external.ExternalInterface;
|
||||
import flash.media.SoundTransform;
|
||||
import flash.media.Video;
|
||||
import flash.net.NetConnection;
|
||||
import flash.net.NetStream;
|
||||
|
@ -19,6 +20,8 @@ package
|
|||
import flash.utils.Timer;
|
||||
import flash.utils.setTimeout;
|
||||
|
||||
import flashx.textLayout.formats.Float;
|
||||
|
||||
public class srs_player extends Sprite
|
||||
{
|
||||
// user set id.
|
||||
|
@ -34,8 +37,8 @@ package
|
|||
private var user_w:int = 0;
|
||||
private var user_h:int = 0;
|
||||
// user set dar den:num
|
||||
private var user_dar_num:int = 0;
|
||||
private var user_dar_den:int = 0;
|
||||
private var user_dar_num:int = 0;
|
||||
// user set fs(fullscreen) refer and percent.
|
||||
private var user_fs_refer:String = null;
|
||||
private var user_fs_percent:int = 0;
|
||||
|
@ -112,7 +115,7 @@ package
|
|||
flash.external.ExternalInterface.addCallback("__stop", this.js_call_stop);
|
||||
flash.external.ExternalInterface.addCallback("__pause", this.js_call_pause);
|
||||
flash.external.ExternalInterface.addCallback("__resume", this.js_call_resume);
|
||||
flash.external.ExternalInterface.addCallback("__dar", this.js_call_dar);
|
||||
flash.external.ExternalInterface.addCallback("__set_dar", this.js_call_set_dar);
|
||||
flash.external.ExternalInterface.addCallback("__set_fs", this.js_call_set_fs_size);
|
||||
flash.external.ExternalInterface.addCallback("__set_bt", this.js_call_set_bt);
|
||||
|
||||
|
@ -218,15 +221,15 @@ package
|
|||
}
|
||||
|
||||
/**
|
||||
* to set the DAR, for example, DAR=16:9
|
||||
* @param num, for example, 9.
|
||||
* use metadata height if 0.
|
||||
* use user specified height if -1.
|
||||
* @param den, for example, 16.
|
||||
* use metadata width if 0.
|
||||
* use user specified width if -1.
|
||||
* to set the DAR, for example, DAR=16:9 where num=16,den=9.
|
||||
* @param num, for example, 16.
|
||||
* use metadata width if 0.
|
||||
* use user specified width if -1.
|
||||
* @param den, for example, 9.
|
||||
* use metadata height if 0.
|
||||
* use user specified height if -1.
|
||||
*/
|
||||
private function js_call_dar(num:int, den:int):void {
|
||||
private function js_call_set_dar(num:int, den:int):void {
|
||||
user_dar_num = num;
|
||||
user_dar_den = den;
|
||||
|
||||
|
@ -280,8 +283,9 @@ package
|
|||
* @param _width, the player width.
|
||||
* @param _height, the player height.
|
||||
* @param buffer_time, the buffer time in seconds. recommend to >=0.5
|
||||
* @param volume, the volume, 0 is mute, 1 is 100%, 2 is 200%.
|
||||
*/
|
||||
private function js_call_play(url:String, _width:int, _height:int, buffer_time:Number):void {
|
||||
private function js_call_play(url:String, _width:int, _height:int, buffer_time:Number, volume:Number):void {
|
||||
this.user_url = url;
|
||||
this.user_w = _width;
|
||||
this.user_h = _height;
|
||||
|
@ -313,6 +317,7 @@ package
|
|||
}
|
||||
|
||||
media_stream = new NetStream(media_conn);
|
||||
media_stream.soundTransform = new SoundTransform(volume);
|
||||
media_stream.bufferTime = buffer_time;
|
||||
media_stream.client = {};
|
||||
media_stream.client.onMetaData = system_on_metadata;
|
||||
|
@ -397,21 +402,21 @@ package
|
|||
var obj:Object = __get_video_size_object();
|
||||
|
||||
// get the DAR
|
||||
var num:int = user_dar_num;
|
||||
var den:int = user_dar_den;
|
||||
|
||||
if (num == 0) {
|
||||
num = obj.height;
|
||||
}
|
||||
if (num == -1) {
|
||||
num = this.stage.fullScreenHeight;
|
||||
}
|
||||
var num:int = user_dar_num;
|
||||
|
||||
if (den == 0) {
|
||||
den = obj.width;
|
||||
den = obj.height;
|
||||
}
|
||||
if (den == -1) {
|
||||
den = this.stage.fullScreenWidth;
|
||||
den = this.stage.fullScreenHeight;
|
||||
}
|
||||
|
||||
if (num == 0) {
|
||||
num = obj.width;
|
||||
}
|
||||
if (num == -1) {
|
||||
num = this.stage.fullScreenWidth;
|
||||
}
|
||||
|
||||
// for refer is screen.
|
||||
|
@ -431,23 +436,23 @@ package
|
|||
*/
|
||||
private function __execute_user_set_dar():void {
|
||||
// get the DAR
|
||||
var num:int = user_dar_num;
|
||||
var den:int = user_dar_den;
|
||||
var num:int = user_dar_num;
|
||||
|
||||
var obj:Object = __get_video_size_object();
|
||||
|
||||
if (num == 0) {
|
||||
num = obj.height;
|
||||
}
|
||||
if (num == -1) {
|
||||
num = this.user_h;
|
||||
}
|
||||
|
||||
if (den == 0) {
|
||||
den = obj.width;
|
||||
den = obj.height;
|
||||
}
|
||||
if (den == -1) {
|
||||
den = this.user_w;
|
||||
den = this.user_h;
|
||||
}
|
||||
|
||||
if (num == 0) {
|
||||
num = obj.width;
|
||||
}
|
||||
if (num == -1) {
|
||||
num = this.user_w;
|
||||
}
|
||||
|
||||
__update_video_size(num, den, this.user_w, this.user_h, this.user_w, this.user_h);
|
||||
|
@ -463,19 +468,19 @@ package
|
|||
* @param _sw/_wh the stage size, >= paper size. used to center the player.
|
||||
*/
|
||||
private function __update_video_size(_num:int, _den:int, _w:int, _h:int, _sw:int, _sh:int):void {
|
||||
if (!this.media_video || _num <= 0 || _den <= 0) {
|
||||
if (!this.media_video || _den <= 0 || _num <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// set DAR.
|
||||
// calc the height by DAR
|
||||
var _height:int = _w * _num / _den;
|
||||
var _height:int = _w * _den / _num;
|
||||
if (_height <= _h) {
|
||||
this.media_video.width = _w;
|
||||
this.media_video.height = _height;
|
||||
} else {
|
||||
// height overflow, calc the width by DAR
|
||||
var _width:int = _h * _den / _num;
|
||||
var _width:int = _h * _num / _den;
|
||||
|
||||
this.media_video.width = _width;
|
||||
this.media_video.height = _h;
|
||||
|
|
|
@ -7,7 +7,12 @@
|
|||
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
|
||||
<script type="text/javascript" src="js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="js/swfobject.js"></script>
|
||||
<script type="text/javascript" src="js/srs.js"></script>
|
||||
<script type="text/javascript" src="js/json2.js"></script>
|
||||
<script type="text/javascript" src="js/srs.page.js"></script>
|
||||
<script type="text/javascript" src="js/srs.log.js"></script>
|
||||
<script type="text/javascript" src="js/srs.player.js"></script>
|
||||
<script type="text/javascript" src="js/srs.publisher.js"></script>
|
||||
<script type="text/javascript" src="js/srs.utility.js"></script>
|
||||
<style>
|
||||
body{
|
||||
padding-top: 55px;
|
||||
|
@ -18,12 +23,18 @@
|
|||
var remote_player = null;
|
||||
var realtime_player = null;
|
||||
|
||||
var query = parse_query_string();
|
||||
$(function(){
|
||||
// get the vhost and port to set the default url.
|
||||
// for example: http://192.168.1.213/players/jwplayer6.html?port=1935&vhost=demo
|
||||
// url set to: rtmp://demo:1935/live/livestream
|
||||
srs_init("#txt_url", null, null);
|
||||
|
||||
if (query.agent == "true") {
|
||||
document.write(navigator.userAgent);
|
||||
return;
|
||||
}
|
||||
|
||||
$("#btn_video_settings").click(function(){
|
||||
$("#video_modal").modal({show:true});
|
||||
});
|
||||
|
@ -60,7 +71,10 @@
|
|||
);
|
||||
};
|
||||
srs_publisher.on_publisher_error = function(code, desc) {
|
||||
error(code, desc);
|
||||
if (!on_publish_stop()) {
|
||||
return;
|
||||
}
|
||||
error(code, desc + "请重试。");
|
||||
};
|
||||
srs_publisher.on_publisher_warn = function(code, desc) {
|
||||
warn(code, desc);
|
||||
|
@ -70,26 +84,43 @@
|
|||
update_play_url();
|
||||
|
||||
// if no play specified, donot show the player, for debug the publisher.
|
||||
var query = parse_query_string();
|
||||
if (query.no_play != "true") {
|
||||
// start the normal player with HLS supported.
|
||||
remote_player = new SrsPlayer("remote_player", 430, 185);
|
||||
remote_player.on_player_ready = function() {
|
||||
this.set_bt(0.8);
|
||||
this.set_fs("screen", 100);
|
||||
};
|
||||
remote_player.on_player_metadata = function(metadata) {
|
||||
this.set_dar(0, 0);
|
||||
this.set_fs("screen", 100);
|
||||
}
|
||||
remote_player.start();
|
||||
|
||||
// start the realtime player.
|
||||
realtime_player = new SrsPlayer("realtime_player", 430, 185);
|
||||
realtime_player.on_player_ready = function() {
|
||||
this.set_bt(0.8);
|
||||
this.set_fs("screen", 100);
|
||||
};
|
||||
realtime_player.on_player_metadata = function(metadata) {
|
||||
this.set_dar(0, 0);
|
||||
this.set_fs("screen", 100);
|
||||
}
|
||||
realtime_player.start();
|
||||
}
|
||||
});
|
||||
|
||||
function on_publish_stop() {
|
||||
if (!srs_can_republish()) {
|
||||
$("#btn_join").attr("disabled", true);
|
||||
error(0, "您使用的浏览器很弱,请关闭页面后重新打开页面(刷新也不管用)。<br/>推荐使用Chrome/Firefox/Safari/Opera浏览器,支持重试");
|
||||
|
||||
srs_log_disabled = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* we generate the transcoded stream url for flash publish donot support HLS
|
||||
* which requires aac, so the publish vhost maybe players for example, we
|
||||
|
@ -101,7 +132,6 @@
|
|||
function update_play_url() {
|
||||
var url = $("#txt_url").val();
|
||||
var ret = srs_parse_rtmp_url(url);
|
||||
var query = parse_query_string();
|
||||
|
||||
var remote_url = "rtmp://" + ret.server + ":" + ret.port + "/" + ret.app + "...vhost..." + srs_get_player_publish_vhost(ret.vhost) + "/" + ret.stream;
|
||||
$("#realtime_player_url").attr("href", url).attr("target", "_blank");
|
||||
|
@ -136,6 +166,10 @@
|
|||
//$("#remote_player_url").attr("href", "#").attr("target", "_self");
|
||||
//$("#txt_play_hls").text("HLS-m3u8(请发布视频)").attr("href", "#").attr("target", "_self");
|
||||
//$("#txt_play_jwplayer").text("HLS-JWPlayer(请发布视频)").attr("href", "#").attr("target", "_self");
|
||||
|
||||
if (!on_publish_stop()) {
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -180,7 +214,7 @@
|
|||
<div class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<a class="brand" href="index.html">SRS</a>
|
||||
<a id="srs_index" class="brand" href="#">SRS</a>
|
||||
<div class="nav-collapse collapse">
|
||||
<ul class="nav">
|
||||
<li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
|
||||
|
|
Binary file not shown.
|
@ -10,9 +10,14 @@ package
|
|||
import flash.media.H264Profile;
|
||||
import flash.media.H264VideoStreamSettings;
|
||||
import flash.media.Microphone;
|
||||
import flash.media.MicrophoneEnhancedMode;
|
||||
import flash.media.MicrophoneEnhancedOptions;
|
||||
import flash.media.SoundCodec;
|
||||
import flash.media.Video;
|
||||
import flash.net.NetConnection;
|
||||
import flash.net.NetStream;
|
||||
import flash.system.Security;
|
||||
import flash.system.SecurityPanel;
|
||||
import flash.ui.ContextMenu;
|
||||
import flash.ui.ContextMenuItem;
|
||||
import flash.utils.setTimeout;
|
||||
|
@ -45,6 +50,8 @@ package
|
|||
private const error_camera_get:int = 100;
|
||||
private const error_microphone_get:int = 101;
|
||||
private const error_camera_muted:int = 102;
|
||||
private const error_connection_closed:int = 103;
|
||||
private const error_connection_failed:int = 104;
|
||||
|
||||
public function srs_publisher()
|
||||
{
|
||||
|
@ -79,6 +86,18 @@ package
|
|||
this.js_on_publisher_error = flashvars.on_publisher_error;
|
||||
this.js_on_publisher_warn = flashvars.on_publisher_warn;
|
||||
|
||||
// initialized size.
|
||||
this.user_w = flashvars.width;
|
||||
this.user_h = flashvars.height;
|
||||
|
||||
// try to get the camera, if muted, alert the security and requires user to open it.
|
||||
var c:Camera = Camera.getCamera();
|
||||
if (c.muted) {
|
||||
Security.showSettings(SecurityPanel.PRIVACY);
|
||||
}
|
||||
|
||||
__show_local_camera(c);
|
||||
|
||||
flash.utils.setTimeout(this.system_on_js_ready, 0);
|
||||
}
|
||||
|
||||
|
@ -135,27 +154,31 @@ package
|
|||
this.js_call_stop();
|
||||
|
||||
// microphone and camera
|
||||
var m:Microphone = Microphone.getMicrophone(acodec.device_code);
|
||||
if(m == null){
|
||||
var microphone:Microphone = null;
|
||||
//microphone = Microphone.getEnhancedMicrophone(acodec.device_code);
|
||||
if (!microphone) {
|
||||
microphone = Microphone.getMicrophone(acodec.device_code);
|
||||
}
|
||||
if(microphone == null){
|
||||
this.system_error(this.error_microphone_get, "failed to open microphone " + acodec.device_code + "(" + acodec.device_name + ")");
|
||||
return;
|
||||
}
|
||||
// ignore muted, for flash will require user to access it.
|
||||
|
||||
// Remark: the name is the index!
|
||||
var c:Camera = Camera.getCamera(vcodec.device_code);
|
||||
if(c == null){
|
||||
var camera:Camera = Camera.getCamera(vcodec.device_code);
|
||||
if(camera == null){
|
||||
this.system_error(this.error_camera_get, "failed to open camera " + vcodec.device_code + "(" + vcodec.device_name + ")");
|
||||
return;
|
||||
}
|
||||
// ignore muted, for flash will require user to access it.
|
||||
// but we still warn user.
|
||||
if(c && c.muted){
|
||||
if(camera && camera.muted){
|
||||
this.system_warn(this.error_camera_muted, "Access Denied, camera " + vcodec.device_code + "(" + vcodec.device_name + ") is muted");
|
||||
}
|
||||
|
||||
this.media_camera = c;
|
||||
this.media_microphone = m;
|
||||
this.media_camera = camera;
|
||||
this.media_microphone = microphone;
|
||||
|
||||
this.media_conn = new NetConnection();
|
||||
this.media_conn.client = {};
|
||||
|
@ -175,6 +198,17 @@ package
|
|||
contextMenu.customItems = customItems;
|
||||
}
|
||||
|
||||
if (evt.info.code == "NetConnection.Connect.Closed") {
|
||||
js_call_stop();
|
||||
system_error(error_connection_closed, "server closed the connection");
|
||||
return;
|
||||
}
|
||||
if (evt.info.code == "NetConnection.Connect.Failed") {
|
||||
js_call_stop();
|
||||
system_error(error_connection_failed, "connect to server failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: FIXME: failed event.
|
||||
if (evt.info.code != "NetConnection.Connect.Success") {
|
||||
return;
|
||||
|
@ -188,30 +222,20 @@ package
|
|||
// TODO: FIXME: failed event.
|
||||
});
|
||||
|
||||
__build_video_codec(media_stream, c, vcodec);
|
||||
__build_audio_codec(media_stream, m, acodec);
|
||||
__build_video_codec(media_stream, camera, vcodec);
|
||||
__build_audio_codec(media_stream, microphone, acodec);
|
||||
|
||||
if (media_microphone) {
|
||||
media_stream.attachAudio(m);
|
||||
media_stream.attachAudio(microphone);
|
||||
}
|
||||
if (media_camera) {
|
||||
media_stream.attachCamera(c);
|
||||
media_stream.attachCamera(camera);
|
||||
}
|
||||
|
||||
var streamName:String = url.substr(url.lastIndexOf("/"));
|
||||
media_stream.publish(streamName);
|
||||
|
||||
media_video = new Video();
|
||||
media_video.width = _width;
|
||||
media_video.height = _height;
|
||||
media_video.attachCamera(media_camera);
|
||||
media_video.smoothing = true;
|
||||
addChild(media_video);
|
||||
|
||||
//__draw_black_background(_width, _height);
|
||||
|
||||
// lowest layer, for mask to cover it.
|
||||
setChildIndex(media_video, 0);
|
||||
__show_local_camera(media_camera);
|
||||
});
|
||||
|
||||
var tcUrl:String = this.user_url.substr(0, this.user_url.lastIndexOf("/"));
|
||||
|
@ -222,11 +246,9 @@ package
|
|||
* function for js to call: to stop the stream. ignore if not publish.
|
||||
*/
|
||||
private function js_call_stop():void {
|
||||
if (this.media_video) {
|
||||
this.removeChild(this.media_video);
|
||||
this.media_video = null;
|
||||
}
|
||||
if (this.media_stream) {
|
||||
this.media_stream.attachAudio(null);
|
||||
this.media_stream.attachCamera(null);
|
||||
this.media_stream.close();
|
||||
this.media_stream = null;
|
||||
}
|
||||
|
@ -264,6 +286,10 @@ package
|
|||
// if your sound capture device supports this value. Otherwise, the default value is the next available capture level above 8 kHz that
|
||||
// your sound capture device supports, usually 11 kHz.
|
||||
m.rate = microRate;
|
||||
|
||||
// see: http://www.adobe.com/cn/devnet/flashplayer/articles/acoustic-echo-cancellation.html
|
||||
m.codec = SoundCodec.SPEEX;
|
||||
m.framesPerPacket = 1;
|
||||
}
|
||||
private function __build_video_codec(stream:NetStream, c:Camera, vcodec:Object):void {
|
||||
if (!c) {
|
||||
|
@ -332,9 +358,56 @@ package
|
|||
// (highest quality, no compression). To specify that picture quality can vary as needed to avoid exceeding bandwidth,
|
||||
// pass 0 for quality.
|
||||
// winlin:
|
||||
// bandwidth is in bps not kbps. 500*1000 = 500kbps.
|
||||
// bandwidth is in Bps not kbps.
|
||||
// quality=1 is lowest quality, 100 is highest quality.
|
||||
c.setQuality(cameraBitrate * 1000, cameraQuality);
|
||||
c.setQuality(cameraBitrate / 8.0 * 1000, cameraQuality);
|
||||
}
|
||||
|
||||
private function __show_local_camera(c:Camera):void {
|
||||
if (this.media_video) {
|
||||
this.media_video.attachCamera(null);
|
||||
this.removeChild(this.media_video);
|
||||
this.media_video = null;
|
||||
}
|
||||
|
||||
// show local camera
|
||||
media_video = new Video();
|
||||
media_video.attachCamera(c);
|
||||
media_video.smoothing = true;
|
||||
addChild(media_video);
|
||||
|
||||
// rescale the local camera.
|
||||
var cw:Number = user_h * c.width / c.height;
|
||||
if (cw > user_w) {
|
||||
var ch:Number = user_w * c.height / c.width;
|
||||
media_video.width = user_w;
|
||||
media_video.height = ch;
|
||||
} else {
|
||||
media_video.width = cw;
|
||||
media_video.height = user_h;
|
||||
}
|
||||
media_video.x = (user_w - media_video.width) / 2;
|
||||
media_video.y = (user_h - media_video.height) / 2;
|
||||
|
||||
__draw_black_background(user_w, user_h);
|
||||
|
||||
// lowest layer, for mask to cover it.
|
||||
setChildIndex(media_video, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* draw black background and draw the fullscreen mask.
|
||||
*/
|
||||
private function __draw_black_background(_width:int, _height:int):void {
|
||||
// draw black bg.
|
||||
this.graphics.beginFill(0x00, 1.0);
|
||||
this.graphics.drawRect(0, 0, _width, _height);
|
||||
this.graphics.endFill();
|
||||
|
||||
// draw the fs mask.
|
||||
//this.control_fs_mask.graphics.beginFill(0xff0000, 0);
|
||||
//this.control_fs_mask.graphics.drawRect(0, 0, _width, _height);
|
||||
//this.control_fs_mask.graphics.endFill();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@
|
|||
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
|
||||
<script type="text/javascript" src="js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="js/swfobject.js"></script>
|
||||
<script type="text/javascript" src="js/srs.js"></script>
|
||||
<script type="text/javascript" src="js/srs.page.js"></script>
|
||||
<style>
|
||||
body{
|
||||
padding-top: 55px;
|
||||
|
@ -24,7 +24,7 @@
|
|||
<div class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<a class="brand" href="index.html">SRS</a>
|
||||
<a id="srs_index" class="brand" href="#">SRS</a>
|
||||
<div class="nav-collapse collapse">
|
||||
<ul class="nav">
|
||||
<li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
|
||||
|
|
|
@ -4,7 +4,9 @@ if [[ ! -d $src_dir ]]; then echo "错误:必须在src同目录执行脚本";
|
|||
|
||||
cmd="sudo ./objs/nginx/sbin/nginx"
|
||||
echo "启动NGINX(HLS服务):$cmd"
|
||||
pids=`ps aux|grep nginx|grep process|awk '{print $2}'`; for pid in $pids; do echo "结束现有进程:$pid"; sudo kill -s SIGKILL $pid; done
|
||||
if [[ -f ./objs/nginx/logs/nginx.pid ]]; then
|
||||
pids=`cat ./objs/nginx/logs/nginx.pid`; for pid in $pids; do echo "结束现有进程:$pid"; sudo kill -s SIGTERM $pid; done
|
||||
fi
|
||||
sudo ./objs/nginx/sbin/nginx
|
||||
ret=$?; if [[ 0 -ne $ret ]]; then echo "错误:启动NGINX(HLS服务)失败"; exit $ret; fi
|
||||
|
||||
|
|
|
@ -31,12 +31,6 @@ bash scripts/_step.start.api.server.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $
|
|||
|
||||
# step 8: add server ip to client hosts as demo.
|
||||
ip=`ifconfig|grep "inet"|grep "addr"|grep "Mask"|grep -v "127.0.0.1"|awk 'NR==1 {print $2}'|awk -F ':' '{print $2}'`
|
||||
echo -e "${GREEN}SRS系统开发环境启动成功${BLACK}"
|
||||
echo -e "${BLACK}播放器演示:${BLACK}"
|
||||
echo -e "${RED} http://$ip/players/srs_player.html?vhost=players${BLACK}"
|
||||
echo -e "${BLACK}编码器演示:${BLACK}"
|
||||
echo -e "${RED} http://$ip/players/srs_publisher.html?vhost=players${BLACK}"
|
||||
echo -e "${BLACK}视频会议演示:${BLACK}"
|
||||
echo -e "${RED} http://$ip/players/srs_chat.html?vhost=players${BLACK}"
|
||||
echo -e "${BLACK}服务器测速演示:${BLACK}"
|
||||
echo -e "${RED} http://$ip/players/srs_bwt.html?vhost=players${BLACK}"
|
||||
echo -e "${GREEN}SRS系统开发环境启动成功。演示:${BLACK}"
|
||||
echo -e "${RED} http://$ip${BLACK}"
|
||||
echo -e "${RED} http://$ip:8085/players/index.html${BLACK}"
|
||||
|
|
|
@ -45,3 +45,4 @@ cat<<END
|
|||
END
|
||||
echo -e "${GREEN}演示地址:${BLACK}"
|
||||
echo -e "${RED} http://$ip${BLACK}"
|
||||
echo -e "${RED} http://$ip:8085${BLACK}"
|
||||
|
|
|
@ -11,7 +11,9 @@ pids=`ps aux|grep srs|grep "./objs"|grep "srs.19350.conf"|awk '{print $2}'`; for
|
|||
# step 4(optinal): start nginx for HLS
|
||||
echo "停止NGINX(HLS服务)"
|
||||
ps aux|grep nginx|grep process
|
||||
pids=`ps aux|grep nginx|grep process|awk '{print $2}'`; for pid in $pids; do echo "结束现有进程:$pid"; sudo kill -s SIGKILL $pid; done
|
||||
if [[ -f ./objs/nginx/logs/nginx.pid ]]; then
|
||||
pids=`cat ./objs/nginx/logs/nginx.pid`; for pid in $pids; do echo "结束现有进程:$pid"; sudo kill -s SIGTERM $pid; done
|
||||
fi
|
||||
|
||||
# step 5(optinal): start http hooks for srs callback
|
||||
echo "停止API服务器"
|
||||
|
|
|
@ -24,6 +24,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
#include <srs_core_bandwidth.hpp>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -260,21 +261,24 @@ int SrsBandwidth::check_play(
|
|||
int64_t current_time = srs_get_system_time_ms();
|
||||
int size = 1024; // TODO: FIXME: magic number
|
||||
char random_data[size];
|
||||
memset(random_data, 0x01, size);
|
||||
memset(random_data, 'A', size);
|
||||
|
||||
int interval = 0;
|
||||
int data_count = 1;
|
||||
while ( (srs_get_system_time_ms() - current_time) < duration_ms ) {
|
||||
st_usleep(interval);
|
||||
|
||||
// TODO: FIXME: use shared ptr message.
|
||||
SrsBandwidthPacket* pkt = SrsBandwidthPacket::create_playing();
|
||||
|
||||
// TODO: FIXME: magic number
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
char buf[32]; // TODO: FIXME: magic number
|
||||
sprintf(buf, "%d", i);
|
||||
pkt->data->set(buf, new SrsAmf0String(random_data));
|
||||
// TODO: FIXME: magic number
|
||||
for (int i = 0; i < data_count; ++i) {
|
||||
std::stringstream seq;
|
||||
seq << i;
|
||||
std::string play_data = "SrS band check data from server's playing......";
|
||||
pkt->data->set(seq.str(), new SrsAmf0String(play_data.c_str()));
|
||||
}
|
||||
data_count += 2;
|
||||
|
||||
// TODO: FIXME: get length from the rtmp protocol stack.
|
||||
play_bytes += pkt->get_payload_length();
|
||||
|
|
|
@ -2789,7 +2789,7 @@ SrsBandwidthPacket* SrsBandwidthPacket::create_start_play()
|
|||
SrsBandwidthPacket* SrsBandwidthPacket::create_playing()
|
||||
{
|
||||
SrsBandwidthPacket* pkt = new SrsBandwidthPacket();
|
||||
return pkt->set_command(SRS_BW_CHECK_STARTING_PLAY);
|
||||
return pkt->set_command(SRS_BW_CHECK_PLAYING);
|
||||
}
|
||||
|
||||
SrsBandwidthPacket* SrsBandwidthPacket::create_stop_play()
|
||||
|
|
|
@ -414,7 +414,17 @@ int SrsRtmpClient::publish(string stream, int stream_id)
|
|||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
SrsProtocol *SrsRtmpClient::get_protocol()
|
||||
{
|
||||
return protocol;
|
||||
}
|
||||
|
||||
st_netfd_t SrsRtmpClient::get_st_fd()
|
||||
{
|
||||
return stfd;
|
||||
}
|
||||
|
||||
SrsRtmp::SrsRtmp(st_netfd_t client_stfd)
|
||||
|
|
|
@ -131,6 +131,10 @@ public:
|
|||
virtual int create_stream(int& stream_id);
|
||||
virtual int play(std::string stream, int stream_id);
|
||||
virtual int publish(std::string stream, int stream_id);
|
||||
|
||||
protected:
|
||||
SrsProtocol* get_protocol();
|
||||
st_netfd_t get_st_fd();
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue