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,