diff --git a/trunk/3rdparty/CherryPy-3.2.4.zip b/trunk/3rdparty/CherryPy-3.2.4.zip new file mode 100644 index 000000000..0f01e298e Binary files /dev/null and b/trunk/3rdparty/CherryPy-3.2.4.zip differ diff --git a/trunk/3rdparty/readme.txt b/trunk/3rdparty/readme.txt index 28142ba7d..be1a3f1c1 100755 --- a/trunk/3rdparty/readme.txt +++ b/trunk/3rdparty/readme.txt @@ -7,6 +7,9 @@ nginx-1.5.7.zip st-1.9.zip basic framework for srs. +CherryPy-3.2.4.zip + sample api server for srs. + ffmpeg-2.1.1.tar.gz yasm-1.2.0.tar.gz lame-3.99.5.tar.gz @@ -38,4 +41,7 @@ links: yasm: http://yasm.tortall.net/ http://www.tortall.net/projects/yasm/releases/yasm-1.2.0.tar.gz + cherrypy: + http://www.cherrypy.org/ + https://pypi.python.org/pypi/CherryPy/3.2.4 diff --git a/trunk/auto/depends.sh b/trunk/auto/depends.sh old mode 100644 new mode 100755 index 35647a851..fbb98fa6f --- a/trunk/auto/depends.sh +++ b/trunk/auto/depends.sh @@ -78,6 +78,31 @@ else echo "#undef SRS_HLS" >> $SRS_AUTO_HEADERS_H fi +##################################################################################### +# cherrypy for http hooks callback, CherryPy-3.2.4 +##################################################################################### +if [ $SRS_HTTP = YES ]; then + if [[ -f ${SRS_OBJS}/CherryPy-3.2.4/setup.py ]]; then + echo "CherryPy-3.2.4 is ok."; + else + echo "install CherryPy-3.2.4"; + ( + sudo rm -rf ${SRS_OBJS}/CherryPy-3.2.4 && cd ${SRS_OBJS} && + unzip -q ../3rdparty/CherryPy-3.2.4.zip && cd CherryPy-3.2.4 && + sudo python setup.py install + ) + fi + # check status + ret=$?; if [[ $ret -ne 0 ]]; then echo "build CherryPy-3.2.4 failed, ret=$ret"; exit $ret; fi + if [ ! -f ${SRS_OBJS}/nginx/sbin/nginx ]; then echo "build CherryPy-3.2.4 failed."; exit -1; fi +fi + +if [ $SRS_HTTP = YES ]; then + echo "#define SRS_HTTP" >> $SRS_AUTO_HEADERS_H +else + echo "#undef SRS_HTTP" >> $SRS_AUTO_HEADERS_H +fi + ##################################################################################### # openssl, for rtmp complex handshake ##################################################################################### diff --git a/trunk/auto/options.sh b/trunk/auto/options.sh old mode 100644 new mode 100755 index f2d05d269..4030f47cc --- a/trunk/auto/options.sh +++ b/trunk/auto/options.sh @@ -5,11 +5,13 @@ help=no SRS_HLS=RESERVED SRS_SSL=RESERVED SRS_FFMPEG=RESERVED +SRS_HTTP=RESERVED # TODO: remove the default to yes. SRS_HLS=YES SRS_SSL=YES SRS_FFMPEG=YES +SRS_HTTP=YES opt= @@ -28,10 +30,12 @@ do --with-ssl) SRS_SSL=YES ;; --with-hls) SRS_HLS=YES ;; --with-ffmpeg) SRS_FFMPEG=YES ;; + --with-http) SRS_HTTP=YES ;; --without-ssl) SRS_SSL=NO ;; --without-hls) SRS_HLS=NO ;; --without-ffmpeg) SRS_FFMPEG=NO ;; + --without-http) SRS_HTTP=NO ;; *) echo "$0: error: invalid option \"$option\"" @@ -50,10 +54,13 @@ cat << END --with-ssl enable rtmp complex handshake, requires openssl-devel installed. to delivery h264 video and aac audio to flash player. + --with-http enable http hooks, build cherrypy as demo api server. + srs will call the http hooks, such as: on_connect. --with-hls enable hls streaming, build nginx as http server for hls. --without-ssl disable rtmp complex handshake. --without-hls disable hls, rtmp streaming only. + --without-http disable http, http hooks callback. END @@ -73,6 +80,10 @@ if [ $SRS_FFMPEG = RESERVED ]; then echo "you must specifies the ffmpeg, see: ./configure --help"; __check_ok=NO fi +if [ $SRS_HTTP = RESERVED ]; then + echo "you must specifies the http, see: ./configure --help"; + __check_ok=NO +fi if [ $__check_ok = NO ]; then exit 1; fi \ No newline at end of file diff --git a/trunk/conf/srs.conf b/trunk/conf/srs.conf index 5d6864bc9..f3ff06a39 100755 --- a/trunk/conf/srs.conf +++ b/trunk/conf/srs.conf @@ -81,14 +81,14 @@ vhost __defaultVhost__ { vhost dev { enabled on; gop_cache on; - hls on; + hls off; hls_path ./objs/nginx/html; hls_fragment 5; hls_window 30; - forward 127.0.0.1:19350; + #forward 127.0.0.1:19350; #forward 127.0.0.1:1936; transcode { - enabled on; + enabled off; ffmpeg ./objs/ffmpeg/bin/ffmpeg; engine dev { enabled off; diff --git a/trunk/configure b/trunk/configure index a8aec63c9..310e2a53b 100755 --- a/trunk/configure +++ b/trunk/configure @@ -15,6 +15,15 @@ BLACK="\\e[0m" # parse user options. . auto/options.sh +# if specifies http, requires sudo to install the CherryPy. +if [ $SRS_HTTP = YES ]; then + sudo echo "" >/dev/null 2>&1 + ret=$?; if [[ 0 -ne $ret ]]; then echo + "--with-http requires sudoer, ret=$ret"; + exit $ret; + fi +fi + # clean the exists if [[ -f Makefile ]]; then make clean @@ -22,6 +31,8 @@ fi # generate the audo headers file. echo "#define SRS_CONFIGURE \"${SRS_CONFIGURE}\"" > $SRS_AUTO_HEADERS_H +echo "#define SRS_BUILD_DATE \"`date \"+%Y-%m-%d %H:%M:%S\"`\"" >> $SRS_AUTO_HEADERS_H +echo "#define SRS_BUILD_TS \"`date +%s`\"" >> $SRS_AUTO_HEADERS_H # apply user options. . auto/depends.sh @@ -143,6 +154,11 @@ if [ $SRS_FFMPEG = YES ]; then else echo -e "${YELLOW}warning: without live stream transcoding over FFMPEG support${BLACK}" fi +if [ $SRS_HTTP = YES ]; then + echo -e "${GREEN}http hooks callback over CherryPy is enabled${BLACK}" +else + echo -e "${YELLOW}warning: without http hooks callback over CherryPy support${BLACK}" +fi # mkdir dirs mkdir -p ${SRS_OBJS}/logs diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp old mode 100644 new mode 100755 index 7e2f92459..13b465866 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -1,105 +1,105 @@ -/* -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. -*/ - -#ifndef SRS_CORE_HPP -#define SRS_CORE_HPP - -/* -#include -*/ - -/** -* the core provides the common defined macros, utilities, -* user must include the srs_core.hpp before any header, or maybe -* build failed. -*/ - -// for int64_t print using PRId64 format. -#ifndef __STDC_FORMAT_MACROS - #define __STDC_FORMAT_MACROS -#endif -#include - -#include -#define srs_assert(expression) assert(expression) - -#include -#include - -// generated by configure. -#include - -// free the p and set to NULL. -// p must be a T*. -#define srs_freep(p) \ - if (p) { \ - delete p; \ - p = NULL; \ - } \ - (void)0 -// free the p which represents a array -#define srs_freepa(p) \ - if (p) { \ - delete[] p; \ - p = NULL; \ - } \ - (void)0 - -// current release version -#define RTMP_SIG_SRS_VERSION "0.5.0" -// server info. -#define RTMP_SIG_SRS_KEY "srs" -#define RTMP_SIG_SRS_ROLE "origin server" -#define RTMP_SIG_SRS_NAME RTMP_SIG_SRS_KEY"(simple rtmp server)" -#define RTMP_SIG_SRS_URL "https://"RTMP_SIG_SRS_URL_SHORT -#define RTMP_SIG_SRS_URL_SHORT "github.com/winlinvip/simple-rtmp-server" -#define RTMP_SIG_SRS_WEB "http://blog.csdn.net/win_lin" -#define RTMP_SIG_SRS_EMAIL "winterserver@126.com" -#define RTMP_SIG_SRS_LICENSE "The MIT License (MIT)" -#define RTMP_SIG_SRS_COPYRIGHT "Copyright (c) 2013 winlin" -#define RTMP_SIG_SRS_CONTRIBUTOR "winlin" - -// compare -#define srs_min(a, b) (((a) < (b))? (a) : (b)) -#define srs_max(a, b) (((a) < (b))? (b) : (a)) - -// get current system time in ms, use cache to avoid performance problem -extern int64_t srs_get_system_time_ms(); -// the deamon st-thread will update it. -extern void srs_update_system_time_ms(); - -// signal defines. -#define SIGNAL_RELOAD SIGHUP - -#include -// replace utility -extern std::string srs_replace(std::string str, std::string old_str, std::string new_str); -// dns resolve utility, return the resolved ip address. -extern std::string srs_dns_resolve(std::string host); -// resolve the vhost in query string -// @param app, may contains the vhost in query string format: -// app?vhost=request_vhost -// app...vhost...request_vhost -extern void srs_vhost_resolve(std::string& vhost, std::string& app); - +/* +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. +*/ + +#ifndef SRS_CORE_HPP +#define SRS_CORE_HPP + +/* +#include +*/ + +/** +* the core provides the common defined macros, utilities, +* user must include the srs_core.hpp before any header, or maybe +* build failed. +*/ + +// for int64_t print using PRId64 format. +#ifndef __STDC_FORMAT_MACROS + #define __STDC_FORMAT_MACROS +#endif +#include + +#include +#define srs_assert(expression) assert(expression) + +#include +#include + +// generated by configure. +#include + +// free the p and set to NULL. +// p must be a T*. +#define srs_freep(p) \ + if (p) { \ + delete p; \ + p = NULL; \ + } \ + (void)0 +// free the p which represents a array +#define srs_freepa(p) \ + if (p) { \ + delete[] p; \ + p = NULL; \ + } \ + (void)0 + +// current release version +#define RTMP_SIG_SRS_VERSION "0.8.0" +// server info. +#define RTMP_SIG_SRS_KEY "srs" +#define RTMP_SIG_SRS_ROLE "origin server" +#define RTMP_SIG_SRS_NAME RTMP_SIG_SRS_KEY"(simple rtmp server)" +#define RTMP_SIG_SRS_URL "https://"RTMP_SIG_SRS_URL_SHORT +#define RTMP_SIG_SRS_URL_SHORT "github.com/winlinvip/simple-rtmp-server" +#define RTMP_SIG_SRS_WEB "http://blog.csdn.net/win_lin" +#define RTMP_SIG_SRS_EMAIL "winterserver@126.com" +#define RTMP_SIG_SRS_LICENSE "The MIT License (MIT)" +#define RTMP_SIG_SRS_COPYRIGHT "Copyright (c) 2013 winlin" +#define RTMP_SIG_SRS_CONTRIBUTOR "winlin" + +// compare +#define srs_min(a, b) (((a) < (b))? (a) : (b)) +#define srs_max(a, b) (((a) < (b))? (b) : (a)) + +// get current system time in ms, use cache to avoid performance problem +extern int64_t srs_get_system_time_ms(); +// the deamon st-thread will update it. +extern void srs_update_system_time_ms(); + +// signal defines. +#define SIGNAL_RELOAD SIGHUP + +#include +// replace utility +extern std::string srs_replace(std::string str, std::string old_str, std::string new_str); +// dns resolve utility, return the resolved ip address. +extern std::string srs_dns_resolve(std::string host); +// resolve the vhost in query string +// @param app, may contains the vhost in query string format: +// app?vhost=request_vhost +// app...vhost...request_vhost +extern void srs_vhost_resolve(std::string& vhost, std::string& app); + #endif \ No newline at end of file diff --git a/trunk/src/core/srs_core_config.cpp b/trunk/src/core/srs_core_config.cpp old mode 100644 new mode 100755 index e7e5e3651..a7aaf1d96 --- a/trunk/src/core/srs_core_config.cpp +++ b/trunk/src/core/srs_core_config.cpp @@ -1,1235 +1,1235 @@ -/* -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 - -#include -#include -#include -#include -// file operations. -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -#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); -} - -#define CONF_BUFFER_SIZE 1024 * 1024 - -SrsFileBuffer::SrsFileBuffer() -{ - fd = -1; - line = 0; - - pos = last = start = new char[CONF_BUFFER_SIZE]; - end = start + CONF_BUFFER_SIZE; -} - -SrsFileBuffer::~SrsFileBuffer() -{ - if (fd > 0) { - close(fd); - } - srs_freepa(start); -} - -int SrsFileBuffer::open(const char* filename) -{ - assert(fd == -1); - - if ((fd = ::open(filename, O_RDONLY, 0)) < 0) { - srs_error("open conf file error. errno=%d(%s)", errno, strerror(errno)); - return ERROR_SYSTEM_CONFIG_INVALID; - } - - line = 1; - - return ERROR_SUCCESS; -} - -SrsConfDirective::SrsConfDirective() -{ -} - -SrsConfDirective::~SrsConfDirective() -{ - std::vector::iterator it; - for (it = directives.begin(); it != directives.end(); ++it) { - SrsConfDirective* directive = *it; - srs_freep(directive); - } - directives.clear(); -} - -std::string SrsConfDirective::arg0() -{ - if (args.size() > 0) { - return args.at(0); - } - - return ""; -} - -std::string SrsConfDirective::arg1() -{ - if (args.size() > 1) { - return args.at(1); - } - - return ""; -} - -std::string SrsConfDirective::arg2() -{ - if (args.size() > 2) { - return args.at(2); - } - - return ""; -} - -SrsConfDirective* SrsConfDirective::at(int index) -{ - return directives.at(index); -} - -SrsConfDirective* SrsConfDirective::get(std::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; -} - -int SrsConfDirective::parse(const char* filename) -{ - int ret = ERROR_SUCCESS; - - SrsFileBuffer buffer; - - if ((ret = buffer.open(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 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& 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 ((ret = refill_buffer(buffer, d_quoted, s_quoted, startline, pstart)) != ERROR_SUCCESS) { - if (!args.empty() || !last_space) { - srs_error("line %d: unexpected end of file, expecting ; or \"}\"", buffer->line); - return ERROR_SYSTEM_CONFIG_INVALID; - } - 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; - - args.push_back(word); - srs_freepa(word); - - if (ch == ';') { - return ERROR_SYSTEM_CONFIG_DIRECTIVE; - } - if (ch == '{') { - return ERROR_SYSTEM_CONFIG_BLOCK_START; - } - } - } - } - - return ret; -} - -int SrsConfDirective::refill_buffer(SrsFileBuffer* buffer, bool d_quoted, bool s_quoted, int startline, char*& pstart) -{ - int ret = ERROR_SUCCESS; - - if (buffer->pos < buffer->last) { - return ret; - } - - int size = FILE_SIZE(buffer->fd) - FILE_OFFSET(buffer->fd); - if (size > CONF_BUFFER_SIZE) { - ret = ERROR_SYSTEM_CONFIG_TOO_LARGE; - srs_error("config file too large, max=%d, actual=%d, ret=%d", - CONF_BUFFER_SIZE, size, ret); - return ret; - } - - if (size <= 0) { - return ERROR_SYSTEM_CONFIG_EOF; - } - - int len = buffer->pos - buffer->start; - if (len >= CONF_BUFFER_SIZE) { - buffer->line = startline; - - if (!d_quoted && !s_quoted) { - srs_error("line %d: too long parameter \"%*s...\" started", - buffer->line, 10, buffer->start); - - } else { - srs_error("line %d: too long parameter, " - "probably missing terminating '%c' character", buffer->line, d_quoted? '"':'\''); - } - return ERROR_SYSTEM_CONFIG_INVALID; - } - - if (len) { - memmove(buffer->start, pstart, len); - } - - size = srs_min(size, buffer->end - (buffer->start + len)); - int n = read(buffer->fd, buffer->start + len, size); - if (n != size) { - srs_error("read file read error. expect %d, actual %d bytes.", size, n); - return ERROR_SYSTEM_CONFIG_INVALID; - } - - buffer->pos = buffer->start + len; - buffer->last = buffer->pos + n; - pstart = buffer->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::iterator it; - - // merge config: listen - if (!srs_directive_equals(root->get("listen"), old_root->get("listen"))) { - for (it = subscribes.begin(); it != subscribes.end(); ++it) { - SrsReloadHandler* 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) { - SrsReloadHandler* 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."); - } - - return ret; -} - -void SrsConfig::subscribe(SrsReloadHandler* 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(SrsReloadHandler* handler) -{ - std::vector::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()); -} - -SrsConfDirective* SrsConfig::get_vhost(std::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; -} - -bool SrsConfig::get_vhost_enabled(std::string vhost) -{ - SrsConfDirective* vhost_conf = get_vhost(vhost); - - if (!vhost_conf) { - return true; - } - - SrsConfDirective* conf = vhost_conf->get("enabled"); - if (!conf) { - return true; - } - - if (conf->arg0() == "off") { - return false; - } - - return true; -} - -SrsConfDirective* SrsConfig::get_transcode(std::string vhost, std::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; -} - -std::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& 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; -} - -std::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()); -} - -std::string SrsConfig::get_engine_vprofile(SrsConfDirective* engine) -{ - if (!engine) { - return ""; - } - - SrsConfDirective* conf = engine->get("vprofile"); - if (!conf) { - return ""; - } - - return conf->arg0(); -} - -std::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& 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& 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()); - } -} - -std::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& 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()); - } -} - -std::string SrsConfig::get_engine_output(SrsConfDirective* engine) -{ - if (!engine) { - return ""; - } - - SrsConfDirective* conf = engine->get("output"); - if (!conf) { - return ""; - } - - return conf->arg0(); -} - -std::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()); -} - -SrsConfDirective* SrsConfig::get_gop_cache(std::string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("gop_cache"); -} - -SrsConfDirective* SrsConfig::get_forward(std::string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("forward"); -} - -SrsConfDirective* SrsConfig::get_hls(std::string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("hls"); -} - -bool SrsConfig::get_hls_enabled(std::string vhost) -{ - SrsConfDirective* hls = get_hls(vhost); - - if (!hls) { - return true; - } - - if (hls->arg0() == "off") { - return false; - } - - return true; -} - -SrsConfDirective* SrsConfig::get_hls_path(std::string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("hls_path"); -} - -SrsConfDirective* SrsConfig::get_hls_fragment(std::string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("hls_fragment"); -} - -SrsConfDirective* SrsConfig::get_hls_window(std::string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("hls_window"); -} - -SrsConfDirective* SrsConfig::get_refer(std::string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("refer"); -} - -SrsConfDirective* SrsConfig::get_refer_play(std::string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("refer_play"); -} - -SrsConfDirective* SrsConfig::get_refer_publish(std::string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("refer_publish"); -} - -SrsConfDirective* SrsConfig::get_listen() -{ - return root->get("listen"); -} - -SrsConfDirective* SrsConfig::get_chunk_size() -{ - return root->get("chunk_size"); -} - -SrsConfDirective* SrsConfig::get_pithy_print_publish() -{ - SrsConfDirective* pithy = root->get("pithy_print"); - if (!pithy) { - return NULL; - } - - return pithy->get("publish"); -} - -SrsConfDirective* SrsConfig::get_pithy_print_forwarder() -{ - SrsConfDirective* pithy = root->get("pithy_print"); - if (!pithy) { - return NULL; - } - - return pithy->get("forwarder"); -} - -SrsConfDirective* SrsConfig::get_pithy_print_hls() -{ - SrsConfDirective* pithy = root->get("pithy_print"); - if (!pithy) { - return NULL; - } - - return pithy->get("hls"); -} - -SrsConfDirective* SrsConfig::get_pithy_print_encoder() -{ - SrsConfDirective* pithy = root->get("encoder"); - if (!pithy) { - return NULL; - } - - return pithy->get("forwarder"); -} - -SrsConfDirective* SrsConfig::get_pithy_print_play() -{ - SrsConfDirective* pithy = root->get("pithy_print"); - if (!pithy) { - return NULL; - } - - return pithy->get("play"); -} - -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 other config. - // TODO: check hls. - // TODO: check ssl. - // TODO: check ffmpeg. - - 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" - "Configuration: "SRS_CONFIGURE"\n" - "Usage: %s [-h?vV] [-c ]\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]); -} - -bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b) -{ - 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; -} - +/* +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 + +#include +#include +#include +#include +// file operations. +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#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); +} + +#define CONF_BUFFER_SIZE 1024 * 1024 + +SrsFileBuffer::SrsFileBuffer() +{ + fd = -1; + line = 0; + + pos = last = start = new char[CONF_BUFFER_SIZE]; + end = start + CONF_BUFFER_SIZE; +} + +SrsFileBuffer::~SrsFileBuffer() +{ + if (fd > 0) { + close(fd); + } + srs_freepa(start); +} + +int SrsFileBuffer::open(const char* filename) +{ + assert(fd == -1); + + if ((fd = ::open(filename, O_RDONLY, 0)) < 0) { + srs_error("open conf file error. errno=%d(%s)", errno, strerror(errno)); + return ERROR_SYSTEM_CONFIG_INVALID; + } + + line = 1; + + return ERROR_SUCCESS; +} + +SrsConfDirective::SrsConfDirective() +{ +} + +SrsConfDirective::~SrsConfDirective() +{ + std::vector::iterator it; + for (it = directives.begin(); it != directives.end(); ++it) { + SrsConfDirective* directive = *it; + srs_freep(directive); + } + directives.clear(); +} + +std::string SrsConfDirective::arg0() +{ + if (args.size() > 0) { + return args.at(0); + } + + return ""; +} + +std::string SrsConfDirective::arg1() +{ + if (args.size() > 1) { + return args.at(1); + } + + return ""; +} + +std::string SrsConfDirective::arg2() +{ + if (args.size() > 2) { + return args.at(2); + } + + return ""; +} + +SrsConfDirective* SrsConfDirective::at(int index) +{ + return directives.at(index); +} + +SrsConfDirective* SrsConfDirective::get(std::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; +} + +int SrsConfDirective::parse(const char* filename) +{ + int ret = ERROR_SUCCESS; + + SrsFileBuffer buffer; + + if ((ret = buffer.open(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 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& 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 ((ret = refill_buffer(buffer, d_quoted, s_quoted, startline, pstart)) != ERROR_SUCCESS) { + if (!args.empty() || !last_space) { + srs_error("line %d: unexpected end of file, expecting ; or \"}\"", buffer->line); + return ERROR_SYSTEM_CONFIG_INVALID; + } + 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; + + args.push_back(word); + srs_freepa(word); + + if (ch == ';') { + return ERROR_SYSTEM_CONFIG_DIRECTIVE; + } + if (ch == '{') { + return ERROR_SYSTEM_CONFIG_BLOCK_START; + } + } + } + } + + return ret; +} + +int SrsConfDirective::refill_buffer(SrsFileBuffer* buffer, bool d_quoted, bool s_quoted, int startline, char*& pstart) +{ + int ret = ERROR_SUCCESS; + + if (buffer->pos < buffer->last) { + return ret; + } + + int size = FILE_SIZE(buffer->fd) - FILE_OFFSET(buffer->fd); + if (size > CONF_BUFFER_SIZE) { + ret = ERROR_SYSTEM_CONFIG_TOO_LARGE; + srs_error("config file too large, max=%d, actual=%d, ret=%d", + CONF_BUFFER_SIZE, size, ret); + return ret; + } + + if (size <= 0) { + return ERROR_SYSTEM_CONFIG_EOF; + } + + int len = buffer->pos - buffer->start; + if (len >= CONF_BUFFER_SIZE) { + buffer->line = startline; + + if (!d_quoted && !s_quoted) { + srs_error("line %d: too long parameter \"%*s...\" started", + buffer->line, 10, buffer->start); + + } else { + srs_error("line %d: too long parameter, " + "probably missing terminating '%c' character", buffer->line, d_quoted? '"':'\''); + } + return ERROR_SYSTEM_CONFIG_INVALID; + } + + if (len) { + memmove(buffer->start, pstart, len); + } + + size = srs_min(size, buffer->end - (buffer->start + len)); + int n = read(buffer->fd, buffer->start + len, size); + if (n != size) { + srs_error("read file read error. expect %d, actual %d bytes.", size, n); + return ERROR_SYSTEM_CONFIG_INVALID; + } + + buffer->pos = buffer->start + len; + buffer->last = buffer->pos + n; + pstart = buffer->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::iterator it; + + // merge config: listen + if (!srs_directive_equals(root->get("listen"), old_root->get("listen"))) { + for (it = subscribes.begin(); it != subscribes.end(); ++it) { + SrsReloadHandler* 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) { + SrsReloadHandler* 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."); + } + + return ret; +} + +void SrsConfig::subscribe(SrsReloadHandler* 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(SrsReloadHandler* handler) +{ + std::vector::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()); +} + +SrsConfDirective* SrsConfig::get_vhost(std::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; +} + +bool SrsConfig::get_vhost_enabled(std::string vhost) +{ + SrsConfDirective* vhost_conf = get_vhost(vhost); + + if (!vhost_conf) { + return true; + } + + SrsConfDirective* conf = vhost_conf->get("enabled"); + if (!conf) { + return true; + } + + if (conf->arg0() == "off") { + return false; + } + + return true; +} + +SrsConfDirective* SrsConfig::get_transcode(std::string vhost, std::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; +} + +std::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& 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; +} + +std::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()); +} + +std::string SrsConfig::get_engine_vprofile(SrsConfDirective* engine) +{ + if (!engine) { + return ""; + } + + SrsConfDirective* conf = engine->get("vprofile"); + if (!conf) { + return ""; + } + + return conf->arg0(); +} + +std::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& 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& 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()); + } +} + +std::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& 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()); + } +} + +std::string SrsConfig::get_engine_output(SrsConfDirective* engine) +{ + if (!engine) { + return ""; + } + + SrsConfDirective* conf = engine->get("output"); + if (!conf) { + return ""; + } + + return conf->arg0(); +} + +std::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()); +} + +SrsConfDirective* SrsConfig::get_gop_cache(std::string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("gop_cache"); +} + +SrsConfDirective* SrsConfig::get_forward(std::string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("forward"); +} + +SrsConfDirective* SrsConfig::get_hls(std::string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("hls"); +} + +bool SrsConfig::get_hls_enabled(std::string vhost) +{ + SrsConfDirective* hls = get_hls(vhost); + + if (!hls) { + return true; + } + + if (hls->arg0() == "off") { + return false; + } + + return true; +} + +SrsConfDirective* SrsConfig::get_hls_path(std::string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("hls_path"); +} + +SrsConfDirective* SrsConfig::get_hls_fragment(std::string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("hls_fragment"); +} + +SrsConfDirective* SrsConfig::get_hls_window(std::string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("hls_window"); +} + +SrsConfDirective* SrsConfig::get_refer(std::string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("refer"); +} + +SrsConfDirective* SrsConfig::get_refer_play(std::string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("refer_play"); +} + +SrsConfDirective* SrsConfig::get_refer_publish(std::string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("refer_publish"); +} + +SrsConfDirective* SrsConfig::get_listen() +{ + return root->get("listen"); +} + +SrsConfDirective* SrsConfig::get_chunk_size() +{ + return root->get("chunk_size"); +} + +SrsConfDirective* SrsConfig::get_pithy_print_publish() +{ + SrsConfDirective* pithy = root->get("pithy_print"); + if (!pithy) { + return NULL; + } + + return pithy->get("publish"); +} + +SrsConfDirective* SrsConfig::get_pithy_print_forwarder() +{ + SrsConfDirective* pithy = root->get("pithy_print"); + if (!pithy) { + return NULL; + } + + return pithy->get("forwarder"); +} + +SrsConfDirective* SrsConfig::get_pithy_print_hls() +{ + SrsConfDirective* pithy = root->get("pithy_print"); + if (!pithy) { + return NULL; + } + + return pithy->get("hls"); +} + +SrsConfDirective* SrsConfig::get_pithy_print_encoder() +{ + SrsConfDirective* pithy = root->get("encoder"); + if (!pithy) { + return NULL; + } + + return pithy->get("forwarder"); +} + +SrsConfDirective* SrsConfig::get_pithy_print_play() +{ + SrsConfDirective* pithy = root->get("pithy_print"); + if (!pithy) { + return NULL; + } + + return pithy->get("play"); +} + +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 other config. + // TODO: check hls. + // TODO: check ssl. + // TODO: check ffmpeg. + + 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 ]\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]); +} + +bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b) +{ + 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; +} +