diff --git a/README.md b/README.md
index 4c13ccb59..1d0f83756 100755
--- a/README.md
+++ b/README.md
@@ -468,6 +468,13 @@ Supported operating systems and hardware:
[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_SrsLibrtmp#publish-audio-raw-stream)
) by srs-librtmp.
1. Support 0.1s+ latency, read [#257](https://github.com/winlinvip/simple-rtmp-server/issues/257).
+1. Support allow/deny publish/play for all or specified ip(
+[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_Security),
+[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_Security)
+).
+1. Support custom dvr path and http callback, read
+[#179](https://github.com/winlinvip/simple-rtmp-server/issues/179) and
+[274](https://github.com/winlinvip/simple-rtmp-server/issues/274).
1. [no-plan] Support <500ms latency, FRSC(Fast RTMP-compatible Stream Channel tech).
1. [no-plan] Support RTMP 302 redirect [#92](https://github.com/winlinvip/simple-rtmp-server/issues/92).
1. [no-plan] Support multiple processes, for both origin and edge
@@ -501,6 +508,10 @@ Supported operating systems and hardware:
* 2013-10-17, Created.
## History
+* v2.0, 2015-01-03, fix [#274](https://github.com/winlinvip/simple-rtmp-server/issues/274), http-callback support on_dvr when reap a dvr file. 2.0.89
+* v2.0, 2015-01-03, hotfix to remove the pageUrl for http callback. 2.0.88
+* v2.0, 2015-01-03, fix [#179](https://github.com/winlinvip/simple-rtmp-server/issues/179), dvr support custom filepath by variables. 2.0.87
+* v2.0, 2015-01-02, fix [#211](https://github.com/winlinvip/simple-rtmp-server/issues/211), support security allow/deny publish/play all/ip. 2.0.86
* v2.0, 2015-01-02, hotfix [#207](https://github.com/winlinvip/simple-rtmp-server/issues/207), trim the last 0 of log. 2.0.85
* v2.0, 2014-01-02, fix [#158](https://github.com/winlinvip/simple-rtmp-server/issues/158), http-callback check http status code ok(200). 2.0.84
* v2.0, 2015-01-02, hotfix [#216](https://github.com/winlinvip/simple-rtmp-server/issues/216), http-callback post in application/json content-type. 2.0.83
@@ -550,6 +561,7 @@ Supported operating systems and hardware:
* v2.0, 2014-10-18, remove supports for OSX(darwin). 2.0.1.
* v2.0, 2014-10-16, revert github srs README to English. 2.0.0.
+* v1.0, 2015-01-03, hotfix to remove the pageUrl for http callback. 1.0.19
* v1.0, 2015-01-02, hotfix [#207](https://github.com/winlinvip/simple-rtmp-server/issues/207), trim the last 0 of log. 1.0.18
* v1.0, 2015-01-02, hotfix [#216](https://github.com/winlinvip/simple-rtmp-server/issues/216), http-callback post in application/json content-type. 1.0.17
* v1.0, 2015-01-01, hotfix [#270](https://github.com/winlinvip/simple-rtmp-server/issues/270), memory leak for http client post. 1.0.16
diff --git a/trunk/conf/dvr.path.conf b/trunk/conf/dvr.path.conf
new file mode 100644
index 000000000..cb07d6612
--- /dev/null
+++ b/trunk/conf/dvr.path.conf
@@ -0,0 +1,16 @@
+# the config for srs to dvr in custom path.
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_DVR#custom-path
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_DVR#custom-path
+# @see full.conf for detail config.
+
+listen 1935;
+max_connections 1000;
+vhost __defaultVhost__ {
+ dvr {
+ enabled on;
+ dvr_path ./objs/nginx/html/[app]/[stream]/[2006]/[01]/[02]/[15].[04].[05].[999].flv;
+ dvr_plan segment;
+ dvr_duration 30;
+ dvr_wait_keyframe on;
+ }
+}
diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf
old mode 100755
new mode 100644
index 6b6b273d7..3f7e860c9
--- a/trunk/conf/full.conf
+++ b/trunk/conf/full.conf
@@ -142,6 +142,35 @@ http_stream {
vhost __defaultVhost__ {
}
+# the security to allow or deny clients.
+vhost security.srs.com {
+ # security for host to allow or deny clients.
+ # @see https://github.com/winlinvip/simple-rtmp-server/issues/211
+ security {
+ # whether enable the security for vhost.
+ # default: off
+ enabled on;
+ # the security list, each item format as:
+ # allow|deny publish|play all|
+ # for example:
+ # allow publish all;
+ # deny publish all;
+ # allow publish 127.0.0.1;
+ # deny publish 127.0.0.1;
+ # allow play all;
+ # deny play all;
+ # allow play 127.0.0.1;
+ # deny play 127.0.0.1;
+ # SRS apply the following simple strategies one by one:
+ # 1. allow all if security disabled.
+ # 2. default to deny all when security enabled.
+ # 3. allow if matches allow strategy.
+ # 4. deny if matches deny strategy.
+ allow play all;
+ allow publish all;
+ }
+}
+
# the MR(merged-read) setting for publisher.
# the MW(merged-write) settings for player.
vhost mrw.srs.com {
@@ -207,15 +236,38 @@ vhost dvr.srs.com {
# default: off
enabled on;
# the dvr output path.
- # the app dir is auto created under the dvr_path.
- # for example, for rtmp stream:
- # rtmp://127.0.0.1/live/livestream
- # http://127.0.0.1/live/livestream.m3u8
- # where dvr_path is /dvr, srs will create the following files:
- # /dvr/live the app dir for all streams.
- # /dvr/live/livestream.{time}.flv the dvr flv file.
- # @remark, the time use system timestamp in ms, user can use http callback to rename it.
- # in a word, the dvr_path is for vhost.
+ # we supports some variables to generate the filename.
+ # [vhost], the vhost of stream.
+ # [app], the app of stream.
+ # [stream], the stream name of stream.
+ # [2006], replace this const to current year.
+ # [01], replace this const to current month.
+ # [02], replace this const to current date.
+ # [15], replace this const to current hour.
+ # [04], repleace this const to current minute.
+ # [05], repleace this const to current second.
+ # [999], repleace this const to current millisecond.
+ # [timestamp],replace this const to current UNIX timestamp in ms.
+ # @remark we use golang time format "2006-01-02 15:04:05.999"
+ # for example, for url rtmp://ossrs.net/live/livestream and time 2015-01-03 10:57:30.776
+ # 1. No variables, the rule of SRS1.0(auto add [stream].[timestamp].flv as filename):
+ # dvr_path ./objs/nginx/html;
+ # =>
+ # dvr_path ./objs/nginx/html/live/livestream.1420254068776.flv;
+ # 2. Use stream and date as dir name, time as filename:
+ # dvr_path /data/[vhost]/[app]/[stream]/[2006]/[01]/[02]/[15].[04].[05].[999].flv;
+ # =>
+ # dvr_path /data/ossrs.net/live/livestream/2015/01/03/10.57.30.776.flv;
+ # 3. Use stream and year/month as dir name, date and time as filename:
+ # dvr_path /data/[vhost]/[app]/[stream]/[2006]/[01]/[02]-[15].[04].[05].[999].flv;
+ # =>
+ # dvr_path /data/ossrs.net/live/livestream/2015/01/03-10.57.30.776.flv;
+ # 4. Use vhost/app and year/month as dir name, stream/date/time as filename:
+ # dvr_path /data/[vhost]/[app]/[2006]/[01]/[stream]-[02]-[15].[04].[05].[999].flv;
+ # =>
+ # dvr_path /data/ossrs.net/live/2015/01/livestream-03-10.57.30.776.flv;
+ # @see https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_DVR#custom-path
+ # @see https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_DVR#custom-path
# default: ./objs/nginx/html
dvr_path ./objs/nginx/html;
# the dvr plan. canbe:
@@ -244,6 +296,11 @@ vhost dvr.srs.com {
# 3. off, disable the time jitter algorithm, like atc.
# default: full
time_jitter full;
+
+ # on_dvr
+ # for the dvr http callback, @see http_hooks.on_dvr of vhost hooks.callback.srs.com
+ # @read https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_DVR#http-callback
+ # @read https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_DVR#http-callback
}
}
@@ -286,7 +343,7 @@ vhost ingest.srs.com {
}
}
-# vhost for http
+# vhost for http server config in each vhost.
vhost http.srs.com {
# http vhost specified config
http {
@@ -438,6 +495,20 @@ vhost hooks.callback.srs.com {
# support multiple api hooks, format:
# on_stop http://xxx/api0 http://xxx/api1 http://xxx/apiN
on_stop http://127.0.0.1:8085/api/v1/sessions http://localhost:8085/api/v1/sessions;
+ # when srs reap a dvr file, call the hook,
+ # the request in the POST data string is a object encode by json:
+ # {
+ # "action": "on_dvr",
+ # "client_id": 1985,
+ # "ip": "192.168.1.10", "vhost": "video.test.com", "app": "live",
+ # "stream": "livestream",
+ # "cwd": "/usr/local/srs",
+ # "file": "./objs/nginx/html/live/livestream.1420254068776.flv"
+ # }
+ # if valid, the hook must return HTTP code 200(Stauts OK) and response
+ # an int value specifies the error code(0 corresponding to success):
+ # 0
+ on_dvr http://127.0.0.1:8085/api/v1/dvrs http://localhost:8085/api/v1/dvrs;
}
}
@@ -501,11 +572,105 @@ vhost same.vhost.forward.srs.com {
# this used to split/forward the current stream for cluster active-standby,
# active-active for cdn to build high available fault tolerance system.
# format: {ip}:{port} {ip_N}:{port_N}
- # or specify the vhost by params, @see: change.vhost.forward.srs.com
- # if vhost not specified, use the request vhost instead.
forward 127.0.0.1:1936 127.0.0.1:1937;
}
+# the main comments for transcode
+vhost example.transcode.srs.com {
+ # the streaming transcode configs.
+ transcode {
+ # whether the transcode enabled.
+ # if off, donot transcode.
+ # default: off.
+ enabled on;
+ # the ffmpeg
+ ffmpeg ./objs/ffmpeg/bin/ffmpeg;
+ # the transcode engine for matched stream.
+ # all matched stream will transcoded to the following stream.
+ # the transcode set name(ie. hd) is optional and not used.
+ engine example {
+ # whether the engine is enabled
+ # default: off.
+ enabled on;
+ # input format, can be:
+ # off, do not specifies the format, ffmpeg will guess it.
+ # flv, for flv or RTMP stream.
+ # other format, for example, mp4/aac whatever.
+ # default: flv
+ iformat flv;
+ # ffmpeg filters, follows the main input.
+ vfilter {
+ # the logo input file.
+ i ./doc/ffmpeg-logo.png;
+ # the ffmpeg complex filter.
+ # for filters, @see: http://ffmpeg.org/ffmpeg-filters.html
+ filter_complex 'overlay=10:10';
+ }
+ # video encoder name. can be:
+ # libx264: use h.264(libx264) video encoder.
+ # copy: donot encoder the video stream, copy it.
+ # vn: disable video output.
+ vcodec libx264;
+ # video bitrate, in kbps
+ vbitrate 1500;
+ # video framerate.
+ vfps 25;
+ # video width, must be even numbers.
+ vwidth 768;
+ # video height, must be even numbers.
+ vheight 320;
+ # the max threads for ffmpeg to used.
+ vthreads 12;
+ # x264 profile, @see x264 -help, can be:
+ # high,main,baseline
+ vprofile main;
+ # x264 preset, @see x264 -help, can be:
+ # ultrafast,superfast,veryfast,faster,fast
+ # medium,slow,slower,veryslow,placebo
+ vpreset medium;
+ # other x264 or ffmpeg video params
+ vparams {
+ # ffmpeg options, @see: http://ffmpeg.org/ffmpeg.html
+ t 100;
+ # 264 params, @see: http://ffmpeg.org/ffmpeg-codecs.html#libx264
+ coder 1;
+ b_strategy 2;
+ bf 3;
+ refs 10;
+ }
+ # audio encoder name. can be:
+ # libaacplus: use aac(libaacplus) audio encoder.
+ # copy: donot encoder the audio stream, copy it.
+ # an: disable audio output.
+ acodec libaacplus;
+ # audio bitrate, in kbps. [16, 72] for libaacplus.
+ abitrate 70;
+ # audio sample rate. for flv/rtmp, it must be:
+ # 44100,22050,11025,5512
+ asample_rate 44100;
+ # audio channel, 1 for mono, 2 for stereo.
+ achannels 2;
+ # other ffmpeg audio params
+ aparams {
+ # audio params, @see: http://ffmpeg.org/ffmpeg-codecs.html#Audio-Encoders
+ profile:a aac_low;
+ }
+ # output format, can be:
+ # off, do not specifies the format, ffmpeg will guess it.
+ # flv, for flv or RTMP stream.
+ # other format, for example, mp4/aac whatever.
+ # default: flv
+ oformat flv;
+ # output stream. variables:
+ # [vhost] the input stream vhost.
+ # [port] the intput stream port.
+ # [app] the input stream app.
+ # [stream] the input stream name.
+ # [engine] the tanscode engine name.
+ output rtmp://127.0.0.1:[port]/[app]?vhost=[vhost]/[stream]_[engine];
+ }
+ }
+}
# the mirror filter of ffmpeg, @see: http://ffmpeg.org/ffmpeg-filters.html#Filtering-Introduction
vhost mirror.transcode.srs.com {
transcode {
@@ -536,10 +701,8 @@ vhost mirror.transcode.srs.com {
}
}
}
-#
# the drawtext filter of ffmpeg, @see: http://ffmpeg.org/ffmpeg-filters.html#drawtext-1
# remark: we remove the libfreetype which always cause build failed, you must add it manual if needed.
-#
#######################################################################################################
# the crop filter of ffmpeg, @see: http://ffmpeg.org/ffmpeg-filters.html#crop
vhost crop.transcode.srs.com {
@@ -656,97 +819,41 @@ vhost copy.transcode.srs.com {
}
}
# transcode all app and stream of vhost
+# the comments, read example.transcode.srs.com
vhost all.transcode.srs.com {
- # the streaming transcode configs.
transcode {
- # whether the transcode enabled.
- # if off, donot transcode.
- # default: off.
enabled on;
- # the ffmpeg
ffmpeg ./objs/ffmpeg/bin/ffmpeg;
- # the transcode engine for matched stream.
- # all matched stream will transcoded to the following stream.
- # the transcode set name(ie. hd) is optional and not used.
engine ffsuper {
- # whether the engine is enabled
- # default: off.
enabled on;
- # input format, can be:
- # off, do not specifies the format, ffmpeg will guess it.
- # flv, for flv or RTMP stream.
- # other format, for example, mp4/aac whatever.
- # default: flv
iformat flv;
- # ffmpeg filters, follows the main input.
vfilter {
- # the logo input file.
i ./doc/ffmpeg-logo.png;
- # the ffmpeg complex filter.
- # for filters, @see: http://ffmpeg.org/ffmpeg-filters.html
filter_complex 'overlay=10:10';
}
- # video encoder name. can be:
- # libx264: use h.264(libx264) video encoder.
- # copy: donot encoder the video stream, copy it.
- # vn: disable video output.
vcodec libx264;
- # video bitrate, in kbps
vbitrate 1500;
- # video framerate.
vfps 25;
- # video width, must be even numbers.
vwidth 768;
- # video height, must be even numbers.
vheight 320;
- # the max threads for ffmpeg to used.
vthreads 12;
- # x264 profile, @see x264 -help, can be:
- # high,main,baseline
vprofile main;
- # x264 preset, @see x264 -help, can be:
- # ultrafast,superfast,veryfast,faster,fast
- # medium,slow,slower,veryslow,placebo
vpreset medium;
- # other x264 or ffmpeg video params
vparams {
- # ffmpeg options, @see: http://ffmpeg.org/ffmpeg.html
t 100;
- # 264 params, @see: http://ffmpeg.org/ffmpeg-codecs.html#libx264
coder 1;
b_strategy 2;
bf 3;
refs 10;
}
- # audio encoder name. can be:
- # libaacplus: use aac(libaacplus) audio encoder.
- # copy: donot encoder the audio stream, copy it.
- # an: disable audio output.
acodec libaacplus;
- # audio bitrate, in kbps. [16, 72] for libaacplus.
abitrate 70;
- # audio sample rate. for flv/rtmp, it must be:
- # 44100,22050,11025,5512
asample_rate 44100;
- # audio channel, 1 for mono, 2 for stereo.
achannels 2;
- # other ffmpeg audio params
aparams {
- # audio params, @see: http://ffmpeg.org/ffmpeg-codecs.html#Audio-Encoders
profile:a aac_low;
}
- # output format, can be:
- # off, do not specifies the format, ffmpeg will guess it.
- # flv, for flv or RTMP stream.
- # other format, for example, mp4/aac whatever.
- # default: flv
oformat flv;
- # output stream. variables:
- # [vhost] the input stream vhost.
- # [port] the intput stream port.
- # [app] the input stream app.
- # [stream] the input stream name.
- # [engine] the tanscode engine name.
output rtmp://127.0.0.1:[port]/[app]?vhost=[vhost]/[stream]_[engine];
}
engine ffhd {
diff --git a/trunk/conf/realtime.conf b/trunk/conf/realtime.conf
old mode 100755
new mode 100644
diff --git a/trunk/conf/security.deny.publish.conf b/trunk/conf/security.deny.publish.conf
new file mode 100644
index 000000000..a2c6e59da
--- /dev/null
+++ b/trunk/conf/security.deny.publish.conf
@@ -0,0 +1,13 @@
+# security config for srs, allow play and deny publish.
+# @see https://github.com/winlinvip/simple-rtmp-server/issues/211#issuecomment-68507035
+# @see full.conf for detail config.
+
+listen 1935;
+max_connections 1000;
+vhost __defaultVhost__ {
+ security {
+ enabled on;
+ deny publish all;
+ allow play all;
+ }
+}
diff --git a/trunk/configure b/trunk/configure
index c543d150c..47265e6fb 100755
--- a/trunk/configure
+++ b/trunk/configure
@@ -389,7 +389,7 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
"srs_app_pithy_print" "srs_app_reload" "srs_app_http_api" "srs_app_http_conn" "srs_app_http_hooks"
"srs_app_json" "srs_app_ingest" "srs_app_ffmpeg" "srs_app_utility" "srs_app_dvr" "srs_app_edge"
"srs_app_kbps" "srs_app_heartbeat" "srs_app_empty" "srs_app_http_client" "srs_app_avc_aac"
- "srs_app_recv_thread" "srs_app_statistic")
+ "srs_app_recv_thread" "srs_app_security" "srs_app_statistic")
APP_INCS="src/app"; MODULE_DIR=${APP_INCS} . auto/modules.sh
APP_OBJS="${MODULE_OBJS[@]}"
fi
diff --git a/trunk/research/api-server/server.py b/trunk/research/api-server/server.py
index 8f0f51e61..8c1f4823b 100755
--- a/trunk/research/api-server/server.py
+++ b/trunk/research/api-server/server.py
@@ -240,6 +240,74 @@ class RESTStreams(object):
return code
+'''
+handle the dvrs requests: dvr stream.
+'''
+class RESTDvrs(object):
+ exposed = True
+
+ def GET(self):
+ enable_crossdomain()
+
+ dvrs = {}
+ return json.dumps(dvrs)
+
+ '''
+ for SRS hook: on_dvr
+ on_dvr:
+ when srs reap a dvr file, call the hook,
+ the request in the POST data string is a object encode by json:
+ {
+ "action": "on_dvr",
+ "client_id": 1985,
+ "ip": "192.168.1.10", "vhost": "video.test.com", "app": "live",
+ "stream": "livestream",
+ "cwd": "/usr/local/srs",
+ "file": "./objs/nginx/html/live/livestream.1420254068776.flv"
+ }
+ if valid, the hook must return HTTP code 200(Stauts OK) and response
+ an int value specifies the error code(0 corresponding to success):
+ 0
+ '''
+ def POST(self):
+ enable_crossdomain()
+
+ # return the error code in str
+ code = Error.success
+
+ req = cherrypy.request.body.read()
+ trace("post to dvrs, req=%s"%(req))
+ try:
+ json_req = json.loads(req)
+ except Exception, ex:
+ code = Error.system_parse_json
+ trace("parse the request to json failed, req=%s, ex=%s, code=%s"%(req, ex, code))
+ return str(code)
+
+ action = json_req["action"]
+ if action == "on_dvr":
+ code = self.__on_dvr(json_req)
+ else:
+ trace("invalid request action: %s"%(json_req["action"]))
+ code = Error.request_invalid_action
+
+ return str(code)
+
+ def OPTIONS(self, *args, **kwargs):
+ enable_crossdomain()
+
+ def __on_dvr(self, req):
+ code = Error.success
+
+ trace("srs %s: client id=%s, ip=%s, vhost=%s, app=%s, stream=%s, cwd=%s, file=%s"%(
+ req["action"], req["client_id"], req["ip"], req["vhost"], req["app"], req["stream"],
+ req["cwd"], req["file"]
+ ))
+
+ # TODO: process the on_dvr event
+
+ return code
+
'''
handle the sessions requests: client play/stop stream
'''
@@ -1039,6 +1107,7 @@ class V1(object):
self.clients = RESTClients()
self.streams = RESTStreams()
self.sessions = RESTSessions()
+ self.dvrs = RESTDvrs()
self.chats = RESTChats()
self.servers = RESTServers()
self.nodes = RESTNodes()
@@ -1048,6 +1117,7 @@ class V1(object):
"clients": "for srs http callback, to handle the clients requests: connect/disconnect vhost/app.",
"streams": "for srs http callback, to handle the streams requests: publish/unpublish stream.",
"sessions": "for srs http callback, to handle the sessions requests: client play/stop stream",
+ "dvrs": "for srs http callback, to handle the dvr requests: dvr stream.",
"chats": "for srs demo meeting, the chat streams, public chat room.",
"nodes": {
"summary": "for srs cdn node",
diff --git a/trunk/scripts/git2unix.sh b/trunk/scripts/git2unix.sh
index 4f78ada6f..9aeddfb00 100755
--- a/trunk/scripts/git2unix.sh
+++ b/trunk/scripts/git2unix.sh
@@ -15,7 +15,7 @@ ret=$?; if [[ 0 -ne $ret ]]; then
exit $ret
fi
-files=`git status|egrep "(modified|new file)"|awk -F ':' '{print $2}'|awk '{print $1}'|egrep "(.hpp$|.cpp$|.cc$|.h$|.c$|.txt$|.sh$)"`;
+files=`git status|egrep "(modified|new file)"|awk -F ':' '{print $2}'|awk '{print $1}'|egrep "(.hpp$|.cpp$|.cc$|.h$|.c$|.txt$|.sh|.conf$)"`;
for file in $files; do
dos2unix $file;
echo $file|grep ".sh$" >/dev/null 2>&1; EOF_SH=$?
diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp
index 72031ad70..a6a695fb8 100644
--- a/trunk/src/app/srs_app_config.cpp
+++ b/trunk/src/app/srs_app_config.cpp
@@ -434,7 +434,8 @@ int SrsConfig::reload_conf(SrsConfig* conf)
// always support reload without additional code:
// chunk_size, ff_log_dir, max_connections,
// bandcheck, http_hooks, heartbeat,
- // token_traverse, debug_srs_upnode
+ // token_traverse, debug_srs_upnode,
+ // security
// merge config: listen
if (!srs_directive_equals(root->get("listen"), old_root->get("listen"))) {
@@ -1363,6 +1364,7 @@ int SrsConfig::check_config()
&& n != "atc" && n != "atc_auto"
&& n != "debug_srs_upnode"
&& n != "mr" && n != "mw_latency" && n != "min_latency"
+ && n != "security"
) {
ret = ERROR_SYSTEM_CONFIG_INVALID;
srs_error("unsupported vhost directive %s, ret=%d", n.c_str(), ret);
@@ -1424,6 +1426,7 @@ int SrsConfig::check_config()
string m = conf->at(j)->name.c_str();
if (m != "enabled" && m != "on_connect" && m != "on_close" && m != "on_publish"
&& m != "on_unpublish" && m != "on_play" && m != "on_stop"
+ && m != "on_dvr"
) {
ret = ERROR_SYSTEM_CONFIG_INVALID;
srs_error("unsupported vhost http_hooks directive %s, ret=%d", m.c_str(), ret);
@@ -1440,6 +1443,16 @@ int SrsConfig::check_config()
return ret;
}
}*/
+ } else if (n == "security") {
+ for (int j = 0; j < (int)conf->directives.size(); j++) {
+ SrsConfDirective* security = conf->at(j);
+ string m = security->name.c_str();
+ if (m != "enabled" && m != "deny" && m != "allow") {
+ ret = ERROR_SYSTEM_CONFIG_INVALID;
+ srs_error("unsupported vhost security directive %s, ret=%d", m.c_str(), ret);
+ return ret;
+ }
+ }
} else if (n == "transcode") {
for (int j = 0; j < (int)conf->directives.size(); j++) {
SrsConfDirective* trans = conf->at(j);
@@ -2323,6 +2336,17 @@ SrsConfDirective* SrsConfig::get_vhost_on_stop(string vhost)
return conf->get("on_stop");
}
+SrsConfDirective* SrsConfig::get_vhost_on_dvr(string vhost)
+{
+ SrsConfDirective* conf = get_vhost_http_hooks(vhost);
+
+ if (!conf) {
+ return NULL;
+ }
+
+ return conf->get("on_dvr");
+}
+
bool SrsConfig::get_bw_check_enabled(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
@@ -2456,6 +2480,43 @@ bool SrsConfig::get_vhost_edge_token_traverse(string vhost)
return true;
}
+bool SrsConfig::get_security_enabled(string vhost)
+{
+ SrsConfDirective* conf = get_vhost(vhost);
+
+ if (!conf) {
+ return SRS_CONF_DEFAULT_SECURITY_ENABLED;
+ }
+
+ SrsConfDirective* security = conf->get("security");
+ if (!security) {
+ return SRS_CONF_DEFAULT_SECURITY_ENABLED;
+ }
+
+ conf = security->get("enabled");
+ if (!conf || conf->arg0() != "on") {
+ return SRS_CONF_DEFAULT_SECURITY_ENABLED;
+ }
+
+ return true;
+}
+
+SrsConfDirective* SrsConfig::get_security_rules(string vhost)
+{
+ SrsConfDirective* conf = get_vhost(vhost);
+
+ if (!conf) {
+ return NULL;
+ }
+
+ SrsConfDirective* security = conf->get("security");
+ if (!security) {
+ return NULL;
+ }
+
+ return security;
+}
+
SrsConfDirective* SrsConfig::get_transcode(string vhost, string scope)
{
SrsConfDirective* conf = get_vhost(vhost);
diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp
index aaf7f973f..80858be5a 100644
--- a/trunk/src/app/srs_app_config.hpp
+++ b/trunk/src/app/srs_app_config.hpp
@@ -76,6 +76,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define SRS_CONF_DEFAULT_HTTP_HEAETBEAT_URL "http://"SRS_CONSTS_LOCALHOST":8085/api/v1/servers"
#define SRS_CONF_DEFAULT_HTTP_HEAETBEAT_SUMMARIES false
+#define SRS_CONF_DEFAULT_SECURITY_ENABLED false
+
#define SRS_CONF_DEFAULT_STATS_NETWORK_DEVICE_INDEX 0
#define SRS_CONF_DEFAULT_STAGE_PLAY_USER_INTERVAL_MS 10000
@@ -608,6 +610,11 @@ public:
* @return the on_stop callback directive, the args is the url to callback.
*/
virtual SrsConfDirective* get_vhost_on_stop(std::string vhost);
+ /**
+ * get the on_dvr callbacks of vhost.
+ * @return the on_dvr callback directive, the args is the url to callback.
+ */
+ virtual SrsConfDirective* get_vhost_on_dvr(std::string vhost);
// bwct(bandwidth check tool) section
public:
/**
@@ -659,6 +666,16 @@ public:
* all clients connected to edge must be tranverse to origin to verify.
*/
virtual bool get_vhost_edge_token_traverse(std::string vhost);
+// vhost security section
+public:
+ /**
+ * whether the secrity of vhost enabled.
+ */
+ virtual bool get_security_enabled(std::string vhost);
+ /**
+ * get the security rules.
+ */
+ virtual SrsConfDirective* get_security_rules(std::string vhost);
// vhost transcode section
public:
/**
@@ -776,7 +793,7 @@ public:
* @remark, we will use some variable, for instance, [vhost] to substitude with vhost.
*/
virtual std::string get_engine_output(SrsConfDirective* engine);
-// ingest section
+// vhost ingest section
public:
/**
* get the ingest directives of vhost.
diff --git a/trunk/src/app/srs_app_dvr.cpp b/trunk/src/app/srs_app_dvr.cpp
index 06c70eaf9..631513698 100644
--- a/trunk/src/app/srs_app_dvr.cpp
+++ b/trunk/src/app/srs_app_dvr.cpp
@@ -27,6 +27,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include
#include
+#include
using namespace std;
#include
@@ -136,14 +137,97 @@ int SrsDvrPlan::open_new_segment()
SrsRequest* req = _req;
- // new flv file
- std::stringstream path;
+ // the path in config, for example,
+ // /data/[vhost]/[app]/[stream]/[2006]/[01]/[02]/[15].[04].[05].[999].flv
+ std::string path_config = _srs_config->get_dvr_path(req->vhost);
- path << _srs_config->get_dvr_path(req->vhost)
- << "/" << req->app << "/"
- << req->stream << "." << srs_get_system_time_ms() << ".flv";
+ // add [stream].[timestamp].flv as filename for dir
+ if (path_config.find(".flv") != path_config.length() - 4) {
+ path_config += "/[stream].[timestamp].flv";
+ }
- if ((ret = flv_open(req->get_stream_url(), path.str())) != ERROR_SUCCESS) {
+ // the flv file path
+ std::string path = path_config;
+
+ // variable [vhost]
+ path = srs_string_replace(path, "[vhost]", req->vhost);
+ // variable [app]
+ path = srs_string_replace(path, "[app]", req->app);
+ // variable [stream]
+ path = srs_string_replace(path, "[stream]", req->stream);
+
+ // date and time substitude
+ // clock time
+ timeval tv;
+ if (gettimeofday(&tv, NULL) == -1) {
+ return ERROR_SYSTEM_TIME;
+ }
+
+ // to calendar time
+ struct tm* tm;
+ if ((tm = localtime(&tv.tv_sec)) == NULL) {
+ return ERROR_SYSTEM_TIME;
+ }
+
+ // the buffer to format the date and time.
+ char buf[64];
+
+ // [2006], replace with current year.
+ if (true) {
+ snprintf(buf, sizeof(buf), "%d", 1900 + tm->tm_year);
+ path = srs_string_replace(path, "[2006]", buf);
+ }
+ // [2006], replace with current year.
+ if (true) {
+ snprintf(buf, sizeof(buf), "%d", 1900 + tm->tm_year);
+ path = srs_string_replace(path, "[2006]", buf);
+ }
+ // [01], replace this const to current month.
+ if (true) {
+ snprintf(buf, sizeof(buf), "%d", 1 + tm->tm_mon);
+ path = srs_string_replace(path, "[01]", buf);
+ }
+ // [02], replace this const to current date.
+ if (true) {
+ snprintf(buf, sizeof(buf), "%d", tm->tm_mday);
+ path = srs_string_replace(path, "[02]", buf);
+ }
+ // [15], replace this const to current hour.
+ if (true) {
+ snprintf(buf, sizeof(buf), "%d", tm->tm_hour);
+ path = srs_string_replace(path, "[15]", buf);
+ }
+ // [04], repleace this const to current minute.
+ if (true) {
+ snprintf(buf, sizeof(buf), "%d", tm->tm_min);
+ path = srs_string_replace(path, "[04]", buf);
+ }
+ // [05], repleace this const to current second.
+ if (true) {
+ snprintf(buf, sizeof(buf), "%d", tm->tm_sec);
+ path = srs_string_replace(path, "[05]", buf);
+ }
+ // [999], repleace this const to current millisecond.
+ if (true) {
+ snprintf(buf, sizeof(buf), "%03d", (int)(tv.tv_usec / 1000));
+ path = srs_string_replace(path, "[999]", buf);
+ }
+ // [timestamp],replace this const to current UNIX timestamp in ms.
+ if (true) {
+ int64_t now_us = ((int64_t)tv.tv_sec) * 1000 * 1000 + (int64_t)tv.tv_usec;
+ snprintf(buf, sizeof(buf), "%"PRId64, now_us / 1000);
+ path = srs_string_replace(path, "[timestamp]", buf);
+ }
+
+ // create dir first.
+ std::string dir = path.substr(0, path.rfind("/"));
+ if ((ret = srs_create_dir_recursively(dir)) != ERROR_SUCCESS) {
+ srs_error("create dir=%s failed. ret=%d", dir.c_str(), ret);
+ return ret;
+ }
+ srs_info("create dir=%s ok", dir.c_str());
+
+ if ((ret = flv_open(req->get_stream_url(), path)) != ERROR_SUCCESS) {
return ret;
}
dvr_enabled = true;
@@ -320,6 +404,30 @@ int SrsDvrPlan::flv_close()
return ret;
}
+#ifdef SRS_AUTO_HTTP_CALLBACK
+ SrsRequest* req = _req;
+ if (_srs_config->get_vhost_http_hooks_enabled(req->vhost)) {
+ // HTTP: on_dvr
+ SrsConfDirective* on_dvr = _srs_config->get_vhost_on_dvr(req->vhost);
+ if (!on_dvr) {
+ srs_info("ignore the empty http callback: on_dvr");
+ return ret;
+ }
+
+ int connection_id = _srs_context->get_id();
+ std::string ip = req->ip;
+ std::string cwd = _srs_config->cwd();
+ std::string file = segment->path;
+ for (int i = 0; i < (int)on_dvr->args.size(); i++) {
+ std::string url = on_dvr->args.at(i);
+ if ((ret = SrsHttpHooks::on_dvr(url, connection_id, ip, req, cwd, file)) != ERROR_SUCCESS) {
+ srs_error("hook client on_dvr failed. url=%s, ret=%d", url.c_str(), ret);
+ return ret;
+ }
+ }
+ }
+#endif
+
return ret;
}
diff --git a/trunk/src/app/srs_app_hls.cpp b/trunk/src/app/srs_app_hls.cpp
index f08c0ac8f..460d03b0a 100644
--- a/trunk/src/app/srs_app_hls.cpp
+++ b/trunk/src/app/srs_app_hls.cpp
@@ -951,16 +951,12 @@ int SrsHlsMuxer::create_dir()
app_dir += app;
// TODO: cleanup the dir when startup.
-
- mode_t mode = S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IXOTH;
- if (::mkdir(app_dir.c_str(), mode) < 0) {
- if (errno != EEXIST) {
- ret = ERROR_HLS_CREATE_DIR;
- srs_error("create app dir %s failed. ret=%d", app_dir.c_str(), ret);
- return ret;
- }
+
+ if ((ret = srs_create_dir_recursively(app_dir)) != ERROR_SUCCESS) {
+ srs_error("create app dir %s failed. ret=%d", app_dir.c_str(), ret);
+ return ret;
}
- srs_info("create app dir %s success.", app_dir.c_str());
+ srs_info("create app dir %s ok", app_dir.c_str());
return ret;
}
diff --git a/trunk/src/app/srs_app_http_api.cpp b/trunk/src/app/srs_app_http_api.cpp
index a0c43492e..41c752dde 100644
--- a/trunk/src/app/srs_app_http_api.cpp
+++ b/trunk/src/app/srs_app_http_api.cpp
@@ -26,7 +26,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#ifdef SRS_AUTO_HTTP_API
#include
-#include
using namespace std;
#include
@@ -153,8 +152,8 @@ int SrsApiV1::do_process_request(SrsStSocket* skt, SrsHttpMessage* req)
<< __SRS_JFIELD_STR("meminfos", "the meminfo of system") << __SRS_JFIELD_CONT
<< __SRS_JFIELD_STR("authors", "the primary authors and contributors") << __SRS_JFIELD_CONT
<< __SRS_JFIELD_STR("requests", "the request itself, for http debug") << __SRS_JFIELD_CONT
- << __SRS_JFIELD_STR("vhosts", "list all vhosts") << __SRS_JFIELD_CONT
- << __SRS_JFIELD_STR("streams", "list streams that match the name or vhost")
+ << __SRS_JFIELD_STR("vhosts", "dumps vhost to json") << __SRS_JFIELD_CONT
+ << __SRS_JFIELD_STR("streams", "dumps streams to json")
<< __SRS_JOBJECT_END
<< __SRS_JOBJECT_END;
@@ -522,30 +521,19 @@ bool SrsApiVhosts::can_handle(const char* path, int length, const char** /*pchil
int SrsApiVhosts::do_process_request(SrsStSocket* skt, SrsHttpMessage* req)
{
+ std::stringstream data;
+ SrsStatistic* stat = SrsStatistic::instance();
+ int ret = stat->dumps_vhosts(data);
+
std::stringstream ss;
- std::set vhost_set;
- SrsStreamInfoMap* pool = SrsStatistic::instance()->get_pool();
- SrsStreamInfoMap::iterator it;
- for (it = pool->begin(); it != pool->end(); it++) {
- if (it->second->_req == NULL)
- continue;
- vhost_set.insert(it->second->_req->vhost);
- }
-
- ss << __SRS_JARRAY_START;
- bool first = true;
- std::set::iterator it_set;
- for (it_set = vhost_set.begin(); it_set != vhost_set.end(); it_set++) {
- if (first) {
- first = false;
- } else {
- ss << __SRS_JFIELD_CONT;
- }
-
- ss << "\"" << (*it_set) << "\"";
- }
- ss << __SRS_JARRAY_END;
+ ss << __SRS_JOBJECT_START
+ << __SRS_JFIELD_ERROR(ret) << __SRS_JFIELD_CONT
+ << __SRS_JFIELD_ORG("server", stat->server_id()) << __SRS_JFIELD_CONT
+ << __SRS_JFIELD_ORG("vhosts", __SRS_JARRAY_START)
+ << data.str()
+ << __SRS_JARRAY_END
+ << __SRS_JOBJECT_END;
return res_json(skt, req, ss.str());
}
@@ -565,41 +553,19 @@ bool SrsApiStreams::can_handle(const char* path, int length, const char** /*pchi
int SrsApiStreams::do_process_request(SrsStSocket* skt, SrsHttpMessage* req)
{
+ std::stringstream data;
+ SrsStatistic* stat = SrsStatistic::instance();
+ int ret = stat->dumps_streams(data);
+
std::stringstream ss;
- std::string query_name = req->query_get("name");
- std::string query_vhost = req->query_get("vhost");
- if (query_name.size() > 0 || query_vhost.size() > 0) {
- ss << __SRS_JARRAY_START;
- bool first = true;
- SrsStreamInfoMap* pool = SrsStatistic::instance()->get_pool();
- SrsStreamInfoMap::iterator it;
- for (it = pool->begin(); it != pool->end(); it++) {
- SrsRequest* reqinfo = it->second->_req;
- if (reqinfo == NULL)
- continue;
-
- if (reqinfo->stream == query_name || reqinfo->vhost == query_vhost) {
- if (first) {
- first = false;
- } else {
- ss << __SRS_JFIELD_CONT;
- }
-
- ss << __SRS_JOBJECT_START
- << __SRS_JFIELD_STR("name", reqinfo->stream) << __SRS_JFIELD_CONT
- << __SRS_JFIELD_STR("url", reqinfo->tcUrl) << __SRS_JFIELD_CONT
- << __SRS_JFIELD_ORG("clients", 0) << __SRS_JFIELD_CONT
- << __SRS_JFIELD_STR("status", "idle") << __SRS_JFIELD_CONT
- << __SRS_JFIELD_STR("type", "") << __SRS_JFIELD_CONT
- << __SRS_JFIELD_STR("codec", "")
- << __SRS_JOBJECT_END;
- }
- }
- ss << __SRS_JARRAY_END;
- } else {
- return res_error(skt, req, 400, "Bad Request", "unknown query");
- }
+ ss << __SRS_JOBJECT_START
+ << __SRS_JFIELD_ERROR(ret) << __SRS_JFIELD_CONT
+ << __SRS_JFIELD_ORG("server", stat->server_id()) << __SRS_JFIELD_CONT
+ << __SRS_JFIELD_ORG("streams", __SRS_JARRAY_START)
+ << data.str()
+ << __SRS_JARRAY_END
+ << __SRS_JOBJECT_END;
return res_json(skt, req, ss.str());
}
diff --git a/trunk/src/app/srs_app_http_hooks.cpp b/trunk/src/app/srs_app_http_hooks.cpp
index 5d792c6a7..cfa4712f6 100644
--- a/trunk/src/app/srs_app_http_hooks.cpp
+++ b/trunk/src/app/srs_app_http_hooks.cpp
@@ -122,8 +122,7 @@ void SrsHttpHooks::on_close(string url, int client_id, string ip, SrsRequest* re
<< __SRS_JFIELD_ORG("client_id", client_id) << __SRS_JFIELD_CONT
<< __SRS_JFIELD_STR("ip", ip) << __SRS_JFIELD_CONT
<< __SRS_JFIELD_STR("vhost", req->vhost) << __SRS_JFIELD_CONT
- << __SRS_JFIELD_STR("app", req->app) << __SRS_JFIELD_CONT
- << __SRS_JFIELD_STR("pageUrl", req->pageUrl)
+ << __SRS_JFIELD_STR("app", req->app)
<< __SRS_JOBJECT_END;
std::string data = ss.str();
std::string res;
@@ -178,7 +177,6 @@ int SrsHttpHooks::on_publish(string url, int client_id, string ip, SrsRequest* r
<< __SRS_JFIELD_STR("ip", ip) << __SRS_JFIELD_CONT
<< __SRS_JFIELD_STR("vhost", req->vhost) << __SRS_JFIELD_CONT
<< __SRS_JFIELD_STR("app", req->app) << __SRS_JFIELD_CONT
- << __SRS_JFIELD_STR("pageUrl", req->pageUrl) << __SRS_JFIELD_CONT
<< __SRS_JFIELD_STR("stream", req->stream)
<< __SRS_JOBJECT_END;
std::string data = ss.str();
@@ -234,7 +232,6 @@ void SrsHttpHooks::on_unpublish(string url, int client_id, string ip, SrsRequest
<< __SRS_JFIELD_STR("ip", ip) << __SRS_JFIELD_CONT
<< __SRS_JFIELD_STR("vhost", req->vhost) << __SRS_JFIELD_CONT
<< __SRS_JFIELD_STR("app", req->app) << __SRS_JFIELD_CONT
- << __SRS_JFIELD_STR("pageUrl", req->pageUrl) << __SRS_JFIELD_CONT
<< __SRS_JFIELD_STR("stream", req->stream)
<< __SRS_JOBJECT_END;
std::string data = ss.str();
@@ -290,7 +287,6 @@ int SrsHttpHooks::on_play(string url, int client_id, string ip, SrsRequest* req)
<< __SRS_JFIELD_STR("ip", ip) << __SRS_JFIELD_CONT
<< __SRS_JFIELD_STR("vhost", req->vhost) << __SRS_JFIELD_CONT
<< __SRS_JFIELD_STR("app", req->app) << __SRS_JFIELD_CONT
- << __SRS_JFIELD_STR("pageUrl", req->pageUrl) << __SRS_JFIELD_CONT
<< __SRS_JFIELD_STR("stream", req->stream)
<< __SRS_JOBJECT_END;
std::string data = ss.str();
@@ -346,7 +342,6 @@ void SrsHttpHooks::on_stop(string url, int client_id, string ip, SrsRequest* req
<< __SRS_JFIELD_STR("ip", ip) << __SRS_JFIELD_CONT
<< __SRS_JFIELD_STR("vhost", req->vhost) << __SRS_JFIELD_CONT
<< __SRS_JFIELD_STR("app", req->app) << __SRS_JFIELD_CONT
- << __SRS_JFIELD_STR("pageUrl", req->pageUrl) << __SRS_JFIELD_CONT
<< __SRS_JFIELD_STR("stream", req->stream)
<< __SRS_JOBJECT_END;
std::string data = ss.str();
@@ -384,5 +379,61 @@ void SrsHttpHooks::on_stop(string url, int client_id, string ip, SrsRequest* req
return;
}
-#endif
+int SrsHttpHooks::on_dvr(string url, int client_id, string ip, SrsRequest* req, string cwd, string file)
+{
+ int ret = ERROR_SUCCESS;
+
+ SrsHttpUri uri;
+ if ((ret = uri.initialize(url)) != ERROR_SUCCESS) {
+ srs_error("http uri parse on_dvr url failed, ignored. "
+ "client_id=%d, url=%s, ret=%d", client_id, url.c_str(), ret);
+ return ret;
+ }
+
+ std::stringstream ss;
+ ss << __SRS_JOBJECT_START
+ << __SRS_JFIELD_STR("action", "on_dvr") << __SRS_JFIELD_CONT
+ << __SRS_JFIELD_ORG("client_id", client_id) << __SRS_JFIELD_CONT
+ << __SRS_JFIELD_STR("ip", ip) << __SRS_JFIELD_CONT
+ << __SRS_JFIELD_STR("vhost", req->vhost) << __SRS_JFIELD_CONT
+ << __SRS_JFIELD_STR("app", req->app) << __SRS_JFIELD_CONT
+ << __SRS_JFIELD_STR("stream", req->stream) << __SRS_JFIELD_CONT
+ << __SRS_JFIELD_STR("cwd", cwd) << __SRS_JFIELD_CONT
+ << __SRS_JFIELD_STR("file", file)
+ << __SRS_JOBJECT_END;
+ std::string data = ss.str();
+ std::string res;
+ int status_code;
+
+ SrsHttpClient http;
+ if ((ret = http.post(&uri, data, status_code, res)) != ERROR_SUCCESS) {
+ srs_error("http post on_dvr uri failed, ignored. "
+ "client_id=%d, url=%s, request=%s, response=%s, ret=%d",
+ client_id, url.c_str(), data.c_str(), res.c_str(), ret);
+ return ret;
+ }
+
+ // ensure the http status is ok.
+ // https://github.com/winlinvip/simple-rtmp-server/issues/158
+ if (status_code != SRS_CONSTS_HTTP_OK) {
+ ret = ERROR_HTTP_STATUS_INVLIAD;
+ srs_error("http hook on_dvr status failed. "
+ "client_id=%d, code=%d, ret=%d", client_id, status_code, ret);
+ return ret;
+ }
+
+ if (res.empty() || res != SRS_HTTP_RESPONSE_OK) {
+ ret = ERROR_HTTP_DATA_INVLIAD;
+ srs_warn("http hook on_dvr validate failed, ignored. "
+ "client_id=%d, res=%s, ret=%d", client_id, res.c_str(), ret);
+ return ret;
+ }
+
+ srs_trace("http hook on_dvr success. "
+ "client_id=%d, url=%s, request=%s, response=%s, ret=%d",
+ client_id, url.c_str(), data.c_str(), res.c_str(), ret);
+
+ return ret;
+}
+#endif
diff --git a/trunk/src/app/srs_app_http_hooks.hpp b/trunk/src/app/srs_app_http_hooks.hpp
index b6c9ca6a2..c4286d13c 100644
--- a/trunk/src/app/srs_app_http_hooks.hpp
+++ b/trunk/src/app/srs_app_http_hooks.hpp
@@ -58,7 +58,6 @@ public:
* @param client_id the id of client on server.
* @param url the api server url, to valid the client.
* ignore if empty.
- * @return valid failed or connect to the url failed.
*/
static int on_connect(std::string url, int client_id, std::string ip, SrsRequest* req);
/**
@@ -73,7 +72,6 @@ public:
* @param client_id the id of client on server.
* @param url the api server url, to valid the client.
* ignore if empty.
- * @return valid failed or connect to the url failed.
*/
static int on_publish(std::string url, int client_id, std::string ip, SrsRequest* req);
/**
@@ -88,7 +86,6 @@ public:
* @param client_id the id of client on server.
* @param url the api server url, to valid the client.
* ignore if empty.
- * @return valid failed or connect to the url failed.
*/
static int on_play(std::string url, int client_id, std::string ip, SrsRequest* req);
/**
@@ -98,6 +95,15 @@ public:
* ignore if empty.
*/
static void on_stop(std::string url, int client_id, std::string ip, SrsRequest* req);
+ /**
+ * on_dvr hook, when reap a dvr file.
+ * @param client_id the id of client on server.
+ * @param url the api server url, to process the event.
+ * ignore if empty.
+ * @param cwd the current work directory, used to resolve the reltive file path.
+ * @param file the file path, can be relative or absolute path.
+ */
+ static int on_dvr(std::string url, int client_id, std::string ip, SrsRequest* req, std::string cwd, std::string file);
};
#endif
diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp
index 43feda5a7..b7bb4d815 100644
--- a/trunk/src/app/srs_app_rtmp_conn.cpp
+++ b/trunk/src/app/srs_app_rtmp_conn.cpp
@@ -51,6 +51,7 @@ using namespace std;
#include
#include
#include
+#include
#include
// when stream is busy, for example, streaming is already
@@ -82,6 +83,7 @@ SrsRtmpConn::SrsRtmpConn(SrsServer* srs_server, st_netfd_t client_stfd)
rtmp = new SrsRtmpServer(skt);
refer = new SrsRefer();
bandwidth = new SrsBandwidth();
+ security = new SrsSecurity();
duration = 0;
kbps = new SrsKbps();
kbps->set_io(skt, skt);
@@ -103,6 +105,7 @@ SrsRtmpConn::~SrsRtmpConn()
srs_freep(skt);
srs_freep(refer);
srs_freep(bandwidth);
+ srs_freep(security);
srs_freep(kbps);
}
@@ -133,6 +136,9 @@ int SrsRtmpConn::do_cycle()
}
srs_verbose("rtmp connect app success");
+ // set client ip to request.
+ req->ip = ip;
+
// discovery vhost, resolve the vhost from config
SrsConfDirective* parsed_vhost = _srs_config->get_vhost(req->vhost);
if (parsed_vhost) {
@@ -361,6 +367,13 @@ int SrsRtmpConn::stream_service_cycle()
req->strip();
srs_trace("client identified, type=%s, stream_name=%s, duration=%.2f",
srs_client_type_string(type).c_str(), req->stream.c_str(), req->duration);
+
+ // security check
+ if ((ret = security->check(type, ip, req)) != ERROR_SUCCESS) {
+ srs_error("security check failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("security check ok");
// client is identified, set the timeout to service timeout.
rtmp->set_recv_timeout(SRS_CONSTS_RTMP_RECV_TIMEOUT_US);
@@ -383,7 +396,12 @@ int SrsRtmpConn::stream_service_cycle()
}
srs_assert(source != NULL);
- SrsStatistic::instance()->add_request_info(source, req);
+ // update the statistic when source disconveried.
+ SrsStatistic* stat = SrsStatistic::instance();
+ if ((ret = stat->on_client(_srs_context->get_id(), req)) != ERROR_SUCCESS) {
+ srs_error("stat client failed. ret=%d", ret);
+ return ret;
+ }
// check ASAP, to fail it faster if invalid.
if (type != SrsRtmpConnPlay && !vhost_is_edge) {
diff --git a/trunk/src/app/srs_app_rtmp_conn.hpp b/trunk/src/app/srs_app_rtmp_conn.hpp
index 111fc0f83..ea3fc7619 100644
--- a/trunk/src/app/srs_app_rtmp_conn.hpp
+++ b/trunk/src/app/srs_app_rtmp_conn.hpp
@@ -51,6 +51,7 @@ class SrsRtmpClient;
class SrsSharedPtrMessage;
class SrsQueueRecvThread;
class SrsPublishRecvThread;
+class SrsSecurity;
/**
* the client provides the main logic control for RTMP clients.
@@ -66,6 +67,7 @@ private:
SrsRtmpServer* rtmp;
SrsRefer* refer;
SrsBandwidth* bandwidth;
+ SrsSecurity* security;
// elapse duration in ms
// for live play duration, for instance, rtmpdump to record.
// @see https://github.com/winlinvip/simple-rtmp-server/issues/47
diff --git a/trunk/src/app/srs_app_security.cpp b/trunk/src/app/srs_app_security.cpp
new file mode 100644
index 000000000..b843e58b0
--- /dev/null
+++ b/trunk/src/app/srs_app_security.cpp
@@ -0,0 +1,159 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2013-2015 winlin
+
+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
+
+#include
+#include
+
+using namespace std;
+
+SrsSecurity::SrsSecurity()
+{
+}
+
+SrsSecurity::~SrsSecurity()
+{
+}
+
+int SrsSecurity::check(SrsRtmpConnType type, string ip, SrsRequest* req)
+{
+ int ret = ERROR_SUCCESS;
+
+ // allow all if security disabled.
+ if (!_srs_config->get_security_enabled(req->vhost)) {
+ return ret;
+ }
+
+ // default to deny all when security enabled.
+ ret = ERROR_SYSTEM_SECURITY;
+
+ // rules to apply
+ SrsConfDirective* rules = _srs_config->get_security_rules(req->vhost);
+ if (!rules) {
+ return ret;
+ }
+
+ // allow if matches allow strategy.
+ if (allow_check(rules, type, ip) == ERROR_SYSTEM_SECURITY_ALLOW) {
+ ret = ERROR_SUCCESS;
+ }
+
+ // deny if matches deny strategy.
+ if (deny_check(rules, type, ip) == ERROR_SYSTEM_SECURITY_DENY) {
+ ret = ERROR_SYSTEM_SECURITY_DENY;
+ }
+
+ return ret;
+}
+
+int SrsSecurity::allow_check(SrsConfDirective* rules, SrsRtmpConnType type, std::string ip)
+{
+ int ret = ERROR_SUCCESS;
+
+ for (int i = 0; i < (int)rules->directives.size(); i++) {
+ SrsConfDirective* rule = rules->at(i);
+
+ if (rule->name != "allow") {
+ continue;
+ }
+
+ switch (type) {
+ case SrsRtmpConnPlay:
+ if (rule->arg0() != "play") {
+ break;
+ }
+ if (rule->arg1() == "all" || rule->arg1() == ip) {
+ ret = ERROR_SYSTEM_SECURITY_ALLOW;
+ break;
+ }
+ break;
+ case SrsRtmpConnFMLEPublish:
+ case SrsRtmpConnFlashPublish:
+ if (rule->arg0() != "publish") {
+ break;
+ }
+ if (rule->arg1() == "all" || rule->arg1() == ip) {
+ ret = ERROR_SYSTEM_SECURITY_ALLOW;
+ break;
+ }
+ break;
+ case SrsRtmpConnUnknown:
+ default:
+ break;
+ }
+
+ // when matched, donot search more.
+ if (ret == ERROR_SYSTEM_SECURITY_ALLOW) {
+ break;
+ }
+ }
+
+ return ret;
+}
+
+int SrsSecurity::deny_check(SrsConfDirective* rules, SrsRtmpConnType type, std::string ip)
+{
+ int ret = ERROR_SUCCESS;
+
+ for (int i = 0; i < (int)rules->directives.size(); i++) {
+ SrsConfDirective* rule = rules->at(i);
+
+ if (rule->name != "deny") {
+ continue;
+ }
+
+ switch (type) {
+ case SrsRtmpConnPlay:
+ if (rule->arg0() != "play") {
+ break;
+ }
+ if (rule->arg1() == "all" || rule->arg1() == ip) {
+ ret = ERROR_SYSTEM_SECURITY_DENY;
+ break;
+ }
+ break;
+ case SrsRtmpConnFMLEPublish:
+ case SrsRtmpConnFlashPublish:
+ if (rule->arg0() != "publish") {
+ break;
+ }
+ if (rule->arg1() == "all" || rule->arg1() == ip) {
+ ret = ERROR_SYSTEM_SECURITY_DENY;
+ break;
+ }
+ break;
+ case SrsRtmpConnUnknown:
+ default:
+ break;
+ }
+
+ // when matched, donot search more.
+ if (ret == ERROR_SYSTEM_SECURITY_DENY) {
+ break;
+ }
+ }
+
+ return ret;
+}
+
diff --git a/trunk/src/app/srs_app_security.hpp b/trunk/src/app/srs_app_security.hpp
new file mode 100644
index 000000000..9f1e9dec4
--- /dev/null
+++ b/trunk/src/app/srs_app_security.hpp
@@ -0,0 +1,70 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2013-2015 winlin
+
+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_APP_SECURITY_HPP
+#define SRS_APP_SECURITY_HPP
+
+/*
+#include
+*/
+
+#include
+
+#include
+
+#include
+
+class SrsConfDirective;
+
+/**
+* the security apply on vhost.
+* @see https://github.com/winlinvip/simple-rtmp-server/issues/211
+*/
+class SrsSecurity
+{
+public:
+ SrsSecurity();
+ virtual ~SrsSecurity();
+public:
+ /**
+ * security check the client apply by vhost security strategy
+ * @param type the client type, publish or play.
+ * @param ip the ip address of client.
+ * @param req the request object of client.
+ */
+ virtual int check(SrsRtmpConnType type, std::string ip, SrsRequest* req);
+private:
+ /**
+ * security check the allow,
+ * @return, if allowed, ERROR_SYSTEM_SECURITY_ALLOW.
+ */
+ virtual int allow_check(SrsConfDirective* rules, SrsRtmpConnType type, std::string ip);
+ /**
+ * security check the deny,
+ * @return, if denied, ERROR_SYSTEM_SECURITY_DENY.
+ */
+ virtual int deny_check(SrsConfDirective* rules, SrsRtmpConnType type, std::string ip);
+};
+
+#endif
+
diff --git a/trunk/src/app/srs_app_statistic.cpp b/trunk/src/app/srs_app_statistic.cpp
index d93dfe737..45e0019c7 100644
--- a/trunk/src/app/srs_app_statistic.cpp
+++ b/trunk/src/app/srs_app_statistic.cpp
@@ -23,53 +23,142 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include
+#include
+#include
+using namespace std;
+
#include
+#include
-SrsStreamInfo::SrsStreamInfo()
+int64_t __srs_gvid = getpid();
+
+int64_t __srs_generate_id()
{
- _req = NULL;
+ return __srs_gvid++;
}
-SrsStreamInfo::~SrsStreamInfo()
+SrsStatisticVhost::SrsStatisticVhost()
{
- if (_req != NULL)
- delete _req;
+ id = __srs_generate_id();
}
-SrsStatistic *SrsStatistic::_instance = NULL;
+SrsStatisticVhost::~SrsStatisticVhost()
+{
+}
+
+SrsStatisticStream::SrsStatisticStream()
+{
+ id = __srs_generate_id();
+ vhost = NULL;
+}
+
+SrsStatisticStream::~SrsStatisticStream()
+{
+}
+
+SrsStatistic* SrsStatistic::_instance = new SrsStatistic();
SrsStatistic::SrsStatistic()
{
-
+ _server_id = __srs_generate_id();
}
SrsStatistic::~SrsStatistic()
{
- SrsStreamInfoMap::iterator it;
- for (it = pool.begin(); it != pool.end(); it++) {
- delete it->second;
- }
+ if (true) {
+ std::map::iterator it;
+ for (it = vhosts.begin(); it != vhosts.end(); it++) {
+ SrsStatisticVhost* vhost = it->second;
+ srs_freep(vhost);
+ }
+ }
+ if (true) {
+ std::map::iterator it;
+ for (it = streams.begin(); it != streams.end(); it++) {
+ SrsStatisticStream* stream = it->second;
+ srs_freep(stream);
+ }
+ }
+ if (true) {
+ std::map::iterator it;
+ for (it = clients.begin(); it != clients.end(); it++) {
+ SrsStatisticClient* client = it->second;
+ srs_freep(client);
+ }
+ }
}
-SrsStreamInfoMap* SrsStatistic::get_pool()
+SrsStatistic* SrsStatistic::instance()
{
- return &pool;
+ return _instance;
}
-SrsStreamInfo* SrsStatistic::get(void *p)
+int SrsStatistic::on_client(int id, SrsRequest* req)
{
- SrsStreamInfoMap::iterator it = pool.find(p);
- if (it == pool.end()) {
- pool[p] = new SrsStreamInfo();
- return pool[p];
- } else {
- return it->second;
- }
+ int ret = ERROR_SUCCESS;
+
+ // create vhost if not exists.
+ SrsStatisticVhost* vhost = NULL;
+ if (vhosts.find(req->vhost) == vhosts.end()) {
+ vhost = new SrsStatisticVhost();
+ vhost->vhost = req->vhost;
+ vhosts[req->vhost] = vhost;
+ } else {
+ vhost = vhosts[req->vhost];
+ }
+
+ // the url to identify the stream.
+ std::string url = req->get_stream_url();
+
+ // create stream if not exists.
+ SrsStatisticStream* stream = NULL;
+ if (streams.find(url) == streams.end()) {
+ stream = new SrsStatisticStream();
+ stream->vhost = vhost;
+ stream->stream = req->stream;
+ stream->url = url;
+ streams[url] = stream;
+ } else {
+ stream = streams[url];
+ }
+
+ return ret;
}
-void SrsStatistic::add_request_info(void *p, SrsRequest *req)
+int64_t SrsStatistic::server_id()
{
- SrsStreamInfo *info = get(p);
- if (info->_req == NULL)
- info->_req = req->copy();
-}
\ No newline at end of file
+ return _server_id;
+}
+
+int SrsStatistic::dumps_vhosts(stringstream& ss)
+{
+ int ret = ERROR_SUCCESS;
+
+ std::map::iterator it;
+ for (it = vhosts.begin(); it != vhosts.end(); it++) {
+ SrsStatisticVhost* vhost = it->second;
+ ss << __SRS_JOBJECT_START
+ << __SRS_JFIELD_ORG("id", vhost->id) << __SRS_JFIELD_CONT
+ << __SRS_JFIELD_STR("name", vhost->vhost)
+ << __SRS_JOBJECT_END;
+ }
+
+ return ret;
+}
+
+int SrsStatistic::dumps_streams(stringstream& ss)
+{
+ int ret = ERROR_SUCCESS;
+
+ std::map::iterator it;
+ for (it = streams.begin(); it != streams.end(); it++) {
+ SrsStatisticStream* stream = it->second;
+ ss << __SRS_JOBJECT_START
+ << __SRS_JFIELD_ORG("id", stream->id) << __SRS_JFIELD_CONT
+ << __SRS_JFIELD_STR("name", stream->stream) << __SRS_JFIELD_CONT
+ << __SRS_JFIELD_ORG("vhost", stream->vhost->id)
+ << __SRS_JOBJECT_END;
+ }
+
+ return ret;
+}
diff --git a/trunk/src/app/srs_app_statistic.hpp b/trunk/src/app/srs_app_statistic.hpp
index 42e0d81d2..f261962ea 100644
--- a/trunk/src/app/srs_app_statistic.hpp
+++ b/trunk/src/app/srs_app_statistic.hpp
@@ -31,40 +31,78 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include
#include