winlin

enhanced on_hls_notify, support HTTP GET when reap ts.

@@ -562,6 +562,7 @@ Supported operating systems and hardware: @@ -562,6 +562,7 @@ Supported operating systems and hardware:
562 562
563 ### SRS 2.0 history 563 ### SRS 2.0 history
564 564
  565 +* v2.0, 2015-04-10, enhanced on_hls_notify, support HTTP GET when reap ts.
565 * v2.0, 2015-04-10, refine the hls deviation for floor algorithm. 566 * v2.0, 2015-04-10, refine the hls deviation for floor algorithm.
566 * v2.0, 2015-04-08, for [#375](https://github.com/winlinvip/simple-rtmp-server/issues/375), fix hls bug, keep cc continous between ts files. 2.0.159. 567 * v2.0, 2015-04-08, for [#375](https://github.com/winlinvip/simple-rtmp-server/issues/375), fix hls bug, keep cc continous between ts files. 2.0.159.
567 * v2.0, 2015-04-04, for [#304](https://github.com/winlinvip/simple-rtmp-server/issues/304), rewrite annexb mux for ts, refer to apple sample. 2.0.157. 568 * v2.0, 2015-04-04, for [#304](https://github.com/winlinvip/simple-rtmp-server/issues/304), rewrite annexb mux for ts, refer to apple sample. 2.0.157.
@@ -618,6 +618,15 @@ vhost with-hls.srs.com { @@ -618,6 +618,15 @@ vhost with-hls.srs.com {
618 # for the hls http callback, @see http_hooks.on_hls of vhost hooks.callback.srs.com 618 # for the hls http callback, @see http_hooks.on_hls of vhost hooks.callback.srs.com
619 # @read https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_DeliveryHLS#http-callback 619 # @read https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_DeliveryHLS#http-callback
620 # @read https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_DeliveryHLS#http-callback 620 # @read https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_DeliveryHLS#http-callback
  621 +
  622 + # on_hls_notify, never config in here, should config in http_hooks.
  623 + # we support the variables to generate the notify url:
  624 + # [app], replace with the app.
  625 + # [stream], replace with the stream.
  626 + # [ts_url], replace with the ts url.
  627 + # for the hls http callback, @see http_hooks.on_hls_notify of vhost hooks.callback.srs.com
  628 + # @read https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_DeliveryHLS#on-hls-notify
  629 + # @read https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_DeliveryHLS#on-hls-notify
621 } 630 }
622 } 631 }
623 # the vhost with hls disabled. 632 # the vhost with hls disabled.
@@ -768,6 +777,14 @@ vhost hooks.callback.srs.com { @@ -768,6 +777,14 @@ vhost hooks.callback.srs.com {
768 # an int value specifies the error code(0 corresponding to success): 777 # an int value specifies the error code(0 corresponding to success):
769 # 0 778 # 0
770 on_hls http://127.0.0.1:8085/api/v1/hls http://localhost:8085/api/v1/hls; 779 on_hls http://127.0.0.1:8085/api/v1/hls http://localhost:8085/api/v1/hls;
  780 + # when srs reap a ts file of hls, call this hook,
  781 + # used to push file to cdn network, by get the ts file from cdn network.
  782 + # so we use HTTP GET and use the variable following:
  783 + # [app], replace with the app.
  784 + # [stream], replace with the stream.
  785 + # [ts_url], replace with the ts url.
  786 + # ignore any return data of server.
  787 + on_hls_notify http://127.0.0.1:8085/api/v1/hls/[app]/[stream][ts_url];
771 } 788 }
772 } 789 }
773 790
@@ -314,10 +314,24 @@ handle the hls requests: hls stream. @@ -314,10 +314,24 @@ handle the hls requests: hls stream.
314 class RESTHls(object): 314 class RESTHls(object):
315 exposed = True 315 exposed = True
316 316
317 - def GET(self): 317 + '''
  318 + for SRS hook: on_hls_notify
  319 + on_hls_notify:
  320 + when srs reap a ts file of hls, call this hook,
  321 + used to push file to cdn network, by get the ts file from cdn network.
  322 + so we use HTTP GET and use the variable following:
  323 + [app], replace with the app.
  324 + [stream], replace with the stream.
  325 + [ts_url], replace with the ts url.
  326 + ignore any return data of server.
  327 + '''
  328 + def GET(self, *args, **kwargs):
