merge upstream

pull/133/head
wenjiegit 11 years ago
commit 00fb37a831

@ -9,21 +9,41 @@ see also: [https://github.com/winlinvip/simple-rtmp-server](https://github.com/w
see also: [http://winlinvip.github.io/simple-rtmp-server](http://winlinvip.github.io/simple-rtmp-server)
### Contributors
winlin(winterserver): [http://blog.csdn.net/win_lin](http://blog.csdn.net/win_lin)
winlin([winterserver](#)): [http://blog.csdn.net/win_lin](http://blog.csdn.net/win_lin) <br/>
wenjie([wenjiegit](https://github.com/wenjiegit/simple-rtmp-server)): [http://blog.chinaunix.net/uid/25006789.html](http://blog.chinaunix.net/uid/25006789.html) <br/>
who is the contributors: <br/>
1. contribute important features to srs. <br/>
2. the name of all contributors will send in the response of NetConnection.connect and metadata.
### Usage
<strong>step 1:</strong> build srs <br/>
### Usage(simple)
<strong>step -1:</strong> get srs<br/>
<pre>
git clone https://github.com/winlinvip/simple-rtmp-server &&
cd simple-rtmp-server/trunk
</pre>
<strong>step 0:</strong> build srs system.<br/>
<pre>
bash scripts/build.sh
</pre>
<strong>step 1:</strong> start srs all demo features.<br/>
<pre>
tar xf simple-rtmp-server-*.*.tar.gz
cd simple-rtmp-server-*.*/trunk
./configure --with-ssl --with-hls --with-ffmpeg --with-http
make
bash scripts/run.sh
</pre>
or get the latest code:<br/>
<strong>step 2:</strong> srs live show: [http://your-server-ip](http://your-server-ip) <br/>
<strong>step 3:</strong> stop srs demo<br/>
<pre>
git clone https://github.com/winlinvip/simple-rtmp-server
bash scripts/stop.sh
</pre>
### Usage(detail)
<strong>step 0:</strong> get srs <br/>
<pre>
git clone https://github.com/winlinvip/simple-rtmp-server &&
cd simple-rtmp-server/trunk
./configure --with-ssl --with-hls --with-ffmpeg --with-http
</pre>
<strong>step 1:</strong> build srs <br/>
<pre>
./configure --with-ssl --with-hls --with-ffmpeg --with-http && make
</pre>
<strong>step 2:</strong> start srs <br/>
<pre>
@ -41,11 +61,11 @@ sudo ./objs/nginx/sbin/nginx
<pre>
python ./research/api-server/server.py 8085
</pre>
<strong>step 6:</strong> publish live stream <br/>
<strong>step 6:</strong> publish demo live stream <br/>
<pre>
FMS URL: rtmp://127.0.0.1/live
FMS URL: rtmp://127.0.0.1/live?vhost=demo.srs.com
Stream: livestream
For example, use ffmpeg to publish:
FFMPEG to publish the default demo stream:
for((;;)); do \
./objs/ffmpeg/bin/ffmpeg -re -i ./doc/source.200kbps.768x320.flv \
-vcodec copy -acodec copy \
@ -53,7 +73,19 @@ For example, use ffmpeg to publish:
sleep 1; \
done
</pre>
<strong>step 7:</strong> add server ip to client hosts as demo. <br/>
<strong>step 7:</strong> publish players live stream <br/>
<pre>
FMS URL: rtmp://127.0.0.1/live?vhost=players
Stream: livestream
FFMPEG to publish the players demo stream:
for((;;)); do \
./objs/ffmpeg/bin/ffmpeg -re -i ./doc/source.200kbps.768x320.flv \
-vcodec copy -acodec copy \
-f flv -y rtmp://127.0.0.1/live?vhost=players/livestream; \
sleep 1; \
done
</pre>
<strong>step 8:</strong> add server ip to client hosts as demo. <br/>
<pre>
# edit the folowing file:
# linux: /etc/hosts
@ -61,35 +93,35 @@ For example, use ffmpeg to publish:
# where server ip is 192.168.2.111
192.168.2.111 demo.srs.com
</pre>
<strong>step 8:</strong> play live stream. <br/>
<strong>step 9:</strong> play live stream. <br/>
<pre>
players: [http://demo.srs.com/players](http://demo.srs.com/players)
players: http://demo.srs.com/players
rtmp url: rtmp://demo.srs.com/live/livestream
m3u8 url: http://demo.srs.com/live/livestream.m3u8
for android: http://demo.srs.com/live/livestream.html
</pre>
<strong>step 9(optinal):</strong> play live stream auto transcoded<br/>
<strong>step 10(optinal):</strong> play live stream auto transcoded<br/>
<pre>
rtmp url: rtmp://demo.srs.com/live/livestream_ld
m3u8 url: http://demo.srs.com/live/livestream_ld.m3u8
for android: [http://demo.srs.com/live/livestream_ld.html](http://demo.srs.com/live/livestream_ld.html)
for android: http://demo.srs.com/live/livestream_ld.html
rtmp url: rtmp://demo.srs.com/live/livestream_sd
m3u8 url: http://demo.srs.com/live/livestream_sd.m3u8
for android: [http://demo.srs.com/live/livestream_sd.html](http://demo.srs.com/live/livestream_sd.html)
for android: http://demo.srs.com/live/livestream_sd.html
</pre>
<strong>step 10(optinal):</strong> play live stream auto forwarded, the hls dir change to /forward<br/>
<strong>step 11(optinal):</strong> play live stream auto forwarded, the hls dir change to /forward<br/>
<pre>
rtmp url: rtmp://demo.srs.com:19350/live/livestream
m3u8 url: http://demo.srs.com/forward/live/livestream.m3u8
for android: [http://demo.srs.com/forward/live/livestream.html](http://demo.srs.com/forward/live/livestream.html)
for android: http://demo.srs.com/forward/live/livestream.html
rtmp url: rtmp://demo.srs.com:19350/live/livestream_ld
m3u8 url: http://demo.srs.com/forward/live/livestream_ld.m3u8
for android: [http://demo.srs.com/forward/live/livestream_ld.html](http://demo.srs.com/forward/live/livestream_ld.html)
for android: http://demo.srs.com/forward/live/livestream_ld.html
rtmp url: rtmp://demo.srs.com:19350/live/livestream_sd
m3u8 url: http://demo.srs.com/forward/live/livestream_sd.m3u8
for android: [http://demo.srs.com/forward/live/livestream_sd.html](http://demo.srs.com/forward/live/livestream_sd.html)
for android: http://demo.srs.com/forward/live/livestream_sd.html
</pre>
<strong>step 11(optinal):</strong> modify the config and reload it (all features support reload)<br/>
<strong>step 12(optinal):</strong> modify the config and reload it (all features support reload)<br/>
<pre>
killall -1 srs
</pre>
@ -125,8 +157,36 @@ Stream Architecture:
| Flash, | +-> Fowarder ---------+-> RTMP Server |
| XSPLIT, | +-> Transcoder -------+-> RTMP Server |
| ...) | +-> DVR --------------+-> FILE |
| | +-> BandwidthTest-----+-> Flash/StLoad |
+-----------+-------------------------+----------------+
</pre>
Bandwidth Test Workflow:
<pre>
+------------+ +----------+
| Client | | Server |
+-----+------+ +-----+----+
| |
| connect vhost-------------> |
| &lt;-----------result(success) |
| |
| &lt;----------call(start play) |
| result(playing)----------> |
| &lt;-------------data(playing) |
| &lt;-----------call(stop play) |
| result(stopped)----------> |
| |
| &lt;-------call(start publish) |
| result(publishing)-------> |
| data(publishing)---------> |
| &lt;--------call(stop publish) |
| result(stopped)(1)-------> |
| |
| &lt;--------------------report |
| final(2)-----------------> |
| &lt;END> |
@see: class SrsBandwidth comments.
</pre>
### System Requirements
Supported operating systems and hardware:
@ -154,15 +214,17 @@ Supported operating systems and hardware:
18. support ffmpeg filters(logo/overlay/crop), x264 params.<br/>
19. support audio transcode only, speex/mp3 to aac<br/>
20. support http callback api hooks(for authentication and injection).<br/>
21. [plan] support network based cli and json result.<br/>
22. [plan] support bandwidth test api and flash client.<br/>
23. [plan] support adobe flash refer/token/swf verification.<br/>
24. [plan] support adobe amf3 codec.<br/>
25. [plan] support dvr(record live to vod file)<br/>
26. [plan] support FMS edge protocol<br/>
27. [plan] support encryption: RTMPE/RTMPS, HLS DRM<br/>
28. [plan] support RTMPT, http to tranverse firewalls<br/>
29. [plan] support file source, transcoding file to live stream<br/>
21. support bandwidth test api and flash client.<br/>
22. player, publisher(encoder), and demo pages(jquery+bootstrap). <br/>
23. demo video meeting or chat(srs+cherrypy+jquery+bootstrap). <br/>
24. [plan] support network based cli and json result.<br/>
25. [plan] support adobe flash refer/token/swf verification.<br/>
26. [plan] support adobe amf3 codec.<br/>
27. [plan] support dvr(record live to vod file)<br/>
28. [plan] support FMS edge protocol<br/>
29. [plan] support encryption: RTMPE/RTMPS, HLS DRM<br/>
30. [plan] support RTMPT, http to tranverse firewalls<br/>
31. [plan] support file source, transcoding file to live stream<br/>
### Performance
1. 300 connections, 150Mbps, 500kbps, CPU 18.8%, 5956KB.
@ -213,6 +275,10 @@ 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-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
* v0.9, 2013-12-21, add [players](http://demo.srs.com/players) for play and publish.
* v0.9, 2013-12-15, ensure the HLS(ts) is continous when republish stream.
* v0.9, 2013-12-15, fix the hls reload bug, feed it the sequence header.
* v0.9, 2013-12-15, refine protocol, use int64_t timestamp for ts and jitter.

Binary file not shown.

Binary file not shown.

@ -28,10 +28,14 @@ vhost __defaultVhost__ {
# for which cannot identify the required vhost.
# for default demo.
vhost demo.srs.com {
chunk_size 4096;
enabled on;
gop_cache on;
queue_length 30;
forward 127.0.0.1:19350;
bandcheck {
enabled off;
}
hls {
enabled on;
hls_path ./objs/nginx/html;
@ -140,6 +144,14 @@ vhost players_pub {
hls_window 30;
}
}
# rtmp only, no hls, for chat(low latecy)
vhost players_pub_rtmp {
gop_cache off;
hls {
enabled off;
}
}
# for development
vhost dev {
chunk_size 65000;
@ -201,16 +213,38 @@ vhost dev {
}
}
# vhost for bandwidth check
# generally, the bandcheck vhost must be: bandcheck.srs.com,
# or need to modify the vhost of client.
vhost bandcheck.srs.com {
chunk_size 65000;
enabled on;
# vhost for band width check
bandcheck{
enabled on;
key test kate;
interval 5;
limit_kbps 4000;
}
enabled on;
chunk_size 65000;
# bandwidth check config.
bandcheck {
# whether support bandwidth check,
# default: off.
enabled on;
# the key for server to valid,
# if invalid key, server disconnect and abort the bandwidth check.
key 35c9b402c12a7246868752e2878f7e0e;
# the interval in seconds for bandwidth check,
# server donot allow new test request.
# default: 30
interval 30;
# the max available check bandwidth in kbps.
# to avoid attack of bandwidth check.
# default: 1000
limit_kbps 4000;
}
}
# set the chunk size of vhost.
vhost chunksize.vhost.com {
# the default chunk size is 128, max is 65536,
# some client does not support chunk size change,
# vhost chunk size will override the global value.
# default: global chunk size.
chunk_size 128;
}
# the http hook callback vhost, srs will invoke the hooks for specified events.
@ -304,6 +338,7 @@ vhost hooks.callback.vhost.com {
on_stop http://127.0.0.1:8085/api/v1/sessions http://localhost:8085/api/v1/sessions;
}
}
# the mirror filter of ffmpeg, @see: http://ffmpeg.org/ffmpeg-filters.html#Filtering-Introduction
vhost mirror.transcode.vhost.com {
transcode {
@ -683,6 +718,7 @@ vhost stream.transcode.vhost.com {
}
}
}
# the vhost which forward publish streams.
vhost same.vhost.forward.vhost.com {
# forward all publish stream to the specified server.
@ -698,6 +734,7 @@ vhost same.vhost.forward.vhost.com {
vhost change.vhost.forward.vhost.com {
forward 127.0.0.1:1936?vhost=forward2.vhost.com 127.0.0.1:1937?vhost=forward3.vhost.com;
}
# the vhost disabled.
vhost removed.vhost.com {
# whether the vhost is enabled.
@ -705,6 +742,7 @@ vhost removed.vhost.com {
# default: on
enabled off;
}
# the vhost with hls specified.
vhost with-hls.vhost.com {
hls {
@ -741,6 +779,7 @@ vhost no-hls.vhost.com {
enabled off;
}
}
# the vhost for min delay, donot cache any stream.
vhost min.delay.com {
# whether cache the last gop.
@ -758,6 +797,7 @@ vhost min.delay.com {
# default: 30
queue_length 10;
}
# the vhost for antisuck.
vhost refer.anti_suck.com {
# the common refer for play and publish.
@ -776,6 +816,7 @@ vhost refer.anti_suck.com {
# default: not specified.
refer_play github.com github.io;
}
# config for the pithy print,
# which always print constant message specified by interval,
# whatever the clients in concurrency.

2
trunk/configure vendored

@ -116,7 +116,7 @@ MODULE_FILES=("srs_core" "srs_core_log" "srs_core_server"
"srs_core_handshake" "srs_core_pithy_print"
"srs_core_config" "srs_core_refer" "srs_core_reload"
"srs_core_hls" "srs_core_forward" "srs_core_encoder"
"srs_core_http" "srs_core_thread")
"srs_core_http" "srs_core_thread" "srs_core_bandwidth")
MODULE_DIR="src/core" . auto/modules.sh
CORE_OBJS="${MODULE_OBJS[@]}"

@ -36,7 +36,7 @@ reload(sys)
exec("sys.setdefaultencoding('utf-8')")
assert sys.getdefaultencoding().lower() == "utf-8"
import json, datetime, cherrypy
import os, json, time, datetime, cherrypy, threading
# simple log functions.
def trace(msg):
@ -320,6 +320,123 @@ class RESTSessions(object):
return code
global_chat_id = os.getpid();
'''
the chat streams, public chat room.
'''
class RESTChats(object):
exposed = True
global_id = 100
def __init__(self):
# object fields:
# id: an int value indicates the id of user.
# username: a str indicates the user name.
# url: a str indicates the url of user stream.
# agent: a str indicates the agent of user.
# join_date: a number indicates the join timestamp in seconds.
# join_date_str: a str specifies the formated friendly time.
# heatbeat: a number indicates the heartbeat timestamp in seconds.
# vcodec: a dict indicates the video codec info.
# acodec: a dict indicates the audio codec info.
self.__chats = [];
self.__chat_lock = threading.Lock();
# dead time in seconds, if exceed, remove the chat.
self.__dead_time = 30;
def GET(self):
enable_crossdomain()
try:
self.__chat_lock.acquire();
chats = [];
copy = self.__chats[:];
for chat in copy:
if time.time() - chat["heartbeat"] > self.__dead_time:
self.__chats.remove(chat);
continue;
chats.append({
"id": chat["id"],
"username": chat["username"],
"url": chat["url"],
"join_date_str": chat["join_date_str"],
"heartbeat": chat["heartbeat"],
});
finally:
self.__chat_lock.release();
return json.dumps({"code":0, "data": {"now": time.time(), "chats": chats}})
def POST(self):
enable_crossdomain()
req = cherrypy.request.body.read()
chat = json.loads(req)
global global_chat_id;
chat["id"] = global_chat_id
global_chat_id += 1
chat["join_date"] = time.time();
chat["heartbeat"] = time.time();
chat["join_date_str"] = time.strftime("%Y-%m-%d %H:%M:%S");
try:
self.__chat_lock.acquire();
self.__chats.append(chat)
finally:
self.__chat_lock.release();
trace("create chat success, id=%s"%(chat["id"]))
return json.dumps({"code":0, "data": chat["id"]})
def DELETE(self, id):
enable_crossdomain()
try:
self.__chat_lock.acquire();
for chat in self.__chats:
if str(id) != str(chat["id"]):
continue
self.__chats.remove(chat)
trace("delete chat success, id=%s"%(id))
return json.dumps({"code":0, "data": None})
finally:
self.__chat_lock.release();
raise cherrypy.HTTPError(405, "Not allowed.")
def PUT(self, id):
enable_crossdomain()
try:
self.__chat_lock.acquire();
for chat in self.__chats:
if str(id) != str(chat["id"]):
continue
chat["heartbeat"] = time.time();
trace("heartbeat chat success, id=%s"%(id))
return json.dumps({"code":0, "data": None})
finally:
self.__chat_lock.release();
raise cherrypy.HTTPError(405, "Not allowed.")
def OPTIONS(self, id=None):
enable_crossdomain()
# HTTP RESTful path.
class Root(object):
def __init__(self):
@ -335,6 +452,7 @@ class V1(object):
self.clients = RESTClients()
self.streams = RESTStreams()
self.sessions = RESTSessions()
self.chats = RESTChats()
'''
main code start.

@ -31,6 +31,7 @@
<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>

@ -1,3 +1,28 @@
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
/**
* 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.9"; }
// 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"); }
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
@ -20,12 +45,38 @@ function padding(number, length, prefix) {
function update_nav() {
$("#nav_srs_player").attr("href", "srs_player.html" + window.location.search);
$("#nav_srs_publisher").attr("href", "srs_publisher.html" + window.location.search);
$("#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);
}
/**
* 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">Usage:</span></strong>
<span id="txt_log_msg">创建会议室或者加入会议室</span>
</div>
*/
function info(desc) {
$("#txt_log").addClass("alert-info").removeClass("alert-error").removeClass("alert-warn");
$("#txt_log_title").text("Info:");
$("#txt_log_msg").text(desc);
}
function warn(code, desc) {
$("#txt_log").removeClass("alert-info").removeClass("alert-error").addClass("alert-warn");
$("#txt_log_title").text("Warn:");
$("#txt_log_msg").text("code: " + code + ", " + desc);
}
function error(code, desc) {
$("#txt_log").removeClass("alert-info").addClass("alert-error").removeClass("alert-warn");
$("#txt_log_title").text("Error:");
$("#txt_log_msg").text("code: " + code + ", " + desc);
}
/**
* parse the query string to object.
*/
@ -82,6 +133,23 @@ function build_default_rtmp_url() {
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
@ -150,23 +218,6 @@ function srs_parse_rtmp_url(rtmp_url) {
return ret;
}
/**
* 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.5"; }
// get the default vhost for players.
function srs_get_player_vhost() { return "players"; }
// 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"); }
/**
* initialize the page.
* @param rtmp_url the div id contains the rtmp stream url to play
@ -187,6 +238,190 @@ function srs_init(rtmp_url, hls_url, modal_player) {
$(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());
}
}
/**
* 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
) {
$(sl_cameras).empty();
for (var i = 0; i < cameras.length; i++) {
$(sl_cameras).append("<option value='" + i + "'>" + cameras[i] + "</option");
}
// optional: select the first no "virtual" signed.
for (var i = 0; i < cameras.length; i++) {
if (cameras[i].toLowerCase().indexOf("virtual") == -1) {
$(sl_cameras + " option[value='" + i + "']").attr("selected", true);
break;
}
}
$(sl_microphones).empty();
for (var i = 0; i < microphones.length; i++) {
$(sl_microphones).append("<option value='" + i + "'>" + microphones[i] + "</option");
}
$(sl_vcodec).empty();
var vcodecs = ["h264", "vp6"];
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_profile + " option[value='main']").attr("selected", true);
$(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_level + " option[value='4.1']").attr("selected", true);
$(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_gop + " option[value='10']").attr("selected", true);
$(sl_size).empty();
var sizes = ["176x144", "320x240", "352x240",
"352x288", "460x240", "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_size + " option[value='640x480']").attr("selected", true);
$(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_fps + " option[value='20']").attr("selected", true);
$(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");
}
$(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
) {
$(sl_cameras).empty();
for (var i = 0; i < cameras.length; i++) {
$(sl_cameras).append("<option value='" + i + "'>" + cameras[i] + "</option");
}
// optional: select the first no "virtual" signed.
for (var i = 0; i < cameras.length; i++) {
if (cameras[i].toLowerCase().indexOf("virtual") == -1) {
$(sl_cameras + " option[value='" + i + "']").attr("selected", true);
break;
}
}
$(sl_microphones).empty();
for (var i = 0; i < microphones.length; i++) {
$(sl_microphones).append("<option value='" + i + "'>" + microphones[i] + "</option");
}
$(sl_vcodec).empty();
var vcodecs = ["h264", "vp6"];
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_profile + " option[value='baseline']").attr("selected", true);
$(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_level + " option[value='3.1']").attr("selected", true);
$(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_gop + " option[value='0.5']").attr("selected", true);
$(sl_size).empty();
var sizes = ["176x144", "320x240", "352x240",
"352x288", "460x240", "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_size + " option[value='460x240']").attr("selected", true);
$(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_fps + " option[value='15']").attr("selected", true);
$(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");
}
$(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();
}
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
@ -196,8 +431,10 @@ function srs_init(rtmp_url, hls_url, modal_player) {
* @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) {
function SrsPlayer(container, width, height, private_object) {
if (!SrsPlayer.__id) {
SrsPlayer.__id = 100;
}
@ -207,6 +444,7 @@ function SrsPlayer(container, width, height) {
SrsPlayer.__players.push(this);
this.private_object = private_object;
this.container = container;
this.width = width;
this.height = height;
@ -222,11 +460,16 @@ function SrsPlayer(container, width, height) {
}
/**
* 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() {
SrsPlayer.prototype.start = function(url) {
if (url) {
this.stream_url = url;
}
// embed the flash.
var flashvars = {};
flashvars.id = this.id;
@ -261,7 +504,9 @@ SrsPlayer.prototype.start = function() {
* @param stream_url the url of stream, rtmp or http.
*/
SrsPlayer.prototype.play = function(url) {
this.stream_url = url;
if (url) {
this.stream_url = url;
}
this.callbackObj.ref.__play(this.stream_url, this.width, this.height, this.buffer_time);
}
SrsPlayer.prototype.stop = function() {
@ -373,8 +618,10 @@ function __srs_on_player_timer(id, time, buffer_length) {
* @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) {
function SrsPublisher(container, width, height, private_object) {
if (!SrsPublisher.__id) {
SrsPublisher.__id = 100;
}
@ -384,6 +631,7 @@ function SrsPublisher(container, width, height) {
SrsPublisher.__publishers.push(this);
this.private_object = private_object;
this.container = container;
this.width = width;
this.height = height;

@ -83,6 +83,7 @@
<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 class="active"><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>
<li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>

@ -79,6 +79,7 @@
<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 class="active"><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>

@ -28,6 +28,7 @@
<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 class="active"><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>

@ -0,0 +1,661 @@
<!DOCTYPE html>
<html>
<head>
<title>SRS</title>
<meta charset="utf-8">
<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/swfobject.js"></script>
<script type="text/javascript" src="js/srs.js"></script>
<style>
body{
padding-top: 55px;
}
</style>
<script type="text/javascript">
var srs_publisher = null;
var realtime_player = null;
var api_server = null;
var self_chat = null;
var global_chat_user_id = 200;
var previous_chats = [];
var no_play = false;
$(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");
$("#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;
}
$("#btn_video_settings").click(function(){
$("#video_modal").modal({show:true});
});
$("#btn_audio_settings").click(function(){
$("#audio_modal").modal({show:true});
});
$("#btn_join").click(on_user_publish);
// for publish, we use randome stream name.
$("#txt_url").val($("#txt_url").val() + "." + new Date().getTime());
// start the publisher.
srs_publisher = new SrsPublisher("local_publisher", 430, 185);
srs_publisher.on_publisher_ready = function(cameras, microphones) {
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_publisher.on_publisher_error = function(code, desc) {
error(code, desc);
};
srs_publisher.on_publisher_warn = function(code, desc) {
warn(code, desc);
};
srs_publisher.start();
update_play_url();
if (!no_play) {
// start the realtime player.
realtime_player = new SrsPlayer("realtime_player", 430, 185);
realtime_player.on_player_ready = function() {
this.set_bt(0.5);
this.set_fs("screen", 100);
};
realtime_player.start();
}
api_server = "http://" + query.hostname + ":" + srs_get_api_server_port() + "/api/v1/chats";
refresh();
});
function update_play_url() {
var url = $("#txt_url").val();
$("#realtime_player_url").attr("href", url).attr("target", "_blank");
}
function refresh() {
if (!self_chat) {
setTimeout(refresh, 1000);
return;
}
$.ajax({
type : "GET",
async : true,
url : api_server,
contentType: "text/html",
data : "",
dataType : "json",
complete : function() {
},
error : function(ret) {
setTimeout(refresh, 5000);
warn(101, "查询会议室失败:" + JSON.stringify(ret));
},
success : function(ret) {
if(0 != ret["code"]) {
warn(102, "查询会议室失败: " + JSON.stringify(ret));
setTimeout(refresh, 5000);
return;
}
var chats = ret["data"]["chats"];
var server_time = ret["now"];
on_get_chats(chats);
setTimeout(refresh, 5000);
}
});
}
function on_get_chats(chats) {
if (!self_chat) {
return;
}
// get self, check self is valid?
var _self_chat = null;
for (var i = 0; i < chats.length; i++) {
var chat = chats[i];
if (self_chat && self_chat.id == chat.id) {
_self_chat = chat;
break;
}
}
// rejoin if invalid.
if (!_self_chat) {
on_user_exit_chat(function(){
on_user_join_chat(function(){
info("重新加入会议室成功");
});
});
return;
}
render_chat_room(chats);
if (!self_chat) {
return;
}
// update self heartbeat.
var url = api_server + "/" + self_chat.id;
var chat = {};
chat.localtime = new Date().getTime();
// publish to api server to requires an id.
$.ajax({
type : "PUT",
async : true,
url : url,
contentType: "text/html",
data : JSON.stringify(chat),
dataType : "json",
complete : function() {
},
error : function(ret) {
warn(105, "更新会议室信息失败:" + JSON.stringify(ret));
},
success : function(ret) {
if(0 != ret["code"]) {
warn(106, "更新会议室信息失败:: " + JSON.stringify(ret));
return;
}
}
});
}
function render_chat_room(chats) {
if (!self_chat) {
return;
}
// 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.player = previous_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) {
continue;
}
if (chat.player) {
chat.player.stop();
}
$("#div_" + chat.id).remove();
}
previous_chats = chats;
}
function get_previous_chat_user(arr, id) {
for (var i = 0; i < arr.length; i++) {
var chat = arr[i];
if (id == chat.id) {
return chat;
}
}
return null;
}
function on_user_publish() {
if ($("#txt_name").val().trim() == "") {
$("#txt_name").focus().parent().parent().addClass("error");
warn(100, "请输入您的名字");
return;
}
$("#txt_name").parent().parent().removeClass("error");
// join chat.
if (!self_chat) {
on_user_join_chat();
} else {
on_user_exit_chat();
}
}
function on_user_exit_chat(complete_pfn) {
srs_publisher.stop();
$("#btn_join").text("加入会议");
if (realtime_player) {
realtime_player.stop();
}
if (!self_chat) {
return;
}
// 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();
}
$("#div_" + chat.id).remove();
}
previous_chats = [];
var url = api_server + "/" + self_chat.id;
// whatever, cleanup local chat.
self_chat = null;
$("#btn_join").attr("disabled", true);
// publish to api server to requires an id.
$.ajax({
type : "DELETE",
async : true,
url : url,
contentType: "text/html",
data : "",
dataType : "json",
complete : function() {
$("#btn_join").attr("disabled", false);
if (complete_pfn) {
complete_pfn();
}
},
error : function(ret) {
warn(103, "退出会议室失败");
},
success : function(ret) {
if(0 != ret["code"]) {
warn(104, "退出会议室失败");
return;
}
info("退出会议室成功");
}
});
}
function on_user_join_chat(complete_pfn) {
if (self_chat) {
return;
}
var url = $("#txt_url").val();
var vcodec = {};
var acodec = {};
srs_publiser_get_codec(
vcodec, acodec,
"#sl_cameras", "#sl_microphones",
"#sl_vcodec", "#sl_profile", "#sl_level", "#sl_gop", "#sl_size",
"#sl_fps", "#sl_bitrate"
);
var chat = {};
chat.id = -1;
chat.username = $("#txt_name").val().trim();
chat.agent = navigator.userAgent;
chat.url = url;
chat.vcodec = vcodec;
chat.acodec = acodec;
$("#btn_join").attr("disabled", true);
// publish to api server to requires an id.
$.ajax({
type : "POST",
async : true,
url : api_server,
contentType: "text/html",
data : JSON.stringify(chat),
dataType : "json",
complete : function() {
$("#btn_join").attr("disabled", false);
if (complete_pfn) {
complete_pfn();
}
},
error : function(ret) {
warn(105, "创建会议室失败:" + JSON.stringify(ret));
},
success : function(ret) {
if(0 != ret["code"]) {
warn(106, "创建会议室失败: " + JSON.stringify(ret));
return;
}
chat.id = ret["data"];
// success, start publish.
self_chat = chat;
$("#btn_join").text("退出会议");
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);
}
}
});
}
</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 class="active"><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">
<!-- 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">Usage:</span></strong>
<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>
<input type="text" id="txt_url" class="input-mini hide" value=""></input>
</div>
</div>
<div class="container">
<div class="row-fluid">
<div class="span6">
<div class="accordion-group">
<div class="accordion-heading">
<span class="accordion-toggle">
<strong>[我的] 本地摄像头</strong>
</span>
</div>
<div class="accordion-body collapse in">
<div class="accordion-inner">
<div id="local_publisher"></div>
</div>
</div>
</div>
</div>
<div class="span6">
<div class="accordion-group">
<div class="accordion-heading">
<span class="accordion-toggle">
<strong>[我的] 远程服务器流</strong>
<a id="realtime_player_url" href="#" data-toggle="tooltip" data-placement="top" title="">
播放地址<img src="img/tooltip.png"/>
</a>
</span>
</div>
<div class="accordion-body collapse in">
<div class="accordion-inner">
<div id="realtime_player"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<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"/>
</span>
</div>
<div id="collapseM" class="accordion-body collapse">
<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>
</div>
</div>
</div>
</div>
</div>
<div class="container" id="lst_chats">
</div>
<div id="video_modal" class="modal hide fade">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3>视频编码</h3>
</div>
<div class="modal-body">
<div class="form-horizontal">
<div class="control-group">
<label class="control-label" for="sl_cameras">
摄像头
<a id="sl_cameras_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
<img src="img/tooltip.png"/>
</a>
</label>
<div class="controls">
<select class="span4" id="sl_cameras"></select>
</div>
</div>
<div class="control-group">
<label class="control-label" for="sl_vcodec">
Codec
<a id="sl_cameras_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
<img src="img/tooltip.png"/>
</a>
</label>
<div class="controls">
<select class="span2" id="sl_vcodec"></select>
</div>
</div>
<div class="control-group">
<label class="control-label" for="sl_profile">
Profile
<a id="sl_profile_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
<img src="img/tooltip.png"/>
</a>
</label>
<div class="controls">
<select class="span2" id="sl_profile"></select>
</div>
</div>
<div class="control-group">
<label class="control-label" for="sl_level">
Level
<a id="sl_level_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
<img src="img/tooltip.png"/>
</a>
</label>
<div class="controls">
<select class="span2" id="sl_level"></select>
</div>
</div>
<div class="control-group">
<label class="control-label" for="sl_gop">
GOP
<a id="sl_gop_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
<img src="img/tooltip.png"/>
</a>
</label>
<div class="controls">
<select class="span2" id="sl_gop"></select>
</div>
</div>
<div class="control-group">
<label class="control-label" for="sl_size">
尺寸
<a id="sl_size_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
<img src="img/tooltip.png"/>
</a>
</label>
<div class="controls">
<select class="span2" id="sl_size"></select>
</div>
</div>
<div class="control-group">
<label class="control-label" for="sl_fps">
帧率
<a id="sl_fps_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
<img src="img/tooltip.png"/>
</a>
</label>
<div class="controls">
<select class="span2" id="sl_fps"></select>
</div>
</div>
<div class="control-group">
<label class="control-label" for="sl_bitrate">
码率
<a id="sl_bitrate_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
<img src="img/tooltip.png"/>
</a>
</label>
<div class="controls">
<select class="span2" id="sl_bitrate"></select>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" data-dismiss="modal" aria-hidden="true">设置</button>
</div>
</div>
<div id="audio_modal" class="modal hide fade">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3>音频编码</h3>
</div>
<div class="modal-body">
<div class="form-horizontal">
<div class="control-group">
<label class="control-label" for="sl_microphones">
麦克风
<a id="worker_id_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
<img src="img/tooltip.png"/>
</a>
</label>
<div class="controls">
<select class="span4" id="sl_microphones"></select>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" data-dismiss="modal" aria-hidden="true">设置</button>
</div>
</div>
<hr/>
<footer>
<p><a href="https://github.com/winlinvip/simple-rtmp-server">SRS Team &copy; 2013</a></p>
</footer>
</div>
</body>

@ -92,7 +92,7 @@
srs_player = new SrsPlayer("player_id", srs_get_player_width(), srs_get_player_height());
srs_player.on_player_ready = function() {
select_buffer_time("#btn_bt_0_8", 0.8);
srs_player.play(url);
this.play(url);
};
srs_player.on_player_metadata = function(metadata) {
$("#btn_dar_original").text("视频原始比例" + "(" + metadata.width + ":" + metadata.height + ")");
@ -100,7 +100,7 @@
select_fs_size("#btn_fs_size_screen_100", "screen", 100);
};
srs_player.on_player_timer = function(time, buffer_length) {
var buffer = buffer_length / srs_player.buffer_time * 100;
var buffer = buffer_length / this.buffer_time * 100;
$("#pb_buffer").width(Number(buffer).toFixed(1) + "%");
$("#pb_buffer_bg").attr("title",
@ -269,6 +269,7 @@
<ul class="nav">
<li class="active"><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>
@ -316,7 +317,7 @@
</div>
<div id="collapse10" class="accordion-body collapse">
<div class="accordion-inner">
<a href="#" id="srs_publish_hls">http://demo.srs.com/live/livestream.m3u8</a> <br/>
<a href="#" id="srs_publish_hls" target="_blank">http://demo.srs.com/live/livestream.m3u8</a> <br/>
<span>对用户的流进行HLS切片若编码为非H264/AACHLS流会自动禁用</span>
</div>
</div>
@ -342,7 +343,7 @@
</div>
<div id="collapse11" class="accordion-body collapse">
<div class="accordion-inner">
<a href="#" id="srs_publish_ld_hls">http://demo.srs.com/live/livestream_ld.m3u8</a> <br/>
<a href="#" id="srs_publish_ld_hls" target="_blank">http://demo.srs.com/live/livestream_ld.m3u8</a> <br/>
<span>对转码配置LD流进行HLS切片。</span>
</div>
</div>
@ -368,7 +369,7 @@
</div>
<div id="collapse12" class="accordion-body collapse">
<div class="accordion-inner">
<a href="#" id="srs_publish_sd_hls">http://demo.srs.com/live/livestream_sd.m3u8</a> <br/>
<a href="#" id="srs_publish_sd_hls" target="_blank">http://demo.srs.com/live/livestream_sd.m3u8</a> <br/>
<span>对转码配置SD流进行HLS切片。</span>
</div>
</div>
@ -394,7 +395,7 @@
</div>
<div id="collapse13" class="accordion-body collapse">
<div class="accordion-inner">
<a href="#" id="srs_publish_fw_hls">http://demo.srs.com/forward/live/livestream.m3u8</a> <br/>
<a href="#" id="srs_publish_fw_hls" target="_blank">http://demo.srs.com/forward/live/livestream.m3u8</a> <br/>
<span>对转发原始流进行HLS切片若编码为非H264/AACHLS流会自动禁用</span>
</div>
</div>
@ -420,7 +421,7 @@
</div>
<div id="collapse14" class="accordion-body collapse">
<div class="accordion-inner">
<a href="#" id="srs_publish_fw_ld_hls">http://demo.srs.com/forward/live/livestream_ld.m3u8</a> <br/>
<a href="#" id="srs_publish_fw_ld_hls" target="_blank">http://demo.srs.com/forward/live/livestream_ld.m3u8</a> <br/>
<span>对转发转码配置LD流进行HLS切片所有转发的流会自动支持HLS。</span>
</div>
</div>
@ -446,7 +447,7 @@
</div>
<div id="collapse15" class="accordion-body collapse">
<div class="accordion-inner">
<a href="#" id="srs_publish_fw_sd_hls">http://demo.srs.com/forward/live/livestream_sd.m3u8</a> <br/>
<a href="#" id="srs_publish_fw_sd_hls" target="_blank">http://demo.srs.com/forward/live/livestream_sd.m3u8</a> <br/>
<span>对转发转码配置SD流进行HLS切片所有转发的流会自动支持HLS。</span>
</div>
</div>

@ -37,6 +37,12 @@
$("#low_latecy_tips").tooltip({
title: "服务器不转码直接转发FLASH编码器的流所以延迟比支持HLS的流要低很多"
});
$("#realtime_player_url").tooltip({
title: "右键复制RTMP地址"
});
$("#remote_player_url").tooltip({
title: "右键复制RTMP地址"
});
$("#btn_publish").click(on_user_publish);
@ -46,74 +52,12 @@
// start the publisher.
srs_publisher = new SrsPublisher("local_publisher", 430, 185);
srs_publisher.on_publisher_ready = function(cameras, microphones) {
$("#sl_cameras").empty();
for (var i = 0; i < cameras.length; i++) {
$("#sl_cameras").append("<option value='" + i + "'>" + cameras[i] + "</option");
}
// optional: select the first no "virtual" signed.
for (var i = 0; i < cameras.length; i++) {
if (cameras[i].toLowerCase().indexOf("virtual") == -1) {
$("#sl_cameras option[value='" + i + "']").attr("selected", true);
break;
}
}
$("#sl_microphones").empty();
for (var i = 0; i < microphones.length; i++) {
$("#sl_microphones").append("<option value='" + i + "'>" + microphones[i] + "</option");
}
$("#sl_vcodec").empty();
var vcodecs = ["h264", "vp6"];
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_level option[value='4.1']").attr("selected", true);
$("#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_gop option[value='5']").attr("selected", true);
$("#sl_size").empty();
var sizes = ["176x144", "320x240", "352x240",
"352x288", "460x240", "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_size option[value='460x240']").attr("selected", true);
$("#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_fps option[value='15']").attr("selected", true);
$("#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");
}
$("#sl_bitrate option[value='350']").attr("selected", true);
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_publisher.on_publisher_error = function(code, desc) {
error(code, desc);
@ -123,22 +67,24 @@
};
srs_publisher.start();
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() {
remote_player.set_bt(0.8);
remote_player.set_fs("screen", 100);
this.set_bt(0.8);
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() {
realtime_player.set_bt(0.8);
realtime_player.set_fs("screen", 100);
this.set_bt(0.8);
this.set_fs("screen", 100);
};
realtime_player.start();
}
@ -157,6 +103,10 @@
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");
$("#remote_player_url").attr("href", remote_url).attr("target", "_blank");
var srs_player_url = "http://" + query.host + query.dir + "/srs_player.html?";
srs_player_url += "vhost=" + srs_get_player_publish_vhost(ret.vhost) + "&port=" + ret.port + "&app=" + ret.app + "&stream=" + ret.stream;
srs_player_url += "&autostart=true";
@ -180,10 +130,12 @@
if ($("#btn_publish").text() == "停止发布") {
srs_publisher.stop();
$("#btn_publish").text("发布视频");
$("#txt_play_realtime").text("RTMP低延时(请发布视频)").attr("href", "#").attr("target", "_self");
$("#txt_play_url").text("RTMP已转码(请发布视频)").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");
//$("#txt_play_realtime").text("RTMP低延时(请发布视频)").attr("href", "#").attr("target", "_self");
//$("#txt_play_realtime").attr("href", "#").attr("target", "_self");
//$("#txt_play_url").text("RTMP已转码(请发布视频)").attr("href", "#").attr("target", "_self");
//$("#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");
return;
}
@ -194,20 +146,12 @@
var url = $("#txt_url").val();
var vcodec = {};
var acodec = {};
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();
srs_publiser_get_codec(
vcodec, acodec,
"#sl_cameras", "#sl_microphones",
"#sl_vcodec", "#sl_profile", "#sl_level", "#sl_gop", "#sl_size",
"#sl_fps", "#sl_bitrate"
);
info("开始推流到服务器");
srs_publisher.publish(url, vcodec, acodec);
@ -230,22 +174,6 @@
remote_player.play(pub_url);
}
}
function info(desc) {
$("#txt_log").addClass("alert-info").removeClass("alert-error").removeClass("alert-warn");
$("#txt_log_title").text("Info:");
$("#txt_log_msg").text(desc);
}
function warn(code, desc) {
$("#txt_log").removeClass("alert-info").removeClass("alert-error").addClass("alert-warn");
$("#txt_log_title").text("Warn:");
$("#txt_log_msg").text("code: " + code + ", " + desc);
}
function error(code, desc) {
$("#txt_log").removeClass("alert-info").addClass("alert-error").removeClass("alert-warn");
$("#txt_log_title").text("Error:");
$("#txt_log_msg").text("code: " + code + ", " + desc);
}
</script>
</head>
<body>
@ -257,6 +185,7 @@
<ul class="nav">
<li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
<li class="active"><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>
@ -424,11 +353,11 @@
<div class="span6">
<div class="accordion-group">
<div class="accordion-heading">
<span class="accordion-toggle" data-toggle="collapse" href="#collapse1">
<span class="accordion-toggle">
<strong>本地摄像头</strong>
</span>
</div>
<div id="collapse1" class="accordion-body collapse in">
<div class="accordion-body collapse in">
<div class="accordion-inner">
<div id="local_publisher"></div>
</div>
@ -438,14 +367,17 @@
<div class="span6">
<div class="accordion-group">
<div class="accordion-heading">
<span class="accordion-toggle" data-toggle="collapse" href="#collapse2">
<span class="accordion-toggle">
<strong>远程服务器</strong>
<a id="remote_tips" href="#" data-toggle="tooltip" data-placement="top" title="">
黑屏<img src="img/tooltip.png"/>
</a>
<a id="remote_player_url" href="#" data-toggle="tooltip" data-placement="top" title="">
播放地址<img src="img/tooltip.png"/>
</a>
</span>
</div>
<div id="collapse2" class="accordion-body collapse in">
<div class="accordion-body collapse in">
<div class="accordion-inner">
<div id="remote_player"></div>
</div>
@ -459,14 +391,17 @@
<div class="span6">
<div class="accordion-group">
<div class="accordion-heading">
<span class="accordion-toggle" data-toggle="collapse" href="#collapse3">
<span class="accordion-toggle">
<strong>远程服务器</strong>
<a id="low_latecy_tips" href="#" data-toggle="tooltip" data-placement="top" title="">
低延时<img src="img/tooltip.png"/>
</a>
<a id="realtime_player_url" href="#" data-toggle="tooltip" data-placement="top" title="">
播放地址<img src="img/tooltip.png"/>
</a>
</span>
</div>
<div id="collapse3" class="accordion-body collapse in">
<div class="accordion-body collapse in">
<div class="accordion-inner">
<div id="realtime_player"></div>
</div>

@ -29,6 +29,7 @@
<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>
@ -39,7 +40,7 @@
</div>
</div>
<div class="container">
<iframe id="main_frame" width="100%" height="800" frameBorder="0"></iframe>
<iframe id="main_frame" width="100%" height="600" frameBorder="0"></iframe>
</div>
<div class="container">
<hr>

@ -0,0 +1,7 @@
#!/bin/bash
for((;;)); do \
./objs/ffmpeg/bin/ffmpeg -re -i ./doc/source.200kbps.768x320.flv \
-vcodec copy -acodec copy \
-f flv -y rtmp://127.0.0.1/live?vhost=demo.srs.com/livestream; \
sleep 1; \
done

@ -0,0 +1,7 @@
#!/bin/bash
for((;;)); do \
./objs/ffmpeg/bin/ffmpeg -re -i ./doc/source.200kbps.768x320.flv \
-vcodec copy -acodec copy \
-f flv -y rtmp://127.0.0.1/live?vhost=players/livestream; \
sleep 1; \
done

@ -0,0 +1,10 @@
#!/bin/bash
src_dir='src'
if [[ ! -d $src_dir ]]; then echo "错误必须在src同目录执行脚本"; exit 1; fi
echo "编译SRS"
./configure --with-ssl --with-hls --with-ffmpeg --with-http && make
ret=$?; if [[ 0 -ne $ret ]]; then echo "错误编译SRS失败"; exit $ret; fi
echo "编译SRS成功"
exit 0

@ -0,0 +1,12 @@
#!/bin/bash
src_dir='src'
if [[ ! -d $src_dir ]]; then echo "错误必须在src同目录执行脚本"; exit 1; fi
cmd="nohup python ./research/api-server/server.py 8085 >./objs/logs/api-server.log 2>&1 &"
echo "启动API服务器$cmd"
pids=`ps aux|grep python|grep research|grep "api-server"|awk '{print $2}'`; for pid in $pids; do echo "结束现有进程:$pid"; kill -s SIGKILL $pid; done
nohup python ./research/api-server/server.py 8085 >./objs/logs/api-server.log 2>&1 &
ret=$?; if [[ 0 -ne $ret ]]; then echo "错误启动API服务器失败"; exit $ret; fi
echo "启动API服务器成功"
exit 0

@ -0,0 +1,12 @@
#!/bin/bash
src_dir='src'
if [[ ! -d $src_dir ]]; then echo "错误必须在src同目录执行脚本"; exit 1; fi
cmd="nohup bash ./scripts/_ffmpeg.demo.sh >./objs/logs/ffmpeg-demo.log 2>&1 &"
echo "启动FFMPEG推送demo流(播放器上12路演示)$cmd"
pids=`ps aux|grep scripts|grep "/_ffmpeg.demo.sh"|awk '{print $2}'`; for pid in $pids; do echo "结束现有进程:$pid"; kill -s SIGKILL $pid; done
nohup bash ./scripts/_ffmpeg.demo.sh >./objs/logs/ffmpeg-demo.log 2>&1 &
ret=$?; if [[ 0 -ne $ret ]]; then echo "错误启动FFMPEG推送demo流(播放器上12路演示)失败"; exit $ret; fi
echo "启动FFMPEG推送demo流(播放器上12路演示)成功"
exit 0

@ -0,0 +1,12 @@
#!/bin/bash
src_dir='src'
if [[ ! -d $src_dir ]]; then echo "错误必须在src同目录执行脚本"; exit 1; fi
cmd="nohup bash ./scripts/_ffmpeg.players.sh >./objs/logs/ffmpeg-players.log 2>&1 &"
echo "启动FFMPEG推送players流(播放器上演示用)$cmd"
pids=`ps aux|grep scripts|grep "/_ffmpeg.players.sh"|awk '{print $2}'`; for pid in $pids; do echo "结束现有进程:$pid"; kill -s SIGKILL $pid; done
nohup bash ./scripts/_ffmpeg.players.sh >./objs/logs/ffmpeg-players.log 2>&1 &
ret=$?; if [[ 0 -ne $ret ]]; then echo "错误启动FFMPEG推送players流(播放器上演示用)失败"; exit $ret; fi
echo "启动FFMPEG推送players流(播放器上演示用)成功"
exit 0

@ -0,0 +1,12 @@
#!/bin/bash
src_dir='src'
if [[ ! -d $src_dir ]]; then echo "错误必须在src同目录执行脚本"; exit 1; fi
cmd="sudo ./objs/nginx/sbin/nginx"
echo "启动NGINXHLS服务$cmd"
pids=`ps aux|grep nginx|grep process|awk '{print $2}'`; for pid in $pids; do echo "结束现有进程:$pid"; sudo kill -s SIGKILL $pid; done
sudo ./objs/nginx/sbin/nginx
ret=$?; if [[ 0 -ne $ret ]]; then echo "错误启动NGINXHLS服务失败"; exit $ret; fi
echo "启动NGINXHLS服务成功"
exit 0

@ -0,0 +1,12 @@
#!/bin/bash
src_dir='src'
if [[ ! -d $src_dir ]]; then echo "错误必须在src同目录执行脚本"; exit 1; fi
cmd="nohup ./objs/srs -c conf/srs.19350.conf > ./objs/logs/srs.19350.log 2>&1 &"
echo "启动SRS转发服务器$cmd"
pids=`ps aux|grep srs|grep "./objs"|grep "srs.19350.conf"|awk '{print $2}'`; for pid in $pids; do echo "结束现有进程:$pid"; kill -s SIGKILL $pid; done
nohup ./objs/srs -c conf/srs.19350.conf > ./objs/logs/srs.19350.log 2>&1 &
ret=$?; if [[ 0 -ne $ret ]]; then echo "错误启动SRS转发服务器失败"; exit $ret; fi
echo "启动SRS转发服务器成功"
exit 0

@ -0,0 +1,12 @@
#!/bin/bash
src_dir='src'
if [[ ! -d $src_dir ]]; then echo "错误必须在src同目录执行脚本"; exit 1; fi
cmd="nohup ./objs/srs -c conf/srs.conf >./objs/logs/srs.log 2>&1 &"
echo "启动SRS服务器$cmd"
pids=`ps aux|grep srs|grep "./objs"|grep "srs.conf"|awk '{print $2}'`; for pid in $pids; do echo "结束现有进程:$pid"; kill -s SIGKILL $pid; done
nohup ./objs/srs -c conf/srs.conf >./objs/logs/srs.log 2>&1 &
ret=$?; if [[ 0 -ne $ret ]]; then echo "错误启动SRS失败"; exit $ret; fi
echo "启动SRS服务器成功"
exit 0

@ -0,0 +1,8 @@
#!/bin/bash
src_dir='src'
if [[ ! -d $src_dir ]]; then echo "错误必须在src同目录执行脚本"; exit 1; fi
# step 1: build srs
bash scripts/_step.build.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $ret; fi
echo "编译SRS成功"

@ -0,0 +1,42 @@
#!/bin/bash
src_dir='src'
if [[ ! -d $src_dir ]]; then echo "错误必须在src同目录执行脚本"; exit 1; fi
# linux shell color support.
RED="\\e[31m"
GREEN="\\e[32m"
YELLOW="\\e[33m"
BLACK="\\e[0m"
# step 1: build srs
#bash scripts/_step.build.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $ret; fi
# step 2: start srs
bash scripts/_step.start.srs.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $ret; fi
# step 3(optinal): start srs listen at 19350 to forward to
#bash scripts/_step.start.srs.19350.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $ret; fi
# step 4(optinal): start nginx for HLS
bash scripts/_step.start.nginx.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $ret; fi
# step 5(optinal): start http hooks for srs callback
bash scripts/_step.start.api.server.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $ret; fi
# step 6: publish demo live stream
#bash scripts/_step.start.ffmpeg.demo.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $ret; fi
# step 7: publish players live stream
#bash scripts/_step.start.ffmpeg.players.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $ret; fi
# 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}"

@ -0,0 +1,45 @@
#!/bin/bash
src_dir='src'
if [[ ! -d $src_dir ]]; then echo "错误必须在src同目录执行脚本"; exit 1; fi
# linux shell color support.
RED="\\e[31m"
GREEN="\\e[32m"
YELLOW="\\e[33m"
BLACK="\\e[0m"
# step 1: build srs
#bash scripts/_step.build.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $ret; fi
# step 2: start srs
bash scripts/_step.start.srs.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $ret; fi
# step 3(optinal): start srs listen at 19350 to forward to
bash scripts/_step.start.srs.19350.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $ret; fi
# step 4(optinal): start nginx for HLS
bash scripts/_step.start.nginx.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $ret; fi
# step 5(optinal): start http hooks for srs callback
bash scripts/_step.start.api.server.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $ret; fi
# step 6: publish demo live stream
bash scripts/_step.start.ffmpeg.demo.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $ret; fi
# step 7: publish players live stream
bash scripts/_step.start.ffmpeg.players.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $ret; fi
# 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}'`
cat<<END
默认的12路流演示
http://$ip/players
默认的播放器流演示:
http://$ip/players/srs_player.html?vhost=players
推流(主播)应用演示:
http://$ip/players/srs_publisher.html?vhost=players
视频会议(聊天室)应用演示:
http://$ip/players/srs_chat.html?vhost=players
END
echo -e "${GREEN}演示地址:${BLACK}"
echo -e "${RED} http://$ip${BLACK}"

@ -0,0 +1,31 @@
#!/bin/bash
echo "停止SRS服务器"
ps aux|grep srs|grep "./objs"|grep "srs.conf"
pids=`ps aux|grep srs|grep "./objs"|grep "srs.conf"|awk '{print $2}'`; for pid in $pids; do echo "结束现有进程:$pid"; kill -s SIGKILL $pid; done
echo "停止SRS转发服务器"
ps aux|grep srs|grep "./objs"|grep "srs.19350.conf"
pids=`ps aux|grep srs|grep "./objs"|grep "srs.19350.conf"|awk '{print $2}'`; for pid in $pids; do echo "结束现有进程:$pid"; kill -s SIGKILL $pid; done
# step 4(optinal): start nginx for HLS
echo "停止NGINXHLS服务"
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
# step 5(optinal): start http hooks for srs callback
echo "停止API服务器"
ps aux|grep python|grep research|grep "api-server"
pids=`ps aux|grep python|grep research|grep "api-server"|awk '{print $2}'`; for pid in $pids; do echo "结束现有进程:$pid"; kill -s SIGKILL $pid; done
# step 6: publish demo live stream
echo "停止FFMPEG推送demo流(播放器上12路演示)"
ps aux|grep scripts|grep "ffmpeg.demo.sh"
pids=`ps aux|grep scripts|grep "/ffmpeg.demo.sh"|awk '{print $2}'`; for pid in $pids; do echo "结束现有进程:$pid"; kill -s SIGKILL $pid; done
# step 7: publish players live stream
echo "停止FFMPEG推送players流(播放器上演示用)"
ps aux|grep scripts|grep "ffmpeg.players.sh"
pids=`ps aux|grep scripts|grep "/ffmpeg.players.sh"|awk '{print $2}'`; for pid in $pids; do echo "结束现有进程:$pid"; kill -s SIGKILL $pid; done
echo "SRS系统服务均已停止"

@ -79,7 +79,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define RTMP_SIG_SRS_WEB "http://blog.csdn.net/win_lin"
#define RTMP_SIG_SRS_EMAIL "winterserver@126.com"
#define RTMP_SIG_SRS_LICENSE "The MIT License (MIT)"
#define RTMP_SIG_SRS_COPYRIGHT "Copyright (c) 2013 winlin,wenjiegit"
#define RTMP_SIG_SRS_COPYRIGHT "Copyright (c) 2013 winlin"
#define RTMP_SIG_SRS_CONTRIBUTOR "winlin,wenjiegit"
// compare
@ -108,4 +108,15 @@ extern void srs_vhost_resolve(std::string& vhost, std::string& app);
// close the netfd, and close the underlayer fd.
extern void srs_close_stfd(st_netfd_t& stfd);
/**
* disable copy constructor of class
*/
#define disable_default_copy(className)\
private:\
/** \
* disable the copy constructor and operator=, donot allow directly copy. \
*/ \
className(const className&); \
className& operator= (const className&)
#endif

@ -0,0 +1,450 @@
/*
The MIT License (MIT)
Copyright (c) 2013 wenjiegit
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <srs_core_bandwidth.hpp>
#include <arpa/inet.h>
using namespace std;
#include <srs_core_rtmp.hpp>
#include <srs_core_error.hpp>
#include <srs_core_amf0.hpp>
#include <srs_core_protocol.hpp>
#include <srs_core_config.hpp>
#include <srs_core_autofree.hpp>
SrsBandwidth::SrsBandwidth()
{
}
SrsBandwidth::~SrsBandwidth()
{
}
int SrsBandwidth::bandwidth_test(SrsRequest* _req, st_netfd_t stfd, SrsRtmp* _rtmp)
{
int ret = ERROR_SUCCESS;
rtmp = _rtmp;
req = _req;
if (!config->get_bw_check_enabled(req->vhost)) {
return ret;
}
// validate the bandwidth check key
std::string key = "key=" + config->get_bw_check_key(req->vhost);
if (req->tcUrl.find(key) == std::string::npos) {
ret = ERROR_SYSTEM_BANDWIDTH_KEY;
srs_error("check the vhost=%s %s failed, tcUrl=%s, ret=%d",
req->vhost.c_str(), key.c_str(), req->tcUrl.c_str(), ret);
return ret;
}
// shared global last check time,
// to avoid attach by bandwidth check,
// if client request check in the window(specifeid by interval),
// directly reject the request.
static int64_t last_check_time = 0;
int interval_ms = config->get_bw_check_interval_ms(req->vhost);
int64_t time_now = srs_get_system_time_ms();
// reject the connection in the interval window.
if (last_check_time > 0 && time_now - last_check_time < interval_ms) {
ret = ERROR_SYSTEM_BANDWIDTH_DENIED;
srs_trace("bandcheck denied, "
"last_check=%"PRId64", now=%"PRId64", interval=%d",
last_check_time, time_now, interval_ms);
rtmp->response_connect_reject(req, "bandcheck rejected");
return ret;
}
// accept and do bandwidth check.
last_check_time = time_now;
char* local_ip = 0;
if ((ret = get_local_ip(stfd, local_ip)) != ERROR_SUCCESS) {
srs_error("get local ip failed. ret = %d", ret);
return ret;
}
if ((ret = rtmp->response_connect_app(req, local_ip)) != ERROR_SUCCESS) {
srs_error("response connect app failed. ret=%d", ret);
return ret;
}
return do_bandwidth_check();
}
int SrsBandwidth::get_local_ip(st_netfd_t stfd, char *&local_ip)
{
int ret = ERROR_SUCCESS;
int fd = st_netfd_fileno(stfd);
// discovery client information
sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
if (getsockname(fd, (sockaddr*)&addr, &addrlen) == -1) {
ret = ERROR_SOCKET_GET_LOCAL_IP;
srs_error("discovery local ip information failed. ret=%d", ret);
return ret;
}
srs_verbose("get local ip success.");
// ip v4 or v6
char buf[INET6_ADDRSTRLEN];
memset(buf, 0, sizeof(buf));
if ((inet_ntop(addr.sin_family, &addr.sin_addr, buf, sizeof(buf))) == NULL) {
ret = ERROR_SOCKET_GET_LOCAL_IP;
srs_error("convert local ip information failed. ret=%d", ret);
return ret;
}
local_ip = new char[strlen(buf) + 1];
strcpy(local_ip, buf);
srs_verbose("get local ip of client ip=%s, fd=%d", buf, fd);
return ret;
}
int SrsBandwidth::do_bandwidth_check()
{
int ret = ERROR_SUCCESS;
SrsProtocol* protocol = rtmp->get_protocol();
int play_duration_ms = 3000;
int play_interval_ms = 0;
int play_actual_duration_ms = 0;
int play_bytes = 0;
int publish_duration_ms = 3000;
int publish_interval_ms = 0;
int publish_actual_duration_ms = 0;
int publish_bytes = 0;
int limit_kbps = config->get_bw_check_limit_kbps(req->vhost);
int64_t start_time = srs_get_system_time_ms();
ret = check_play(play_duration_ms,
play_interval_ms, play_actual_duration_ms, play_bytes, limit_kbps);
if (ret != ERROR_SUCCESS) {
srs_error("band width play check failed. ret=%d", ret);
return ret;
}
ret = check_publish(publish_duration_ms,
publish_interval_ms, publish_actual_duration_ms, publish_bytes, limit_kbps);
if (ret != ERROR_SUCCESS) {
srs_error("band width publish check failed. ret=%d", ret);
return ret;
}
int64_t end_time = srs_get_system_time_ms();
int play_kbps = play_bytes * 8 / play_actual_duration_ms;
int publish_kbps = publish_bytes * 8 / publish_actual_duration_ms;
srs_trace("bandwidth check finished. start=%"PRId64"ms, end=%"PRId64"ms, "
"duartion=%dms, play=%dkbps, publish=%dkbps, tcUrl=%s, ret=%#x",
start_time, end_time, (int)(end_time - start_time), play_kbps, publish_kbps,
req->tcUrl.c_str(), ret);
// send finished msg
SrsBandwidthPacket* pkt = SrsBandwidthPacket::create_finish();
pkt->data->set("code", new SrsAmf0Number(ERROR_SUCCESS));
pkt->data->set("start_time", new SrsAmf0Number(start_time));
pkt->data->set("end_time", new SrsAmf0Number(end_time));
pkt->data->set("play_kbps", new SrsAmf0Number(play_kbps));
pkt->data->set("publish_kbps", new SrsAmf0Number(publish_kbps));
pkt->data->set("play_bytes", new SrsAmf0Number(play_bytes));
pkt->data->set("play_time", new SrsAmf0Number(play_actual_duration_ms));
pkt->data->set("publish_bytes", new SrsAmf0Number(publish_bytes));
pkt->data->set("publish_time", new SrsAmf0Number(publish_actual_duration_ms));
SrsCommonMessage* msg = (new SrsCommonMessage())->set_packet(pkt, 0);
if ((ret = rtmp->send_message(msg)) != ERROR_SUCCESS) {
srs_error("send bandwidth check finish message failed. ret=%d", ret);
return ret;
}
// if flash, we notice the result, and expect a final packet.
while (true) {
SrsCommonMessage* msg = NULL;
SrsBandwidthPacket* pkt = NULL;
if ((ret = srs_rtmp_expect_message<SrsBandwidthPacket>(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
// info level to ignore and return success.
srs_info("expect final message failed. ret=%d", ret);
return ERROR_SUCCESS;
}
SrsAutoFree(SrsCommonMessage, msg, false);
srs_info("get final message success.");
if (pkt->is_flash_final()) {
srs_info("BW check recv flash final response.");
break;
}
}
srs_info("BW check finished.");
return ret;
}
int SrsBandwidth::check_play(
int duration_ms, int interval_ms, int& actual_duration_ms,
int& play_bytes, int max_play_kbps)
{
int ret = ERROR_SUCCESS;
SrsProtocol* protocol = rtmp->get_protocol();
if (true) {
// send start play command to client
SrsBandwidthPacket* pkt = SrsBandwidthPacket::create_start_play();
pkt->data->set("duration_ms", new SrsAmf0Number(duration_ms));
pkt->data->set("interval_ms", new SrsAmf0Number(interval_ms));
SrsCommonMessage* msg = (new SrsCommonMessage())->set_packet(pkt, 0);
if ((ret = rtmp->send_message(msg)) != ERROR_SUCCESS) {
srs_error("send bandwidth check start play message failed. ret=%d", ret);
return ret;
}
srs_info("BW check begin.");
}
while (true) {
// recv client's starting play response
SrsCommonMessage* msg = NULL;
SrsBandwidthPacket* pkt = NULL;
if ((ret = srs_rtmp_expect_message<SrsBandwidthPacket>(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
srs_error("expect bandwidth message failed. ret=%d", ret);
return ret;
}
SrsAutoFree(SrsCommonMessage, msg, false);
srs_info("get bandwidth message succes.");
if (pkt->is_starting_play()) {
srs_info("BW check recv play begin response.");
break;
}
}
// send play data to client
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);
int interval = 0;
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: get length from the rtmp protocol stack.
play_bytes += pkt->get_payload_length();
SrsCommonMessage* msg = (new SrsCommonMessage())->set_packet(pkt, 0);
if ((ret = rtmp->send_message(msg)) != ERROR_SUCCESS) {
srs_error("send bandwidth check play messages failed. ret=%d", ret);
return ret;
}
// sleep while current kbps <= max_play_kbps
int kbps = 0;
while (true) {
if(srs_get_system_time_ms() - current_time != 0)
kbps = play_bytes * 8 / (srs_get_system_time_ms() - current_time);
if (kbps > max_play_kbps) {
st_usleep(500);
} else {
break;
}
}
}
actual_duration_ms = srs_get_system_time_ms() - current_time;
srs_info("BW check send play bytes over.");
if (true) {
// notify client to stop play
SrsBandwidthPacket* pkt = SrsBandwidthPacket::create_stop_play();
pkt->data->set("duration_ms", new SrsAmf0Number(duration_ms));
pkt->data->set("interval_ms", new SrsAmf0Number(interval_ms));
pkt->data->set("duration_delta", new SrsAmf0Number(actual_duration_ms));
pkt->data->set("bytes_delta", new SrsAmf0Number(play_bytes));
SrsCommonMessage* msg = (new SrsCommonMessage())->set_packet(pkt, 0);
if ((ret = rtmp->send_message(msg)) != ERROR_SUCCESS) {
srs_error("send bandwidth check stop play message failed. ret=%d", ret);
return ret;
}
srs_info("BW check stop play bytes.");
}
while (true) {
// recv client's stop play response.
SrsCommonMessage* msg = NULL;
SrsBandwidthPacket* pkt = NULL;
if ((ret = srs_rtmp_expect_message<SrsBandwidthPacket>(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
srs_error("expect bandwidth message failed. ret=%d", ret);
return ret;
}
SrsAutoFree(SrsCommonMessage, msg, false);
srs_info("get bandwidth message succes.");
if (pkt->is_stopped_play()) {
srs_info("BW check recv stop play response.");
break;
}
}
return ret;
}
int SrsBandwidth::check_publish(
int duration_ms, int interval_ms, int& actual_duration_ms,
int& publish_bytes, int max_pub_kbps)
{
int ret = ERROR_SUCCESS;
SrsProtocol* protocol = rtmp->get_protocol();
if (true) {
// notify client to start publish
SrsBandwidthPacket* pkt = SrsBandwidthPacket::create_start_publish();
pkt->data->set("duration_ms", new SrsAmf0Number(duration_ms));
pkt->data->set("interval_ms", new SrsAmf0Number(interval_ms));
SrsCommonMessage* msg = (new SrsCommonMessage())->set_packet(pkt, 0);
if ((ret = rtmp->send_message(msg)) != ERROR_SUCCESS) {
srs_error("send bandwidth check start publish message failed. ret=%d", ret);
return ret;
}
srs_info("BW check publish begin.");
}
while (true) {
// read client's notification of starting publish
SrsCommonMessage* msg = NULL;
SrsBandwidthPacket* pkt = NULL;
if ((ret = srs_rtmp_expect_message<SrsBandwidthPacket>(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
srs_error("expect bandwidth message failed. ret=%d", ret);
return ret;
}
SrsAutoFree(SrsCommonMessage, msg, false);
srs_info("get bandwidth message succes.");
if (pkt->is_starting_publish()) {
srs_info("BW check recv publish begin response.");
break;
}
}
// recv publish msgs until @duration_ms ms
int64_t current_time = srs_get_system_time_ms();
while ( (srs_get_system_time_ms() - current_time) < duration_ms ) {
st_usleep(0);
SrsCommonMessage* msg = NULL;
if ((ret = rtmp->recv_message(&msg)) != ERROR_SUCCESS) {
srs_error("recv message failed. ret=%d", ret);
return ret;
}
SrsAutoFree(SrsCommonMessage, msg, false);
// TODO: FIXME.
publish_bytes += msg->header.payload_length;
int kbps = 0;
while (true) {
if(srs_get_system_time_ms() - current_time != 0)
kbps = publish_bytes * 8 / (srs_get_system_time_ms() - current_time);
if (kbps > max_pub_kbps) {
st_usleep(500);
} else {
break;
}
}
}
actual_duration_ms = srs_get_system_time_ms() - current_time;
srs_info("BW check recv publish data over.");
if (true) {
// notify client to stop publish
SrsBandwidthPacket* pkt = SrsBandwidthPacket::create_stop_publish();
pkt->data->set("duration_ms", new SrsAmf0Number(duration_ms));
pkt->data->set("interval_ms", new SrsAmf0Number(interval_ms));
pkt->data->set("duration_delta", new SrsAmf0Number(actual_duration_ms));
pkt->data->set("bytes_delta", new SrsAmf0Number(publish_bytes));
SrsCommonMessage* msg = (new SrsCommonMessage())->set_packet(pkt, 0);
if ((ret = rtmp->send_message(msg)) != ERROR_SUCCESS) {
srs_error("send bandwidth check stop publish message failed. ret=%d", ret);
return ret;
}
srs_info("BW check stop publish bytes.");
}
// expect client to stop publish
// if flash client, we never expect the client stop publish bytes,
// for the flash send call packet to test publish bandwidth,
// there are many many packets in the queue.
// we just ignore the packet and send the bandwidth test data.
// TODO: FIXME: check whether flash client.
while (false) {
// recv client's stop publish response.
SrsCommonMessage* msg = NULL;
SrsBandwidthPacket* pkt = NULL;
if ((ret = srs_rtmp_expect_message<SrsBandwidthPacket>(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
srs_error("expect bandwidth message failed. ret=%d", ret);
return ret;
}
SrsAutoFree(SrsCommonMessage, msg, false);
srs_info("get bandwidth message succes.");
if (pkt->is_stopped_publish()) {
srs_info("BW check recv stop publish response.");
break;
}
}
return ret;
}

@ -0,0 +1,94 @@
/*
The MIT License (MIT)
Copyright (c) 2013 wenjiegit
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef SRS_CORE_BANDWIDTH_HPP
#define SRS_CORE_BANDWIDTH_HPP
/*
#include <srs_core_bandwidth.hpp>
*/
#include <srs_core.hpp>
class SrsRequest;
class SrsRtmp;
/**
* bandwidth test agent which provides the interfaces for bandwidth check.
* 1. if vhost disabled bandwidth check, ignore.
* 2. otherwise, check the key, error if verify failed.
* 3. check the interval limit, error if bandwidth in the interval window.
* 4. check the bandwidth under the max kbps.
* 5. send the bandwidth data to client.
* bandwidth workflow:
* +------------+ +----------+
* | Client | | Server |
* +-----+------+ +-----+----+
* | |
* | connect vhost------> | if vhost enable bandwidth,
* | <-----result(success) | do bandwidth check.
* | |
* | <----call(start play) | onSrsBandCheckStartPlayBytes
* | result(playing)-----> | onSrsBandCheckStartingPlayBytes
* | <-------data(playing) | onSrsBandCheckStartingPlayBytes
* | <-----call(stop play) | onSrsBandCheckStopPlayBytes
* | result(stopped)-----> | onSrsBandCheckStoppedPlayBytes
* | |
* | <-call(start publish) | onSrsBandCheckStartPublishBytes
* | result(publishing)--> | onSrsBandCheckStartingPublishBytes
* | data(publishing)----> | onSrsBandCheckStartingPublishBytes
* | <--call(stop publish) | onSrsBandCheckStopPublishBytes
* | result(stopped)(1)--> | onSrsBandCheckStoppedPublishBytes
* | |
* | <--------------report |
* | final(2)------------> | finalClientPacket
* | <END> |
*
* 1. when flash client, server ignore the publish stopped result,
* and directly send the report to flash client.
* 2. flash client only. when got report, flash client should send out
* a final packet and close the connection immediately.
*/
class SrsBandwidth
{
private:
SrsRequest* req;
SrsRtmp* rtmp;
public:
SrsBandwidth();
virtual ~SrsBandwidth();
public:
/**
* do the bandwidth test.
*/
virtual int bandwidth_test(SrsRequest* _req, st_netfd_t stfd, SrsRtmp* _rtmp);
private:
virtual int get_local_ip(st_netfd_t stfd, char *&local_ip);
/**
* used to process band width check from client.
*/
virtual int do_bandwidth_check();
virtual int check_play(int duration_ms, int interval_ms, int& actual_duration_ms, int& play_bytes, int max_play_kbps);
virtual int check_publish(int duration_ms, int interval_ms, int& actual_duration_ms, int& publish_bytes, int max_pub_kbps);
};
#endif

@ -41,6 +41,7 @@ using namespace std;
#include <srs_core_refer.hpp>
#include <srs_core_hls.hpp>
#include <srs_core_http.hpp>
#include <srs_core_bandwidth.hpp>
#define SRS_PULSE_TIMEOUT_MS 100
#define SRS_SEND_TIMEOUT_US 5000000L
@ -58,6 +59,7 @@ SrsClient::SrsClient(SrsServer* srs_server, st_netfd_t client_stfd)
#ifdef SRS_HTTP
http_hooks = new SrsHttpHooks();
#endif
bandwidth = new SrsBandwidth();
config->subscribe(this);
}
@ -74,6 +76,7 @@ SrsClient::~SrsClient()
#ifdef SRS_HTTP
srs_freep(http_hooks);
#endif
srs_freep(bandwidth);
}
// TODO: return detail message when error for client.
@ -154,38 +157,11 @@ int SrsClient::service_cycle()
}
srs_verbose("set peer bandwidth success");
if (config->get_bw_check_enabled(req->vhost, req->bw_key)) {
static int64_t last_check_time_ms = srs_get_system_time_ms();
int64_t interval_ms = 0;
int limit_kbps = 0;
config->get_bw_check_settings(req->vhost, interval_ms, limit_kbps);
if((srs_get_system_time_ms() - last_check_time_ms) < interval_ms
&& last_check_time_ms != srs_get_system_time_ms())
{
srs_trace("bandcheck interval less than limted interval. last time=%lld, current time=%lld"
, last_check_time_ms, srs_get_system_time_ms());
return rtmp->response_connect_reject(req, "your bandcheck frequency is too high!");
} else {
last_check_time_ms = srs_get_system_time_ms(); // update last check time
char* local_ip = 0;
if ((ret = get_local_ip(local_ip)) != ERROR_SUCCESS) {
srs_error("get local ip failed. ret = %d", ret);
return ret;
}
if ((ret = rtmp->response_connect_app(req, local_ip)) != ERROR_SUCCESS) {
srs_error("response connect app failed. ret=%d", ret);
return ret;
}
return rtmp->start_bandwidth_check(limit_kbps);
}
}
// do bandwidth test if connect to the vhost which is for bandwidth check.
if (config->get_bw_check_enabled(req->vhost)) {
return bandwidth->bandwidth_test(req, stfd, rtmp);
}
if ((ret = rtmp->response_connect_app(req)) != ERROR_SUCCESS) {
srs_error("response connect app failed. ret=%d", ret);
return ret;

@ -44,6 +44,7 @@ class SrsCommonMessage;
#ifdef SRS_HTTP
class SrsHttpHooks;
#endif
class SrsBandwidth;
/**
* the client provides the main logic control for RTMP clients.
@ -59,6 +60,7 @@ private:
#ifdef SRS_HTTP
SrsHttpHooks* http_hooks;
#endif
SrsBandwidth* bandwidth;
public:
SrsClient(SrsServer* srs_server, st_netfd_t client_stfd);
virtual ~SrsClient();

@ -1443,30 +1443,27 @@ SrsConfDirective* SrsConfig::get_listen()
return root->get("listen");
}
int SrsConfig::get_chunk_size()
{
SrsConfDirective* conf = root->get("chunk_size");
if (!conf) {
return SRS_CONF_DEFAULT_CHUNK_SIZE;
}
return ::atoi(conf->arg0().c_str());
}
int SrsConfig::get_chunk_size(const std::string &vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return get_chunk_size();
return SRS_CONF_DEFAULT_CHUNK_SIZE;
}
SrsConfDirective* conf_vhost = conf->get("chunk_size");
if (!conf_vhost) {
return get_chunk_size();
conf = conf->get("chunk_size");
if (!conf) {
// vhost does not specify the chunk size,
// use the global instead.
conf = root->get("chunk_size");
if (!conf) {
return SRS_CONF_DEFAULT_CHUNK_SIZE;
}
return ::atoi(conf->arg0().c_str());
}
return ::atoi(conf_vhost->arg0().c_str());
return ::atoi(conf->arg0().c_str());
}
int SrsConfig::get_pithy_print_publish()
@ -1568,6 +1565,90 @@ void SrsConfig::get_bw_check_settings(const std::string &vhost, int64_t &interva
}
}
bool SrsConfig::get_bw_check_enabled(const string &vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return false;
}
conf = conf->get("bandcheck");
if (!conf) {
return false;
}
conf = conf->get("enabled");
if (!conf || conf->arg0() != "on") {
return false;
}
return true;
}
string SrsConfig::get_bw_check_key(const string &vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return "";
}
conf = conf->get("bandcheck");
if (!conf) {
return "";
}
conf = conf->get("key");
if (!conf) {
return "";
}
return conf->arg0();
}
int SrsConfig::get_bw_check_interval_ms(const string &vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return SRS_CONF_DEFAULT_BANDWIDTH_INTERVAL;
}
conf = conf->get("bandcheck");
if (!conf) {
return SRS_CONF_DEFAULT_BANDWIDTH_INTERVAL;
}
conf = conf->get("interval_ms");
if (!conf) {
return SRS_CONF_DEFAULT_BANDWIDTH_INTERVAL;
}
return ::atoi(conf->arg0().c_str()) * 1000;
}
int SrsConfig::get_bw_check_limit_kbps(const string &vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return SRS_CONF_DEFAULT_BANDWIDTH_LIMIT_KBPS;
}
conf = conf->get("bandcheck");
if (!conf) {
return SRS_CONF_DEFAULT_BANDWIDTH_LIMIT_KBPS;
}
conf = conf->get("limit_kbps");
if (!conf) {
return SRS_CONF_DEFAULT_BANDWIDTH_LIMIT_KBPS;
}
return ::atoi(conf->arg0().c_str());
}
int SrsConfig::get_pithy_print_encoder()
{
SrsConfDirective* pithy = root->get("encoder");

@ -53,6 +53,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define SRS_CONF_DEFAULT_QUEUE_LENGTH 30
// in seconds, the paused queue length.
#define SRS_CONF_DEFAULT_PAUSED_LENGTH 10
// the interval in seconds for bandwidth check
#define SRS_CONF_DEFAULT_BANDWIDTH_INTERVAL 30
// the interval in seconds for bandwidth check
#define SRS_CONF_DEFAULT_BANDWIDTH_LIMIT_KBPS 1000
#define SRS_CONF_DEFAULT_CHUNK_SIZE 4096
@ -164,15 +168,16 @@ public:
virtual SrsConfDirective* get_refer_play(std::string vhost);
virtual SrsConfDirective* get_refer_publish(std::string vhost);
virtual SrsConfDirective* get_listen();
virtual int get_chunk_size();
virtual int get_chunk_size(const std::string& vhost);
virtual int get_pithy_print_publish();
virtual int get_pithy_print_forwarder();
virtual int get_pithy_print_encoder();
virtual int get_pithy_print_hls();
virtual int get_pithy_print_play();
virtual bool get_bw_check_enabled(const std::string &vhost, const std::string &key);
virtual void get_bw_check_settings(const std::string &vhost, int64_t &interval_ms, int &limit_kbps);
virtual bool get_bw_check_enabled(const std::string& vhost);
virtual std::string get_bw_check_key(const std::string& vhost);
virtual int get_bw_check_interval_ms(const std::string& vhost);
virtual int get_bw_check_limit_kbps(const std::string& vhost);
};
/**

@ -87,6 +87,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define ERROR_SYSTEM_IP_INVALID 411
#define ERROR_SYSTEM_FORWARD_LOOP 412
#define ERROR_SYSTEM_WAITPID 413
#define ERROR_SYSTEM_BANDWIDTH_KEY 414
#define ERROR_SYSTEM_BANDWIDTH_DENIED 415
// see librtmp.
// failed when open ssl create the dh

@ -32,6 +32,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <srs_core_stream.hpp>
#include <srs_core_autofree.hpp>
using namespace std;
/****************************************************************************
*****************************************************************************
****************************************************************************/
@ -209,6 +211,35 @@ messages.
#define RTMP_AMF0_DATA_SET_DATAFRAME "@setDataFrame"
#define RTMP_AMF0_DATA_ON_METADATA "onMetaData"
/**
* band width check method name, which will be invoked by client.
* band width check mothods use SrsBandwidthPacket as its internal packet type,
* so ensure you set command name when you use it.
*/
// server play control
#define SRS_BW_CHECK_START_PLAY "onSrsBandCheckStartPlayBytes"
#define SRS_BW_CHECK_STARTING_PLAY "onSrsBandCheckStartingPlayBytes"
#define SRS_BW_CHECK_STOP_PLAY "onSrsBandCheckStopPlayBytes"
#define SRS_BW_CHECK_STOPPED_PLAY "onSrsBandCheckStoppedPlayBytes"
// server publish control
#define SRS_BW_CHECK_START_PUBLISH "onSrsBandCheckStartPublishBytes"
#define SRS_BW_CHECK_STARTING_PUBLISH "onSrsBandCheckStartingPublishBytes"
#define SRS_BW_CHECK_STOP_PUBLISH "onSrsBandCheckStopPublishBytes"
#define SRS_BW_CHECK_STOPPED_PUBLISH "onSrsBandCheckStoppedPublishBytes"
// EOF control.
#define SRS_BW_CHECK_FINISHED "onSrsBandCheckFinished"
// for flash, it will sendout a final call,
// used to confirm got the report.
// actually, client send out this packet and close the connection,
// so server may cannot got this packet, ignore is ok.
#define SRS_BW_CHECK_FLASH_FINAL "finalClientPacket"
// client only
#define SRS_BW_CHECK_PLAYING "onSrsBandCheckPlaying"
#define SRS_BW_CHECK_PUBLISHING "onSrsBandCheckPublishing"
/****************************************************************************
*****************************************************************************
****************************************************************************/
@ -284,7 +315,7 @@ SrsProtocol::~SrsProtocol()
srs_freep(skt);
}
std::string SrsProtocol::get_request_name(double transcationId)
string SrsProtocol::get_request_name(double transcationId)
{
if (requests.find(transcationId) == requests.end()) {
return "";
@ -1324,19 +1355,21 @@ int SrsCommonMessage::decode_packet(SrsProtocol* protocol)
srs_info("decode the AMF0/AMF3 data(onMetaData message).");
packet = new SrsOnMetaDataPacket();
return packet->decode(stream);
} else if( command == SRS_BW_CHECK_FINISHED
|| command == SRS_BW_CHECK_PLAYING
|| command == SRS_BW_CHECK_PUBLISHING
|| command == SRS_BW_CHECK_STARTING_PLAY
|| command == SRS_BW_CHECK_STARTING_PUBLISH
|| command == SRS_BW_CHECK_START_PLAY
|| command == SRS_BW_CHECK_START_PUBLISH
|| command == SRS_BW_CHECK_STOPPED_PLAY
|| command == SRS_BW_CHECK_STOP_PLAY
|| command == SRS_BW_CHECK_STOP_PUBLISH)
} else if(command == SRS_BW_CHECK_FINISHED
|| command == SRS_BW_CHECK_PLAYING
|| command == SRS_BW_CHECK_PUBLISHING
|| command == SRS_BW_CHECK_STARTING_PLAY
|| command == SRS_BW_CHECK_STARTING_PUBLISH
|| command == SRS_BW_CHECK_START_PLAY
|| command == SRS_BW_CHECK_START_PUBLISH
|| command == SRS_BW_CHECK_STOPPED_PLAY
|| command == SRS_BW_CHECK_STOP_PLAY
|| command == SRS_BW_CHECK_STOP_PUBLISH
|| command == SRS_BW_CHECK_STOPPED_PUBLISH
|| command == SRS_BW_CHECK_FLASH_FINAL)
{
srs_info("decode the AMF0/AMF3 band width check message.");
packet = new SrsOnStatusCallPacket();
packet = new SrsBandwidthPacket();
return packet->decode(stream);
}
@ -1390,7 +1423,7 @@ int SrsCommonMessage::get_perfer_cid()
return packet->get_perfer_cid();
}
void SrsCommonMessage::set_packet(SrsPacket* pkt, int stream_id)
SrsCommonMessage* SrsCommonMessage::set_packet(SrsPacket* pkt, int stream_id)
{
srs_freep(packet);
@ -1399,6 +1432,8 @@ void SrsCommonMessage::set_packet(SrsPacket* pkt, int stream_id)
header.message_type = packet->get_message_type();
header.payload_length = packet->get_payload_length();
header.stream_id = stream_id;
return this;
}
int SrsCommonMessage::encode_packet()
@ -1803,10 +1838,14 @@ int SrsConnectAppResPacket::get_message_type()
int SrsConnectAppResPacket::get_size()
{
int size = srs_amf0_get_string_size(command_name) + srs_amf0_get_number_size();
if(props->size() > 0)
if (props->size() > 0) {
size += srs_amf0_get_object_size(props);
if(info->size() > 0)
}
if (info->size() > 0) {
size += srs_amf0_get_object_size(info);
}
return size;
}
@ -1827,7 +1866,7 @@ int SrsConnectAppResPacket::encode_packet(SrsStream* stream)
}
srs_verbose("encode transaction_id success.");
if(props->size() > 0){
if (props->size() > 0) {
if ((ret = srs_amf0_write_object(stream, props)) != ERROR_SUCCESS) {
srs_error("encode props failed. ret=%d", ret);
return ret;
@ -1836,7 +1875,7 @@ int SrsConnectAppResPacket::encode_packet(SrsStream* stream)
srs_verbose("encode props success.");
if(info->size() > 0){
if (info->size() > 0) {
if ((ret = srs_amf0_write_object(stream, info)) != ERROR_SUCCESS) {
srs_error("encode info failed. ret=%d", ret);
return ret;
@ -2651,6 +2690,163 @@ int SrsOnStatusCallPacket::encode_packet(SrsStream* stream)
return ret;
}
SrsBandwidthPacket::SrsBandwidthPacket()
{
command_name = RTMP_AMF0_COMMAND_ON_STATUS;
transaction_id = 0;
args = new SrsAmf0Null();
data = new SrsAmf0Object();
}
SrsBandwidthPacket::~SrsBandwidthPacket()
{
srs_freep(args);
srs_freep(data);
}
int SrsBandwidthPacket::get_perfer_cid()
{
return RTMP_CID_OverStream;
}
int SrsBandwidthPacket::get_message_type()
{
return RTMP_MSG_AMF0CommandMessage;
}
int SrsBandwidthPacket::get_size()
{
return srs_amf0_get_string_size(command_name) + srs_amf0_get_number_size()
+ srs_amf0_get_null_size() + srs_amf0_get_object_size(data);
}
int SrsBandwidthPacket::encode_packet(SrsStream* stream)
{
int ret = ERROR_SUCCESS;
if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) {
srs_error("encode command_name failed. ret=%d", ret);
return ret;
}
srs_verbose("encode command_name success.");
if ((ret = srs_amf0_write_number(stream, transaction_id)) != ERROR_SUCCESS) {
srs_error("encode transaction_id failed. ret=%d", ret);
return ret;
}
srs_verbose("encode transaction_id success.");
if ((ret = srs_amf0_write_null(stream)) != ERROR_SUCCESS) {
srs_error("encode args failed. ret=%d", ret);
return ret;
}
srs_verbose("encode args success.");;
if ((ret = srs_amf0_write_object(stream, data)) != ERROR_SUCCESS) {
srs_error("encode data failed. ret=%d", ret);
return ret;
}
srs_verbose("encode data success.");
srs_info("encode onStatus(Call) packet success.");
return ret;
}
int SrsBandwidthPacket::decode(SrsStream *stream)
{
int ret = ERROR_SUCCESS;
if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) {
srs_error("amf0 decode play command_name failed. ret=%d", ret);
return ret;
}
if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) {
srs_error("amf0 decode play transaction_id failed. ret=%d", ret);
return ret;
}
if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) {
srs_error("amf0 decode play command_object failed. ret=%d", ret);
return ret;
}
// @remark, for bandwidth test, ignore the data field.
srs_info("decode SrsBandwidthPacket success.");
return ret;
}
bool SrsBandwidthPacket::is_starting_play()
{
return command_name == SRS_BW_CHECK_STARTING_PLAY;
}
bool SrsBandwidthPacket::is_stopped_play()
{
return command_name == SRS_BW_CHECK_STOPPED_PLAY;
}
bool SrsBandwidthPacket::is_starting_publish()
{
return command_name == SRS_BW_CHECK_STARTING_PUBLISH;
}
bool SrsBandwidthPacket::is_stopped_publish()
{
return command_name == SRS_BW_CHECK_STOPPED_PUBLISH;
}
bool SrsBandwidthPacket::is_flash_final()
{
return command_name == SRS_BW_CHECK_FLASH_FINAL;
}
SrsBandwidthPacket* SrsBandwidthPacket::create_finish()
{
SrsBandwidthPacket* pkt = new SrsBandwidthPacket();
return pkt->set_command(SRS_BW_CHECK_FINISHED);
}
SrsBandwidthPacket* SrsBandwidthPacket::create_start_play()
{
SrsBandwidthPacket* pkt = new SrsBandwidthPacket();
return pkt->set_command(SRS_BW_CHECK_START_PLAY);
}
SrsBandwidthPacket* SrsBandwidthPacket::create_playing()
{
SrsBandwidthPacket* pkt = new SrsBandwidthPacket();
return pkt->set_command(SRS_BW_CHECK_STARTING_PLAY);
}
SrsBandwidthPacket* SrsBandwidthPacket::create_stop_play()
{
SrsBandwidthPacket* pkt = new SrsBandwidthPacket();
return pkt->set_command(SRS_BW_CHECK_STOP_PLAY);
}
SrsBandwidthPacket* SrsBandwidthPacket::create_start_publish()
{
SrsBandwidthPacket* pkt = new SrsBandwidthPacket();
return pkt->set_command(SRS_BW_CHECK_START_PUBLISH);
}
SrsBandwidthPacket* SrsBandwidthPacket::create_stop_publish()
{
SrsBandwidthPacket* pkt = new SrsBandwidthPacket();
return pkt->set_command(SRS_BW_CHECK_STOP_PUBLISH);
}
SrsBandwidthPacket* SrsBandwidthPacket::set_command(string command)
{
command_name = command;
return this;
}
SrsOnStatusDataPacket::SrsOnStatusDataPacket()
{
command_name = RTMP_AMF0_COMMAND_ON_STATUS;

@ -331,6 +331,7 @@ class SrsCommonMessage : public ISrsMessage
{
private:
typedef ISrsMessage super;
disable_default_copy(SrsCommonMessage);
// decoded message payload.
private:
SrsStream* stream;
@ -366,9 +367,10 @@ public:
* set the encoded packet to encode_packet() to payload.
* @stream_id, the id of stream which is created by createStream.
* @remark, user never free the pkt, the message will auto free it.
* @return message itself.
*/
// TODO: refine the send methods.
virtual void set_packet(SrsPacket* pkt, int stream_id);
virtual SrsCommonMessage* set_packet(SrsPacket* pkt, int stream_id);
/**
* encode the packet to message payload bytes.
* @remark there exists empty packet, so maybe the payload is NULL.
@ -856,6 +858,55 @@ protected:
virtual int encode_packet(SrsStream* stream);
};
/**
* the special packet for the bandwidth test.
* actually, it's a SrsOnStatusCallPacket, but
* 1. encode with data field, to send data to client.
* 2. decode ignore the data field, donot care.
*/
class SrsBandwidthPacket : public SrsPacket
{
private:
typedef SrsPacket super;
disable_default_copy(SrsBandwidthPacket);
protected:
virtual const char* get_class_name()
{
return CLASS_NAME_STRING(SrsBandwidthPacket);
}
public:
std::string command_name;
double transaction_id;
SrsAmf0Null* args;
SrsAmf0Object* data;
public:
SrsBandwidthPacket();
virtual ~SrsBandwidthPacket();
public:
virtual int get_perfer_cid();
public:
virtual int get_message_type();
protected:
virtual int get_size();
virtual int encode_packet(SrsStream* stream);
public:
virtual int decode(SrsStream* stream);
public:
virtual bool is_starting_play();
virtual bool is_stopped_play();
virtual bool is_starting_publish();
virtual bool is_stopped_publish();
virtual bool is_flash_final();
static SrsBandwidthPacket* create_finish();
static SrsBandwidthPacket* create_start_play();
static SrsBandwidthPacket* create_playing();
static SrsBandwidthPacket* create_stop_play();
static SrsBandwidthPacket* create_start_publish();
static SrsBandwidthPacket* create_stop_publish();
private:
virtual SrsBandwidthPacket* set_command(std::string command);
};
/**
* onStatus data, AMF0 Data
* @remark, user must set the stream_id by SrsMessage.set_packet().

@ -594,7 +594,7 @@ int SrsRtmp::set_peer_bandwidth(int bandwidth, int type)
return ret;
}
int SrsRtmp::response_connect_app(SrsRequest *req, const char *ip)
int SrsRtmp::response_connect_app(SrsRequest *req, const char* server_ip)
{
int ret = ERROR_SUCCESS;
@ -621,13 +621,12 @@ int SrsRtmp::response_connect_app(SrsRequest *req, const char *ip)
data->set("srs_site", new SrsAmf0String(RTMP_SIG_SRS_WEB));
data->set("srs_email", new SrsAmf0String(RTMP_SIG_SRS_EMAIL));
data->set("srs_copyright", new SrsAmf0String(RTMP_SIG_SRS_COPYRIGHT));
if (ip) {
data->set("srs_server_ip", new SrsAmf0String(ip));
data->set("srs_contributor", new SrsAmf0String(RTMP_SIG_SRS_CONTRIBUTOR));
if (server_ip) {
data->set("srs_server_ip", new SrsAmf0String(server_ip));
}
data->set("srs_contributor", new SrsAmf0String(RTMP_SIG_SRS_CONTRIBUTOR));
msg->set_packet(pkt, 0);
if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
@ -639,27 +638,25 @@ int SrsRtmp::response_connect_app(SrsRequest *req, const char *ip)
return ret;
}
int SrsRtmp::response_connect_reject(SrsRequest *req, const std::string &description)
void SrsRtmp::response_connect_reject(SrsRequest *req, const char* desc)
{
int ret = ERROR_SUCCESS;
SrsCommonMessage* msg = new SrsCommonMessage();
SrsConnectAppResPacket* pkt = new SrsConnectAppResPacket();
pkt->command_name = "_error";
pkt->props->set(StatusLevel, new SrsAmf0String(StatusLevelError));
pkt->props->set(StatusCode, new SrsAmf0String(StatusCodeConnectRejected));
pkt->props->set(StatusDescription, new SrsAmf0String(description.c_str()));
pkt->props->set(StatusDescription, new SrsAmf0String(desc));
//pkt->props->set("objectEncoding", new SrsAmf0Number(req->objectEncoding));
msg->set_packet(pkt, 0);
SrsCommonMessage* msg = (new SrsCommonMessage())->set_packet(pkt, 0);
if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
srs_error("send connect app response rejected message failed. ret=%d", ret);
return ret;
return;
}
srs_info("send connect app response rejected message success.");
return ret;
return;
}
int SrsRtmp::on_bw_done()

@ -169,8 +169,16 @@ public:
* using the Limit type field.
*/
virtual int set_peer_bandwidth(int bandwidth, int type);
<<<<<<< HEAD
virtual int response_connect_app(SrsRequest* req, const char *ip = 0);
virtual int response_connect_reject(SrsRequest* req, const std::string& description);
=======
/**
* @param server_ip the ip of server.
*/
virtual int response_connect_app(SrsRequest* req, const char* server_ip = NULL);
virtual void response_connect_reject(SrsRequest* req, const char* desc);
>>>>>>> upstream/master
virtual int on_bw_done();
/**
* recv some message to identify the client.

@ -10,6 +10,8 @@ file
..\core\srs_core_amf0.cpp,
..\core\srs_core_autofree.hpp,
..\core\srs_core_autofree.cpp,
..\core\srs_core_bandwidth.hpp,
..\core\srs_core_bandwidth.cpp,
..\core\srs_core_buffer.hpp,
..\core\srs_core_buffer.cpp,
..\core\srs_core_client.hpp,

Loading…
Cancel
Save