正在显示
9 个修改的文件
包含
183 行增加
和
13 行删除
| @@ -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 | }; |
-
请 注册 或 登录 后发表评论