/* The MIT License (MIT) Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include // file operations. #include #include #include #include #include #include using namespace std; #include #include #include #include #include #include #include using namespace _srs_internal; #define SRS_WIKI_URL_LOG "https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLog" // '\n' #define __LF (char)0x0a // '\r' #define __CR (char)0x0d bool is_common_space(char ch) { return (ch == ' ' || ch == '\t' || ch == __CR || ch == __LF); } SrsConfDirective::SrsConfDirective() { } SrsConfDirective::~SrsConfDirective() { std::vector::iterator it; for (it = directives.begin(); it != directives.end(); ++it) { SrsConfDirective* directive = *it; srs_freep(directive); } directives.clear(); } string SrsConfDirective::arg0() { if (args.size() > 0) { return args.at(0); } return ""; } string SrsConfDirective::arg1() { if (args.size() > 1) { return args.at(1); } return ""; } string SrsConfDirective::arg2() { if (args.size() > 2) { return args.at(2); } return ""; } SrsConfDirective* SrsConfDirective::at(int index) { srs_assert(index < (int)directives.size()); return directives.at(index); } SrsConfDirective* SrsConfDirective::get(string _name) { std::vector::iterator it; for (it = directives.begin(); it != directives.end(); ++it) { SrsConfDirective* directive = *it; if (directive->name == _name) { return directive; } } return NULL; } SrsConfDirective* SrsConfDirective::get(string _name, string _arg0) { std::vector::iterator it; for (it = directives.begin(); it != directives.end(); ++it) { SrsConfDirective* directive = *it; if (directive->name == _name && directive->arg0() == _arg0) { return directive; } } return NULL; } bool SrsConfDirective::is_vhost() { return name == "vhost"; } int SrsConfDirective::parse(SrsConfigBuffer* buffer) { return parse_conf(buffer, parse_file); } // see: ngx_conf_parse int SrsConfDirective::parse_conf(SrsConfigBuffer* buffer, SrsDirectiveType type) { int ret = ERROR_SUCCESS; while (true) { std::vector args; int line_start = 0; ret = read_token(buffer, args, line_start); /** * ret maybe: * ERROR_SYSTEM_CONFIG_INVALID error. * ERROR_SYSTEM_CONFIG_DIRECTIVE directive terminated by ';' found * ERROR_SYSTEM_CONFIG_BLOCK_START token terminated by '{' found * ERROR_SYSTEM_CONFIG_BLOCK_END the '}' found * ERROR_SYSTEM_CONFIG_EOF the config file is done */ if (ret == ERROR_SYSTEM_CONFIG_INVALID) { return ret; } if (ret == ERROR_SYSTEM_CONFIG_BLOCK_END) { if (type != parse_block) { srs_error("line %d: unexpected \"}\", ret=%d", buffer->line, ret); return ret; } return ERROR_SUCCESS; } if (ret == ERROR_SYSTEM_CONFIG_EOF) { if (type == parse_block) { srs_error("line %d: unexpected end of file, expecting \"}\", ret=%d", conf_line, ret); return ret; } return ERROR_SUCCESS; } if (args.empty()) { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("line %d: empty directive. ret=%d", conf_line, ret); return ret; } // build directive tree. SrsConfDirective* directive = new SrsConfDirective(); directive->conf_line = line_start; directive->name = args[0]; args.erase(args.begin()); directive->args.swap(args); directives.push_back(directive); if (ret == ERROR_SYSTEM_CONFIG_BLOCK_START) { if ((ret = directive->parse_conf(buffer, parse_block)) != ERROR_SUCCESS) { return ret; } } } return ret; } // see: ngx_conf_read_token int SrsConfDirective::read_token(SrsConfigBuffer* buffer, vector& args, int& line_start) { int ret = ERROR_SUCCESS; char* pstart = buffer->pos; bool sharp_comment = false; bool d_quoted = false; bool s_quoted = false; bool need_space = false; bool last_space = true; while (true) { if (buffer->empty()) { ret = ERROR_SYSTEM_CONFIG_EOF; if (!args.empty() || !last_space) { srs_error("line %d: unexpected end of file, expecting ; or \"}\"", buffer->line); return ERROR_SYSTEM_CONFIG_INVALID; } srs_trace("config parse complete"); return ret; } char ch = *buffer->pos++; if (ch == __LF) { buffer->line++; sharp_comment = false; } if (sharp_comment) { continue; } if (need_space) { if (is_common_space(ch)) { last_space = true; need_space = false; continue; } if (ch == ';') { return ERROR_SYSTEM_CONFIG_DIRECTIVE; } if (ch == '{') { return ERROR_SYSTEM_CONFIG_BLOCK_START; } srs_error("line %d: unexpected '%c'", buffer->line, ch); return ERROR_SYSTEM_CONFIG_INVALID; } // last charecter is space. if (last_space) { if (is_common_space(ch)) { continue; } pstart = buffer->pos - 1; switch (ch) { case ';': if (args.size() == 0) { srs_error("line %d: unexpected ';'", buffer->line); return ERROR_SYSTEM_CONFIG_INVALID; } return ERROR_SYSTEM_CONFIG_DIRECTIVE; case '{': if (args.size() == 0) { srs_error("line %d: unexpected '{'", buffer->line); return ERROR_SYSTEM_CONFIG_INVALID; } return ERROR_SYSTEM_CONFIG_BLOCK_START; case '}': if (args.size() != 0) { srs_error("line %d: unexpected '}'", buffer->line); return ERROR_SYSTEM_CONFIG_INVALID; } return ERROR_SYSTEM_CONFIG_BLOCK_END; case '#': sharp_comment = 1; continue; case '"': pstart++; d_quoted = true; last_space = 0; continue; case '\'': pstart++; s_quoted = true; last_space = 0; continue; default: last_space = 0; continue; } } else { // last charecter is not space if (line_start == 0) { line_start = buffer->line; } bool found = false; if (d_quoted) { if (ch == '"') { d_quoted = false; need_space = true; found = true; } } else if (s_quoted) { if (ch == '\'') { s_quoted = false; need_space = true; found = true; } } else if (is_common_space(ch) || ch == ';' || ch == '{') { last_space = true; found = 1; } if (found) { int len = buffer->pos - pstart; char* aword = new char[len]; memcpy(aword, pstart, len); aword[len - 1] = 0; string word_str = aword; if (!word_str.empty()) { args.push_back(word_str); } srs_freep(aword); if (ch == ';') { return ERROR_SYSTEM_CONFIG_DIRECTIVE; } if (ch == '{') { return ERROR_SYSTEM_CONFIG_BLOCK_START; } } } } return ret; } SrsConfig::SrsConfig() { show_help = false; show_version = false; test_conf = false; root = new SrsConfDirective(); root->conf_line = 0; root->name = "root"; } SrsConfig::~SrsConfig() { srs_freep(root); } void SrsConfig::subscribe(ISrsReloadHandler* handler) { std::vector::iterator it; it = std::find(subscribes.begin(), subscribes.end(), handler); if (it != subscribes.end()) { return; } subscribes.push_back(handler); } void SrsConfig::unsubscribe(ISrsReloadHandler* handler) { std::vector::iterator it; it = std::find(subscribes.begin(), subscribes.end(), handler); if (it == subscribes.end()) { return; } subscribes.erase(it); } int SrsConfig::reload() { int ret = ERROR_SUCCESS; SrsConfig conf; if ((ret = conf.parse_file(config_file.c_str())) != ERROR_SUCCESS) { srs_error("ignore config reloader parse file failed. ret=%d", ret); ret = ERROR_SUCCESS; return ret; } srs_info("config reloader parse file success."); if ((ret = conf.check_config()) != ERROR_SUCCESS) { srs_error("ignore config reloader check config failed. ret=%d", ret); ret = ERROR_SUCCESS; return ret; } return reload_conf(&conf); } int SrsConfig::reload_conf(SrsConfig* conf) { int ret = ERROR_SUCCESS; SrsConfDirective* old_root = root; SrsAutoFree(SrsConfDirective, old_root); root = conf->root; conf->root = NULL; // merge config. std::vector::iterator it; // never support reload: // daemon // // always support reload without additional code: // chunk_size, ff_log_dir, max_connections, // bandcheck, http_hooks, heartbeat, // token_traverse, debug_srs_upnode // merge config: listen if (!srs_directive_equals(root->get("listen"), old_root->get("listen"))) { for (it = subscribes.begin(); it != subscribes.end(); ++it) { ISrsReloadHandler* subscribe = *it; if ((ret = subscribe->on_reload_listen()) != ERROR_SUCCESS) { srs_error("notify subscribes reload listen failed. ret=%d", ret); return ret; } } srs_trace("reload listen success."); } // merge config: pid if (!srs_directive_equals(root->get("pid"), old_root->get("pid"))) { for (it = subscribes.begin(); it != subscribes.end(); ++it) { ISrsReloadHandler* subscribe = *it; if ((ret = subscribe->on_reload_pid()) != ERROR_SUCCESS) { srs_error("notify subscribes reload pid failed. ret=%d", ret); return ret; } } srs_trace("reload pid success."); } // merge config: srs_log_tank if (!srs_directive_equals(root->get("srs_log_tank"), old_root->get("srs_log_tank"))) { for (it = subscribes.begin(); it != subscribes.end(); ++it) { ISrsReloadHandler* subscribe = *it; if ((ret = subscribe->on_reload_log_tank()) != ERROR_SUCCESS) { srs_error("notify subscribes reload srs_log_tank failed. ret=%d", ret); return ret; } } srs_trace("reload srs_log_tank success."); } // merge config: srs_log_level if (!srs_directive_equals(root->get("srs_log_level"), old_root->get("srs_log_level"))) { for (it = subscribes.begin(); it != subscribes.end(); ++it) { ISrsReloadHandler* subscribe = *it; if ((ret = subscribe->on_reload_log_level()) != ERROR_SUCCESS) { srs_error("notify subscribes reload srs_log_level failed. ret=%d", ret); return ret; } } srs_trace("reload srs_log_level success."); } // merge config: srs_log_file if (!srs_directive_equals(root->get("srs_log_file"), old_root->get("srs_log_file"))) { for (it = subscribes.begin(); it != subscribes.end(); ++it) { ISrsReloadHandler* subscribe = *it; if ((ret = subscribe->on_reload_log_file()) != ERROR_SUCCESS) { srs_error("notify subscribes reload srs_log_file failed. ret=%d", ret); return ret; } } srs_trace("reload srs_log_file success."); } // merge config: pithy_print if (!srs_directive_equals(root->get("pithy_print"), old_root->get("pithy_print"))) { for (it = subscribes.begin(); it != subscribes.end(); ++it) { ISrsReloadHandler* subscribe = *it; if ((ret = subscribe->on_reload_pithy_print()) != ERROR_SUCCESS) { srs_error("notify subscribes pithy_print listen failed. ret=%d", ret); return ret; } } srs_trace("reload pithy_print success."); } // merge config: http_api if ((ret = reload_http_api(old_root)) != ERROR_SUCCESS) { return ret; } // merge config: http_stream if ((ret = reload_http_stream(old_root)) != ERROR_SUCCESS) { return ret; } // merge config: vhost if ((ret = reload_vhost(old_root)) != ERROR_SUCCESS) { return ret; } return ret; } int SrsConfig::reload_http_api(SrsConfDirective* old_root) { int ret = ERROR_SUCCESS; // merge config. std::vector::iterator it; // state graph // old_http_api new_http_api // DISABLED => ENABLED // ENABLED => DISABLED // ENABLED => ENABLED (modified) SrsConfDirective* new_http_api = root->get("http_api"); SrsConfDirective* old_http_api = old_root->get("http_api"); // DISABLED => ENABLED if (!get_http_api_enabled(old_http_api) && get_http_api_enabled(new_http_api)) { for (it = subscribes.begin(); it != subscribes.end(); ++it) { ISrsReloadHandler* subscribe = *it; if ((ret = subscribe->on_reload_http_api_enabled()) != ERROR_SUCCESS) { srs_error("notify subscribes http_api disabled=>enabled failed. ret=%d", ret); return ret; } } srs_trace("reload disabled=>enabled http_api success."); return ret; } // ENABLED => DISABLED if (get_http_api_enabled(old_http_api) && !get_http_api_enabled(new_http_api)) { for (it = subscribes.begin(); it != subscribes.end(); ++it) { ISrsReloadHandler* subscribe = *it; if ((ret = subscribe->on_reload_http_api_disabled()) != ERROR_SUCCESS) { srs_error("notify subscribes http_api enabled=>disabled failed. ret=%d", ret); return ret; } } srs_trace("reload enabled=>disabled http_api success."); return ret; } // ENABLED => ENABLED (modified) if (get_http_api_enabled(old_http_api) && get_http_api_enabled(new_http_api) && !srs_directive_equals(old_http_api, new_http_api) ) { for (it = subscribes.begin(); it != subscribes.end(); ++it) { ISrsReloadHandler* subscribe = *it; if ((ret = subscribe->on_reload_http_api_enabled()) != ERROR_SUCCESS) { srs_error("notify subscribes http_api enabled modified failed. ret=%d", ret); return ret; } } srs_trace("reload enabled modified http_api success."); return ret; } srs_trace("reload http_api not changed success."); return ret; } int SrsConfig::reload_http_stream(SrsConfDirective* old_root) { int ret = ERROR_SUCCESS; // merge config. std::vector::iterator it; // state graph // old_http_stream new_http_stream // DISABLED => ENABLED // ENABLED => DISABLED // ENABLED => ENABLED (modified) SrsConfDirective* new_http_stream = root->get("http_stream"); SrsConfDirective* old_http_stream = old_root->get("http_stream"); // DISABLED => ENABLED if (!get_http_stream_enabled(old_http_stream) && get_http_stream_enabled(new_http_stream)) { for (it = subscribes.begin(); it != subscribes.end(); ++it) { ISrsReloadHandler* subscribe = *it; if ((ret = subscribe->on_reload_http_stream_enabled()) != ERROR_SUCCESS) { srs_error("notify subscribes http_stream disabled=>enabled failed. ret=%d", ret); return ret; } } srs_trace("reload disabled=>enabled http_stream success."); return ret; } // ENABLED => DISABLED if (get_http_stream_enabled(old_http_stream) && !get_http_stream_enabled(new_http_stream)) { for (it = subscribes.begin(); it != subscribes.end(); ++it) { ISrsReloadHandler* subscribe = *it; if ((ret = subscribe->on_reload_http_stream_disabled()) != ERROR_SUCCESS) { srs_error("notify subscribes http_stream enabled=>disabled failed. ret=%d", ret); return ret; } } srs_trace("reload enabled=>disabled http_stream success."); return ret; } // ENABLED => ENABLED (modified) if (get_http_stream_enabled(old_http_stream) && get_http_stream_enabled(new_http_stream) && !srs_directive_equals(old_http_stream, new_http_stream) ) { for (it = subscribes.begin(); it != subscribes.end(); ++it) { ISrsReloadHandler* subscribe = *it; if ((ret = subscribe->on_reload_http_stream_updated()) != ERROR_SUCCESS) { srs_error("notify subscribes http_stream enabled modified failed. ret=%d", ret); return ret; } } srs_trace("reload enabled modified http_stream success."); return ret; } srs_trace("reload http_stream not changed success."); return ret; } int SrsConfig::reload_vhost(SrsConfDirective* old_root) { int ret = ERROR_SUCCESS; // merge config. std::vector::iterator it; // state graph // old_vhost new_vhost // DISABLED => ENABLED // ENABLED => DISABLED // ENABLED => ENABLED (modified) // collect all vhost names std::vector vhosts; for (int i = 0; i < (int)root->directives.size(); i++) { SrsConfDirective* vhost = root->at(i); if (vhost->name != "vhost") { continue; } vhosts.push_back(vhost->arg0()); } for (int i = 0; i < (int)old_root->directives.size(); i++) { SrsConfDirective* vhost = old_root->at(i); if (vhost->name != "vhost") { continue; } if (root->get("vhost", vhost->arg0())) { continue; } vhosts.push_back(vhost->arg0()); } // process each vhost for (int i = 0; i < (int)vhosts.size(); i++) { std::string vhost = vhosts.at(i); SrsConfDirective* old_vhost = old_root->get("vhost", vhost); SrsConfDirective* new_vhost = root->get("vhost", vhost); // DISABLED => ENABLED if (!get_vhost_enabled(old_vhost) && get_vhost_enabled(new_vhost)) { srs_trace("vhost %s added, reload it.", vhost.c_str()); for (it = subscribes.begin(); it != subscribes.end(); ++it) { ISrsReloadHandler* subscribe = *it; if ((ret = subscribe->on_reload_vhost_added(vhost)) != ERROR_SUCCESS) { srs_error("notify subscribes added " "vhost %s failed. ret=%d", vhost.c_str(), ret); return ret; } } srs_trace("reload new vhost %s success.", vhost.c_str()); continue; } // ENABLED => DISABLED if (get_vhost_enabled(old_vhost) && !get_vhost_enabled(new_vhost)) { srs_trace("vhost %s removed, reload it.", vhost.c_str()); for (it = subscribes.begin(); it != subscribes.end(); ++it) { ISrsReloadHandler* subscribe = *it; if ((ret = subscribe->on_reload_vhost_removed(vhost)) != ERROR_SUCCESS) { srs_error("notify subscribes removed " "vhost %s failed. ret=%d", vhost.c_str(), ret); return ret; } } srs_trace("reload removed vhost %s success.", vhost.c_str()); continue; } // mode, never supports reload. // first, for the origin and edge role change is too complex. // second, the vhosts in origin device group normally are all origin, // they never change to edge sometimes. // third, the origin or upnode device can always be restart, // edge will retry and the users connected to edge are ok. // it's ok to add or remove edge/origin vhost. if (get_vhost_is_edge(old_vhost) != get_vhost_is_edge(new_vhost)) { ret = ERROR_RTMP_EDGE_RELOAD; srs_error("reload never supports mode changed. ret=%d", ret); return ret; } // ENABLED => ENABLED (modified) if (get_vhost_enabled(new_vhost) && get_vhost_enabled(old_vhost)) { srs_trace("vhost %s maybe modified, reload its detail.", vhost.c_str()); // atc, only one per vhost if (!srs_directive_equals(new_vhost->get("atc"), old_vhost->get("atc"))) { for (it = subscribes.begin(); it != subscribes.end(); ++it) { ISrsReloadHandler* subscribe = *it; if ((ret = subscribe->on_reload_vhost_atc(vhost)) != ERROR_SUCCESS) { srs_error("vhost %s notify subscribes atc failed. ret=%d", vhost.c_str(), ret); return ret; } } srs_trace("vhost %s reload atc success.", vhost.c_str()); } // gop_cache, only one per vhost if (!srs_directive_equals(new_vhost->get("gop_cache"), old_vhost->get("gop_cache"))) { for (it = subscribes.begin(); it != subscribes.end(); ++it) { ISrsReloadHandler* subscribe = *it; if ((ret = subscribe->on_reload_vhost_gop_cache(vhost)) != ERROR_SUCCESS) { srs_error("vhost %s notify subscribes gop_cache failed. ret=%d", vhost.c_str(), ret); return ret; } } srs_trace("vhost %s reload gop_cache success.", vhost.c_str()); } // queue_length, only one per vhost if (!srs_directive_equals(new_vhost->get("queue_length"), old_vhost->get("queue_length"))) { for (it = subscribes.begin(); it != subscribes.end(); ++it) { ISrsReloadHandler* subscribe = *it; if ((ret = subscribe->on_reload_vhost_queue_length(vhost)) != ERROR_SUCCESS) { srs_error("vhost %s notify subscribes queue_length failed. ret=%d", vhost.c_str(), ret); return ret; } } srs_trace("vhost %s reload queue_length success.", vhost.c_str()); } // time_jitter, only one per vhost if (!srs_directive_equals(new_vhost->get("time_jitter"), old_vhost->get("time_jitter"))) { for (it = subscribes.begin(); it != subscribes.end(); ++it) { ISrsReloadHandler* subscribe = *it; if ((ret = subscribe->on_reload_vhost_time_jitter(vhost)) != ERROR_SUCCESS) { srs_error("vhost %s notify subscribes time_jitter failed. ret=%d", vhost.c_str(), ret); return ret; } } srs_trace("vhost %s reload time_jitter success.", vhost.c_str()); } // forward, only one per vhost if (!srs_directive_equals(new_vhost->get("forward"), old_vhost->get("forward"))) { for (it = subscribes.begin(); it != subscribes.end(); ++it) { ISrsReloadHandler* subscribe = *it; if ((ret = subscribe->on_reload_vhost_forward(vhost)) != ERROR_SUCCESS) { srs_error("vhost %s notify subscribes forward failed. ret=%d", vhost.c_str(), ret); return ret; } } srs_trace("vhost %s reload forward success.", vhost.c_str()); } // hls, only one per vhost if (!srs_directive_equals(new_vhost->get("hls"), old_vhost->get("hls"))) { for (it = subscribes.begin(); it != subscribes.end(); ++it) { ISrsReloadHandler* subscribe = *it; if ((ret = subscribe->on_reload_vhost_hls(vhost)) != ERROR_SUCCESS) { srs_error("vhost %s notify subscribes hls failed. ret=%d", vhost.c_str(), ret); return ret; } } srs_trace("vhost %s reload hls success.", vhost.c_str()); } // dvr, only one per vhost if (!srs_directive_equals(new_vhost->get("dvr"), old_vhost->get("dvr"))) { for (it = subscribes.begin(); it != subscribes.end(); ++it) { ISrsReloadHandler* subscribe = *it; if ((ret = subscribe->on_reload_vhost_dvr(vhost)) != ERROR_SUCCESS) { srs_error("vhost %s notify subscribes dvr failed. ret=%d", vhost.c_str(), ret); return ret; } } srs_trace("vhost %s reload hls success.", vhost.c_str()); } // http, only one per vhost. if (!srs_directive_equals(new_vhost->get("http"), old_vhost->get("http"))) { for (it = subscribes.begin(); it != subscribes.end(); ++it) { ISrsReloadHandler* subscribe = *it; if ((ret = subscribe->on_reload_vhost_http_updated()) != ERROR_SUCCESS) { srs_error("vhost %s notify subscribes http failed. ret=%d", vhost.c_str(), ret); return ret; } } srs_trace("vhost %s reload http success.", vhost.c_str()); } // transcode, many per vhost. if ((ret = reload_transcode(new_vhost, old_vhost)) != ERROR_SUCCESS) { return ret; } // ingest, many per vhost. if ((ret = reload_ingest(new_vhost, old_vhost)) != ERROR_SUCCESS) { return ret; } continue; } srs_trace("igreno reload vhost, enabled old: %d, new: %d", get_vhost_enabled(old_vhost), get_vhost_enabled(new_vhost)); } return ret; } int SrsConfig::reload_transcode(SrsConfDirective* new_vhost, SrsConfDirective* old_vhost) { int ret = ERROR_SUCCESS; std::vector old_transcoders; for (int i = 0; i < (int)old_vhost->directives.size(); i++) { SrsConfDirective* conf = old_vhost->at(i); if (conf->name == "transcode") { old_transcoders.push_back(conf); } } std::vector new_transcoders; for (int i = 0; i < (int)new_vhost->directives.size(); i++) { SrsConfDirective* conf = new_vhost->at(i); if (conf->name == "transcode") { new_transcoders.push_back(conf); } } std::vector::iterator it; std::string vhost = new_vhost->arg0(); // to be simple: // whatever, once tiny changed of transcode, // restart all ffmpeg of vhost. bool changed = false; // discovery the removed ffmpeg. for (int i = 0; !changed && i < (int)old_transcoders.size(); i++) { SrsConfDirective* old_transcoder = old_transcoders.at(i); std::string transcoder_id = old_transcoder->arg0(); // if transcoder exists in new vhost, not removed, ignore. if (new_vhost->get("transcode", transcoder_id)) { continue; } changed = true; } // discovery the added ffmpeg. for (int i = 0; !changed && i < (int)new_transcoders.size(); i++) { SrsConfDirective* new_transcoder = new_transcoders.at(i); std::string transcoder_id = new_transcoder->arg0(); // if transcoder exists in old vhost, not added, ignore. if (old_vhost->get("transcode", transcoder_id)) { continue; } changed = true; } // for updated transcoders, restart them. for (int i = 0; !changed && i < (int)new_transcoders.size(); i++) { SrsConfDirective* new_transcoder = new_transcoders.at(i); std::string transcoder_id = new_transcoder->arg0(); SrsConfDirective* old_transcoder = old_vhost->get("transcode", transcoder_id); srs_assert(old_transcoder); if (srs_directive_equals(new_transcoder, old_transcoder)) { continue; } changed = true; } // transcode, many per vhost if (changed) { for (it = subscribes.begin(); it != subscribes.end(); ++it) { ISrsReloadHandler* subscribe = *it; if ((ret = subscribe->on_reload_vhost_transcode(vhost)) != ERROR_SUCCESS) { srs_error("vhost %s notify subscribes transcode failed. ret=%d", vhost.c_str(), ret); return ret; } } srs_trace("vhost %s reload transcode success.", vhost.c_str()); } return ret; } int SrsConfig::reload_ingest(SrsConfDirective* new_vhost, SrsConfDirective* old_vhost) { int ret = ERROR_SUCCESS; std::vector old_ingesters; for (int i = 0; i < (int)old_vhost->directives.size(); i++) { SrsConfDirective* conf = old_vhost->at(i); if (conf->name == "ingest") { old_ingesters.push_back(conf); } } std::vector new_ingesters; for (int i = 0; i < (int)new_vhost->directives.size(); i++) { SrsConfDirective* conf = new_vhost->at(i); if (conf->name == "ingest") { new_ingesters.push_back(conf); } } std::vector::iterator it; std::string vhost = new_vhost->arg0(); // for removed ingesters, stop them. for (int i = 0; i < (int)old_ingesters.size(); i++) { SrsConfDirective* old_ingester = old_ingesters.at(i); std::string ingest_id = old_ingester->arg0(); SrsConfDirective* new_ingester = new_vhost->get("ingest", ingest_id); // ENABLED => DISABLED if (get_ingest_enabled(old_ingester) && !get_ingest_enabled(new_ingester)) { // notice handler ingester removed. for (it = subscribes.begin(); it != subscribes.end(); ++it) { ISrsReloadHandler* subscribe = *it; if ((ret = subscribe->on_reload_ingest_removed(vhost, ingest_id)) != ERROR_SUCCESS) { srs_error("vhost %s notify subscribes ingest=%s removed failed. ret=%d", vhost.c_str(), ingest_id.c_str(), ret); return ret; } } srs_trace("vhost %s reload ingest=%s removed success.", vhost.c_str(), ingest_id.c_str()); } } // for added ingesters, start them. for (int i = 0; i < (int)new_ingesters.size(); i++) { SrsConfDirective* new_ingester = new_ingesters.at(i); std::string ingest_id = new_ingester->arg0(); SrsConfDirective* old_ingester = old_vhost->get("ingest", ingest_id); // DISABLED => ENABLED if (!get_ingest_enabled(old_ingester) && get_ingest_enabled(new_ingester)) { for (it = subscribes.begin(); it != subscribes.end(); ++it) { ISrsReloadHandler* subscribe = *it; if ((ret = subscribe->on_reload_ingest_added(vhost, ingest_id)) != ERROR_SUCCESS) { srs_error("vhost %s notify subscribes ingest=%s added failed. ret=%d", vhost.c_str(), ingest_id.c_str(), ret); return ret; } } srs_trace("vhost %s reload ingest=%s added success.", vhost.c_str(), ingest_id.c_str()); } } // for updated ingesters, restart them. for (int i = 0; i < (int)new_ingesters.size(); i++) { SrsConfDirective* new_ingester = new_ingesters.at(i); std::string ingest_id = new_ingester->arg0(); SrsConfDirective* old_ingester = old_vhost->get("ingest", ingest_id); // ENABLED => ENABLED if (get_ingest_enabled(old_ingester) && get_ingest_enabled(new_ingester)) { if (srs_directive_equals(new_ingester, old_ingester)) { continue; } // notice handler ingester removed. for (it = subscribes.begin(); it != subscribes.end(); ++it) { ISrsReloadHandler* subscribe = *it; if ((ret = subscribe->on_reload_ingest_updated(vhost, ingest_id)) != ERROR_SUCCESS) { srs_error("vhost %s notify subscribes ingest=%s updated failed. ret=%d", vhost.c_str(), ingest_id.c_str(), ret); return ret; } } srs_trace("vhost %s reload ingest=%s updated success.", vhost.c_str(), ingest_id.c_str()); } } srs_trace("ingest not changed for vhost=%s", vhost.c_str()); return ret; } // see: ngx_get_options int SrsConfig::parse_options(int argc, char** argv) { int ret = ERROR_SUCCESS; // argv for (int i = 0; i < argc; i++) { _argv.append(argv[i]); if (i < argc - 1) { _argv.append(" "); } } // cwd char cwd[256]; getcwd(cwd, sizeof(cwd)); _cwd = cwd; // config show_help = true; for (int i = 1; i < argc; i++) { if ((ret = parse_argv(i, argv)) != ERROR_SUCCESS) { return ret; } } if (show_help) { print_help(argv); exit(0); } if (show_version) { fprintf(stderr, "%s\n", RTMP_SIG_SRS_VERSION); exit(0); } if (config_file.empty()) { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("config file not specified, see help: %s -h, ret=%d", argv[0], ret); return ret; } ret = parse_file(config_file.c_str()); if (test_conf) { // the parse_file never check the config, // we check it when user requires check config file. if (ret == ERROR_SUCCESS) { ret = check_config(); } if (ret == ERROR_SUCCESS) { srs_trace("config file is ok"); exit(0); } else { srs_error("config file is invalid"); exit(ret); } } //////////////////////////////////////////////////////////////////////// // check log name and level //////////////////////////////////////////////////////////////////////// if (true) { std::string log_filename = this->get_log_file(); if (get_log_tank_file() && log_filename.empty()) { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("must specifies the file to write log to. ret=%d", ret); return ret; } if (get_log_tank_file()) { srs_trace("write log to file %s", log_filename.c_str()); srs_trace("you can: tailf %s", log_filename.c_str()); srs_trace("@see: %s", SRS_WIKI_URL_LOG); } else { srs_trace("write log to console"); } } return ret; } string SrsConfig::config() { return config_file; } int SrsConfig::parse_argv(int& i, char** argv) { int ret = ERROR_SUCCESS; char* p = argv[i]; if (*p++ != '-') { show_help = true; return ret; } while (*p) { switch (*p++) { case '?': case 'h': show_help = true; break; case 't': show_help = false; test_conf = true; break; case 'v': case 'V': show_help = false; show_version = true; break; case 'c': show_help = false; if (*p) { config_file = p; return ret; } if (argv[++i]) { config_file = argv[i]; return ret; } ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("option \"-c\" requires parameter, ret=%d", ret); return ret; default: ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("invalid option: \"%c\", see help: %s -h, ret=%d", *(p - 1), argv[0], ret); return ret; } } return ret; } void SrsConfig::print_help(char** argv) { printf( RTMP_SIG_SRS_NAME" "RTMP_SIG_SRS_VERSION" "RTMP_SIG_SRS_COPYRIGHT"\n" "License: "RTMP_SIG_SRS_LICENSE"\n" "Primary Authors: "RTMP_SIG_SRS_PRIMARY_AUTHROS"\n" "Build: "SRS_AUTO_BUILD_DATE" Configuration:"SRS_AUTO_USER_CONFIGURE"\n" "Features:"SRS_AUTO_CONFIGURE"\n""\n" "Usage: %s [-h?vV] [[-t] -c ]\n" "\n" "Options:\n" " -?, -h : show this help and exit(0)\n" " -v, -V : show version and exit(0)\n" " -t : test configuration file, exit(error_code).\n" " -c filename : use configuration file for SRS\n" "\n" RTMP_SIG_SRS_WEB"\n" RTMP_SIG_SRS_URL"\n" "Email: "RTMP_SIG_SRS_EMAIL"\n" "\n" "For example:\n" " %s -v\n" " %s -t -c "SRS_CONF_DEFAULT_COFNIG_FILE"\n" " %s -c "SRS_CONF_DEFAULT_COFNIG_FILE"\n", argv[0], argv[0], argv[0], argv[0]); } int SrsConfig::parse_file(const char* filename) { int ret = ERROR_SUCCESS; config_file = filename; if (config_file.empty()) { return ERROR_SYSTEM_CONFIG_INVALID; } SrsConfigBuffer buffer; if ((ret = buffer.fullfill(config_file.c_str())) != ERROR_SUCCESS) { return ret; } return parse_buffer(&buffer); } int SrsConfig::check_config() { int ret = ERROR_SUCCESS; srs_trace("srs checking config..."); vector vhosts = get_vhosts(); //////////////////////////////////////////////////////////////////////// // check empty //////////////////////////////////////////////////////////////////////// if (root->directives.size() == 0) { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("conf is empty, ret=%d", ret); return ret; } //////////////////////////////////////////////////////////////////////// // check root directives. //////////////////////////////////////////////////////////////////////// for (int i = 0; i < (int)root->directives.size(); i++) { SrsConfDirective* conf = root->at(i); std::string n = conf->name; if (n != "listen" && n != "pid" && n != "chunk_size" && n != "ff_log_dir" && n != "srs_log_tank" && n != "srs_log_level" && n != "srs_log_file" && n != "max_connections" && n != "daemon" && n != "heartbeat" && n != "http_api" && n != "http_stream" && n != "stats" && n != "vhost" && n != "pithy_print") { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("unsupported directive %s, ret=%d", n.c_str(), ret); return ret; } } if (true) { SrsConfDirective* conf = get_http_api(); for (int i = 0; conf && i < (int)conf->directives.size(); i++) { string n = conf->at(i)->name; if (n != "enabled" && n != "listen") { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("unsupported http_api directive %s, ret=%d", n.c_str(), ret); return ret; } } } if (true) { SrsConfDirective* conf = get_http_stream(); for (int i = 0; conf && i < (int)conf->directives.size(); i++) { string n = conf->at(i)->name; if (n != "enabled" && n != "listen" && n != "dir") { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("unsupported http_stream directive %s, ret=%d", n.c_str(), ret); return ret; } } } if (true) { SrsConfDirective* conf = get_heartbeart(); for (int i = 0; conf && i < (int)conf->directives.size(); i++) { string n = conf->at(i)->name; if (n != "enabled" && n != "interval" && n != "url" && n != "device_id" && n != "summaries" ) { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("unsupported heartbeat directive %s, ret=%d", n.c_str(), ret); return ret; } } } if (true) { SrsConfDirective* conf = get_stats(); for (int i = 0; conf && i < (int)conf->directives.size(); i++) { string n = conf->at(i)->name; if (n != "network" && n != "disk") { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("unsupported stats directive %s, ret=%d", n.c_str(), ret); return ret; } } } if (true) { SrsConfDirective* conf = get_pithy_print(); for (int i = 0; conf && i < (int)conf->directives.size(); i++) { string n = conf->at(i)->name; if (n != "publish" && n != "play" && n != "forwarder" && n != "encoder" && n != "ingester" && n != "hls" && n != "edge" ) { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("unsupported pithy_print directive %s, ret=%d", n.c_str(), ret); return ret; } } } for (int n = 0; n < (int)vhosts.size(); n++) { SrsConfDirective* vhost = vhosts[n]; for (int i = 0; vhost && i < (int)vhost->directives.size(); i++) { SrsConfDirective* conf = vhost->at(i); string n = conf->name; if (n != "enabled" && n != "chunk_size" && n != "mode" && n != "origin" && n != "token_traverse" && n != "dvr" && n != "ingest" && n != "http" && n != "hls" && n != "http_hooks" && n != "gop_cache" && n != "queue_length" && n != "refer" && n != "refer_publish" && n != "refer_play" && n != "forward" && n != "transcode" && n != "bandcheck" && n != "time_jitter" && n != "atc" && n != "atc_auto" && n != "debug_srs_upnode" ) { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("unsupported vhost directive %s, ret=%d", n.c_str(), ret); return ret; } // for each sub directives of vhost. if (n == "dvr") { for (int j = 0; j < (int)conf->directives.size(); j++) { string m = conf->at(j)->name.c_str(); if (m != "enabled" && m != "dvr_path" && m != "dvr_plan" && m != "dvr_duration" && m != "dvr_wait_keyframe" && m != "time_jitter" ) { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("unsupported vhost dvr directive %s, ret=%d", m.c_str(), ret); return ret; } } } else if (n == "ingest") { for (int j = 0; j < (int)conf->directives.size(); j++) { string m = conf->at(j)->name.c_str(); if (m != "enabled" && m != "input" && m != "ffmpeg" && m != "engine" ) { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("unsupported vhost ingest directive %s, ret=%d", m.c_str(), ret); return ret; } } } else if (n == "http") { for (int j = 0; j < (int)conf->directives.size(); j++) { string m = conf->at(j)->name.c_str(); if (m != "enabled" && m != "mount" && m != "dir") { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("unsupported vhost http directive %s, ret=%d", m.c_str(), ret); return ret; } } } else if (n == "hls") { for (int j = 0; j < (int)conf->directives.size(); j++) { string m = conf->at(j)->name.c_str(); if (m != "enabled" && m != "hls_path" && m != "hls_fragment" && m != "hls_window") { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("unsupported vhost hls directive %s, ret=%d", m.c_str(), ret); return ret; } } } else if (n == "http_hooks") { for (int j = 0; j < (int)conf->directives.size(); j++) { string m = conf->at(j)->name.c_str(); if (m != "enabled" && m != "on_connect" && m != "on_close" && m != "on_publish" && m != "on_unpublish" && m != "on_play" && m != "on_stop" ) { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("unsupported vhost http_hooks directive %s, ret=%d", m.c_str(), ret); return ret; } } } else if (n == "forward") { // TODO: FIXME: implements it. /*for (int j = 0; j < (int)conf->directives.size(); j++) { string m = conf->at(j)->name.c_str(); if (m != "enabled" && m != "vhost" && m != "refer") { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("unsupported vhost forward directive %s, ret=%d", m.c_str(), ret); return ret; } }*/ } else if (n == "transcode") { for (int j = 0; j < (int)conf->directives.size(); j++) { SrsConfDirective* trans = conf->at(j); string m = trans->name.c_str(); if (m != "enabled" && m != "ffmpeg" && m != "engine") { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("unsupported vhost transcode directive %s, ret=%d", m.c_str(), ret); return ret; } if (m == "engine") { for (int k = 0; k < (int)trans->directives.size(); k++) { string e = trans->at(k)->name; if (e != "enabled" && e != "vfilter" && e != "vcodec" && e != "vbitrate" && e != "vfps" && e != "vwidth" && e != "vheight" && e != "vthreads" && e != "vprofile" && e != "vpreset" && e != "vparams" && e != "acodec" && e != "abitrate" && e != "asample_rate" && e != "achannels" && e != "aparams" && e != "output" && e != "iformat" && e != "oformat" ) { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("unsupported vhost transcode engine directive %s, ret=%d", e.c_str(), ret); return ret; } } } } } else if (n == "bandcheck") { for (int j = 0; j < (int)conf->directives.size(); j++) { string m = conf->at(j)->name.c_str(); if (m != "enabled" && m != "key" && m != "interval" && m != "limit_kbps") { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("unsupported vhost bandcheck directive %s, ret=%d", m.c_str(), ret); return ret; } } } } } // check ingest id unique. for (int i = 0; i < (int)vhosts.size(); i++) { SrsConfDirective* vhost = vhosts[i]; std::vector ids; for (int j = 0; j < (int)vhost->directives.size(); j++) { SrsConfDirective* conf = vhost->at(j); if (conf->name != "ingest") { continue; } std::string id = conf->arg0(); for (int k = 0; k < (int)ids.size(); k++) { if (id == ids.at(k)) { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("directive \"ingest\" id duplicated, vhost=%s, id=%s, ret=%d", vhost->name.c_str(), id.c_str(), ret); return ret; } } ids.push_back(id); } } //////////////////////////////////////////////////////////////////////// // check listen for rtmp. //////////////////////////////////////////////////////////////////////// if (true) { vector listens = get_listen(); if (listens.size() <= 0) { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("directive \"listen\" is empty, ret=%d", ret); return ret; } for (int i = 0; i < (int)listens.size(); i++) { string port = listens[i]; if (port.empty() || ::atoi(port.c_str()) <= 0) { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("directive listen invalid, port=%s, ret=%d", port.c_str(), ret); return ret; } } } //////////////////////////////////////////////////////////////////////// // check max connections //////////////////////////////////////////////////////////////////////// if (get_max_connections() <= 0) { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("directive max_connections invalid, max_connections=%d, ret=%d", get_max_connections(), ret); return ret; } // check max connections of system limits if (true) { int nb_consumed_fds = (int)get_listen().size(); if (get_http_api_listen() > 0) { nb_consumed_fds++; } if (get_http_stream_listen() > 0) { nb_consumed_fds++; } if (get_log_tank_file()) { nb_consumed_fds++; } // 0, 1, 2 for stdin, stdout and stderr. nb_consumed_fds += 3; int nb_connections = get_max_connections(); int nb_pipes = nb_connections * 2; int nb_reserved = 10; // reserved int nb_total = nb_connections + nb_pipes + nb_consumed_fds + nb_reserved; int max_open_files = sysconf(_SC_OPEN_MAX); int nb_canbe = (max_open_files - (nb_consumed_fds + nb_reserved)) / 3 - 1; // for each play connections, we open a pipe(2fds) to convert SrsConsumver to io, // refine performance, @see: https://github.com/winlinvip/simple-rtmp-server/issues/194 if (nb_total >= max_open_files) { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("invalid max_connections=%d, required=%d, system limit to %d, " "total=%d(max_connections=%d, nb_pipes=%d, nb_consumed_fds=%d, nb_reserved=%d), ret=%d. " "you can change max_connections from %d to %d, or " "you can login as root and set the limit: ulimit -HSn %d", nb_connections, nb_total, max_open_files, nb_total, nb_connections, nb_pipes, nb_consumed_fds, nb_reserved, ret, nb_connections, nb_canbe, nb_total); return ret; } } //////////////////////////////////////////////////////////////////////// // check heartbeat //////////////////////////////////////////////////////////////////////// if (get_heartbeat_interval() <= 0) { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("directive heartbeat interval invalid, interval=%"PRId64", ret=%d", get_heartbeat_interval(), ret); return ret; } //////////////////////////////////////////////////////////////////////// // check stats //////////////////////////////////////////////////////////////////////// if (get_stats_network() < 0) { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("directive stats network invalid, network=%d, ret=%d", get_stats_network(), ret); return ret; } if (true) { vector ips = srs_get_local_ipv4_ips(); int index = get_stats_network(); if (index >= (int)ips.size()) { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("stats network invalid, total local ip count=%d, index=%d, ret=%d", (int)ips.size(), index, ret); return ret; } srs_warn("stats network use index=%d, ip=%s", index, ips.at(index).c_str()); } if (true) { SrsConfDirective* conf = get_stats_disk_device(); if (conf == NULL || (int)conf->args.size() <= 0) { srs_warn("stats disk not configed, disk iops disabled."); } else { string disks; for (int i = 0; i < (int)conf->args.size(); i++) { disks += conf->args.at(i); disks += " "; } srs_warn("stats disk list: %s", disks.c_str()); } } //////////////////////////////////////////////////////////////////////// // check http api //////////////////////////////////////////////////////////////////////// if (get_http_api_listen() <= 0) { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("directive http_api listen invalid, listen=%d, ret=%d", get_http_api_listen(), ret); return ret; } //////////////////////////////////////////////////////////////////////// // check http stream //////////////////////////////////////////////////////////////////////// if (get_http_stream_listen() <= 0) { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("directive http_stream listen invalid, listen=%d, ret=%d", get_http_stream_listen(), ret); return ret; } //////////////////////////////////////////////////////////////////////// // check chunk size //////////////////////////////////////////////////////////////////////// if (get_global_chunk_size() < SRS_CONSTS_RTMP_MIN_CHUNK_SIZE || get_global_chunk_size() > SRS_CONSTS_RTMP_MAX_CHUNK_SIZE ) { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("directive chunk_size invalid, chunk_size=%d, must in [%d, %d], ret=%d", get_global_chunk_size(), SRS_CONSTS_RTMP_MIN_CHUNK_SIZE, SRS_CONSTS_RTMP_MAX_CHUNK_SIZE, ret); return ret; } for (int i = 0; i < (int)vhosts.size(); i++) { SrsConfDirective* vhost = vhosts[i]; if (get_chunk_size(vhost->arg0()) < SRS_CONSTS_RTMP_MIN_CHUNK_SIZE || get_chunk_size(vhost->arg0()) > SRS_CONSTS_RTMP_MAX_CHUNK_SIZE ) { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("directive vhost %s chunk_size invalid, chunk_size=%d, must in [%d, %d], ret=%d", vhost->arg0().c_str(), get_chunk_size(vhost->arg0()), SRS_CONSTS_RTMP_MIN_CHUNK_SIZE, SRS_CONSTS_RTMP_MAX_CHUNK_SIZE, ret); return ret; } } //////////////////////////////////////////////////////////////////////// // check log name and level //////////////////////////////////////////////////////////////////////// if (true) { std::string log_filename = this->get_log_file(); if (get_log_tank_file() && log_filename.empty()) { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("must specifies the file to write log to. ret=%d", ret); return ret; } if (get_log_tank_file()) { srs_trace("write log to file %s", log_filename.c_str()); srs_trace("you can: tailf %s", log_filename.c_str()); srs_trace("@see: %s", SRS_WIKI_URL_LOG); } else { srs_trace("write log to console"); } } //////////////////////////////////////////////////////////////////////// // check features //////////////////////////////////////////////////////////////////////// #ifndef SRS_AUTO_HTTP_SERVER if (get_http_stream_enabled()) { srs_warn("http_stream is disabled by configure"); } #endif #ifndef SRS_AUTO_HTTP_API if (get_http_api_enabled()) { srs_warn("http_api is disabled by configure"); } #endif for (int i = 0; i < (int)vhosts.size(); i++) { SrsConfDirective* vhost = vhosts[i]; srs_assert(vhost != NULL); #ifndef SRS_AUTO_DVR if (get_dvr_enabled(vhost->arg0())) { srs_warn("dvr of vhost %s is disabled by configure", vhost->arg0().c_str()); } #endif #ifndef SRS_AUTO_HLS if (get_hls_enabled(vhost->arg0())) { srs_warn("hls of vhost %s is disabled by configure", vhost->arg0().c_str()); } #endif #ifndef SRS_AUTO_HTTP_CALLBACK if (get_vhost_http_hooks_enabled(vhost->arg0())) { srs_warn("http_hooks of vhost %s is disabled by configure", vhost->arg0().c_str()); } #endif #ifndef SRS_AUTO_TRANSCODE if (get_transcode_enabled(get_transcode(vhost->arg0(), ""))) { srs_warn("transcode of vhost %s is disabled by configure", vhost->arg0().c_str()); } #endif #ifndef SRS_AUTO_INGEST vector ingesters = get_ingesters(vhost->arg0()); for (int j = 0; j < (int)ingesters.size(); j++) { SrsConfDirective* ingest = ingesters[j]; if (get_ingest_enabled(ingest)) { srs_warn("ingest %s of vhost %s is disabled by configure", ingest->arg0().c_str(), vhost->arg0().c_str() ); } } #endif } return ret; } int SrsConfig::parse_buffer(SrsConfigBuffer* buffer) { int ret = ERROR_SUCCESS; if ((ret = root->parse(buffer)) != ERROR_SUCCESS) { return ret; } return ret; } string SrsConfig::cwd() { return _cwd; } string SrsConfig::argv() { return _argv; } bool SrsConfig::get_deamon() { srs_assert(root); SrsConfDirective* conf = root->get("daemon"); if (conf && conf->arg0() == "off") { return false; } return true; } SrsConfDirective* SrsConfig::get_root() { return root; } int SrsConfig::get_max_connections() { srs_assert(root); SrsConfDirective* conf = root->get("max_connections"); if (!conf || conf->arg0().empty()) { return SRS_CONF_DEFAULT_MAX_CONNECTIONS; } return ::atoi(conf->arg0().c_str()); } vector SrsConfig::get_listen() { std::vector ports; SrsConfDirective* conf = root->get("listen"); if (!conf) { return ports; } for (int i = 0; i < (int)conf->args.size(); i++) { ports.push_back(conf->args.at(i)); } return ports; } string SrsConfig::get_pid_file() { SrsConfDirective* conf = root->get("pid"); if (!conf) { return SRS_CONF_DEFAULT_PID_FILE; } return conf->arg0(); } SrsConfDirective* SrsConfig::get_pithy_print() { return root->get("pithy_print"); } int SrsConfig::get_pithy_print_publish() { SrsConfDirective* pithy = get_pithy_print(); if (!pithy) { return SRS_CONF_DEFAULT_STAGE_PUBLISH_USER_INTERVAL_MS; } pithy = pithy->get("publish"); if (!pithy) { return SRS_CONF_DEFAULT_STAGE_PUBLISH_USER_INTERVAL_MS; } return ::atoi(pithy->arg0().c_str()); } int SrsConfig::get_pithy_print_forwarder() { SrsConfDirective* pithy = get_pithy_print(); if (!pithy) { return SRS_CONF_DEFAULT_STAGE_FORWARDER_INTERVAL_MS; } pithy = pithy->get("forwarder"); if (!pithy) { return SRS_CONF_DEFAULT_STAGE_FORWARDER_INTERVAL_MS; } return ::atoi(pithy->arg0().c_str()); } int SrsConfig::get_pithy_print_encoder() { SrsConfDirective* pithy = get_pithy_print(); if (!pithy) { return SRS_CONF_DEFAULT_STAGE_ENCODER_INTERVAL_MS; } pithy = pithy->get("encoder"); if (!pithy) { return SRS_CONF_DEFAULT_STAGE_ENCODER_INTERVAL_MS; } return ::atoi(pithy->arg0().c_str()); } int SrsConfig::get_pithy_print_ingester() { SrsConfDirective* pithy = get_pithy_print(); if (!pithy) { return SRS_CONF_DEFAULT_STAGE_INGESTER_INTERVAL_MS; } pithy = pithy->get("ingester"); if (!pithy) { return SRS_CONF_DEFAULT_STAGE_INGESTER_INTERVAL_MS; } return ::atoi(pithy->arg0().c_str()); } int SrsConfig::get_pithy_print_hls() { SrsConfDirective* pithy = get_pithy_print(); if (!pithy) { return SRS_CONF_DEFAULT_STAGE_HLS_INTERVAL_MS; } pithy = pithy->get("hls"); if (!pithy) { return SRS_CONF_DEFAULT_STAGE_HLS_INTERVAL_MS; } return ::atoi(pithy->arg0().c_str()); } int SrsConfig::get_pithy_print_play() { SrsConfDirective* pithy = get_pithy_print(); if (!pithy) { return SRS_CONF_DEFAULT_STAGE_PLAY_USER_INTERVAL_MS; } pithy = pithy->get("play"); if (!pithy) { return SRS_CONF_DEFAULT_STAGE_PLAY_USER_INTERVAL_MS; } return ::atoi(pithy->arg0().c_str()); } int SrsConfig::get_pithy_print_edge() { SrsConfDirective* pithy = get_pithy_print(); if (!pithy) { return SRS_CONF_DEFAULT_STAGE_EDGE_INTERVAL_MS; } pithy = pithy->get("edge"); if (!pithy) { return SRS_CONF_DEFAULT_STAGE_EDGE_INTERVAL_MS; } return ::atoi(pithy->arg0().c_str()); } SrsConfDirective* SrsConfig::get_vhost(string vhost) { srs_assert(root); for (int i = 0; i < (int)root->directives.size(); i++) { SrsConfDirective* conf = root->at(i); if (!conf->is_vhost()) { continue; } if (conf->arg0() == vhost) { return conf; } } if (vhost != SRS_CONSTS_RTMP_DEFAULT_VHOST) { return get_vhost(SRS_CONSTS_RTMP_DEFAULT_VHOST); } return NULL; } vector SrsConfig::get_vhosts() { srs_assert(root); std::vector vhosts; for (int i = 0; i < (int)root->directives.size(); i++) { SrsConfDirective* conf = root->at(i); if (!conf->is_vhost()) { continue; } vhosts.push_back(conf); } return vhosts; } bool SrsConfig::get_vhost_enabled(string vhost) { SrsConfDirective* vhost_conf = get_vhost(vhost); return get_vhost_enabled(vhost_conf); } bool SrsConfig::get_vhost_enabled(SrsConfDirective* vhost) { if (!vhost) { return false; } SrsConfDirective* conf = vhost->get("enabled"); if (!conf) { return true; } if (conf->arg0() == "off") { return false; } return true; } bool SrsConfig::get_gop_cache(string vhost) { SrsConfDirective* conf = get_vhost(vhost); if (!conf) { return true; } conf = conf->get("gop_cache"); if (conf && conf->arg0() == "off") { return false; } return true; } bool SrsConfig::get_debug_srs_upnode(string vhost) { SrsConfDirective* conf = get_vhost(vhost); if (!conf) { return true; } conf = conf->get("debug_srs_upnode"); if (conf && conf->arg0() == "off") { return false; } return true; } bool SrsConfig::get_atc(string vhost) { SrsConfDirective* conf = get_vhost(vhost); if (!conf) { return false; } conf = conf->get("atc"); if (conf && conf->arg0() == "on") { return true; } return false; } bool SrsConfig::get_atc_auto(string vhost) { SrsConfDirective* conf = get_vhost(vhost); if (!conf) { return true; } conf = conf->get("atc_auto"); if (conf && conf->arg0() == "off") { return false; } return true; } int SrsConfig::get_time_jitter(string vhost) { SrsConfDirective* dvr = get_vhost(vhost); std::string time_jitter = SRS_CONF_DEFAULT_TIME_JITTER; if (dvr) { SrsConfDirective* conf = dvr->get("time_jitter"); if (conf) { time_jitter = conf->arg0(); } } return _srs_time_jitter_string2int(time_jitter); } double SrsConfig::get_queue_length(string vhost) { SrsConfDirective* conf = get_vhost(vhost); if (!conf) { return SRS_CONF_DEFAULT_QUEUE_LENGTH; } conf = conf->get("queue_length"); if (!conf || conf->arg0().empty()) { return SRS_CONF_DEFAULT_QUEUE_LENGTH; } return ::atoi(conf->arg0().c_str()); } SrsConfDirective* SrsConfig::get_refer(string vhost) { SrsConfDirective* conf = get_vhost(vhost); if (!conf) { return NULL; } return conf->get("refer"); } SrsConfDirective* SrsConfig::get_refer_play(string vhost) { SrsConfDirective* conf = get_vhost(vhost); if (!conf) { return NULL; } return conf->get("refer_play"); } SrsConfDirective* SrsConfig::get_refer_publish(string vhost) { SrsConfDirective* conf = get_vhost(vhost); if (!conf) { return NULL; } return conf->get("refer_publish"); } int SrsConfig::get_chunk_size(string vhost) { if (vhost.empty()) { return get_global_chunk_size(); } SrsConfDirective* conf = get_vhost(vhost); if (!conf) { // vhost does not specify the chunk size, // use the global instead. return get_global_chunk_size(); } conf = conf->get("chunk_size"); if (!conf) { // vhost does not specify the chunk size, // use the global instead. return get_global_chunk_size(); } return ::atoi(conf->arg0().c_str()); } int SrsConfig::get_global_chunk_size() { SrsConfDirective* conf = root->get("chunk_size"); if (!conf) { return SRS_CONSTS_RTMP_SRS_CHUNK_SIZE; } return ::atoi(conf->arg0().c_str()); } SrsConfDirective* SrsConfig::get_forward(string vhost) { SrsConfDirective* conf = get_vhost(vhost); if (!conf) { return NULL; } return conf->get("forward"); } SrsConfDirective* SrsConfig::get_vhost_http_hooks(string vhost) { SrsConfDirective* conf = get_vhost(vhost); if (!conf) { return NULL; } return conf->get("http_hooks"); } bool SrsConfig::get_vhost_http_hooks_enabled(string vhost) { SrsConfDirective* conf = get_vhost_http_hooks(vhost); if (!conf) { return false; } SrsConfDirective* enabled = conf->get("enabled"); if (!enabled || enabled->arg0() != "on") { return false; } return true; } SrsConfDirective* SrsConfig::get_vhost_on_connect(string vhost) { SrsConfDirective* conf = get_vhost_http_hooks(vhost); if (!conf) { return NULL; } return conf->get("on_connect"); } SrsConfDirective* SrsConfig::get_vhost_on_close(string vhost) { SrsConfDirective* conf = get_vhost_http_hooks(vhost); if (!conf) { return NULL; } return conf->get("on_close"); } SrsConfDirective* SrsConfig::get_vhost_on_publish(string vhost) { SrsConfDirective* conf = get_vhost_http_hooks(vhost); if (!conf) { return NULL; } return conf->get("on_publish"); } SrsConfDirective* SrsConfig::get_vhost_on_unpublish(string vhost) { SrsConfDirective* conf = get_vhost_http_hooks(vhost); if (!conf) { return NULL; } return conf->get("on_unpublish"); } SrsConfDirective* SrsConfig::get_vhost_on_play(string vhost) { SrsConfDirective* conf = get_vhost_http_hooks(vhost); if (!conf) { return NULL; } return conf->get("on_play"); } SrsConfDirective* SrsConfig::get_vhost_on_stop(string vhost) { SrsConfDirective* conf = get_vhost_http_hooks(vhost); if (!conf) { return NULL; } return conf->get("on_stop"); } bool SrsConfig::get_bw_check_enabled(string vhost) { SrsConfDirective* conf = get_vhost(vhost); if (!conf) { return false; } conf = conf->get("bandcheck"); if (!conf) { return false; } conf = conf->get("enabled"); if (!conf || conf->arg0() != "on") { return false; } return true; } string SrsConfig::get_bw_check_key(string vhost) { SrsConfDirective* conf = get_vhost(vhost); if (!conf) { return ""; } conf = conf->get("bandcheck"); if (!conf) { return ""; } conf = conf->get("key"); if (!conf) { return ""; } return conf->arg0(); } int SrsConfig::get_bw_check_interval_ms(string vhost) { SrsConfDirective* conf = get_vhost(vhost); if (!conf) { return SRS_CONF_DEFAULT_BANDWIDTH_INTERVAL * 1000; } conf = conf->get("bandcheck"); if (!conf) { return SRS_CONF_DEFAULT_BANDWIDTH_INTERVAL * 1000; } conf = conf->get("interval"); if (!conf) { return SRS_CONF_DEFAULT_BANDWIDTH_INTERVAL * 1000; } return (int)(::atof(conf->arg0().c_str()) * 1000); } int SrsConfig::get_bw_check_limit_kbps(string vhost) { SrsConfDirective* conf = get_vhost(vhost); if (!conf) { return SRS_CONF_DEFAULT_BANDWIDTH_LIMIT_KBPS; } conf = conf->get("bandcheck"); if (!conf) { return SRS_CONF_DEFAULT_BANDWIDTH_LIMIT_KBPS; } conf = conf->get("limit_kbps"); if (!conf) { return SRS_CONF_DEFAULT_BANDWIDTH_LIMIT_KBPS; } return ::atoi(conf->arg0().c_str()); } bool SrsConfig::get_vhost_is_edge(string vhost) { SrsConfDirective* conf = get_vhost(vhost); return get_vhost_is_edge(conf); } bool SrsConfig::get_vhost_is_edge(SrsConfDirective* vhost) { SrsConfDirective* conf = vhost; if (!conf) { return false; } conf = conf->get("mode"); if (!conf || conf->arg0() != "remote") { return false; } return true; } SrsConfDirective* SrsConfig::get_vhost_edge_origin(string vhost) { SrsConfDirective* conf = get_vhost(vhost); if (!conf) { return NULL; } return conf->get("origin"); } bool SrsConfig::get_vhost_edge_token_traverse(string vhost) { SrsConfDirective* conf = get_vhost(vhost); if (!conf) { return false; } conf = conf->get("token_traverse"); if (!conf || conf->arg0() != "on") { return false; } return true; } SrsConfDirective* SrsConfig::get_transcode(string vhost, string scope) { SrsConfDirective* conf = get_vhost(vhost); if (!conf) { return NULL; } SrsConfDirective* transcode = conf->get("transcode"); if (!transcode) { return NULL; } if (transcode->arg0() == scope) { return transcode; } return NULL; } bool SrsConfig::get_transcode_enabled(SrsConfDirective* transcode) { if (!transcode) { return false; } SrsConfDirective* conf = transcode->get("enabled"); if (!conf || conf->arg0() != "on") { return false; } return true; } string SrsConfig::get_transcode_ffmpeg(SrsConfDirective* transcode) { if (!transcode) { return ""; } SrsConfDirective* conf = transcode->get("ffmpeg"); if (!conf) { return ""; } return conf->arg0(); } vector SrsConfig::get_transcode_engines(SrsConfDirective* transcode) { vector engines; if (!transcode) { return engines; } for (int i = 0; i < (int)transcode->directives.size(); i++) { SrsConfDirective* conf = transcode->directives[i]; if (conf->name == "engine") { engines.push_back(conf); } } return engines; } bool SrsConfig::get_engine_enabled(SrsConfDirective* engine) { if (!engine) { return false; } SrsConfDirective* conf = engine->get("enabled"); if (!conf || conf->arg0() != "on") { return false; } return true; } string SrsConfig::get_engine_iformat(SrsConfDirective* engine) { if (!engine) { return SRS_CONF_DEFAULT_TRANSCODE_IFORMAT; } SrsConfDirective* conf = engine->get("iformat"); if (!conf) { return SRS_CONF_DEFAULT_TRANSCODE_IFORMAT; } return conf->arg0(); } vector SrsConfig::get_engine_vfilter(SrsConfDirective* engine) { vector vfilter; if (!engine) { return vfilter; } SrsConfDirective* conf = engine->get("vfilter"); if (!conf) { return vfilter; } for (int i = 0; i < (int)conf->directives.size(); i++) { SrsConfDirective* p = conf->directives[i]; if (!p) { continue; } vfilter.push_back("-" + p->name); vfilter.push_back(p->arg0()); } return vfilter; } string SrsConfig::get_engine_vcodec(SrsConfDirective* engine) { if (!engine) { return ""; } SrsConfDirective* conf = engine->get("vcodec"); if (!conf) { return ""; } return conf->arg0(); } int SrsConfig::get_engine_vbitrate(SrsConfDirective* engine) { if (!engine) { return 0; } SrsConfDirective* conf = engine->get("vbitrate"); if (!conf) { return 0; } return ::atoi(conf->arg0().c_str()); } double SrsConfig::get_engine_vfps(SrsConfDirective* engine) { if (!engine) { return 0; } SrsConfDirective* conf = engine->get("vfps"); if (!conf) { return 0; } return ::atof(conf->arg0().c_str()); } int SrsConfig::get_engine_vwidth(SrsConfDirective* engine) { if (!engine) { return 0; } SrsConfDirective* conf = engine->get("vwidth"); if (!conf) { return 0; } return ::atoi(conf->arg0().c_str()); } int SrsConfig::get_engine_vheight(SrsConfDirective* engine) { if (!engine) { return 0; } SrsConfDirective* conf = engine->get("vheight"); if (!conf) { return 0; } return ::atoi(conf->arg0().c_str()); } int SrsConfig::get_engine_vthreads(SrsConfDirective* engine) { if (!engine) { return 0; } SrsConfDirective* conf = engine->get("vthreads"); if (!conf) { return 0; } return ::atoi(conf->arg0().c_str()); } string SrsConfig::get_engine_vprofile(SrsConfDirective* engine) { if (!engine) { return ""; } SrsConfDirective* conf = engine->get("vprofile"); if (!conf) { return ""; } return conf->arg0(); } string SrsConfig::get_engine_vpreset(SrsConfDirective* engine) { if (!engine) { return ""; } SrsConfDirective* conf = engine->get("vpreset"); if (!conf) { return ""; } return conf->arg0(); } vector SrsConfig::get_engine_vparams(SrsConfDirective* engine) { vector vparams; if (!engine) { return vparams; } SrsConfDirective* conf = engine->get("vparams"); if (!conf) { return vparams; } for (int i = 0; i < (int)conf->directives.size(); i++) { SrsConfDirective* p = conf->directives[i]; if (!p) { continue; } vparams.push_back("-" + p->name); vparams.push_back(p->arg0()); } return vparams; } string SrsConfig::get_engine_acodec(SrsConfDirective* engine) { if (!engine) { return ""; } SrsConfDirective* conf = engine->get("acodec"); if (!conf) { return ""; } return conf->arg0(); } int SrsConfig::get_engine_abitrate(SrsConfDirective* engine) { if (!engine) { return 0; } SrsConfDirective* conf = engine->get("abitrate"); if (!conf) { return 0; } return ::atoi(conf->arg0().c_str()); } int SrsConfig::get_engine_asample_rate(SrsConfDirective* engine) { if (!engine) { return 0; } SrsConfDirective* conf = engine->get("asample_rate"); if (!conf) { return 0; } return ::atoi(conf->arg0().c_str()); } int SrsConfig::get_engine_achannels(SrsConfDirective* engine) { if (!engine) { return 0; } SrsConfDirective* conf = engine->get("achannels"); if (!conf) { return 0; } return ::atoi(conf->arg0().c_str()); } vector SrsConfig::get_engine_aparams(SrsConfDirective* engine) { vector aparams; if (!engine) { return aparams; } SrsConfDirective* conf = engine->get("aparams"); if (!conf) { return aparams; } for (int i = 0; i < (int)conf->directives.size(); i++) { SrsConfDirective* p = conf->directives[i]; if (!p) { continue; } aparams.push_back("-" + p->name); aparams.push_back(p->arg0()); } return aparams; } string SrsConfig::get_engine_oformat(SrsConfDirective* engine) { if (!engine) { return SRS_CONF_DEFAULT_TRANSCODE_OFORMAT; } SrsConfDirective* conf = engine->get("oformat"); if (!conf) { return SRS_CONF_DEFAULT_TRANSCODE_OFORMAT; } return conf->arg0(); } string SrsConfig::get_engine_output(SrsConfDirective* engine) { if (!engine) { return ""; } SrsConfDirective* conf = engine->get("output"); if (!conf) { return ""; } return conf->arg0(); } vector SrsConfig::get_ingesters(string vhost) { vector ingeters; SrsConfDirective* vhost_conf = get_vhost(vhost); if (!vhost_conf) { return ingeters; } for (int i = 0; i < (int)vhost_conf->directives.size(); i++) { SrsConfDirective* conf = vhost_conf->directives[i]; if (conf->name == "ingest") { ingeters.push_back(conf); } } return ingeters; } SrsConfDirective* SrsConfig::get_ingest_by_id(string vhost, string ingest_id) { SrsConfDirective* conf = get_vhost(vhost); if (!conf) { return NULL; } conf = conf->get("ingest", ingest_id); return conf; } bool SrsConfig::get_ingest_enabled(SrsConfDirective* ingest) { if (!ingest) { return false; } SrsConfDirective* conf = ingest->get("enabled"); if (!conf || conf->arg0() != "on") { return false; } return true; } string SrsConfig::get_ingest_ffmpeg(SrsConfDirective* ingest) { if (!ingest) { return ""; } SrsConfDirective* conf = ingest->get("ffmpeg"); if (!conf) { return ""; } return conf->arg0(); } string SrsConfig::get_ingest_input_type(SrsConfDirective* ingest) { if (!ingest) { return SRS_CONF_DEFAULT_INGEST_TYPE_FILE; } SrsConfDirective* conf = ingest->get("input"); if (!conf) { return SRS_CONF_DEFAULT_INGEST_TYPE_FILE; } conf = conf->get("type"); if (!conf) { return SRS_CONF_DEFAULT_INGEST_TYPE_FILE; } return conf->arg0(); } string SrsConfig::get_ingest_input_url(SrsConfDirective* ingest) { if (!ingest) { return ""; } SrsConfDirective* conf = ingest->get("input"); if (!conf) { return ""; } conf = conf->get("url"); if (!conf) { return ""; } return conf->arg0(); } bool SrsConfig::get_log_tank_file() { srs_assert(root); SrsConfDirective* conf = root->get("srs_log_tank"); if (conf && conf->arg0() == SRS_CONF_DEFAULT_LOG_TANK_CONSOLE) { return false; } return true; } string SrsConfig::get_log_level() { srs_assert(root); SrsConfDirective* conf = root->get("srs_log_level"); if (!conf || conf->arg0().empty()) { return SRS_CONF_DEFAULT_LOG_LEVEL; } return conf->arg0(); } string SrsConfig::get_log_file() { srs_assert(root); SrsConfDirective* conf = root->get("srs_log_file"); if (!conf || conf->arg0().empty()) { return SRS_CONF_DEFAULT_LOG_FILE; } return conf->arg0(); } bool SrsConfig::get_ffmpeg_log_enabled() { string log = get_ffmpeg_log_dir(); return log != SRS_CONSTS_NULL_FILE; } string SrsConfig::get_ffmpeg_log_dir() { srs_assert(root); SrsConfDirective* conf = root->get("ff_log_dir"); if (!conf || conf->arg0().empty()) { return SRS_CONF_DEFAULT_FF_LOG_DIR; } return conf->arg0(); } SrsConfDirective* SrsConfig::get_hls(string vhost) { SrsConfDirective* conf = get_vhost(vhost); if (!conf) { return NULL; } return conf->get("hls"); } bool SrsConfig::get_hls_enabled(string vhost) { SrsConfDirective* hls = get_hls(vhost); if (!hls) { return false; } SrsConfDirective* conf = hls->get("enabled"); if (!conf) { return false; } if (conf->arg0() == "on") { return true; } return false; } string SrsConfig::get_hls_path(string vhost) { SrsConfDirective* hls = get_hls(vhost); if (!hls) { return SRS_CONF_DEFAULT_HLS_PATH; } SrsConfDirective* conf = hls->get("hls_path"); if (!conf) { return SRS_CONF_DEFAULT_HLS_PATH; } return conf->arg0(); } double SrsConfig::get_hls_fragment(string vhost) { SrsConfDirective* hls = get_hls(vhost); if (!hls) { return SRS_CONF_DEFAULT_HLS_FRAGMENT; } SrsConfDirective* conf = hls->get("hls_fragment"); if (!conf) { return SRS_CONF_DEFAULT_HLS_FRAGMENT; } return ::atof(conf->arg0().c_str()); } double SrsConfig::get_hls_window(string vhost) { SrsConfDirective* hls = get_hls(vhost); if (!hls) { return SRS_CONF_DEFAULT_HLS_WINDOW; } SrsConfDirective* conf = hls->get("hls_window"); if (!conf) { return SRS_CONF_DEFAULT_HLS_WINDOW; } return ::atof(conf->arg0().c_str()); } SrsConfDirective* SrsConfig::get_dvr(string vhost) { SrsConfDirective* conf = get_vhost(vhost); if (!conf) { return NULL; } return conf->get("dvr"); } bool SrsConfig::get_dvr_enabled(string vhost) { SrsConfDirective* dvr = get_dvr(vhost); if (!dvr) { return false; } SrsConfDirective* conf = dvr->get("enabled"); if (!conf) { return false; } if (conf->arg0() == "on") { return true; } return false; } string SrsConfig::get_dvr_path(string vhost) { SrsConfDirective* dvr = get_dvr(vhost); if (!dvr) { return SRS_CONF_DEFAULT_DVR_PATH; } SrsConfDirective* conf = dvr->get("dvr_path"); if (!conf) { return SRS_CONF_DEFAULT_DVR_PATH; } return conf->arg0(); } string SrsConfig::get_dvr_plan(string vhost) { SrsConfDirective* dvr = get_dvr(vhost); if (!dvr) { return SRS_CONF_DEFAULT_DVR_PLAN; } SrsConfDirective* conf = dvr->get("dvr_plan"); if (!conf) { return SRS_CONF_DEFAULT_DVR_PLAN; } return conf->arg0(); } int SrsConfig::get_dvr_duration(string vhost) { SrsConfDirective* dvr = get_dvr(vhost); if (!dvr) { return SRS_CONF_DEFAULT_DVR_DURATION; } SrsConfDirective* conf = dvr->get("dvr_duration"); if (!conf) { return SRS_CONF_DEFAULT_DVR_DURATION; } return ::atoi(conf->arg0().c_str()); } bool SrsConfig::get_dvr_wait_keyframe(string vhost) { SrsConfDirective* dvr = get_dvr(vhost); if (!dvr) { return true; } SrsConfDirective* conf = dvr->get("dvr_wait_keyframe"); if (!conf || conf->arg0() != "off") { return true; } return false; } int SrsConfig::get_dvr_time_jitter(string vhost) { SrsConfDirective* dvr = get_dvr(vhost); std::string time_jitter = SRS_CONF_DEFAULT_TIME_JITTER; if (dvr) { SrsConfDirective* conf = dvr->get("time_jitter"); if (conf) { time_jitter = conf->arg0(); } } return _srs_time_jitter_string2int(time_jitter); } bool SrsConfig::get_http_api_enabled() { SrsConfDirective* conf = get_http_api(); return get_http_api_enabled(conf); } SrsConfDirective* SrsConfig::get_http_api() { return root->get("http_api"); } bool SrsConfig::get_http_api_enabled(SrsConfDirective* conf) { if (!conf) { return false; } conf = conf->get("enabled"); if (conf && conf->arg0() == "on") { return true; } return false; } int SrsConfig::get_http_api_listen() { SrsConfDirective* conf = get_http_api(); if (!conf) { return SRS_CONF_DEFAULT_HTTP_API_PORT; } conf = conf->get("listen"); if (!conf || conf->arg0().empty()) { return SRS_CONF_DEFAULT_HTTP_API_PORT; } return ::atoi(conf->arg0().c_str()); } bool SrsConfig::get_http_stream_enabled() { SrsConfDirective* conf = get_http_stream(); return get_http_stream_enabled(conf); } SrsConfDirective* SrsConfig::get_http_stream() { return root->get("http_stream"); } bool SrsConfig::get_http_stream_enabled(SrsConfDirective* conf) { if (!conf) { return false; } conf = conf->get("enabled"); if (conf && conf->arg0() == "on") { return true; } return false; } int SrsConfig::get_http_stream_listen() { SrsConfDirective* conf = get_http_stream(); if (!conf) { return SRS_CONF_DEFAULT_HTTP_STREAM_PORT; } conf = conf->get("listen"); if (!conf || conf->arg0().empty()) { return SRS_CONF_DEFAULT_HTTP_STREAM_PORT; } return ::atoi(conf->arg0().c_str()); } string SrsConfig::get_http_stream_dir() { SrsConfDirective* conf = get_http_stream(); if (!conf) { return SRS_CONF_DEFAULT_HTTP_DIR; } conf = conf->get("dir"); if (!conf) { return SRS_CONF_DEFAULT_HTTP_DIR; } if (conf->arg0().empty()) { return SRS_CONF_DEFAULT_HTTP_DIR; } return conf->arg0(); } bool SrsConfig::get_vhost_http_enabled(string vhost) { SrsConfDirective* conf = get_vhost(vhost); if (!conf) { return false; } conf = conf->get("http"); if (!conf) { return false; } conf = conf->get("enabled"); if (!conf) { return false; } if (conf->arg0() == "on") { return true; } return false; } string SrsConfig::get_vhost_http_mount(string vhost) { SrsConfDirective* conf = get_vhost(vhost); if (!conf) { return SRS_CONF_DEFAULT_HTTP_MOUNT; } conf = conf->get("http"); if (!conf) { return SRS_CONF_DEFAULT_HTTP_MOUNT; } conf = conf->get("mount"); if (!conf || conf->arg0().empty()) { return SRS_CONF_DEFAULT_HTTP_MOUNT; } return conf->arg0(); } string SrsConfig::get_vhost_http_dir(string vhost) { SrsConfDirective* conf = get_vhost(vhost); if (!conf) { return SRS_CONF_DEFAULT_HTTP_DIR; } conf = conf->get("http"); if (!conf) { return SRS_CONF_DEFAULT_HTTP_DIR; } conf = conf->get("dir"); if (!conf || conf->arg0().empty()) { return SRS_CONF_DEFAULT_HTTP_DIR; } return conf->arg0(); } SrsConfDirective* SrsConfig::get_heartbeart() { return root->get("heartbeat"); } bool SrsConfig::get_heartbeat_enabled() { SrsConfDirective* conf = get_heartbeart(); if (!conf) { return SRS_CONF_DEFAULT_HTTP_HEAETBEAT_ENABLED; } conf = conf->get("enabled"); if (!conf || conf->arg0() != "on") { return SRS_CONF_DEFAULT_HTTP_HEAETBEAT_ENABLED; } return true; } int64_t SrsConfig::get_heartbeat_interval() { SrsConfDirective* conf = get_heartbeart(); if (!conf) { return (int64_t)(SRS_CONF_DEFAULT_HTTP_HEAETBEAT_INTERVAL * 1000); } conf = conf->get("interval"); if (!conf || conf->arg0().empty()) { return (int64_t)(SRS_CONF_DEFAULT_HTTP_HEAETBEAT_INTERVAL * 1000); } return (int64_t)(::atof(conf->arg0().c_str()) * 1000); } string SrsConfig::get_heartbeat_url() { SrsConfDirective* conf = get_heartbeart(); if (!conf) { return SRS_CONF_DEFAULT_HTTP_HEAETBEAT_URL; } conf = conf->get("url"); if (!conf || conf->arg0().empty()) { return SRS_CONF_DEFAULT_HTTP_HEAETBEAT_URL; } return conf->arg0(); } string SrsConfig::get_heartbeat_device_id() { SrsConfDirective* conf = get_heartbeart(); if (!conf) { return ""; } conf = conf->get("device_id"); if (!conf || conf->arg0().empty()) { return ""; } return conf->arg0(); } bool SrsConfig::get_heartbeat_summaries() { SrsConfDirective* conf = get_heartbeart(); if (!conf) { return SRS_CONF_DEFAULT_HTTP_HEAETBEAT_SUMMARIES; } conf = conf->get("summaries"); if (!conf || conf->arg0() != "on") { return SRS_CONF_DEFAULT_HTTP_HEAETBEAT_SUMMARIES; } return true; } SrsConfDirective* SrsConfig::get_stats() { return root->get("stats"); } int SrsConfig::get_stats_network() { SrsConfDirective* conf = get_stats(); if (!conf) { return SRS_CONF_DEFAULT_STATS_NETWORK_DEVICE_INDEX; } conf = conf->get("network"); if (!conf || conf->arg0().empty()) { return SRS_CONF_DEFAULT_STATS_NETWORK_DEVICE_INDEX; } return ::atoi(conf->arg0().c_str()); } SrsConfDirective* SrsConfig::get_stats_disk_device() { SrsConfDirective* conf = get_stats(); if (!conf) { return NULL; } conf = conf->get("disk"); if (!conf || conf->args.size() == 0) { return NULL; } return conf; } namespace _srs_internal { SrsConfigBuffer::SrsConfigBuffer() { line = 1; pos = last = start = NULL; end = start; } SrsConfigBuffer::~SrsConfigBuffer() { srs_freep(start); } int SrsConfigBuffer::fullfill(const char* filename) { int ret = ERROR_SUCCESS; SrsFileReader reader; // open file reader. if ((ret = reader.open(filename)) != ERROR_SUCCESS) { srs_error("open conf file error. ret=%d", ret); return ret; } // read all. int filesize = (int)reader.filesize(); // create buffer srs_freep(start); pos = last = start = new char[filesize]; end = start + filesize; // read total content from file. ssize_t nread = 0; if ((ret = reader.read(start, filesize, &nread)) != ERROR_SUCCESS) { srs_error("read file read error. expect %d, actual %d bytes, ret=%d", filesize, nread, ret); return ret; } return ret; } bool SrsConfigBuffer::empty() { return pos >= end; } }; bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b) { // both NULL, equal. if (!a && !b) { return true; } if (!a || !b) { return false; } if (a->name != b->name) { return false; } if (a->args.size() != b->args.size()) { return false; } for (int i = 0; i < (int)a->args.size(); i++) { if (a->args.at(i) != b->args.at(i)) { return false; } } if (a->directives.size() != b->directives.size()) { return false; } for (int i = 0; i < (int)a->directives.size(); i++) { SrsConfDirective* a0 = a->at(i); SrsConfDirective* b0 = b->at(i); if (!srs_directive_equals(a0, b0)) { return false; } } return true; }