318 enable_crossdomain() 329 enable_crossdomain()
319 330
320 - hls = {} 331 + hls = {
  332 + "args": args,
  333 + "kwargs": kwargs
  334 + }
321 return json.dumps(hls) 335 return json.dumps(hls)
322 336
323 ''' 337 '''
@@ -1499,7 +1499,7 @@ int SrsConfig::check_config() @@ -1499,7 +1499,7 @@ int SrsConfig::check_config()
1499 string m = conf->at(j)->name.c_str(); 1499 string m = conf->at(j)->name.c_str();
1500 if (m != "enabled" && m != "on_connect" && m != "on_close" && m != "on_publish" 1500 if (m != "enabled" && m != "on_connect" && m != "on_close" && m != "on_publish"
1501 && m != "on_unpublish" && m != "on_play" && m != "on_stop" 1501 && m != "on_unpublish" && m != "on_play" && m != "on_stop"
1502 - && m != "on_dvr" && m != "on_hls" 1502 + && m != "on_dvr" && m != "on_hls" && m != "on_hls_notify"
1503 ) { 1503 ) {
1504 ret = ERROR_SYSTEM_CONFIG_INVALID; 1504 ret = ERROR_SYSTEM_CONFIG_INVALID;
1505 srs_error("unsupported vhost http_hooks directive %s, ret=%d", m.c_str(), ret); 1505 srs_error("unsupported vhost http_hooks directive %s, ret=%d", m.c_str(), ret);
@@ -2429,6 +2429,17 @@ SrsConfDirective* SrsConfig::get_vhost_on_hls(string vhost) @@ -2429,6 +2429,17 @@ SrsConfDirective* SrsConfig::get_vhost_on_hls(string vhost)
2429 return conf->get("on_hls"); 2429 return conf->get("on_hls");
2430 } 2430 }
2431 2431
  2432 +SrsConfDirective* SrsConfig::get_vhost_on_hls_notify(string vhost)
  2433 +{
  2434 + SrsConfDirective* conf = get_vhost_http_hooks(vhost);
  2435 +
  2436 + if (!conf) {
  2437 + return NULL;
  2438 + }
  2439 +
  2440 + return conf->get("on_hls_notify");
  2441 +}
  2442 +
