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