From 2aa15a11345912b8d7a09d3fe7d0b287f254488b Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 2 Nov 2013 17:55:59 +0800 Subject: [PATCH] support nginx-style config file --- README.md | 1 + trunk/conf/srs.conf | 5 + trunk/configure | 3 +- trunk/src/core/srs_core.hpp | 6 +- trunk/src/core/srs_core_client.cpp | 4 +- trunk/src/core/srs_core_config.cpp | 471 +++++++++++++++++++++++++++++ trunk/src/core/srs_core_config.hpp | 92 ++++++ trunk/src/core/srs_core_error.hpp | 5 + trunk/src/main/srs_main_server.cpp | 20 +- trunk/src/srs/srs.upp | 2 + 10 files changed, 589 insertions(+), 20 deletions(-) create mode 100755 trunk/conf/srs.conf create mode 100755 trunk/src/core/srs_core_config.cpp create mode 100755 trunk/src/core/srs_core_config.hpp diff --git a/README.md b/README.md index b82dc8b00..ac891996a 100755 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ url: rtmp://127.0.0.1:1935/live/livestream * nginx v1.5.0: 139524 lines
### History +* v0.3, 2013-11-02, support config file in nginx-conf style. * v0.3, 2013-10-29, support pithy print log message specified by stage. * v0.3, 2013-10-28, support librtmp without extended-timestamp in 0xCX chunk packet. * v0.3, 2013-10-27, support cache last gop for client fast startup. diff --git a/trunk/conf/srs.conf b/trunk/conf/srs.conf new file mode 100755 index 000000000..783d05581 --- /dev/null +++ b/trunk/conf/srs.conf @@ -0,0 +1,5 @@ +listen 1935; +vhost __defaultVhost__ { + application live { + } +} diff --git a/trunk/configure b/trunk/configure index 59f674a54..ad23cb03b 100755 --- a/trunk/configure +++ b/trunk/configure @@ -90,7 +90,8 @@ MODULE_FILES=("srs_core" "srs_core_log" "srs_core_server" "srs_core_rtmp" "srs_core_socket" "srs_core_buffer" "srs_core_auto_free" "srs_core_protocol" "srs_core_amf0" "srs_core_stream" "srs_core_source" "srs_core_codec" - "srs_core_complex_handshake" "srs_core_pithy_print") + "srs_core_complex_handshake" "srs_core_pithy_print" + "srs_core_config") MODULE_DIR="src/core" . auto/modules.sh CORE_OBJS="${MODULE_OBJS[@]}" diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index d3ddd53a7..e0ecf021d 100755 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -63,7 +63,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (void)0 // current release version -#define RTMP_SIG_SRS_VERSION "0.2" +#define RTMP_SIG_SRS_VERSION "0.2.0" // server info. #define RTMP_SIG_SRS_KEY "srs" #define RTMP_SIG_SRS_ROLE "origin server" @@ -76,8 +76,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define RTMP_SIG_SRS_COPYRIGHT "Copyright (c) 2013 winlin" // compare -#define srs_min(a, b) ((a < b)? a : b) -#define srs_max(a, b) ((a < b)? b : a) +#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(); diff --git a/trunk/src/core/srs_core_client.cpp b/trunk/src/core/srs_core_client.cpp index 3bdca698d..782e0a872 100755 --- a/trunk/src/core/srs_core_client.cpp +++ b/trunk/src/core/srs_core_client.cpp @@ -35,7 +35,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #define SRS_PULSE_TIMEOUT_MS 100 -#define SRS_SEND_TIMEOUT_MS 5000 +#define SRS_SEND_TIMEOUT_MS 5000000L #define SRS_RECV_TIMEOUT_MS SRS_SEND_TIMEOUT_MS SrsClient::SrsClient(SrsServer* srs_server, st_netfd_t client_stfd) @@ -63,7 +63,7 @@ int SrsClient::do_cycle() srs_error("get peer ip failed. ret=%d", ret); return ret; } - srs_trace("get peer ip success. ip=%s, send_to=%d, recv_to=%d", + srs_trace("get peer ip success. ip=%s, send_to=%"PRId64", recv_to=%"PRId64"", ip, SRS_SEND_TIMEOUT_MS, SRS_RECV_TIMEOUT_MS); rtmp->set_recv_timeout(SRS_RECV_TIMEOUT_MS * 1000); diff --git a/trunk/src/core/srs_core_config.cpp b/trunk/src/core/srs_core_config.cpp new file mode 100755 index 000000000..dc11eed6e --- /dev/null +++ b/trunk/src/core/srs_core_config.cpp @@ -0,0 +1,471 @@ +/* +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 + +#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 4096 + +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) { + fprintf(stderr, "open conf file error. errno=%d(%s)\n", errno, strerror(errno)); + return ERROR_SYSTEM_CONFIG_INVALID; + } + + 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(); +} + +SrsConfDirective* SrsConfDirective::at(int index) +{ + return directives.at(index); +} + +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) { + fprintf(stderr, "line %d: unexpected \"}\"\n", buffer->line); + return ret; + } + return ERROR_SUCCESS; + } + if (ret == ERROR_SYSTEM_CONFIG_EOF) { + if (type == parse_block) { + fprintf(stderr, "line %d: unexpected end of file, expecting \"}\"\n", buffer->line); + return ret; + } + return ERROR_SUCCESS; + } + + if (args.empty()) { + fprintf(stderr, "line %d: empty directive.\n", buffer->line); + return ret; + } + + // build directive tree. + SrsConfDirective* directive = new SrsConfDirective(); + + 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) { + fprintf(stderr, "line %d: unexpected end of file, expecting ; or \"}\"\n", 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; + } + fprintf(stderr, "line %d: unexpected '%c'\n", 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) { + fprintf(stderr, "line %d: unexpected ';'\n", buffer->line); + return ERROR_SYSTEM_CONFIG_INVALID; + } + return ERROR_SYSTEM_CONFIG_DIRECTIVE; + case '{': + if (args.size() == 0) { + fprintf(stderr, "line %d: unexpected '{'\n", buffer->line); + return ERROR_SYSTEM_CONFIG_INVALID; + } + return ERROR_SYSTEM_CONFIG_BLOCK_START; + case '}': + if (args.size() != 0) { + fprintf(stderr, "line %d: unexpected '}'\n", 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 <= 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) { + fprintf(stderr, "line %d: too long parameter \"%*s...\" started\n", + buffer->line, 10, buffer->start); + + } else { + fprintf(stderr, "line %d: too long parameter, " + "probably missing terminating '%c' character\n", 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) { + fprintf(stderr, "read file read error. expect %d, actual %d bytes.\n", size, n); + return ERROR_SYSTEM_CONFIG_INVALID; + } + + buffer->pos = buffer->start + len; + buffer->last = buffer->pos + n; + pstart = buffer->start; + + return ret; +} + +Config::Config() +{ + show_help = false; + show_version = false; + config_file = NULL; +} + +Config::~Config() +{ +} + +// see: ngx_get_options +int Config::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) { + fprintf(stderr, "%s\n", RTMP_SIG_SRS_VERSION); + } + + if (show_help || show_version) { + exit(0); + } + + if (!config_file) { + fprintf(stderr, "config file not specified, see help: %s -h\n", argv[0]); + return ERROR_SYSTEM_CONFIG_INVALID; + } + + SrsConfDirective root; + root.name = "root"; + + if ((ret = root.parse(config_file)) != ERROR_SUCCESS) { + return ret; + } + + return ret; +} + +int Config::parse_argv(int& i, char** argv) +{ + int ret = ERROR_SUCCESS; + + char* p = argv[i]; + + if (*p++ != '-') { + fprintf(stderr, "invalid options(index=%d, value=%s), " + "must starts with -, see help: %s -h\n", i, argv[i], argv[0]); + return ERROR_SYSTEM_CONFIG_INVALID; + } + + 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; + } + fprintf(stderr, "option \"-c\" requires parameter\n"); + return ERROR_SYSTEM_CONFIG_INVALID; + default: + fprintf(stderr, "invalid option: \"%c\", see help: %s -h\n", *(p - 1), argv[0]); + return ERROR_SYSTEM_CONFIG_INVALID; + } + } + + return ret; +} + +void Config::print_help(char** argv) +{ + fprintf(stderr, RTMP_SIG_SRS_NAME" "RTMP_SIG_SRS_VERSION + " Copyright (c) 2013 winlin\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", + argv[0]); +} + diff --git a/trunk/src/core/srs_core_config.hpp b/trunk/src/core/srs_core_config.hpp new file mode 100755 index 000000000..5ea6595af --- /dev/null +++ b/trunk/src/core/srs_core_config.hpp @@ -0,0 +1,92 @@ +/* +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_CONIFG_HPP +#define SRS_CORE_CONIFG_HPP + +/* +#include +*/ +#include + +#include +#include + +/** +* the config parser. +*/ +class Config +{ +private: + bool show_help; + bool show_version; + char* config_file; +public: + Config(); + virtual ~Config(); +public: + virtual int parse_options(int argc, char** argv); +private: + virtual int parse_argv(int& i, char** argv); + virtual void print_help(char** argv); +}; + +class SrsFileBuffer +{ +public: + int fd; + int line; + // start of buffer. + char* start; + // end of buffer. + char* end; + // current consumed position. + char* pos; + // last available position. + char* last; + + SrsFileBuffer(); + virtual ~SrsFileBuffer(); + virtual int open(const char* filename); +}; + +class SrsConfDirective +{ +public: + std::string name; + std::vector args; + std::vector directives; +public: + SrsConfDirective(); + virtual ~SrsConfDirective(); + SrsConfDirective* at(int index); +public: + virtual int parse(const char* filename); +public: + enum SrsDirectiveType{parse_file, parse_block}; + virtual int parse_conf(SrsFileBuffer* buffer, SrsDirectiveType type); + virtual int read_token(SrsFileBuffer* buffer, std::vector& args); + virtual int refill_buffer(SrsFileBuffer* buffer, bool d_quoted, bool s_quoted, int startline, char*& pstart); +}; + +#endif \ No newline at end of file diff --git a/trunk/src/core/srs_core_error.hpp b/trunk/src/core/srs_core_error.hpp index 427022592..e51cde12b 100755 --- a/trunk/src/core/srs_core_error.hpp +++ b/trunk/src/core/srs_core_error.hpp @@ -71,6 +71,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define ERROR_SYSTEM_CLIENT_INVALID 402 #define ERROR_SYSTEM_ASSERT_FAILED 403 #define ERROR_SYSTEM_SIZE_NEGATIVE 404 +#define ERROR_SYSTEM_CONFIG_INVALID 405 +#define ERROR_SYSTEM_CONFIG_DIRECTIVE 406 +#define ERROR_SYSTEM_CONFIG_BLOCK_START 407 +#define ERROR_SYSTEM_CONFIG_BLOCK_END 408 +#define ERROR_SYSTEM_CONFIG_EOF 409 // see librtmp. // failed when open ssl create the dh diff --git a/trunk/src/main/srs_main_server.cpp b/trunk/src/main/srs_main_server.cpp index 0243dde62..557016e9a 100755 --- a/trunk/src/main/srs_main_server.cpp +++ b/trunk/src/main/srs_main_server.cpp @@ -24,26 +24,18 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #include +#include #include int main(int argc, char** argv){ int ret = ERROR_SUCCESS; - if (argc <= 1) { - printf(RTMP_SIG_SRS_NAME" "RTMP_SIG_SRS_VERSION - " Copyright (c) 2013 winlin\n" - "Usage: %s \n" - "\n" - RTMP_SIG_SRS_WEB"\n" - RTMP_SIG_SRS_URL"\n" - "Email: "RTMP_SIG_SRS_EMAIL"\n", - argv[0]); - exit(1); - } + Config config; - int listen_port = ::atoi(argv[1]); - srs_trace("listen_port=%d", listen_port); + if ((ret = config.parse_options(argc, argv)) != ERROR_SUCCESS) { + return ret; + } SrsServer server; @@ -51,7 +43,7 @@ int main(int argc, char** argv){ return ret; } - if ((ret = server.listen(listen_port)) != ERROR_SUCCESS) { + if ((ret = server.listen(1935)) != ERROR_SUCCESS) { return ret; } diff --git a/trunk/src/srs/srs.upp b/trunk/src/srs/srs.upp index d5ff1ad2d..a9120396b 100755 --- a/trunk/src/srs/srs.upp +++ b/trunk/src/srs/srs.upp @@ -10,6 +10,8 @@ file ..\core\srs_core_auto_free.cpp, ..\core\srs_core_server.hpp, ..\core\srs_core_server.cpp, + ..\core\srs_core_config.hpp, + ..\core\srs_core_config.cpp, ..\core\srs_core_conn.hpp, ..\core\srs_core_conn.cpp, ..\core\srs_core_client.hpp,