winlin

fix #274: http-callback support on_dvr when reap a dvr file. 2.0.89

@@ -501,6 +501,7 @@ Supported operating systems and hardware: @@ -501,6 +501,7 @@ Supported operating systems and hardware:
501 * 2013-10-17, Created.<br/> 501 * 2013-10-17, Created.<br/>
502 502
503 ## History 503 ## History
  504 +* 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
504 * v2.0, 2015-01-03, hotfix to remove the pageUrl for http callback. 2.0.88 505 * v2.0, 2015-01-03, hotfix to remove the pageUrl for http callback. 2.0.88
505 * 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 506 * 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
506 * 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 507 * 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
@@ -296,6 +296,11 @@ vhost dvr.srs.com { @@ -296,6 +296,11 @@ vhost dvr.srs.com {
296 # 3. off, disable the time jitter algorithm, like atc. 296 # 3. off, disable the time jitter algorithm, like atc.
297 # default: full 297 # default: full
298 time_jitter full; 298 time_jitter full;
  299 +
  300 + # on_dvr
  301 + # for the dvr http callback, @see http_hooks.on_dvr of vhost hooks.callback.srs.com
  302 + # @read https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_DVR#http-callback
  303 + # @read https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_DVR#http-callback
299 } 304 }
300 } 305 }
301 306
@@ -338,7 +343,7 @@ vhost ingest.srs.com { @@ -338,7 +343,7 @@ vhost ingest.srs.com {
338 } 343 }
339 } 344 }
340 345
341 -# vhost for http 346 +# vhost for http server config in each vhost.
342 vhost http.srs.com { 347 vhost http.srs.com {
343 # http vhost specified config 348 # http vhost specified config
344 http { 349 http {
@@ -490,6 +495,20 @@ vhost hooks.callback.srs.com { @@ -490,6 +495,20 @@ vhost hooks.callback.srs.com {
490 # support multiple api hooks, format: 495 # support multiple api hooks, format:
491 # on_stop http://xxx/api0 http://xxx/api1 http://xxx/apiN 496 # on_stop http://xxx/api0 http://xxx/api1 http://xxx/apiN
492 on_stop http://127.0.0.1:8085/api/v1/sessions http://localhost:8085/api/v1/sessions; 497 on_stop http://127.0.0.1:8085/api/v1/sessions http://localhost:8085/api/v1/sessions;
  498 + # when srs reap a dvr file, call the hook,
  499 + # the request in the POST data string is a object encode by json:
  500 + # {
  501 + # "action": "on_dvr",
  502 + # "client_id": 1985,
  503 + # "ip": "192.168.1.10", "vhost": "video.test.com", "app": "live",
  504 + # "stream": "livestream",
  505 + # "cwd": "/usr/local/srs",
  506 + # "file": "./objs/nginx/html/live/livestream.1420254068776.flv"
  507 + # }
  508 + # if valid, the hook must return HTTP code 200(Stauts OK) and response
  509 + # an int value specifies the error code(0 corresponding to success):
  510 + # 0
  511 + on_dvr http://127.0.0.1:8085/api/v1/dvrs http://localhost:8085/api/v1/dvrs;
493 } 512 }
494 } 513 }
495 514
@@ -154,7 +154,7 @@ class RESTClients(object): @@ -154,7 +154,7 @@ class RESTClients(object):
154 return code 154 return code
155 155
156 ''' 156 '''
157 -handle the streams requests: publish/unpublish/dvr stream. 157 +handle the streams requests: publish/unpublish stream.
158 ''' 158 '''
159 class RESTStreams(object): 159 class RESTStreams(object):
160 exposed = True 160 exposed = True
@@ -241,6 +241,74 @@ class RESTStreams(object): @@ -241,6 +241,74 @@ class RESTStreams(object):
241 return code 241 return code
242 242
243 ''' 243 '''
  244 +handle the dvrs requests: dvr stream.
  245 +'''
  246 +class RESTDvrs(object):
  247 + exposed = True
  248 +
  249 + def GET(self):
  250 + enable_crossdomain()
  251 +
  252 + dvrs = {}
  253 + return json.dumps(dvrs)
  254 +
  255 + '''
  256 + for SRS hook: on_dvr
  257 + on_dvr:
  258 + when srs reap a dvr file, call the hook,
  259 + the request in the POST data string is a object encode by json:
  260 + {
  261 + "action": "on_dvr",
  262 + "client_id": 1985,
  263 + "ip": "192.168.1.10", "vhost": "video.test.com", "app": "live",
  264 + "stream": "livestream",
  265 + "cwd": "/usr/local/srs",
  266 + "file": "./objs/nginx/html/live/livestream.1420254068776.flv"
  267 + }
  268 + if valid, the hook must return HTTP code 200(Stauts OK) and response
  269 + an int value specifies the error code(0 corresponding to success):
  270 + 0
  271 + '''
  272 + def POST(self):
  273 + enable_crossdomain()
  274 +
  275 + # return the error code in str
  276 + code = Error.success
  277 +
  278 + req = cherrypy.request.body.read()
  279 + trace("post to dvrs, req=%s"%(req))
  280 + try:
  281 + json_req = json.loads(req)
  282 + except Exception, ex:
  283 + code = Error.system_parse_json
  284 + trace("parse the request to json failed, req=%s, ex=%s, code=%s"%(req, ex, code))
  285 + return str(code)
  286 +
  287 + action = json_req["action"]
  288 + if action == "on_dvr":
  289 + code = self.__on_dvr(json_req)
  290 + else:
  291 + trace("invalid request action: %s"%(json_req["action"]))
  292 + code = Error.request_invalid_action
  293 +
  294 + return str(code)
  295 +
  296 + def OPTIONS(self, *args, **kwargs):
  297 + enable_crossdomain()
  298 +
  299 + def __on_dvr(self, req):
  300 + code = Error.success
  301 +
  302 + trace("srs %s: client id=%s, ip=%s, vhost=%s, app=%s, stream=%s, cwd=%s, file=%s"%(
  303 + req["action"], req["client_id"], req["ip"], req["vhost"], req["app"], req["stream"],
  304 + req["cwd"], req["file"]
  305 + ))
  306 +
  307 + # TODO: process the on_dvr event
  308 +
  309 + return code
  310 +
  311 +'''
244 handle the sessions requests: client play/stop stream 312 handle the sessions requests: client play/stop stream
245 ''' 313 '''
246 class RESTSessions(object): 314 class RESTSessions(object):
@@ -1039,6 +1107,7 @@ class V1(object): @@ -1039,6 +1107,7 @@ class V1(object):
1039 self.clients = RESTClients() 1107 self.clients = RESTClients()
1040 self.streams = RESTStreams() 1108 self.streams = RESTStreams()
1041 self.sessions = RESTSessions() 1109 self.sessions = RESTSessions()
  1110 + self.dvrs = RESTDvrs()
1042 self.chats = RESTChats() 1111 self.chats = RESTChats()
1043 self.servers = RESTServers() 1112 self.servers = RESTServers()
1044 self.nodes = RESTNodes() 1113 self.nodes = RESTNodes()
@@ -1048,6 +1117,7 @@ class V1(object): @@ -1048,6 +1117,7 @@ class V1(object):
1048 "clients": "for srs http callback, to handle the clients requests: connect/disconnect vhost/app.", 1117 "clients": "for srs http callback, to handle the clients requests: connect/disconnect vhost/app.",
1049 "streams": "for srs http callback, to handle the streams requests: publish/unpublish stream.", 1118 "streams": "for srs http callback, to handle the streams requests: publish/unpublish stream.",
1050 "sessions": "for srs http callback, to handle the sessions requests: client play/stop stream", 1119 "sessions": "for srs http callback, to handle the sessions requests: client play/stop stream",
  1120 + "dvrs": "for srs http callback, to handle the dvr requests: dvr stream.",
1051 "chats": "for srs demo meeting, the chat streams, public chat room.", 1121 "chats": "for srs demo meeting, the chat streams, public chat room.",
1052 "nodes": { 1122 "nodes": {
1053 "summary": "for srs cdn node", 1123 "summary": "for srs cdn node",
@@ -1426,6 +1426,7 @@ int SrsConfig::check_config() @@ -1426,6 +1426,7 @@ int SrsConfig::check_config()
1426 string m = conf->at(j)->name.c_str(); 1426 string m = conf->at(j)->name.c_str();
1427 if (m != "enabled" && m != "on_connect" && m != "on_close" && m != "on_publish" 1427 if (m != "enabled" && m != "on_connect" && m != "on_close" && m != "on_publish"
1428 && m != "on_unpublish" && m != "on_play" && m != "on_stop" 1428 && m != "on_unpublish" && m != "on_play" && m != "on_stop"
  1429 + && m != "on_dvr"
1429 ) { 1430 ) {
1430 ret = ERROR_SYSTEM_CONFIG_INVALID; 1431 ret = ERROR_SYSTEM_CONFIG_INVALID;
1431 srs_error("unsupported vhost http_hooks directive %s, ret=%d", m.c_str(), ret); 1432 srs_error("unsupported vhost http_hooks directive %s, ret=%d", m.c_str(), ret);
@@ -2335,6 +2336,17 @@ SrsConfDirective* SrsConfig::get_vhost_on_stop(string vhost) @@ -2335,6 +2336,17 @@ SrsConfDirective* SrsConfig::get_vhost_on_stop(string vhost)
2335 return conf->get("on_stop"); 2336 return conf->get("on_stop");
2336 } 2337 }
2337 2338
  2339 +SrsConfDirective* SrsConfig::get_vhost_on_dvr(string vhost)
  2340 +{
  2341 + SrsConfDirective* conf = get_vhost_http_hooks(vhost);
  2342 +
  2343 + if (!conf) {
  2344 + return NULL;
  2345 + }
  2346 +
  2347 + return conf->get("on_dvr");
  2348 +}
  2349 +
2338 bool SrsConfig::get_bw_check_enabled(string vhost) 2350 bool SrsConfig::get_bw_check_enabled(string vhost)
2339 { 2351 {
2340 SrsConfDirective* conf = get_vhost(vhost); 2352 SrsConfDirective* conf = get_vhost(vhost);
@@ -610,6 +610,11 @@ public: @@ -610,6 +610,11 @@ public:
610 * @return the on_stop callback directive, the args is the url to callback. 610 * @return the on_stop callback directive, the args is the url to callback.
611 */ 611 */
612 virtual SrsConfDirective* get_vhost_on_stop(std::string vhost); 612 virtual SrsConfDirective* get_vhost_on_stop(std::string vhost);
  613 + /**
  614 + * get the on_dvr callbacks of vhost.
  615 + * @return the on_dvr callback directive, the args is the url to callback.
  616 + */
  617 + virtual SrsConfDirective* get_vhost_on_dvr(std::string vhost);
613 // bwct(bandwidth check tool) section 618 // bwct(bandwidth check tool) section
614 public: 619 public:
615 /** 620 /**
@@ -404,6 +404,30 @@ int SrsDvrPlan::flv_close() @@ -404,6 +404,30 @@ int SrsDvrPlan::flv_close()
404 return ret; 404 return ret;
405 } 405 }
406 406
  407 +#ifdef SRS_AUTO_HTTP_CALLBACK
  408 + SrsRequest* req = _req;
  409 + if (_srs_config->get_vhost_http_hooks_enabled(req->vhost)) {
  410 + // HTTP: on_dvr
  411 + SrsConfDirective* on_dvr = _srs_config->get_vhost_on_dvr(req->vhost);
  412 + if (!on_dvr) {
  413 + srs_info("ignore the empty http callback: on_dvr");
  414 + return ret;
  415 + }
  416 +
  417 + int connection_id = _srs_context->get_id();
  418 + std::string ip = req->ip;
  419 + std::string cwd = _srs_config->cwd();
  420 + std::string file = segment->path;
  421 + for (int i = 0; i < (int)on_dvr->args.size(); i++) {
  422 + std::string url = on_dvr->args.at(i);
  423 + if ((ret = SrsHttpHooks::on_dvr(url, connection_id, ip, req, cwd, file)) != ERROR_SUCCESS) {
  424 + srs_error("hook client on_dvr failed. url=%s, ret=%d", url.c_str(), ret);
  425 + return ret;
  426 + }
  427 + }
  428 + }
  429 +#endif
  430 +
407 return ret; 431 return ret;
408 } 432 }
409 433
@@ -379,5 +379,61 @@ void SrsHttpHooks::on_stop(string url, int client_id, string ip, SrsRequest* req @@ -379,5 +379,61 @@ void SrsHttpHooks::on_stop(string url, int client_id, string ip, SrsRequest* req
379 return; 379 return;
380 } 380 }
381 381
382 -#endif 382 +int SrsHttpHooks::on_dvr(string url, int client_id, string ip, SrsRequest* req, string cwd, string file)
  383 +{
  384 + int ret = ERROR_SUCCESS;
  385 +
  386 + SrsHttpUri uri;
  387 + if ((ret = uri.initialize(url)) != ERROR_SUCCESS) {
  388 + srs_error("http uri parse on_dvr url failed, ignored. "
  389 + "client_id=%d, url=%s, ret=%d", client_id, url.c_str(), ret);
  390 + return ret;
  391 + }
  392 +
  393 + std::stringstream ss;
  394 + ss << __SRS_JOBJECT_START
  395 + << __SRS_JFIELD_STR("action", "on_dvr") << __SRS_JFIELD_CONT
  396 + << __SRS_JFIELD_ORG("client_id", client_id) << __SRS_JFIELD_CONT
  397 + << __SRS_JFIELD_STR("ip", ip) << __SRS_JFIELD_CONT
  398 + << __SRS_JFIELD_STR("vhost", req->vhost) << __SRS_JFIELD_CONT
  399 + << __SRS_JFIELD_STR("app", req->app) << __SRS_JFIELD_CONT
  400 + << __SRS_JFIELD_STR("stream", req->stream) << __SRS_JFIELD_CONT
  401 + << __SRS_JFIELD_STR("cwd", cwd) << __SRS_JFIELD_CONT
  402 + << __SRS_JFIELD_STR("file", file)
  403 + << __SRS_JOBJECT_END;
  404 + std::string data = ss.str();
  405 + std::string res;
  406 + int status_code;
  407 +
  408 + SrsHttpClient http;
  409 + if ((ret = http.post(&uri, data, status_code, res)) != ERROR_SUCCESS) {
  410 + srs_error("http post on_dvr uri failed, ignored. "
  411 + "client_id=%d, url=%s, request=%s, response=%s, ret=%d",
  412 + client_id, url.c_str(), data.c_str(), res.c_str(), ret);
  413 + return ret;
  414 + }
  415 +
  416 + // ensure the http status is ok.
  417 + // https://github.com/winlinvip/simple-rtmp-server/issues/158
  418 + if (status_code != SRS_CONSTS_HTTP_OK) {
  419 + ret = ERROR_HTTP_STATUS_INVLIAD;
  420 + srs_error("http hook on_dvr status failed. "
  421 + "client_id=%d, code=%d, ret=%d", client_id, status_code, ret);
  422 + return ret;
  423 + }
  424 +
  425 + if (res.empty() || res != SRS_HTTP_RESPONSE_OK) {
  426 + ret = ERROR_HTTP_DATA_INVLIAD;
  427 + srs_warn("http hook on_dvr validate failed, ignored. "
  428 + "client_id=%d, res=%s, ret=%d", client_id, res.c_str(), ret);
  429 + return ret;
  430 + }
  431 +
  432 + srs_trace("http hook on_dvr success. "
  433 + "client_id=%d, url=%s, request=%s, response=%s, ret=%d",
  434 + client_id, url.c_str(), data.c_str(), res.c_str(), ret);
  435 +
  436 + return ret;
  437 +}
383 438
  439 +#endif
@@ -58,7 +58,6 @@ public: @@ -58,7 +58,6 @@ public:
58 * @param client_id the id of client on server. 58 * @param client_id the id of client on server.
59 * @param url the api server url, to valid the client. 59 * @param url the api server url, to valid the client.
60 * ignore if empty. 60 * ignore if empty.
61 - * @return valid failed or connect to the url failed.  
62 */ 61 */
63 static int on_connect(std::string url, int client_id, std::string ip, SrsRequest* req); 62 static int on_connect(std::string url, int client_id, std::string ip, SrsRequest* req);
64 /** 63 /**
@@ -73,7 +72,6 @@ public: @@ -73,7 +72,6 @@ public:
73 * @param client_id the id of client on server. 72 * @param client_id the id of client on server.
74 * @param url the api server url, to valid the client. 73 * @param url the api server url, to valid the client.
75 * ignore if empty. 74 * ignore if empty.
76 - * @return valid failed or connect to the url failed.  
77 */ 75 */
78 static int on_publish(std::string url, int client_id, std::string ip, SrsRequest* req); 76 static int on_publish(std::string url, int client_id, std::string ip, SrsRequest* req);
79 /** 77 /**
@@ -88,7 +86,6 @@ public: @@ -88,7 +86,6 @@ public:
88 * @param client_id the id of client on server. 86 * @param client_id the id of client on server.
89 * @param url the api server url, to valid the client. 87 * @param url the api server url, to valid the client.
90 * ignore if empty. 88 * ignore if empty.
91 - * @return valid failed or connect to the url failed.  
92 */ 89 */
93 static int on_play(std::string url, int client_id, std::string ip, SrsRequest* req); 90 static int on_play(std::string url, int client_id, std::string ip, SrsRequest* req);
94 /** 91 /**
@@ -98,6 +95,15 @@ public: @@ -98,6 +95,15 @@ public:
98 * ignore if empty. 95 * ignore if empty.
99 */ 96 */
100 static void on_stop(std::string url, int client_id, std::string ip, SrsRequest* req); 97 static void on_stop(std::string url, int client_id, std::string ip, SrsRequest* req);
  98 + /**
  99 + * on_dvr hook, when reap a dvr file.
  100 + * @param client_id the id of client on server.
  101 + * @param url the api server url, to process the event.
  102 + * ignore if empty.
  103 + * @param cwd the current work directory, used to resolve the reltive file path.
  104 + * @param file the file path, can be relative or absolute path.
  105 + */
  106 + static int on_dvr(std::string url, int client_id, std::string ip, SrsRequest* req, std::string cwd, std::string file);
101 }; 107 };
102 108
103 #endif 109 #endif
@@ -135,6 +135,9 @@ int SrsRtmpConn::do_cycle() @@ -135,6 +135,9 @@ int SrsRtmpConn::do_cycle()
135 } 135 }
136 srs_verbose("rtmp connect app success"); 136 srs_verbose("rtmp connect app success");
137 137
  138 + // set client ip to request.
  139 + req->ip = ip;
  140 +
138 // discovery vhost, resolve the vhost from config 141 // discovery vhost, resolve the vhost from config
139 SrsConfDirective* parsed_vhost = _srs_config->get_vhost(req->vhost); 142 SrsConfDirective* parsed_vhost = _srs_config->get_vhost(req->vhost);
140 if (parsed_vhost) { 143 if (parsed_vhost) {
@@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 // current release version 31 // current release version
32 #define VERSION_MAJOR 2 32 #define VERSION_MAJOR 2
33 #define VERSION_MINOR 0 33 #define VERSION_MINOR 0
34 -#define VERSION_REVISION 88 34 +#define VERSION_REVISION 89
35 // server info. 35 // server info.
36 #define RTMP_SIG_SRS_KEY "SRS" 36 #define RTMP_SIG_SRS_KEY "SRS"
37 #define RTMP_SIG_SRS_ROLE "origin/edge server" 37 #define RTMP_SIG_SRS_ROLE "origin/edge server"
@@ -91,6 +91,7 @@ SrsRequest* SrsRequest::copy() @@ -91,6 +91,7 @@ SrsRequest* SrsRequest::copy()
91 { 91 {
92 SrsRequest* cp = new SrsRequest(); 92 SrsRequest* cp = new SrsRequest();
93 93
  94 + cp->ip = ip;
94 cp->app = app; 95 cp->app = app;
95 cp->objectEncoding = objectEncoding; 96 cp->objectEncoding = objectEncoding;
96 cp->pageUrl = pageUrl; 97 cp->pageUrl = pageUrl;
@@ -54,6 +54,9 @@ class IMergeReadHandler; @@ -54,6 +54,9 @@ class IMergeReadHandler;
54 class SrsRequest 54 class SrsRequest
55 { 55 {
56 public: 56 public:
  57 + // client ip.
  58 + std::string ip;
  59 +public:
57 /** 60 /**
58 * tcUrl: rtmp://request_vhost:port/app/stream 61 * tcUrl: rtmp://request_vhost:port/app/stream
59 * support pass vhost in query string, such as: 62 * support pass vhost in query string, such as: