diff --git a/README.md b/README.md index 58b9c76ecf..af855d2312 100755 --- a/README.md +++ b/README.md @@ -532,6 +532,7 @@ Supported operating systems and hardware: ### SRS 2.0 history +* v2.0, 2015-02-24, for [#179](https://github.com/winlinvip/simple-rtmp-server/issues/179), dvr suport vhost/app/stream level control. 2.0.125. * v2.0, 2015-02-24, for [#304](https://github.com/winlinvip/simple-rtmp-server/issues/304), fix hls bug, write pts/dts error. 2.0.124. * v2.0, 2015-02-24, fix [#179](https://github.com/winlinvip/simple-rtmp-server/issues/179), support dvr http api. 2.0.123. * v2.0, 2015-02-19, refine dvr, append file when dvr file exists. 2.0.122. diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index 2d875e0ee8..f6de5728f1 100644 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -293,19 +293,24 @@ vhost dvr.srs.com { # http url to control dvr, for example, http://dev:1985/api/v1/dvrs # method=GET # to query dvrs of server. - # request params, for example ?vhost=__defaultVhost__, where: - # vhost, query all dvr of this vhost. + # request params, for example ?vhost=__defaultVhost__&&app=live&&stream=livestream, where: + # vhost, , query all dvr of this vhost. + # app, [optinal], query all dvr of this app. query all app if not specified. + # stream, [optional], query specified dvr stream. query all stream if not specified. # response in json, where: # {code:0, dvrs: [{path_tmpl:"./[15].[04].[05].[999].flv", path_dvr:"./22.7.43.312.flv", - # wait_keyframe:true, vhost:"__defaultVhost", callback:"http://127.0.0.1:8085/api/v1/dvrs", + # vhost:"__defaultVhost", app:"live", stream:"livestream", + # wait_keyframe:true, callback:"http://127.0.0.1:8085/api/v1/dvrs", # status:"stop"|"start" # }]} # method=POST # to start dvr of specified vhost. # request should encode in json, specifies the dvr to create, where: # {path_tmpl:"./[15].[04].[05].[999].flv", - # wait_keyframe:true, vhost:"__defaultVhost", callback:"http://127.0.0.1:8085/api/v1/dvrs" + # vhost:"__defaultVhost", app:"live", stream:"livestream", + # wait_keyframe:true, callback:"http://127.0.0.1:8085/api/v1/dvrs" # } + # @remark, the app and stream is optional. # response in json, where: # {code:0} # method=DELETE, to stop dvr @@ -316,7 +321,10 @@ vhost dvr.srs.com { # {code:0} # method=PUT, use as RPC(remote process call). # reap_segment, the request params in json, where: - # {action:"reap_segment", vhost:"__defaultVhost", path_tmpl:"./[15].[04].[05].[999].flv"} + # {action:"reap_segment", vhost:"__defaultVhost", app:"live", stream:"livestream", + # path_tmpl:"./[15].[04].[05].[999].flv" + # } + # @remark, the app and stream is optional. # when reap segment, the callback POST request in json: # {action:"on_dvr_reap_segment", client_id:100, vhost:"__defaultVhost__", # app:"live", stream:"livestream", cwd:"/home/winlin/srs", file:"./dvr.flv" diff --git a/trunk/src/app/srs_app_dvr.cpp b/trunk/src/app/srs_app_dvr.cpp index 6ed944b6ef..7a562c8948 100644 --- a/trunk/src/app/srs_app_dvr.cpp +++ b/trunk/src/app/srs_app_dvr.cpp @@ -1078,6 +1078,8 @@ int SrsDvrApiPlan::dumps(stringstream& ss) << __SRS_JFIELD_STR("path_dvr", segment->get_path()) << __SRS_JFIELD_CONT << __SRS_JFIELD_BOOL("wait_keyframe", wait_keyframe) << __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("callback", callback) << __SRS_JFIELD_CONT << __SRS_JFIELD_STR("status", (dvr_enabled? "start":"stop")) << __SRS_JOBJECT_END; @@ -1482,7 +1484,7 @@ int SrsApiDvrPool::add_dvr(SrsDvrApiPlan* dvr) return ERROR_SUCCESS; } -int SrsApiDvrPool::dumps(string vhost, stringstream& ss) +int SrsApiDvrPool::dumps(string vhost, string app, string stream, stringstream& ss) { int ret = ERROR_SUCCESS; @@ -1494,6 +1496,12 @@ int SrsApiDvrPool::dumps(string vhost, stringstream& ss) if (!vhost.empty() && plan->req->vhost != vhost) { continue; } + if (!app.empty() && plan->req->app != app) { + continue; + } + if (!stream.empty() && plan->req->stream != stream) { + continue; + } plans.push_back(plan); } @@ -1504,7 +1512,7 @@ int SrsApiDvrPool::dumps(string vhost, stringstream& ss) return ret; } - if (i < (int)dvrs.size() - 1) { + if (i < (int)plans.size() - 1) { ss << __SRS_JFIELD_CONT; } } @@ -1534,19 +1542,33 @@ int SrsApiDvrPool::create(SrsJsonAny* json) } std::string vhost = prop->to_str(); + std::string app, stream; + if ((prop = obj->ensure_property_string("app")) != NULL) { + app = prop->to_str(); + } + if ((prop = obj->ensure_property_string("stream")) != NULL) { + stream = prop->to_str(); + } + SrsDvrApiPlan* dvr = NULL; for (int i = 0; i < (int)dvrs.size(); i++) { SrsDvrApiPlan* plan = dvrs.at(i); if (!vhost.empty() && plan->req->vhost != vhost) { continue; } + if (!app.empty() && plan->req->app != app) { + continue; + } + if (!stream.empty() && plan->req->stream != stream) { + continue; + } dvr = plan; break; } if (!dvr) { - ret = ERROR_HTTP_DVR_CREATE_REQUEST; - srs_error("dvr: api create dvr request vhost invalid. vhost=%s. ret=%d", vhost.c_str(), ret); + ret = ERROR_HTTP_DVR_NO_TAEGET; + srs_error("dvr: create not found for url=%s/%s/%s, ret=%d", vhost.c_str(), app.c_str(), stream.c_str(), ret); return ret; } @@ -1570,7 +1592,7 @@ int SrsApiDvrPool::create(SrsJsonAny* json) return dvr->start(); } -int SrsApiDvrPool::stop(string vhost) +int SrsApiDvrPool::stop(string vhost, string app, string stream) { int ret = ERROR_SUCCESS; @@ -1580,9 +1602,21 @@ int SrsApiDvrPool::stop(string vhost) if (!vhost.empty() && plan->req->vhost != vhost) { continue; } + if (!app.empty() && plan->req->app != app) { + continue; + } + if (!stream.empty() && plan->req->stream != stream) { + continue; + } plans.push_back(plan); } + if (plans.empty()) { + ret = ERROR_HTTP_DVR_NO_TAEGET; + srs_error("dvr: stop not found for url=%s/%s/%s, ret=%d", vhost.c_str(), app.c_str(), stream.c_str(), ret); + return ret; + } + for (int i = 0; i < (int)plans.size(); i++) { SrsDvrApiPlan* plan = plans.at(i); @@ -1613,6 +1647,13 @@ int SrsApiDvrPool::rpc(SrsJsonAny* json) return ret; } std::string vhost = prop->to_str(); + std::string app, stream; + if ((prop = obj->ensure_property_string("app")) != NULL) { + app = prop->to_str(); + } + if ((prop = obj->ensure_property_string("stream")) != NULL) { + stream = prop->to_str(); + } std::vector plans; for (int i = 0; i < (int)dvrs.size(); i++) { @@ -1623,6 +1664,12 @@ int SrsApiDvrPool::rpc(SrsJsonAny* json) plans.push_back(plan); } + if (plans.empty()) { + ret = ERROR_HTTP_DVR_NO_TAEGET; + srs_error("dvr: rpc not found for url=%s/%s/%s, ret=%d", vhost.c_str(), app.c_str(), stream.c_str(), ret); + return ret; + } + for (int i = 0; i < (int)plans.size(); i++) { SrsDvrApiPlan* plan = plans.at(i); diff --git a/trunk/src/app/srs_app_dvr.hpp b/trunk/src/app/srs_app_dvr.hpp index c1619c7677..6e68a683c6 100644 --- a/trunk/src/app/srs_app_dvr.hpp +++ b/trunk/src/app/srs_app_dvr.hpp @@ -394,9 +394,9 @@ class SrsApiDvrPool public: virtual int add_dvr(SrsDvrApiPlan* dvr); public: - virtual int dumps(std::string vhost, std::stringstream& ss); + virtual int dumps(std::string vhost, std::string app, std::string stream, std::stringstream& ss); virtual int create(SrsJsonAny* json); - virtual int stop(std::string vhost); + virtual int stop(std::string vhost, std::string app, std::string stream); virtual int rpc(SrsJsonAny* json); }; diff --git a/trunk/src/app/srs_app_http_api.cpp b/trunk/src/app/srs_app_http_api.cpp index f8236567b1..cb02f890f0 100644 --- a/trunk/src/app/srs_app_http_api.cpp +++ b/trunk/src/app/srs_app_http_api.cpp @@ -494,7 +494,7 @@ int SrsGoApiDvrs::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r) SrsApiDvrPool* pool = SrsApiDvrPool::instance(); if (r->is_http_get()) { std::stringstream data; - int ret = pool->dumps(r->query_get("vhost"), data); + int ret = pool->dumps(r->query_get("vhost"), r->query_get("app"), r->query_get("stream"), data); ss << __SRS_JOBJECT_START << __SRS_JFIELD_ERROR(ret) << __SRS_JFIELD_CONT @@ -514,7 +514,7 @@ int SrsGoApiDvrs::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r) << __SRS_JFIELD_ERROR(ret) << __SRS_JOBJECT_END; } else if (r->is_http_delete()) { - int ret = pool->stop(r->query_get("vhost")); + int ret = pool->stop(r->query_get("vhost"), r->query_get("app"), r->query_get("stream")); ss << __SRS_JOBJECT_START << __SRS_JFIELD_ERROR(ret) diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 8d14c1b112..77ffcf3efb 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 124 +#define VERSION_REVISION 125 // server info. #define RTMP_SIG_SRS_KEY "SRS" diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index 49f3553a51..be35e673d7 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -213,6 +213,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define ERROR_HTTP_DVR_REQUEST 3051 #define ERROR_HTTP_JSON_REQUIRED 3052 #define ERROR_HTTP_DVR_CREATE_REQUEST 3053 +#define ERROR_HTTP_DVR_NO_TAEGET 3054 /////////////////////////////////////////////////////// // HTTP/StreamCaster protocol error.