/* The MIT License (MIT) Copyright (c) 2013 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 <srs_core_config.hpp> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> // file operations. #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <vector> #include <algorithm> using namespace std; #include <srs_core_error.hpp> #include <srs_core_log.hpp> #include <srs_core_autofree.hpp> #define FILE_OFFSET(fd) lseek(fd, 0, SEEK_CUR) int64_t FILE_SIZE(int fd) { int64_t pre = FILE_OFFSET(fd); int64_t pos = lseek(fd, 0, SEEK_END); lseek(fd, pre, SEEK_SET); return pos; } #define LF (char)0x0a #define CR (char)0x0d bool is_common_space(char ch) { return (ch == ' ' || ch == '\t' || ch == CR || ch == LF); } class SrsFileBuffer { private: // last available position. char* last; // end of buffer. char* end; // start of buffer. char* start; public: // current consumed position. char* pos; // current parsed line. int line; SrsFileBuffer(); virtual ~SrsFileBuffer(); virtual int fullfill(const char* filename); virtual bool empty(); }; SrsFileBuffer::SrsFileBuffer() { line = 0; pos = last = start = NULL; end = start; } SrsFileBuffer::~SrsFileBuffer() { srs_freepa(start); } int SrsFileBuffer::fullfill(const char* filename) { int ret = ERROR_SUCCESS; int fd = -1; int nread = 0; int filesize = 0; if ((fd = ::open(filename, O_RDONLY, 0)) < 0) { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("open conf file error. ret=%d", ret); goto finish; } if ((filesize = FILE_SIZE(fd) - FILE_OFFSET(fd)) <= 0) { ret = ERROR_SYSTEM_CONFIG_EOF; srs_error("read conf file error. ret=%d", ret); goto finish; } srs_freepa(start); pos = last = start = new char[filesize]; end = start + filesize; if ((nread = read(fd, start, filesize)) != filesize) { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("read file read error. expect %d, actual %d bytes, ret=%d", filesize, nread, ret); goto finish; } line = 1; finish: if (fd > 0) { ::close(fd); } return ret; } bool SrsFileBuffer::empty() { return pos >= end; } SrsConfDirective::SrsConfDirective() { } SrsConfDirective::~SrsConfDirective() { std::vector<SrsConfDirective*>::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) { return directives.at(index); } SrsConfDirective* SrsConfDirective::get(string _name) { std::vector<SrsConfDirective*>::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<SrsConfDirective*>::iterator it; for (it = directives.begin(); it != directives.end(); ++it) { SrsConfDirective* directive = *it; if (directive->name == _name && directive->arg0() == _arg0) { return directive; } } return NULL; } int SrsConfDirective::parse(const char* filename) { int ret = ERROR_SUCCESS; SrsFileBuffer buffer; if ((ret = buffer.fullfill(filename)) != ERROR_SUCCESS) { return ret; } return parse_conf(&buffer, parse_file); } // see: ngx_conf_parse int SrsConfDirective::parse_conf(SrsFileBuffer* buffer, SrsDirectiveType type) { int ret = ERROR_SUCCESS; while (true) { std::vector<string> args; ret = read_token(buffer, args); /** * 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 \"}\"", buffer->line); return ret; } return ERROR_SUCCESS; } if (ret == ERROR_SYSTEM_CONFIG_EOF) { if (type == parse_block) { srs_error("line %d: unexpected end of file, expecting \"}\"", buffer->line); return ret; } return ERROR_SUCCESS; } if (args.empty()) { srs_error("line %d: empty directive.", buffer->line); return ret; } // build directive tree. SrsConfDirective* directive = new SrsConfDirective(); directive->conf_line = buffer->line; 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(SrsFileBuffer* buffer, std::vector<string>& args) { int ret = ERROR_SUCCESS; char* pstart = buffer->pos; int startline = buffer->line; 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_error("end of file. ret=%d", ret); 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; startline = buffer->line; 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 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* word = new char[len]; memcpy(word, pstart, len); word[len - 1] = 0; string word_str = word; if (!word_str.empty()) { args.push_back(word_str); } srs_freepa(word); if (ch == ';') { return ERROR_SYSTEM_CONFIG_DIRECTIVE; } if (ch == '{') { return ERROR_SYSTEM_CONFIG_BLOCK_START; } } } } return ret; } SrsConfig* config = new SrsConfig(); SrsConfig::SrsConfig() { show_help = false; show_version = false; root = new SrsConfDirective(); root->conf_line = 0; root->name = "root"; } SrsConfig::~SrsConfig() { srs_freep(root); } int SrsConfig::reload() { int ret = ERROR_SUCCESS; SrsConfig conf; if ((ret = conf.parse_file(config_file.c_str())) != ERROR_SUCCESS) { srs_error("config reloader parse file failed. ret=%d", ret); return ret; } srs_info("config reloader parse file success."); // store current root to old_root, // and reap the root from conf to current root. SrsConfDirective* old_root = root; SrsAutoFree(SrsConfDirective, old_root, false); root = conf.root; conf.root = NULL; // merge config. std::vector<ISrsReloadHandler*>::iterator it; // 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: 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: vhost added, directly supported. // merge config: vhost removed/disabled/modified. for (int i = 0; i < (int)old_root->directives.size(); i++) { SrsConfDirective* old_vhost = old_root->at(i); // only process vhost directives. if (old_vhost->name != "vhost") { continue; } std::string vhost = old_vhost->arg0(); SrsConfDirective* new_vhost = root->get("vhost", vhost); // ignore if absolutely equal if (new_vhost && srs_directive_equals(old_vhost, new_vhost)) { srs_trace("vhost %s absolutely equal, ignore.", vhost.c_str()); continue; } // ignore if enable the new vhost when old vhost is disabled. if (get_vhost_enabled(new_vhost) && !get_vhost_enabled(old_vhost)) { srs_trace("vhost %s disabled=>enabled, ignore.", vhost.c_str()); continue; } // ignore if both old and new vhost are disabled. if (!get_vhost_enabled(new_vhost) && !get_vhost_enabled(old_vhost)) { srs_trace("vhost %s disabled=>disabled, ignore.", vhost.c_str()); continue; } // merge config: vhost removed/disabled. if (!get_vhost_enabled(new_vhost) && get_vhost_enabled(old_vhost)) { srs_trace("vhost %s disabled, 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 pithy_print remove " "vhost %s failed. ret=%d", vhost.c_str(), ret); return ret; } } srs_trace("reload remove vhost %s success.", vhost.c_str()); } // merge config: vhost modified. srs_trace("vhost %s modified, reload its detail.", vhost.c_str()); if (get_vhost_enabled(new_vhost) && get_vhost_enabled(old_vhost)) { // gop_cache 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_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 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_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()); } // forward 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_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 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_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()); } // transcode if (!srs_directive_equals(new_vhost->get("transcode"), old_vhost->get("transcode"))) { for (it = subscribes.begin(); it != subscribes.end(); ++it) { ISrsReloadHandler* subscribe = *it; if ((ret = subscribe->on_reload_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()); } // TODO: suppor reload hls/forward/ffmpeg/http continue; } srs_warn("invalid reload path, enabled old: %d, new: %d", get_vhost_enabled(old_vhost), get_vhost_enabled(new_vhost)); } return ret; } void SrsConfig::subscribe(ISrsReloadHandler* handler) { std::vector<ISrsReloadHandler*>::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<ISrsReloadHandler*>::iterator it; it = std::find(subscribes.begin(), subscribes.end(), handler); if (it == subscribes.end()) { return; } subscribes.erase(it); } // see: ngx_get_options int SrsConfig::parse_options(int argc, char** argv) { int ret = ERROR_SUCCESS; for (int i = 1; i < argc; i++) { if ((ret = parse_argv(i, argv)) != ERROR_SUCCESS) { return ret; } } if (show_help) { print_help(argv); } if (show_version) { printf("%s\n", RTMP_SIG_SRS_VERSION); } if (show_help || show_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; } return parse_file(config_file.c_str()); } int SrsConfig::parse_file(const char* filename) { int ret = ERROR_SUCCESS; config_file = filename; if (config_file.empty()) { return ERROR_SYSTEM_CONFIG_INVALID; } if ((ret = root->parse(config_file.c_str())) != ERROR_SUCCESS) { return ret; } SrsConfDirective* conf = NULL; if ((conf = get_listen()) == NULL || conf->args.size() == 0) { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("line %d: conf error, " "directive \"listen\" is empty, ret=%d", (conf? conf->conf_line:0), ret); return ret; } // TODO: check the hls. // TODO: check forward. // TODO: check ffmpeg. // TODO: check http. return ret; } int SrsConfig::parse_argv(int& i, char** argv) { int ret = ERROR_SUCCESS; char* p = argv[i]; if (*p++ != '-') { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("invalid options(index=%d, value=%s), " "must starts with -, see help: %s -h, ret=%d", i, argv[i], argv[0], ret); return ret; } while (*p) { switch (*p++) { case '?': case 'h': show_help = true; break; case 'v': case 'V': show_version = true; break; case 'c': 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 " Copyright (c) 2013 winlin\n" "Contributors: "RTMP_SIG_SRS_CONTRIBUTOR"\n" "Build: "SRS_BUILD_DATE" Configuration: "SRS_CONFIGURE"\n" "Usage: %s [-h?vV] [-c <filename>]\n" "\n" "Options:\n" " -?-h : show help\n" " -v-V : show version and exit\n" " -c filename : set configuration file\n" "\n" RTMP_SIG_SRS_WEB"\n" RTMP_SIG_SRS_URL"\n" "Email: "RTMP_SIG_SRS_EMAIL"\n" "\n", argv[0]); } 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->name != "vhost") { continue; } if (conf->arg0() == vhost) { return conf; } } if (vhost != RTMP_VHOST_DEFAULT) { return get_vhost(RTMP_VHOST_DEFAULT); } return NULL; } SrsConfDirective* SrsConfig::get_vhost_on_connect(string vhost) { SrsConfDirective* conf = get_vhost(vhost); if (!conf) { return NULL; } conf = conf->get("http_hooks"); if (!conf) { return NULL; } SrsConfDirective* enabled = conf->get("enabled"); if (!enabled || enabled->arg0() != "on") { return NULL; } return conf->get("on_connect"); } SrsConfDirective* SrsConfig::get_vhost_on_close(string vhost) { SrsConfDirective* conf = get_vhost(vhost); if (!conf) { return NULL; } conf = conf->get("http_hooks"); if (!conf) { return NULL; } SrsConfDirective* enabled = conf->get("enabled"); if (!enabled || enabled->arg0() != "on") { return NULL; } return conf->get("on_close"); } SrsConfDirective* SrsConfig::get_vhost_on_publish(string vhost) { SrsConfDirective* conf = get_vhost(vhost); if (!conf) { return NULL; } conf = conf->get("http_hooks"); if (!conf) { return NULL; } SrsConfDirective* enabled = conf->get("enabled"); if (!enabled || enabled->arg0() != "on") { return NULL; } return conf->get("on_publish"); } SrsConfDirective* SrsConfig::get_vhost_on_unpublish(string vhost) { SrsConfDirective* conf = get_vhost(vhost); if (!conf) { return NULL; } conf = conf->get("http_hooks"); if (!conf) { return NULL; } SrsConfDirective* enabled = conf->get("enabled"); if (!enabled || enabled->arg0() != "on") { return NULL; } return conf->get("on_unpublish"); } SrsConfDirective* SrsConfig::get_vhost_on_play(string vhost) { SrsConfDirective* conf = get_vhost(vhost); if (!conf) { return NULL; } conf = conf->get("http_hooks"); if (!conf) { return NULL; } SrsConfDirective* enabled = conf->get("enabled"); if (!enabled || enabled->arg0() != "on") { return NULL; } return conf->get("on_play"); } SrsConfDirective* SrsConfig::get_vhost_on_stop(string vhost) { SrsConfDirective* conf = get_vhost(vhost); if (!conf) { return NULL; } conf = conf->get("http_hooks"); if (!conf) { return NULL; } SrsConfDirective* enabled = conf->get("enabled"); if (!enabled || enabled->arg0() != "on") { return NULL; } return conf->get("on_stop"); } 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; } 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(); } void SrsConfig::get_transcode_engines(SrsConfDirective* transcode, std::vector<SrsConfDirective*>& engines) { if (!transcode) { return; } for (int i = 0; i < (int)transcode->directives.size(); i++) { SrsConfDirective* conf = transcode->directives[i]; if (conf->name == "engine") { engines.push_back(conf); } } return; } 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_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(); } void SrsConfig::get_engine_vparams(SrsConfDirective* engine, std::vector<string>& vparams) { if (!engine) { return; } SrsConfDirective* conf = engine->get("vparams"); if (!conf) { return; } 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()); } } void SrsConfig::get_engine_vfilter(SrsConfDirective* engine, std::vector<string>& vfilter) { if (!engine) { return; } SrsConfDirective* conf = engine->get("vfilter"); if (!conf) { return; } 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()); } } 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()); } void SrsConfig::get_engine_aparams(SrsConfDirective* engine, std::vector<string>& aparams) { if (!engine) { return; } SrsConfDirective* conf = engine->get("aparams"); if (!conf) { return; } 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()); } } string SrsConfig::get_engine_output(SrsConfDirective* engine) { if (!engine) { return ""; } SrsConfDirective* conf = engine->get("output"); if (!conf) { return ""; } return conf->arg0(); } string SrsConfig::get_log_dir() { srs_assert(root); SrsConfDirective* conf = root->get("log_dir"); if (!conf || conf->arg0().empty()) { return "./objs/logs"; } return conf->arg0(); } int SrsConfig::get_max_connections() { srs_assert(root); SrsConfDirective* conf = root->get("max_connections"); if (!conf || conf->arg0().empty()) { return 2000; } return ::atoi(conf->arg0().c_str()); } 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; } 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_forward(string vhost) { SrsConfDirective* conf = get_vhost(vhost); if (!conf) { return NULL; } return conf->get("forward"); } 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_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"); } SrsConfDirective* SrsConfig::get_listen() { return root->get("listen"); } int SrsConfig::get_chunk_size() { SrsConfDirective* conf = root->get("chunk_size"); if (!conf) { return SRS_CONF_DEFAULT_CHUNK_SIZE; } return ::atoi(conf->arg0().c_str()); } int SrsConfig::get_pithy_print_publish() { SrsConfDirective* pithy = root->get("pithy_print"); if (!pithy) { return SRS_STAGE_PUBLISH_USER_INTERVAL_MS; } pithy = pithy->get("publish"); if (!pithy) { return SRS_STAGE_PUBLISH_USER_INTERVAL_MS; } return ::atoi(pithy->arg0().c_str()); } int SrsConfig::get_pithy_print_forwarder() { SrsConfDirective* pithy = root->get("pithy_print"); if (!pithy) { return SRS_STAGE_FORWARDER_INTERVAL_MS; } pithy = pithy->get("forwarder"); if (!pithy) { return SRS_STAGE_FORWARDER_INTERVAL_MS; } return ::atoi(pithy->arg0().c_str()); } int SrsConfig::get_pithy_print_hls() { SrsConfDirective* pithy = root->get("pithy_print"); if (!pithy) { return SRS_STAGE_HLS_INTERVAL_MS; } pithy = pithy->get("hls"); if (!pithy) { return SRS_STAGE_HLS_INTERVAL_MS; } return ::atoi(pithy->arg0().c_str()); } int SrsConfig::get_pithy_print_encoder() { SrsConfDirective* pithy = root->get("encoder"); if (!pithy) { return SRS_STAGE_ENCODER_INTERVAL_MS; } pithy = pithy->get("forwarder"); if (!pithy) { return SRS_STAGE_ENCODER_INTERVAL_MS; } return ::atoi(pithy->arg0().c_str()); } int SrsConfig::get_pithy_print_play() { SrsConfDirective* pithy = root->get("pithy_print"); if (!pithy) { return SRS_STAGE_PLAY_USER_INTERVAL_MS; } pithy = pithy->get("play"); if (!pithy) { return SRS_STAGE_PLAY_USER_INTERVAL_MS; } return ::atoi(pithy->arg0().c_str()); } 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; }