diff --git a/README.md b/README.md
index 42e747cea..d0c54d209 100755
--- a/README.md
+++ b/README.md
@@ -47,14 +47,16 @@ m3u8 url: http://127.0.0.1:80/live/livestream.m3u8
8. support cache last gop for flash player to fast startup.
9. support listen at multiple ports.
10. support long time(>4.6hours) publish/play.
-11. [dev] support forward publish stream to build active-standby cluster.
-12. [plan] support live stream transcoding by ffmpeg.
-13. [plan] support full http callback api.
-14. [plan] support network based cli and json result.
-15. [plan] support bandwidth test api and flash client.
-16. no edge server, origin server only.
-17. no vod streaming, live streaming only.
-18. no multiple processes, single process only.
+11. high performace, 1800 connections(500kbps), 900Mbps, CPU 90.2%, 41MB
+12. support forward publish stream to build active-standby cluster.
+13. support broadcast by forward the stream to other servers(origin/edge).
+14. [plan] support live stream transcoding by ffmpeg.
+15. [plan] support full http callback api.
+16. [plan] support network based cli and json result.
+17. [plan] support bandwidth test api and flash client.
+18. no edge server, origin server only.
+19. no vod streaming, live streaming only.
+20. no multiple processes, single process only.
### Performance
1. 300 connections, 150Mbps, 500kbps, CPU 18.8%, 5956KB.
diff --git a/trunk/conf/srs.conf b/trunk/conf/srs.conf
index 66525035f..8230d7e00 100755
--- a/trunk/conf/srs.conf
+++ b/trunk/conf/srs.conf
@@ -15,7 +15,7 @@ vhost __defaultVhost__ {
hls_path ./objs/nginx/html;
hls_fragment 5;
hls_window 30;
- forward 192.168.1.50;
+ forward 127.0.0.1:1936;
}
# the vhost which forward publish streams.
vhost forward.vhost.com {
@@ -103,6 +103,9 @@ pithy_print {
publish 2000;
# shared print interval for all play clients, in milliseconds.
# if not specified, set to 1300.
- play 3000;
+ play 3000;
+ # shared print interval for all forwarders, in milliseconds.
+ # if not specified, set to 2000.
+ forwarder 3000;
}
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 a930a1507..ead88b892
--- a/trunk/src/core/srs_core_client.cpp
+++ b/trunk/src/core/srs_core_client.cpp
@@ -1,524 +1,524 @@
-/*
-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
-
-#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();
-}
-
-SrsClient::~SrsClient()
-{
- srs_freepa(ip);
- srs_freep(req);
- srs_freep(res);
- srs_freep(rtmp);
- srs_freep(refer);
-}
-
-// 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;
- }
-
- SrsConfDirective* conf = NULL;
- if ((conf = config->get_vhost_enabled(req->vhost)) != NULL && conf->arg0() != "on") {
- 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();
- }
-
- 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("-> clock=%u, time=%"PRId64", cmr=%d, msgs=%d, obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d",
- (int)(srs_get_system_time_ms()/1000), 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->vhost, req->app, req->stream)) != 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("<- clock=%u, time=%"PRId64", obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d",
- (int)(srs_get_system_time_ms()/1000), 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
+
+#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();
+}
+
+SrsClient::~SrsClient()
+{
+ srs_freepa(ip);
+ srs_freep(req);
+ srs_freep(res);
+ srs_freep(rtmp);
+ srs_freep(refer);
+}
+
+// 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;
+ }
+
+ SrsConfDirective* conf = NULL;
+ if ((conf = config->get_vhost_enabled(req->vhost)) != NULL && conf->arg0() != "on") {
+ 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();
+ }
+
+ 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("-> clock=%u, time=%"PRId64", cmr=%d, msgs=%d, obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d",
+ (int)(srs_get_system_time_ms()/1000), 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->vhost, req->app, req->stream)) != 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("<- clock=%u, time=%"PRId64", obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d",
+ (int)(srs_get_system_time_ms()/1000), 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 dcce32f02..4c0de37b4
--- a/trunk/src/core/srs_core_config.cpp
+++ b/trunk/src/core/srs_core_config.cpp
@@ -1,822 +1,832 @@
-/*
-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 4096
-
-SrsFileBuffer::SrsFileBuffer()
-{
- fd = -1;
- line = 0;
-
- pos = last = start = new char[CONF_BUFFER_SIZE];
- end = start + CONF_BUFFER_SIZE;
-}
-
-SrsFileBuffer::~SrsFileBuffer()
-{
- if (fd > 0) {
- close(fd);
- }
- srs_freepa(start);
-}
-
-int SrsFileBuffer::open(const char* filename)
-{
- assert(fd == -1);
-
- if ((fd = ::open(filename, O_RDONLY, 0)) < 0) {
- 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 <= 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.");
- }
-
- 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_enabled(std::string vhost)
-{
- SrsConfDirective* conf = get_vhost(vhost);
-
- if (!conf) {
- return NULL;
- }
-
- return conf->get("enabled");
-}
-
-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");
-}
-
-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_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.
-
- 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"
- "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 4096
+
+SrsFileBuffer::SrsFileBuffer()
+{
+ fd = -1;
+ line = 0;
+
+ pos = last = start = new char[CONF_BUFFER_SIZE];
+ end = start + CONF_BUFFER_SIZE;
+}
+
+SrsFileBuffer::~SrsFileBuffer()
+{
+ if (fd > 0) {
+ close(fd);
+ }
+ srs_freepa(start);
+}
+
+int SrsFileBuffer::open(const char* filename)
+{
+ assert(fd == -1);
+
+ if ((fd = ::open(filename, O_RDONLY, 0)) < 0) {
+ 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 <= 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.");
+ }
+
+ 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_enabled(std::string vhost)
+{
+ SrsConfDirective* conf = get_vhost(vhost);
+
+ if (!conf) {
+ return NULL;
+ }
+
+ return conf->get("enabled");
+}
+
+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");
+}
+
+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_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.
+
+ 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"
+ "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 8c27c1c27..4e2bc6b58
--- a/trunk/src/core/srs_core_config.hpp
+++ b/trunk/src/core/srs_core_config.hpp
@@ -1,144 +1,145 @@
-/*
-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_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);
- virtual SrsConfDirective* get_vhost(std::string vhost);
- virtual SrsConfDirective* get_vhost_enabled(std::string vhost);
- virtual SrsConfDirective* get_gop_cache(std::string vhost);
- virtual SrsConfDirective* get_forward(std::string vhost);
- virtual SrsConfDirective* get_hls(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_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_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);
+ virtual SrsConfDirective* get_vhost(std::string vhost);
+ virtual SrsConfDirective* get_vhost_enabled(std::string vhost);
+ virtual SrsConfDirective* get_gop_cache(std::string vhost);
+ virtual SrsConfDirective* get_forward(std::string vhost);
+ virtual SrsConfDirective* get_hls(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_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
diff --git a/trunk/src/core/srs_core_forward.cpp b/trunk/src/core/srs_core_forward.cpp
old mode 100644
new mode 100755
index f3b3e03f2..dce9a4b21
--- a/trunk/src/core/srs_core_forward.cpp
+++ b/trunk/src/core/srs_core_forward.cpp
@@ -1,277 +1,350 @@
-/*
-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
-
-#define SRS_FORWARDER_SLEEP_MS 2000
-#define SRS_SEND_TIMEOUT_US 3000000L
-#define SRS_RECV_TIMEOUT_US SRS_SEND_TIMEOUT_US
-
-SrsForwarder::SrsForwarder()
-{
- client = NULL;
- tid = NULL;
- stfd = NULL;
- loop = false;
- stream_id = 0;
-}
-
-SrsForwarder::~SrsForwarder()
-{
- on_unpublish();
-}
-
-int SrsForwarder::on_publish(std::string vhost, std::string _app, std::string stream, std::string forward_server)
-{
- int ret = ERROR_SUCCESS;
-
- app = _app;
-
- tc_url = "rtmp://";
- tc_url += vhost;
- tc_url += "/";
- tc_url += app;
-
- stream_name = stream;
- server = forward_server;
- port = 1935;
-
- size_t pos = forward_server.find(":");
- if (pos != std::string::npos) {
- port = ::atoi(forward_server.substr(pos + 1).c_str());
- server = forward_server.substr(0, pos);
- }
-
- if ((ret = open_socket()) != ERROR_SUCCESS) {
- return ret;
- }
-
- srs_assert(!tid);
- if((tid = st_thread_create(forward_thread, this, 1, 0)) == NULL){
- ret = ERROR_ST_CREATE_FORWARD_THREAD;
- srs_error("st_thread_create failed. ret=%d", ret);
- return ret;
- }
-
- return ret;
-}
-
-void SrsForwarder::on_unpublish()
-{
- if (tid) {
- loop = false;
- st_thread_interrupt(tid);
- st_thread_join(tid, NULL);
- tid = NULL;
- }
-
- if (stfd) {
- int fd = st_netfd_fileno(stfd);
- st_netfd_close(stfd);
- stfd = NULL;
-
- // st does not close it sometimes,
- // close it manually.
- close(fd);
- }
-
- srs_freep(client);
-}
-
-int SrsForwarder::on_meta_data(SrsSharedPtrMessage* metadata)
-{
- int ret = ERROR_SUCCESS;
- return ret;
-}
-
-int SrsForwarder::on_audio(SrsSharedPtrMessage* msg)
-{
- int ret = ERROR_SUCCESS;
- return ret;
-}
-
-int SrsForwarder::on_video(SrsSharedPtrMessage* msg)
-{
- int ret = ERROR_SUCCESS;
- return ret;
-}
-
-int SrsForwarder::open_socket()
-{
- int ret = ERROR_SUCCESS;
-
- srs_trace("forward stream=%s, tcUrl=%s to server=%s, port=%d",
- stream_name.c_str(), tc_url.c_str(), server.c_str(), port);
-
- int sock = socket(AF_INET, SOCK_STREAM, 0);
- if(sock == -1){
- ret = ERROR_SOCKET_CREATE;
- srs_error("create socket error. ret=%d", ret);
- return ret;
- }
-
- stfd = st_netfd_open_socket(sock);
- if(stfd == NULL){
- ret = ERROR_ST_OPEN_SOCKET;
- srs_error("st_netfd_open_socket failed. ret=%d", ret);
- return ret;
- }
-
- srs_freep(client);
- client = new SrsRtmpClient(stfd);
-
- return ret;
-}
-
-int SrsForwarder::connect_server()
-{
- int ret = ERROR_SUCCESS;
-
- std::string ip = parse_server(server);
- if (ip.empty()) {
- ret = ERROR_SYSTEM_IP_INVALID;
- srs_error("dns resolve server error, ip empty. ret=%d", ret);
- return ret;
- }
-
- sockaddr_in addr;
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- addr.sin_addr.s_addr = inet_addr(ip.c_str());
-
- if (st_connect(stfd, (const struct sockaddr*)&addr, sizeof(sockaddr_in), ST_UTIME_NO_TIMEOUT) == -1){
- ret = ERROR_ST_CONNECT;
- srs_error("connect to server error. ip=%s, port=%d, ret=%d", ip.c_str(), port, ret);
- return ret;
- }
- srs_trace("connect to server success. server=%s, ip=%s, port=%d", server.c_str(), ip.c_str(), port);
-
- return ret;
-}
-
-std::string SrsForwarder::parse_server(std::string host)
-{
- if (inet_addr(host.c_str()) != INADDR_NONE) {
- return host;
- }
-
- hostent* answer = gethostbyname(host.c_str());
- if (answer == NULL) {
- srs_error("dns resolve host %s error.", host.c_str());
- return "";
- }
-
- char ipv4[16];
- memset(ipv4, 0, sizeof(ipv4));
- for (int i = 0; i < answer->h_length; i++) {
- inet_ntop(AF_INET, answer->h_addr_list[i], ipv4, sizeof(ipv4));
- srs_info("dns resolve host %s to %s.", host.c_str(), ipv4);
- break;
- }
-
- return ipv4;
-}
-
-int SrsForwarder::forward_cycle_imp()
-{
- int ret = ERROR_SUCCESS;
-
- client->set_recv_timeout(SRS_RECV_TIMEOUT_US);
- client->set_send_timeout(SRS_SEND_TIMEOUT_US);
-
- if ((ret = connect_server()) != ERROR_SUCCESS) {
- return ret;
- }
- srs_assert(client);
-
- if ((ret = client->handshake()) != ERROR_SUCCESS) {
- srs_error("handshake with server failed. ret=%d", ret);
- return ret;
- }
- if ((ret = client->connect_app(app, tc_url)) != ERROR_SUCCESS) {
- srs_error("connect with server failed, tcUrl=%s. ret=%d", tc_url.c_str(), ret);
- return ret;
- }
- if ((ret = client->create_stream(stream_id)) != ERROR_SUCCESS) {
- srs_error("connect with server failed, stream_id=%d. ret=%d", stream_id, ret);
- return ret;
- }
- if ((ret = client->publish(stream_name, stream_id)) != ERROR_SUCCESS) {
- srs_error("connect with server failed, stream_name=%s, stream_id=%d. ret=%d",
- stream_name.c_str(), stream_id, ret);
- return ret;
- }
-
- return ret;
-}
-
-void SrsForwarder::forward_cycle()
-{
- int ret = ERROR_SUCCESS;
-
- log_context->generate_id();
- srs_trace("forward cycle start");
-
- while (loop) {
- if ((ret = forward_cycle_imp()) != ERROR_SUCCESS) {
- srs_warn("forward cycle failed, ignored and retry, ret=%d", ret);
- } else {
- srs_info("forward cycle success, retry");
- }
-
- if (!loop) {
- break;
- }
-
- st_usleep(SRS_FORWARDER_SLEEP_MS * 1000);
-
- if ((ret = open_socket()) != ERROR_SUCCESS) {
- srs_warn("forward cycle reopen failed, ignored and retry, ret=%d", ret);
- } else {
- srs_info("forward cycle reopen success");
- }
- }
- srs_trace("forward cycle finished");
-}
-
-void* SrsForwarder::forward_thread(void* arg)
-{
- SrsForwarder* obj = (SrsForwarder*)arg;
- srs_assert(obj != NULL);
-
- obj->loop = true;
- obj->forward_cycle();
-
- return NULL;
-}
-
+/*
+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
+
+#define SRS_PULSE_TIMEOUT_MS 100
+#define SRS_FORWARDER_SLEEP_MS 2000
+#define SRS_SEND_TIMEOUT_US 3000000L
+#define SRS_RECV_TIMEOUT_US SRS_SEND_TIMEOUT_US
+
+SrsForwarder::SrsForwarder()
+{
+ client = NULL;
+ tid = NULL;
+ stfd = NULL;
+ loop = false;
+ stream_id = 0;
+}
+
+SrsForwarder::~SrsForwarder()
+{
+ on_unpublish();
+
+ std::vector::iterator it;
+ for (it = msgs.begin(); it != msgs.end(); ++it) {
+ SrsSharedPtrMessage* msg = *it;
+ srs_freep(msg);
+ }
+ msgs.clear();
+}
+
+int SrsForwarder::on_publish(std::string vhost, std::string _app, std::string stream, std::string forward_server)
+{
+ int ret = ERROR_SUCCESS;
+
+ app = _app;
+
+ tc_url = "rtmp://";
+ tc_url += vhost;
+ tc_url += "/";
+ tc_url += app;
+
+ stream_name = stream;
+ server = forward_server;
+ port = 1935;
+
+ size_t pos = forward_server.find(":");
+ if (pos != std::string::npos) {
+ port = ::atoi(forward_server.substr(pos + 1).c_str());
+ server = forward_server.substr(0, pos);
+ }
+
+ if ((ret = open_socket()) != ERROR_SUCCESS) {
+ return ret;
+ }
+
+ srs_assert(!tid);
+ if((tid = st_thread_create(forward_thread, this, 1, 0)) == NULL){
+ ret = ERROR_ST_CREATE_FORWARD_THREAD;
+ srs_error("st_thread_create failed. ret=%d", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+void SrsForwarder::on_unpublish()
+{
+ if (tid) {
+ loop = false;
+ st_thread_interrupt(tid);
+ st_thread_join(tid, NULL);
+ tid = NULL;
+ }
+
+ if (stfd) {
+ int fd = st_netfd_fileno(stfd);
+ st_netfd_close(stfd);
+ stfd = NULL;
+
+ // st does not close it sometimes,
+ // close it manually.
+ close(fd);
+ }
+
+ srs_freep(client);
+}
+
+int SrsForwarder::on_meta_data(SrsSharedPtrMessage* metadata)
+{
+ int ret = ERROR_SUCCESS;
+
+ msgs.push_back(metadata);
+
+ return ret;
+}
+
+int SrsForwarder::on_audio(SrsSharedPtrMessage* msg)
+{
+ int ret = ERROR_SUCCESS;
+
+ msgs.push_back(msg);
+
+ return ret;
+}
+
+int SrsForwarder::on_video(SrsSharedPtrMessage* msg)
+{
+ int ret = ERROR_SUCCESS;
+
+ msgs.push_back(msg);
+
+ return ret;
+}
+
+int SrsForwarder::open_socket()
+{
+ int ret = ERROR_SUCCESS;
+
+ srs_trace("forward stream=%s, tcUrl=%s to server=%s, port=%d",
+ stream_name.c_str(), tc_url.c_str(), server.c_str(), port);
+
+ int sock = socket(AF_INET, SOCK_STREAM, 0);
+ if(sock == -1){
+ ret = ERROR_SOCKET_CREATE;
+ srs_error("create socket error. ret=%d", ret);
+ return ret;
+ }
+
+ stfd = st_netfd_open_socket(sock);
+ if(stfd == NULL){
+ ret = ERROR_ST_OPEN_SOCKET;
+ srs_error("st_netfd_open_socket failed. ret=%d", ret);
+ return ret;
+ }
+
+ srs_freep(client);
+ client = new SrsRtmpClient(stfd);
+
+ return ret;
+}
+
+int SrsForwarder::connect_server()
+{
+ int ret = ERROR_SUCCESS;
+
+ std::string ip = parse_server(server);
+ if (ip.empty()) {
+ ret = ERROR_SYSTEM_IP_INVALID;
+ srs_error("dns resolve server error, ip empty. ret=%d", ret);
+ return ret;
+ }
+
+ sockaddr_in addr;
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = inet_addr(ip.c_str());
+
+ if (st_connect(stfd, (const struct sockaddr*)&addr, sizeof(sockaddr_in), ST_UTIME_NO_TIMEOUT) == -1){
+ ret = ERROR_ST_CONNECT;
+ srs_error("connect to server error. ip=%s, port=%d, ret=%d", ip.c_str(), port, ret);
+ return ret;
+ }
+ srs_trace("connect to server success. server=%s, ip=%s, port=%d", server.c_str(), ip.c_str(), port);
+
+ return ret;
+}
+
+std::string SrsForwarder::parse_server(std::string host)
+{
+ if (inet_addr(host.c_str()) != INADDR_NONE) {
+ return host;
+ }
+
+ hostent* answer = gethostbyname(host.c_str());
+ if (answer == NULL) {
+ srs_error("dns resolve host %s error.", host.c_str());
+ return "";
+ }
+
+ char ipv4[16];
+ memset(ipv4, 0, sizeof(ipv4));
+ for (int i = 0; i < answer->h_length; i++) {
+ inet_ntop(AF_INET, answer->h_addr_list[i], ipv4, sizeof(ipv4));
+ srs_info("dns resolve host %s to %s.", host.c_str(), ipv4);
+ break;
+ }
+
+ return ipv4;
+}
+
+int SrsForwarder::forward_cycle_imp()
+{
+ int ret = ERROR_SUCCESS;
+
+ client->set_recv_timeout(SRS_RECV_TIMEOUT_US);
+ client->set_send_timeout(SRS_SEND_TIMEOUT_US);
+
+ if ((ret = connect_server()) != ERROR_SUCCESS) {
+ return ret;
+ }
+ srs_assert(client);
+
+ if ((ret = client->handshake()) != ERROR_SUCCESS) {
+ srs_error("handshake with server failed. ret=%d", ret);
+ return ret;
+ }
+ if ((ret = client->connect_app(app, tc_url)) != ERROR_SUCCESS) {
+ srs_error("connect with server failed, tcUrl=%s. ret=%d", tc_url.c_str(), ret);
+ return ret;
+ }
+ if ((ret = client->create_stream(stream_id)) != ERROR_SUCCESS) {
+ srs_error("connect with server failed, stream_id=%d. ret=%d", stream_id, ret);
+ return ret;
+ }
+ if ((ret = client->publish(stream_name, stream_id)) != ERROR_SUCCESS) {
+ srs_error("connect with server failed, stream_name=%s, stream_id=%d. ret=%d",
+ stream_name.c_str(), stream_id, ret);
+ return ret;
+ }
+
+ if ((ret = forward()) != ERROR_SUCCESS) {
+ return ret;
+ }
+
+ return ret;
+}
+
+int SrsForwarder::forward()
+{
+ int ret = ERROR_SUCCESS;
+
+ client->set_recv_timeout(SRS_PULSE_TIMEOUT_MS * 1000);
+
+ SrsPithyPrint pithy_print(SRS_STAGE_FORWARDER);
+
+ while (loop) {
+ pithy_print.elapse(SRS_PULSE_TIMEOUT_MS);
+
+ // switch to other st-threads.
+ st_usleep(0);
+
+ // read from client.
+ if (true) {
+ SrsCommonMessage* msg = NULL;
+ ret = client->recv_message(&msg);
+
+ srs_verbose("play loop recv message. ret=%d", ret);
+ if (ret != ERROR_SUCCESS && ret != ERROR_SOCKET_TIMEOUT) {
+ srs_error("recv server control message failed. ret=%d", ret);
+ return ret;
+ }
+ }
+
+ int count = (int)msgs.size();
+
+ // reportable
+ if (pithy_print.can_print()) {
+ srs_trace("-> clock=%u, time=%"PRId64", msgs=%d, obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d",
+ (int)(srs_get_system_time_ms()/1000), pithy_print.get_age(), count, client->get_send_bytes(), client->get_recv_bytes(), client->get_send_kbps(), client->get_recv_kbps());
+ }
+
+ // all msgs to forward.
+ for (int i = 0; i < count; i++) {
+ SrsSharedPtrMessage* msg = msgs[i];
+ msgs[i] = NULL;
+
+ if ((ret = client->send_message(msg)) != ERROR_SUCCESS) {
+ srs_error("forwarder send message to server failed. ret=%d", ret);
+ return ret;
+ }
+ }
+ msgs.clear();
+ }
+
+ return ret;
+}
+
+void SrsForwarder::forward_cycle()
+{
+ int ret = ERROR_SUCCESS;
+
+ log_context->generate_id();
+ srs_trace("forward cycle start");
+
+ while (loop) {
+ if ((ret = forward_cycle_imp()) != ERROR_SUCCESS) {
+ srs_warn("forward cycle failed, ignored and retry, ret=%d", ret);
+ } else {
+ srs_info("forward cycle success, retry");
+ }
+
+ if (!loop) {
+ break;
+ }
+
+ st_usleep(SRS_FORWARDER_SLEEP_MS * 1000);
+
+ if ((ret = open_socket()) != ERROR_SUCCESS) {
+ srs_warn("forward cycle reopen failed, ignored and retry, ret=%d", ret);
+ } else {
+ srs_info("forward cycle reopen success");
+ }
+ }
+ srs_trace("forward cycle finished");
+}
+
+void* SrsForwarder::forward_thread(void* arg)
+{
+ SrsForwarder* obj = (SrsForwarder*)arg;
+ srs_assert(obj != NULL);
+
+ obj->loop = true;
+ obj->forward_cycle();
+
+ return NULL;
+}
+
diff --git a/trunk/src/core/srs_core_forward.hpp b/trunk/src/core/srs_core_forward.hpp
old mode 100644
new mode 100755
index 0eaf1d236..530032430
--- a/trunk/src/core/srs_core_forward.hpp
+++ b/trunk/src/core/srs_core_forward.hpp
@@ -1,77 +1,80 @@
-/*
-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_FORWARD_HPP
-#define SRS_CORE_FORWARD_HPP
-
-/*
-#include
-*/
-#include
-
-#include
-
-#include
-
-class SrsSharedPtrMessage;
-class SrsOnMetaDataPacket;
-class SrsRtmpClient;
-
-/**
-* forward the stream to other servers.
-*/
-class SrsForwarder
-{
-private:
- std::string app;
- std::string tc_url;
- std::string stream_name;
- int stream_id;
- std::string server;
- int port;
-private:
- st_netfd_t stfd;
- st_thread_t tid;
- bool loop;
-private:
- SrsRtmpClient* client;
-public:
- SrsForwarder();
- virtual ~SrsForwarder();
-public:
- virtual int on_publish(std::string vhost, std::string app, std::string stream, std::string forward_server);
- virtual void on_unpublish();
- virtual int on_meta_data(SrsSharedPtrMessage* metadata);
- virtual int on_audio(SrsSharedPtrMessage* msg);
- virtual int on_video(SrsSharedPtrMessage* msg);
-private:
- virtual int open_socket();
- virtual int connect_server();
- std::string parse_server(std::string host);
-private:
- virtual int forward_cycle_imp();
- virtual void forward_cycle();
- static void* forward_thread(void* arg);
-};
-
-#endif
+/*
+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_FORWARD_HPP
+#define SRS_CORE_FORWARD_HPP
+
+/*
+#include
+*/
+#include
+
+#include
+#include
+
+#include
+
+class SrsSharedPtrMessage;
+class SrsOnMetaDataPacket;
+class SrsRtmpClient;
+
+/**
+* forward the stream to other servers.
+*/
+class SrsForwarder
+{
+private:
+ std::string app;
+ std::string tc_url;
+ std::string stream_name;
+ int stream_id;
+ std::string server;
+ int port;
+private:
+ st_netfd_t stfd;
+ st_thread_t tid;
+ bool loop;
+private:
+ SrsRtmpClient* client;
+ std::vector msgs;
+public:
+ SrsForwarder();
+ virtual ~SrsForwarder();
+public:
+ virtual int on_publish(std::string vhost, std::string app, std::string stream, std::string forward_server);
+ virtual void on_unpublish();
+ virtual int on_meta_data(SrsSharedPtrMessage* metadata);
+ virtual int on_audio(SrsSharedPtrMessage* msg);
+ virtual int on_video(SrsSharedPtrMessage* msg);
+private:
+ virtual int open_socket();
+ virtual int connect_server();
+ std::string parse_server(std::string host);
+private:
+ virtual int forward_cycle_imp();
+ virtual int forward();
+ virtual void forward_cycle();
+ static void* forward_thread(void* arg);
+};
+
+#endif
diff --git a/trunk/src/core/srs_core_pithy_print.cpp b/trunk/src/core/srs_core_pithy_print.cpp
old mode 100644
new mode 100755
index fc875d399..77bca2853
--- a/trunk/src/core/srs_core_pithy_print.cpp
+++ b/trunk/src/core/srs_core_pithy_print.cpp
@@ -1,164 +1,173 @@
-/*
-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