2432 bool SrsConfig::get_bw_check_enabled(string vhost) 2443 bool SrsConfig::get_bw_check_enabled(string vhost)
2433 { 2444 {
2434 SrsConfDirective* conf = get_vhost(vhost); 2445 SrsConfDirective* conf = get_vhost(vhost);
@@ -646,6 +646,11 @@ public: @@ -646,6 +646,11 @@ public:
646 * @return the on_hls callback directive, the args is the url to callback. 646 * @return the on_hls callback directive, the args is the url to callback.
647 */ 647 */
648 virtual SrsConfDirective* get_vhost_on_hls(std::string vhost); 648 virtual SrsConfDirective* get_vhost_on_hls(std::string vhost);
  649 + /**
  650 + * get the on_hls_notify callbacks of vhost.
  651 + * @return the on_hls_notify callback directive, the args is the url to callback.
  652 + */
  653 + virtual SrsConfDirective* get_vhost_on_hls_notify(std::string vhost);
649 // bwct(bandwidth check tool) section 654 // bwct(bandwidth check tool) section
650 public: 655 public:
651 /** 656 /**
@@ -213,9 +213,49 @@ int SrsDvrAsyncCallOnHls::call() @@ -213,9 +213,49 @@ int SrsDvrAsyncCallOnHls::call()
213 213
214 string SrsDvrAsyncCallOnHls::to_string() 214 string SrsDvrAsyncCallOnHls::to_string()
215 { 215 {
216 - std::stringstream ss;  
217 - ss << "vhost=" << req->vhost << ", file=" << path;  
218 - return ss.str(); 216 + return "on_hls: " + path;
  217 +}
  218 +
  219 +SrsDvrAsyncCallOnHlsNotify::SrsDvrAsyncCallOnHlsNotify(SrsRequest* r, string u)
  220 +{
  221 + req = r;
  222 + ts_url = u;
  223 +}
  224 +
  225 +SrsDvrAsyncCallOnHlsNotify::~SrsDvrAsyncCallOnHlsNotify()
  226 +{
  227 +}
  228 +
  229 +int SrsDvrAsyncCallOnHlsNotify::call()
  230 +{
  231 + int ret = ERROR_SUCCESS;
  232 +
  233 +#ifdef SRS_AUTO_HTTP_CALLBACK
  234 + // http callback for on_hls_notify in config.
  235 + if (_srs_config->get_vhost_http_hooks_enabled(req->vhost)) {
  236 + // HTTP: on_hls
  237 + SrsConfDirective* on_hls = _srs_config->get_vhost_on_hls_notify(req->vhost);
  238 + if (!on_hls) {
  239 + srs_info("ignore the empty http callback: on_hls_notify");
  240 + return ret;
  241 + }
  242 +
  243 + for (int i = 0; i < (int)on_hls->args.size(); i++) {
  244 + std::string url = on_hls->args.at(i);
  245 + if ((ret = SrsHttpHooks::on_hls_notify(url, req, ts_url)) != ERROR_SUCCESS) {
  246 + srs_error("hook client on_hls_notify failed. url=%s, ret=%d", url.c_str(), ret);
  247 + return ret;
  248 + }
  249 + }
  250 + }
  251 +#endif
  252 +
  253 + return ret;
  254 +}
  255 +
  256 +string SrsDvrAsyncCallOnHlsNotify::to_string()
  257 +{
  258 + return "on_hls_notify: " + ts_url;
219 } 259 }
220 260
221 SrsHlsMuxer::SrsHlsMuxer() 261 SrsHlsMuxer::SrsHlsMuxer()
@@ -414,19 +454,23 @@ int SrsHlsMuxer::segment_open(int64_t segment_start_dts) @@ -414,19 +454,23 @@ int SrsHlsMuxer::segment_open(int64_t segment_start_dts)
414 // when reap ts, adjust the deviation. 454 // when reap ts, adjust the deviation.
415 deviation_ts = (int)(accept_floor_ts - current_floor_ts); 455 deviation_ts = (int)(accept_floor_ts - current_floor_ts);
416 456
417 - // we always ensure the piece is increase one by one.  
418 - std::stringstream ts_floor;  
419 - ts_floor << accept_floor_ts;  
420 - ts_file = srs_string_replace(ts_file, "[timestamp]", ts_floor.str());  
421 -  
422 // dup/jmp detect for ts in floor mode. 457 // dup/jmp detect for ts in floor mode.
423 if (previous_floor_ts && previous_floor_ts != current_floor_ts - 1) { 458 if (previous_floor_ts && previous_floor_ts != current_floor_ts - 1) {
424 srs_warn("hls: dup or jmp for floor ts, previous=%"PRId64", current=%"PRId64", accept=%"PRId64", deviation=%d", 459 srs_warn("hls: dup or jmp for floor ts, previous=%"PRId64", current=%"PRId64", accept=%"PRId64", deviation=%d",
425 previous_floor_ts, current_floor_ts, accept_floor_ts, deviation_ts); 460 previous_floor_ts, current_floor_ts, accept_floor_ts, deviation_ts);
426 } 461 }
427 previous_floor_ts = current_floor_ts; 462 previous_floor_ts = current_floor_ts;
  463 +
  464 + // we always ensure the piece is increase one by one.
  465 + std::stringstream ts_floor;
  466 + ts_floor << accept_floor_ts;
  467 + ts_file = srs_string_replace(ts_file, "[timestamp]", ts_floor.str());
  468 +
  469 + // TODO: FIMXE: we must use the accept ts floor time to generate the hour variable.
  470 + ts_file = srs_path_build_timestamp(ts_file);
  471 + } else {
  472 + ts_file = srs_path_build_timestamp(ts_file);
428 } 473 }
429 - ts_file = srs_path_build_timestamp(ts_file);  
430 if (true) { 474 if (true) {
431 std::stringstream ss; 475 std::stringstream ss;
432 ss << current->sequence_no; 476 ss << current->sequence_no;
@@ -593,6 +637,11 @@ int SrsHlsMuxer::segment_close(string log_desc) @@ -593,6 +637,11 @@ int SrsHlsMuxer::segment_close(string log_desc)
593 if ((ret = async->call(new SrsDvrAsyncCallOnHls(req, current->full_path, current->sequence_no, current->duration))) != ERROR_SUCCESS) { 637 if ((ret = async->call(new SrsDvrAsyncCallOnHls(req, current->full_path, current->sequence_no, current->duration))) != ERROR_SUCCESS) {
594 return ret; 638 return ret;
595 } 639 }
  640 +
  641 + // use async to call the http hooks, for it will cause thread switch.
  642 + if ((ret = async->call(new SrsDvrAsyncCallOnHlsNotify(req, current->uri))) != ERROR_SUCCESS) {
  643 + return ret;
  644 + }
596 645
597 srs_info("%s reap ts segment, sequence_no=%d, uri=%s, duration=%.2f, start=%"PRId64, 646 srs_info("%s reap ts segment, sequence_no=%d, uri=%s, duration=%.2f, start=%"PRId64,
598 log_desc.c_str(), current->sequence_no, current->uri.c_str(), current->duration, 647 log_desc.c_str(), current->sequence_no, current->uri.c_str(), current->duration,
@@ -157,7 +157,7 @@ public: @@ -157,7 +157,7 @@ public:
157 }; 157 };
158 158
159 /** 159 /**
160 - * the dvr async call. 160 + * the hls async call: on_hls
161 */ 161 */
162 class SrsDvrAsyncCallOnHls : public ISrsDvrAsyncCall 162 class SrsDvrAsyncCallOnHls : public ISrsDvrAsyncCall
163 { 163 {
@@ -175,6 +175,22 @@ public: @@ -175,6 +175,22 @@ public:
175 }; 175 };
176 176
177 /** 177 /**
  178 + * the hls async call: on_hls_notify
  179 + */
  180 +class SrsDvrAsyncCallOnHlsNotify : public ISrsDvrAsyncCall
  181 +{
  182 +private:
  183 + std::string ts_url;
  184 + SrsRequest* req;
  185 +public:
  186 + SrsDvrAsyncCallOnHlsNotify(SrsRequest* r, std::string u);
  187 + virtual ~SrsDvrAsyncCallOnHlsNotify();
  188 +public:
  189 + virtual int call();
  190 + virtual std::string to_string();
  191 +};
  192 +
  193 +/**
178 * muxer the HLS stream(m3u8 and ts files). 194 * muxer the HLS stream(m3u8 and ts files).
179 * generally, the m3u8 muxer only provides methods to open/close segments, 195 * generally, the m3u8 muxer only provides methods to open/close segments,
180 * to flush video/audio, without any mechenisms. 196 * to flush video/audio, without any mechenisms.
@@ -37,6 +37,7 @@ using namespace std; @@ -37,6 +37,7 @@ using namespace std;
37 #include <srs_app_http_client.hpp> 37 #include <srs_app_http_client.hpp>
38 #include <srs_core_autofree.hpp> 38 #include <srs_core_autofree.hpp>
39 #include <srs_app_config.hpp> 39 #include <srs_app_config.hpp>
  40 +#include <srs_kernel_utility.hpp>
40 41
41 #define SRS_HTTP_RESPONSE_OK SRS_XSTR(ERROR_SUCCESS) 42 #define SRS_HTTP_RESPONSE_OK SRS_XSTR(ERROR_SUCCESS)
42 43
@@ -325,6 +326,55 @@ int SrsHttpHooks::on_hls(string url, SrsRequest* req, string file, int sn, doubl @@ -325,6 +326,55 @@ int SrsHttpHooks::on_hls(string url, SrsRequest* req, string file, int sn, doubl
325 return ret; 326 return ret;
326 } 327 }
327 328
  329 +int SrsHttpHooks::on_hls_notify(std::string url, SrsRequest* req, std::string ts_url)
  330 +{
  331 + int ret = ERROR_SUCCESS;
  332 +
  333 + int client_id = _srs_context->get_id();
  334 + std::string cwd = _srs_config->cwd();
  335 +
  336 + if (srs_string_starts_with(ts_url, "http://") || srs_string_starts_with(ts_url, "https://")) {
  337 + url = ts_url;
  338 + }
  339 +
  340 + url = srs_string_replace(url, "[app]", req->app);
  341 + url = srs_string_replace(url, "[stream]", req->stream);
  342 + url = srs_string_replace(url, "[ts_url]", ts_url);
  343 +
  344 + SrsHttpUri uri;
  345 + if ((ret = uri.initialize(url)) != ERROR_SUCCESS) {
  346 + srs_error("http: post failed. url=%s, ret=%d", url.c_str(), ret);
  347 + return ret;
  348 + }
  349 +
  350 + SrsHttpClient http;
  351 + if ((ret = http.initialize(uri.get_host(), uri.get_port())) != ERROR_SUCCESS) {
  352 + return ret;
  353 + }
  354 +
  355 + SrsHttpMessage* msg = NULL;
  356 + if ((ret = http.get(uri.get_path(), "", &msg)) != ERROR_SUCCESS) {
  357 + return ret;
  358 + }
  359 + SrsAutoFree(SrsHttpMessage, msg);
  360 +
  361 + ISrsHttpResponseReader* br = msg->body_reader();
  362 + while (!br->eof()) {
  363 + std::string data;
  364 + if ((ret = br->read(data)) != ERROR_SUCCESS) {
  365 + break;
  366 + }
  367 + }
  368 +
  369 + srs_trace("http hook on_hls_notify success. client_id=%d, url=%s, code=%d, ret=%d",
  370 + client_id, url.c_str(), msg->status_code(), ret);
  371 +
  372 + // ignore any error for on_hls_notify.
  373 + ret = ERROR_SUCCESS;
  374 +
  375 + return ret;
  376 +}
  377 +
328 int SrsHttpHooks::do_post(std::string url, std::string req, int& code, string& res) 378 int SrsHttpHooks::do_post(std::string url, std::string req, int& code, string& res)
329 { 379 {
330 int ret = ERROR_SUCCESS; 380 int ret = ERROR_SUCCESS;
@@ -105,6 +105,13 @@ public: @@ -105,6 +105,13 @@ public:
105 * @param duration the segment duration in seconds. 105 * @param duration the segment duration in seconds.
106 */ 106 */
107 static int on_hls(std::string url, SrsRequest* req, std::string file, int sn, double duration); 107 static int on_hls(std::string url, SrsRequest* req, std::string file, int sn, double duration);
  108 + /**
  109 + * when hls reap segment, callback.
  110 + * @param url the api server url, to process the event.
  111 + * ignore if empty.
  112 + * @param ts_url the ts uri, used to replace the variable [ts_url] in url.
  113 + */
  114 + static int on_hls_notify(std::string url, SrsRequest* req, std::string ts_url);
108 private: 115 private:
109 static int do_post(std::string url, std::string req, int& code, std::string& res); 116 static int do_post(std::string url, std::string req, int& code, std::string& res);
110 }; 117 };