diff --git a/README.md b/README.md
index 339fcb10a..79bae01d4 100755
--- a/README.md
+++ b/README.md
@@ -190,6 +190,7 @@ usr sys idl wai hiq siq| read writ| recv send| in out | int csw
* nginx v1.5.0: 139524 lines
### History
+* v0.8, 2013-12-08, support multiple http hooks for a event.
* v0.8, 2013-12-07, support http callback hooks, on_connect.
* v0.8, 2013-12-07, support network based cli and json result, add CherryPy 3.2.4.
* v0.8, 2013-12-07, update http/hls/rtmp load test tool [st_load](https://github.com/winlinvip/st-load), use srs rtmp sdk.
diff --git a/trunk/conf/srs.conf b/trunk/conf/srs.conf
index f5052f76a..6469d1e6a 100755
--- a/trunk/conf/srs.conf
+++ b/trunk/conf/srs.conf
@@ -87,12 +87,14 @@ vhost dev {
hls_window 30;
#forward 127.0.0.1:19350;
#forward 127.0.0.1:1936;
- on_connect http://127.0.0.1:8085/api/v1/clients;
- on_close http://127.0.0.1:8085/api/v1/clients;
- on_publish http://127.0.0.1:8085/api/v1/streams;
- on_unpublish http://127.0.0.1:8085/api/v1/streams;
- on_play http://127.0.0.1:8085/api/v1/sessions;
- on_stop http://127.0.0.1:8085/api/v1/sessions;
+ http_hooks {
+ on_connect http://127.0.0.1:8085/api/v1/clients http://localhost:8085/api/v1/clients;
+ on_close http://127.0.0.1:8085/api/v1/clients;
+ on_publish http://127.0.0.1:8085/api/v1/streams;
+ on_unpublish http://127.0.0.1:8085/api/v1/streams;
+ on_play http://127.0.0.1:8085/api/v1/sessions;
+ on_stop http://127.0.0.1:8085/api/v1/sessions;
+ }
transcode {
enabled off;
ffmpeg ./objs/ffmpeg/bin/ffmpeg;
@@ -133,16 +135,20 @@ vhost dev {
}
# the http hook callback vhost, srs will invoke the hooks for specified events.
vhost hooks.callback.vhost.com {
- # when client connect to vhost/app, call the hook,
- # the request in the POST data string is a object encode by json:
- # {
- # "ip": "192.168.1.10", "vhost": "video.test.com", "app": "live",
- # "pageUrl": "http://www.test.com/live.html"
- # }
- # if valid, the hook must return HTTP code 200(Stauts OK) and response
- # an int value specifies the error code(0 corresponding to success):
- # 0
- on_connect http://127.0.0.1:8085/api/v1/clients;
+ http_hooks {
+ # when client connect to vhost/app, call the hook,
+ # the request in the POST data string is a object encode by json:
+ # {
+ # "ip": "192.168.1.10", "vhost": "video.test.com", "app": "live",
+ # "pageUrl": "http://www.test.com/live.html"
+ # }
+ # if valid, the hook must return HTTP code 200(Stauts OK) and response
+ # an int value specifies the error code(0 corresponding to success):
+ # 0
+ # support multiple api hooks, format:
+ # on_connect http://xxx/api0 http://xxx/api1 http://xxx/apiN
+ on_connect http://127.0.0.1:8085/api/v1/clients http://localhost:8085/api/v1/clients;
+ }
}
# the mirror filter of ffmpeg, @see: http://ffmpeg.org/ffmpeg-filters.html#Filtering-Introduction
vhost mirror.transcode.vhost.com {
diff --git a/trunk/src/core/srs_core_client.cpp b/trunk/src/core/srs_core_client.cpp
old mode 100644
new mode 100755
index 0faf861e6..c8a609fb3
--- a/trunk/src/core/srs_core_client.cpp
+++ b/trunk/src/core/srs_core_client.cpp
@@ -1,544 +1,547 @@
-/*
-The MIT License (MIT)
-
-Copyright (c) 2013 winlin
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-#include
-
-#include
-#include
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#define SRS_PULSE_TIMEOUT_MS 100
-#define SRS_SEND_TIMEOUT_US 5000000L
-#define SRS_RECV_TIMEOUT_US SRS_SEND_TIMEOUT_US
-#define SRS_STREAM_BUSY_SLEEP_MS 2000
-
-SrsClient::SrsClient(SrsServer* srs_server, st_netfd_t client_stfd)
- : SrsConnection(srs_server, client_stfd)
-{
- ip = NULL;
- req = new SrsRequest();
- res = new SrsResponse();
- rtmp = new SrsRtmp(client_stfd);
- refer = new SrsRefer();
-#ifdef SRS_HTTP
- http_hooks = new SrsHttpHooks();
-#endif
-}
-
-SrsClient::~SrsClient()
-{
- srs_freepa(ip);
- srs_freep(req);
- srs_freep(res);
- srs_freep(rtmp);
- srs_freep(refer);
-#ifdef SRS_HTTP
- srs_freep(http_hooks);
-#endif
-}
-
-// TODO: return detail message when error for client.
-int SrsClient::do_cycle()
-{
- int ret = ERROR_SUCCESS;
-
- if ((ret = get_peer_ip()) != ERROR_SUCCESS) {
- srs_error("get peer ip failed. ret=%d", ret);
- return ret;
- }
- srs_trace("get peer ip success. ip=%s, send_to=%"PRId64", recv_to=%"PRId64"",
- ip, SRS_SEND_TIMEOUT_US, SRS_RECV_TIMEOUT_US);
-
- rtmp->set_recv_timeout(SRS_RECV_TIMEOUT_US);
- rtmp->set_send_timeout(SRS_SEND_TIMEOUT_US);
-
- if ((ret = rtmp->handshake()) != ERROR_SUCCESS) {
- srs_error("rtmp handshake failed. ret=%d", ret);
- return ret;
- }
- srs_verbose("rtmp handshake success");
-
- if ((ret = rtmp->connect_app(req)) != ERROR_SUCCESS) {
- srs_error("rtmp connect vhost/app failed. ret=%d", ret);
- return ret;
- }
- srs_verbose("rtmp connect app success");
-
- if ((ret = check_vhost()) != ERROR_SUCCESS) {
- srs_error("check vhost failed. ret=%d", ret);
- return ret;
- }
- srs_verbose("check vhost success.");
-
- srs_trace("rtmp connect app success. "
- "tcUrl=%s, pageUrl=%s, swfUrl=%s, schema=%s, vhost=%s, port=%s, app=%s",
- req->tcUrl.c_str(), req->pageUrl.c_str(), req->swfUrl.c_str(),
- req->schema.c_str(), req->vhost.c_str(), req->port.c_str(),
- req->app.c_str());
-
- if ((ret = refer->check(req->pageUrl, config->get_refer(req->vhost))) != ERROR_SUCCESS) {
- srs_error("check refer failed. ret=%d", ret);
- return ret;
- }
- srs_verbose("check refer success.");
-
- if ((ret = rtmp->set_window_ack_size(2.5 * 1000 * 1000)) != ERROR_SUCCESS) {
- srs_error("set window acknowledgement size failed. ret=%d", ret);
- return ret;
- }
- srs_verbose("set window acknowledgement size success");
-
- if ((ret = rtmp->set_peer_bandwidth(2.5 * 1000 * 1000, 2)) != ERROR_SUCCESS) {
- srs_error("set peer bandwidth failed. ret=%d", ret);
- return ret;
- }
- srs_verbose("set peer bandwidth success");
-
- if ((ret = rtmp->response_connect_app(req)) != ERROR_SUCCESS) {
- srs_error("response connect app failed. ret=%d", ret);
- return ret;
- }
- srs_verbose("response connect app success");
-
- if ((ret = rtmp->on_bw_done()) != ERROR_SUCCESS) {
- srs_error("on_bw_done failed. ret=%d", ret);
- return ret;
- }
- srs_verbose("on_bw_done success");
-
- SrsClientType type;
- if ((ret = rtmp->identify_client(res->stream_id, type, req->stream)) != ERROR_SUCCESS) {
- srs_error("identify client failed. ret=%d", ret);
- return ret;
- }
- req->strip();
- srs_trace("identify client success. type=%d, stream_name=%s", type, req->stream.c_str());
-
- int chunk_size = 4096;
- SrsConfDirective* conf = config->get_chunk_size();
- if (conf && !conf->arg0().empty()) {
- chunk_size = ::atoi(conf->arg0().c_str());
- }
- if ((ret = rtmp->set_chunk_size(chunk_size)) != ERROR_SUCCESS) {
- srs_error("set chunk_size=%d failed. ret=%d", chunk_size, ret);
- return ret;
- }
- srs_trace("set chunk_size=%d success", chunk_size);
-
- // find a source to publish.
- SrsSource* source = SrsSource::find(req->get_stream_url());
- srs_assert(source != NULL);
-
- // check publish available.
- if (type != SrsClientPlay && !source->can_publish()) {
- ret = ERROR_SYSTEM_STREAM_BUSY;
- srs_warn("stream %s is already publishing. ret=%d",
- req->get_stream_url().c_str(), ret);
- // to delay request
- st_usleep(SRS_STREAM_BUSY_SLEEP_MS * 1000);
- return ret;
- }
-
- bool enabled_cache = true;
- conf = config->get_gop_cache(req->vhost);
- if (conf && conf->arg0() == "off") {
- enabled_cache = false;
- }
- source->set_cache(enabled_cache);
-
- srs_info("source found, url=%s, enabled_cache=%d", req->get_stream_url().c_str(), enabled_cache);
-
- switch (type) {
- case SrsClientPlay: {
- srs_verbose("start to play stream %s.", req->stream.c_str());
-
- if ((ret = rtmp->start_play(res->stream_id)) != ERROR_SUCCESS) {
- srs_error("start to play stream failed. ret=%d", ret);
- return ret;
- }
- srs_info("start to play stream %s success", req->stream.c_str());
- return playing(source);
- }
- case SrsClientFMLEPublish: {
- srs_verbose("FMLE start to publish stream %s.", req->stream.c_str());
-
- if ((ret = rtmp->start_fmle_publish(res->stream_id)) != ERROR_SUCCESS) {
- srs_error("start to publish stream failed. ret=%d", ret);
- return ret;
- }
- srs_info("start to publish stream %s success", req->stream.c_str());
- ret = publish(source, true);
- source->on_unpublish();
- return ret;
- }
- case SrsClientFlashPublish: {
- srs_verbose("flash start to publish stream %s.", req->stream.c_str());
-
- if ((ret = rtmp->start_flash_publish(res->stream_id)) != ERROR_SUCCESS) {
- srs_error("flash start to publish stream failed. ret=%d", ret);
- return ret;
- }
- srs_info("flash start to publish stream %s success", req->stream.c_str());
- ret = publish(source, false);
- source->on_unpublish();
- return ret;
- }
- default: {
- ret = ERROR_SYSTEM_CLIENT_INVALID;
- srs_info("invalid client type=%d. ret=%d", type, ret);
- return ret;
- }
- }
-
- return ret;
-}
-
-int SrsClient::check_vhost()
-{
- int ret = ERROR_SUCCESS;
-
- srs_assert(req != NULL);
-
- SrsConfDirective* vhost = config->get_vhost(req->vhost);
- if (vhost == NULL) {
- ret = ERROR_RTMP_VHOST_NOT_FOUND;
- srs_error("vhost %s not found. ret=%d", req->vhost.c_str(), ret);
- return ret;
- }
-
- if (!config->get_vhost_enabled(req->vhost)) {
- ret = ERROR_RTMP_VHOST_NOT_FOUND;
- srs_error("vhost %s disabled. ret=%d", req->vhost.c_str(), ret);
- return ret;
- }
-
- if (req->vhost != vhost->arg0()) {
- srs_trace("vhost change from %s to %s", req->vhost.c_str(), vhost->arg0().c_str());
- req->vhost = vhost->arg0();
- }
-
-#ifdef SRS_HTTP
- // HTTP: on_connect
- std::string on_connect = config->get_vhost_on_connect(req->vhost);
- if (on_connect.empty()) {
- srs_info("ignore the empty http callback: on_connect");
- return ret;
- }
-
- if ((ret = http_hooks->on_connect(on_connect, ip, req)) != ERROR_SUCCESS) {
- srs_error("hook client failed. ret=%d", ret);
- return ret;
- }
-#endif
-
- return ret;
-}
-
-int SrsClient::playing(SrsSource* source)
-{
- int ret = ERROR_SUCCESS;
-
- if ((ret = refer->check(req->pageUrl, config->get_refer_play(req->vhost))) != ERROR_SUCCESS) {
- srs_error("check play_refer failed. ret=%d", ret);
- return ret;
- }
- srs_verbose("check play_refer success.");
-
- SrsConsumer* consumer = NULL;
- if ((ret = source->create_consumer(consumer)) != ERROR_SUCCESS) {
- srs_error("create consumer failed. ret=%d", ret);
- return ret;
- }
-
- srs_assert(consumer != NULL);
- SrsAutoFree(SrsConsumer, consumer, false);
- srs_verbose("consumer created success.");
-
- rtmp->set_recv_timeout(SRS_PULSE_TIMEOUT_MS * 1000);
-
- SrsPithyPrint pithy_print(SRS_STAGE_PLAY_USER);
-
- while (true) {
- pithy_print.elapse(SRS_PULSE_TIMEOUT_MS);
-
- // switch to other st-threads.
- st_usleep(0);
-
- // read from client.
- int ctl_msg_ret = ERROR_SUCCESS;
- if (true) {
- SrsCommonMessage* msg = NULL;
- ctl_msg_ret = ret = rtmp->recv_message(&msg);
-
- srs_verbose("play loop recv message. ret=%d", ret);
- if (ret != ERROR_SUCCESS && ret != ERROR_SOCKET_TIMEOUT) {
- srs_error("recv client control message failed. ret=%d", ret);
- return ret;
- }
- if ((ret = process_play_control_msg(consumer, msg)) != ERROR_SUCCESS) {
- srs_error("process play control message failed. ret=%d", ret);
- return ret;
- }
- }
-
- // get messages from consumer.
- SrsSharedPtrMessage** msgs = NULL;
- int count = 0;
- if ((ret = consumer->get_packets(0, msgs, count)) != ERROR_SUCCESS) {
- srs_error("get messages from consumer failed. ret=%d", ret);
- return ret;
- }
-
- // reportable
- if (pithy_print.can_print()) {
- srs_trace("-> time=%"PRId64", cmr=%d, msgs=%d, obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d",
- pithy_print.get_age(), ctl_msg_ret, count, rtmp->get_send_bytes(), rtmp->get_recv_bytes(), rtmp->get_send_kbps(), rtmp->get_recv_kbps());
- }
-
- if (count <= 0) {
- srs_verbose("no packets in queue.");
- continue;
- }
- SrsAutoFree(SrsSharedPtrMessage*, msgs, true);
-
- // sendout messages
- for (int i = 0; i < count; i++) {
- SrsSharedPtrMessage* msg = msgs[i];
-
- // the send_message will free the msg,
- // so set the msgs[i] to NULL.
- msgs[i] = NULL;
-
- if ((ret = rtmp->send_message(msg)) != ERROR_SUCCESS) {
- srs_error("send message to client failed. ret=%d", ret);
- return ret;
- }
- }
- }
-
- return ret;
-}
-
-int SrsClient::publish(SrsSource* source, bool is_fmle)
-{
- int ret = ERROR_SUCCESS;
-
- if ((ret = refer->check(req->pageUrl, config->get_refer_publish(req->vhost))) != ERROR_SUCCESS) {
- srs_error("check publish_refer failed. ret=%d", ret);
- return ret;
- }
- srs_verbose("check publish_refer success.");
-
- SrsPithyPrint pithy_print(SRS_STAGE_PUBLISH_USER);
-
- // notify the hls to prepare when publish start.
- if ((ret = source->on_publish(req)) != ERROR_SUCCESS) {
- srs_error("hls on_publish failed. ret=%d", ret);
- return ret;
- }
- srs_verbose("hls on_publish success.");
-
- while (true) {
- // switch to other st-threads.
- st_usleep(0);
-
- SrsCommonMessage* msg = NULL;
- if ((ret = rtmp->recv_message(&msg)) != ERROR_SUCCESS) {
- srs_error("recv identify client message failed. ret=%d", ret);
- return ret;
- }
-
- SrsAutoFree(SrsCommonMessage, msg, false);
-
- pithy_print.set_age(msg->header.timestamp);
-
- // reportable
- if (pithy_print.can_print()) {
- srs_trace("<- time=%"PRId64", obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d",
- pithy_print.get_age(), rtmp->get_send_bytes(), rtmp->get_recv_bytes(), rtmp->get_send_kbps(), rtmp->get_recv_kbps());
- }
-
- if ((ret = process_publish_message(source, msg, is_fmle)) != ERROR_SUCCESS) {
- srs_error("process publish message failed. ret=%d", ret);
- return ret;
- }
- }
-
- return ret;
-}
-
-int SrsClient::process_publish_message(SrsSource* source, SrsCommonMessage* msg, bool is_fmle)
-{
- int ret = ERROR_SUCCESS;
-
- // process audio packet
- if (msg->header.is_audio()) {
- if ((ret = source->on_audio(msg)) != ERROR_SUCCESS) {
- srs_error("source process audio message failed. ret=%d", ret);
- return ret;
- }
- }
- // process video packet
- if (msg->header.is_video()) {
- if ((ret = source->on_video(msg)) != ERROR_SUCCESS) {
- srs_error("source process video message failed. ret=%d", ret);
- return ret;
- }
- }
-
- // process onMetaData
- if (msg->header.is_amf0_data() || msg->header.is_amf3_data()) {
- if ((ret = msg->decode_packet(rtmp->get_protocol())) != ERROR_SUCCESS) {
- srs_error("decode onMetaData message failed. ret=%d", ret);
- return ret;
- }
-
- SrsPacket* pkt = msg->get_packet();
- if (dynamic_cast(pkt)) {
- SrsOnMetaDataPacket* metadata = dynamic_cast(pkt);
- if ((ret = source->on_meta_data(msg, metadata)) != ERROR_SUCCESS) {
- srs_error("source process onMetaData message failed. ret=%d", ret);
- return ret;
- }
- srs_trace("process onMetaData message success.");
- return ret;
- }
-
- srs_trace("ignore AMF0/AMF3 data message.");
- return ret;
- }
-
- // process UnPublish event.
- if (msg->header.is_amf0_command() || msg->header.is_amf3_command()) {
- if ((ret = msg->decode_packet(rtmp->get_protocol())) != ERROR_SUCCESS) {
- srs_error("decode unpublish message failed. ret=%d", ret);
- return ret;
- }
-
- // flash unpublish.
- if (!is_fmle) {
- srs_trace("flash publish finished.");
- return ret;
- }
-
- SrsPacket* pkt = msg->get_packet();
- if (dynamic_cast(pkt)) {
- SrsFMLEStartPacket* unpublish = dynamic_cast(pkt);
- return rtmp->fmle_unpublish(res->stream_id, unpublish->transaction_id);
- }
-
- srs_trace("ignore AMF0/AMF3 command message.");
- return ret;
- }
-
- return ret;
-}
-
-int SrsClient::get_peer_ip()
-{
- int ret = ERROR_SUCCESS;
-
- int fd = st_netfd_fileno(stfd);
-
- // discovery client information
- sockaddr_in addr;
- socklen_t addrlen = sizeof(addr);
- if (getpeername(fd, (sockaddr*)&addr, &addrlen) == -1) {
- ret = ERROR_SOCKET_GET_PEER_NAME;
- srs_error("discovery client information failed. ret=%d", ret);
- return ret;
- }
- srs_verbose("get peer name success.");
-
- // ip v4 or v6
- char buf[INET6_ADDRSTRLEN];
- memset(buf, 0, sizeof(buf));
-
- if ((inet_ntop(addr.sin_family, &addr.sin_addr, buf, sizeof(buf))) == NULL) {
- ret = ERROR_SOCKET_GET_PEER_IP;
- srs_error("convert client information failed. ret=%d", ret);
- return ret;
- }
- srs_verbose("get peer ip of client ip=%s, fd=%d", buf, fd);
-
- ip = new char[strlen(buf) + 1];
- strcpy(ip, buf);
-
- srs_verbose("get peer ip success. ip=%s, fd=%d", ip, fd);
-
- return ret;
-}
-
-int SrsClient::process_play_control_msg(SrsConsumer* consumer, SrsCommonMessage* msg)
-{
- int ret = ERROR_SUCCESS;
-
- if (!msg) {
- srs_verbose("ignore all empty message.");
- return ret;
- }
- SrsAutoFree(SrsCommonMessage, msg, false);
-
- if (!msg->header.is_amf0_command() && !msg->header.is_amf3_command()) {
- srs_info("ignore all message except amf0/amf3 command.");
- return ret;
- }
-
- if ((ret = msg->decode_packet(rtmp->get_protocol())) != ERROR_SUCCESS) {
- srs_error("decode the amf0/amf3 command packet failed. ret=%d", ret);
- return ret;
- }
- srs_info("decode the amf0/amf3 command packet success.");
-
- SrsPausePacket* pause = dynamic_cast(msg->get_packet());
- if (!pause) {
- srs_info("ignore all amf0/amf3 command except pause.");
- return ret;
- }
-
- if ((ret = rtmp->on_play_client_pause(res->stream_id, pause->is_pause)) != ERROR_SUCCESS) {
- srs_error("rtmp process play client pause failed. ret=%d", ret);
- return ret;
- }
-
- if ((ret = consumer->on_play_client_pause(pause->is_pause)) != ERROR_SUCCESS) {
- srs_error("consumer process play client pause failed. ret=%d", ret);
- return ret;
- }
- srs_info("process pause success, is_pause=%d, time=%d.", pause->is_pause, pause->time_ms);
-
- return ret;
-}
-
+/*
+The MIT License (MIT)
+
+Copyright (c) 2013 winlin
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define SRS_PULSE_TIMEOUT_MS 100
+#define SRS_SEND_TIMEOUT_US 5000000L
+#define SRS_RECV_TIMEOUT_US SRS_SEND_TIMEOUT_US
+#define SRS_STREAM_BUSY_SLEEP_MS 2000
+
+SrsClient::SrsClient(SrsServer* srs_server, st_netfd_t client_stfd)
+ : SrsConnection(srs_server, client_stfd)
+{
+ ip = NULL;
+ req = new SrsRequest();
+ res = new SrsResponse();
+ rtmp = new SrsRtmp(client_stfd);
+ refer = new SrsRefer();
+#ifdef SRS_HTTP
+ http_hooks = new SrsHttpHooks();
+#endif
+}
+
+SrsClient::~SrsClient()
+{
+ srs_freepa(ip);
+ srs_freep(req);
+ srs_freep(res);
+ srs_freep(rtmp);
+ srs_freep(refer);
+#ifdef SRS_HTTP
+ srs_freep(http_hooks);
+#endif
+}
+
+// TODO: return detail message when error for client.
+int SrsClient::do_cycle()
+{
+ int ret = ERROR_SUCCESS;
+
+ if ((ret = get_peer_ip()) != ERROR_SUCCESS) {
+ srs_error("get peer ip failed. ret=%d", ret);
+ return ret;
+ }
+ srs_trace("get peer ip success. ip=%s, send_to=%"PRId64", recv_to=%"PRId64"",
+ ip, SRS_SEND_TIMEOUT_US, SRS_RECV_TIMEOUT_US);
+
+ rtmp->set_recv_timeout(SRS_RECV_TIMEOUT_US);
+ rtmp->set_send_timeout(SRS_SEND_TIMEOUT_US);
+
+ if ((ret = rtmp->handshake()) != ERROR_SUCCESS) {
+ srs_error("rtmp handshake failed. ret=%d", ret);
+ return ret;
+ }
+ srs_verbose("rtmp handshake success");
+
+ if ((ret = rtmp->connect_app(req)) != ERROR_SUCCESS) {
+ srs_error("rtmp connect vhost/app failed. ret=%d", ret);
+ return ret;
+ }
+ srs_verbose("rtmp connect app success");
+
+ if ((ret = check_vhost()) != ERROR_SUCCESS) {
+ srs_error("check vhost failed. ret=%d", ret);
+ return ret;
+ }
+ srs_verbose("check vhost success.");
+
+ srs_trace("rtmp connect app success. "
+ "tcUrl=%s, pageUrl=%s, swfUrl=%s, schema=%s, vhost=%s, port=%s, app=%s",
+ req->tcUrl.c_str(), req->pageUrl.c_str(), req->swfUrl.c_str(),
+ req->schema.c_str(), req->vhost.c_str(), req->port.c_str(),
+ req->app.c_str());
+
+ if ((ret = refer->check(req->pageUrl, config->get_refer(req->vhost))) != ERROR_SUCCESS) {
+ srs_error("check refer failed. ret=%d", ret);
+ return ret;
+ }
+ srs_verbose("check refer success.");
+
+ if ((ret = rtmp->set_window_ack_size(2.5 * 1000 * 1000)) != ERROR_SUCCESS) {
+ srs_error("set window acknowledgement size failed. ret=%d", ret);
+ return ret;
+ }
+ srs_verbose("set window acknowledgement size success");
+
+ if ((ret = rtmp->set_peer_bandwidth(2.5 * 1000 * 1000, 2)) != ERROR_SUCCESS) {
+ srs_error("set peer bandwidth failed. ret=%d", ret);
+ return ret;
+ }
+ srs_verbose("set peer bandwidth success");
+
+ if ((ret = rtmp->response_connect_app(req)) != ERROR_SUCCESS) {
+ srs_error("response connect app failed. ret=%d", ret);
+ return ret;
+ }
+ srs_verbose("response connect app success");
+
+ if ((ret = rtmp->on_bw_done()) != ERROR_SUCCESS) {
+ srs_error("on_bw_done failed. ret=%d", ret);
+ return ret;
+ }
+ srs_verbose("on_bw_done success");
+
+ SrsClientType type;
+ if ((ret = rtmp->identify_client(res->stream_id, type, req->stream)) != ERROR_SUCCESS) {
+ srs_error("identify client failed. ret=%d", ret);
+ return ret;
+ }
+ req->strip();
+ srs_trace("identify client success. type=%d, stream_name=%s", type, req->stream.c_str());
+
+ int chunk_size = 4096;
+ SrsConfDirective* conf = config->get_chunk_size();
+ if (conf && !conf->arg0().empty()) {
+ chunk_size = ::atoi(conf->arg0().c_str());
+ }
+ if ((ret = rtmp->set_chunk_size(chunk_size)) != ERROR_SUCCESS) {
+ srs_error("set chunk_size=%d failed. ret=%d", chunk_size, ret);
+ return ret;
+ }
+ srs_trace("set chunk_size=%d success", chunk_size);
+
+ // find a source to publish.
+ SrsSource* source = SrsSource::find(req->get_stream_url());
+ srs_assert(source != NULL);
+
+ // check publish available.
+ if (type != SrsClientPlay && !source->can_publish()) {
+ ret = ERROR_SYSTEM_STREAM_BUSY;
+ srs_warn("stream %s is already publishing. ret=%d",
+ req->get_stream_url().c_str(), ret);
+ // to delay request
+ st_usleep(SRS_STREAM_BUSY_SLEEP_MS * 1000);
+ return ret;
+ }
+
+ bool enabled_cache = true;
+ conf = config->get_gop_cache(req->vhost);
+ if (conf && conf->arg0() == "off") {
+ enabled_cache = false;
+ }
+ source->set_cache(enabled_cache);
+
+ srs_info("source found, url=%s, enabled_cache=%d", req->get_stream_url().c_str(), enabled_cache);
+
+ switch (type) {
+ case SrsClientPlay: {
+ srs_verbose("start to play stream %s.", req->stream.c_str());
+
+ if ((ret = rtmp->start_play(res->stream_id)) != ERROR_SUCCESS) {
+ srs_error("start to play stream failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("start to play stream %s success", req->stream.c_str());
+ return playing(source);
+ }
+ case SrsClientFMLEPublish: {
+ srs_verbose("FMLE start to publish stream %s.", req->stream.c_str());
+
+ if ((ret = rtmp->start_fmle_publish(res->stream_id)) != ERROR_SUCCESS) {
+ srs_error("start to publish stream failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("start to publish stream %s success", req->stream.c_str());
+ ret = publish(source, true);
+ source->on_unpublish();
+ return ret;
+ }
+ case SrsClientFlashPublish: {
+ srs_verbose("flash start to publish stream %s.", req->stream.c_str());
+
+ if ((ret = rtmp->start_flash_publish(res->stream_id)) != ERROR_SUCCESS) {
+ srs_error("flash start to publish stream failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("flash start to publish stream %s success", req->stream.c_str());
+ ret = publish(source, false);
+ source->on_unpublish();
+ return ret;
+ }
+ default: {
+ ret = ERROR_SYSTEM_CLIENT_INVALID;
+ srs_info("invalid client type=%d. ret=%d", type, ret);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+int SrsClient::check_vhost()
+{
+ int ret = ERROR_SUCCESS;
+
+ srs_assert(req != NULL);
+
+ SrsConfDirective* vhost = config->get_vhost(req->vhost);
+ if (vhost == NULL) {
+ ret = ERROR_RTMP_VHOST_NOT_FOUND;
+ srs_error("vhost %s not found. ret=%d", req->vhost.c_str(), ret);
+ return ret;
+ }
+
+ if (!config->get_vhost_enabled(req->vhost)) {
+ ret = ERROR_RTMP_VHOST_NOT_FOUND;
+ srs_error("vhost %s disabled. ret=%d", req->vhost.c_str(), ret);
+ return ret;
+ }
+
+ if (req->vhost != vhost->arg0()) {
+ srs_trace("vhost change from %s to %s", req->vhost.c_str(), vhost->arg0().c_str());
+ req->vhost = vhost->arg0();
+ }
+
+#ifdef SRS_HTTP
+ // HTTP: on_connect
+ SrsConfDirective* on_connect = config->get_vhost_on_connect(req->vhost);
+ if (!on_connect) {
+ srs_info("ignore the empty http callback: on_connect");
+ return ret;
+ }
+
+ for (int i = 0; i < (int)on_connect->args.size(); i++) {
+ std::string url = on_connect->args.at(i);
+ if ((ret = http_hooks->on_connect(url, ip, req)) != ERROR_SUCCESS) {
+ srs_error("hook client failed. url=%s, ret=%d", url.c_str(), ret);
+ return ret;
+ }
+ }
+#endif
+
+ return ret;
+}
+
+int SrsClient::playing(SrsSource* source)
+{
+ int ret = ERROR_SUCCESS;
+
+ if ((ret = refer->check(req->pageUrl, config->get_refer_play(req->vhost))) != ERROR_SUCCESS) {
+ srs_error("check play_refer failed. ret=%d", ret);
+ return ret;
+ }
+ srs_verbose("check play_refer success.");
+
+ SrsConsumer* consumer = NULL;
+ if ((ret = source->create_consumer(consumer)) != ERROR_SUCCESS) {
+ srs_error("create consumer failed. ret=%d", ret);
+ return ret;
+ }
+
+ srs_assert(consumer != NULL);
+ SrsAutoFree(SrsConsumer, consumer, false);
+ srs_verbose("consumer created success.");
+
+ rtmp->set_recv_timeout(SRS_PULSE_TIMEOUT_MS * 1000);
+
+ SrsPithyPrint pithy_print(SRS_STAGE_PLAY_USER);
+
+ while (true) {
+ pithy_print.elapse(SRS_PULSE_TIMEOUT_MS);
+
+ // switch to other st-threads.
+ st_usleep(0);
+
+ // read from client.
+ int ctl_msg_ret = ERROR_SUCCESS;
+ if (true) {
+ SrsCommonMessage* msg = NULL;
+ ctl_msg_ret = ret = rtmp->recv_message(&msg);
+
+ srs_verbose("play loop recv message. ret=%d", ret);
+ if (ret != ERROR_SUCCESS && ret != ERROR_SOCKET_TIMEOUT) {
+ srs_error("recv client control message failed. ret=%d", ret);
+ return ret;
+ }
+ if ((ret = process_play_control_msg(consumer, msg)) != ERROR_SUCCESS) {
+ srs_error("process play control message failed. ret=%d", ret);
+ return ret;
+ }
+ }
+
+ // get messages from consumer.
+ SrsSharedPtrMessage** msgs = NULL;
+ int count = 0;
+ if ((ret = consumer->get_packets(0, msgs, count)) != ERROR_SUCCESS) {
+ srs_error("get messages from consumer failed. ret=%d", ret);
+ return ret;
+ }
+
+ // reportable
+ if (pithy_print.can_print()) {
+ srs_trace("-> time=%"PRId64", cmr=%d, msgs=%d, obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d",
+ pithy_print.get_age(), ctl_msg_ret, count, rtmp->get_send_bytes(), rtmp->get_recv_bytes(), rtmp->get_send_kbps(), rtmp->get_recv_kbps());
+ }
+
+ if (count <= 0) {
+ srs_verbose("no packets in queue.");
+ continue;
+ }
+ SrsAutoFree(SrsSharedPtrMessage*, msgs, true);
+
+ // sendout messages
+ for (int i = 0; i < count; i++) {
+ SrsSharedPtrMessage* msg = msgs[i];
+
+ // the send_message will free the msg,
+ // so set the msgs[i] to NULL.
+ msgs[i] = NULL;
+
+ if ((ret = rtmp->send_message(msg)) != ERROR_SUCCESS) {
+ srs_error("send message to client failed. ret=%d", ret);
+ return ret;
+ }
+ }
+ }
+
+ return ret;
+}
+
+int SrsClient::publish(SrsSource* source, bool is_fmle)
+{
+ int ret = ERROR_SUCCESS;
+
+ if ((ret = refer->check(req->pageUrl, config->get_refer_publish(req->vhost))) != ERROR_SUCCESS) {
+ srs_error("check publish_refer failed. ret=%d", ret);
+ return ret;
+ }
+ srs_verbose("check publish_refer success.");
+
+ SrsPithyPrint pithy_print(SRS_STAGE_PUBLISH_USER);
+
+ // notify the hls to prepare when publish start.
+ if ((ret = source->on_publish(req)) != ERROR_SUCCESS) {
+ srs_error("hls on_publish failed. ret=%d", ret);
+ return ret;
+ }
+ srs_verbose("hls on_publish success.");
+
+ while (true) {
+ // switch to other st-threads.
+ st_usleep(0);
+
+ SrsCommonMessage* msg = NULL;
+ if ((ret = rtmp->recv_message(&msg)) != ERROR_SUCCESS) {
+ srs_error("recv identify client message failed. ret=%d", ret);
+ return ret;
+ }
+
+ SrsAutoFree(SrsCommonMessage, msg, false);
+
+ pithy_print.set_age(msg->header.timestamp);
+
+ // reportable
+ if (pithy_print.can_print()) {
+ srs_trace("<- time=%"PRId64", obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d",
+ pithy_print.get_age(), rtmp->get_send_bytes(), rtmp->get_recv_bytes(), rtmp->get_send_kbps(), rtmp->get_recv_kbps());
+ }
+
+ if ((ret = process_publish_message(source, msg, is_fmle)) != ERROR_SUCCESS) {
+ srs_error("process publish message failed. ret=%d", ret);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+int SrsClient::process_publish_message(SrsSource* source, SrsCommonMessage* msg, bool is_fmle)
+{
+ int ret = ERROR_SUCCESS;
+
+ // process audio packet
+ if (msg->header.is_audio()) {
+ if ((ret = source->on_audio(msg)) != ERROR_SUCCESS) {
+ srs_error("source process audio message failed. ret=%d", ret);
+ return ret;
+ }
+ }
+ // process video packet
+ if (msg->header.is_video()) {
+ if ((ret = source->on_video(msg)) != ERROR_SUCCESS) {
+ srs_error("source process video message failed. ret=%d", ret);
+ return ret;
+ }
+ }
+
+ // process onMetaData
+ if (msg->header.is_amf0_data() || msg->header.is_amf3_data()) {
+ if ((ret = msg->decode_packet(rtmp->get_protocol())) != ERROR_SUCCESS) {
+ srs_error("decode onMetaData message failed. ret=%d", ret);
+ return ret;
+ }
+
+ SrsPacket* pkt = msg->get_packet();
+ if (dynamic_cast(pkt)) {
+ SrsOnMetaDataPacket* metadata = dynamic_cast(pkt);
+ if ((ret = source->on_meta_data(msg, metadata)) != ERROR_SUCCESS) {
+ srs_error("source process onMetaData message failed. ret=%d", ret);
+ return ret;
+ }
+ srs_trace("process onMetaData message success.");
+ return ret;
+ }
+
+ srs_trace("ignore AMF0/AMF3 data message.");
+ return ret;
+ }
+
+ // process UnPublish event.
+ if (msg->header.is_amf0_command() || msg->header.is_amf3_command()) {
+ if ((ret = msg->decode_packet(rtmp->get_protocol())) != ERROR_SUCCESS) {
+ srs_error("decode unpublish message failed. ret=%d", ret);
+ return ret;
+ }
+
+ // flash unpublish.
+ if (!is_fmle) {
+ srs_trace("flash publish finished.");
+ return ret;
+ }
+
+ SrsPacket* pkt = msg->get_packet();
+ if (dynamic_cast(pkt)) {
+ SrsFMLEStartPacket* unpublish = dynamic_cast(pkt);
+ return rtmp->fmle_unpublish(res->stream_id, unpublish->transaction_id);
+ }
+
+ srs_trace("ignore AMF0/AMF3 command message.");
+ return ret;
+ }
+
+ return ret;
+}
+
+int SrsClient::get_peer_ip()
+{
+ int ret = ERROR_SUCCESS;
+
+ int fd = st_netfd_fileno(stfd);
+
+ // discovery client information
+ sockaddr_in addr;
+ socklen_t addrlen = sizeof(addr);
+ if (getpeername(fd, (sockaddr*)&addr, &addrlen) == -1) {
+ ret = ERROR_SOCKET_GET_PEER_NAME;
+ srs_error("discovery client information failed. ret=%d", ret);
+ return ret;
+ }
+ srs_verbose("get peer name success.");
+
+ // ip v4 or v6
+ char buf[INET6_ADDRSTRLEN];
+ memset(buf, 0, sizeof(buf));
+
+ if ((inet_ntop(addr.sin_family, &addr.sin_addr, buf, sizeof(buf))) == NULL) {
+ ret = ERROR_SOCKET_GET_PEER_IP;
+ srs_error("convert client information failed. ret=%d", ret);
+ return ret;
+ }
+ srs_verbose("get peer ip of client ip=%s, fd=%d", buf, fd);
+
+ ip = new char[strlen(buf) + 1];
+ strcpy(ip, buf);
+
+ srs_verbose("get peer ip success. ip=%s, fd=%d", ip, fd);
+
+ return ret;
+}
+
+int SrsClient::process_play_control_msg(SrsConsumer* consumer, SrsCommonMessage* msg)
+{
+ int ret = ERROR_SUCCESS;
+
+ if (!msg) {
+ srs_verbose("ignore all empty message.");
+ return ret;
+ }
+ SrsAutoFree(SrsCommonMessage, msg, false);
+
+ if (!msg->header.is_amf0_command() && !msg->header.is_amf3_command()) {
+ srs_info("ignore all message except amf0/amf3 command.");
+ return ret;
+ }
+
+ if ((ret = msg->decode_packet(rtmp->get_protocol())) != ERROR_SUCCESS) {
+ srs_error("decode the amf0/amf3 command packet failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("decode the amf0/amf3 command packet success.");
+
+ SrsPausePacket* pause = dynamic_cast(msg->get_packet());
+ if (!pause) {
+ srs_info("ignore all amf0/amf3 command except pause.");
+ return ret;
+ }
+
+ if ((ret = rtmp->on_play_client_pause(res->stream_id, pause->is_pause)) != ERROR_SUCCESS) {
+ srs_error("rtmp process play client pause failed. ret=%d", ret);
+ return ret;
+ }
+
+ if ((ret = consumer->on_play_client_pause(pause->is_pause)) != ERROR_SUCCESS) {
+ srs_error("consumer process play client pause failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("process pause success, is_pause=%d, time=%d.", pause->is_pause, pause->time_ms);
+
+ return ret;
+}
+
diff --git a/trunk/src/core/srs_core_config.cpp b/trunk/src/core/srs_core_config.cpp
old mode 100644
new mode 100755
index 7b57a8662..f333b0b50
--- a/trunk/src/core/srs_core_config.cpp
+++ b/trunk/src/core/srs_core_config.cpp
@@ -1,1254 +1,1257 @@
-/*
-The MIT License (MIT)
-
-Copyright (c) 2013 winlin
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-#include
-
-#include
-#include
-#include
-#include
-// file operations.
-#include
-#include
-#include
-#include
-
-#include
-#include
-
-#include
-#include
-#include
-
-#define FILE_OFFSET(fd) lseek(fd, 0, SEEK_CUR)
-
-int64_t FILE_SIZE(int fd)
-{
- int64_t pre = FILE_OFFSET(fd);
- int64_t pos = lseek(fd, 0, SEEK_END);
- lseek(fd, pre, SEEK_SET);
- return pos;
-}
-
-#define LF (char)0x0a
-#define CR (char)0x0d
-
-bool is_common_space(char ch)
-{
- return (ch == ' ' || ch == '\t' || ch == CR || ch == LF);
-}
-
-#define CONF_BUFFER_SIZE 1024 * 1024
-
-SrsFileBuffer::SrsFileBuffer()
-{
- fd = -1;
- line = 0;
-
- pos = last = start = new char[CONF_BUFFER_SIZE];
- end = start + CONF_BUFFER_SIZE;
-}
-
-SrsFileBuffer::~SrsFileBuffer()
-{
- if (fd > 0) {
- close(fd);
- }
- srs_freepa(start);
-}
-
-int SrsFileBuffer::open(const char* filename)
-{
- assert(fd == -1);
-
- if ((fd = ::open(filename, O_RDONLY, 0)) < 0) {
- srs_error("open conf file error. errno=%d(%s)", errno, strerror(errno));
- return ERROR_SYSTEM_CONFIG_INVALID;
- }
-
- line = 1;
-
- return ERROR_SUCCESS;
-}
-
-SrsConfDirective::SrsConfDirective()
-{
-}
-
-SrsConfDirective::~SrsConfDirective()
-{
- std::vector::iterator it;
- for (it = directives.begin(); it != directives.end(); ++it) {
- SrsConfDirective* directive = *it;
- srs_freep(directive);
- }
- directives.clear();
-}
-
-std::string SrsConfDirective::arg0()
-{
- if (args.size() > 0) {
- return args.at(0);
- }
-
- return "";
-}
-
-std::string SrsConfDirective::arg1()
-{
- if (args.size() > 1) {
- return args.at(1);
- }
-
- return "";
-}
-
-std::string SrsConfDirective::arg2()
-{
- if (args.size() > 2) {
- return args.at(2);
- }
-
- return "";
-}
-
-SrsConfDirective* SrsConfDirective::at(int index)
-{
- return directives.at(index);
-}
-
-SrsConfDirective* SrsConfDirective::get(std::string _name)
-{
- std::vector::iterator it;
- for (it = directives.begin(); it != directives.end(); ++it) {
- SrsConfDirective* directive = *it;
- if (directive->name == _name) {
- return directive;
- }
- }
-
- return NULL;
-}
-
-int SrsConfDirective::parse(const char* filename)
-{
- int ret = ERROR_SUCCESS;
-
- SrsFileBuffer buffer;
-
- if ((ret = buffer.open(filename)) != ERROR_SUCCESS) {
- return ret;
- }
-
- return parse_conf(&buffer, parse_file);
-}
-
-// see: ngx_conf_parse
-int SrsConfDirective::parse_conf(SrsFileBuffer* buffer, SrsDirectiveType type)
-{
- int ret = ERROR_SUCCESS;
-
- while (true) {
- std::vector args;
- ret = read_token(buffer, args);
-
- /**
- * ret maybe:
- * ERROR_SYSTEM_CONFIG_INVALID error.
- * ERROR_SYSTEM_CONFIG_DIRECTIVE directive terminated by ';' found
- * ERROR_SYSTEM_CONFIG_BLOCK_START token terminated by '{' found
- * ERROR_SYSTEM_CONFIG_BLOCK_END the '}' found
- * ERROR_SYSTEM_CONFIG_EOF the config file is done
- */
- if (ret == ERROR_SYSTEM_CONFIG_INVALID) {
- return ret;
- }
- if (ret == ERROR_SYSTEM_CONFIG_BLOCK_END) {
- if (type != parse_block) {
- srs_error("line %d: unexpected \"}\"", buffer->line);
- return ret;
- }
- return ERROR_SUCCESS;
- }
- if (ret == ERROR_SYSTEM_CONFIG_EOF) {
- if (type == parse_block) {
- srs_error("line %d: unexpected end of file, expecting \"}\"", buffer->line);
- return ret;
- }
- return ERROR_SUCCESS;
- }
-
- if (args.empty()) {
- srs_error("line %d: empty directive.", buffer->line);
- return ret;
- }
-
- // build directive tree.
- SrsConfDirective* directive = new SrsConfDirective();
-
- directive->conf_line = buffer->line;
- directive->name = args[0];
- args.erase(args.begin());
- directive->args.swap(args);
-
- directives.push_back(directive);
-
- if (ret == ERROR_SYSTEM_CONFIG_BLOCK_START) {
- if ((ret = directive->parse_conf(buffer, parse_block)) != ERROR_SUCCESS) {
- return ret;
- }
- }
- }
-
- return ret;
-}
-
-// see: ngx_conf_read_token
-int SrsConfDirective::read_token(SrsFileBuffer* buffer, std::vector& args)
-{
- int ret = ERROR_SUCCESS;
-
- char* pstart = buffer->pos;
- int startline = buffer->line;
-
- bool sharp_comment = false;
-
- bool d_quoted = false;
- bool s_quoted = false;
-
- bool need_space = false;
- bool last_space = true;
-
- while (true) {
- if ((ret = refill_buffer(buffer, d_quoted, s_quoted, startline, pstart)) != ERROR_SUCCESS) {
- if (!args.empty() || !last_space) {
- srs_error("line %d: unexpected end of file, expecting ; or \"}\"", buffer->line);
- return ERROR_SYSTEM_CONFIG_INVALID;
- }
- return ret;
- }
-
- char ch = *buffer->pos++;
-
- if (ch == LF) {
- buffer->line++;
- sharp_comment = false;
- }
-
- if (sharp_comment) {
- continue;
- }
-
- if (need_space) {
- if (is_common_space(ch)) {
- last_space = true;
- need_space = false;
- continue;
- }
- if (ch == ';') {
- return ERROR_SYSTEM_CONFIG_DIRECTIVE;
- }
- if (ch == '{') {
- return ERROR_SYSTEM_CONFIG_BLOCK_START;
- }
- srs_error("line %d: unexpected '%c'", buffer->line, ch);
- return ERROR_SYSTEM_CONFIG_INVALID;
- }
-
- // last charecter is space.
- if (last_space) {
- if (is_common_space(ch)) {
- continue;
- }
- pstart = buffer->pos - 1;
- startline = buffer->line;
- switch (ch) {
- case ';':
- if (args.size() == 0) {
- srs_error("line %d: unexpected ';'", buffer->line);
- return ERROR_SYSTEM_CONFIG_INVALID;
- }
- return ERROR_SYSTEM_CONFIG_DIRECTIVE;
- case '{':
- if (args.size() == 0) {
- srs_error("line %d: unexpected '{'", buffer->line);
- return ERROR_SYSTEM_CONFIG_INVALID;
- }
- return ERROR_SYSTEM_CONFIG_BLOCK_START;
- case '}':
- if (args.size() != 0) {
- srs_error("line %d: unexpected '}'", buffer->line);
- return ERROR_SYSTEM_CONFIG_INVALID;
- }
- return ERROR_SYSTEM_CONFIG_BLOCK_END;
- case '#':
- sharp_comment = 1;
- continue;
- case '"':
- pstart++;
- d_quoted = true;
- last_space = 0;
- continue;
- case '\'':
- pstart++;
- s_quoted = true;
- last_space = 0;
- continue;
- default:
- last_space = 0;
- continue;
- }
- } else {
- // last charecter is not space
- bool found = false;
- if (d_quoted) {
- if (ch == '"') {
- d_quoted = false;
- need_space = true;
- found = true;
- }
- } else if (s_quoted) {
- if (ch == '\'') {
- s_quoted = false;
- need_space = true;
- found = true;
- }
- } else if (is_common_space(ch) || ch == ';' || ch == '{') {
- last_space = true;
- found = 1;
- }
-
- if (found) {
- int len = buffer->pos - pstart;
- char* word = new char[len];
- memcpy(word, pstart, len);
- word[len - 1] = 0;
-
- args.push_back(word);
- srs_freepa(word);
-
- if (ch == ';') {
- return ERROR_SYSTEM_CONFIG_DIRECTIVE;
- }
- if (ch == '{') {
- return ERROR_SYSTEM_CONFIG_BLOCK_START;
- }
- }
- }
- }
-
- return ret;
-}
-
-int SrsConfDirective::refill_buffer(SrsFileBuffer* buffer, bool d_quoted, bool s_quoted, int startline, char*& pstart)
-{
- int ret = ERROR_SUCCESS;
-
- if (buffer->pos < buffer->last) {
- return ret;
- }
-
- int size = FILE_SIZE(buffer->fd) - FILE_OFFSET(buffer->fd);
- if (size > CONF_BUFFER_SIZE) {
- ret = ERROR_SYSTEM_CONFIG_TOO_LARGE;
- srs_error("config file too large, max=%d, actual=%d, ret=%d",
- CONF_BUFFER_SIZE, size, ret);
- return ret;
- }
-
- if (size <= 0) {
- return ERROR_SYSTEM_CONFIG_EOF;
- }
-
- int len = buffer->pos - buffer->start;
- if (len >= CONF_BUFFER_SIZE) {
- buffer->line = startline;
-
- if (!d_quoted && !s_quoted) {
- srs_error("line %d: too long parameter \"%*s...\" started",
- buffer->line, 10, buffer->start);
-
- } else {
- srs_error("line %d: too long parameter, "
- "probably missing terminating '%c' character", buffer->line, d_quoted? '"':'\'');
- }
- return ERROR_SYSTEM_CONFIG_INVALID;
- }
-
- if (len) {
- memmove(buffer->start, pstart, len);
- }
-
- size = srs_min(size, buffer->end - (buffer->start + len));
- int n = read(buffer->fd, buffer->start + len, size);
- if (n != size) {
- srs_error("read file read error. expect %d, actual %d bytes.", size, n);
- return ERROR_SYSTEM_CONFIG_INVALID;
- }
-
- buffer->pos = buffer->start + len;
- buffer->last = buffer->pos + n;
- pstart = buffer->start;
-
- return ret;
-}
-
-SrsConfig* config = new SrsConfig();
-
-SrsConfig::SrsConfig()
-{
- show_help = false;
- show_version = false;
-
- root = new SrsConfDirective();
- root->conf_line = 0;
- root->name = "root";
-}
-
-SrsConfig::~SrsConfig()
-{
- srs_freep(root);
-}
-
-int SrsConfig::reload()
-{
- int ret = ERROR_SUCCESS;
-
- SrsConfig conf;
- if ((ret = conf.parse_file(config_file.c_str())) != ERROR_SUCCESS) {
- srs_error("config reloader parse file failed. ret=%d", ret);
- return ret;
- }
- srs_info("config reloader parse file success.");
-
- // store current root to old_root,
- // and reap the root from conf to current root.
- SrsConfDirective* old_root = root;
- SrsAutoFree(SrsConfDirective, old_root, false);
-
- root = conf.root;
- conf.root = NULL;
-
- // merge config.
- std::vector::iterator it;
-
- // merge config: listen
- if (!srs_directive_equals(root->get("listen"), old_root->get("listen"))) {
- for (it = subscribes.begin(); it != subscribes.end(); ++it) {
- SrsReloadHandler* subscribe = *it;
- if ((ret = subscribe->on_reload_listen()) != ERROR_SUCCESS) {
- srs_error("notify subscribes reload listen failed. ret=%d", ret);
- return ret;
- }
- }
- srs_trace("reload listen success.");
- }
- // merge config: pithy_print
- if (!srs_directive_equals(root->get("pithy_print"), old_root->get("pithy_print"))) {
- for (it = subscribes.begin(); it != subscribes.end(); ++it) {
- SrsReloadHandler* subscribe = *it;
- if ((ret = subscribe->on_reload_pithy_print()) != ERROR_SUCCESS) {
- srs_error("notify subscribes pithy_print listen failed. ret=%d", ret);
- return ret;
- }
- }
- srs_trace("reload pithy_print success.");
- }
-
- // TODO: suppor reload hls/forward/ffmpeg/http
-
- return ret;
-}
-
-void SrsConfig::subscribe(SrsReloadHandler* handler)
-{
- std::vector::iterator it;
-
- it = std::find(subscribes.begin(), subscribes.end(), handler);
- if (it != subscribes.end()) {
- return;
- }
-
- subscribes.push_back(handler);
-}
-
-void SrsConfig::unsubscribe(SrsReloadHandler* handler)
-{
- std::vector::iterator it;
-
- it = std::find(subscribes.begin(), subscribes.end(), handler);
- if (it == subscribes.end()) {
- return;
- }
-
- subscribes.erase(it);
-}
-
-// see: ngx_get_options
-int SrsConfig::parse_options(int argc, char** argv)
-{
- int ret = ERROR_SUCCESS;
-
- for (int i = 1; i < argc; i++) {
- if ((ret = parse_argv(i, argv)) != ERROR_SUCCESS) {
- return ret;
- }
- }
-
- if (show_help) {
- print_help(argv);
- }
-
- if (show_version) {
- printf("%s\n", RTMP_SIG_SRS_VERSION);
- }
-
- if (show_help || show_version) {
- exit(0);
- }
-
- if (config_file.empty()) {
- ret = ERROR_SYSTEM_CONFIG_INVALID;
- srs_error("config file not specified, see help: %s -h, ret=%d", argv[0], ret);
- return ret;
- }
-
- return parse_file(config_file.c_str());
-}
-
-SrsConfDirective* SrsConfig::get_vhost(std::string vhost)
-{
- srs_assert(root);
-
- for (int i = 0; i < (int)root->directives.size(); i++) {
- SrsConfDirective* conf = root->at(i);
-
- if (conf->name != "vhost") {
- continue;
- }
-
- if (conf->arg0() == vhost) {
- return conf;
- }
- }
-
- if (vhost != RTMP_VHOST_DEFAULT) {
- return get_vhost(RTMP_VHOST_DEFAULT);
- }
-
- return NULL;
-}
-
-std::string SrsConfig::get_vhost_on_connect(std::string vhost)
-{
- SrsConfDirective* vhost_conf = get_vhost(vhost);
-
- if (!vhost_conf) {
- return "";
- }
-
- SrsConfDirective* conf = vhost_conf->get("on_connect");
- if (!conf) {
- return "";
- }
-
- return conf->arg0();
-}
-
-bool SrsConfig::get_vhost_enabled(std::string vhost)
-{
- SrsConfDirective* vhost_conf = get_vhost(vhost);
-
- if (!vhost_conf) {
- return true;
- }
-
- SrsConfDirective* conf = vhost_conf->get("enabled");
- if (!conf) {
- return true;
- }
-
- if (conf->arg0() == "off") {
- return false;
- }
-
- return true;
-}
-
-SrsConfDirective* SrsConfig::get_transcode(std::string vhost, std::string scope)
-{
- SrsConfDirective* conf = get_vhost(vhost);
-
- if (!conf) {
- return NULL;
- }
-
- SrsConfDirective* transcode = conf->get("transcode");
- if (!transcode) {
- return NULL;
- }
-
- if (transcode->arg0() == scope) {
- return transcode;
- }
-
- return NULL;
-}
-
-bool SrsConfig::get_transcode_enabled(SrsConfDirective* transcode)
-{
- if (!transcode) {
- return false;
- }
-
- SrsConfDirective* conf = transcode->get("enabled");
- if (!conf || conf->arg0() != "on") {
- return false;
- }
-
- return true;
-}
-
-std::string SrsConfig::get_transcode_ffmpeg(SrsConfDirective* transcode)
-{
- if (!transcode) {
- return "";
- }
-
- SrsConfDirective* conf = transcode->get("ffmpeg");
- if (!conf) {
- return "";
- }
-
- return conf->arg0();
-}
-
-void SrsConfig::get_transcode_engines(SrsConfDirective* transcode, std::vector& engines)
-{
- if (!transcode) {
- return;
- }
-
- for (int i = 0; i < (int)transcode->directives.size(); i++) {
- SrsConfDirective* conf = transcode->directives[i];
-
- if (conf->name == "engine") {
- engines.push_back(conf);
- }
- }
-
- return;
-}
-
-bool SrsConfig::get_engine_enabled(SrsConfDirective* engine)
-{
- if (!engine) {
- return false;
- }
-
- SrsConfDirective* conf = engine->get("enabled");
- if (!conf || conf->arg0() != "on") {
- return false;
- }
-
- return true;
-}
-
-std::string SrsConfig::get_engine_vcodec(SrsConfDirective* engine)
-{
- if (!engine) {
- return "";
- }
-
- SrsConfDirective* conf = engine->get("vcodec");
- if (!conf) {
- return "";
- }
-
- return conf->arg0();
-}
-
-int SrsConfig::get_engine_vbitrate(SrsConfDirective* engine)
-{
- if (!engine) {
- return 0;
- }
-
- SrsConfDirective* conf = engine->get("vbitrate");
- if (!conf) {
- return 0;
- }
-
- return ::atoi(conf->arg0().c_str());
-}
-
-double SrsConfig::get_engine_vfps(SrsConfDirective* engine)
-{
- if (!engine) {
- return 0;
- }
-
- SrsConfDirective* conf = engine->get("vfps");
- if (!conf) {
- return 0;
- }
-
- return ::atof(conf->arg0().c_str());
-}
-
-int SrsConfig::get_engine_vwidth(SrsConfDirective* engine)
-{
- if (!engine) {
- return 0;
- }
-
- SrsConfDirective* conf = engine->get("vwidth");
- if (!conf) {
- return 0;
- }
-
- return ::atoi(conf->arg0().c_str());
-}
-
-int SrsConfig::get_engine_vheight(SrsConfDirective* engine)
-{
- if (!engine) {
- return 0;
- }
-
- SrsConfDirective* conf = engine->get("vheight");
- if (!conf) {
- return 0;
- }
-
- return ::atoi(conf->arg0().c_str());
-}
-
-int SrsConfig::get_engine_vthreads(SrsConfDirective* engine)
-{
- if (!engine) {
- return 0;
- }
-
- SrsConfDirective* conf = engine->get("vthreads");
- if (!conf) {
- return 0;
- }
-
- return ::atoi(conf->arg0().c_str());
-}
-
-std::string SrsConfig::get_engine_vprofile(SrsConfDirective* engine)
-{
- if (!engine) {
- return "";
- }
-
- SrsConfDirective* conf = engine->get("vprofile");
- if (!conf) {
- return "";
- }
-
- return conf->arg0();
-}
-
-std::string SrsConfig::get_engine_vpreset(SrsConfDirective* engine)
-{
- if (!engine) {
- return "";
- }
-
- SrsConfDirective* conf = engine->get("vpreset");
- if (!conf) {
- return "";
- }
-
- return conf->arg0();
-}
-
-void SrsConfig::get_engine_vparams(SrsConfDirective* engine, std::vector& vparams)
-{
- if (!engine) {
- return;
- }
-
- SrsConfDirective* conf = engine->get("vparams");
- if (!conf) {
- return;
- }
-
- for (int i = 0; i < (int)conf->directives.size(); i++) {
- SrsConfDirective* p = conf->directives[i];
- if (!p) {
- continue;
- }
-
- vparams.push_back("-" + p->name);
- vparams.push_back(p->arg0());
- }
-}
-
-void SrsConfig::get_engine_vfilter(SrsConfDirective* engine, std::vector& vfilter)
-{
- if (!engine) {
- return;
- }
-
- SrsConfDirective* conf = engine->get("vfilter");
- if (!conf) {
- return;
- }
-
- for (int i = 0; i < (int)conf->directives.size(); i++) {
- SrsConfDirective* p = conf->directives[i];
- if (!p) {
- continue;
- }
-
- vfilter.push_back("-" + p->name);
- vfilter.push_back(p->arg0());
- }
-}
-
-std::string SrsConfig::get_engine_acodec(SrsConfDirective* engine)
-{
- if (!engine) {
- return "";
- }
-
- SrsConfDirective* conf = engine->get("acodec");
- if (!conf) {
- return "";
- }
-
- return conf->arg0();
-}
-
-int SrsConfig::get_engine_abitrate(SrsConfDirective* engine)
-{
- if (!engine) {
- return 0;
- }
-
- SrsConfDirective* conf = engine->get("abitrate");
- if (!conf) {
- return 0;
- }
-
- return ::atoi(conf->arg0().c_str());
-}
-
-int SrsConfig::get_engine_asample_rate(SrsConfDirective* engine)
-{
- if (!engine) {
- return 0;
- }
-
- SrsConfDirective* conf = engine->get("asample_rate");
- if (!conf) {
- return 0;
- }
-
- return ::atoi(conf->arg0().c_str());
-}
-
-int SrsConfig::get_engine_achannels(SrsConfDirective* engine)
-{
- if (!engine) {
- return 0;
- }
-
- SrsConfDirective* conf = engine->get("achannels");
- if (!conf) {
- return 0;
- }
-
- return ::atoi(conf->arg0().c_str());
-}
-
-void SrsConfig::get_engine_aparams(SrsConfDirective* engine, std::vector& aparams)
-{
- if (!engine) {
- return;
- }
-
- SrsConfDirective* conf = engine->get("aparams");
- if (!conf) {
- return;
- }
-
- for (int i = 0; i < (int)conf->directives.size(); i++) {
- SrsConfDirective* p = conf->directives[i];
- if (!p) {
- continue;
- }
-
- aparams.push_back("-" + p->name);
- aparams.push_back(p->arg0());
- }
-}
-
-std::string SrsConfig::get_engine_output(SrsConfDirective* engine)
-{
- if (!engine) {
- return "";
- }
-
- SrsConfDirective* conf = engine->get("output");
- if (!conf) {
- return "";
- }
-
- return conf->arg0();
-}
-
-std::string SrsConfig::get_log_dir()
-{
- srs_assert(root);
-
- SrsConfDirective* conf = root->get("log_dir");
- if (!conf || conf->arg0().empty()) {
- return "./objs/logs";
- }
-
- return conf->arg0();
-}
-
-int SrsConfig::get_max_connections()
-{
- srs_assert(root);
-
- SrsConfDirective* conf = root->get("max_connections");
- if (!conf || conf->arg0().empty()) {
- return 2000;
- }
-
- return ::atoi(conf->arg0().c_str());
-}
-
-SrsConfDirective* SrsConfig::get_gop_cache(std::string vhost)
-{
- SrsConfDirective* conf = get_vhost(vhost);
-
- if (!conf) {
- return NULL;
- }
-
- return conf->get("gop_cache");
-}
-
-SrsConfDirective* SrsConfig::get_forward(std::string vhost)
-{
- SrsConfDirective* conf = get_vhost(vhost);
-
- if (!conf) {
- return NULL;
- }
-
- return conf->get("forward");
-}
-
-SrsConfDirective* SrsConfig::get_hls(std::string vhost)
-{
- SrsConfDirective* conf = get_vhost(vhost);
-
- if (!conf) {
- return NULL;
- }
-
- return conf->get("hls");
-}
-
-bool SrsConfig::get_hls_enabled(std::string vhost)
-{
- SrsConfDirective* hls = get_hls(vhost);
-
- if (!hls) {
- return true;
- }
-
- if (hls->arg0() == "off") {
- return false;
- }
-
- return true;
-}
-
-SrsConfDirective* SrsConfig::get_hls_path(std::string vhost)
-{
- SrsConfDirective* conf = get_vhost(vhost);
-
- if (!conf) {
- return NULL;
- }
-
- return conf->get("hls_path");
-}
-
-SrsConfDirective* SrsConfig::get_hls_fragment(std::string vhost)
-{
- SrsConfDirective* conf = get_vhost(vhost);
-
- if (!conf) {
- return NULL;
- }
-
- return conf->get("hls_fragment");
-}
-
-SrsConfDirective* SrsConfig::get_hls_window(std::string vhost)
-{
- SrsConfDirective* conf = get_vhost(vhost);
-
- if (!conf) {
- return NULL;
- }
-
- return conf->get("hls_window");
-}
-
-SrsConfDirective* SrsConfig::get_refer(std::string vhost)
-{
- SrsConfDirective* conf = get_vhost(vhost);
-
- if (!conf) {
- return NULL;
- }
-
- return conf->get("refer");
-}
-
-SrsConfDirective* SrsConfig::get_refer_play(std::string vhost)
-{
- SrsConfDirective* conf = get_vhost(vhost);
-
- if (!conf) {
- return NULL;
- }
-
- return conf->get("refer_play");
-}
-
-SrsConfDirective* SrsConfig::get_refer_publish(std::string vhost)
-{
- SrsConfDirective* conf = get_vhost(vhost);
-
- if (!conf) {
- return NULL;
- }
-
- return conf->get("refer_publish");
-}
-
-SrsConfDirective* SrsConfig::get_listen()
-{
- return root->get("listen");
-}
-
-SrsConfDirective* SrsConfig::get_chunk_size()
-{
- return root->get("chunk_size");
-}
-
-SrsConfDirective* SrsConfig::get_pithy_print_publish()
-{
- SrsConfDirective* pithy = root->get("pithy_print");
- if (!pithy) {
- return NULL;
- }
-
- return pithy->get("publish");
-}
-
-SrsConfDirective* SrsConfig::get_pithy_print_forwarder()
-{
- SrsConfDirective* pithy = root->get("pithy_print");
- if (!pithy) {
- return NULL;
- }
-
- return pithy->get("forwarder");
-}
-
-SrsConfDirective* SrsConfig::get_pithy_print_hls()
-{
- SrsConfDirective* pithy = root->get("pithy_print");
- if (!pithy) {
- return NULL;
- }
-
- return pithy->get("hls");
-}
-
-SrsConfDirective* SrsConfig::get_pithy_print_encoder()
-{
- SrsConfDirective* pithy = root->get("encoder");
- if (!pithy) {
- return NULL;
- }
-
- return pithy->get("forwarder");
-}
-
-SrsConfDirective* SrsConfig::get_pithy_print_play()
-{
- SrsConfDirective* pithy = root->get("pithy_print");
- if (!pithy) {
- return NULL;
- }
-
- return pithy->get("play");
-}
-
-int SrsConfig::parse_file(const char* filename)
-{
- int ret = ERROR_SUCCESS;
-
- config_file = filename;
-
- if (config_file.empty()) {
- return ERROR_SYSTEM_CONFIG_INVALID;
- }
-
- if ((ret = root->parse(config_file.c_str())) != ERROR_SUCCESS) {
- return ret;
- }
-
- SrsConfDirective* conf = NULL;
- if ((conf = get_listen()) == NULL || conf->args.size() == 0) {
- ret = ERROR_SYSTEM_CONFIG_INVALID;
- srs_error("line %d: conf error, "
- "directive \"listen\" is empty, ret=%d", (conf? conf->conf_line:0), ret);
- return ret;
- }
- // TODO: check the hls.
- // TODO: check other config.
- // TODO: check hls.
- // TODO: check ssl.
- // TODO: check ffmpeg.
- // 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
- " Copyright (c) 2013 winlin\n"
- "Contributors: "RTMP_SIG_SRS_CONTRIBUTOR"\n"
- "Build: "SRS_BUILD_DATE" Configuration: "SRS_CONFIGURE"\n"
- "Usage: %s [-h?vV] [-c ]\n"
- "\n"
- "Options:\n"
- " -?-h : show help\n"
- " -v-V : show version and exit\n"
- " -c filename : set configuration file\n"
- "\n"
- RTMP_SIG_SRS_WEB"\n"
- RTMP_SIG_SRS_URL"\n"
- "Email: "RTMP_SIG_SRS_EMAIL"\n"
- "\n",
- argv[0]);
-}
-
-bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b)
-{
- if (!a || !b) {
- return false;
- }
-
- if (a->name != b->name) {
- return false;
- }
-
- if (a->args.size() != b->args.size()) {
- return false;
- }
-
- for (int i = 0; i < (int)a->args.size(); i++) {
- if (a->args.at(i) != b->args.at(i)) {
- return false;
- }
- }
-
- if (a->directives.size() != b->directives.size()) {
- return false;
- }
-
- for (int i = 0; i < (int)a->directives.size(); i++) {
- SrsConfDirective* a0 = a->at(i);
- SrsConfDirective* b0 = b->at(i);
-
- if (!srs_directive_equals(a0, b0)) {
- return false;
- }
- }
-
- return true;
-}
-
+/*
+The MIT License (MIT)
+
+Copyright (c) 2013 winlin
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include
+
+#include
+#include
+#include
+#include
+// file operations.
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+
+#define FILE_OFFSET(fd) lseek(fd, 0, SEEK_CUR)
+
+int64_t FILE_SIZE(int fd)
+{
+ int64_t pre = FILE_OFFSET(fd);
+ int64_t pos = lseek(fd, 0, SEEK_END);
+ lseek(fd, pre, SEEK_SET);
+ return pos;
+}
+
+#define LF (char)0x0a
+#define CR (char)0x0d
+
+bool is_common_space(char ch)
+{
+ return (ch == ' ' || ch == '\t' || ch == CR || ch == LF);
+}
+
+#define CONF_BUFFER_SIZE 1024 * 1024
+
+SrsFileBuffer::SrsFileBuffer()
+{
+ fd = -1;
+ line = 0;
+
+ pos = last = start = new char[CONF_BUFFER_SIZE];
+ end = start + CONF_BUFFER_SIZE;
+}
+
+SrsFileBuffer::~SrsFileBuffer()
+{
+ if (fd > 0) {
+ close(fd);
+ }
+ srs_freepa(start);
+}
+
+int SrsFileBuffer::open(const char* filename)
+{
+ assert(fd == -1);
+
+ if ((fd = ::open(filename, O_RDONLY, 0)) < 0) {
+ srs_error("open conf file error. errno=%d(%s)", errno, strerror(errno));
+ return ERROR_SYSTEM_CONFIG_INVALID;
+ }
+
+ line = 1;
+
+ return ERROR_SUCCESS;
+}
+
+SrsConfDirective::SrsConfDirective()
+{
+}
+
+SrsConfDirective::~SrsConfDirective()
+{
+ std::vector::iterator it;
+ for (it = directives.begin(); it != directives.end(); ++it) {
+ SrsConfDirective* directive = *it;
+ srs_freep(directive);
+ }
+ directives.clear();
+}
+
+std::string SrsConfDirective::arg0()
+{
+ if (args.size() > 0) {
+ return args.at(0);
+ }
+
+ return "";
+}
+
+std::string SrsConfDirective::arg1()
+{
+ if (args.size() > 1) {
+ return args.at(1);
+ }
+
+ return "";
+}
+
+std::string SrsConfDirective::arg2()
+{
+ if (args.size() > 2) {
+ return args.at(2);
+ }
+
+ return "";
+}
+
+SrsConfDirective* SrsConfDirective::at(int index)
+{
+ return directives.at(index);
+}
+
+SrsConfDirective* SrsConfDirective::get(std::string _name)
+{
+ std::vector::iterator it;
+ for (it = directives.begin(); it != directives.end(); ++it) {
+ SrsConfDirective* directive = *it;
+ if (directive->name == _name) {
+ return directive;
+ }
+ }
+
+ return NULL;
+}
+
+int SrsConfDirective::parse(const char* filename)
+{
+ int ret = ERROR_SUCCESS;
+
+ SrsFileBuffer buffer;
+
+ if ((ret = buffer.open(filename)) != ERROR_SUCCESS) {
+ return ret;
+ }
+
+ return parse_conf(&buffer, parse_file);
+}
+
+// see: ngx_conf_parse
+int SrsConfDirective::parse_conf(SrsFileBuffer* buffer, SrsDirectiveType type)
+{
+ int ret = ERROR_SUCCESS;
+
+ while (true) {
+ std::vector args;
+ ret = read_token(buffer, args);
+
+ /**
+ * ret maybe:
+ * ERROR_SYSTEM_CONFIG_INVALID error.
+ * ERROR_SYSTEM_CONFIG_DIRECTIVE directive terminated by ';' found
+ * ERROR_SYSTEM_CONFIG_BLOCK_START token terminated by '{' found
+ * ERROR_SYSTEM_CONFIG_BLOCK_END the '}' found
+ * ERROR_SYSTEM_CONFIG_EOF the config file is done
+ */
+ if (ret == ERROR_SYSTEM_CONFIG_INVALID) {
+ return ret;
+ }
+ if (ret == ERROR_SYSTEM_CONFIG_BLOCK_END) {
+ if (type != parse_block) {
+ srs_error("line %d: unexpected \"}\"", buffer->line);
+ return ret;
+ }
+ return ERROR_SUCCESS;
+ }
+ if (ret == ERROR_SYSTEM_CONFIG_EOF) {
+ if (type == parse_block) {
+ srs_error("line %d: unexpected end of file, expecting \"}\"", buffer->line);
+ return ret;
+ }
+ return ERROR_SUCCESS;
+ }
+
+ if (args.empty()) {
+ srs_error("line %d: empty directive.", buffer->line);
+ return ret;
+ }
+
+ // build directive tree.
+ SrsConfDirective* directive = new SrsConfDirective();
+
+ directive->conf_line = buffer->line;
+ directive->name = args[0];
+ args.erase(args.begin());
+ directive->args.swap(args);
+
+ directives.push_back(directive);
+
+ if (ret == ERROR_SYSTEM_CONFIG_BLOCK_START) {
+ if ((ret = directive->parse_conf(buffer, parse_block)) != ERROR_SUCCESS) {
+ return ret;
+ }
+ }
+ }
+
+ return ret;
+}
+
+// see: ngx_conf_read_token
+int SrsConfDirective::read_token(SrsFileBuffer* buffer, std::vector& args)
+{
+ int ret = ERROR_SUCCESS;
+
+ char* pstart = buffer->pos;
+ int startline = buffer->line;
+
+ bool sharp_comment = false;
+
+ bool d_quoted = false;
+ bool s_quoted = false;
+
+ bool need_space = false;
+ bool last_space = true;
+
+ while (true) {
+ if ((ret = refill_buffer(buffer, d_quoted, s_quoted, startline, pstart)) != ERROR_SUCCESS) {
+ if (!args.empty() || !last_space) {
+ srs_error("line %d: unexpected end of file, expecting ; or \"}\"", buffer->line);
+ return ERROR_SYSTEM_CONFIG_INVALID;
+ }
+ return ret;
+ }
+
+ char ch = *buffer->pos++;
+
+ if (ch == LF) {
+ buffer->line++;
+ sharp_comment = false;
+ }
+
+ if (sharp_comment) {
+ continue;
+ }
+
+ if (need_space) {
+ if (is_common_space(ch)) {
+ last_space = true;
+ need_space = false;
+ continue;
+ }
+ if (ch == ';') {
+ return ERROR_SYSTEM_CONFIG_DIRECTIVE;
+ }
+ if (ch == '{') {
+ return ERROR_SYSTEM_CONFIG_BLOCK_START;
+ }
+ srs_error("line %d: unexpected '%c'", buffer->line, ch);
+ return ERROR_SYSTEM_CONFIG_INVALID;
+ }
+
+ // last charecter is space.
+ if (last_space) {
+ if (is_common_space(ch)) {
+ continue;
+ }
+ pstart = buffer->pos - 1;
+ startline = buffer->line;
+ switch (ch) {
+ case ';':
+ if (args.size() == 0) {
+ srs_error("line %d: unexpected ';'", buffer->line);
+ return ERROR_SYSTEM_CONFIG_INVALID;
+ }
+ return ERROR_SYSTEM_CONFIG_DIRECTIVE;
+ case '{':
+ if (args.size() == 0) {
+ srs_error("line %d: unexpected '{'", buffer->line);
+ return ERROR_SYSTEM_CONFIG_INVALID;
+ }
+ return ERROR_SYSTEM_CONFIG_BLOCK_START;
+ case '}':
+ if (args.size() != 0) {
+ srs_error("line %d: unexpected '}'", buffer->line);
+ return ERROR_SYSTEM_CONFIG_INVALID;
+ }
+ return ERROR_SYSTEM_CONFIG_BLOCK_END;
+ case '#':
+ sharp_comment = 1;
+ continue;
+ case '"':
+ pstart++;
+ d_quoted = true;
+ last_space = 0;
+ continue;
+ case '\'':
+ pstart++;
+ s_quoted = true;
+ last_space = 0;
+ continue;
+ default:
+ last_space = 0;
+ continue;
+ }
+ } else {
+ // last charecter is not space
+ bool found = false;
+ if (d_quoted) {
+ if (ch == '"') {
+ d_quoted = false;
+ need_space = true;
+ found = true;
+ }
+ } else if (s_quoted) {
+ if (ch == '\'') {
+ s_quoted = false;
+ need_space = true;
+ found = true;
+ }
+ } else if (is_common_space(ch) || ch == ';' || ch == '{') {
+ last_space = true;
+ found = 1;
+ }
+
+ if (found) {
+ int len = buffer->pos - pstart;
+ char* word = new char[len];
+ memcpy(word, pstart, len);
+ word[len - 1] = 0;
+
+ std::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;
+}
+
+int SrsConfDirective::refill_buffer(SrsFileBuffer* buffer, bool d_quoted, bool s_quoted, int startline, char*& pstart)
+{
+ int ret = ERROR_SUCCESS;
+
+ if (buffer->pos < buffer->last) {
+ return ret;
+ }
+
+ int size = FILE_SIZE(buffer->fd) - FILE_OFFSET(buffer->fd);
+ if (size > CONF_BUFFER_SIZE) {
+ ret = ERROR_SYSTEM_CONFIG_TOO_LARGE;
+ srs_error("config file too large, max=%d, actual=%d, ret=%d",
+ CONF_BUFFER_SIZE, size, ret);
+ return ret;
+ }
+
+ if (size <= 0) {
+ return ERROR_SYSTEM_CONFIG_EOF;
+ }
+
+ int len = buffer->pos - buffer->start;
+ if (len >= CONF_BUFFER_SIZE) {
+ buffer->line = startline;
+
+ if (!d_quoted && !s_quoted) {
+ srs_error("line %d: too long parameter \"%*s...\" started",
+ buffer->line, 10, buffer->start);
+
+ } else {
+ srs_error("line %d: too long parameter, "
+ "probably missing terminating '%c' character", buffer->line, d_quoted? '"':'\'');
+ }
+ return ERROR_SYSTEM_CONFIG_INVALID;
+ }
+
+ if (len) {
+ memmove(buffer->start, pstart, len);
+ }
+
+ size = srs_min(size, buffer->end - (buffer->start + len));
+ int n = read(buffer->fd, buffer->start + len, size);
+ if (n != size) {
+ srs_error("read file read error. expect %d, actual %d bytes.", size, n);
+ return ERROR_SYSTEM_CONFIG_INVALID;
+ }
+
+ buffer->pos = buffer->start + len;
+ buffer->last = buffer->pos + n;
+ pstart = buffer->start;
+
+ return ret;
+}
+
+SrsConfig* config = new SrsConfig();
+
+SrsConfig::SrsConfig()
+{
+ show_help = false;
+ show_version = false;
+
+ root = new SrsConfDirective();
+ root->conf_line = 0;
+ root->name = "root";
+}
+
+SrsConfig::~SrsConfig()
+{
+ srs_freep(root);
+}
+
+int SrsConfig::reload()
+{
+ int ret = ERROR_SUCCESS;
+
+ SrsConfig conf;
+ if ((ret = conf.parse_file(config_file.c_str())) != ERROR_SUCCESS) {
+ srs_error("config reloader parse file failed. ret=%d", ret);
+ return ret;
+ }
+ srs_info("config reloader parse file success.");
+
+ // store current root to old_root,
+ // and reap the root from conf to current root.
+ SrsConfDirective* old_root = root;
+ SrsAutoFree(SrsConfDirective, old_root, false);
+
+ root = conf.root;
+ conf.root = NULL;
+
+ // merge config.
+ std::vector::iterator it;
+
+ // merge config: listen
+ if (!srs_directive_equals(root->get("listen"), old_root->get("listen"))) {
+ for (it = subscribes.begin(); it != subscribes.end(); ++it) {
+ SrsReloadHandler* subscribe = *it;
+ if ((ret = subscribe->on_reload_listen()) != ERROR_SUCCESS) {
+ srs_error("notify subscribes reload listen failed. ret=%d", ret);
+ return ret;
+ }
+ }
+ srs_trace("reload listen success.");
+ }
+ // merge config: pithy_print
+ if (!srs_directive_equals(root->get("pithy_print"), old_root->get("pithy_print"))) {
+ for (it = subscribes.begin(); it != subscribes.end(); ++it) {
+ SrsReloadHandler* subscribe = *it;
+ if ((ret = subscribe->on_reload_pithy_print()) != ERROR_SUCCESS) {
+ srs_error("notify subscribes pithy_print listen failed. ret=%d", ret);
+ return ret;
+ }
+ }
+ srs_trace("reload pithy_print success.");
+ }
+
+ // TODO: suppor reload hls/forward/ffmpeg/http
+
+ return ret;
+}
+
+void SrsConfig::subscribe(SrsReloadHandler* handler)
+{
+ std::vector::iterator it;
+
+ it = std::find(subscribes.begin(), subscribes.end(), handler);
+ if (it != subscribes.end()) {
+ return;
+ }
+
+ subscribes.push_back(handler);
+}
+
+void SrsConfig::unsubscribe(SrsReloadHandler* handler)
+{
+ std::vector::iterator it;
+
+ it = std::find(subscribes.begin(), subscribes.end(), handler);
+ if (it == subscribes.end()) {
+ return;
+ }
+
+ subscribes.erase(it);
+}
+
+// see: ngx_get_options
+int SrsConfig::parse_options(int argc, char** argv)
+{
+ int ret = ERROR_SUCCESS;
+
+ for (int i = 1; i < argc; i++) {
+ if ((ret = parse_argv(i, argv)) != ERROR_SUCCESS) {
+ return ret;
+ }
+ }
+
+ if (show_help) {
+ print_help(argv);
+ }
+
+ if (show_version) {
+ printf("%s\n", RTMP_SIG_SRS_VERSION);
+ }
+
+ if (show_help || show_version) {
+ exit(0);
+ }
+
+ if (config_file.empty()) {
+ ret = ERROR_SYSTEM_CONFIG_INVALID;
+ srs_error("config file not specified, see help: %s -h, ret=%d", argv[0], ret);
+ return ret;
+ }
+
+ return parse_file(config_file.c_str());
+}
+
+SrsConfDirective* SrsConfig::get_vhost(std::string vhost)
+{
+ srs_assert(root);
+
+ for (int i = 0; i < (int)root->directives.size(); i++) {
+ SrsConfDirective* conf = root->at(i);
+
+ if (conf->name != "vhost") {
+ continue;
+ }
+
+ if (conf->arg0() == vhost) {
+ return conf;
+ }
+ }
+
+ if (vhost != RTMP_VHOST_DEFAULT) {
+ return get_vhost(RTMP_VHOST_DEFAULT);
+ }
+
+ return NULL;
+}
+
+SrsConfDirective* SrsConfig::get_vhost_on_connect(std::string vhost)
+{
+ SrsConfDirective* conf = get_vhost(vhost);
+
+ if (!conf) {
+ return NULL;
+ }
+
+ conf = conf->get("http_hooks");
+ if (!conf) {
+ return NULL;
+ }
+
+ return conf->get("on_connect");
+}
+
+bool SrsConfig::get_vhost_enabled(std::string vhost)
+{
+ SrsConfDirective* vhost_conf = get_vhost(vhost);
+
+ if (!vhost_conf) {
+ return true;
+ }
+
+ SrsConfDirective* conf = vhost_conf->get("enabled");
+ if (!conf) {
+ return true;
+ }
+
+ if (conf->arg0() == "off") {
+ return false;
+ }
+
+ return true;
+}
+
+SrsConfDirective* SrsConfig::get_transcode(std::string vhost, std::string scope)
+{
+ SrsConfDirective* conf = get_vhost(vhost);
+
+ if (!conf) {
+ return NULL;
+ }
+
+ SrsConfDirective* transcode = conf->get("transcode");
+ if (!transcode) {
+ return NULL;
+ }
+
+ if (transcode->arg0() == scope) {
+ return transcode;
+ }
+
+ return NULL;
+}
+
+bool SrsConfig::get_transcode_enabled(SrsConfDirective* transcode)
+{
+ if (!transcode) {
+ return false;
+ }
+
+ SrsConfDirective* conf = transcode->get("enabled");
+ if (!conf || conf->arg0() != "on") {
+ return false;
+ }
+
+ return true;
+}
+
+std::string SrsConfig::get_transcode_ffmpeg(SrsConfDirective* transcode)
+{
+ if (!transcode) {
+ return "";
+ }
+
+ SrsConfDirective* conf = transcode->get("ffmpeg");
+ if (!conf) {
+ return "";
+ }
+
+ return conf->arg0();
+}
+
+void SrsConfig::get_transcode_engines(SrsConfDirective* transcode, std::vector& engines)
+{
+ if (!transcode) {
+ return;
+ }
+
+ for (int i = 0; i < (int)transcode->directives.size(); i++) {
+ SrsConfDirective* conf = transcode->directives[i];
+
+ if (conf->name == "engine") {
+ engines.push_back(conf);
+ }
+ }
+
+ return;
+}
+
+bool SrsConfig::get_engine_enabled(SrsConfDirective* engine)
+{
+ if (!engine) {
+ return false;
+ }
+
+ SrsConfDirective* conf = engine->get("enabled");
+ if (!conf || conf->arg0() != "on") {
+ return false;
+ }
+
+ return true;
+}
+
+std::string SrsConfig::get_engine_vcodec(SrsConfDirective* engine)
+{
+ if (!engine) {
+ return "";
+ }
+
+ SrsConfDirective* conf = engine->get("vcodec");
+ if (!conf) {
+ return "";
+ }
+
+ return conf->arg0();
+}
+
+int SrsConfig::get_engine_vbitrate(SrsConfDirective* engine)
+{
+ if (!engine) {
+ return 0;
+ }
+
+ SrsConfDirective* conf = engine->get("vbitrate");
+ if (!conf) {
+ return 0;
+ }
+
+ return ::atoi(conf->arg0().c_str());
+}
+
+double SrsConfig::get_engine_vfps(SrsConfDirective* engine)
+{
+ if (!engine) {
+ return 0;
+ }
+
+ SrsConfDirective* conf = engine->get("vfps");
+ if (!conf) {
+ return 0;
+ }
+
+ return ::atof(conf->arg0().c_str());
+}
+
+int SrsConfig::get_engine_vwidth(SrsConfDirective* engine)
+{
+ if (!engine) {
+ return 0;
+ }
+
+ SrsConfDirective* conf = engine->get("vwidth");
+ if (!conf) {
+ return 0;
+ }
+
+ return ::atoi(conf->arg0().c_str());
+}
+
+int SrsConfig::get_engine_vheight(SrsConfDirective* engine)
+{
+ if (!engine) {
+ return 0;
+ }
+
+ SrsConfDirective* conf = engine->get("vheight");
+ if (!conf) {
+ return 0;
+ }
+
+ return ::atoi(conf->arg0().c_str());
+}
+
+int SrsConfig::get_engine_vthreads(SrsConfDirective* engine)
+{
+ if (!engine) {
+ return 0;
+ }
+
+ SrsConfDirective* conf = engine->get("vthreads");
+ if (!conf) {
+ return 0;
+ }
+
+ return ::atoi(conf->arg0().c_str());
+}
+
+std::string SrsConfig::get_engine_vprofile(SrsConfDirective* engine)
+{
+ if (!engine) {
+ return "";
+ }
+
+ SrsConfDirective* conf = engine->get("vprofile");
+ if (!conf) {
+ return "";
+ }
+
+ return conf->arg0();
+}
+
+std::string SrsConfig::get_engine_vpreset(SrsConfDirective* engine)
+{
+ if (!engine) {
+ return "";
+ }
+
+ SrsConfDirective* conf = engine->get("vpreset");
+ if (!conf) {
+ return "";
+ }
+
+ return conf->arg0();
+}
+
+void SrsConfig::get_engine_vparams(SrsConfDirective* engine, std::vector& vparams)
+{
+ if (!engine) {
+ return;
+ }
+
+ SrsConfDirective* conf = engine->get("vparams");
+ if (!conf) {
+ return;
+ }
+
+ for (int i = 0; i < (int)conf->directives.size(); i++) {
+ SrsConfDirective* p = conf->directives[i];
+ if (!p) {
+ continue;
+ }
+
+ vparams.push_back("-" + p->name);
+ vparams.push_back(p->arg0());
+ }
+}
+
+void SrsConfig::get_engine_vfilter(SrsConfDirective* engine, std::vector& vfilter)
+{
+ if (!engine) {
+ return;
+ }
+
+ SrsConfDirective* conf = engine->get("vfilter");
+ if (!conf) {
+ return;
+ }
+
+ for (int i = 0; i < (int)conf->directives.size(); i++) {
+ SrsConfDirective* p = conf->directives[i];
+ if (!p) {
+ continue;
+ }
+
+ vfilter.push_back("-" + p->name);
+ vfilter.push_back(p->arg0());
+ }
+}
+
+std::string SrsConfig::get_engine_acodec(SrsConfDirective* engine)
+{
+ if (!engine) {
+ return "";
+ }
+
+ SrsConfDirective* conf = engine->get("acodec");
+ if (!conf) {
+ return "";
+ }
+
+ return conf->arg0();
+}
+
+int SrsConfig::get_engine_abitrate(SrsConfDirective* engine)
+{
+ if (!engine) {
+ return 0;
+ }
+
+ SrsConfDirective* conf = engine->get("abitrate");
+ if (!conf) {
+ return 0;
+ }
+
+ return ::atoi(conf->arg0().c_str());
+}
+
+int SrsConfig::get_engine_asample_rate(SrsConfDirective* engine)
+{
+ if (!engine) {
+ return 0;
+ }
+
+ SrsConfDirective* conf = engine->get("asample_rate");
+ if (!conf) {
+ return 0;
+ }
+
+ return ::atoi(conf->arg0().c_str());
+}
+
+int SrsConfig::get_engine_achannels(SrsConfDirective* engine)
+{
+ if (!engine) {
+ return 0;
+ }
+
+ SrsConfDirective* conf = engine->get("achannels");
+ if (!conf) {
+ return 0;
+ }
+
+ return ::atoi(conf->arg0().c_str());
+}
+
+void SrsConfig::get_engine_aparams(SrsConfDirective* engine, std::vector& aparams)
+{
+ if (!engine) {
+ return;
+ }
+
+ SrsConfDirective* conf = engine->get("aparams");
+ if (!conf) {
+ return;
+ }
+
+ for (int i = 0; i < (int)conf->directives.size(); i++) {
+ SrsConfDirective* p = conf->directives[i];
+ if (!p) {
+ continue;
+ }
+
+ aparams.push_back("-" + p->name);
+ aparams.push_back(p->arg0());
+ }
+}
+
+std::string SrsConfig::get_engine_output(SrsConfDirective* engine)
+{
+ if (!engine) {
+ return "";
+ }
+
+ SrsConfDirective* conf = engine->get("output");
+ if (!conf) {
+ return "";
+ }
+
+ return conf->arg0();
+}
+
+std::string SrsConfig::get_log_dir()
+{
+ srs_assert(root);
+
+ SrsConfDirective* conf = root->get("log_dir");
+ if (!conf || conf->arg0().empty()) {
+ return "./objs/logs";
+ }
+
+ return conf->arg0();
+}
+
+int SrsConfig::get_max_connections()
+{
+ srs_assert(root);
+
+ SrsConfDirective* conf = root->get("max_connections");
+ if (!conf || conf->arg0().empty()) {
+ return 2000;
+ }
+
+ return ::atoi(conf->arg0().c_str());
+}
+
+SrsConfDirective* SrsConfig::get_gop_cache(std::string vhost)
+{
+ SrsConfDirective* conf = get_vhost(vhost);
+
+ if (!conf) {
+ return NULL;
+ }
+
+ return conf->get("gop_cache");
+}
+
+SrsConfDirective* SrsConfig::get_forward(std::string vhost)
+{
+ SrsConfDirective* conf = get_vhost(vhost);
+
+ if (!conf) {
+ return NULL;
+ }
+
+ return conf->get("forward");
+}
+
+SrsConfDirective* SrsConfig::get_hls(std::string vhost)
+{
+ SrsConfDirective* conf = get_vhost(vhost);
+
+ if (!conf) {
+ return NULL;
+ }
+
+ return conf->get("hls");
+}
+
+bool SrsConfig::get_hls_enabled(std::string vhost)
+{
+ SrsConfDirective* hls = get_hls(vhost);
+
+ if (!hls) {
+ return true;
+ }
+
+ if (hls->arg0() == "off") {
+ return false;
+ }
+
+ return true;
+}
+
+SrsConfDirective* SrsConfig::get_hls_path(std::string vhost)
+{
+ SrsConfDirective* conf = get_vhost(vhost);
+
+ if (!conf) {
+ return NULL;
+ }
+
+ return conf->get("hls_path");
+}
+
+SrsConfDirective* SrsConfig::get_hls_fragment(std::string vhost)
+{
+ SrsConfDirective* conf = get_vhost(vhost);
+
+ if (!conf) {
+ return NULL;
+ }
+
+ return conf->get("hls_fragment");
+}
+
+SrsConfDirective* SrsConfig::get_hls_window(std::string vhost)
+{
+ SrsConfDirective* conf = get_vhost(vhost);
+
+ if (!conf) {
+ return NULL;
+ }
+
+ return conf->get("hls_window");
+}
+
+SrsConfDirective* SrsConfig::get_refer(std::string vhost)
+{
+ SrsConfDirective* conf = get_vhost(vhost);
+
+ if (!conf) {
+ return NULL;
+ }
+
+ return conf->get("refer");
+}
+
+SrsConfDirective* SrsConfig::get_refer_play(std::string vhost)
+{
+ SrsConfDirective* conf = get_vhost(vhost);
+
+ if (!conf) {
+ return NULL;
+ }
+
+ return conf->get("refer_play");
+}
+
+SrsConfDirective* SrsConfig::get_refer_publish(std::string vhost)
+{
+ SrsConfDirective* conf = get_vhost(vhost);
+
+ if (!conf) {
+ return NULL;
+ }
+
+ return conf->get("refer_publish");
+}
+
+SrsConfDirective* SrsConfig::get_listen()
+{
+ return root->get("listen");
+}
+
+SrsConfDirective* SrsConfig::get_chunk_size()
+{
+ return root->get("chunk_size");
+}
+
+SrsConfDirective* SrsConfig::get_pithy_print_publish()
+{
+ SrsConfDirective* pithy = root->get("pithy_print");
+ if (!pithy) {
+ return NULL;
+ }
+
+ return pithy->get("publish");
+}
+
+SrsConfDirective* SrsConfig::get_pithy_print_forwarder()
+{
+ SrsConfDirective* pithy = root->get("pithy_print");
+ if (!pithy) {
+ return NULL;
+ }
+
+ return pithy->get("forwarder");
+}
+
+SrsConfDirective* SrsConfig::get_pithy_print_hls()
+{
+ SrsConfDirective* pithy = root->get("pithy_print");
+ if (!pithy) {
+ return NULL;
+ }
+
+ return pithy->get("hls");
+}
+
+SrsConfDirective* SrsConfig::get_pithy_print_encoder()
+{
+ SrsConfDirective* pithy = root->get("encoder");
+ if (!pithy) {
+ return NULL;
+ }
+
+ return pithy->get("forwarder");
+}
+
+SrsConfDirective* SrsConfig::get_pithy_print_play()
+{
+ SrsConfDirective* pithy = root->get("pithy_print");
+ if (!pithy) {
+ return NULL;
+ }
+
+ return pithy->get("play");
+}
+
+int SrsConfig::parse_file(const char* filename)
+{
+ int ret = ERROR_SUCCESS;
+
+ config_file = filename;
+
+ if (config_file.empty()) {
+ return ERROR_SYSTEM_CONFIG_INVALID;
+ }
+
+ if ((ret = root->parse(config_file.c_str())) != ERROR_SUCCESS) {
+ return ret;
+ }
+
+ SrsConfDirective* conf = NULL;
+ if ((conf = get_listen()) == NULL || conf->args.size() == 0) {
+ ret = ERROR_SYSTEM_CONFIG_INVALID;
+ srs_error("line %d: conf error, "
+ "directive \"listen\" is empty, ret=%d", (conf? conf->conf_line:0), ret);
+ return ret;
+ }
+ // TODO: check the hls.
+ // TODO: check other config.
+ // TODO: check hls.
+ // TODO: check ssl.
+ // TODO: check ffmpeg.
+ // 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
+ " Copyright (c) 2013 winlin\n"
+ "Contributors: "RTMP_SIG_SRS_CONTRIBUTOR"\n"
+ "Build: "SRS_BUILD_DATE" Configuration: "SRS_CONFIGURE"\n"
+ "Usage: %s [-h?vV] [-c ]\n"
+ "\n"
+ "Options:\n"
+ " -?-h : show help\n"
+ " -v-V : show version and exit\n"
+ " -c filename : set configuration file\n"
+ "\n"
+ RTMP_SIG_SRS_WEB"\n"
+ RTMP_SIG_SRS_URL"\n"
+ "Email: "RTMP_SIG_SRS_EMAIL"\n"
+ "\n",
+ argv[0]);
+}
+
+bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b)
+{
+ if (!a || !b) {
+ return false;
+ }
+
+ if (a->name != b->name) {
+ return false;
+ }
+
+ if (a->args.size() != b->args.size()) {
+ return false;
+ }
+
+ for (int i = 0; i < (int)a->args.size(); i++) {
+ if (a->args.at(i) != b->args.at(i)) {
+ return false;
+ }
+ }
+
+ if (a->directives.size() != b->directives.size()) {
+ return false;
+ }
+
+ for (int i = 0; i < (int)a->directives.size(); i++) {
+ SrsConfDirective* a0 = a->at(i);
+ SrsConfDirective* b0 = b->at(i);
+
+ if (!srs_directive_equals(a0, b0)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
diff --git a/trunk/src/core/srs_core_config.hpp b/trunk/src/core/srs_core_config.hpp
old mode 100644
new mode 100755
index 4b4c6a893..6dda5a53a
--- a/trunk/src/core/srs_core_config.hpp
+++ b/trunk/src/core/srs_core_config.hpp
@@ -1,177 +1,177 @@
-/*
-The MIT License (MIT)
-
-Copyright (c) 2013 winlin
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-#ifndef SRS_CORE_CONIFG_HPP
-#define SRS_CORE_CONIFG_HPP
-
-/*
-#include
-*/
-#include
-
-#include
-#include
-
-#include
-
-// default vhost for rtmp
-#define RTMP_VHOST_DEFAULT "__defaultVhost__"
-
-#define SRS_LOCALHOST "127.0.0.1"
-#define RTMP_DEFAULT_PORT 1935
-#define RTMP_DEFAULT_PORTS "1935"
-
-#define SRS_CONF_DEFAULT_HLS_PATH "./objs/nginx/html"
-#define SRS_CONF_DEFAULT_HLS_FRAGMENT 10
-#define SRS_CONF_DEFAULT_HLS_WINDOW 60
-// in ms, for HLS aac sync time.
-#define SRS_CONF_DEFAULT_AAC_SYNC 100
-// in ms, for HLS aac flush the audio
-#define SRS_CONF_DEFAULT_AAC_DELAY 300
-
-class SrsFileBuffer
-{
-public:
- int fd;
- int line;
- // start of buffer.
- char* start;
- // end of buffer.
- char* end;
- // current consumed position.
- char* pos;
- // last available position.
- char* last;
-
- SrsFileBuffer();
- virtual ~SrsFileBuffer();
- virtual int open(const char* filename);
-};
-
-class SrsConfDirective
-{
-public:
- int conf_line;
- std::string name;
- std::vector args;
- std::vector directives;
-public:
- SrsConfDirective();
- virtual ~SrsConfDirective();
- std::string arg0();
- std::string arg1();
- std::string arg2();
- SrsConfDirective* at(int index);
- SrsConfDirective* get(std::string _name);
-public:
- virtual int parse(const char* filename);
-public:
- enum SrsDirectiveType{parse_file, parse_block};
- virtual int parse_conf(SrsFileBuffer* buffer, SrsDirectiveType type);
- virtual int read_token(SrsFileBuffer* buffer, std::vector& args);
- virtual int refill_buffer(SrsFileBuffer* buffer, bool d_quoted, bool s_quoted, int startline, char*& pstart);
-};
-
-/**
-* the config parser.
-* for the config supports reload, so never keep the reference cross st-thread,
-* that is, never save the SrsConfDirective* get by any api of config,
-* for it maybe free in the reload st-thread cycle.
-* you can keep it before st-thread switch, or simply never keep it.
-*/
-class SrsConfig
-{
-private:
- bool show_help;
- bool show_version;
- std::string config_file;
- SrsConfDirective* root;
- std::vector subscribes;
-public:
- SrsConfig();
- virtual ~SrsConfig();
-public:
- virtual int reload();
- virtual void subscribe(SrsReloadHandler* handler);
- virtual void unsubscribe(SrsReloadHandler* handler);
-public:
- virtual int parse_options(int argc, char** argv);
-public:
- virtual SrsConfDirective* get_vhost(std::string vhost);
- virtual bool get_vhost_enabled(std::string vhost);
- virtual std::string get_vhost_on_connect(std::string vhost);
- virtual SrsConfDirective* get_transcode(std::string vhost, std::string scope);
- virtual bool get_transcode_enabled(SrsConfDirective* transcode);
- virtual std::string get_transcode_ffmpeg(SrsConfDirective* transcode);
- virtual void get_transcode_engines(SrsConfDirective* transcode, std::vector& engines);
- virtual bool get_engine_enabled(SrsConfDirective* engine);
- virtual std::string get_engine_vcodec(SrsConfDirective* engine);
- virtual int get_engine_vbitrate(SrsConfDirective* engine);
- virtual double get_engine_vfps(SrsConfDirective* engine);
- virtual int get_engine_vwidth(SrsConfDirective* engine);
- virtual int get_engine_vheight(SrsConfDirective* engine);
- virtual int get_engine_vthreads(SrsConfDirective* engine);
- virtual std::string get_engine_vprofile(SrsConfDirective* engine);
- virtual std::string get_engine_vpreset(SrsConfDirective* engine);
- virtual void get_engine_vparams(SrsConfDirective* engine, std::vector& vparams);
- virtual void get_engine_vfilter(SrsConfDirective* engine, std::vector& vfilter);
- virtual std::string get_engine_acodec(SrsConfDirective* engine);
- virtual int get_engine_abitrate(SrsConfDirective* engine);
- virtual int get_engine_asample_rate(SrsConfDirective* engine);
- virtual int get_engine_achannels(SrsConfDirective* engine);
- virtual void get_engine_aparams(SrsConfDirective* engine, std::vector& aparams);
- virtual std::string get_engine_output(SrsConfDirective* engine);
- virtual std::string get_log_dir();
- virtual int get_max_connections();
- virtual SrsConfDirective* get_gop_cache(std::string vhost);
- virtual SrsConfDirective* get_forward(std::string vhost);
- virtual SrsConfDirective* get_hls(std::string vhost);
- virtual bool get_hls_enabled(std::string vhost);
- virtual SrsConfDirective* get_hls_path(std::string vhost);
- virtual SrsConfDirective* get_hls_fragment(std::string vhost);
- virtual SrsConfDirective* get_hls_window(std::string vhost);
- virtual SrsConfDirective* get_refer(std::string vhost);
- virtual SrsConfDirective* get_refer_play(std::string vhost);
- virtual SrsConfDirective* get_refer_publish(std::string vhost);
- virtual SrsConfDirective* get_listen();
- virtual SrsConfDirective* get_chunk_size();
- virtual SrsConfDirective* get_pithy_print_publish();
- virtual SrsConfDirective* get_pithy_print_forwarder();
- virtual SrsConfDirective* get_pithy_print_encoder();
- virtual SrsConfDirective* get_pithy_print_hls();
- virtual SrsConfDirective* get_pithy_print_play();
-private:
- virtual int parse_file(const char* filename);
- virtual int parse_argv(int& i, char** argv);
- virtual void print_help(char** argv);
-};
-
-/**
-* deep compare directive.
-*/
-bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b);
-
-// global config
-extern SrsConfig* config;
-
+/*
+The MIT License (MIT)
+
+Copyright (c) 2013 winlin
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef SRS_CORE_CONIFG_HPP
+#define SRS_CORE_CONIFG_HPP
+
+/*
+#include
+*/
+#include
+
+#include
+#include
+
+#include
+
+// default vhost for rtmp
+#define RTMP_VHOST_DEFAULT "__defaultVhost__"
+
+#define SRS_LOCALHOST "127.0.0.1"
+#define RTMP_DEFAULT_PORT 1935
+#define RTMP_DEFAULT_PORTS "1935"
+
+#define SRS_CONF_DEFAULT_HLS_PATH "./objs/nginx/html"
+#define SRS_CONF_DEFAULT_HLS_FRAGMENT 10
+#define SRS_CONF_DEFAULT_HLS_WINDOW 60
+// in ms, for HLS aac sync time.
+#define SRS_CONF_DEFAULT_AAC_SYNC 100
+// in ms, for HLS aac flush the audio
+#define SRS_CONF_DEFAULT_AAC_DELAY 300
+
+class SrsFileBuffer
+{
+public:
+ int fd;
+ int line;
+ // start of buffer.
+ char* start;
+ // end of buffer.
+ char* end;
+ // current consumed position.
+ char* pos;
+ // last available position.
+ char* last;
+
+ SrsFileBuffer();
+ virtual ~SrsFileBuffer();
+ virtual int open(const char* filename);
+};
+
+class SrsConfDirective
+{
+public:
+ int conf_line;
+ std::string name;
+ std::vector args;
+ std::vector directives;
+public:
+ SrsConfDirective();
+ virtual ~SrsConfDirective();
+ std::string arg0();
+ std::string arg1();
+ std::string arg2();
+ SrsConfDirective* at(int index);
+ SrsConfDirective* get(std::string _name);
+public:
+ virtual int parse(const char* filename);
+public:
+ enum SrsDirectiveType{parse_file, parse_block};
+ virtual int parse_conf(SrsFileBuffer* buffer, SrsDirectiveType type);
+ virtual int read_token(SrsFileBuffer* buffer, std::vector& args);
+ virtual int refill_buffer(SrsFileBuffer* buffer, bool d_quoted, bool s_quoted, int startline, char*& pstart);
+};
+
+/**
+* the config parser.
+* for the config supports reload, so never keep the reference cross st-thread,
+* that is, never save the SrsConfDirective* get by any api of config,
+* for it maybe free in the reload st-thread cycle.
+* you can keep it before st-thread switch, or simply never keep it.
+*/
+class SrsConfig
+{
+private:
+ bool show_help;
+ bool show_version;
+ std::string config_file;
+ SrsConfDirective* root;
+ std::vector subscribes;
+public:
+ SrsConfig();
+ virtual ~SrsConfig();
+public:
+ virtual int reload();
+ virtual void subscribe(SrsReloadHandler* handler);
+ virtual void unsubscribe(SrsReloadHandler* handler);
+public:
+ virtual int parse_options(int argc, char** argv);
+public:
+ virtual SrsConfDirective* get_vhost(std::string vhost);
+ virtual bool get_vhost_enabled(std::string vhost);
+ virtual SrsConfDirective* get_vhost_on_connect(std::string vhost);
+ virtual SrsConfDirective* get_transcode(std::string vhost, std::string scope);
+ virtual bool get_transcode_enabled(SrsConfDirective* transcode);
+ virtual std::string get_transcode_ffmpeg(SrsConfDirective* transcode);
+ virtual void get_transcode_engines(SrsConfDirective* transcode, std::vector& engines);
+ virtual bool get_engine_enabled(SrsConfDirective* engine);
+ virtual std::string get_engine_vcodec(SrsConfDirective* engine);
+ virtual int get_engine_vbitrate(SrsConfDirective* engine);
+ virtual double get_engine_vfps(SrsConfDirective* engine);
+ virtual int get_engine_vwidth(SrsConfDirective* engine);
+ virtual int get_engine_vheight(SrsConfDirective* engine);
+ virtual int get_engine_vthreads(SrsConfDirective* engine);
+ virtual std::string get_engine_vprofile(SrsConfDirective* engine);
+ virtual std::string get_engine_vpreset(SrsConfDirective* engine);
+ virtual void get_engine_vparams(SrsConfDirective* engine, std::vector& vparams);
+ virtual void get_engine_vfilter(SrsConfDirective* engine, std::vector& vfilter);
+ virtual std::string get_engine_acodec(SrsConfDirective* engine);
+ virtual int get_engine_abitrate(SrsConfDirective* engine);
+ virtual int get_engine_asample_rate(SrsConfDirective* engine);
+ virtual int get_engine_achannels(SrsConfDirective* engine);
+ virtual void get_engine_aparams(SrsConfDirective* engine, std::vector& aparams);
+ virtual std::string get_engine_output(SrsConfDirective* engine);
+ virtual std::string get_log_dir();
+ virtual int get_max_connections();
+ virtual SrsConfDirective* get_gop_cache(std::string vhost);
+ virtual SrsConfDirective* get_forward(std::string vhost);
+ virtual SrsConfDirective* get_hls(std::string vhost);
+ virtual bool get_hls_enabled(std::string vhost);
+ virtual SrsConfDirective* get_hls_path(std::string vhost);
+ virtual SrsConfDirective* get_hls_fragment(std::string vhost);
+ virtual SrsConfDirective* get_hls_window(std::string vhost);
+ virtual SrsConfDirective* get_refer(std::string vhost);
+ virtual SrsConfDirective* get_refer_play(std::string vhost);
+ virtual SrsConfDirective* get_refer_publish(std::string vhost);
+ virtual SrsConfDirective* get_listen();
+ virtual SrsConfDirective* get_chunk_size();
+ virtual SrsConfDirective* get_pithy_print_publish();
+ virtual SrsConfDirective* get_pithy_print_forwarder();
+ virtual SrsConfDirective* get_pithy_print_encoder();
+ virtual SrsConfDirective* get_pithy_print_hls();
+ virtual SrsConfDirective* get_pithy_print_play();
+private:
+ virtual int parse_file(const char* filename);
+ virtual int parse_argv(int& i, char** argv);
+ virtual void print_help(char** argv);
+};
+
+/**
+* deep compare directive.
+*/
+bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b);
+
+// global config
+extern SrsConfig* config;
+
#endif
\ No newline at end of file