winlin

refine readme.

要显示太多修改。

为保证性能只显示 3 of 3+ 个文件。

... ... @@ -21,52 +21,52 @@ people who have submitted patches, reported bugs, added translations, helped<br/
answer newbie questions, and generally made SRS that much better: [AUTHORS.txt](https://github.com/winlinvip/simple-rtmp-server/blob/master/AUTHORS.txt)
### Usage(simple)
<strong>step -1:</strong> get SRS<br/>
<strong>Step -1:</strong> get SRS<br/>
<pre>
git clone https://github.com/winlinvip/simple-rtmp-server &&
cd simple-rtmp-server/trunk
</pre>
<strong>step 0:</strong> build SRS system.<br/>
<strong>Step 0:</strong> build SRS system.<br/>
<pre>
bash scripts/build.sh
</pre>
<strong>step 1:</strong> start SRS all demo features.<br/>
<strong>Step 1:</strong> start SRS all demo features.<br/>
<pre>
bash scripts/run.sh
</pre>
<strong>step 2:</strong> SRS live show: [http://your-server-ip](http://your-server-ip) <br/>
<strong>step 3:</strong> stop SRS demo<br/>
<strong>Step 2:</strong> SRS live show: [http://your-server-ip](http://your-server-ip) <br/>
<strong>Step 3:</strong> stop SRS demo<br/>
<pre>
bash scripts/stop.sh
</pre>
### Usage(detail)
<strong>step 0:</strong> get SRS <br/>
<strong>Step 0:</strong> get SRS <br/>
<pre>
git clone https://github.com/winlinvip/simple-rtmp-server &&
cd simple-rtmp-server/trunk
</pre>
<strong>step 1:</strong> build SRS <br/>
<strong>Step 1:</strong> build SRS <br/>
<pre>
./configure --with-ssl --with-hls --with-ffmpeg --with-http && make
</pre>
<strong>step 2:</strong> start SRS <br/>
<strong>Step 2:</strong> start SRS <br/>
<pre>
./objs/srs -c conf/srs.conf
</pre>
<strong>step 3(optinal):</strong> start SRS listen at 19350 to forward to<br/>
<strong>Step 3(optinal):</strong> start SRS listen at 19350 to forward to<br/>
<pre>
./objs/srs -c conf/srs.19350.conf
</pre>
<strong>step 4(optinal):</strong> start nginx for HLS <br/>
<strong>Step 4(optinal):</strong> start nginx for HLS <br/>
<pre>
sudo ./objs/nginx/sbin/nginx
</pre>
<strong>step 5(optinal):</strong> start http hooks for SRS callback <br/>
<strong>Step 5(optinal):</strong> start http hooks for SRS callback <br/>
<pre>
python ./research/api-server/server.py 8085
</pre>
<strong>step 6:</strong> publish demo live stream <br/>
<strong>Step 6:</strong> publish demo live stream <br/>
<pre>
FMS URL: rtmp://127.0.0.1/live?vhost=demo.srs.com
Stream: livestream
... ... @@ -78,7 +78,7 @@ FFMPEG to publish the default demo stream:
sleep 1; \
done
</pre>
<strong>step 7:</strong> publish players live stream <br/>
<strong>Step 7:</strong> publish players live stream <br/>
<pre>
FMS URL: rtmp://127.0.0.1/live?vhost=players
Stream: livestream
... ... @@ -90,7 +90,7 @@ FFMPEG to publish the players demo stream:
sleep 1; \
done
</pre>
<strong>step 8:</strong> add server ip to client hosts as demo. <br/>
<strong>Step 8:</strong> add server ip to client hosts as demo. <br/>
<pre>
# edit the folowing file:
# linux: /etc/hosts
... ... @@ -98,14 +98,14 @@ FFMPEG to publish the players demo stream:
# where server ip is 192.168.2.111
192.168.2.111 demo.srs.com
</pre>
<strong>step 9:</strong> play live stream. <br/>
<strong>Step 9:</strong> play live stream. <br/>
<pre>
players: http://demo.srs.com/players
rtmp url: rtmp://demo.srs.com/live/livestream
m3u8 url: http://demo.srs.com/live/livestream.m3u8
for android: http://demo.srs.com/live/livestream.html
</pre>
<strong>step 10(optinal):</strong> play live stream auto transcoded<br/>
<strong>Step 10(optinal):</strong> play live stream auto transcoded<br/>
<pre>
rtmp url: rtmp://demo.srs.com/live/livestream_ld
m3u8 url: http://demo.srs.com/live/livestream_ld.m3u8
... ... @@ -114,7 +114,7 @@ rtmp url: rtmp://demo.srs.com/live/livestream_sd
m3u8 url: http://demo.srs.com/live/livestream_sd.m3u8
for android: http://demo.srs.com/live/livestream_sd.html
</pre>
<strong>step 11(optinal):</strong> play live stream auto forwarded, the hls dir change to /forward<br/>
<strong>Step 11(optinal):</strong> play live stream auto forwarded, the hls dir change to /forward<br/>
<pre>
rtmp url: rtmp://demo.srs.com:19350/live/livestream
m3u8 url: http://demo.srs.com/forward/live/livestream.m3u8
... ... @@ -126,7 +126,7 @@ rtmp url: rtmp://demo.srs.com:19350/live/livestream_sd
m3u8 url: http://demo.srs.com/forward/live/livestream_sd.m3u8
for android: http://demo.srs.com/forward/live/livestream_sd.html
</pre>
<strong>step 12(optinal):</strong> modify the config and reload it (all features support reload)<br/>
<strong>Step 12(optinal):</strong> modify the config and reload it (all features support reload)<br/>
<pre>
killall -1 srs
</pre>
... ... @@ -190,7 +190,7 @@ Bandwidth Test Workflow:
| final(2)-----------------> |
| &lt;END> |
@see: class SrsBandwidth comments.
@See: class SrsBandwidth comments.
</pre>
### System Requirements
... ... @@ -199,37 +199,38 @@ Supported operating systems and hardware:
* All handware.
### Summary
1. simple: also stable enough.<br/>
2. high-performance: single-thread, async socket, event/st-thread driven.<br/>
3. no edge server, origin server only.<br/>
4. no vod streaming, live streaming only.<br/>
5. no multiple processes, single process only.<br/>
6. support vhost, support \_\_defaultVhost\_\_.<br/>
7. support adobe rtmp live streaming.<br/>
8. support apple hls(m3u8) live streaming.<br/>
9. support reload config to enable changes.<br/>
10. support cache last gop for flash player to fast startup.<br/>
11. support listen at multiple ports.<br/>
12. support long time(>4.6hours) publish/play.<br/>
13. high performace, 1800 connections(500kbps), 900Mbps, CPU 90.2%, 41MB<br/>
14. support forward publish stream to build active-standby cluster.<br/>
15. support broadcast by forward the stream to other servers(origin/edge).<br/>
16. support live stream transcoding by ffmpeg.<br/>
17. support live stream forward(acopy/vcopy) by ffmpeg.<br/>
18. support ffmpeg filters(logo/overlay/crop), x264 params.<br/>
19. support audio transcode only, speex/mp3 to aac<br/>
20. support http callback api hooks(for authentication and injection).<br/>
21. support bandwidth test api and flash client.<br/>
22. player, publisher(encoder), and demo pages(jquery+bootstrap). <br/>
23. demo video meeting or chat(SRS+cherrypy+jquery+bootstrap). <br/>
24. [plan] support network based cli and json result.<br/>
25. [plan] support adobe flash refer/token/swf verification.<br/>
26. [plan] support adobe amf3 codec.<br/>
27. [plan] support dvr(record live to vod file)<br/>
28. [plan] support FMS edge protocol<br/>
29. [plan] support encryption: RTMPE/RTMPS, HLS DRM<br/>
30. [plan] support RTMPT, http to tranverse firewalls<br/>
31. [plan] support file source, transcoding file to live stream<br/>
1. Simple: also stable enough.<br/>
2. High-performance: single-thread, async socket, event/st-thread driven.<br/>
3. NO edge server, origin server only.<br/>
4. NO vod streaming, live streaming only.<br/>
5. NO multiple processes, single process only.<br/>
6. Support vhost, support \_\_defaultVhost\_\_.<br/>
7. Support adobe rtmp live streaming.<br/>
8. Support apple hls(m3u8) live streaming.<br/>
9. Support reload config to enable changes.<br/>
10. Support cache last gop for flash player to fast startup.<br/>
11. Support listen at multiple ports.<br/>
12. Support long time(>4.6hours) publish/play.<br/>
13. High performace, 1800 connections(500kbps), 900Mbps, CPU 90.2%, 41MB<br/>
14. Support forward publish stream to build active-standby cluster.<br/>
15. Support broadcast by forward the stream to other servers(origin/edge).<br/>
16. Support live stream transcoding by ffmpeg.<br/>
17. Support live stream forward(acopy/vcopy) by ffmpeg.<br/>
18. Support ffmpeg filters(logo/overlay/crop), x264 params.<br/>
19. Support audio transcode only, speex/mp3 to aac<br/>
20. Support http callback api hooks(for authentication and injection).<br/>
21. Support bandwidth test api and flash client.<br/>
22. Player, publisher(encoder), and demo pages(jquery+bootstrap). <br/>
23. Demo video meeting or chat(SRS+cherrypy+jquery+bootstrap). <br/>
24. [dev] Full documents in wiki, in chineses. <br/>
25. [plan] Support network based cli and json result.<br/>
26. [plan] Support adobe flash refer/token/swf verification.<br/>
27. [plan] Support adobe amf3 codec.<br/>
28. [plan] Support dvr(record live to vod file)<br/>
29. [plan] Support FMS edge protocol<br/>
30. [plan] Support encryption: RTMPE/RTMPS, HLS DRM<br/>
31. [plan] Support RTMPT, http to tranverse firewalls<br/>
32. [plan] Support file source, transcoding file to live stream<br/>
### Performance
1. 300 connections, 150Mbps, 500kbps, CPU 18.8%, 5956KB.
... ... @@ -239,6 +240,7 @@ Supported operating systems and hardware:
5. 1500 connections, 750Mbps, 500kbps, CPU 81.9%, 28MB.
6. 1800 connections, 900Mbps, 500kbps, CPU 90.2%, 41MB.
<pre>
[winlin@dev6 srs]$ dstat
----total-cpu-usage---- -dsk/total- ---net/lo-- ---paging-- ---system--
usr sys idl wai hiq siq| read writ| recv send| in out | int csw
58 9 32 0 0 1| 0 4168k| 277M 277M| 0 0 | 29k 25k
... ... @@ -257,16 +259,16 @@ usr sys idl wai hiq siq| read writ| recv send| in out | int csw
</pre>
### Releases
* 2013-12-25, [release v0.9](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.9), support bandwidth test, add player/encoder/chat demos. 20926 lines.<br/>
* 2013-12-08, [release v0.8](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.8), support http hooks callback, update [st_load](https://github.com/winlinvip/st-load). 19186 lines.<br/>
* 2013-12-03, [release v0.7](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.7), support live stream transcoding. 17605 lines.<br/>
* 2013-11-29, [release v0.6](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.6), support forward stream to origin/edge. 16094 lines.<br/>
* 2013-11-26, [release v0.5](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.5), support HLS(m3u8), fragment and window. 14449 lines.<br/>
* 2013-11-10, [release v0.4](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.4), support reload config, pause, longtime publish/play. 12500 lines.<br/>
* 2013-11-04, [release v0.3](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.3), support vhost, refer, gop cache, listen multiple ports. 11773 lines.<br/>
* 2013-10-25, [release v0.2](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.2), support rtmp flash publish, h264, time jitter correct. 10125 lines.<br/>
* 2013-10-23, [release v0.1](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.1), support rtmp FMLE/FFMPEG publish, vp6. 8287 lines.<br/>
* 2013-10-17, created.<br/>
* 2013-12-25, [Release v0.9](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.9), support bandwidth test, add player/encoder/chat demos. 20926 lines.<br/>
* 2013-12-08, [Release v0.8](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.8), support http hooks callback, update [st_load](https://github.com/winlinvip/st-load). 19186 lines.<br/>
* 2013-12-03, [Release v0.7](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.7), support live stream transcoding. 17605 lines.<br/>
* 2013-11-29, [Release v0.6](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.6), support forward stream to origin/edge. 16094 lines.<br/>
* 2013-11-26, [Release v0.5](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.5), support HLS(m3u8), fragment and window. 14449 lines.<br/>
* 2013-11-10, [Release v0.4](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.4), support reload config, pause, longtime publish/play. 12500 lines.<br/>
* 2013-11-04, [Release v0.3](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.3), support vhost, refer, gop cache, listen multiple ports. 11773 lines.<br/>
* 2013-10-25, [Release v0.2](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.2), support rtmp flash publish, h264, time jitter correct. 10125 lines.<br/>
* 2013-10-23, [Release v0.1](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.1), support rtmp FMLE/FFMPEG publish, vp6. 8287 lines.<br/>
* 2013-10-17, Created.<br/>
### Compare
* SRS v0.9: 20926 lines. player/encoder/chat demos. bandwidth test for encoder/CDN.<br/>
... ...
... ... @@ -76,7 +76,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#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_EMAIL "winlin@vip.126.com"
#define RTMP_SIG_SRS_LICENSE "The MIT License (MIT)"
#define RTMP_SIG_SRS_COPYRIGHT "Copyright (c) 2013-2014 winlin"
#define RTMP_SIG_SRS_PRIMARY_AUTHROS "winlin,wenjiegit"
... ...
/*
The MIT License (MIT)
Copyright (c) 2013-2014 winlin
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <srs_core_config.hpp>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
// file operations.
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <vector>
#include <algorithm>
using namespace std;
#include <srs_core_error.hpp>
#include <srs_core_log.hpp>
#include <srs_core_autofree.hpp>
#define FILE_OFFSET(fd) lseek(fd, 0, SEEK_CUR)
int64_t FILE_SIZE(int fd)
{
int64_t pre = FILE_OFFSET(fd);
int64_t pos = lseek(fd, 0, SEEK_END);
lseek(fd, pre, SEEK_SET);
return pos;
}
#define LF (char)0x0a
#define CR (char)0x0d
bool is_common_space(char ch)
{
return (ch == ' ' || ch == '\t' || ch == CR || ch == LF);
}
class SrsFileBuffer
{
private:
// last available position.
char* last;
// end of buffer.
char* end;
// start of buffer.
char* start;
public:
// current consumed position.
char* pos;
// current parsed line.
int line;
SrsFileBuffer();
virtual ~SrsFileBuffer();
virtual int fullfill(const char* filename);
virtual bool empty();
};
SrsFileBuffer::SrsFileBuffer()
{
line = 0;
pos = last = start = NULL;
end = start;
}
SrsFileBuffer::~SrsFileBuffer()
{
srs_freepa(start);
}
int SrsFileBuffer::fullfill(const char* filename)
{
int ret = ERROR_SUCCESS;
int fd = -1;
int nread = 0;
int filesize = 0;
if ((fd = ::open(filename, O_RDONLY, 0)) < 0) {
ret = ERROR_SYSTEM_CONFIG_INVALID;
srs_error("open conf file error. ret=%d", ret);
goto finish;
}
if ((filesize = FILE_SIZE(fd) - FILE_OFFSET(fd)) <= 0) {
ret = ERROR_SYSTEM_CONFIG_EOF;
srs_error("read conf file error. ret=%d", ret);
goto finish;
}
srs_freepa(start);
pos = last = start = new char[filesize];
end = start + filesize;
if ((nread = read(fd, start, filesize)) != filesize) {
ret = ERROR_SYSTEM_CONFIG_INVALID;
srs_error("read file read error. expect %d, actual %d bytes, ret=%d",
filesize, nread, ret);
goto finish;
}
line = 1;
finish:
if (fd > 0) {
::close(fd);
}
return ret;
}
bool SrsFileBuffer::empty()
{
return pos >= end;
}
SrsConfDirective::SrsConfDirective()
{
}
SrsConfDirective::~SrsConfDirective()
{
std::vector<SrsConfDirective*>::iterator it;
for (it = directives.begin(); it != directives.end(); ++it) {
SrsConfDirective* directive = *it;
srs_freep(directive);
}
directives.clear();
}
string SrsConfDirective::arg0()
{
if (args.size() > 0) {
return args.at(0);
}
return "";
}
string SrsConfDirective::arg1()
{
if (args.size() > 1) {
return args.at(1);
}
return "";
}
string SrsConfDirective::arg2()
{
if (args.size() > 2) {
return args.at(2);
}
return "";
}
SrsConfDirective* SrsConfDirective::at(int index)
{
return directives.at(index);
}
SrsConfDirective* SrsConfDirective::get(string _name)
{
std::vector<SrsConfDirective*>::iterator it;
for (it = directives.begin(); it != directives.end(); ++it) {
SrsConfDirective* directive = *it;
if (directive->name == _name) {
return directive;
}
}
return NULL;
}
SrsConfDirective* SrsConfDirective::get(string _name, string _arg0)
{
std::vector<SrsConfDirective*>::iterator it;
for (it = directives.begin(); it != directives.end(); ++it) {
SrsConfDirective* directive = *it;
if (directive->name == _name && directive->arg0() == _arg0) {
return directive;
}
}
return NULL;
}
int SrsConfDirective::parse(const char* filename)
{
int ret = ERROR_SUCCESS;
SrsFileBuffer buffer;
if ((ret = buffer.fullfill(filename)) != ERROR_SUCCESS) {
return ret;
}
return parse_conf(&buffer, parse_file);
}
// see: ngx_conf_parse
int SrsConfDirective::parse_conf(SrsFileBuffer* buffer, SrsDirectiveType type)
{
int ret = ERROR_SUCCESS;
while (true) {
std::vector<string> args;
ret = read_token(buffer, args);
/**
* ret maybe:
* ERROR_SYSTEM_CONFIG_INVALID error.
* ERROR_SYSTEM_CONFIG_DIRECTIVE directive terminated by ';' found
* ERROR_SYSTEM_CONFIG_BLOCK_START token terminated by '{' found
* ERROR_SYSTEM_CONFIG_BLOCK_END the '}' found
* ERROR_SYSTEM_CONFIG_EOF the config file is done
*/
if (ret == ERROR_SYSTEM_CONFIG_INVALID) {
return ret;
}
if (ret == ERROR_SYSTEM_CONFIG_BLOCK_END) {
if (type != parse_block) {
srs_error("line %d: unexpected \"}\"", buffer->line);
return ret;
}
return ERROR_SUCCESS;
}
if (ret == ERROR_SYSTEM_CONFIG_EOF) {
if (type == parse_block) {
srs_error("line %d: unexpected end of file, expecting \"}\"", buffer->line);
return ret;
}
return ERROR_SUCCESS;
}
if (args.empty()) {
srs_error("line %d: empty directive.", buffer->line);
return ret;
}
// build directive tree.
SrsConfDirective* directive = new SrsConfDirective();
directive->conf_line = buffer->line;
directive->name = args[0];
args.erase(args.begin());
directive->args.swap(args);
directives.push_back(directive);
if (ret == ERROR_SYSTEM_CONFIG_BLOCK_START) {
if ((ret = directive->parse_conf(buffer, parse_block)) != ERROR_SUCCESS) {
return ret;
}
}
}
return ret;
}
// see: ngx_conf_read_token
int SrsConfDirective::read_token(SrsFileBuffer* buffer, std::vector<string>& args)
{
int ret = ERROR_SUCCESS;
char* pstart = buffer->pos;
int startline = buffer->line;
bool sharp_comment = false;
bool d_quoted = false;
bool s_quoted = false;
bool need_space = false;
bool last_space = true;
while (true) {
if (buffer->empty()) {
ret = ERROR_SYSTEM_CONFIG_EOF;
if (!args.empty() || !last_space) {
srs_error("line %d: unexpected end of file, expecting ; or \"}\"", buffer->line);
return ERROR_SYSTEM_CONFIG_INVALID;
}
srs_error("end of file. ret=%d", ret);
return ret;
}
char ch = *buffer->pos++;
if (ch == LF) {
buffer->line++;
sharp_comment = false;
}
if (sharp_comment) {
continue;
}
if (need_space) {
if (is_common_space(ch)) {
last_space = true;
need_space = false;
continue;
}
if (ch == ';') {
return ERROR_SYSTEM_CONFIG_DIRECTIVE;
}
if (ch == '{') {
return ERROR_SYSTEM_CONFIG_BLOCK_START;
}
srs_error("line %d: unexpected '%c'", buffer->line, ch);
return ERROR_SYSTEM_CONFIG_INVALID;
}
// last charecter is space.
if (last_space) {
if (is_common_space(ch)) {
continue;
}
pstart = buffer->pos - 1;
startline = buffer->line;
switch (ch) {
case ';':
if (args.size() == 0) {
srs_error("line %d: unexpected ';'", buffer->line);
return ERROR_SYSTEM_CONFIG_INVALID;
}
return ERROR_SYSTEM_CONFIG_DIRECTIVE;
case '{':
if (args.size() == 0) {
srs_error("line %d: unexpected '{'", buffer->line);
return ERROR_SYSTEM_CONFIG_INVALID;
}
return ERROR_SYSTEM_CONFIG_BLOCK_START;
case '}':
if (args.size() != 0) {
srs_error("line %d: unexpected '}'", buffer->line);
return ERROR_SYSTEM_CONFIG_INVALID;
}
return ERROR_SYSTEM_CONFIG_BLOCK_END;
case '#':
sharp_comment = 1;
continue;
case '"':
pstart++;
d_quoted = true;
last_space = 0;
continue;
case '\'':
pstart++;
s_quoted = true;
last_space = 0;
continue;
default:
last_space = 0;
continue;
}
} else {
// last charecter is not space
bool found = false;
if (d_quoted) {
if (ch == '"') {
d_quoted = false;
need_space = true;
found = true;
}
} else if (s_quoted) {
if (ch == '\'') {
s_quoted = false;
need_space = true;
found = true;
}
} else if (is_common_space(ch) || ch == ';' || ch == '{') {
last_space = true;
found = 1;
}
if (found) {
int len = buffer->pos - pstart;
char* word = new char[len];
memcpy(word, pstart, len);
word[len - 1] = 0;
string word_str = word;
if (!word_str.empty()) {
args.push_back(word_str);
}
srs_freepa(word);
if (ch == ';') {
return ERROR_SYSTEM_CONFIG_DIRECTIVE;
}
if (ch == '{') {
return ERROR_SYSTEM_CONFIG_BLOCK_START;
}
}
}
}
return ret;
}
SrsConfig* config = new SrsConfig();
SrsConfig::SrsConfig()
{
show_help = false;
show_version = false;
root = new SrsConfDirective();
root->conf_line = 0;
root->name = "root";
}
SrsConfig::~SrsConfig()
{
srs_freep(root);
}
int SrsConfig::reload()
{
int ret = ERROR_SUCCESS;
SrsConfig conf;
if ((ret = conf.parse_file(config_file.c_str())) != ERROR_SUCCESS) {
srs_error("config reloader parse file failed. ret=%d", ret);
return ret;
}
srs_info("config reloader parse file success.");
// store current root to old_root,
// and reap the root from conf to current root.
SrsConfDirective* old_root = root;
SrsAutoFree(SrsConfDirective, old_root, false);
root = conf.root;
conf.root = NULL;
// merge config.
std::vector<ISrsReloadHandler*>::iterator it;
// merge config: listen
if (!srs_directive_equals(root->get("listen"), old_root->get("listen"))) {
for (it = subscribes.begin(); it != subscribes.end(); ++it) {
ISrsReloadHandler* subscribe = *it;
if ((ret = subscribe->on_reload_listen()) != ERROR_SUCCESS) {
srs_error("notify subscribes reload listen failed. ret=%d", ret);
return ret;
}
}
srs_trace("reload listen success.");
}
// merge config: pithy_print
if (!srs_directive_equals(root->get("pithy_print"), old_root->get("pithy_print"))) {
for (it = subscribes.begin(); it != subscribes.end(); ++it) {
ISrsReloadHandler* subscribe = *it;
if ((ret = subscribe->on_reload_pithy_print()) != ERROR_SUCCESS) {
srs_error("notify subscribes pithy_print listen failed. ret=%d", ret);
return ret;
}
}
srs_trace("reload pithy_print success.");
}
// merge config: vhost added, directly supported.
// merge config: vhost removed/disabled/modified.
for (int i = 0; i < (int)old_root->directives.size(); i++) {
SrsConfDirective* old_vhost = old_root->at(i);
// only process vhost directives.
if (old_vhost->name != "vhost") {
continue;
}
std::string vhost = old_vhost->arg0();
SrsConfDirective* new_vhost = root->get("vhost", vhost);
// ignore if absolutely equal
if (new_vhost && srs_directive_equals(old_vhost, new_vhost)) {
srs_trace("vhost %s absolutely equal, ignore.", vhost.c_str());
continue;
}
// ignore if enable the new vhost when old vhost is disabled.
if (get_vhost_enabled(new_vhost) && !get_vhost_enabled(old_vhost)) {
srs_trace("vhost %s disabled=>enabled, ignore.", vhost.c_str());
continue;
}
// ignore if both old and new vhost are disabled.
if (!get_vhost_enabled(new_vhost) && !get_vhost_enabled(old_vhost)) {
srs_trace("vhost %s disabled=>disabled, ignore.", vhost.c_str());
continue;
}
// merge config: vhost removed/disabled.
if (!get_vhost_enabled(new_vhost) && get_vhost_enabled(old_vhost)) {
srs_trace("vhost %s disabled, reload it.", vhost.c_str());
for (it = subscribes.begin(); it != subscribes.end(); ++it) {
ISrsReloadHandler* subscribe = *it;
if ((ret = subscribe->on_reload_vhost_removed(vhost)) != ERROR_SUCCESS) {
srs_error("notify subscribes pithy_print remove "
"vhost %s failed. ret=%d", vhost.c_str(), ret);
return ret;
}
}
srs_trace("reload remove vhost %s success.", vhost.c_str());
}
// merge config: vhost modified.
srs_trace("vhost %s modified, reload its detail.", vhost.c_str());
if (get_vhost_enabled(new_vhost) && get_vhost_enabled(old_vhost)) {
// gop_cache
if (!srs_directive_equals(new_vhost->get("gop_cache"), old_vhost->get("gop_cache"))) {
for (it = subscribes.begin(); it != subscribes.end(); ++it) {
ISrsReloadHandler* subscribe = *it;
if ((ret = subscribe->on_reload_gop_cache(vhost)) != ERROR_SUCCESS) {
srs_error("vhost %s notify subscribes gop_cache failed. ret=%d", vhost.c_str(), ret);
return ret;
}
}
srs_trace("vhost %s reload gop_cache success.", vhost.c_str());
}
// queue_length
if (!srs_directive_equals(new_vhost->get("queue_length"), old_vhost->get("queue_length"))) {
for (it = subscribes.begin(); it != subscribes.end(); ++it) {
ISrsReloadHandler* subscribe = *it;
if ((ret = subscribe->on_reload_queue_length(vhost)) != ERROR_SUCCESS) {
srs_error("vhost %s notify subscribes queue_length failed. ret=%d", vhost.c_str(), ret);
return ret;
}
}
srs_trace("vhost %s reload queue_length success.", vhost.c_str());
}
// forward
if (!srs_directive_equals(new_vhost->get("forward"), old_vhost->get("forward"))) {
for (it = subscribes.begin(); it != subscribes.end(); ++it) {
ISrsReloadHandler* subscribe = *it;
if ((ret = subscribe->on_reload_forward(vhost)) != ERROR_SUCCESS) {
srs_error("vhost %s notify subscribes forward failed. ret=%d", vhost.c_str(), ret);
return ret;
}
}
srs_trace("vhost %s reload forward success.", vhost.c_str());
}
// hls
if (!srs_directive_equals(new_vhost->get("hls"), old_vhost->get("hls"))) {
for (it = subscribes.begin(); it != subscribes.end(); ++it) {
ISrsReloadHandler* subscribe = *it;
if ((ret = subscribe->on_reload_hls(vhost)) != ERROR_SUCCESS) {
srs_error("vhost %s notify subscribes hls failed. ret=%d", vhost.c_str(), ret);
return ret;
}
}
srs_trace("vhost %s reload hls success.", vhost.c_str());
}
// transcode
if (!srs_directive_equals(new_vhost->get("transcode"), old_vhost->get("transcode"))) {
for (it = subscribes.begin(); it != subscribes.end(); ++it) {
ISrsReloadHandler* subscribe = *it;
if ((ret = subscribe->on_reload_transcode(vhost)) != ERROR_SUCCESS) {
srs_error("vhost %s notify subscribes transcode failed. ret=%d", vhost.c_str(), ret);
return ret;
}
}
srs_trace("vhost %s reload transcode success.", vhost.c_str());
}
// TODO: suppor reload hls/forward/ffmpeg/http
continue;
}
srs_warn("invalid reload path, enabled old: %d, new: %d",
get_vhost_enabled(old_vhost), get_vhost_enabled(new_vhost));
}
return ret;
}
void SrsConfig::subscribe(ISrsReloadHandler* handler)
{
std::vector<ISrsReloadHandler*>::iterator it;
it = std::find(subscribes.begin(), subscribes.end(), handler);
if (it != subscribes.end()) {
return;
}
subscribes.push_back(handler);
}
void SrsConfig::unsubscribe(ISrsReloadHandler* handler)
{
std::vector<ISrsReloadHandler*>::iterator it;
it = std::find(subscribes.begin(), subscribes.end(), handler);
if (it == subscribes.end()) {
return;
}
subscribes.erase(it);
}
// see: ngx_get_options
int SrsConfig::parse_options(int argc, char** argv)
{
int ret = ERROR_SUCCESS;
for (int i = 1; i < argc; i++) {
if ((ret = parse_argv(i, argv)) != ERROR_SUCCESS) {
return ret;
}
}
if (show_help) {
print_help(argv);
}
if (show_version) {
printf("%s\n", RTMP_SIG_SRS_VERSION);
}
if (show_help || show_version) {
exit(0);
}
if (config_file.empty()) {
ret = ERROR_SYSTEM_CONFIG_INVALID;
srs_error("config file not specified, see help: %s -h, ret=%d", argv[0], ret);
return ret;
}
return parse_file(config_file.c_str());
}
int SrsConfig::parse_file(const char* filename)
{
int ret = ERROR_SUCCESS;
config_file = filename;
if (config_file.empty()) {
return ERROR_SYSTEM_CONFIG_INVALID;
}
if ((ret = root->parse(config_file.c_str())) != ERROR_SUCCESS) {
return ret;
}
SrsConfDirective* conf = NULL;
if ((conf = get_listen()) == NULL || conf->args.size() == 0) {
ret = ERROR_SYSTEM_CONFIG_INVALID;
srs_error("line %d: conf error, "
"directive \"listen\" is empty, ret=%d", (conf? conf->conf_line:0), ret);
return ret;
}
// TODO: check the hls.
// TODO: check forward.
// TODO: check ffmpeg.
// TODO: check http.
return ret;
}
int SrsConfig::parse_argv(int& i, char** argv)
{
int ret = ERROR_SUCCESS;
char* p = argv[i];
if (*p++ != '-') {
ret = ERROR_SYSTEM_CONFIG_INVALID;
srs_error("invalid options(index=%d, value=%s), "
"must starts with -, see help: %s -h, ret=%d", i, argv[i], argv[0], ret);
return ret;
}
while (*p) {
switch (*p++) {
case '?':
case 'h':
show_help = true;
break;
case 'v':
case 'V':
show_version = true;
break;
case 'c':
if (*p) {
config_file = p;
return ret;
}
if (argv[++i]) {
config_file = argv[i];
return ret;
}
ret = ERROR_SYSTEM_CONFIG_INVALID;
srs_error("option \"-c\" requires parameter, ret=%d", ret);
return ret;
default:
ret = ERROR_SYSTEM_CONFIG_INVALID;
srs_error("invalid option: \"%c\", see help: %s -h, ret=%d", *(p - 1), argv[0], ret);
return ret;
}
}
return ret;
}
void SrsConfig::print_help(char** argv)
{
printf(
RTMP_SIG_SRS_NAME" "RTMP_SIG_SRS_VERSION" "RTMP_SIG_SRS_COPYRIGHT"\n"
"Primary Authors: "RTMP_SIG_SRS_PRIMARY_AUTHROS"\n"
"Build: "SRS_BUILD_DATE" Configuration: "SRS_CONFIGURE"\n"
"Usage: %s [-h?vV] [-c <filename>]\n"
"\n"
"Options:\n"
" -?-h : show help\n"
" -v-V : show version and exit\n"
" -c filename : set configuration file\n"
"\n"
RTMP_SIG_SRS_WEB"\n"
RTMP_SIG_SRS_URL"\n"
"Email: "RTMP_SIG_SRS_EMAIL"\n"
"\n",
argv[0]);
}
SrsConfDirective* SrsConfig::get_vhost(string vhost)
{
srs_assert(root);
for (int i = 0; i < (int)root->directives.size(); i++) {
SrsConfDirective* conf = root->at(i);
if (conf->name != "vhost") {
continue;
}
if (conf->arg0() == vhost) {
return conf;
}
}
if (vhost != RTMP_VHOST_DEFAULT) {
return get_vhost(RTMP_VHOST_DEFAULT);
}
return NULL;
}
SrsConfDirective* SrsConfig::get_vhost_on_connect(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return NULL;
}
conf = conf->get("http_hooks");
if (!conf) {
return NULL;
}
SrsConfDirective* enabled = conf->get("enabled");
if (!enabled || enabled->arg0() != "on") {
return NULL;
}
return conf->get("on_connect");
}
SrsConfDirective* SrsConfig::get_vhost_on_close(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return NULL;
}
conf = conf->get("http_hooks");
if (!conf) {
return NULL;
}
SrsConfDirective* enabled = conf->get("enabled");
if (!enabled || enabled->arg0() != "on") {
return NULL;
}
return conf->get("on_close");
}
SrsConfDirective* SrsConfig::get_vhost_on_publish(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return NULL;
}
conf = conf->get("http_hooks");
if (!conf) {
return NULL;
}
SrsConfDirective* enabled = conf->get("enabled");
if (!enabled || enabled->arg0() != "on") {
return NULL;
}
return conf->get("on_publish");
}
SrsConfDirective* SrsConfig::get_vhost_on_unpublish(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return NULL;
}
conf = conf->get("http_hooks");
if (!conf) {
return NULL;
}
SrsConfDirective* enabled = conf->get("enabled");
if (!enabled || enabled->arg0() != "on") {
return NULL;
}
return conf->get("on_unpublish");
}
SrsConfDirective* SrsConfig::get_vhost_on_play(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return NULL;
}
conf = conf->get("http_hooks");
if (!conf) {
return NULL;
}
SrsConfDirective* enabled = conf->get("enabled");
if (!enabled || enabled->arg0() != "on") {
return NULL;
}
return conf->get("on_play");
}
SrsConfDirective* SrsConfig::get_vhost_on_stop(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return NULL;
}
conf = conf->get("http_hooks");
if (!conf) {
return NULL;
}
SrsConfDirective* enabled = conf->get("enabled");
if (!enabled || enabled->arg0() != "on") {
return NULL;
}
return conf->get("on_stop");
}
bool SrsConfig::get_vhost_enabled(string vhost)
{
SrsConfDirective* vhost_conf = get_vhost(vhost);
return get_vhost_enabled(vhost_conf);
}
bool SrsConfig::get_vhost_enabled(SrsConfDirective* vhost)
{
if (!vhost) {
return false;
}
SrsConfDirective* conf = vhost->get("enabled");
if (!conf) {
return true;
}
if (conf->arg0() == "off") {
return false;
}
return true;
}
SrsConfDirective* SrsConfig::get_transcode(string vhost, string scope)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return NULL;
}
SrsConfDirective* transcode = conf->get("transcode");
if (!transcode) {
return NULL;
}
if (transcode->arg0() == scope) {
return transcode;
}
return NULL;
}
bool SrsConfig::get_transcode_enabled(SrsConfDirective* transcode)
{
if (!transcode) {
return false;
}
SrsConfDirective* conf = transcode->get("enabled");
if (!conf || conf->arg0() != "on") {
return false;
}
return true;
}
string SrsConfig::get_transcode_ffmpeg(SrsConfDirective* transcode)
{
if (!transcode) {
return "";
}
SrsConfDirective* conf = transcode->get("ffmpeg");
if (!conf) {
return "";
}
return conf->arg0();
}
void SrsConfig::get_transcode_engines(SrsConfDirective* transcode, std::vector<SrsConfDirective*>& engines)
{
if (!transcode) {
return;
}
for (int i = 0; i < (int)transcode->directives.size(); i++) {
SrsConfDirective* conf = transcode->directives[i];
if (conf->name == "engine") {
engines.push_back(conf);
}
}
return;
}
bool SrsConfig::get_engine_enabled(SrsConfDirective* engine)
{
if (!engine) {
return false;
}
SrsConfDirective* conf = engine->get("enabled");
if (!conf || conf->arg0() != "on") {
return false;
}
return true;
}
string SrsConfig::get_engine_vcodec(SrsConfDirective* engine)
{
if (!engine) {
return "";
}
SrsConfDirective* conf = engine->get("vcodec");
if (!conf) {
return "";
}
return conf->arg0();
}
int SrsConfig::get_engine_vbitrate(SrsConfDirective* engine)
{
if (!engine) {
return 0;
}
SrsConfDirective* conf = engine->get("vbitrate");
if (!conf) {
return 0;
}
return ::atoi(conf->arg0().c_str());
}
double SrsConfig::get_engine_vfps(SrsConfDirective* engine)
{
if (!engine) {
return 0;
}
SrsConfDirective* conf = engine->get("vfps");
if (!conf) {
return 0;
}
return ::atof(conf->arg0().c_str());
}
int SrsConfig::get_engine_vwidth(SrsConfDirective* engine)
{
if (!engine) {
return 0;
}
SrsConfDirective* conf = engine->get("vwidth");
if (!conf) {
return 0;
}
return ::atoi(conf->arg0().c_str());
}
int SrsConfig::get_engine_vheight(SrsConfDirective* engine)
{
if (!engine) {
return 0;
}
SrsConfDirective* conf = engine->get("vheight");
if (!conf) {
return 0;
}
return ::atoi(conf->arg0().c_str());
}
int SrsConfig::get_engine_vthreads(SrsConfDirective* engine)
{
if (!engine) {
return 0;
}
SrsConfDirective* conf = engine->get("vthreads");
if (!conf) {
return 0;
}
return ::atoi(conf->arg0().c_str());
}
string SrsConfig::get_engine_vprofile(SrsConfDirective* engine)
{
if (!engine) {
return "";
}
SrsConfDirective* conf = engine->get("vprofile");
if (!conf) {
return "";
}
return conf->arg0();
}
string SrsConfig::get_engine_vpreset(SrsConfDirective* engine)
{
if (!engine) {
return "";
}
SrsConfDirective* conf = engine->get("vpreset");
if (!conf) {
return "";
}
return conf->arg0();
}
void SrsConfig::get_engine_vparams(SrsConfDirective* engine, std::vector<string>& vparams)
{
if (!engine) {
return;
}
SrsConfDirective* conf = engine->get("vparams");
if (!conf) {
return;
}
for (int i = 0; i < (int)conf->directives.size(); i++) {
SrsConfDirective* p = conf->directives[i];
if (!p) {
continue;
}
vparams.push_back("-" + p->name);
vparams.push_back(p->arg0());
}
}
void SrsConfig::get_engine_vfilter(SrsConfDirective* engine, std::vector<string>& vfilter)
{
if (!engine) {
return;
}
SrsConfDirective* conf = engine->get("vfilter");
if (!conf) {
return;
}
for (int i = 0; i < (int)conf->directives.size(); i++) {
SrsConfDirective* p = conf->directives[i];
if (!p) {
continue;
}
vfilter.push_back("-" + p->name);
vfilter.push_back(p->arg0());
}
}
string SrsConfig::get_engine_acodec(SrsConfDirective* engine)
{
if (!engine) {
return "";
}
SrsConfDirective* conf = engine->get("acodec");
if (!conf) {
return "";
}
return conf->arg0();
}
int SrsConfig::get_engine_abitrate(SrsConfDirective* engine)
{
if (!engine) {
return 0;
}
SrsConfDirective* conf = engine->get("abitrate");
if (!conf) {
return 0;
}
return ::atoi(conf->arg0().c_str());
}
int SrsConfig::get_engine_asample_rate(SrsConfDirective* engine)
{
if (!engine) {
return 0;
}
SrsConfDirective* conf = engine->get("asample_rate");
if (!conf) {
return 0;
}
return ::atoi(conf->arg0().c_str());
}
int SrsConfig::get_engine_achannels(SrsConfDirective* engine)
{
if (!engine) {
return 0;
}
SrsConfDirective* conf = engine->get("achannels");
if (!conf) {
return 0;
}
return ::atoi(conf->arg0().c_str());
}
void SrsConfig::get_engine_aparams(SrsConfDirective* engine, std::vector<string>& aparams)
{
if (!engine) {
return;
}
SrsConfDirective* conf = engine->get("aparams");
if (!conf) {
return;
}
for (int i = 0; i < (int)conf->directives.size(); i++) {
SrsConfDirective* p = conf->directives[i];
if (!p) {
continue;
}
aparams.push_back("-" + p->name);
aparams.push_back(p->arg0());
}
}
string SrsConfig::get_engine_output(SrsConfDirective* engine)
{
if (!engine) {
return "";
}
SrsConfDirective* conf = engine->get("output");
if (!conf) {
return "";
}
return conf->arg0();
}
string SrsConfig::get_log_dir()
{
srs_assert(root);
SrsConfDirective* conf = root->get("log_dir");
if (!conf || conf->arg0().empty()) {
return "./objs/logs";
}
return conf->arg0();
}
int SrsConfig::get_max_connections()
{
srs_assert(root);
SrsConfDirective* conf = root->get("max_connections");
if (!conf || conf->arg0().empty()) {
return 2000;
}
return ::atoi(conf->arg0().c_str());
}
bool SrsConfig::get_gop_cache(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return true;
}
conf = conf->get("gop_cache");
if (conf && conf->arg0() == "off") {
return false;
}
return true;
}
double SrsConfig::get_queue_length(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return SRS_CONF_DEFAULT_QUEUE_LENGTH;
}
conf = conf->get("queue_length");
if (!conf || conf->arg0().empty()) {
return SRS_CONF_DEFAULT_QUEUE_LENGTH;
}
return ::atoi(conf->arg0().c_str());
}
SrsConfDirective* SrsConfig::get_forward(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return NULL;
}
return conf->get("forward");
}
SrsConfDirective* SrsConfig::get_hls(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return NULL;
}
return conf->get("hls");
}
bool SrsConfig::get_hls_enabled(string vhost)
{
SrsConfDirective* hls = get_hls(vhost);
if (!hls) {
return false;
}
SrsConfDirective* conf = hls->get("enabled");
if (!conf) {
return false;
}
if (conf->arg0() == "on") {
return true;
}
return false;
}
string SrsConfig::get_hls_path(string vhost)
{
SrsConfDirective* hls = get_hls(vhost);
if (!hls) {
return SRS_CONF_DEFAULT_HLS_PATH;
}
SrsConfDirective* conf = hls->get("hls_path");
if (!conf) {
return SRS_CONF_DEFAULT_HLS_PATH;
}
return conf->arg0();
}
double SrsConfig::get_hls_fragment(string vhost)
{
SrsConfDirective* hls = get_hls(vhost);
if (!hls) {
return SRS_CONF_DEFAULT_HLS_FRAGMENT;
}
SrsConfDirective* conf = hls->get("hls_fragment");
if (!conf) {
return SRS_CONF_DEFAULT_HLS_FRAGMENT;
}
return ::atof(conf->arg0().c_str());
}
double SrsConfig::get_hls_window(string vhost)
{
SrsConfDirective* hls = get_hls(vhost);
if (!hls) {
return SRS_CONF_DEFAULT_HLS_WINDOW;
}
SrsConfDirective* conf = hls->get("hls_window");
if (!conf) {
return SRS_CONF_DEFAULT_HLS_WINDOW;
}
return ::atof(conf->arg0().c_str());
}
SrsConfDirective* SrsConfig::get_refer(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return NULL;
}
return conf->get("refer");
}
SrsConfDirective* SrsConfig::get_refer_play(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return NULL;
}
return conf->get("refer_play");
}
SrsConfDirective* SrsConfig::get_refer_publish(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return NULL;
}
return conf->get("refer_publish");
}
SrsConfDirective* SrsConfig::get_listen()
{
return root->get("listen");
}
int SrsConfig::get_chunk_size(const std::string &vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return SRS_CONF_DEFAULT_CHUNK_SIZE;
}
conf = conf->get("chunk_size");
if (!conf) {
// vhost does not specify the chunk size,
// use the global instead.
conf = root->get("chunk_size");
if (!conf) {
return SRS_CONF_DEFAULT_CHUNK_SIZE;
}
return ::atoi(conf->arg0().c_str());
}
return ::atoi(conf->arg0().c_str());
}
int SrsConfig::get_pithy_print_publish()
{
SrsConfDirective* pithy = root->get("pithy_print");
if (!pithy) {
return SRS_STAGE_PUBLISH_USER_INTERVAL_MS;
}
pithy = pithy->get("publish");
if (!pithy) {
return SRS_STAGE_PUBLISH_USER_INTERVAL_MS;
}
return ::atoi(pithy->arg0().c_str());
}
int SrsConfig::get_pithy_print_forwarder()
{
SrsConfDirective* pithy = root->get("pithy_print");
if (!pithy) {
return SRS_STAGE_FORWARDER_INTERVAL_MS;
}
pithy = pithy->get("forwarder");
if (!pithy) {
return SRS_STAGE_FORWARDER_INTERVAL_MS;
}
return ::atoi(pithy->arg0().c_str());
}
int SrsConfig::get_pithy_print_hls()
{
SrsConfDirective* pithy = root->get("pithy_print");
if (!pithy) {
return SRS_STAGE_HLS_INTERVAL_MS;
}
pithy = pithy->get("hls");
if (!pithy) {
return SRS_STAGE_HLS_INTERVAL_MS;
}
return ::atoi(pithy->arg0().c_str());
}
bool SrsConfig::get_bw_check_enabled(const string &vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return false;
}
conf = conf->get("bandcheck");
if (!conf) {
return false;
}
conf = conf->get("enabled");
if (!conf || conf->arg0() != "on") {
return false;
}
return true;
}
string SrsConfig::get_bw_check_key(const string &vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return "";
}
conf = conf->get("bandcheck");
if (!conf) {
return "";
}
conf = conf->get("key");
if (!conf) {
return "";
}
return conf->arg0();
}
int SrsConfig::get_bw_check_interval_ms(const string &vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return SRS_CONF_DEFAULT_BANDWIDTH_INTERVAL;
}
conf = conf->get("bandcheck");
if (!conf) {
return SRS_CONF_DEFAULT_BANDWIDTH_INTERVAL;
}
conf = conf->get("interval_ms");
if (!conf) {
return SRS_CONF_DEFAULT_BANDWIDTH_INTERVAL;
}
return ::atoi(conf->arg0().c_str()) * 1000;
}
int SrsConfig::get_bw_check_limit_kbps(const string &vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return SRS_CONF_DEFAULT_BANDWIDTH_LIMIT_KBPS;
}
conf = conf->get("bandcheck");
if (!conf) {
return SRS_CONF_DEFAULT_BANDWIDTH_LIMIT_KBPS;
}
conf = conf->get("limit_kbps");
if (!conf) {
return SRS_CONF_DEFAULT_BANDWIDTH_LIMIT_KBPS;
}
return ::atoi(conf->arg0().c_str());
}
int SrsConfig::get_pithy_print_encoder()
{
SrsConfDirective* pithy = root->get("encoder");
if (!pithy) {
return SRS_STAGE_ENCODER_INTERVAL_MS;
}
pithy = pithy->get("forwarder");
if (!pithy) {
return SRS_STAGE_ENCODER_INTERVAL_MS;
}
return ::atoi(pithy->arg0().c_str());
}
int SrsConfig::get_pithy_print_play()
{
SrsConfDirective* pithy = root->get("pithy_print");
if (!pithy) {
return SRS_STAGE_PLAY_USER_INTERVAL_MS;
}
pithy = pithy->get("play");
if (!pithy) {
return SRS_STAGE_PLAY_USER_INTERVAL_MS;
}
return ::atoi(pithy->arg0().c_str());
}
bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b)
{
// both NULL, equal.
if (!a && !b) {
return true;
}
if (!a || !b) {
return false;
}
if (a->name != b->name) {
return false;
}
if (a->args.size() != b->args.size()) {
return false;
}
for (int i = 0; i < (int)a->args.size(); i++) {
if (a->args.at(i) != b->args.at(i)) {
return false;
}
}
if (a->directives.size() != b->directives.size()) {
return false;
}
for (int i = 0; i < (int)a->directives.size(); i++) {
SrsConfDirective* a0 = a->at(i);
SrsConfDirective* b0 = b->at(i);
if (!srs_directive_equals(a0, b0)) {
return false;
}
}
return true;
}
/*
The MIT License (MIT)
Copyright (c) 2013-2014 winlin
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <srs_core_config.hpp>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
// file operations.
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <vector>
#include <algorithm>
using namespace std;
#include <srs_core_error.hpp>
#include <srs_core_log.hpp>
#include <srs_core_autofree.hpp>
#define FILE_OFFSET(fd) lseek(fd, 0, SEEK_CUR)
int64_t FILE_SIZE(int fd)
{
int64_t pre = FILE_OFFSET(fd);
int64_t pos = lseek(fd, 0, SEEK_END);
lseek(fd, pre, SEEK_SET);
return pos;
}
#define LF (char)0x0a
#define CR (char)0x0d
bool is_common_space(char ch)
{
return (ch == ' ' || ch == '\t' || ch == CR || ch == LF);
}
class SrsFileBuffer
{
private:
// last available position.
char* last;
// end of buffer.
char* end;
// start of buffer.
char* start;
public:
// current consumed position.
char* pos;
// current parsed line.
int line;
SrsFileBuffer();
virtual ~SrsFileBuffer();
virtual int fullfill(const char* filename);
virtual bool empty();
};
SrsFileBuffer::SrsFileBuffer()
{
line = 0;
pos = last = start = NULL;
end = start;
}
SrsFileBuffer::~SrsFileBuffer()
{
srs_freepa(start);
}
int SrsFileBuffer::fullfill(const char* filename)
{
int ret = ERROR_SUCCESS;
int fd = -1;
int nread = 0;
int filesize = 0;
if ((fd = ::open(filename, O_RDONLY, 0)) < 0) {
ret = ERROR_SYSTEM_CONFIG_INVALID;
srs_error("open conf file error. ret=%d", ret);
goto finish;
}
if ((filesize = FILE_SIZE(fd) - FILE_OFFSET(fd)) <= 0) {
ret = ERROR_SYSTEM_CONFIG_EOF;
srs_error("read conf file error. ret=%d", ret);
goto finish;
}
srs_freepa(start);
pos = last = start = new char[filesize];
end = start + filesize;
if ((nread = read(fd, start, filesize)) != filesize) {
ret = ERROR_SYSTEM_CONFIG_INVALID;
srs_error("read file read error. expect %d, actual %d bytes, ret=%d",
filesize, nread, ret);
goto finish;
}
line = 1;
finish:
if (fd > 0) {
::close(fd);
}
return ret;
}
bool SrsFileBuffer::empty()
{
return pos >= end;
}
SrsConfDirective::SrsConfDirective()
{
}
SrsConfDirective::~SrsConfDirective()
{
std::vector<SrsConfDirective*>::iterator it;
for (it = directives.begin(); it != directives.end(); ++it) {
SrsConfDirective* directive = *it;
srs_freep(directive);
}
directives.clear();
}
string SrsConfDirective::arg0()
{
if (args.size() > 0) {
return args.at(0);
}
return "";
}
string SrsConfDirective::arg1()
{
if (args.size() > 1) {
return args.at(1);
}
return "";
}
string SrsConfDirective::arg2()
{
if (args.size() > 2) {
return args.at(2);
}
return "";
}
SrsConfDirective* SrsConfDirective::at(int index)
{
return directives.at(index);
}
SrsConfDirective* SrsConfDirective::get(string _name)
{
std::vector<SrsConfDirective*>::iterator it;
for (it = directives.begin(); it != directives.end(); ++it) {
SrsConfDirective* directive = *it;
if (directive->name == _name) {
return directive;
}
}
return NULL;
}
SrsConfDirective* SrsConfDirective::get(string _name, string _arg0)
{
std::vector<SrsConfDirective*>::iterator it;
for (it = directives.begin(); it != directives.end(); ++it) {
SrsConfDirective* directive = *it;
if (directive->name == _name && directive->arg0() == _arg0) {
return directive;
}
}
return NULL;
}
int SrsConfDirective::parse(const char* filename)
{
int ret = ERROR_SUCCESS;
SrsFileBuffer buffer;
if ((ret = buffer.fullfill(filename)) != ERROR_SUCCESS) {
return ret;
}
return parse_conf(&buffer, parse_file);
}
// see: ngx_conf_parse
int SrsConfDirective::parse_conf(SrsFileBuffer* buffer, SrsDirectiveType type)
{
int ret = ERROR_SUCCESS;
while (true) {
std::vector<string> args;
ret = read_token(buffer, args);
/**
* ret maybe:
* ERROR_SYSTEM_CONFIG_INVALID error.
* ERROR_SYSTEM_CONFIG_DIRECTIVE directive terminated by ';' found
* ERROR_SYSTEM_CONFIG_BLOCK_START token terminated by '{' found
* ERROR_SYSTEM_CONFIG_BLOCK_END the '}' found
* ERROR_SYSTEM_CONFIG_EOF the config file is done
*/
if (ret == ERROR_SYSTEM_CONFIG_INVALID) {
return ret;
}
if (ret == ERROR_SYSTEM_CONFIG_BLOCK_END) {
if (type != parse_block) {
srs_error("line %d: unexpected \"}\"", buffer->line);
return ret;
}
return ERROR_SUCCESS;
}
if (ret == ERROR_SYSTEM_CONFIG_EOF) {
if (type == parse_block) {
srs_error("line %d: unexpected end of file, expecting \"}\"", buffer->line);
return ret;
}
return ERROR_SUCCESS;
}
if (args.empty()) {
srs_error("line %d: empty directive.", buffer->line);
return ret;
}
// build directive tree.
SrsConfDirective* directive = new SrsConfDirective();
directive->conf_line = buffer->line;
directive->name = args[0];
args.erase(args.begin());
directive->args.swap(args);
directives.push_back(directive);
if (ret == ERROR_SYSTEM_CONFIG_BLOCK_START) {
if ((ret = directive->parse_conf(buffer, parse_block)) != ERROR_SUCCESS) {
return ret;
}
}
}
return ret;
}
// see: ngx_conf_read_token
int SrsConfDirective::read_token(SrsFileBuffer* buffer, std::vector<string>& args)
{
int ret = ERROR_SUCCESS;
char* pstart = buffer->pos;
int startline = buffer->line;
bool sharp_comment = false;
bool d_quoted = false;
bool s_quoted = false;
bool need_space = false;
bool last_space = true;
while (true) {
if (buffer->empty()) {
ret = ERROR_SYSTEM_CONFIG_EOF;
if (!args.empty() || !last_space) {
srs_error("line %d: unexpected end of file, expecting ; or \"}\"", buffer->line);
return ERROR_SYSTEM_CONFIG_INVALID;
}
srs_error("end of file. ret=%d", ret);
return ret;
}
char ch = *buffer->pos++;
if (ch == LF) {
buffer->line++;
sharp_comment = false;
}
if (sharp_comment) {
continue;
}
if (need_space) {
if (is_common_space(ch)) {
last_space = true;
need_space = false;
continue;
}
if (ch == ';') {
return ERROR_SYSTEM_CONFIG_DIRECTIVE;
}
if (ch == '{') {
return ERROR_SYSTEM_CONFIG_BLOCK_START;
}
srs_error("line %d: unexpected '%c'", buffer->line, ch);
return ERROR_SYSTEM_CONFIG_INVALID;
}
// last charecter is space.
if (last_space) {
if (is_common_space(ch)) {
continue;
}
pstart = buffer->pos - 1;
startline = buffer->line;
switch (ch) {
case ';':
if (args.size() == 0) {
srs_error("line %d: unexpected ';'", buffer->line);
return ERROR_SYSTEM_CONFIG_INVALID;
}
return ERROR_SYSTEM_CONFIG_DIRECTIVE;
case '{':
if (args.size() == 0) {
srs_error("line %d: unexpected '{'", buffer->line);
return ERROR_SYSTEM_CONFIG_INVALID;
}
return ERROR_SYSTEM_CONFIG_BLOCK_START;
case '}':
if (args.size() != 0) {
srs_error("line %d: unexpected '}'", buffer->line);
return ERROR_SYSTEM_CONFIG_INVALID;
}
return ERROR_SYSTEM_CONFIG_BLOCK_END;
case '#':
sharp_comment = 1;
continue;
case '"':
pstart++;
d_quoted = true;
last_space = 0;
continue;
case '\'':
pstart++;
s_quoted = true;
last_space = 0;
continue;
default:
last_space = 0;
continue;
}
} else {
// last charecter is not space
bool found = false;
if (d_quoted) {
if (ch == '"') {
d_quoted = false;
need_space = true;
found = true;
}
} else if (s_quoted) {
if (ch == '\'') {
s_quoted = false;
need_space = true;
found = true;
}
} else if (is_common_space(ch) || ch == ';' || ch == '{') {
last_space = true;
found = 1;
}
if (found) {
int len = buffer->pos - pstart;
char* word = new char[len];
memcpy(word, pstart, len);
word[len - 1] = 0;
string word_str = word;
if (!word_str.empty()) {
args.push_back(word_str);
}
srs_freepa(word);
if (ch == ';') {
return ERROR_SYSTEM_CONFIG_DIRECTIVE;
}
if (ch == '{') {
return ERROR_SYSTEM_CONFIG_BLOCK_START;
}
}
}
}
return ret;
}
SrsConfig* config = new SrsConfig();
SrsConfig::SrsConfig()
{
show_help = false;
show_version = false;
root = new SrsConfDirective();
root->conf_line = 0;
root->name = "root";
}
SrsConfig::~SrsConfig()
{
srs_freep(root);
}
int SrsConfig::reload()
{
int ret = ERROR_SUCCESS;
SrsConfig conf;
if ((ret = conf.parse_file(config_file.c_str())) != ERROR_SUCCESS) {
srs_error("config reloader parse file failed. ret=%d", ret);
return ret;
}
srs_info("config reloader parse file success.");
// store current root to old_root,
// and reap the root from conf to current root.
SrsConfDirective* old_root = root;
SrsAutoFree(SrsConfDirective, old_root, false);
root = conf.root;
conf.root = NULL;
// merge config.
std::vector<ISrsReloadHandler*>::iterator it;
// merge config: listen
if (!srs_directive_equals(root->get("listen"), old_root->get("listen"))) {
for (it = subscribes.begin(); it != subscribes.end(); ++it) {
ISrsReloadHandler* subscribe = *it;
if ((ret = subscribe->on_reload_listen()) != ERROR_SUCCESS) {
srs_error("notify subscribes reload listen failed. ret=%d", ret);
return ret;
}
}
srs_trace("reload listen success.");
}
// merge config: pithy_print
if (!srs_directive_equals(root->get("pithy_print"), old_root->get("pithy_print"))) {
for (it = subscribes.begin(); it != subscribes.end(); ++it) {
ISrsReloadHandler* subscribe = *it;
if ((ret = subscribe->on_reload_pithy_print()) != ERROR_SUCCESS) {
srs_error("notify subscribes pithy_print listen failed. ret=%d", ret);
return ret;
}
}
srs_trace("reload pithy_print success.");
}
// merge config: vhost added, directly supported.
// merge config: vhost removed/disabled/modified.
for (int i = 0; i < (int)old_root->directives.size(); i++) {
SrsConfDirective* old_vhost = old_root->at(i);
// only process vhost directives.
if (old_vhost->name != "vhost") {
continue;
}
std::string vhost = old_vhost->arg0();
SrsConfDirective* new_vhost = root->get("vhost", vhost);
// ignore if absolutely equal
if (new_vhost && srs_directive_equals(old_vhost, new_vhost)) {
srs_trace("vhost %s absolutely equal, ignore.", vhost.c_str());
continue;
}
// ignore if enable the new vhost when old vhost is disabled.
if (get_vhost_enabled(new_vhost) && !get_vhost_enabled(old_vhost)) {
srs_trace("vhost %s disabled=>enabled, ignore.", vhost.c_str());
continue;
}
// ignore if both old and new vhost are disabled.
if (!get_vhost_enabled(new_vhost) && !get_vhost_enabled(old_vhost)) {
srs_trace("vhost %s disabled=>disabled, ignore.", vhost.c_str());
continue;
}
// merge config: vhost removed/disabled.
if (!get_vhost_enabled(new_vhost) && get_vhost_enabled(old_vhost)) {
srs_trace("vhost %s disabled, reload it.", vhost.c_str());
for (it = subscribes.begin(); it != subscribes.end(); ++it) {
ISrsReloadHandler* subscribe = *it;
if ((ret = subscribe->on_reload_vhost_removed(vhost)) != ERROR_SUCCESS) {
srs_error("notify subscribes pithy_print remove "
"vhost %s failed. ret=%d", vhost.c_str(), ret);
return ret;
}
}
srs_trace("reload remove vhost %s success.", vhost.c_str());
}
// merge config: vhost modified.
srs_trace("vhost %s modified, reload its detail.", vhost.c_str());
if (get_vhost_enabled(new_vhost) && get_vhost_enabled(old_vhost)) {
// gop_cache
if (!srs_directive_equals(new_vhost->get("gop_cache"), old_vhost->get("gop_cache"))) {
for (it = subscribes.begin(); it != subscribes.end(); ++it) {
ISrsReloadHandler* subscribe = *it;
if ((ret = subscribe->on_reload_gop_cache(vhost)) != ERROR_SUCCESS) {
srs_error("vhost %s notify subscribes gop_cache failed. ret=%d", vhost.c_str(), ret);
return ret;
}
}
srs_trace("vhost %s reload gop_cache success.", vhost.c_str());
}
// queue_length
if (!srs_directive_equals(new_vhost->get("queue_length"), old_vhost->get("queue_length"))) {
for (it = subscribes.begin(); it != subscribes.end(); ++it) {
ISrsReloadHandler* subscribe = *it;
if ((ret = subscribe->on_reload_queue_length(vhost)) != ERROR_SUCCESS) {
srs_error("vhost %s notify subscribes queue_length failed. ret=%d", vhost.c_str(), ret);
return ret;
}
}
srs_trace("vhost %s reload queue_length success.", vhost.c_str());
}
// forward
if (!srs_directive_equals(new_vhost->get("forward"), old_vhost->get("forward"))) {
for (it = subscribes.begin(); it != subscribes.end(); ++it) {
ISrsReloadHandler* subscribe = *it;
if ((ret = subscribe->on_reload_forward(vhost)) != ERROR_SUCCESS) {
srs_error("vhost %s notify subscribes forward failed. ret=%d", vhost.c_str(), ret);
return ret;
}
}
srs_trace("vhost %s reload forward success.", vhost.c_str());
}
// hls
if (!srs_directive_equals(new_vhost->get("hls"), old_vhost->get("hls"))) {
for (it = subscribes.begin(); it != subscribes.end(); ++it) {
ISrsReloadHandler* subscribe = *it;
if ((ret = subscribe->on_reload_hls(vhost)) != ERROR_SUCCESS) {
srs_error("vhost %s notify subscribes hls failed. ret=%d", vhost.c_str(), ret);
return ret;
}
}
srs_trace("vhost %s reload hls success.", vhost.c_str());
}
// transcode
if (!srs_directive_equals(new_vhost->get("transcode"), old_vhost->get("transcode"))) {
for (it = subscribes.begin(); it != subscribes.end(); ++it) {
ISrsReloadHandler* subscribe = *it;
if ((ret = subscribe->on_reload_transcode(vhost)) != ERROR_SUCCESS) {
srs_error("vhost %s notify subscribes transcode failed. ret=%d", vhost.c_str(), ret);
return ret;
}
}
srs_trace("vhost %s reload transcode success.", vhost.c_str());
}
// TODO: suppor reload hls/forward/ffmpeg/http
continue;
}
srs_warn("invalid reload path, enabled old: %d, new: %d",
get_vhost_enabled(old_vhost), get_vhost_enabled(new_vhost));
}
return ret;
}
void SrsConfig::subscribe(ISrsReloadHandler* handler)
{
std::vector<ISrsReloadHandler*>::iterator it;
it = std::find(subscribes.begin(), subscribes.end(), handler);
if (it != subscribes.end()) {
return;
}
subscribes.push_back(handler);
}
void SrsConfig::unsubscribe(ISrsReloadHandler* handler)
{
std::vector<ISrsReloadHandler*>::iterator it;
it = std::find(subscribes.begin(), subscribes.end(), handler);
if (it == subscribes.end()) {
return;
}
subscribes.erase(it);
}
// see: ngx_get_options
int SrsConfig::parse_options(int argc, char** argv)
{
int ret = ERROR_SUCCESS;
for (int i = 1; i < argc; i++) {
if ((ret = parse_argv(i, argv)) != ERROR_SUCCESS) {
return ret;
}
}
if (show_help) {
print_help(argv);
}
if (show_version) {
printf("%s\n", RTMP_SIG_SRS_VERSION);
}
if (show_help || show_version) {
exit(0);
}
if (config_file.empty()) {
ret = ERROR_SYSTEM_CONFIG_INVALID;
srs_error("config file not specified, see help: %s -h, ret=%d", argv[0], ret);
return ret;
}
return parse_file(config_file.c_str());
}
int SrsConfig::parse_file(const char* filename)
{
int ret = ERROR_SUCCESS;
config_file = filename;
if (config_file.empty()) {
return ERROR_SYSTEM_CONFIG_INVALID;
}
if ((ret = root->parse(config_file.c_str())) != ERROR_SUCCESS) {
return ret;
}
SrsConfDirective* conf = NULL;
if ((conf = get_listen()) == NULL || conf->args.size() == 0) {
ret = ERROR_SYSTEM_CONFIG_INVALID;
srs_error("line %d: conf error, "
"directive \"listen\" is empty, ret=%d", (conf? conf->conf_line:0), ret);
return ret;
}
// TODO: check the hls.
// TODO: check forward.
// TODO: check ffmpeg.
// TODO: check http.
return ret;
}
int SrsConfig::parse_argv(int& i, char** argv)
{
int ret = ERROR_SUCCESS;
char* p = argv[i];
if (*p++ != '-') {
ret = ERROR_SYSTEM_CONFIG_INVALID;
srs_error("invalid options(index=%d, value=%s), "
"must starts with -, see help: %s -h, ret=%d", i, argv[i], argv[0], ret);
return ret;
}
while (*p) {
switch (*p++) {
case '?':
case 'h':
show_help = true;
break;
case 'v':
case 'V':
show_version = true;
break;
case 'c':
if (*p) {
config_file = p;
return ret;
}
if (argv[++i]) {
config_file = argv[i];
return ret;
}
ret = ERROR_SYSTEM_CONFIG_INVALID;
srs_error("option \"-c\" requires parameter, ret=%d", ret);
return ret;
default:
ret = ERROR_SYSTEM_CONFIG_INVALID;
srs_error("invalid option: \"%c\", see help: %s -h, ret=%d", *(p - 1), argv[0], ret);
return ret;
}
}
return ret;
}
void SrsConfig::print_help(char** argv)
{
printf(
RTMP_SIG_SRS_NAME" "RTMP_SIG_SRS_VERSION" "RTMP_SIG_SRS_COPYRIGHT"\n"
"Primary Authors: "RTMP_SIG_SRS_PRIMARY_AUTHROS"\n"
"Build: "SRS_BUILD_DATE" Configuration: "SRS_CONFIGURE"\n"
"Usage: %s [-h?vV] [-c <filename>]\n"
"\n"
"Options:\n"
" -?-h : show help\n"
" -v-V : show version and exit\n"
" -c filename : set configuration file\n"
"\n"
RTMP_SIG_SRS_WEB"\n"
RTMP_SIG_SRS_URL"\n"
"Email: "RTMP_SIG_SRS_EMAIL"\n"
"\n",
argv[0]);
}
SrsConfDirective* SrsConfig::get_vhost(string vhost)
{
srs_assert(root);
for (int i = 0; i < (int)root->directives.size(); i++) {
SrsConfDirective* conf = root->at(i);
if (conf->name != "vhost") {
continue;
}
if (conf->arg0() == vhost) {
return conf;
}
}
if (vhost != RTMP_VHOST_DEFAULT) {
return get_vhost(RTMP_VHOST_DEFAULT);
}
return NULL;
}
SrsConfDirective* SrsConfig::get_vhost_on_connect(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return NULL;
}
conf = conf->get("http_hooks");
if (!conf) {
return NULL;
}
SrsConfDirective* enabled = conf->get("enabled");
if (!enabled || enabled->arg0() != "on") {
return NULL;
}
return conf->get("on_connect");
}
SrsConfDirective* SrsConfig::get_vhost_on_close(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return NULL;
}
conf = conf->get("http_hooks");
if (!conf) {
return NULL;
}
SrsConfDirective* enabled = conf->get("enabled");
if (!enabled || enabled->arg0() != "on") {
return NULL;
}
return conf->get("on_close");
}
SrsConfDirective* SrsConfig::get_vhost_on_publish(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return NULL;
}
conf = conf->get("http_hooks");
if (!conf) {
return NULL;
}
SrsConfDirective* enabled = conf->get("enabled");
if (!enabled || enabled->arg0() != "on") {
return NULL;
}
return conf->get("on_publish");
}
SrsConfDirective* SrsConfig::get_vhost_on_unpublish(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return NULL;
}
conf = conf->get("http_hooks");
if (!conf) {
return NULL;
}
SrsConfDirective* enabled = conf->get("enabled");
if (!enabled || enabled->arg0() != "on") {
return NULL;
}
return conf->get("on_unpublish");
}
SrsConfDirective* SrsConfig::get_vhost_on_play(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return NULL;
}
conf = conf->get("http_hooks");
if (!conf) {
return NULL;
}
SrsConfDirective* enabled = conf->get("enabled");
if (!enabled || enabled->arg0() != "on") {
return NULL;
}
return conf->get("on_play");
}
SrsConfDirective* SrsConfig::get_vhost_on_stop(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return NULL;
}
conf = conf->get("http_hooks");
if (!conf) {
return NULL;
}
SrsConfDirective* enabled = conf->get("enabled");
if (!enabled || enabled->arg0() != "on") {
return NULL;
}
return conf->get("on_stop");
}
bool SrsConfig::get_vhost_enabled(string vhost)
{
SrsConfDirective* vhost_conf = get_vhost(vhost);
return get_vhost_enabled(vhost_conf);
}
bool SrsConfig::get_vhost_enabled(SrsConfDirective* vhost)
{
if (!vhost) {
return false;
}
SrsConfDirective* conf = vhost->get("enabled");
if (!conf) {
return true;
}
if (conf->arg0() == "off") {
return false;
}
return true;
}
SrsConfDirective* SrsConfig::get_transcode(string vhost, string scope)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return NULL;
}
SrsConfDirective* transcode = conf->get("transcode");
if (!transcode) {
return NULL;
}
if (transcode->arg0() == scope) {
return transcode;
}
return NULL;
}
bool SrsConfig::get_transcode_enabled(SrsConfDirective* transcode)
{
if (!transcode) {
return false;
}
SrsConfDirective* conf = transcode->get("enabled");
if (!conf || conf->arg0() != "on") {
return false;
}
return true;
}
string SrsConfig::get_transcode_ffmpeg(SrsConfDirective* transcode)
{
if (!transcode) {
return "";
}
SrsConfDirective* conf = transcode->get("ffmpeg");
if (!conf) {
return "";
}
return conf->arg0();
}
void SrsConfig::get_transcode_engines(SrsConfDirective* transcode, std::vector<SrsConfDirective*>& engines)
{
if (!transcode) {
return;
}
for (int i = 0; i < (int)transcode->directives.size(); i++) {
SrsConfDirective* conf = transcode->directives[i];
if (conf->name == "engine") {
engines.push_back(conf);
}
}
return;
}
bool SrsConfig::get_engine_enabled(SrsConfDirective* engine)
{
if (!engine) {
return false;
}
SrsConfDirective* conf = engine->get("enabled");
if (!conf || conf->arg0() != "on") {
return false;
}
return true;
}
string SrsConfig::get_engine_vcodec(SrsConfDirective* engine)
{
if (!engine) {
return "";
}
SrsConfDirective* conf = engine->get("vcodec");
if (!conf) {
return "";
}
return conf->arg0();
}
int SrsConfig::get_engine_vbitrate(SrsConfDirective* engine)
{
if (!engine) {
return 0;
}
SrsConfDirective* conf = engine->get("vbitrate");
if (!conf) {
return 0;
}
return ::atoi(conf->arg0().c_str());
}
double SrsConfig::get_engine_vfps(SrsConfDirective* engine)
{
if (!engine) {
return 0;
}
SrsConfDirective* conf = engine->get("vfps");
if (!conf) {
return 0;
}
return ::atof(conf->arg0().c_str());
}
int SrsConfig::get_engine_vwidth(SrsConfDirective* engine)
{
if (!engine) {
return 0;
}
SrsConfDirective* conf = engine->get("vwidth");
if (!conf) {
return 0;
}
return ::atoi(conf->arg0().c_str());
}
int SrsConfig::get_engine_vheight(SrsConfDirective* engine)
{
if (!engine) {
return 0;
}
SrsConfDirective* conf = engine->get("vheight");
if (!conf) {
return 0;
}
return ::atoi(conf->arg0().c_str());
}
int SrsConfig::get_engine_vthreads(SrsConfDirective* engine)
{
if (!engine) {
return 0;
}
SrsConfDirective* conf = engine->get("vthreads");
if (!conf) {
return 0;
}
return ::atoi(conf->arg0().c_str());
}
string SrsConfig::get_engine_vprofile(SrsConfDirective* engine)
{
if (!engine) {
return "";
}
SrsConfDirective* conf = engine->get("vprofile");
if (!conf) {
return "";
}
return conf->arg0();
}
string SrsConfig::get_engine_vpreset(SrsConfDirective* engine)
{
if (!engine) {
return "";
}
SrsConfDirective* conf = engine->get("vpreset");
if (!conf) {
return "";
}
return conf->arg0();
}
void SrsConfig::get_engine_vparams(SrsConfDirective* engine, std::vector<string>& vparams)
{
if (!engine) {
return;
}
SrsConfDirective* conf = engine->get("vparams");
if (!conf) {
return;
}
for (int i = 0; i < (int)conf->directives.size(); i++) {
SrsConfDirective* p = conf->directives[i];
if (!p) {
continue;
}
vparams.push_back("-" + p->name);
vparams.push_back(p->arg0());
}
}
void SrsConfig::get_engine_vfilter(SrsConfDirective* engine, std::vector<string>& vfilter)
{
if (!engine) {
return;
}
SrsConfDirective* conf = engine->get("vfilter");
if (!conf) {
return;
}
for (int i = 0; i < (int)conf->directives.size(); i++) {
SrsConfDirective* p = conf->directives[i];
if (!p) {
continue;
}
vfilter.push_back("-" + p->name);
vfilter.push_back(p->arg0());
}
}
string SrsConfig::get_engine_acodec(SrsConfDirective* engine)
{
if (!engine) {
return "";
}
SrsConfDirective* conf = engine->get("acodec");
if (!conf) {
return "";
}
return conf->arg0();
}
int SrsConfig::get_engine_abitrate(SrsConfDirective* engine)
{
if (!engine) {
return 0;
}
SrsConfDirective* conf = engine->get("abitrate");
if (!conf) {
return 0;
}
return ::atoi(conf->arg0().c_str());
}
int SrsConfig::get_engine_asample_rate(SrsConfDirective* engine)
{
if (!engine) {
return 0;
}
SrsConfDirective* conf = engine->get("asample_rate");
if (!conf) {
return 0;
}
return ::atoi(conf->arg0().c_str());
}
int SrsConfig::get_engine_achannels(SrsConfDirective* engine)
{
if (!engine) {
return 0;
}
SrsConfDirective* conf = engine->get("achannels");
if (!conf) {
return 0;
}
return ::atoi(conf->arg0().c_str());
}
void SrsConfig::get_engine_aparams(SrsConfDirective* engine, std::vector<string>& aparams)
{
if (!engine) {
return;
}
SrsConfDirective* conf = engine->get("aparams");
if (!conf) {
return;
}
for (int i = 0; i < (int)conf->directives.size(); i++) {
SrsConfDirective* p = conf->directives[i];
if (!p) {
continue;
}
aparams.push_back("-" + p->name);
aparams.push_back(p->arg0());
}
}
string SrsConfig::get_engine_output(SrsConfDirective* engine)
{
if (!engine) {
return "";
}
SrsConfDirective* conf = engine->get("output");
if (!conf) {
return "";
}
return conf->arg0();
}
string SrsConfig::get_log_dir()
{
srs_assert(root);
SrsConfDirective* conf = root->get("log_dir");
if (!conf || conf->arg0().empty()) {
return "./objs/logs";
}
return conf->arg0();
}
int SrsConfig::get_max_connections()
{
srs_assert(root);
SrsConfDirective* conf = root->get("max_connections");
if (!conf || conf->arg0().empty()) {
return 2000;
}
return ::atoi(conf->arg0().c_str());
}
bool SrsConfig::get_gop_cache(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return true;
}
conf = conf->get("gop_cache");
if (conf && conf->arg0() == "off") {
return false;
}
return true;
}
double SrsConfig::get_queue_length(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return SRS_CONF_DEFAULT_QUEUE_LENGTH;
}
conf = conf->get("queue_length");
if (!conf || conf->arg0().empty()) {
return SRS_CONF_DEFAULT_QUEUE_LENGTH;
}
return ::atoi(conf->arg0().c_str());
}
SrsConfDirective* SrsConfig::get_forward(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return NULL;
}
return conf->get("forward");
}
SrsConfDirective* SrsConfig::get_hls(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return NULL;
}
return conf->get("hls");
}
bool SrsConfig::get_hls_enabled(string vhost)
{
SrsConfDirective* hls = get_hls(vhost);
if (!hls) {
return false;
}
SrsConfDirective* conf = hls->get("enabled");
if (!conf) {
return false;
}
if (conf->arg0() == "on") {
return true;
}
return false;
}
string SrsConfig::get_hls_path(string vhost)
{
SrsConfDirective* hls = get_hls(vhost);
if (!hls) {
return SRS_CONF_DEFAULT_HLS_PATH;
}
SrsConfDirective* conf = hls->get("hls_path");
if (!conf) {
return SRS_CONF_DEFAULT_HLS_PATH;
}
return conf->arg0();
}
double SrsConfig::get_hls_fragment(string vhost)
{
SrsConfDirective* hls = get_hls(vhost);
if (!hls) {
return SRS_CONF_DEFAULT_HLS_FRAGMENT;
}
SrsConfDirective* conf = hls->get("hls_fragment");
if (!conf) {
return SRS_CONF_DEFAULT_HLS_FRAGMENT;
}
return ::atof(conf->arg0().c_str());
}
double SrsConfig::get_hls_window(string vhost)
{
SrsConfDirective* hls = get_hls(vhost);
if (!hls) {
return SRS_CONF_DEFAULT_HLS_WINDOW;
}
SrsConfDirective* conf = hls->get("hls_window");
if (!conf) {
return SRS_CONF_DEFAULT_HLS_WINDOW;
}
return ::atof(conf->arg0().c_str());
}
SrsConfDirective* SrsConfig::get_refer(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return NULL;
}
return conf->get("refer");
}
SrsConfDirective* SrsConfig::get_refer_play(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return NULL;
}
return conf->get("refer_play");
}
SrsConfDirective* SrsConfig::get_refer_publish(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return NULL;
}
return conf->get("refer_publish");
}
SrsConfDirective* SrsConfig::get_listen()
{
return root->get("listen");
}
int SrsConfig::get_chunk_size(const std::string &vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return SRS_CONF_DEFAULT_CHUNK_SIZE;
}
conf = conf->get("chunk_size");
if (!conf) {
// vhost does not specify the chunk size,
// use the global instead.
conf = root->get("chunk_size");
if (!conf) {
return SRS_CONF_DEFAULT_CHUNK_SIZE;
}
return ::atoi(conf->arg0().c_str());
}
return ::atoi(conf->arg0().c_str());
}
int SrsConfig::get_pithy_print_publish()
{
SrsConfDirective* pithy = root->get("pithy_print");
if (!pithy) {
return SRS_STAGE_PUBLISH_USER_INTERVAL_MS;
}
pithy = pithy->get("publish");
if (!pithy) {
return SRS_STAGE_PUBLISH_USER_INTERVAL_MS;
}
return ::atoi(pithy->arg0().c_str());
}
int SrsConfig::get_pithy_print_forwarder()
{
SrsConfDirective* pithy = root->get("pithy_print");
if (!pithy) {
return SRS_STAGE_FORWARDER_INTERVAL_MS;
}
pithy = pithy->get("forwarder");
if (!pithy) {
return SRS_STAGE_FORWARDER_INTERVAL_MS;
}
return ::atoi(pithy->arg0().c_str());
}
int SrsConfig::get_pithy_print_hls()
{
SrsConfDirective* pithy = root->get("pithy_print");
if (!pithy) {
return SRS_STAGE_HLS_INTERVAL_MS;
}
pithy = pithy->get("hls");
if (!pithy) {
return SRS_STAGE_HLS_INTERVAL_MS;
}
return ::atoi(pithy->arg0().c_str());
}
bool SrsConfig::get_bw_check_enabled(const string &vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return false;
}
conf = conf->get("bandcheck");
if (!conf) {
return false;
}
conf = conf->get("enabled");
if (!conf || conf->arg0() != "on") {
return false;
}
return true;
}
string SrsConfig::get_bw_check_key(const string &vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return "";
}
conf = conf->get("bandcheck");
if (!conf) {
return "";
}
conf = conf->get("key");
if (!conf) {
return "";
}
return conf->arg0();
}
int SrsConfig::get_bw_check_interval_ms(const string &vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return SRS_CONF_DEFAULT_BANDWIDTH_INTERVAL;
}
conf = conf->get("bandcheck");
if (!conf) {
return SRS_CONF_DEFAULT_BANDWIDTH_INTERVAL;
}
conf = conf->get("interval_ms");
if (!conf) {
return SRS_CONF_DEFAULT_BANDWIDTH_INTERVAL;
}
return ::atoi(conf->arg0().c_str()) * 1000;
}
int SrsConfig::get_bw_check_limit_kbps(const string &vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return SRS_CONF_DEFAULT_BANDWIDTH_LIMIT_KBPS;
}
conf = conf->get("bandcheck");
if (!conf) {
return SRS_CONF_DEFAULT_BANDWIDTH_LIMIT_KBPS;
}
conf = conf->get("limit_kbps");
if (!conf) {
return SRS_CONF_DEFAULT_BANDWIDTH_LIMIT_KBPS;
}
return ::atoi(conf->arg0().c_str());
}
int SrsConfig::get_pithy_print_encoder()
{
SrsConfDirective* pithy = root->get("encoder");
if (!pithy) {
return SRS_STAGE_ENCODER_INTERVAL_MS;
}
pithy = pithy->get("forwarder");
if (!pithy) {
return SRS_STAGE_ENCODER_INTERVAL_MS;
}
return ::atoi(pithy->arg0().c_str());
}
int SrsConfig::get_pithy_print_play()
{
SrsConfDirective* pithy = root->get("pithy_print");
if (!pithy) {
return SRS_STAGE_PLAY_USER_INTERVAL_MS;
}
pithy = pithy->get("play");
if (!pithy) {
return SRS_STAGE_PLAY_USER_INTERVAL_MS;
}
return ::atoi(pithy->arg0().c_str());
}
bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b)
{
// both NULL, equal.
if (!a && !b) {
return true;
}
if (!a || !b) {
return false;
}
if (a->name != b->name) {
return false;
}
if (a->args.size() != b->args.size()) {
return false;
}
for (int i = 0; i < (int)a->args.size(); i++) {
if (a->args.at(i) != b->args.at(i)) {
return false;
}
}
if (a->directives.size() != b->directives.size()) {
return false;
}
for (int i = 0; i < (int)a->directives.size(); i++) {
SrsConfDirective* a0 = a->at(i);
SrsConfDirective* b0 = b->at(i);
if (!srs_directive_equals(a0, b0)) {
return false;
}
}
return true;
}
... ...