From d6cbd277cc804207edae1a019a14b04f2dba1aa5 Mon Sep 17 00:00:00 2001 From: chundonglinlin Date: Tue, 28 Jun 2022 21:20:18 +0800 Subject: [PATCH] HLS: support load exists m3u8, to continue publish stream. --- trunk/conf/hls.conf | 1 + trunk/src/app/srs_app_config.cpp | 69 ++++++++++++++++++++------------ trunk/src/app/srs_app_config.hpp | 4 ++ trunk/src/app/srs_app_hls.cpp | 35 ++++++++++++++++ trunk/src/app/srs_app_hls.hpp | 3 ++ 5 files changed, 86 insertions(+), 26 deletions(-) diff --git a/trunk/conf/hls.conf b/trunk/conf/hls.conf index 91bff6ee7..74ee6944c 100644 --- a/trunk/conf/hls.conf +++ b/trunk/conf/hls.conf @@ -17,5 +17,6 @@ vhost __defaultVhost__ { hls_path ./objs/nginx/html; hls_fragment 10; hls_window 60; + hls_continuous on; } } diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index 5fb625667..8e3ccd419 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -540,7 +540,7 @@ srs_error_t srs_config_transform_vhost(SrsConfDirective* root) // SRS3+: // vhost { play { shadow; } } if (n == "time_jitter" || n == "mix_correct" || n == "atc" || n == "atc_auto" - || n == "mw_latency" || n == "gop_cache" || n == "gop_cache_max_frames" + || n == "mw_latency" || n == "gop_cache" || n == "gop_cache_max_frames" || n == "queue_length" || n == "send_min_interval" || n == "reduce_sequence_header") { it = dir->directives.erase(it); @@ -1553,7 +1553,7 @@ srs_error_t SrsConfig::reload_vhost(SrsConfDirective* old_root) } srs_trace("vhost %s reload publish success.", vhost.c_str()); } - + // transcode, many per vhost. if ((err = reload_transcode(new_vhost, old_vhost)) != srs_success) { return srs_error_wrap(err, "reload transcode"); @@ -1596,14 +1596,14 @@ srs_error_t SrsConfig::reload_conf(SrsConfig* conf) return srs_error_wrap(err, "listen"); } } - + // merge config: max_connections if (!srs_directive_equals(root->get("max_connections"), old_root->get("max_connections"))) { if ((err = do_reload_max_connections()) != srs_success) { return srs_error_wrap(err, "max connections");; } } - + // merge config: pithy_print_ms if (!srs_directive_equals(root->get("pithy_print_ms"), old_root->get("pithy_print_ms"))) { if ((err = do_reload_pithy_print_ms()) != srs_success) { @@ -2609,7 +2609,7 @@ srs_error_t SrsConfig::check_normal_config() && m != "hls_storage" && m != "hls_mount" && m != "hls_td_ratio" && m != "hls_aof_ratio" && m != "hls_acodec" && m != "hls_vcodec" && m != "hls_m3u8_file" && m != "hls_ts_file" && m != "hls_ts_floor" && m != "hls_cleanup" && m != "hls_nb_notify" && m != "hls_wait_keyframe" && m != "hls_dispose" && m != "hls_keys" && m != "hls_fragments_per_key" && m != "hls_key_file" - && m != "hls_key_file_path" && m != "hls_key_url" && m != "hls_dts_directly" && m != "hls_ctx" && m != "hls_ts_ctx") { + && m != "hls_key_file_path" && m != "hls_key_url" && m != "hls_dts_directly" && m != "hls_ctx" && m != "hls_ts_ctx" && m != "hls_continuous") { return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal vhost.hls.%s of %s", m.c_str(), vhost->arg0().c_str()); } @@ -2828,7 +2828,7 @@ srs_error_t SrsConfig::parse_buffer(SrsConfigBuffer* buffer) if ((err = root->parse(buffer, this)) != srs_success) { return srs_error_wrap(err, "root parse"); } - + return err; } @@ -2988,7 +2988,7 @@ vector SrsConfig::get_listens() if (!srs_getenv("srs.listen").empty()) { // SRS_LISTEN return srs_string_split(srs_getenv("srs.listen"), " "); } - + SrsConfDirective* conf = root->get("listen"); if (!conf) { return ports; @@ -4747,7 +4747,7 @@ int SrsConfig::get_gop_cache_max_frames(string vhost) if (!conf || conf->arg0().empty()) { return DEFAULT; } - + return ::atoi(conf->arg0().c_str()); } @@ -5372,12 +5372,12 @@ srs_utime_t SrsConfig::get_publish_kickoff_for_idle(SrsConfDirective* vhost) SRS_OVERWRITE_BY_ENV_FLOAT_SECONDS("srs.vhost.publish.kickoff_for_idle"); // SRS_VHOST_PUBLISH_KICKOFF_FOR_IDLE static srs_utime_t DEFAULT = 0 * SRS_UTIME_SECONDS; - + SrsConfDirective* conf = vhost; if (!conf) { return DEFAULT; } - + conf = conf->get("publish"); if (!conf) { return DEFAULT; @@ -5387,7 +5387,7 @@ srs_utime_t SrsConfig::get_publish_kickoff_for_idle(SrsConfDirective* vhost) if (!conf || conf->arg0().empty()) { return DEFAULT; } - + return (srs_utime_t)(::atof(conf->arg0().c_str()) * SRS_UTIME_SECONDS); } @@ -6683,17 +6683,17 @@ int SrsConfig::get_dash_window_size(std::string vhost) SRS_OVERWRITE_BY_ENV_FLOAT_SECONDS("srs.vhost.dash.dash_window_size"); // SRS_VHOST_DASH_DASH_WINDOW_SIZE static int DEFAULT = 5; - + SrsConfDirective* conf = get_dash(vhost); if (!conf) { return DEFAULT; } - + conf = conf->get("dash_window_size"); if (!conf || conf->arg0().empty()) { return DEFAULT; } - + return ::atoi(conf->arg0().c_str()); } @@ -6702,17 +6702,17 @@ bool SrsConfig::get_dash_cleanup(std::string vhost) SRS_OVERWRITE_BY_ENV_BOOL2("srs.vhost.dash.dash_cleanup"); // SRS_VHOST_DASH_DASH_CLEANUP static bool DEFAULT = true; - + SrsConfDirective* conf = get_dash(vhost); if (!conf) { return DEFAULT; } - + conf = conf->get("dash_cleanup"); if (!conf || conf->arg0().empty()) { return DEFAULT; } - + return SRS_CONF_PERFER_TRUE(conf->arg0()); } @@ -6721,17 +6721,17 @@ srs_utime_t SrsConfig::get_dash_dispose(std::string vhost) SRS_OVERWRITE_BY_ENV_SECONDS("srs.vhost.dash.dash_dispose"); // SRS_VHOST_DASH_DASH_DISPOSE static srs_utime_t DEFAULT = 0; - + SrsConfDirective* conf = get_dash(vhost); if (!conf) { return DEFAULT; } - + conf = conf->get("dash_dispose"); if (!conf || conf->arg0().empty()) { return DEFAULT; } - + return (srs_utime_t)(::atoi(conf->arg0().c_str()) * SRS_UTIME_SECONDS); } @@ -7234,6 +7234,23 @@ string SrsConfig::get_hls_key_url(std::string vhost) return conf->arg0(); } +bool SrsConfig::get_hls_continuous(string vhost) +{ + static bool DEFAULT = true; + + SrsConfDirective* conf = get_hls(vhost); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("hls_continuous"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + return SRS_CONF_PERFER_TRUE(conf->arg0()); +} + SrsConfDirective *SrsConfig::get_hds(const string &vhost) { SrsConfDirective* conf = get_vhost(vhost); @@ -7619,22 +7636,22 @@ bool SrsConfig::get_http_api_auth_enabled() SRS_OVERWRITE_BY_ENV_BOOL("srs.http_api.auth.enabled"); // SRS_HTTP_API_AUTH_ENABLED static bool DEFAULT = false; - + SrsConfDirective* conf = root->get("http_api"); if (!conf) { return DEFAULT; } - + conf = conf->get("auth"); if (!conf) { return DEFAULT; } - + conf = conf->get("enabled"); if (!conf || conf->arg0().empty()) { return DEFAULT; } - + return SRS_CONF_PERFER_FALSE(conf->arg0()); } @@ -8043,7 +8060,7 @@ string SrsConfig::get_srto_passphrase() if (!conf) { return DEFAULT; } - + conf = conf->get("passphrase"); if (!conf || conf->arg0().empty()) { return DEFAULT; @@ -8060,7 +8077,7 @@ int SrsConfig::get_srto_pbkeylen() if (!conf) { return DEFAULT; } - + conf = conf->get("pbkeylen"); if (!conf || conf->arg0().empty()) { return DEFAULT; diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index 469c45925..6fbe95621 100644 --- a/trunk/src/app/srs_app_config.hpp +++ b/trunk/src/app/srs_app_config.hpp @@ -967,6 +967,10 @@ public: virtual bool get_hls_ctx_enabled(std::string vhost); // Whether enable session for ts file. virtual bool get_hls_ts_ctx_enabled(std::string vhost); + // Toggles HLS continuous mode. + // In this mode HLS sequence number is started from where it stopped last time. + // Old fragments are kept. Default is on. + virtual bool get_hls_continuous(std::string vhost); // hds section private: // Get the hds directive of vhost. diff --git a/trunk/src/app/srs_app_hls.cpp b/trunk/src/app/srs_app_hls.cpp index 268f48b3c..3ae959897 100644 --- a/trunk/src/app/srs_app_hls.cpp +++ b/trunk/src/app/srs_app_hls.cpp @@ -383,6 +383,36 @@ srs_error_t SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix, return err; } +srs_error_t SrsHlsMuxer::restore_stream() +{ + srs_error_t err = srs_success; + + // exist the m3u8 file. + if (!srs_path_exists(m3u8)) { + return err; + } + + srs_trace("hls: restore stream m3u8=%s, m3u8_url=%s", m3u8.c_str(), m3u8_url.c_str()); + + // read m3u8 + SrsFileReader fr; + if ((err = fr.open(m3u8)) != srs_success) { + return srs_error_wrap(err, "open file"); + } + + int nb_fbuf = fr.filesize(); + char* fbuf = new char[nb_fbuf]; + SrsAutoFreeA(char, fbuf); + if ((err = fr.read(fbuf, nb_fbuf, NULL)) != srs_success) { + return srs_error_wrap(err, "read data"); + } + fr.close(); + + // parse + + return err; +} + srs_error_t SrsHlsMuxer::segment_open() { srs_error_t err = srs_success; @@ -949,6 +979,7 @@ srs_error_t SrsHlsController::on_publish(SrsRequest* req) // TODO: FIXME: support load exists m3u8, to continue publish stream. // for the HLS donot requires the EXT-X-MEDIA-SEQUENCE be monotonically increase. + bool continuous = _srs_config->get_hls_continuous(vhost); if ((err = muxer->on_publish(req)) != srs_success) { return srs_error_wrap(err, "muxer publish"); @@ -959,6 +990,10 @@ srs_error_t SrsHlsController::on_publish(SrsRequest* req) hls_key_file, hls_key_file_path, hls_key_url)) != srs_success ) { return srs_error_wrap(err, "hls: update config"); } + + if (continuous && (err = muxer->restore_stream()) != srs_success ) { + return srs_error_wrap(err, "hls: restore stream"); + } if ((err = muxer->segment_open()) != srs_success) { return srs_error_wrap(err, "hls: segment open"); diff --git a/trunk/src/app/srs_app_hls.hpp b/trunk/src/app/srs_app_hls.hpp index 1f4a94296..ad1273513 100644 --- a/trunk/src/app/srs_app_hls.hpp +++ b/trunk/src/app/srs_app_hls.hpp @@ -211,6 +211,9 @@ private: virtual srs_error_t write_hls_key(); virtual srs_error_t refresh_m3u8(); virtual srs_error_t _refresh_m3u8(std::string m3u8_file); +public: + // HLS continuous mode. + srs_error_t restore_stream(); }; // The hls stream cache,