diff --git a/README.md b/README.md
old mode 100644
new mode 100755
index 9c3920568..0ad829cb0
--- a/README.md
+++ b/README.md
@@ -21,52 +21,52 @@ people who have submitted patches, reported bugs, added translations, helped
step -1: get SRS
+Step -1: get SRS
git clone https://github.com/winlinvip/simple-rtmp-server &&
cd simple-rtmp-server/trunk
-step 0: build SRS system.
+Step 0: build SRS system.
bash scripts/build.sh
-step 1: start SRS all demo features.
+Step 1: start SRS all demo features.
bash scripts/run.sh
-step 2: SRS live show: [http://your-server-ip](http://your-server-ip)
-step 3: stop SRS demo
+Step 2: SRS live show: [http://your-server-ip](http://your-server-ip)
+Step 3: stop SRS demo
bash scripts/stop.sh
### Usage(detail)
-step 0: get SRS
+Step 0: get SRS
git clone https://github.com/winlinvip/simple-rtmp-server &&
cd simple-rtmp-server/trunk
-step 1: build SRS
+Step 1: build SRS
./configure --with-ssl --with-hls --with-ffmpeg --with-http && make
-step 2: start SRS
+Step 2: start SRS
./objs/srs -c conf/srs.conf
-step 3(optinal): start SRS listen at 19350 to forward to
+Step 3(optinal): start SRS listen at 19350 to forward to
./objs/srs -c conf/srs.19350.conf
-step 4(optinal): start nginx for HLS
+Step 4(optinal): start nginx for HLS
sudo ./objs/nginx/sbin/nginx
-step 5(optinal): start http hooks for SRS callback
+Step 5(optinal): start http hooks for SRS callback
python ./research/api-server/server.py 8085
-step 6: publish demo live stream
+Step 6: publish demo live stream
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
-step 7: publish players live stream
+Step 7: publish players live stream
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
-step 8: add server ip to client hosts as demo.
+Step 8: add server ip to client hosts as demo.
# 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
-step 9: play live stream.
+Step 9: play live stream.
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
-step 10(optinal): play live stream auto transcoded
+Step 10(optinal): play live stream auto transcoded
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
-step 11(optinal): play live stream auto forwarded, the hls dir change to /forward
+Step 11(optinal): play live stream auto forwarded, the hls dir change to /forward
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
-step 12(optinal): modify the config and reload it (all features support reload)
+Step 12(optinal): modify the config and reload it (all features support reload)
killall -1 srs
@@ -190,7 +190,7 @@ Bandwidth Test Workflow:
| final(2)-----------------> |
| <END> |
-@see: class SrsBandwidth comments.
+@See: class SrsBandwidth comments.
### System Requirements
@@ -199,37 +199,38 @@ Supported operating systems and hardware:
* All handware.
### Summary
-1. simple: also stable enough.
-2. high-performance: single-thread, async socket, event/st-thread driven.
-3. no edge server, origin server only.
-4. no vod streaming, live streaming only.
-5. no multiple processes, single process only.
-6. support vhost, support \_\_defaultVhost\_\_.
-7. support adobe rtmp live streaming.
-8. support apple hls(m3u8) live streaming.
-9. support reload config to enable changes.
-10. support cache last gop for flash player to fast startup.
-11. support listen at multiple ports.
-12. support long time(>4.6hours) publish/play.
-13. high performace, 1800 connections(500kbps), 900Mbps, CPU 90.2%, 41MB
-14. support forward publish stream to build active-standby cluster.
-15. support broadcast by forward the stream to other servers(origin/edge).
-16. support live stream transcoding by ffmpeg.
-17. support live stream forward(acopy/vcopy) by ffmpeg.
-18. support ffmpeg filters(logo/overlay/crop), x264 params.
-19. support audio transcode only, speex/mp3 to aac
-20. support http callback api hooks(for authentication and injection).
-21. support bandwidth test api and flash client.
-22. player, publisher(encoder), and demo pages(jquery+bootstrap).
-23. demo video meeting or chat(SRS+cherrypy+jquery+bootstrap).
-24. [plan] support network based cli and json result.
-25. [plan] support adobe flash refer/token/swf verification.
-26. [plan] support adobe amf3 codec.
-27. [plan] support dvr(record live to vod file)
-28. [plan] support FMS edge protocol
-29. [plan] support encryption: RTMPE/RTMPS, HLS DRM
-30. [plan] support RTMPT, http to tranverse firewalls
-31. [plan] support file source, transcoding file to live stream
+1. Simple: also stable enough.
+2. High-performance: single-thread, async socket, event/st-thread driven.
+3. NO edge server, origin server only.
+4. NO vod streaming, live streaming only.
+5. NO multiple processes, single process only.
+6. Support vhost, support \_\_defaultVhost\_\_.
+7. Support adobe rtmp live streaming.
+8. Support apple hls(m3u8) live streaming.
+9. Support reload config to enable changes.
+10. Support cache last gop for flash player to fast startup.
+11. Support listen at multiple ports.
+12. Support long time(>4.6hours) publish/play.
+13. High performace, 1800 connections(500kbps), 900Mbps, CPU 90.2%, 41MB
+14. Support forward publish stream to build active-standby cluster.
+15. Support broadcast by forward the stream to other servers(origin/edge).
+16. Support live stream transcoding by ffmpeg.
+17. Support live stream forward(acopy/vcopy) by ffmpeg.
+18. Support ffmpeg filters(logo/overlay/crop), x264 params.
+19. Support audio transcode only, speex/mp3 to aac
+20. Support http callback api hooks(for authentication and injection).
+21. Support bandwidth test api and flash client.
+22. Player, publisher(encoder), and demo pages(jquery+bootstrap).
+23. Demo video meeting or chat(SRS+cherrypy+jquery+bootstrap).
+24. [dev] Full documents in wiki, in chineses.
+25. [plan] Support network based cli and json result.
+26. [plan] Support adobe flash refer/token/swf verification.
+27. [plan] Support adobe amf3 codec.
+28. [plan] Support dvr(record live to vod file)
+29. [plan] Support FMS edge protocol
+30. [plan] Support encryption: RTMPE/RTMPS, HLS DRM
+31. [plan] Support RTMPT, http to tranverse firewalls
+32. [plan] Support file source, transcoding file to live stream
### 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.
+[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
### 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.
-* 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.
-* 2013-12-03, [release v0.7](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.7), support live stream transcoding. 17605 lines.
-* 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.
-* 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.
-* 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.
-* 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.
-* 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.
-* 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.
-* 2013-10-17, created.
+* 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.
+* 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.
+* 2013-12-03, [Release v0.7](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.7), support live stream transcoding. 17605 lines.
+* 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.
+* 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.
+* 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.
+* 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.
+* 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.
+* 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.
+* 2013-10-17, Created.
### Compare
* SRS v0.9: 20926 lines. player/encoder/chat demos. bandwidth test for encoder/CDN.
diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp
index 8e6a8859d..b42d35e70 100644
--- a/trunk/src/core/srs_core.hpp
+++ b/trunk/src/core/srs_core.hpp
@@ -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"
diff --git a/trunk/src/core/srs_core_config.cpp b/trunk/src/core/srs_core_config.cpp
old mode 100755
new mode 100644
index f8223df68..812bb7062
--- a/trunk/src/core/srs_core_config.cpp
+++ b/trunk/src/core/srs_core_config.cpp
@@ -1,1667 +1,1666 @@
-/*
-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
-
-#include
-#include
-#include
-#include
-// file operations.
-#include
-#include
-#include
-#include
-
-#include
-#include
-using namespace std;
-
-#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);
-}
-
-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::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::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::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 args;
- ret = read_token(buffer, args);
-
- /**
- * ret maybe:
- * ERROR_SYSTEM_CONFIG_INVALID error.
- * ERROR_SYSTEM_CONFIG_DIRECTIVE directive terminated by ';' found
- * ERROR_SYSTEM_CONFIG_BLOCK_START token terminated by '{' found
- * ERROR_SYSTEM_CONFIG_BLOCK_END the '}' found
- * ERROR_SYSTEM_CONFIG_EOF the config file is done
- */
- if (ret == ERROR_SYSTEM_CONFIG_INVALID) {
- return ret;
- }
- if (ret == ERROR_SYSTEM_CONFIG_BLOCK_END) {
- if (type != parse_block) {
- srs_error("line %d: unexpected \"}\"", buffer->line);
- return ret;
- }
- return ERROR_SUCCESS;
- }
- if (ret == ERROR_SYSTEM_CONFIG_EOF) {
- if (type == parse_block) {
- srs_error("line %d: unexpected end of file, expecting \"}\"", buffer->line);
- return ret;
- }
- return ERROR_SUCCESS;
- }
-
- if (args.empty()) {
- srs_error("line %d: empty directive.", buffer->line);
- return ret;
- }
-
- // build directive tree.
- SrsConfDirective* directive = new SrsConfDirective();
-
- directive->conf_line = buffer->line;
- directive->name = args[0];
- args.erase(args.begin());
- directive->args.swap(args);
-
- directives.push_back(directive);
-
- if (ret == ERROR_SYSTEM_CONFIG_BLOCK_START) {
- if ((ret = directive->parse_conf(buffer, parse_block)) != ERROR_SUCCESS) {
- return ret;
- }
- }
- }
-
- return ret;
-}
-
-// see: ngx_conf_read_token
-int SrsConfDirective::read_token(SrsFileBuffer* buffer, std::vector& args)
-{
- int ret = ERROR_SUCCESS;
-
- char* pstart = buffer->pos;
- int startline = buffer->line;
-
- bool sharp_comment = false;
-
- bool d_quoted = false;
- bool s_quoted = false;
-
- bool need_space = false;
- bool last_space = true;
-
- while (true) {
- if (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::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::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::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 ]\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& 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& vparams)
-{
- if (!engine) {
- return;
- }
-
- SrsConfDirective* conf = engine->get("vparams");
- if (!conf) {
- return;
- }
-
- for (int i = 0; i < (int)conf->directives.size(); i++) {
- SrsConfDirective* p = conf->directives[i];
- if (!p) {
- continue;
- }
-
- vparams.push_back("-" + p->name);
- vparams.push_back(p->arg0());
- }
-}
-
-void SrsConfig::get_engine_vfilter(SrsConfDirective* engine, std::vector& vfilter)
-{
- if (!engine) {
- return;
- }
-
- SrsConfDirective* conf = engine->get("vfilter");
- if (!conf) {
- return;
- }
-
- for (int i = 0; i < (int)conf->directives.size(); i++) {
- SrsConfDirective* p = conf->directives[i];
- if (!p) {
- continue;
- }
-
- vfilter.push_back("-" + p->name);
- vfilter.push_back(p->arg0());
- }
-}
-
-string SrsConfig::get_engine_acodec(SrsConfDirective* engine)
-{
- if (!engine) {
- return "";
- }
-
- SrsConfDirective* conf = engine->get("acodec");
- if (!conf) {
- return "";
- }
-
- return conf->arg0();
-}
-
-int SrsConfig::get_engine_abitrate(SrsConfDirective* engine)
-{
- if (!engine) {
- return 0;
- }
-
- SrsConfDirective* conf = engine->get("abitrate");
- if (!conf) {
- return 0;
- }
-
- return ::atoi(conf->arg0().c_str());
-}
-
-int SrsConfig::get_engine_asample_rate(SrsConfDirective* engine)
-{
- if (!engine) {
- return 0;
- }
-
- SrsConfDirective* conf = engine->get("asample_rate");
- if (!conf) {
- return 0;
- }
-
- return ::atoi(conf->arg0().c_str());
-}
-
-int SrsConfig::get_engine_achannels(SrsConfDirective* engine)
-{
- if (!engine) {
- return 0;
- }
-
- SrsConfDirective* conf = engine->get("achannels");
- if (!conf) {
- return 0;
- }
-
- return ::atoi(conf->arg0().c_str());
-}
-
-void SrsConfig::get_engine_aparams(SrsConfDirective* engine, std::vector& aparams)
-{
- if (!engine) {
- return;
- }
-
- SrsConfDirective* conf = engine->get("aparams");
- if (!conf) {
- return;
- }
-
- for (int i = 0; i < (int)conf->directives.size(); i++) {
- SrsConfDirective* p = conf->directives[i];
- if (!p) {
- continue;
- }
-
- aparams.push_back("-" + p->name);
- aparams.push_back(p->arg0());
- }
-}
-
-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
+
+#include
+#include
+#include
+#include
+// file operations.
+#include
+#include
+#include
+#include
+
+#include
+#include
+using namespace std;
+
+#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);
+}
+
+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::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::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::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 args;
+ ret = read_token(buffer, args);
+
+ /**
+ * ret maybe:
+ * ERROR_SYSTEM_CONFIG_INVALID error.
+ * ERROR_SYSTEM_CONFIG_DIRECTIVE directive terminated by ';' found
+ * ERROR_SYSTEM_CONFIG_BLOCK_START token terminated by '{' found
+ * ERROR_SYSTEM_CONFIG_BLOCK_END the '}' found
+ * ERROR_SYSTEM_CONFIG_EOF the config file is done
+ */
+ if (ret == ERROR_SYSTEM_CONFIG_INVALID) {
+ return ret;
+ }
+ if (ret == ERROR_SYSTEM_CONFIG_BLOCK_END) {
+ if (type != parse_block) {
+ srs_error("line %d: unexpected \"}\"", buffer->line);
+ return ret;
+ }
+ return ERROR_SUCCESS;
+ }
+ if (ret == ERROR_SYSTEM_CONFIG_EOF) {
+ if (type == parse_block) {
+ srs_error("line %d: unexpected end of file, expecting \"}\"", buffer->line);
+ return ret;
+ }
+ return ERROR_SUCCESS;
+ }
+
+ if (args.empty()) {
+ srs_error("line %d: empty directive.", buffer->line);
+ return ret;
+ }
+
+ // build directive tree.
+ SrsConfDirective* directive = new SrsConfDirective();
+
+ directive->conf_line = buffer->line;
+ directive->name = args[0];
+ args.erase(args.begin());
+ directive->args.swap(args);
+
+ directives.push_back(directive);
+
+ if (ret == ERROR_SYSTEM_CONFIG_BLOCK_START) {
+ if ((ret = directive->parse_conf(buffer, parse_block)) != ERROR_SUCCESS) {
+ return ret;
+ }
+ }
+ }
+
+ return ret;
+}
+
+// see: ngx_conf_read_token
+int SrsConfDirective::read_token(SrsFileBuffer* buffer, std::vector& args)
+{
+ int ret = ERROR_SUCCESS;
+
+ char* pstart = buffer->pos;
+ int startline = buffer->line;
+
+ bool sharp_comment = false;
+
+ bool d_quoted = false;
+ bool s_quoted = false;
+
+ bool need_space = false;
+ bool last_space = true;
+
+ while (true) {
+ if (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::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::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::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 ]\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& 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& vparams)
+{
+ if (!engine) {
+ return;
+ }
+
+ SrsConfDirective* conf = engine->get("vparams");
+ if (!conf) {
+ return;
+ }
+
+ for (int i = 0; i < (int)conf->directives.size(); i++) {
+ SrsConfDirective* p = conf->directives[i];
+ if (!p) {
+ continue;
+ }
+
+ vparams.push_back("-" + p->name);
+ vparams.push_back(p->arg0());
+ }
+}
+
+void SrsConfig::get_engine_vfilter(SrsConfDirective* engine, std::vector& vfilter)
+{
+ if (!engine) {
+ return;
+ }
+
+ SrsConfDirective* conf = engine->get("vfilter");
+ if (!conf) {
+ return;
+ }
+
+ for (int i = 0; i < (int)conf->directives.size(); i++) {
+ SrsConfDirective* p = conf->directives[i];
+ if (!p) {
+ continue;
+ }
+
+ vfilter.push_back("-" + p->name);
+ vfilter.push_back(p->arg0());
+ }
+}
+
+string SrsConfig::get_engine_acodec(SrsConfDirective* engine)
+{
+ if (!engine) {
+ return "";
+ }
+
+ SrsConfDirective* conf = engine->get("acodec");
+ if (!conf) {
+ return "";
+ }
+
+ return conf->arg0();
+}
+
+int SrsConfig::get_engine_abitrate(SrsConfDirective* engine)
+{
+ if (!engine) {
+ return 0;
+ }
+
+ SrsConfDirective* conf = engine->get("abitrate");
+ if (!conf) {
+ return 0;
+ }
+
+ return ::atoi(conf->arg0().c_str());
+}
+
+int SrsConfig::get_engine_asample_rate(SrsConfDirective* engine)
+{
+ if (!engine) {
+ return 0;
+ }
+
+ SrsConfDirective* conf = engine->get("asample_rate");
+ if (!conf) {
+ return 0;
+ }
+
+ return ::atoi(conf->arg0().c_str());
+}
+
+int SrsConfig::get_engine_achannels(SrsConfDirective* engine)
+{
+ if (!engine) {
+ return 0;
+ }
+
+ SrsConfDirective* conf = engine->get("achannels");
+ if (!conf) {
+ return 0;
+ }
+
+ return ::atoi(conf->arg0().c_str());
+}
+
+void SrsConfig::get_engine_aparams(SrsConfDirective* engine, std::vector& aparams)
+{
+ if (!engine) {
+ return;
+ }
+
+ SrsConfDirective* conf = engine->get("aparams");
+ if (!conf) {
+ return;
+ }
+
+ for (int i = 0; i < (int)conf->directives.size(); i++) {
+ SrsConfDirective* p = conf->directives[i];
+ if (!p) {
+ continue;
+ }
+
+ aparams.push_back("-" + p->name);
+ aparams.push_back(p->arg0());
+ }
+}
+
+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;
+}
diff --git a/trunk/src/core/srs_core_rtmp.cpp b/trunk/src/core/srs_core_rtmp.cpp
old mode 100755
new mode 100644
index 11ead12ef..4164436d2
--- a/trunk/src/core/srs_core_rtmp.cpp
+++ b/trunk/src/core/srs_core_rtmp.cpp
@@ -1,1187 +1,1186 @@
-/*
-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
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-using namespace std;
-
-/**
-* the signature for packets to client.
-*/
-#define RTMP_SIG_FMS_VER "3,5,3,888"
-#define RTMP_SIG_AMF0_VER 0
-#define RTMP_SIG_CLIENT_ID "ASAICiss"
-
-/**
-* onStatus consts.
-*/
-#define StatusLevel "level"
-#define StatusCode "code"
-#define StatusDescription "description"
-#define StatusDetails "details"
-#define StatusClientId "clientid"
-// status value
-#define StatusLevelStatus "status"
-// status error
-#define StatusLevelError "error"
-// code value
-#define StatusCodeConnectSuccess "NetConnection.Connect.Success"
-#define StatusCodeConnectRejected "NetConnection.Connect.Rejected"
-#define StatusCodeStreamReset "NetStream.Play.Reset"
-#define StatusCodeStreamStart "NetStream.Play.Start"
-#define StatusCodeStreamPause "NetStream.Pause.Notify"
-#define StatusCodeStreamUnpause "NetStream.Unpause.Notify"
-#define StatusCodePublishStart "NetStream.Publish.Start"
-#define StatusCodeDataStart "NetStream.Data.Start"
-#define StatusCodeUnpublishSuccess "NetStream.Unpublish.Success"
-
-// FMLE
-#define RTMP_AMF0_COMMAND_ON_FC_PUBLISH "onFCPublish"
-#define RTMP_AMF0_COMMAND_ON_FC_UNPUBLISH "onFCUnpublish"
-
-// default stream id for response the createStream request.
-#define SRS_DEFAULT_SID 1
-
-SrsRequest::SrsRequest()
-{
- objectEncoding = RTMP_SIG_AMF0_VER;
-}
-
-SrsRequest::~SrsRequest()
-{
-}
-
-SrsRequest* SrsRequest::copy()
-{
- SrsRequest* cp = new SrsRequest();
-
- cp->app = app;
- cp->objectEncoding = objectEncoding;
- cp->pageUrl = pageUrl;
- cp->port = port;
- cp->schema = schema;
- cp->stream = stream;
- cp->swfUrl = swfUrl;
- cp->tcUrl = tcUrl;
- cp->vhost = vhost;
-
- return cp;
-}
-
-int SrsRequest::discovery_app()
-{
- int ret = ERROR_SUCCESS;
-
- size_t pos = std::string::npos;
- std::string url = tcUrl;
-
- if ((pos = url.find("://")) != std::string::npos) {
- schema = url.substr(0, pos);
- url = url.substr(schema.length() + 3);
- srs_verbose("discovery schema=%s", schema.c_str());
- }
-
- if ((pos = url.find("/")) != std::string::npos) {
- vhost = url.substr(0, pos);
- url = url.substr(vhost.length() + 1);
- srs_verbose("discovery vhost=%s", vhost.c_str());
- }
-
- port = RTMP_DEFAULT_PORTS;
- if ((pos = vhost.find(":")) != std::string::npos) {
- port = vhost.substr(pos + 1);
- vhost = vhost.substr(0, pos);
- srs_verbose("discovery vhost=%s, port=%s", vhost.c_str(), port.c_str());
- }
-
- app = url;
- srs_vhost_resolve(vhost, app);
- strip();
-
- // resolve the vhost from config
- SrsConfDirective* parsed_vhost = config->get_vhost(vhost);
- if (parsed_vhost) {
- vhost = parsed_vhost->arg0();
- }
-
- // TODO: discovery the params of vhost.
-
- srs_info("discovery app success. schema=%s, vhost=%s, port=%s, app=%s",
- schema.c_str(), vhost.c_str(), port.c_str(), app.c_str());
-
- if (schema.empty() || vhost.empty() || port.empty() || app.empty()) {
- ret = ERROR_RTMP_REQ_TCURL;
- srs_error("discovery tcUrl failed. "
- "tcUrl=%s, schema=%s, vhost=%s, port=%s, app=%s, ret=%d",
- tcUrl.c_str(), schema.c_str(), vhost.c_str(), port.c_str(), app.c_str(), ret);
- return ret;
- }
-
- strip();
-
- return ret;
-}
-
-string SrsRequest::get_stream_url()
-{
- std::string url = "";
-
- url += vhost;
- url += "/";
- url += app;
- url += "/";
- url += stream;
-
- return url;
-}
-
-void SrsRequest::strip()
-{
- trim(vhost, "/ \n\r\t");
- trim(app, "/ \n\r\t");
- trim(stream, "/ \n\r\t");
-}
-
-std::string& SrsRequest::trim(string& str, string chs)
-{
- for (int i = 0; i < (int)chs.length(); i++) {
- char ch = chs.at(i);
-
- for (std::string::iterator it = str.begin(); it != str.end();) {
- if (ch == *it) {
- it = str.erase(it);
- } else {
- ++it;
- }
- }
- }
-
- return str;
-}
-
-SrsResponse::SrsResponse()
-{
- stream_id = SRS_DEFAULT_SID;
-}
-
-SrsResponse::~SrsResponse()
-{
-}
-
-SrsRtmpClient::SrsRtmpClient(st_netfd_t _stfd)
-{
- stfd = _stfd;
- protocol = new SrsProtocol(stfd);
-}
-
-SrsRtmpClient::~SrsRtmpClient()
-{
- srs_freep(protocol);
-}
-
-void SrsRtmpClient::set_recv_timeout(int64_t timeout_us)
-{
- protocol->set_recv_timeout(timeout_us);
-}
-
-void SrsRtmpClient::set_send_timeout(int64_t timeout_us)
-{
- protocol->set_send_timeout(timeout_us);
-}
-
-int64_t SrsRtmpClient::get_recv_bytes()
-{
- return protocol->get_recv_bytes();
-}
-
-int64_t SrsRtmpClient::get_send_bytes()
-{
- return protocol->get_send_bytes();
-}
-
-int SrsRtmpClient::get_recv_kbps()
-{
- return protocol->get_recv_kbps();
-}
-
-int SrsRtmpClient::get_send_kbps()
-{
- return protocol->get_send_kbps();
-}
-
-int SrsRtmpClient::recv_message(SrsCommonMessage** pmsg)
-{
- return protocol->recv_message(pmsg);
-}
-
-int SrsRtmpClient::send_message(ISrsMessage* msg)
-{
- return protocol->send_message(msg);
-}
-
-int SrsRtmpClient::handshake()
-{
- int ret = ERROR_SUCCESS;
-
- SrsSocket skt(stfd);
-
- skt.set_recv_timeout(protocol->get_recv_timeout());
- skt.set_send_timeout(protocol->get_send_timeout());
-
- SrsComplexHandshake complex_hs;
- SrsSimpleHandshake simple_hs;
- if ((ret = simple_hs.handshake_with_server(skt, complex_hs)) != ERROR_SUCCESS) {
- return ret;
- }
-
- return ret;
-}
-
-int SrsRtmpClient::connect_app(string app, string tc_url)
-{
- int ret = ERROR_SUCCESS;
-
- // Connect(vhost, app)
- if (true) {
- SrsCommonMessage* msg = new SrsCommonMessage();
- SrsConnectAppPacket* pkt = new SrsConnectAppPacket();
- msg->set_packet(pkt, 0);
-
- pkt->command_object = new SrsAmf0Object();
- pkt->command_object->set("app", new SrsAmf0String(app.c_str()));
- pkt->command_object->set("swfUrl", new SrsAmf0String());
- pkt->command_object->set("tcUrl", new SrsAmf0String(tc_url.c_str()));
- pkt->command_object->set("fpad", new SrsAmf0Boolean(false));
- pkt->command_object->set("capabilities", new SrsAmf0Number(239));
- pkt->command_object->set("audioCodecs", new SrsAmf0Number(3575));
- pkt->command_object->set("videoCodecs", new SrsAmf0Number(252));
- pkt->command_object->set("videoFunction", new SrsAmf0Number(1));
- pkt->command_object->set("pageUrl", new SrsAmf0String());
- pkt->command_object->set("objectEncoding", new SrsAmf0Number(0));
-
- if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
- return ret;
- }
- }
-
- // Set Window Acknowledgement size(2500000)
- if (true) {
- SrsCommonMessage* msg = new SrsCommonMessage();
- SrsSetWindowAckSizePacket* pkt = new SrsSetWindowAckSizePacket();
-
- pkt->ackowledgement_window_size = 2500000;
- msg->set_packet(pkt, 0);
-
- if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
- return ret;
- }
- }
-
- // expect connect _result
- SrsCommonMessage* msg = NULL;
- SrsConnectAppResPacket* pkt = NULL;
- if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
- srs_error("expect connect app response message failed. ret=%d", ret);
- return ret;
- }
- SrsAutoFree(SrsCommonMessage, msg, false);
- srs_info("get connect app response message");
-
- return ret;
-}
-
-int SrsRtmpClient::create_stream(int& stream_id)
-{
- int ret = ERROR_SUCCESS;
-
- // CreateStream
- if (true) {
- SrsCommonMessage* msg = new SrsCommonMessage();
- SrsCreateStreamPacket* pkt = new SrsCreateStreamPacket();
-
- msg->set_packet(pkt, 0);
-
- if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
- return ret;
- }
- }
-
- // CreateStream _result.
- if (true) {
- SrsCommonMessage* msg = NULL;
- SrsCreateStreamResPacket* pkt = NULL;
- if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
- srs_error("expect create stream response message failed. ret=%d", ret);
- return ret;
- }
- SrsAutoFree(SrsCommonMessage, msg, false);
- srs_info("get create stream response message");
-
- stream_id = (int)pkt->stream_id;
- }
-
- return ret;
-}
-
-int SrsRtmpClient::play(string stream, int stream_id)
-{
- int ret = ERROR_SUCCESS;
-
- // Play(stream)
- if (true) {
- SrsCommonMessage* msg = new SrsCommonMessage();
- SrsPlayPacket* pkt = new SrsPlayPacket();
-
- pkt->stream_name = stream;
- msg->set_packet(pkt, stream_id);
-
- if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
- srs_error("send play stream failed. "
- "stream=%s, stream_id=%d, ret=%d",
- stream.c_str(), stream_id, ret);
- return ret;
- }
- }
-
- // SetBufferLength(1000ms)
- int buffer_length_ms = 1000;
- if (true) {
- SrsCommonMessage* msg = new SrsCommonMessage();
- SrsUserControlPacket* pkt = new SrsUserControlPacket();
-
- pkt->event_type = SrcPCUCSetBufferLength;
- pkt->event_data = stream_id;
- pkt->extra_data = buffer_length_ms;
- msg->set_packet(pkt, 0);
-
- if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
- srs_error("send set buffer length failed. "
- "stream=%s, stream_id=%d, bufferLength=%d, ret=%d",
- stream.c_str(), stream_id, buffer_length_ms, ret);
- return ret;
- }
- }
-
- return ret;
-}
-
-int SrsRtmpClient::publish(string stream, int stream_id)
-{
- int ret = ERROR_SUCCESS;
-
- // publish(stream)
- if (true) {
- SrsCommonMessage* msg = new SrsCommonMessage();
- SrsPublishPacket* pkt = new SrsPublishPacket();
-
- pkt->stream_name = stream;
- msg->set_packet(pkt, stream_id);
-
- if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
- srs_error("send publish message failed. "
- "stream=%s, stream_id=%d, ret=%d",
- stream.c_str(), stream_id, ret);
- return ret;
- }
- }
-
- return ret;
-}
-
-SrsRtmp::SrsRtmp(st_netfd_t client_stfd)
-{
- protocol = new SrsProtocol(client_stfd);
- stfd = client_stfd;
-}
-
-SrsRtmp::~SrsRtmp()
-{
- srs_freep(protocol);
-}
-
-SrsProtocol* SrsRtmp::get_protocol()
-{
- return protocol;
-}
-
-void SrsRtmp::set_recv_timeout(int64_t timeout_us)
-{
- protocol->set_recv_timeout(timeout_us);
-}
-
-int64_t SrsRtmp::get_recv_timeout()
-{
- return protocol->get_recv_timeout();
-}
-
-void SrsRtmp::set_send_timeout(int64_t timeout_us)
-{
- protocol->set_send_timeout(timeout_us);
-}
-
-int64_t SrsRtmp::get_send_timeout()
-{
- return protocol->get_send_timeout();
-}
-
-int64_t SrsRtmp::get_recv_bytes()
-{
- return protocol->get_recv_bytes();
-}
-
-int64_t SrsRtmp::get_send_bytes()
-{
- return protocol->get_send_bytes();
-}
-
-int SrsRtmp::get_recv_kbps()
-{
- return protocol->get_recv_kbps();
-}
-
-int SrsRtmp::get_send_kbps()
-{
- return protocol->get_send_kbps();
-}
-
-int SrsRtmp::recv_message(SrsCommonMessage** pmsg)
-{
- return protocol->recv_message(pmsg);
-}
-
-int SrsRtmp::send_message(ISrsMessage* msg)
-{
- return protocol->send_message(msg);
-}
-
-int SrsRtmp::handshake()
-{
- int ret = ERROR_SUCCESS;
-
- SrsSocket skt(stfd);
-
- skt.set_recv_timeout(protocol->get_recv_timeout());
- skt.set_send_timeout(protocol->get_send_timeout());
-
- SrsComplexHandshake complex_hs;
- SrsSimpleHandshake simple_hs;
- if ((ret = simple_hs.handshake_with_client(skt, complex_hs)) != ERROR_SUCCESS) {
- return ret;
- }
-
- return ret;
-}
-
-int SrsRtmp::connect_app(SrsRequest* req)
-{
- int ret = ERROR_SUCCESS;
-
- SrsCommonMessage* msg = NULL;
- SrsConnectAppPacket* pkt = NULL;
- if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
- srs_error("expect connect app message failed. ret=%d", ret);
- return ret;
- }
- SrsAutoFree(SrsCommonMessage, msg, false);
- srs_info("get connect app message");
-
- SrsAmf0Any* prop = NULL;
-
- if ((prop = pkt->command_object->ensure_property_string("tcUrl")) == NULL) {
- ret = ERROR_RTMP_REQ_CONNECT;
- srs_error("invalid request, must specifies the tcUrl. ret=%d", ret);
- return ret;
- }
- req->tcUrl = srs_amf0_convert(prop)->value;
-
- if ((prop = pkt->command_object->ensure_property_string("pageUrl")) != NULL) {
- req->pageUrl = srs_amf0_convert(prop)->value;
- }
-
- if ((prop = pkt->command_object->ensure_property_string("swfUrl")) != NULL) {
- req->swfUrl = srs_amf0_convert(prop)->value;
- }
-
- if ((prop = pkt->command_object->ensure_property_number("objectEncoding")) != NULL) {
- req->objectEncoding = srs_amf0_convert(prop)->value;
- }
-
- srs_info("get connect app message params success.");
-
- return req->discovery_app();
-}
-
-int SrsRtmp::set_window_ack_size(int ack_size)
-{
- int ret = ERROR_SUCCESS;
-
- SrsCommonMessage* msg = new SrsCommonMessage();
- SrsSetWindowAckSizePacket* pkt = new SrsSetWindowAckSizePacket();
-
- pkt->ackowledgement_window_size = ack_size;
- msg->set_packet(pkt, 0);
-
- if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
- srs_error("send ack size message failed. ret=%d", ret);
- return ret;
- }
- srs_info("send ack size message success. ack_size=%d", ack_size);
-
- return ret;
-}
-
-int SrsRtmp::set_peer_bandwidth(int bandwidth, int type)
-{
- int ret = ERROR_SUCCESS;
-
- SrsCommonMessage* msg = new SrsCommonMessage();
- SrsSetPeerBandwidthPacket* pkt = new SrsSetPeerBandwidthPacket();
-
- pkt->bandwidth = bandwidth;
- pkt->type = type;
- msg->set_packet(pkt, 0);
-
- if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
- srs_error("send set bandwidth message failed. ret=%d", ret);
- return ret;
- }
- srs_info("send set bandwidth message "
- "success. bandwidth=%d, type=%d", bandwidth, type);
-
- return ret;
-}
-
-int SrsRtmp::response_connect_app(SrsRequest *req, const char* server_ip)
-{
- int ret = ERROR_SUCCESS;
-
- SrsCommonMessage* msg = new SrsCommonMessage();
- SrsConnectAppResPacket* pkt = new SrsConnectAppResPacket();
-
- pkt->props->set("fmsVer", new SrsAmf0String("FMS/"RTMP_SIG_FMS_VER));
- pkt->props->set("capabilities", new SrsAmf0Number(127));
- pkt->props->set("mode", new SrsAmf0Number(1));
-
- pkt->info->set(StatusLevel, new SrsAmf0String(StatusLevelStatus));
- pkt->info->set(StatusCode, new SrsAmf0String(StatusCodeConnectSuccess));
- pkt->info->set(StatusDescription, new SrsAmf0String("Connection succeeded"));
- pkt->info->set("objectEncoding", new SrsAmf0Number(req->objectEncoding));
- SrsASrsAmf0EcmaArray* data = new SrsASrsAmf0EcmaArray();
- pkt->info->set("data", data);
-
- data->set("srs_version", new SrsAmf0String(RTMP_SIG_FMS_VER));
- data->set("srs_server", new SrsAmf0String(RTMP_SIG_SRS_KEY" "RTMP_SIG_SRS_VERSION" ("RTMP_SIG_SRS_URL_SHORT")"));
- data->set("srs_license", new SrsAmf0String(RTMP_SIG_SRS_LICENSE));
- data->set("srs_role", new SrsAmf0String(RTMP_SIG_SRS_ROLE));
- data->set("srs_url", new SrsAmf0String(RTMP_SIG_SRS_URL));
- data->set("srs_version", new SrsAmf0String(RTMP_SIG_SRS_VERSION));
- data->set("srs_site", new SrsAmf0String(RTMP_SIG_SRS_WEB));
- data->set("srs_email", new SrsAmf0String(RTMP_SIG_SRS_EMAIL));
- data->set("srs_copyright", new SrsAmf0String(RTMP_SIG_SRS_COPYRIGHT));
- data->set("srs_primary_authors", new SrsAmf0String(RTMP_SIG_SRS_PRIMARY_AUTHROS));
-
- if (server_ip) {
- data->set("srs_server_ip", new SrsAmf0String(server_ip));
- }
-
- msg->set_packet(pkt, 0);
-
- if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
- srs_error("send connect app response message failed. ret=%d", ret);
- return ret;
- }
- srs_info("send connect app response message success.");
-
- return ret;
-}
-
-void SrsRtmp::response_connect_reject(SrsRequest *req, const char* desc)
-{
- int ret = ERROR_SUCCESS;
-
- SrsConnectAppResPacket* pkt = new SrsConnectAppResPacket();
- pkt->command_name = "_error";
- pkt->props->set(StatusLevel, new SrsAmf0String(StatusLevelError));
- pkt->props->set(StatusCode, new SrsAmf0String(StatusCodeConnectRejected));
- pkt->props->set(StatusDescription, new SrsAmf0String(desc));
- //pkt->props->set("objectEncoding", new SrsAmf0Number(req->objectEncoding));
-
- SrsCommonMessage* msg = (new SrsCommonMessage())->set_packet(pkt, 0);
- if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
- srs_error("send connect app response rejected message failed. ret=%d", ret);
- return;
- }
- srs_info("send connect app response rejected message success.");
-
- return;
-}
-
-int SrsRtmp::on_bw_done()
-{
- int ret = ERROR_SUCCESS;
-
- SrsCommonMessage* msg = new SrsCommonMessage();
- SrsOnBWDonePacket* pkt = new SrsOnBWDonePacket();
-
- msg->set_packet(pkt, 0);
-
- if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
- srs_error("send onBWDone message failed. ret=%d", ret);
- return ret;
- }
- srs_info("send onBWDone message success.");
-
- return ret;
-}
-
-int SrsRtmp::identify_client(int stream_id, SrsClientType& type, std::string& stream_name)
-{
- type = SrsClientUnknown;
- int ret = ERROR_SUCCESS;
-
- while (true) {
- SrsCommonMessage* msg = NULL;
- if ((ret = protocol->recv_message(&msg)) != ERROR_SUCCESS) {
- srs_error("recv identify client message failed. ret=%d", ret);
- return ret;
- }
-
- SrsAutoFree(SrsCommonMessage, msg, false);
-
- if (!msg->header.is_amf0_command() && !msg->header.is_amf3_command()) {
- srs_trace("identify ignore messages except "
- "AMF0/AMF3 command message. type=%#x", msg->header.message_type);
- continue;
- }
-
- if ((ret = msg->decode_packet(protocol)) != ERROR_SUCCESS) {
- srs_error("identify decode message failed. ret=%d", ret);
- return ret;
- }
-
- SrsPacket* pkt = msg->get_packet();
- if (dynamic_cast(pkt)) {
- srs_info("identify client by create stream, play or flash publish.");
- return identify_create_stream_client(
- dynamic_cast(pkt), stream_id, type, stream_name);
- }
- if (dynamic_cast(pkt)) {
- srs_info("identify client by releaseStream, fmle publish.");
- return identify_fmle_publish_client(
- dynamic_cast(pkt), type, stream_name);
- }
-
- srs_trace("ignore AMF0/AMF3 command message.");
- }
-
- return ret;
-}
-
-int SrsRtmp::set_chunk_size(int chunk_size)
-{
- int ret = ERROR_SUCCESS;
-
- SrsCommonMessage* msg = new SrsCommonMessage();
- SrsSetChunkSizePacket* pkt = new SrsSetChunkSizePacket();
-
- pkt->chunk_size = chunk_size;
- msg->set_packet(pkt, 0);
-
- if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
- srs_error("send set chunk size message failed. ret=%d", ret);
- return ret;
- }
- srs_info("send set chunk size message success. chunk_size=%d", chunk_size);
-
- return ret;
-}
-
-int SrsRtmp::start_play(int stream_id)
-{
- int ret = ERROR_SUCCESS;
-
- // StreamBegin
- if (true) {
- SrsCommonMessage* msg = new SrsCommonMessage();
- SrsUserControlPacket* pkt = new SrsUserControlPacket();
-
- pkt->event_type = SrcPCUCStreamBegin;
- pkt->event_data = stream_id;
- msg->set_packet(pkt, 0);
-
- if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
- srs_error("send PCUC(StreamBegin) message failed. ret=%d", ret);
- return ret;
- }
- srs_info("send PCUC(StreamBegin) message success.");
- }
-
- // onStatus(NetStream.Play.Reset)
- if (true) {
- SrsCommonMessage* msg = new SrsCommonMessage();
- SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket();
-
- pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus));
- pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamReset));
- pkt->data->set(StatusDescription, new SrsAmf0String("Playing and resetting stream."));
- pkt->data->set(StatusDetails, new SrsAmf0String("stream"));
- pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID));
-
- msg->set_packet(pkt, stream_id);
-
- if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
- srs_error("send onStatus(NetStream.Play.Reset) message failed. ret=%d", ret);
- return ret;
- }
- srs_info("send onStatus(NetStream.Play.Reset) message success.");
- }
-
- // onStatus(NetStream.Play.Start)
- if (true) {
- SrsCommonMessage* msg = new SrsCommonMessage();
- SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket();
-
- pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus));
- pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamStart));
- pkt->data->set(StatusDescription, new SrsAmf0String("Started playing stream."));
- pkt->data->set(StatusDetails, new SrsAmf0String("stream"));
- pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID));
-
- msg->set_packet(pkt, stream_id);
-
- if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
- srs_error("send onStatus(NetStream.Play.Reset) message failed. ret=%d", ret);
- return ret;
- }
- srs_info("send onStatus(NetStream.Play.Reset) message success.");
- }
-
- // |RtmpSampleAccess(false, false)
- if (true) {
- SrsCommonMessage* msg = new SrsCommonMessage();
- SrsSampleAccessPacket* pkt = new SrsSampleAccessPacket();
-
- msg->set_packet(pkt, stream_id);
-
- if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
- srs_error("send |RtmpSampleAccess(false, false) message failed. ret=%d", ret);
- return ret;
- }
- srs_info("send |RtmpSampleAccess(false, false) message success.");
- }
-
- // onStatus(NetStream.Data.Start)
- if (true) {
- SrsCommonMessage* msg = new SrsCommonMessage();
- SrsOnStatusDataPacket* pkt = new SrsOnStatusDataPacket();
-
- pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeDataStart));
-
- msg->set_packet(pkt, stream_id);
-
- if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
- srs_error("send onStatus(NetStream.Data.Start) message failed. ret=%d", ret);
- return ret;
- }
- srs_info("send onStatus(NetStream.Data.Start) message success.");
- }
-
- srs_info("start play success.");
-
- return ret;
-}
-
-int SrsRtmp::on_play_client_pause(int stream_id, bool is_pause)
-{
- int ret = ERROR_SUCCESS;
-
- if (is_pause) {
- // onStatus(NetStream.Pause.Notify)
- if (true) {
- SrsCommonMessage* msg = new SrsCommonMessage();
- SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket();
-
- pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus));
- pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamPause));
- pkt->data->set(StatusDescription, new SrsAmf0String("Paused stream."));
-
- msg->set_packet(pkt, stream_id);
-
- if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
- srs_error("send onStatus(NetStream.Pause.Notify) message failed. ret=%d", ret);
- return ret;
- }
- srs_info("send onStatus(NetStream.Pause.Notify) message success.");
- }
- // StreamEOF
- if (true) {
- SrsCommonMessage* msg = new SrsCommonMessage();
- SrsUserControlPacket* pkt = new SrsUserControlPacket();
-
- pkt->event_type = SrcPCUCStreamEOF;
- pkt->event_data = stream_id;
- msg->set_packet(pkt, 0);
-
- if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
- srs_error("send PCUC(StreamEOF) message failed. ret=%d", ret);
- return ret;
- }
- srs_info("send PCUC(StreamEOF) message success.");
- }
- } else {
- // onStatus(NetStream.Unpause.Notify)
- if (true) {
- SrsCommonMessage* msg = new SrsCommonMessage();
- SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket();
-
- pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus));
- pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamUnpause));
- pkt->data->set(StatusDescription, new SrsAmf0String("Unpaused stream."));
-
- msg->set_packet(pkt, stream_id);
-
- if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
- srs_error("send onStatus(NetStream.Unpause.Notify) message failed. ret=%d", ret);
- return ret;
- }
- srs_info("send onStatus(NetStream.Unpause.Notify) message success.");
- }
- // StreanBegin
- if (true) {
- SrsCommonMessage* msg = new SrsCommonMessage();
- SrsUserControlPacket* pkt = new SrsUserControlPacket();
-
- pkt->event_type = SrcPCUCStreamBegin;
- pkt->event_data = stream_id;
- msg->set_packet(pkt, 0);
-
- if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
- srs_error("send PCUC(StreanBegin) message failed. ret=%d", ret);
- return ret;
- }
- srs_info("send PCUC(StreanBegin) message success.");
- }
- }
-
- return ret;
-}
-
-int SrsRtmp::start_fmle_publish(int stream_id)
-{
- int ret = ERROR_SUCCESS;
-
- // FCPublish
- double fc_publish_tid = 0;
- if (true) {
- SrsCommonMessage* msg = NULL;
- SrsFMLEStartPacket* pkt = NULL;
- if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
- srs_error("recv FCPublish message failed. ret=%d", ret);
- return ret;
- }
- srs_info("recv FCPublish request message success.");
-
- SrsAutoFree(SrsCommonMessage, msg, false);
- fc_publish_tid = pkt->transaction_id;
- }
- // FCPublish response
- if (true) {
- SrsCommonMessage* msg = new SrsCommonMessage();
- SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(fc_publish_tid);
-
- msg->set_packet(pkt, 0);
-
- if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
- srs_error("send FCPublish response message failed. ret=%d", ret);
- return ret;
- }
- srs_info("send FCPublish response message success.");
- }
-
- // createStream
- double create_stream_tid = 0;
- if (true) {
- SrsCommonMessage* msg = NULL;
- SrsCreateStreamPacket* pkt = NULL;
- if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
- srs_error("recv createStream message failed. ret=%d", ret);
- return ret;
- }
- srs_info("recv createStream request message success.");
-
- SrsAutoFree(SrsCommonMessage, msg, false);
- create_stream_tid = pkt->transaction_id;
- }
- // createStream response
- if (true) {
- SrsCommonMessage* msg = new SrsCommonMessage();
- SrsCreateStreamResPacket* pkt = new SrsCreateStreamResPacket(create_stream_tid, stream_id);
-
- msg->set_packet(pkt, 0);
-
- if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
- srs_error("send createStream response message failed. ret=%d", ret);
- return ret;
- }
- srs_info("send createStream response message success.");
- }
-
- // publish
- if (true) {
- SrsCommonMessage* msg = NULL;
- SrsPublishPacket* pkt = NULL;
- if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
- srs_error("recv publish message failed. ret=%d", ret);
- return ret;
- }
- srs_info("recv publish request message success.");
-
- SrsAutoFree(SrsCommonMessage, msg, false);
- }
- // publish response onFCPublish(NetStream.Publish.Start)
- if (true) {
- SrsCommonMessage* msg = new SrsCommonMessage();
- SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket();
-
- pkt->command_name = RTMP_AMF0_COMMAND_ON_FC_PUBLISH;
- pkt->data->set(StatusCode, new SrsAmf0String(StatusCodePublishStart));
- pkt->data->set(StatusDescription, new SrsAmf0String("Started publishing stream."));
-
- msg->set_packet(pkt, stream_id);
-
- if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
- srs_error("send onFCPublish(NetStream.Publish.Start) message failed. ret=%d", ret);
- return ret;
- }
- srs_info("send onFCPublish(NetStream.Publish.Start) message success.");
- }
- // publish response onStatus(NetStream.Publish.Start)
- if (true) {
- SrsCommonMessage* msg = new SrsCommonMessage();
- SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket();
-
- pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus));
- pkt->data->set(StatusCode, new SrsAmf0String(StatusCodePublishStart));
- pkt->data->set(StatusDescription, new SrsAmf0String("Started publishing stream."));
- pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID));
-
- msg->set_packet(pkt, stream_id);
-
- if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
- srs_error("send onStatus(NetStream.Publish.Start) message failed. ret=%d", ret);
- return ret;
- }
- srs_info("send onStatus(NetStream.Publish.Start) message success.");
- }
-
- srs_info("FMLE publish success.");
-
- return ret;
-}
-
-int SrsRtmp::fmle_unpublish(int stream_id, double unpublish_tid)
-{
- int ret = ERROR_SUCCESS;
-
- // publish response onFCUnpublish(NetStream.unpublish.Success)
- if (true) {
- SrsCommonMessage* msg = new SrsCommonMessage();
- SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket();
-
- pkt->command_name = RTMP_AMF0_COMMAND_ON_FC_UNPUBLISH;
- pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeUnpublishSuccess));
- pkt->data->set(StatusDescription, new SrsAmf0String("Stop publishing stream."));
-
- msg->set_packet(pkt, stream_id);
-
- if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
- srs_error("send onFCUnpublish(NetStream.unpublish.Success) message failed. ret=%d", ret);
- return ret;
- }
- srs_info("send onFCUnpublish(NetStream.unpublish.Success) message success.");
- }
- // FCUnpublish response
- if (true) {
- SrsCommonMessage* msg = new SrsCommonMessage();
- SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(unpublish_tid);
-
- msg->set_packet(pkt, stream_id);
-
- if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
- srs_error("send FCUnpublish response message failed. ret=%d", ret);
- return ret;
- }
- srs_info("send FCUnpublish response message success.");
- }
- // publish response onStatus(NetStream.Unpublish.Success)
- if (true) {
- SrsCommonMessage* msg = new SrsCommonMessage();
- SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket();
-
- pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus));
- pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeUnpublishSuccess));
- pkt->data->set(StatusDescription, new SrsAmf0String("Stream is now unpublished"));
- pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID));
-
- msg->set_packet(pkt, stream_id);
-
- if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
- srs_error("send onStatus(NetStream.Unpublish.Success) message failed. ret=%d", ret);
- return ret;
- }
- srs_info("send onStatus(NetStream.Unpublish.Success) message success.");
- }
-
- srs_info("FMLE unpublish success.");
-
- return ret;
-}
-
-int SrsRtmp::start_flash_publish(int stream_id)
-{
- int ret = ERROR_SUCCESS;
-
- // publish response onStatus(NetStream.Publish.Start)
- if (true) {
- SrsCommonMessage* msg = new SrsCommonMessage();
- SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket();
-
- pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus));
- pkt->data->set(StatusCode, new SrsAmf0String(StatusCodePublishStart));
- pkt->data->set(StatusDescription, new SrsAmf0String("Started publishing stream."));
- pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID));
-
- msg->set_packet(pkt, stream_id);
-
- if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
- srs_error("send onStatus(NetStream.Publish.Start) message failed. ret=%d", ret);
- return ret;
- }
- srs_info("send onStatus(NetStream.Publish.Start) message success.");
- }
-
- srs_info("flash publish success.");
-
- return ret;
-}
-
-int SrsRtmp::identify_create_stream_client(SrsCreateStreamPacket* req, int stream_id, SrsClientType& type, string& stream_name)
-{
- int ret = ERROR_SUCCESS;
-
- if (true) {
- SrsCommonMessage* msg = new SrsCommonMessage();
- SrsCreateStreamResPacket* pkt = new SrsCreateStreamResPacket(req->transaction_id, stream_id);
-
- msg->set_packet(pkt, 0);
-
- if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
- srs_error("send createStream response message failed. ret=%d", ret);
- return ret;
- }
- srs_info("send createStream response message success.");
- }
-
- while (true) {
- SrsCommonMessage* msg = NULL;
- if ((ret = protocol->recv_message(&msg)) != ERROR_SUCCESS) {
- srs_error("recv identify client message failed. ret=%d", ret);
- return ret;
- }
-
- SrsAutoFree(SrsCommonMessage, msg, false);
-
- if (!msg->header.is_amf0_command() && !msg->header.is_amf3_command()) {
- srs_trace("identify ignore messages except "
- "AMF0/AMF3 command message. type=%#x", msg->header.message_type);
- continue;
- }
-
- if ((ret = msg->decode_packet(protocol)) != ERROR_SUCCESS) {
- srs_error("identify decode message failed. ret=%d", ret);
- return ret;
- }
-
- SrsPacket* pkt = msg->get_packet();
- if (dynamic_cast(pkt)) {
- SrsPlayPacket* play = dynamic_cast(pkt);
- type = SrsClientPlay;
- stream_name = play->stream_name;
- srs_trace("identity client type=play, stream_name=%s", stream_name.c_str());
- return ret;
- }
- if (dynamic_cast(pkt)) {
- srs_info("identify client by publish, falsh publish.");
- return identify_flash_publish_client(
- dynamic_cast(pkt), type, stream_name);
- }
-
- srs_trace("ignore AMF0/AMF3 command message.");
- }
-
- return ret;
-}
-
-int SrsRtmp::identify_fmle_publish_client(SrsFMLEStartPacket* req, SrsClientType& type, string& stream_name)
-{
- int ret = ERROR_SUCCESS;
-
- type = SrsClientFMLEPublish;
- stream_name = req->stream_name;
-
- // releaseStream response
- if (true) {
- SrsCommonMessage* msg = new SrsCommonMessage();
- SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(req->transaction_id);
-
- msg->set_packet(pkt, 0);
-
- if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
- srs_error("send releaseStream response message failed. ret=%d", ret);
- return ret;
- }
- srs_info("send releaseStream response message success.");
- }
-
- return ret;
-}
-
-int SrsRtmp::identify_flash_publish_client(SrsPublishPacket* req, SrsClientType& type, string& stream_name)
-{
- int ret = ERROR_SUCCESS;
-
- type = SrsClientFlashPublish;
- stream_name = req->stream_name;
-
- return ret;
-}
-
+/*
+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
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+using namespace std;
+
+/**
+* the signature for packets to client.
+*/
+#define RTMP_SIG_FMS_VER "3,5,3,888"
+#define RTMP_SIG_AMF0_VER 0
+#define RTMP_SIG_CLIENT_ID "ASAICiss"
+
+/**
+* onStatus consts.
+*/
+#define StatusLevel "level"
+#define StatusCode "code"
+#define StatusDescription "description"
+#define StatusDetails "details"
+#define StatusClientId "clientid"
+// status value
+#define StatusLevelStatus "status"
+// status error
+#define StatusLevelError "error"
+// code value
+#define StatusCodeConnectSuccess "NetConnection.Connect.Success"
+#define StatusCodeConnectRejected "NetConnection.Connect.Rejected"
+#define StatusCodeStreamReset "NetStream.Play.Reset"
+#define StatusCodeStreamStart "NetStream.Play.Start"
+#define StatusCodeStreamPause "NetStream.Pause.Notify"
+#define StatusCodeStreamUnpause "NetStream.Unpause.Notify"
+#define StatusCodePublishStart "NetStream.Publish.Start"
+#define StatusCodeDataStart "NetStream.Data.Start"
+#define StatusCodeUnpublishSuccess "NetStream.Unpublish.Success"
+
+// FMLE
+#define RTMP_AMF0_COMMAND_ON_FC_PUBLISH "onFCPublish"
+#define RTMP_AMF0_COMMAND_ON_FC_UNPUBLISH "onFCUnpublish"
+
+// default stream id for response the createStream request.
+#define SRS_DEFAULT_SID 1
+
+SrsRequest::SrsRequest()
+{
+ objectEncoding = RTMP_SIG_AMF0_VER;
+}
+
+SrsRequest::~SrsRequest()
+{
+}
+
+SrsRequest* SrsRequest::copy()
+{
+ SrsRequest* cp = new SrsRequest();
+
+ cp->app = app;
+ cp->objectEncoding = objectEncoding;
+ cp->pageUrl = pageUrl;
+ cp->port = port;
+ cp->schema = schema;
+ cp->stream = stream;
+ cp->swfUrl = swfUrl;
+ cp->tcUrl = tcUrl;
+ cp->vhost = vhost;
+
+ return cp;
+}
+
+int SrsRequest::discovery_app()
+{
+ int ret = ERROR_SUCCESS;
+
+ size_t pos = std::string::npos;
+ std::string url = tcUrl;
+
+ if ((pos = url.find("://")) != std::string::npos) {
+ schema = url.substr(0, pos);
+ url = url.substr(schema.length() + 3);
+ srs_verbose("discovery schema=%s", schema.c_str());
+ }
+
+ if ((pos = url.find("/")) != std::string::npos) {
+ vhost = url.substr(0, pos);
+ url = url.substr(vhost.length() + 1);
+ srs_verbose("discovery vhost=%s", vhost.c_str());
+ }
+
+ port = RTMP_DEFAULT_PORTS;
+ if ((pos = vhost.find(":")) != std::string::npos) {
+ port = vhost.substr(pos + 1);
+ vhost = vhost.substr(0, pos);
+ srs_verbose("discovery vhost=%s, port=%s", vhost.c_str(), port.c_str());
+ }
+
+ app = url;
+ srs_vhost_resolve(vhost, app);
+ strip();
+
+ // resolve the vhost from config
+ SrsConfDirective* parsed_vhost = config->get_vhost(vhost);
+ if (parsed_vhost) {
+ vhost = parsed_vhost->arg0();
+ }
+
+ // TODO: discovery the params of vhost.
+
+ srs_info("discovery app success. schema=%s, vhost=%s, port=%s, app=%s",
+ schema.c_str(), vhost.c_str(), port.c_str(), app.c_str());
+
+ if (schema.empty() || vhost.empty() || port.empty() || app.empty()) {
+ ret = ERROR_RTMP_REQ_TCURL;
+ srs_error("discovery tcUrl failed. "
+ "tcUrl=%s, schema=%s, vhost=%s, port=%s, app=%s, ret=%d",
+ tcUrl.c_str(), schema.c_str(), vhost.c_str(), port.c_str(), app.c_str(), ret);
+ return ret;
+ }
+
+ strip();
+
+ return ret;
+}
+
+string SrsRequest::get_stream_url()
+{
+ std::string url = "";
+
+ url += vhost;
+ url += "/";
+ url += app;
+ url += "/";
+ url += stream;
+
+ return url;
+}
+
+void SrsRequest::strip()
+{
+ trim(vhost, "/ \n\r\t");
+ trim(app, "/ \n\r\t");
+ trim(stream, "/ \n\r\t");
+}
+
+std::string& SrsRequest::trim(string& str, string chs)
+{
+ for (int i = 0; i < (int)chs.length(); i++) {
+ char ch = chs.at(i);
+
+ for (std::string::iterator it = str.begin(); it != str.end();) {
+ if (ch == *it) {
+ it = str.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+
+ return str;
+}
+
+SrsResponse::SrsResponse()
+{
+ stream_id = SRS_DEFAULT_SID;
+}
+
+SrsResponse::~SrsResponse()
+{
+}
+
+SrsRtmpClient::SrsRtmpClient(st_netfd_t _stfd)
+{
+ stfd = _stfd;
+ protocol = new SrsProtocol(stfd);
+}
+
+SrsRtmpClient::~SrsRtmpClient()
+{
+ srs_freep(protocol);
+}
+
+void SrsRtmpClient::set_recv_timeout(int64_t timeout_us)
+{
+ protocol->set_recv_timeout(timeout_us);
+}
+
+void SrsRtmpClient::set_send_timeout(int64_t timeout_us)
+{
+ protocol->set_send_timeout(timeout_us);
+}
+
+int64_t SrsRtmpClient::get_recv_bytes()
+{
+ return protocol->get_recv_bytes();
+}
+
+int64_t SrsRtmpClient::get_send_bytes()
+{
+ return protocol->get_send_bytes();
+}
+
+int SrsRtmpClient::get_recv_kbps()
+{
+ return protocol->get_recv_kbps();
+}
+
+int SrsRtmpClient::get_send_kbps()
+{
+ return protocol->get_send_kbps();
+}
+
+int SrsRtmpClient::recv_message(SrsCommonMessage** pmsg)
+{
+ return protocol->recv_message(pmsg);
+}
+
+int SrsRtmpClient::send_message(ISrsMessage* msg)
+{
+ return protocol->send_message(msg);
+}
+
+int SrsRtmpClient::handshake()
+{
+ int ret = ERROR_SUCCESS;
+
+ SrsSocket skt(stfd);
+
+ skt.set_recv_timeout(protocol->get_recv_timeout());
+ skt.set_send_timeout(protocol->get_send_timeout());
+
+ SrsComplexHandshake complex_hs;
+ SrsSimpleHandshake simple_hs;
+ if ((ret = simple_hs.handshake_with_server(skt, complex_hs)) != ERROR_SUCCESS) {
+ return ret;
+ }
+
+ return ret;
+}
+
+int SrsRtmpClient::connect_app(string app, string tc_url)
+{
+ int ret = ERROR_SUCCESS;
+
+ // Connect(vhost, app)
+ if (true) {
+ SrsCommonMessage* msg = new SrsCommonMessage();
+ SrsConnectAppPacket* pkt = new SrsConnectAppPacket();
+ msg->set_packet(pkt, 0);
+
+ pkt->command_object = new SrsAmf0Object();
+ pkt->command_object->set("app", new SrsAmf0String(app.c_str()));
+ pkt->command_object->set("swfUrl", new SrsAmf0String());
+ pkt->command_object->set("tcUrl", new SrsAmf0String(tc_url.c_str()));
+ pkt->command_object->set("fpad", new SrsAmf0Boolean(false));
+ pkt->command_object->set("capabilities", new SrsAmf0Number(239));
+ pkt->command_object->set("audioCodecs", new SrsAmf0Number(3575));
+ pkt->command_object->set("videoCodecs", new SrsAmf0Number(252));
+ pkt->command_object->set("videoFunction", new SrsAmf0Number(1));
+ pkt->command_object->set("pageUrl", new SrsAmf0String());
+ pkt->command_object->set("objectEncoding", new SrsAmf0Number(0));
+
+ if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
+ return ret;
+ }
+ }
+
+ // Set Window Acknowledgement size(2500000)
+ if (true) {
+ SrsCommonMessage* msg = new SrsCommonMessage();
+ SrsSetWindowAckSizePacket* pkt = new SrsSetWindowAckSizePacket();
+
+ pkt->ackowledgement_window_size = 2500000;
+ msg->set_packet(pkt, 0);
+
+ if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
+ return ret;
+ }
+ }
+
+ // expect connect _result
+ SrsCommonMessage* msg = NULL;
+ SrsConnectAppResPacket* pkt = NULL;
+ if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
+ srs_error("expect connect app response message failed. ret=%d", ret);
+ return ret;
+ }
+ SrsAutoFree(SrsCommonMessage, msg, false);
+ srs_info("get connect app response message");
+
+ return ret;
+}
+
+int SrsRtmpClient::create_stream(int& stream_id)
+{
+ int ret = ERROR_SUCCESS;
+
+ // CreateStream
+ if (true) {
+ SrsCommonMessage* msg = new SrsCommonMessage();
+ SrsCreateStreamPacket* pkt = new SrsCreateStreamPacket();
+
+ msg->set_packet(pkt, 0);
+
+ if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
+ return ret;
+ }
+ }
+
+ // CreateStream _result.
+ if (true) {
+ SrsCommonMessage* msg = NULL;
+ SrsCreateStreamResPacket* pkt = NULL;
+ if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
+ srs_error("expect create stream response message failed. ret=%d", ret);
+ return ret;
+ }
+ SrsAutoFree(SrsCommonMessage, msg, false);
+ srs_info("get create stream response message");
+
+ stream_id = (int)pkt->stream_id;
+ }
+
+ return ret;
+}
+
+int SrsRtmpClient::play(string stream, int stream_id)
+{
+ int ret = ERROR_SUCCESS;
+
+ // Play(stream)
+ if (true) {
+ SrsCommonMessage* msg = new SrsCommonMessage();
+ SrsPlayPacket* pkt = new SrsPlayPacket();
+
+ pkt->stream_name = stream;
+ msg->set_packet(pkt, stream_id);
+
+ if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
+ srs_error("send play stream failed. "
+ "stream=%s, stream_id=%d, ret=%d",
+ stream.c_str(), stream_id, ret);
+ return ret;
+ }
+ }
+
+ // SetBufferLength(1000ms)
+ int buffer_length_ms = 1000;
+ if (true) {
+ SrsCommonMessage* msg = new SrsCommonMessage();
+ SrsUserControlPacket* pkt = new SrsUserControlPacket();
+
+ pkt->event_type = SrcPCUCSetBufferLength;
+ pkt->event_data = stream_id;
+ pkt->extra_data = buffer_length_ms;
+ msg->set_packet(pkt, 0);
+
+ if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
+ srs_error("send set buffer length failed. "
+ "stream=%s, stream_id=%d, bufferLength=%d, ret=%d",
+ stream.c_str(), stream_id, buffer_length_ms, ret);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+int SrsRtmpClient::publish(string stream, int stream_id)
+{
+ int ret = ERROR_SUCCESS;
+
+ // publish(stream)
+ if (true) {
+ SrsCommonMessage* msg = new SrsCommonMessage();
+ SrsPublishPacket* pkt = new SrsPublishPacket();
+
+ pkt->stream_name = stream;
+ msg->set_packet(pkt, stream_id);
+
+ if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
+ srs_error("send publish message failed. "
+ "stream=%s, stream_id=%d, ret=%d",
+ stream.c_str(), stream_id, ret);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+SrsRtmp::SrsRtmp(st_netfd_t client_stfd)
+{
+ protocol = new SrsProtocol(client_stfd);
+ stfd = client_stfd;
+}
+
+SrsRtmp::~SrsRtmp()
+{
+ srs_freep(protocol);
+}
+
+SrsProtocol* SrsRtmp::get_protocol()
+{
+ return protocol;
+}
+
+void SrsRtmp::set_recv_timeout(int64_t timeout_us)
+{
+ protocol->set_recv_timeout(timeout_us);
+}
+
+int64_t SrsRtmp::get_recv_timeout()
+{
+ return protocol->get_recv_timeout();
+}
+
+void SrsRtmp::set_send_timeout(int64_t timeout_us)
+{
+ protocol->set_send_timeout(timeout_us);
+}
+
+int64_t SrsRtmp::get_send_timeout()
+{
+ return protocol->get_send_timeout();
+}
+
+int64_t SrsRtmp::get_recv_bytes()
+{
+ return protocol->get_recv_bytes();
+}
+
+int64_t SrsRtmp::get_send_bytes()
+{
+ return protocol->get_send_bytes();
+}
+
+int SrsRtmp::get_recv_kbps()
+{
+ return protocol->get_recv_kbps();
+}
+
+int SrsRtmp::get_send_kbps()
+{
+ return protocol->get_send_kbps();
+}
+
+int SrsRtmp::recv_message(SrsCommonMessage** pmsg)
+{
+ return protocol->recv_message(pmsg);
+}
+
+int SrsRtmp::send_message(ISrsMessage* msg)
+{
+ return protocol->send_message(msg);
+}
+
+int SrsRtmp::handshake()
+{
+ int ret = ERROR_SUCCESS;
+
+ SrsSocket skt(stfd);
+
+ skt.set_recv_timeout(protocol->get_recv_timeout());
+ skt.set_send_timeout(protocol->get_send_timeout());
+
+ SrsComplexHandshake complex_hs;
+ SrsSimpleHandshake simple_hs;
+ if ((ret = simple_hs.handshake_with_client(skt, complex_hs)) != ERROR_SUCCESS) {
+ return ret;
+ }
+
+ return ret;
+}
+
+int SrsRtmp::connect_app(SrsRequest* req)
+{
+ int ret = ERROR_SUCCESS;
+
+ SrsCommonMessage* msg = NULL;
+ SrsConnectAppPacket* pkt = NULL;
+ if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
+ srs_error("expect connect app message failed. ret=%d", ret);
+ return ret;
+ }
+ SrsAutoFree(SrsCommonMessage, msg, false);
+ srs_info("get connect app message");
+
+ SrsAmf0Any* prop = NULL;
+
+ if ((prop = pkt->command_object->ensure_property_string("tcUrl")) == NULL) {
+ ret = ERROR_RTMP_REQ_CONNECT;
+ srs_error("invalid request, must specifies the tcUrl. ret=%d", ret);
+ return ret;
+ }
+ req->tcUrl = srs_amf0_convert(prop)->value;
+
+ if ((prop = pkt->command_object->ensure_property_string("pageUrl")) != NULL) {
+ req->pageUrl = srs_amf0_convert(prop)->value;
+ }
+
+ if ((prop = pkt->command_object->ensure_property_string("swfUrl")) != NULL) {
+ req->swfUrl = srs_amf0_convert(prop)->value;
+ }
+
+ if ((prop = pkt->command_object->ensure_property_number("objectEncoding")) != NULL) {
+ req->objectEncoding = srs_amf0_convert(prop)->value;
+ }
+
+ srs_info("get connect app message params success.");
+
+ return req->discovery_app();
+}
+
+int SrsRtmp::set_window_ack_size(int ack_size)
+{
+ int ret = ERROR_SUCCESS;
+
+ SrsCommonMessage* msg = new SrsCommonMessage();
+ SrsSetWindowAckSizePacket* pkt = new SrsSetWindowAckSizePacket();
+
+ pkt->ackowledgement_window_size = ack_size;
+ msg->set_packet(pkt, 0);
+
+ if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
+ srs_error("send ack size message failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("send ack size message success. ack_size=%d", ack_size);
+
+ return ret;
+}
+
+int SrsRtmp::set_peer_bandwidth(int bandwidth, int type)
+{
+ int ret = ERROR_SUCCESS;
+
+ SrsCommonMessage* msg = new SrsCommonMessage();
+ SrsSetPeerBandwidthPacket* pkt = new SrsSetPeerBandwidthPacket();
+
+ pkt->bandwidth = bandwidth;
+ pkt->type = type;
+ msg->set_packet(pkt, 0);
+
+ if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
+ srs_error("send set bandwidth message failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("send set bandwidth message "
+ "success. bandwidth=%d, type=%d", bandwidth, type);
+
+ return ret;
+}
+
+int SrsRtmp::response_connect_app(SrsRequest *req, const char* server_ip)
+{
+ int ret = ERROR_SUCCESS;
+
+ SrsCommonMessage* msg = new SrsCommonMessage();
+ SrsConnectAppResPacket* pkt = new SrsConnectAppResPacket();
+
+ pkt->props->set("fmsVer", new SrsAmf0String("FMS/"RTMP_SIG_FMS_VER));
+ pkt->props->set("capabilities", new SrsAmf0Number(127));
+ pkt->props->set("mode", new SrsAmf0Number(1));
+
+ pkt->info->set(StatusLevel, new SrsAmf0String(StatusLevelStatus));
+ pkt->info->set(StatusCode, new SrsAmf0String(StatusCodeConnectSuccess));
+ pkt->info->set(StatusDescription, new SrsAmf0String("Connection succeeded"));
+ pkt->info->set("objectEncoding", new SrsAmf0Number(req->objectEncoding));
+ SrsASrsAmf0EcmaArray* data = new SrsASrsAmf0EcmaArray();
+ pkt->info->set("data", data);
+
+ data->set("srs_version", new SrsAmf0String(RTMP_SIG_FMS_VER));
+ data->set("srs_server", new SrsAmf0String(RTMP_SIG_SRS_KEY" "RTMP_SIG_SRS_VERSION" ("RTMP_SIG_SRS_URL_SHORT")"));
+ data->set("srs_license", new SrsAmf0String(RTMP_SIG_SRS_LICENSE));
+ data->set("srs_role", new SrsAmf0String(RTMP_SIG_SRS_ROLE));
+ data->set("srs_url", new SrsAmf0String(RTMP_SIG_SRS_URL));
+ data->set("srs_version", new SrsAmf0String(RTMP_SIG_SRS_VERSION));
+ data->set("srs_site", new SrsAmf0String(RTMP_SIG_SRS_WEB));
+ data->set("srs_email", new SrsAmf0String(RTMP_SIG_SRS_EMAIL));
+ data->set("srs_copyright", new SrsAmf0String(RTMP_SIG_SRS_COPYRIGHT));
+ data->set("srs_primary_authors", new SrsAmf0String(RTMP_SIG_SRS_PRIMARY_AUTHROS));
+
+ if (server_ip) {
+ data->set("srs_server_ip", new SrsAmf0String(server_ip));
+ }
+
+ msg->set_packet(pkt, 0);
+
+ if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
+ srs_error("send connect app response message failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("send connect app response message success.");
+
+ return ret;
+}
+
+void SrsRtmp::response_connect_reject(SrsRequest *req, const char* desc)
+{
+ int ret = ERROR_SUCCESS;
+
+ SrsConnectAppResPacket* pkt = new SrsConnectAppResPacket();
+ pkt->command_name = "_error";
+ pkt->props->set(StatusLevel, new SrsAmf0String(StatusLevelError));
+ pkt->props->set(StatusCode, new SrsAmf0String(StatusCodeConnectRejected));
+ pkt->props->set(StatusDescription, new SrsAmf0String(desc));
+ //pkt->props->set("objectEncoding", new SrsAmf0Number(req->objectEncoding));
+
+ SrsCommonMessage* msg = (new SrsCommonMessage())->set_packet(pkt, 0);
+ if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
+ srs_error("send connect app response rejected message failed. ret=%d", ret);
+ return;
+ }
+ srs_info("send connect app response rejected message success.");
+
+ return;
+}
+
+int SrsRtmp::on_bw_done()
+{
+ int ret = ERROR_SUCCESS;
+
+ SrsCommonMessage* msg = new SrsCommonMessage();
+ SrsOnBWDonePacket* pkt = new SrsOnBWDonePacket();
+
+ msg->set_packet(pkt, 0);
+
+ if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
+ srs_error("send onBWDone message failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("send onBWDone message success.");
+
+ return ret;
+}
+
+int SrsRtmp::identify_client(int stream_id, SrsClientType& type, std::string& stream_name)
+{
+ type = SrsClientUnknown;
+ int ret = ERROR_SUCCESS;
+
+ while (true) {
+ SrsCommonMessage* msg = NULL;
+ if ((ret = protocol->recv_message(&msg)) != ERROR_SUCCESS) {
+ srs_error("recv identify client message failed. ret=%d", ret);
+ return ret;
+ }
+
+ SrsAutoFree(SrsCommonMessage, msg, false);
+
+ if (!msg->header.is_amf0_command() && !msg->header.is_amf3_command()) {
+ srs_trace("identify ignore messages except "
+ "AMF0/AMF3 command message. type=%#x", msg->header.message_type);
+ continue;
+ }
+
+ if ((ret = msg->decode_packet(protocol)) != ERROR_SUCCESS) {
+ srs_error("identify decode message failed. ret=%d", ret);
+ return ret;
+ }
+
+ SrsPacket* pkt = msg->get_packet();
+ if (dynamic_cast(pkt)) {
+ srs_info("identify client by create stream, play or flash publish.");
+ return identify_create_stream_client(
+ dynamic_cast(pkt), stream_id, type, stream_name);
+ }
+ if (dynamic_cast(pkt)) {
+ srs_info("identify client by releaseStream, fmle publish.");
+ return identify_fmle_publish_client(
+ dynamic_cast(pkt), type, stream_name);
+ }
+
+ srs_trace("ignore AMF0/AMF3 command message.");
+ }
+
+ return ret;
+}
+
+int SrsRtmp::set_chunk_size(int chunk_size)
+{
+ int ret = ERROR_SUCCESS;
+
+ SrsCommonMessage* msg = new SrsCommonMessage();
+ SrsSetChunkSizePacket* pkt = new SrsSetChunkSizePacket();
+
+ pkt->chunk_size = chunk_size;
+ msg->set_packet(pkt, 0);
+
+ if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
+ srs_error("send set chunk size message failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("send set chunk size message success. chunk_size=%d", chunk_size);
+
+ return ret;
+}
+
+int SrsRtmp::start_play(int stream_id)
+{
+ int ret = ERROR_SUCCESS;
+
+ // StreamBegin
+ if (true) {
+ SrsCommonMessage* msg = new SrsCommonMessage();
+ SrsUserControlPacket* pkt = new SrsUserControlPacket();
+
+ pkt->event_type = SrcPCUCStreamBegin;
+ pkt->event_data = stream_id;
+ msg->set_packet(pkt, 0);
+
+ if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
+ srs_error("send PCUC(StreamBegin) message failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("send PCUC(StreamBegin) message success.");
+ }
+
+ // onStatus(NetStream.Play.Reset)
+ if (true) {
+ SrsCommonMessage* msg = new SrsCommonMessage();
+ SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket();
+
+ pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus));
+ pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamReset));
+ pkt->data->set(StatusDescription, new SrsAmf0String("Playing and resetting stream."));
+ pkt->data->set(StatusDetails, new SrsAmf0String("stream"));
+ pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID));
+
+ msg->set_packet(pkt, stream_id);
+
+ if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
+ srs_error("send onStatus(NetStream.Play.Reset) message failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("send onStatus(NetStream.Play.Reset) message success.");
+ }
+
+ // onStatus(NetStream.Play.Start)
+ if (true) {
+ SrsCommonMessage* msg = new SrsCommonMessage();
+ SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket();
+
+ pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus));
+ pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamStart));
+ pkt->data->set(StatusDescription, new SrsAmf0String("Started playing stream."));
+ pkt->data->set(StatusDetails, new SrsAmf0String("stream"));
+ pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID));
+
+ msg->set_packet(pkt, stream_id);
+
+ if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
+ srs_error("send onStatus(NetStream.Play.Reset) message failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("send onStatus(NetStream.Play.Reset) message success.");
+ }
+
+ // |RtmpSampleAccess(false, false)
+ if (true) {
+ SrsCommonMessage* msg = new SrsCommonMessage();
+ SrsSampleAccessPacket* pkt = new SrsSampleAccessPacket();
+
+ msg->set_packet(pkt, stream_id);
+
+ if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
+ srs_error("send |RtmpSampleAccess(false, false) message failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("send |RtmpSampleAccess(false, false) message success.");
+ }
+
+ // onStatus(NetStream.Data.Start)
+ if (true) {
+ SrsCommonMessage* msg = new SrsCommonMessage();
+ SrsOnStatusDataPacket* pkt = new SrsOnStatusDataPacket();
+
+ pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeDataStart));
+
+ msg->set_packet(pkt, stream_id);
+
+ if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
+ srs_error("send onStatus(NetStream.Data.Start) message failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("send onStatus(NetStream.Data.Start) message success.");
+ }
+
+ srs_info("start play success.");
+
+ return ret;
+}
+
+int SrsRtmp::on_play_client_pause(int stream_id, bool is_pause)
+{
+ int ret = ERROR_SUCCESS;
+
+ if (is_pause) {
+ // onStatus(NetStream.Pause.Notify)
+ if (true) {
+ SrsCommonMessage* msg = new SrsCommonMessage();
+ SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket();
+
+ pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus));
+ pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamPause));
+ pkt->data->set(StatusDescription, new SrsAmf0String("Paused stream."));
+
+ msg->set_packet(pkt, stream_id);
+
+ if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
+ srs_error("send onStatus(NetStream.Pause.Notify) message failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("send onStatus(NetStream.Pause.Notify) message success.");
+ }
+ // StreamEOF
+ if (true) {
+ SrsCommonMessage* msg = new SrsCommonMessage();
+ SrsUserControlPacket* pkt = new SrsUserControlPacket();
+
+ pkt->event_type = SrcPCUCStreamEOF;
+ pkt->event_data = stream_id;
+ msg->set_packet(pkt, 0);
+
+ if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
+ srs_error("send PCUC(StreamEOF) message failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("send PCUC(StreamEOF) message success.");
+ }
+ } else {
+ // onStatus(NetStream.Unpause.Notify)
+ if (true) {
+ SrsCommonMessage* msg = new SrsCommonMessage();
+ SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket();
+
+ pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus));
+ pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamUnpause));
+ pkt->data->set(StatusDescription, new SrsAmf0String("Unpaused stream."));
+
+ msg->set_packet(pkt, stream_id);
+
+ if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
+ srs_error("send onStatus(NetStream.Unpause.Notify) message failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("send onStatus(NetStream.Unpause.Notify) message success.");
+ }
+ // StreanBegin
+ if (true) {
+ SrsCommonMessage* msg = new SrsCommonMessage();
+ SrsUserControlPacket* pkt = new SrsUserControlPacket();
+
+ pkt->event_type = SrcPCUCStreamBegin;
+ pkt->event_data = stream_id;
+ msg->set_packet(pkt, 0);
+
+ if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
+ srs_error("send PCUC(StreanBegin) message failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("send PCUC(StreanBegin) message success.");
+ }
+ }
+
+ return ret;
+}
+
+int SrsRtmp::start_fmle_publish(int stream_id)
+{
+ int ret = ERROR_SUCCESS;
+
+ // FCPublish
+ double fc_publish_tid = 0;
+ if (true) {
+ SrsCommonMessage* msg = NULL;
+ SrsFMLEStartPacket* pkt = NULL;
+ if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
+ srs_error("recv FCPublish message failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("recv FCPublish request message success.");
+
+ SrsAutoFree(SrsCommonMessage, msg, false);
+ fc_publish_tid = pkt->transaction_id;
+ }
+ // FCPublish response
+ if (true) {
+ SrsCommonMessage* msg = new SrsCommonMessage();
+ SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(fc_publish_tid);
+
+ msg->set_packet(pkt, 0);
+
+ if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
+ srs_error("send FCPublish response message failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("send FCPublish response message success.");
+ }
+
+ // createStream
+ double create_stream_tid = 0;
+ if (true) {
+ SrsCommonMessage* msg = NULL;
+ SrsCreateStreamPacket* pkt = NULL;
+ if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
+ srs_error("recv createStream message failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("recv createStream request message success.");
+
+ SrsAutoFree(SrsCommonMessage, msg, false);
+ create_stream_tid = pkt->transaction_id;
+ }
+ // createStream response
+ if (true) {
+ SrsCommonMessage* msg = new SrsCommonMessage();
+ SrsCreateStreamResPacket* pkt = new SrsCreateStreamResPacket(create_stream_tid, stream_id);
+
+ msg->set_packet(pkt, 0);
+
+ if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
+ srs_error("send createStream response message failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("send createStream response message success.");
+ }
+
+ // publish
+ if (true) {
+ SrsCommonMessage* msg = NULL;
+ SrsPublishPacket* pkt = NULL;
+ if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
+ srs_error("recv publish message failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("recv publish request message success.");
+
+ SrsAutoFree(SrsCommonMessage, msg, false);
+ }
+ // publish response onFCPublish(NetStream.Publish.Start)
+ if (true) {
+ SrsCommonMessage* msg = new SrsCommonMessage();
+ SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket();
+
+ pkt->command_name = RTMP_AMF0_COMMAND_ON_FC_PUBLISH;
+ pkt->data->set(StatusCode, new SrsAmf0String(StatusCodePublishStart));
+ pkt->data->set(StatusDescription, new SrsAmf0String("Started publishing stream."));
+
+ msg->set_packet(pkt, stream_id);
+
+ if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
+ srs_error("send onFCPublish(NetStream.Publish.Start) message failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("send onFCPublish(NetStream.Publish.Start) message success.");
+ }
+ // publish response onStatus(NetStream.Publish.Start)
+ if (true) {
+ SrsCommonMessage* msg = new SrsCommonMessage();
+ SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket();
+
+ pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus));
+ pkt->data->set(StatusCode, new SrsAmf0String(StatusCodePublishStart));
+ pkt->data->set(StatusDescription, new SrsAmf0String("Started publishing stream."));
+ pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID));
+
+ msg->set_packet(pkt, stream_id);
+
+ if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
+ srs_error("send onStatus(NetStream.Publish.Start) message failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("send onStatus(NetStream.Publish.Start) message success.");
+ }
+
+ srs_info("FMLE publish success.");
+
+ return ret;
+}
+
+int SrsRtmp::fmle_unpublish(int stream_id, double unpublish_tid)
+{
+ int ret = ERROR_SUCCESS;
+
+ // publish response onFCUnpublish(NetStream.unpublish.Success)
+ if (true) {
+ SrsCommonMessage* msg = new SrsCommonMessage();
+ SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket();
+
+ pkt->command_name = RTMP_AMF0_COMMAND_ON_FC_UNPUBLISH;
+ pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeUnpublishSuccess));
+ pkt->data->set(StatusDescription, new SrsAmf0String("Stop publishing stream."));
+
+ msg->set_packet(pkt, stream_id);
+
+ if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
+ srs_error("send onFCUnpublish(NetStream.unpublish.Success) message failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("send onFCUnpublish(NetStream.unpublish.Success) message success.");
+ }
+ // FCUnpublish response
+ if (true) {
+ SrsCommonMessage* msg = new SrsCommonMessage();
+ SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(unpublish_tid);
+
+ msg->set_packet(pkt, stream_id);
+
+ if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
+ srs_error("send FCUnpublish response message failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("send FCUnpublish response message success.");
+ }
+ // publish response onStatus(NetStream.Unpublish.Success)
+ if (true) {
+ SrsCommonMessage* msg = new SrsCommonMessage();
+ SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket();
+
+ pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus));
+ pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeUnpublishSuccess));
+ pkt->data->set(StatusDescription, new SrsAmf0String("Stream is now unpublished"));
+ pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID));
+
+ msg->set_packet(pkt, stream_id);
+
+ if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
+ srs_error("send onStatus(NetStream.Unpublish.Success) message failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("send onStatus(NetStream.Unpublish.Success) message success.");
+ }
+
+ srs_info("FMLE unpublish success.");
+
+ return ret;
+}
+
+int SrsRtmp::start_flash_publish(int stream_id)
+{
+ int ret = ERROR_SUCCESS;
+
+ // publish response onStatus(NetStream.Publish.Start)
+ if (true) {
+ SrsCommonMessage* msg = new SrsCommonMessage();
+ SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket();
+
+ pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus));
+ pkt->data->set(StatusCode, new SrsAmf0String(StatusCodePublishStart));
+ pkt->data->set(StatusDescription, new SrsAmf0String("Started publishing stream."));
+ pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID));
+
+ msg->set_packet(pkt, stream_id);
+
+ if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
+ srs_error("send onStatus(NetStream.Publish.Start) message failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("send onStatus(NetStream.Publish.Start) message success.");
+ }
+
+ srs_info("flash publish success.");
+
+ return ret;
+}
+
+int SrsRtmp::identify_create_stream_client(SrsCreateStreamPacket* req, int stream_id, SrsClientType& type, string& stream_name)
+{
+ int ret = ERROR_SUCCESS;
+
+ if (true) {
+ SrsCommonMessage* msg = new SrsCommonMessage();
+ SrsCreateStreamResPacket* pkt = new SrsCreateStreamResPacket(req->transaction_id, stream_id);
+
+ msg->set_packet(pkt, 0);
+
+ if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
+ srs_error("send createStream response message failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("send createStream response message success.");
+ }
+
+ while (true) {
+ SrsCommonMessage* msg = NULL;
+ if ((ret = protocol->recv_message(&msg)) != ERROR_SUCCESS) {
+ srs_error("recv identify client message failed. ret=%d", ret);
+ return ret;
+ }
+
+ SrsAutoFree(SrsCommonMessage, msg, false);
+
+ if (!msg->header.is_amf0_command() && !msg->header.is_amf3_command()) {
+ srs_trace("identify ignore messages except "
+ "AMF0/AMF3 command message. type=%#x", msg->header.message_type);
+ continue;
+ }
+
+ if ((ret = msg->decode_packet(protocol)) != ERROR_SUCCESS) {
+ srs_error("identify decode message failed. ret=%d", ret);
+ return ret;
+ }
+
+ SrsPacket* pkt = msg->get_packet();
+ if (dynamic_cast(pkt)) {
+ SrsPlayPacket* play = dynamic_cast(pkt);
+ type = SrsClientPlay;
+ stream_name = play->stream_name;
+ srs_trace("identity client type=play, stream_name=%s", stream_name.c_str());
+ return ret;
+ }
+ if (dynamic_cast(pkt)) {
+ srs_info("identify client by publish, falsh publish.");
+ return identify_flash_publish_client(
+ dynamic_cast(pkt), type, stream_name);
+ }
+
+ srs_trace("ignore AMF0/AMF3 command message.");
+ }
+
+ return ret;
+}
+
+int SrsRtmp::identify_fmle_publish_client(SrsFMLEStartPacket* req, SrsClientType& type, string& stream_name)
+{
+ int ret = ERROR_SUCCESS;
+
+ type = SrsClientFMLEPublish;
+ stream_name = req->stream_name;
+
+ // releaseStream response
+ if (true) {
+ SrsCommonMessage* msg = new SrsCommonMessage();
+ SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(req->transaction_id);
+
+ msg->set_packet(pkt, 0);
+
+ if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
+ srs_error("send releaseStream response message failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("send releaseStream response message success.");
+ }
+
+ return ret;
+}
+
+int SrsRtmp::identify_flash_publish_client(SrsPublishPacket* req, SrsClientType& type, string& stream_name)
+{
+ int ret = ERROR_SUCCESS;
+
+ type = SrsClientFlashPublish;
+ stream_name = req->stream_name;
+
+ return ret;
+}