From aa89f9f51e549134a45c6b614a1b9b178db40738 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 27 Mar 2014 12:14:04 +0800 Subject: [PATCH] change to 0.9.37, for http api/stream --- trunk/conf/full.conf | 7 +- trunk/configure | 5 +- trunk/src/app/srs_app_config.cpp | 73 + trunk/src/app/srs_app_config.hpp | 15 + trunk/src/app/srs_app_http_api.cpp | 30 + trunk/src/app/srs_app_http_api.hpp | 40 + trunk/src/app/srs_app_http_conn.cpp | 24 + trunk/src/app/srs_app_http_conn.hpp | 40 + ...s_app_client.cpp => srs_app_rtmp_conn.cpp} | 1700 +++++++++-------- ...s_app_client.hpp => srs_app_rtmp_conn.hpp} | 6 +- trunk/src/app/srs_app_server.cpp | 30 +- trunk/src/app/srs_app_server.hpp | 3 +- trunk/src/core/srs_core.hpp | 2 +- trunk/src/srs/srs.upp | 180 +- 14 files changed, 1205 insertions(+), 950 deletions(-) create mode 100644 trunk/src/app/srs_app_http_api.cpp create mode 100644 trunk/src/app/srs_app_http_api.hpp create mode 100644 trunk/src/app/srs_app_http_conn.cpp create mode 100644 trunk/src/app/srs_app_http_conn.hpp rename trunk/src/app/{srs_app_client.cpp => srs_app_rtmp_conn.cpp} (96%) mode change 100755 => 100644 rename trunk/src/app/{srs_app_client.hpp => srs_app_rtmp_conn.hpp} (96%) diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index ac28e81c7..e3f63dc32 100644 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -67,10 +67,11 @@ http_api { http_stream { # whether http streaming service is enabled. # default: off - enabled on; + enabled on; # the http streaming port - # default: 80 - listen 80; + # @remark, if use lower port, for instance 80, user must start srs by root. + # default: 8080 + listen 8080; } ############################################################################################# diff --git a/trunk/configure b/trunk/configure index 4abe30c45..7101e99ae 100755 --- a/trunk/configure +++ b/trunk/configure @@ -416,10 +416,11 @@ RTMP_OBJS="${MODULE_OBJS[@]}" MODULE_ID="APP" MODULE_DEPENDS=("CORE" "KERNEL" "RTMP") ModuleLibIncs=(${LibSTRoot} ${LibHttpParserRoot} ${SRS_OBJS}) -MODULE_FILES=("srs_app_server" "srs_app_conn" "srs_app_client" "srs_app_socket" "srs_app_source" +MODULE_FILES=("srs_app_server" "srs_app_conn" "srs_app_rtmp_conn" "srs_app_socket" "srs_app_source" "srs_app_codec" "srs_app_refer" "srs_app_hls" "srs_app_forward" "srs_app_encoder" "srs_app_http" "srs_app_thread" "srs_app_bandwidth" "srs_app_st" "srs_app_log" - "srs_app_config" "srs_app_pithy_print" "srs_app_reload") + "srs_app_config" "srs_app_pithy_print" "srs_app_reload" "srs_app_http_api" + "srs_app_http_conn") APP_INCS="src/app"; MODULE_DIR=${APP_INCS} . auto/modules.sh APP_OBJS="${MODULE_OBJS[@]}" # diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index 80eca830c..aae9f33de 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -1466,6 +1466,79 @@ double SrsConfig::get_hls_window(string vhost) return ::atof(conf->arg0().c_str()); } +SrsConfDirective* SrsConfig::get_http_api() +{ + return root->get("http_api"); +} + +bool SrsConfig::get_http_api_enabled() +{ + SrsConfDirective* conf = get_http_api(); + + if (!conf) { + return false; + } + + conf = conf->get("enabled"); + if (conf && conf->arg0() == "on") { + return true; + } + + return false; +} + +int SrsConfig::get_http_api_listen() +{ + SrsConfDirective* conf = get_http_api(); + + if (conf) { + conf = conf->get("listen"); + + if (conf && !conf->arg0().empty()) { + return ::atoi(conf->arg0().c_str()); + } + } + + return 1985; +} + +SrsConfDirective* SrsConfig::get_http_stream() +{ + return root->get("http_stream"); +} + +bool SrsConfig::get_http_stream_enabled() +{ + SrsConfDirective* conf = get_http_stream(); + + if (!conf) { + return false; + } + + conf = conf->get("enabled"); + + if (conf && conf->arg0() == "on") { + return true; + } + + return false; +} + +int SrsConfig::get_http_stream_listen() +{ + SrsConfDirective* conf = get_http_stream(); + + if (conf) { + conf = conf->get("listen"); + + if (conf && !conf->arg0().empty()) { + return ::atoi(conf->arg0().c_str()); + } + } + + return 8080; +} + SrsConfDirective* SrsConfig::get_refer(string vhost) { SrsConfDirective* conf = get_vhost(vhost); diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index 431da58b2..8a7e995ef 100644 --- a/trunk/src/app/srs_app_config.hpp +++ b/trunk/src/app/srs_app_config.hpp @@ -156,6 +156,7 @@ public: virtual bool get_atc(std::string vhost); virtual double get_queue_length(std::string vhost); virtual SrsConfDirective* get_forward(std::string vhost); +// hls section private: virtual SrsConfDirective* get_hls(std::string vhost); public: @@ -163,6 +164,20 @@ public: virtual std::string get_hls_path(std::string vhost); virtual double get_hls_fragment(std::string vhost); virtual double get_hls_window(std::string vhost); +// http api section +private: + virtual SrsConfDirective* get_http_api(); +public: + virtual bool get_http_api_enabled(); + virtual int get_http_api_listen(); +// http stream section +private: + virtual SrsConfDirective* get_http_stream(); +public: + virtual bool get_http_stream_enabled(); + virtual int get_http_stream_listen(); +// others +public: virtual SrsConfDirective* get_refer(std::string vhost); virtual SrsConfDirective* get_refer_play(std::string vhost); virtual SrsConfDirective* get_refer_publish(std::string vhost); diff --git a/trunk/src/app/srs_app_http_api.cpp b/trunk/src/app/srs_app_http_api.cpp new file mode 100644 index 000000000..369ffa04c --- /dev/null +++ b/trunk/src/app/srs_app_http_api.cpp @@ -0,0 +1,30 @@ +/* +The MIT License (MIT) + +Copyright (c) 2013-2014 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + +SrsHttpApi::SrsHttpApi() { +} + +SrsHttpApi::~SrsHttpApi() { +} diff --git a/trunk/src/app/srs_app_http_api.hpp b/trunk/src/app/srs_app_http_api.hpp new file mode 100644 index 000000000..c77393458 --- /dev/null +++ b/trunk/src/app/srs_app_http_api.hpp @@ -0,0 +1,40 @@ +/* +The MIT License (MIT) + +Copyright (c) 2013-2014 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef SRS_APP_HTTP_API_HPP +#define SRS_APP_HTTP_API_HPP + +/* +#include +*/ + +#include + +class SrsHttpApi +{ +public: + SrsHttpApi(); + virtual ~SrsHttpApi(); +}; + +#endif diff --git a/trunk/src/app/srs_app_http_conn.cpp b/trunk/src/app/srs_app_http_conn.cpp new file mode 100644 index 000000000..256a043c1 --- /dev/null +++ b/trunk/src/app/srs_app_http_conn.cpp @@ -0,0 +1,24 @@ +/* +The MIT License (MIT) + +Copyright (c) 2013-2014 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include diff --git a/trunk/src/app/srs_app_http_conn.hpp b/trunk/src/app/srs_app_http_conn.hpp new file mode 100644 index 000000000..db6a805d1 --- /dev/null +++ b/trunk/src/app/srs_app_http_conn.hpp @@ -0,0 +1,40 @@ +/* +The MIT License (MIT) + +Copyright (c) 2013-2014 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef SRS_APP_HTTP_CONN_HPP +#define SRS_APP_HTTP_CONN_HPP + +/* +#include +*/ + +#include + +class SrsHttpConn +{ +public: + SrsHttpConn(); + virtual ~SrsHttpConn(); +}; + +#endif diff --git a/trunk/src/app/srs_app_client.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp old mode 100755 new mode 100644 similarity index 96% rename from trunk/src/app/srs_app_client.cpp rename to trunk/src/app/srs_app_rtmp_conn.cpp index f1fc5b4a6..5e5110fb1 --- a/trunk/src/app/srs_app_client.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -1,849 +1,851 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013-2014 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include - -#include -#include - -using namespace std; - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -SrsClient::SrsClient(SrsServer* srs_server, st_netfd_t client_stfd) - : SrsConnection(srs_server, client_stfd) -{ - ip = NULL; - req = new SrsRequest(); - res = new SrsResponse(); - skt = new SrsSocket(client_stfd); - rtmp = new SrsRtmpServer(skt); - refer = new SrsRefer(); -#ifdef SRS_HTTP_CALLBACK - http_hooks = new SrsHttpHooks(); -#endif - bandwidth = new SrsBandwidth(); - - _srs_config->subscribe(this); -} - -SrsClient::~SrsClient() -{ - _srs_config->unsubscribe(this); - - srs_freepa(ip); - srs_freep(req); - srs_freep(res); - srs_freep(rtmp); - srs_freep(skt); - srs_freep(refer); -#ifdef SRS_HTTP_CALLBACK - srs_freep(http_hooks); -#endif - srs_freep(bandwidth); -} - -// 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"); - - // discovery vhost, resolve the vhost from config - SrsConfDirective* parsed_vhost = _srs_config->get_vhost(req->vhost); - if (parsed_vhost) { - req->vhost = parsed_vhost->arg0(); - } - - srs_info("discovery app success. schema=%s, vhost=%s, port=%s, app=%s", - req->schema.c_str(), req->vhost.c_str(), req->port.c_str(), req->app.c_str()); - - if (req->schema.empty() || req->vhost.empty() || req->port.empty() || req->app.empty()) { - ret = ERROR_RTMP_REQ_TCURL; - srs_error("discovery tcUrl failed. " - "tcUrl=%s, schema=%s, vhost=%s, port=%s, app=%s, ret=%d", - req->tcUrl.c_str(), req->schema.c_str(), req->vhost.c_str(), req->port.c_str(), req->app.c_str(), ret); - return ret; - } - - // check vhost - 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()); - - ret = service_cycle(); - on_close(); - - return ret; -} - -int SrsClient::on_reload_vhost_removed(string vhost) -{ - int ret = ERROR_SUCCESS; - - if (req->vhost != vhost) { - return ret; - } - - // if the vhost connected is removed, disconnect the client. - srs_trace("vhost %s removed/disabled, close client url=%s", - vhost.c_str(), req->get_stream_url().c_str()); - - srs_close_stfd(stfd); - - return ret; -} - -int SrsClient::service_cycle() -{ - int ret = ERROR_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"); - - // do bandwidth test if connect to the vhost which is for bandwidth check. - if (_srs_config->get_bw_check_enabled(req->vhost)) { - return bandwidth->bandwidth_test(req, stfd, rtmp); - } - - 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"); - - while (true) { - ret = stream_service_cycle(); - - // stream service must terminated with error, never success. - srs_assert(ret != ERROR_SUCCESS); - - // when not system control error, fatal error, return. - if (!srs_is_system_control_error(ret)) { - if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) { - srs_error("stream service cycle failed. ret=%d", ret); - } - return ret; - } - - // for republish, continue service - if (ret == ERROR_CONTROL_REPUBLISH) { - // set timeout to a larger value, wait for encoder to republish. - rtmp->set_send_timeout(SRS_REPUBLISH_RECV_TIMEOUT_US); - rtmp->set_recv_timeout(SRS_REPUBLISH_SEND_TIMEOUT_US); - - srs_trace("control message(unpublish) accept, retry stream service."); - continue; - } - - // for "some" system control error, - // logical accept and retry stream service. - if (ret == ERROR_CONTROL_RTMP_CLOSE) { - // set timeout to a larger value, for user paused. - rtmp->set_recv_timeout(SRS_PAUSED_RECV_TIMEOUT_US); - rtmp->set_send_timeout(SRS_PAUSED_SEND_TIMEOUT_US); - - srs_trace("control message(close) accept, retry stream service."); - continue; - } - - // for other system control message, fatal error. - srs_error("control message(%d) reject as error. ret=%d", ret, ret); - return ret; - } - - return ret; -} - -int SrsClient::stream_service_cycle() -{ - int ret = ERROR_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=%s, stream_name=%s", - srs_client_type_string(type).c_str(), req->stream.c_str()); - - // client is identified, set the timeout to service timeout. - rtmp->set_recv_timeout(SRS_RECV_TIMEOUT_US); - rtmp->set_send_timeout(SRS_SEND_TIMEOUT_US); - - // set chunk size to larger. - int chunk_size = _srs_config->get_chunk_size(req->vhost); - 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 serve. - SrsSource* source = SrsSource::find(req); - 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_US); - return ret; - } - - bool enabled_cache = _srs_config->get_gop_cache(req->vhost); - srs_info("source found, url=%s, enabled_cache=%d", req->get_stream_url().c_str(), enabled_cache); - source->set_cache(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; - } - if ((ret = on_play()) != ERROR_SUCCESS) { - srs_error("http hook on_play failed. ret=%d", ret); - return ret; - } - srs_info("start to play stream %s success", req->stream.c_str()); - ret = playing(source); - on_stop(); - return ret; - } - 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; - } - if ((ret = on_publish()) != ERROR_SUCCESS) { - srs_error("http hook on_publish failed. ret=%d", ret); - return ret; - } - srs_info("start to publish stream %s success", req->stream.c_str()); - ret = fmle_publish(source); - source->on_unpublish(); - 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; - } - if ((ret = on_publish()) != ERROR_SUCCESS) { - srs_error("http hook on_publish failed. ret=%d", ret); - return ret; - } - srs_info("flash start to publish stream %s success", req->stream.c_str()); - ret = flash_publish(source); - source->on_unpublish(); - 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 = _srs_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 (!_srs_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(); - } - - if ((ret = refer->check(req->pageUrl, _srs_config->get_refer(req->vhost))) != ERROR_SUCCESS) { - srs_error("check refer failed. ret=%d", ret); - return ret; - } - srs_verbose("check refer success."); - - if ((ret = on_connect()) != ERROR_SUCCESS) { - return ret; - } - - return ret; -} - -int SrsClient::playing(SrsSource* source) -{ - int ret = ERROR_SUCCESS; - - if ((ret = refer->check(req->pageUrl, _srs_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_US); - - SrsPithyPrint pithy_print(SRS_STAGE_PLAY_USER); - - while (true) { - pithy_print.elapse(SRS_PULSE_TIMEOUT_US / 1000); - - // 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) { - if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) { - srs_error("recv client control message failed. ret=%d", ret); - } - return ret; - } - if ((ret = process_play_control_msg(consumer, msg)) != ERROR_SUCCESS) { - if (!srs_is_system_control_error(ret)) { - 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::fmle_publish(SrsSource* source) -{ - int ret = ERROR_SUCCESS; - - if ((ret = refer->check(req->pageUrl, _srs_config->get_refer_publish(req->vhost))) != ERROR_SUCCESS) { - srs_error("fmle check publish_refer failed. ret=%d", ret); - return ret; - } - srs_verbose("fmle 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("fmle hls on_publish failed. ret=%d", ret); - return ret; - } - srs_verbose("fmle 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("fmle 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()); - } - - // 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("fmle decode unpublish message failed. ret=%d", ret); - return ret; - } - - SrsPacket* pkt = msg->get_packet(); - if (dynamic_cast(pkt)) { - SrsFMLEStartPacket* unpublish = dynamic_cast(pkt); - if ((ret = rtmp->fmle_unpublish(res->stream_id, unpublish->transaction_id)) != ERROR_SUCCESS) { - return ret; - } - return ERROR_CONTROL_REPUBLISH; - } - - srs_trace("fmle ignore AMF0/AMF3 command message."); - continue; - } - - // video, audio, data message - if ((ret = process_publish_message(source, msg)) != ERROR_SUCCESS) { - srs_error("fmle process publish message failed. ret=%d", ret); - return ret; - } - } - - return ret; -} - -int SrsClient::flash_publish(SrsSource* source) -{ - int ret = ERROR_SUCCESS; - - if ((ret = refer->check(req->pageUrl, _srs_config->get_refer_publish(req->vhost))) != ERROR_SUCCESS) { - srs_error("flash check publish_refer failed. ret=%d", ret); - return ret; - } - srs_verbose("flash 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("flash hls on_publish failed. ret=%d", ret); - return ret; - } - srs_verbose("flash 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) { - if (!srs_is_client_gracefully_close(ret)) { - srs_error("flash 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()); - } - - // 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("flash decode unpublish message failed. ret=%d", ret); - return ret; - } - - // flash unpublish. - // TODO: maybe need to support republish. - srs_trace("flash flash publish finished."); - return ERROR_CONTROL_REPUBLISH; - } - - // video, audio, data message - if ((ret = process_publish_message(source, msg)) != ERROR_SUCCESS) { - srs_error("flash process publish message failed. ret=%d", ret); - return ret; - } - } - - return ret; -} - -int SrsClient::process_publish_message(SrsSource* source, SrsCommonMessage* msg) -{ - 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; - } - - 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."); - - SrsCloseStreamPacket* close = dynamic_cast(msg->get_packet()); - if (close) { - ret = ERROR_CONTROL_RTMP_CLOSE; - srs_trace("system control message: rtmp close stream. ret=%d", ret); - return ret; - } - - 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; -} - -int SrsClient::on_connect() -{ - int ret = ERROR_SUCCESS; - -#ifdef SRS_HTTP_CALLBACK - // HTTP: on_connect - SrsConfDirective* on_connect = _srs_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, connection_id, ip, req)) != ERROR_SUCCESS) { - srs_error("hook client on_connect failed. url=%s, ret=%d", url.c_str(), ret); - return ret; - } - } -#endif - - return ret; -} - -void SrsClient::on_close() -{ -#ifdef SRS_HTTP_CALLBACK - // whatever the ret code, notify the api hooks. - // HTTP: on_close - SrsConfDirective* on_close = _srs_config->get_vhost_on_close(req->vhost); - if (!on_close) { - srs_info("ignore the empty http callback: on_close"); - return; - } - - for (int i = 0; i < (int)on_close->args.size(); i++) { - std::string url = on_close->args.at(i); - http_hooks->on_close(url, connection_id, ip, req); - } -#endif -} - -int SrsClient::on_publish() -{ - int ret = ERROR_SUCCESS; - -#ifdef SRS_HTTP_CALLBACK - // HTTP: on_publish - SrsConfDirective* on_publish = _srs_config->get_vhost_on_publish(req->vhost); - if (!on_publish) { - srs_info("ignore the empty http callback: on_publish"); - return ret; - } - - for (int i = 0; i < (int)on_publish->args.size(); i++) { - std::string url = on_publish->args.at(i); - if ((ret = http_hooks->on_publish(url, connection_id, ip, req)) != ERROR_SUCCESS) { - srs_error("hook client on_publish failed. url=%s, ret=%d", url.c_str(), ret); - return ret; - } - } -#endif - - return ret; -} - -void SrsClient::on_unpublish() -{ -#ifdef SRS_HTTP_CALLBACK - // whatever the ret code, notify the api hooks. - // HTTP: on_unpublish - SrsConfDirective* on_unpublish = _srs_config->get_vhost_on_unpublish(req->vhost); - if (!on_unpublish) { - srs_info("ignore the empty http callback: on_unpublish"); - return; - } - - for (int i = 0; i < (int)on_unpublish->args.size(); i++) { - std::string url = on_unpublish->args.at(i); - http_hooks->on_unpublish(url, connection_id, ip, req); - } -#endif -} - -int SrsClient::on_play() -{ - int ret = ERROR_SUCCESS; - -#ifdef SRS_HTTP_CALLBACK - // HTTP: on_play - SrsConfDirective* on_play = _srs_config->get_vhost_on_play(req->vhost); - if (!on_play) { - srs_info("ignore the empty http callback: on_play"); - return ret; - } - - for (int i = 0; i < (int)on_play->args.size(); i++) { - std::string url = on_play->args.at(i); - if ((ret = http_hooks->on_play(url, connection_id, ip, req)) != ERROR_SUCCESS) { - srs_error("hook client on_play failed. url=%s, ret=%d", url.c_str(), ret); - return ret; - } - } -#endif - - return ret; -} - -void SrsClient::on_stop() -{ -#ifdef SRS_HTTP_CALLBACK - // whatever the ret code, notify the api hooks. - // HTTP: on_stop - SrsConfDirective* on_stop = _srs_config->get_vhost_on_stop(req->vhost); - if (!on_stop) { - srs_info("ignore the empty http callback: on_stop"); - return; - } - - for (int i = 0; i < (int)on_stop->args.size(); i++) { - std::string url = on_stop->args.at(i); - http_hooks->on_stop(url, connection_id, ip, req); - } -#endif -} +/* +The MIT License (MIT) + +Copyright (c) 2013-2014 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include +#include + +using namespace std; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +SrsClient::SrsClient(SrsServer* srs_server, st_netfd_t client_stfd) + : SrsConnection(srs_server, client_stfd) +{ + ip = NULL; + req = new SrsRequest(); + res = new SrsResponse(); + skt = new SrsSocket(client_stfd); + rtmp = new SrsRtmpServer(skt); + refer = new SrsRefer(); +#ifdef SRS_HTTP_CALLBACK + http_hooks = new SrsHttpHooks(); +#endif + bandwidth = new SrsBandwidth(); + + _srs_config->subscribe(this); +} + +SrsClient::~SrsClient() +{ + _srs_config->unsubscribe(this); + + srs_freepa(ip); + srs_freep(req); + srs_freep(res); + srs_freep(rtmp); + srs_freep(skt); + srs_freep(refer); +#ifdef SRS_HTTP_CALLBACK + srs_freep(http_hooks); +#endif + srs_freep(bandwidth); +} + +// 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"); + + // discovery vhost, resolve the vhost from config + SrsConfDirective* parsed_vhost = _srs_config->get_vhost(req->vhost); + if (parsed_vhost) { + req->vhost = parsed_vhost->arg0(); + } + + srs_info("discovery app success. schema=%s, vhost=%s, port=%s, app=%s", + req->schema.c_str(), req->vhost.c_str(), req->port.c_str(), req->app.c_str()); + + if (req->schema.empty() || req->vhost.empty() || req->port.empty() || req->app.empty()) { + ret = ERROR_RTMP_REQ_TCURL; + srs_error("discovery tcUrl failed. " + "tcUrl=%s, schema=%s, vhost=%s, port=%s, app=%s, ret=%d", + req->tcUrl.c_str(), req->schema.c_str(), req->vhost.c_str(), req->port.c_str(), req->app.c_str(), ret); + return ret; + } + + // check vhost + 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()); + + ret = service_cycle(); + on_close(); + + return ret; +} + +int SrsClient::on_reload_vhost_removed(string vhost) +{ + int ret = ERROR_SUCCESS; + + if (req->vhost != vhost) { + return ret; + } + + // if the vhost connected is removed, disconnect the client. + srs_trace("vhost %s removed/disabled, close client url=%s", + vhost.c_str(), req->get_stream_url().c_str()); + + srs_close_stfd(stfd); + + return ret; +} + +int SrsClient::service_cycle() +{ + int ret = ERROR_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"); + + // do bandwidth test if connect to the vhost which is for bandwidth check. + if (_srs_config->get_bw_check_enabled(req->vhost)) { + return bandwidth->bandwidth_test(req, stfd, rtmp); + } + + 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"); + + while (true) { + ret = stream_service_cycle(); + + // stream service must terminated with error, never success. + srs_assert(ret != ERROR_SUCCESS); + + // when not system control error, fatal error, return. + if (!srs_is_system_control_error(ret)) { + if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) { + srs_error("stream service cycle failed. ret=%d", ret); + } + return ret; + } + + // for republish, continue service + if (ret == ERROR_CONTROL_REPUBLISH) { + // set timeout to a larger value, wait for encoder to republish. + rtmp->set_send_timeout(SRS_REPUBLISH_RECV_TIMEOUT_US); + rtmp->set_recv_timeout(SRS_REPUBLISH_SEND_TIMEOUT_US); + + srs_trace("control message(unpublish) accept, retry stream service."); + continue; + } + + // for "some" system control error, + // logical accept and retry stream service. + if (ret == ERROR_CONTROL_RTMP_CLOSE) { + // set timeout to a larger value, for user paused. + rtmp->set_recv_timeout(SRS_PAUSED_RECV_TIMEOUT_US); + rtmp->set_send_timeout(SRS_PAUSED_SEND_TIMEOUT_US); + + srs_trace("control message(close) accept, retry stream service."); + continue; + } + + // for other system control message, fatal error. + srs_error("control message(%d) reject as error. ret=%d", ret, ret); + return ret; + } + + return ret; +} + +int SrsClient::stream_service_cycle() +{ + int ret = ERROR_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=%s, stream_name=%s", + srs_client_type_string(type).c_str(), req->stream.c_str()); + + // client is identified, set the timeout to service timeout. + rtmp->set_recv_timeout(SRS_RECV_TIMEOUT_US); + rtmp->set_send_timeout(SRS_SEND_TIMEOUT_US); + + // set chunk size to larger. + int chunk_size = _srs_config->get_chunk_size(req->vhost); + 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 serve. + SrsSource* source = SrsSource::find(req); + 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_US); + return ret; + } + + bool enabled_cache = _srs_config->get_gop_cache(req->vhost); + srs_info("source found, url=%s, enabled_cache=%d", req->get_stream_url().c_str(), enabled_cache); + source->set_cache(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; + } + if ((ret = on_play()) != ERROR_SUCCESS) { + srs_error("http hook on_play failed. ret=%d", ret); + return ret; + } + srs_info("start to play stream %s success", req->stream.c_str()); + ret = playing(source); + on_stop(); + return ret; + } + 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; + } + if ((ret = on_publish()) != ERROR_SUCCESS) { + srs_error("http hook on_publish failed. ret=%d", ret); + return ret; + } + srs_info("start to publish stream %s success", req->stream.c_str()); + ret = fmle_publish(source); + source->on_unpublish(); + 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; + } + if ((ret = on_publish()) != ERROR_SUCCESS) { + srs_error("http hook on_publish failed. ret=%d", ret); + return ret; + } + srs_info("flash start to publish stream %s success", req->stream.c_str()); + ret = flash_publish(source); + source->on_unpublish(); + 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 = _srs_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 (!_srs_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(); + } + + if ((ret = refer->check(req->pageUrl, _srs_config->get_refer(req->vhost))) != ERROR_SUCCESS) { + srs_error("check refer failed. ret=%d", ret); + return ret; + } + srs_verbose("check refer success."); + + if ((ret = on_connect()) != ERROR_SUCCESS) { + return ret; + } + + return ret; +} + +int SrsClient::playing(SrsSource* source) +{ + int ret = ERROR_SUCCESS; + + if ((ret = refer->check(req->pageUrl, _srs_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_US); + + SrsPithyPrint pithy_print(SRS_STAGE_PLAY_USER); + + while (true) { + pithy_print.elapse(SRS_PULSE_TIMEOUT_US / 1000); + + // 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) { + if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) { + srs_error("recv client control message failed. ret=%d", ret); + } + return ret; + } + if ((ret = process_play_control_msg(consumer, msg)) != ERROR_SUCCESS) { + if (!srs_is_system_control_error(ret)) { + 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::fmle_publish(SrsSource* source) +{ + int ret = ERROR_SUCCESS; + + if ((ret = refer->check(req->pageUrl, _srs_config->get_refer_publish(req->vhost))) != ERROR_SUCCESS) { + srs_error("fmle check publish_refer failed. ret=%d", ret); + return ret; + } + srs_verbose("fmle 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("fmle hls on_publish failed. ret=%d", ret); + return ret; + } + srs_verbose("fmle 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("fmle 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()); + } + + // 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("fmle decode unpublish message failed. ret=%d", ret); + return ret; + } + + SrsPacket* pkt = msg->get_packet(); + if (dynamic_cast(pkt)) { + SrsFMLEStartPacket* unpublish = dynamic_cast(pkt); + if ((ret = rtmp->fmle_unpublish(res->stream_id, unpublish->transaction_id)) != ERROR_SUCCESS) { + return ret; + } + return ERROR_CONTROL_REPUBLISH; + } + + srs_trace("fmle ignore AMF0/AMF3 command message."); + continue; + } + + // video, audio, data message + if ((ret = process_publish_message(source, msg)) != ERROR_SUCCESS) { + srs_error("fmle process publish message failed. ret=%d", ret); + return ret; + } + } + + return ret; +} + +int SrsClient::flash_publish(SrsSource* source) +{ + int ret = ERROR_SUCCESS; + + if ((ret = refer->check(req->pageUrl, _srs_config->get_refer_publish(req->vhost))) != ERROR_SUCCESS) { + srs_error("flash check publish_refer failed. ret=%d", ret); + return ret; + } + srs_verbose("flash 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("flash hls on_publish failed. ret=%d", ret); + return ret; + } + srs_verbose("flash 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) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("flash 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()); + } + + // 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("flash decode unpublish message failed. ret=%d", ret); + return ret; + } + + // flash unpublish. + // TODO: maybe need to support republish. + srs_trace("flash flash publish finished."); + return ERROR_CONTROL_REPUBLISH; + } + + // video, audio, data message + if ((ret = process_publish_message(source, msg)) != ERROR_SUCCESS) { + srs_error("flash process publish message failed. ret=%d", ret); + return ret; + } + } + + return ret; +} + +int SrsClient::process_publish_message(SrsSource* source, SrsCommonMessage* msg) +{ + 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; + } + + 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."); + + SrsCloseStreamPacket* close = dynamic_cast(msg->get_packet()); + if (close) { + ret = ERROR_CONTROL_RTMP_CLOSE; + srs_trace("system control message: rtmp close stream. ret=%d", ret); + return ret; + } + + 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; +} + +int SrsClient::on_connect() +{ + int ret = ERROR_SUCCESS; + +#ifdef SRS_HTTP_CALLBACK + // HTTP: on_connect + SrsConfDirective* on_connect = _srs_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, connection_id, ip, req)) != ERROR_SUCCESS) { + srs_error("hook client on_connect failed. url=%s, ret=%d", url.c_str(), ret); + return ret; + } + } +#endif + + return ret; +} + +void SrsClient::on_close() +{ +#ifdef SRS_HTTP_CALLBACK + // whatever the ret code, notify the api hooks. + // HTTP: on_close + SrsConfDirective* on_close = _srs_config->get_vhost_on_close(req->vhost); + if (!on_close) { + srs_info("ignore the empty http callback: on_close"); + return; + } + + for (int i = 0; i < (int)on_close->args.size(); i++) { + std::string url = on_close->args.at(i); + http_hooks->on_close(url, connection_id, ip, req); + } +#endif +} + +int SrsClient::on_publish() +{ + int ret = ERROR_SUCCESS; + +#ifdef SRS_HTTP_CALLBACK + // HTTP: on_publish + SrsConfDirective* on_publish = _srs_config->get_vhost_on_publish(req->vhost); + if (!on_publish) { + srs_info("ignore the empty http callback: on_publish"); + return ret; + } + + for (int i = 0; i < (int)on_publish->args.size(); i++) { + std::string url = on_publish->args.at(i); + if ((ret = http_hooks->on_publish(url, connection_id, ip, req)) != ERROR_SUCCESS) { + srs_error("hook client on_publish failed. url=%s, ret=%d", url.c_str(), ret); + return ret; + } + } +#endif + + return ret; +} + +void SrsClient::on_unpublish() +{ +#ifdef SRS_HTTP_CALLBACK + // whatever the ret code, notify the api hooks. + // HTTP: on_unpublish + SrsConfDirective* on_unpublish = _srs_config->get_vhost_on_unpublish(req->vhost); + if (!on_unpublish) { + srs_info("ignore the empty http callback: on_unpublish"); + return; + } + + for (int i = 0; i < (int)on_unpublish->args.size(); i++) { + std::string url = on_unpublish->args.at(i); + http_hooks->on_unpublish(url, connection_id, ip, req); + } +#endif +} + +int SrsClient::on_play() +{ + int ret = ERROR_SUCCESS; + +#ifdef SRS_HTTP_CALLBACK + // HTTP: on_play + SrsConfDirective* on_play = _srs_config->get_vhost_on_play(req->vhost); + if (!on_play) { + srs_info("ignore the empty http callback: on_play"); + return ret; + } + + for (int i = 0; i < (int)on_play->args.size(); i++) { + std::string url = on_play->args.at(i); + if ((ret = http_hooks->on_play(url, connection_id, ip, req)) != ERROR_SUCCESS) { + srs_error("hook client on_play failed. url=%s, ret=%d", url.c_str(), ret); + return ret; + } + } +#endif + + return ret; +} + +void SrsClient::on_stop() +{ +#ifdef SRS_HTTP_CALLBACK + // whatever the ret code, notify the api hooks. + // HTTP: on_stop + SrsConfDirective* on_stop = _srs_config->get_vhost_on_stop(req->vhost); + if (!on_stop) { + srs_info("ignore the empty http callback: on_stop"); + return; + } + + for (int i = 0; i < (int)on_stop->args.size(); i++) { + std::string url = on_stop->args.at(i); + http_hooks->on_stop(url, connection_id, ip, req); + } +#endif + + return; +} diff --git a/trunk/src/app/srs_app_client.hpp b/trunk/src/app/srs_app_rtmp_conn.hpp similarity index 96% rename from trunk/src/app/srs_app_client.hpp rename to trunk/src/app/srs_app_rtmp_conn.hpp index dcdcb1bae..3806a4960 100644 --- a/trunk/src/app/srs_app_client.hpp +++ b/trunk/src/app/srs_app_rtmp_conn.hpp @@ -21,11 +21,11 @@ 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_APP_CLIENT_HPP -#define SRS_APP_CLIENT_HPP +#ifndef SRS_APP_RTMP_CONN_HPP +#define SRS_APP_RTMP_CONN_HPP /* -#include +#include */ #include diff --git a/trunk/src/app/srs_app_server.cpp b/trunk/src/app/srs_app_server.cpp index 42e5a19f4..a48c23838 100644 --- a/trunk/src/app/srs_app_server.cpp +++ b/trunk/src/app/srs_app_server.cpp @@ -35,7 +35,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include -#include +#include #include #include @@ -118,7 +118,7 @@ int SrsListener::listen(int _port) } srs_verbose("create st listen thread success."); - srs_trace("server started, listen at port=%d, fd=%d", port, fd); + srs_trace("server started, listen at port=%d, type=%d, fd=%d", port, type, fd); return ret; } @@ -301,7 +301,29 @@ int SrsServer::listen() int port = ::atoi(conf->args.at(i).c_str()); if ((ret = listener->listen(port)) != ERROR_SUCCESS) { - srs_error("listen at port %d failed. ret=%d", port, ret); + srs_error("RTMP stream listen at port %d failed. ret=%d", port, ret); + return ret; + } + } + + if (_srs_config->get_http_api_enabled()) { + SrsListener* listener = new SrsListener(this, SrsListenerHttpApi); + listeners.push_back(listener); + + int port = _srs_config->get_http_api_listen(); + if ((ret = listener->listen(port)) != ERROR_SUCCESS) { + srs_error("HTTP api listen at port %d failed. ret=%d", port, ret); + return ret; + } + } + + if (_srs_config->get_http_stream_enabled()) { + SrsListener* listener = new SrsListener(this, SrsListenerHttpStream); + listeners.push_back(listener); + + int port = _srs_config->get_http_stream_listen(); + if ((ret = listener->listen(port)) != ERROR_SUCCESS) { + srs_error("HTTP stream listen at port %d failed. ret=%d", port, ret); return ret; } } @@ -413,6 +435,8 @@ int SrsServer::accept_client(SrsListenerType type, st_netfd_t client_stfd) SrsConnection* conn = NULL; if (type == SrsListenerStream) { conn = new SrsClient(this, client_stfd); + } else if (type == SrsListenerHttpApi) { + } else if (type == SrsListenerHttpStream) { } else { // TODO: FIXME: handler others } diff --git a/trunk/src/app/srs_app_server.hpp b/trunk/src/app/srs_app_server.hpp index 2d6ca8f94..812afb91e 100644 --- a/trunk/src/app/srs_app_server.hpp +++ b/trunk/src/app/srs_app_server.hpp @@ -42,7 +42,8 @@ class SrsConnection; enum SrsListenerType { SrsListenerStream = 0, - SrsListenerApi + SrsListenerHttpApi, + SrsListenerHttpStream }; class SrsListener : public ISrsThreadHandler diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index a08f0f2dc..37a44d123 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR "0" #define VERSION_MINOR "9" -#define VERSION_REVISION "36" +#define VERSION_REVISION "37" #define RTMP_SIG_SRS_VERSION VERSION_MAJOR"."VERSION_MINOR"."VERSION_REVISION // server info. #define RTMP_SIG_SRS_KEY "srs" diff --git a/trunk/src/srs/srs.upp b/trunk/src/srs/srs.upp index 2ee760de1..e04462986 100755 --- a/trunk/src/srs/srs.upp +++ b/trunk/src/srs/srs.upp @@ -1,92 +1,96 @@ file - main readonly separator, - ..\main\srs_main_server.cpp, - ..\main\srs_main_bandcheck.cpp, - auto readonly separator, - ..\..\objs\srs_auto_headers.hpp, - libs readonly separator, - ..\libs\srs_librtmp.hpp, - ..\libs\srs_librtmp.cpp, - ..\libs\srs_lib_simple_socket.hpp, - ..\libs\srs_lib_simple_socket.cpp, - core readonly separator, - ..\core\srs_core.hpp, - ..\core\srs_core.cpp, - ..\core\srs_core_autofree.hpp, - ..\core\srs_core_autofree.cpp, - kernel readonly separator, - ..\kernel\srs_kernel_buffer.hpp, - ..\kernel\srs_kernel_buffer.cpp, - ..\kernel\srs_kernel_error.hpp, - ..\kernel\srs_kernel_error.cpp, - ..\kernel\srs_kernel_log.hpp, - ..\kernel\srs_kernel_log.cpp, - ..\kernel\srs_kernel_stream.hpp, - ..\kernel\srs_kernel_stream.cpp, - ..\kernel\srs_kernel_utility.hpp, - ..\kernel\srs_kernel_utility.cpp, - rtmp-protocol readonly separator, - ..\rtmp\srs_protocol_amf0.hpp, - ..\rtmp\srs_protocol_amf0.cpp, - ..\rtmp\srs_protocol_handshake.hpp, - ..\rtmp\srs_protocol_handshake.cpp, - ..\rtmp\srs_protocol_io.hpp, - ..\rtmp\srs_protocol_io.cpp, - ..\rtmp\srs_protocol_rtmp.hpp, - ..\rtmp\srs_protocol_rtmp.cpp, - ..\rtmp\srs_protocol_rtmp_stack.hpp, - ..\rtmp\srs_protocol_rtmp_stack.cpp, - ..\rtmp\srs_protocol_utility.hpp, - ..\rtmp\srs_protocol_utility.cpp, - app readonly separator, - ..\app\srs_app_bandwidth.hpp, - ..\app\srs_app_bandwidth.cpp, - ..\app\srs_app_client.hpp, - ..\app\srs_app_client.cpp, - ..\app\srs_app_codec.hpp, - ..\app\srs_app_codec.cpp, - ..\app\srs_app_conn.hpp, - ..\app\srs_app_conn.cpp, - ..\app\srs_app_config.hpp, - ..\app\srs_app_config.cpp, - ..\app\srs_app_encoder.hpp, - ..\app\srs_app_encoder.cpp, - ..\app\srs_app_forward.hpp, - ..\app\srs_app_forward.cpp, - ..\app\srs_app_hls.hpp, - ..\app\srs_app_hls.cpp, - ..\app\srs_app_http.hpp, - ..\app\srs_app_http.cpp, - ..\app\srs_app_log.hpp, - ..\app\srs_app_log.cpp, - ..\app\srs_app_refer.hpp, - ..\app\srs_app_refer.cpp, - ..\app\srs_app_reload.hpp, - ..\app\srs_app_reload.cpp, - ..\app\srs_app_pithy_print.hpp, - ..\app\srs_app_pithy_print.cpp, - ..\app\srs_app_thread.hpp, - ..\app\srs_app_thread.cpp, - ..\app\srs_app_server.hpp, - ..\app\srs_app_server.cpp, - ..\app\srs_app_st.hpp, - ..\app\srs_app_st.cpp, - ..\app\srs_app_socket.hpp, - ..\app\srs_app_socket.cpp, - ..\app\srs_app_source.hpp, - ..\app\srs_app_source.cpp, - utest readonly separator, - ..\utest\srs_utest.hpp, - ..\utest\srs_utest.cpp, - ..\utest\srs_utest_amf0.hpp, - ..\utest\srs_utest_amf0.cpp, - ..\utest\srs_utest_handshake.hpp, - ..\utest\srs_utest_handshake.cpp, - research readonly separator, - ..\..\research\librtmp\srs_play.c, - ..\..\research\librtmp\srs_publish.c, - ..\..\research\hls\ts_info.cc; + main readonly separator, + ..\main\srs_main_server.cpp, + ..\main\srs_main_bandcheck.cpp, + auto readonly separator, + ..\..\objs\srs_auto_headers.hpp, + libs readonly separator, + ..\libs\srs_librtmp.hpp, + ..\libs\srs_librtmp.cpp, + ..\libs\srs_lib_simple_socket.hpp, + ..\libs\srs_lib_simple_socket.cpp, + core readonly separator, + ..\core\srs_core.hpp, + ..\core\srs_core.cpp, + ..\core\srs_core_autofree.hpp, + ..\core\srs_core_autofree.cpp, + kernel readonly separator, + ..\kernel\srs_kernel_buffer.hpp, + ..\kernel\srs_kernel_buffer.cpp, + ..\kernel\srs_kernel_error.hpp, + ..\kernel\srs_kernel_error.cpp, + ..\kernel\srs_kernel_log.hpp, + ..\kernel\srs_kernel_log.cpp, + ..\kernel\srs_kernel_stream.hpp, + ..\kernel\srs_kernel_stream.cpp, + ..\kernel\srs_kernel_utility.hpp, + ..\kernel\srs_kernel_utility.cpp, + rtmp-protocol readonly separator, + ..\rtmp\srs_protocol_amf0.hpp, + ..\rtmp\srs_protocol_amf0.cpp, + ..\rtmp\srs_protocol_handshake.hpp, + ..\rtmp\srs_protocol_handshake.cpp, + ..\rtmp\srs_protocol_io.hpp, + ..\rtmp\srs_protocol_io.cpp, + ..\rtmp\srs_protocol_rtmp.hpp, + ..\rtmp\srs_protocol_rtmp.cpp, + ..\rtmp\srs_protocol_rtmp_stack.hpp, + ..\rtmp\srs_protocol_rtmp_stack.cpp, + ..\rtmp\srs_protocol_utility.hpp, + ..\rtmp\srs_protocol_utility.cpp, + app readonly separator, + ..\app\srs_app_bandwidth.hpp, + ..\app\srs_app_bandwidth.cpp, + ..\app\srs_app_codec.hpp, + ..\app\srs_app_codec.cpp, + ..\app\srs_app_conn.hpp, + ..\app\srs_app_conn.cpp, + ..\app\srs_app_config.hpp, + ..\app\srs_app_config.cpp, + ..\app\srs_app_encoder.hpp, + ..\app\srs_app_encoder.cpp, + ..\app\srs_app_forward.hpp, + ..\app\srs_app_forward.cpp, + ..\app\srs_app_hls.hpp, + ..\app\srs_app_hls.cpp, + ..\app\srs_app_http.hpp, + ..\app\srs_app_http.cpp, + ..\app\srs_app_http_api.hpp, + ..\app\srs_app_http_api.cpp, + ..\app\srs_app_http_conn.hpp, + ..\app\srs_app_http_conn.cpp, + ..\app\srs_app_log.hpp, + ..\app\srs_app_log.cpp, + ..\app\srs_app_refer.hpp, + ..\app\srs_app_refer.cpp, + ..\app\srs_app_reload.hpp, + ..\app\srs_app_reload.cpp, + ..\app\srs_app_rtmp_conn.hpp, + ..\app\srs_app_rtmp_conn.cpp, + ..\app\srs_app_pithy_print.hpp, + ..\app\srs_app_pithy_print.cpp, + ..\app\srs_app_thread.hpp, + ..\app\srs_app_thread.cpp, + ..\app\srs_app_server.hpp, + ..\app\srs_app_server.cpp, + ..\app\srs_app_st.hpp, + ..\app\srs_app_st.cpp, + ..\app\srs_app_socket.hpp, + ..\app\srs_app_socket.cpp, + ..\app\srs_app_source.hpp, + ..\app\srs_app_source.cpp, + utest readonly separator, + ..\utest\srs_utest.hpp, + ..\utest\srs_utest.cpp, + ..\utest\srs_utest_amf0.hpp, + ..\utest\srs_utest_amf0.cpp, + ..\utest\srs_utest_handshake.hpp, + ..\utest\srs_utest_handshake.cpp, + research readonly separator, + ..\..\research\librtmp\srs_play.c, + ..\..\research\librtmp\srs_publish.c, + ..\..\research\hls\ts_info.cc; mainconfig - "" = "MAIN"; + "" = "MAIN";