diff --git a/trunk/conf/srs.conf b/trunk/conf/srs.conf index 65411f6b0..a76f811e7 100755 --- a/trunk/conf/srs.conf +++ b/trunk/conf/srs.conf @@ -81,11 +81,11 @@ vhost __defaultVhost__ { vhost dev { enabled on; gop_cache on; - hls on; + hls off; hls_path ./objs/nginx/html; hls_fragment 5; hls_window 30; - #forward 127.0.0.1:19350; + forward 127.0.0.1:19350; http_hooks { enabled off; on_connect http://127.0.0.1:8085/api/v1/clients; @@ -96,7 +96,7 @@ vhost dev { on_stop http://127.0.0.1:8085/api/v1/sessions; } transcode { - enabled off; + enabled on; ffmpeg ./objs/ffmpeg/bin/ffmpeg; engine dev { enabled on; diff --git a/trunk/configure b/trunk/configure index 85493c767..40455a1de 100755 --- a/trunk/configure +++ b/trunk/configure @@ -116,7 +116,7 @@ MODULE_FILES=("srs_core" "srs_core_log" "srs_core_server" "srs_core_handshake" "srs_core_pithy_print" "srs_core_config" "srs_core_refer" "srs_core_reload" "srs_core_hls" "srs_core_forward" "srs_core_encoder" - "srs_core_http") + "srs_core_http" "srs_core_thread") MODULE_DIR="src/core" . auto/modules.sh CORE_OBJS="${MODULE_OBJS[@]}" diff --git a/trunk/research/players/rtmp/RtmpPlayer.swf b/trunk/research/players/rtmp/RtmpPlayer.swf old mode 100644 new mode 100755 index 467de310a..50ff1af00 Binary files a/trunk/research/players/rtmp/RtmpPlayer.swf and b/trunk/research/players/rtmp/RtmpPlayer.swf differ diff --git a/trunk/src/core/srs_core.cpp b/trunk/src/core/srs_core.cpp index 0aa6c5207..090dfe80f 100644 --- a/trunk/src/core/srs_core.cpp +++ b/trunk/src/core/srs_core.cpp @@ -111,3 +111,17 @@ void srs_vhost_resolve(std::string& vhost, std::string& app) } } } + +void srs_close_stfd(st_netfd_t& stfd) +{ + if (stfd) { + int fd = st_netfd_fileno(stfd); + st_netfd_close(stfd); + stfd = NULL; + + // st does not close it sometimes, + // close it manually. + close(fd); + } +} + diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 254efa40e..763b460e7 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -46,6 +46,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +#include + // generated by configure. #include @@ -102,4 +104,7 @@ extern std::string srs_dns_resolve(std::string host); // app...vhost...request_vhost extern void srs_vhost_resolve(std::string& vhost, std::string& app); +// close the netfd, and close the underlayer fd. +extern void srs_close_stfd(st_netfd_t& stfd); + #endif \ No newline at end of file diff --git a/trunk/src/core/srs_core_conn.cpp b/trunk/src/core/srs_core_conn.cpp index b11e8ea18..e148db79a 100644 --- a/trunk/src/core/srs_core_conn.cpp +++ b/trunk/src/core/srs_core_conn.cpp @@ -36,15 +36,7 @@ SrsConnection::SrsConnection(SrsServer* srs_server, st_netfd_t client_stfd) SrsConnection::~SrsConnection() { - if (stfd) { - int fd = st_netfd_fileno(stfd); - st_netfd_close(stfd); - stfd = NULL; - - // st does not close it sometimes, - // close it manually. - close(fd); - } + srs_close_stfd(stfd); } int SrsConnection::start() diff --git a/trunk/src/core/srs_core_conn.hpp b/trunk/src/core/srs_core_conn.hpp index 8f8519c95..4689eb7d7 100644 --- a/trunk/src/core/srs_core_conn.hpp +++ b/trunk/src/core/srs_core_conn.hpp @@ -30,8 +30,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include -#include - class SrsServer; class SrsConnection { diff --git a/trunk/src/core/srs_core_encoder.cpp b/trunk/src/core/srs_core_encoder.cpp index 3400948a5..f2b041ac6 100644 --- a/trunk/src/core/srs_core_encoder.cpp +++ b/trunk/src/core/srs_core_encoder.cpp @@ -483,13 +483,103 @@ void SrsFFMPEG::stop() SrsEncoder::SrsEncoder() { - tid = NULL; - loop = false; + pthread = new SrsThread(this, SRS_ENCODER_SLEEP_MS); + pithy_print = new SrsPithyPrint(SRS_STAGE_ENCODER); } SrsEncoder::~SrsEncoder() { on_unpublish(); + + srs_freep(pthread); +} + +int SrsEncoder::on_publish(SrsRequest* req) +{ + int ret = ERROR_SUCCESS; + + ret = parse_scope_engines(req); + + // ignore the loop encoder + if (ret == ERROR_ENCODER_LOOP) { + clear_engines(); + ret = ERROR_SUCCESS; + } + + // return for error or no engine. + if (ret != ERROR_SUCCESS || ffmpegs.empty()) { + return ret; + } + + // start thread to run all encoding engines. + if ((ret = pthread->start()) != ERROR_SUCCESS) { + srs_error("st_thread_create failed. ret=%d", ret); + return ret; + } + + return ret; +} + +void SrsEncoder::on_unpublish() +{ + pthread->stop(); + clear_engines(); +} + +int SrsEncoder::cycle() +{ + int ret = ERROR_SUCCESS; + + std::vector::iterator it; + for (it = ffmpegs.begin(); it != ffmpegs.end(); ++it) { + SrsFFMPEG* ffmpeg = *it; + + // start all ffmpegs. + if ((ret = ffmpeg->start()) != ERROR_SUCCESS) { + srs_error("ffmpeg start failed. ret=%d", ret); + return ret; + } + + // check ffmpeg status. + if ((ret = ffmpeg->cycle()) != ERROR_SUCCESS) { + srs_error("ffmpeg cycle failed. ret=%d", ret); + return ret; + } + } + + // pithy print + encoder(); + pithy_print->elapse(SRS_ENCODER_SLEEP_MS); + + return ret; +} + +void SrsEncoder::on_leave_loop() +{ + // kill ffmpeg when finished and it alive + std::vector::iterator it; + + for (it = ffmpegs.begin(); it != ffmpegs.end(); ++it) { + SrsFFMPEG* ffmpeg = *it; + ffmpeg->stop(); + } +} + +void SrsEncoder::clear_engines() +{ + std::vector::iterator it; + + for (it = ffmpegs.begin(); it != ffmpegs.end(); ++it) { + SrsFFMPEG* ffmpeg = *it; + srs_freep(ffmpeg); + } + + ffmpegs.clear(); +} + +SrsFFMPEG* SrsEncoder::at(int index) +{ + return ffmpegs[index]; } int SrsEncoder::parse_scope_engines(SrsRequest* req) @@ -531,60 +621,6 @@ int SrsEncoder::parse_scope_engines(SrsRequest* req) return ret; } -int SrsEncoder::on_publish(SrsRequest* req) -{ - int ret = ERROR_SUCCESS; - - ret = parse_scope_engines(req); - - // ignore the loop encoder - if (ret == ERROR_ENCODER_LOOP) { - ret = ERROR_SUCCESS; - } - - // return for error or no engine. - if (ret != ERROR_SUCCESS || ffmpegs.empty()) { - return ret; - } - - // start thread to run all encoding engines. - srs_assert(!tid); - if((tid = st_thread_create(encoder_thread, this, 1, 0)) == NULL) { - ret = ERROR_ST_CREATE_FORWARD_THREAD; - srs_error("st_thread_create failed. ret=%d", ret); - return ret; - } - - return ret; -} - -void SrsEncoder::on_unpublish() -{ - if (tid) { - loop = false; - st_thread_interrupt(tid); - st_thread_join(tid, NULL); - tid = NULL; - } - - clear_engines(); -} - -void SrsEncoder::clear_engines() -{ - std::vector::iterator it; - for (it = ffmpegs.begin(); it != ffmpegs.end(); ++it) { - SrsFFMPEG* ffmpeg = *it; - srs_freep(ffmpeg); - } - ffmpegs.clear(); -} - -SrsFFMPEG* SrsEncoder::at(int index) -{ - return ffmpegs[index]; -} - int SrsEncoder::parse_transcode(SrsRequest* req, SrsConfDirective* conf) { int ret = ERROR_SUCCESS; @@ -631,7 +667,6 @@ int SrsEncoder::parse_transcode(SrsRequest* req, SrsConfDirective* conf) // if got a loop, donot transcode the whole stream. if (ret == ERROR_ENCODER_LOOP) { - clear_engines(); break; } @@ -646,85 +681,14 @@ int SrsEncoder::parse_transcode(SrsRequest* req, SrsConfDirective* conf) return ret; } -int SrsEncoder::cycle() -{ - int ret = ERROR_SUCCESS; - - std::vector::iterator it; - for (it = ffmpegs.begin(); it != ffmpegs.end(); ++it) { - SrsFFMPEG* ffmpeg = *it; - - // start all ffmpegs. - if ((ret = ffmpeg->start()) != ERROR_SUCCESS) { - srs_error("ffmpeg start failed. ret=%d", ret); - return ret; - } - - // check ffmpeg status. - if ((ret = ffmpeg->cycle()) != ERROR_SUCCESS) { - srs_error("ffmpeg cycle failed. ret=%d", ret); - return ret; - } - } - - return ret; -} - -void SrsEncoder::encoder_cycle() -{ - int ret = ERROR_SUCCESS; - - log_context->generate_id(); - srs_trace("encoder cycle start"); - - SrsPithyPrint pithy_print(SRS_STAGE_ENCODER); - - while (loop) { - if ((ret = cycle()) != ERROR_SUCCESS) { - srs_warn("encoder cycle failed, ignored and retry, ret=%d", ret); - } else { - srs_info("encoder cycle success, retry"); - } - - if (!loop) { - break; - } - - encoder(&pithy_print); - pithy_print.elapse(SRS_ENCODER_SLEEP_MS); - - st_usleep(SRS_ENCODER_SLEEP_MS * 1000); - } - - // kill ffmpeg when finished and it alive - std::vector::iterator it; - for (it = ffmpegs.begin(); it != ffmpegs.end(); ++it) { - SrsFFMPEG* ffmpeg = *it; - ffmpeg->stop(); - } - - srs_trace("encoder cycle finished"); -} - -void SrsEncoder::encoder(SrsPithyPrint* pithy_print) +void SrsEncoder::encoder() { // reportable if (pithy_print->can_print()) { - srs_trace("-> time=%"PRId64", encoders=%d", - pithy_print->get_age(), (int)ffmpegs.size()); + // TODO: FIXME: show more info. + srs_trace("-> time=%"PRId64", encoders=%d", pithy_print->get_age(), (int)ffmpegs.size()); } } -void* SrsEncoder::encoder_thread(void* arg) -{ - SrsEncoder* obj = (SrsEncoder*)arg; - srs_assert(obj != NULL); - - obj->loop = true; - obj->encoder_cycle(); - - return NULL; -} - #endif diff --git a/trunk/src/core/srs_core_encoder.hpp b/trunk/src/core/srs_core_encoder.hpp index 6149ce4f3..4ecfd59c9 100644 --- a/trunk/src/core/srs_core_encoder.hpp +++ b/trunk/src/core/srs_core_encoder.hpp @@ -32,7 +32,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include -#include +#include class SrsConfDirective; class SrsRequest; @@ -85,28 +85,29 @@ public: * the encoder for a stream, * may use multiple ffmpegs to transcode the specified stream. */ -class SrsEncoder +class SrsEncoder : public ISrsThreadHandler { private: std::vector ffmpegs; private: - st_thread_t tid; - bool loop; + SrsThread* pthread; + SrsPithyPrint* pithy_print; public: SrsEncoder(); virtual ~SrsEncoder(); public: virtual int on_publish(SrsRequest* req); virtual void on_unpublish(); +// interface ISrsThreadHandler. +public: + virtual int cycle(); + virtual void on_leave_loop(); private: - virtual int parse_scope_engines(SrsRequest* req); virtual void clear_engines(); virtual SrsFFMPEG* at(int index); + virtual int parse_scope_engines(SrsRequest* req); virtual int parse_transcode(SrsRequest* req, SrsConfDirective* conf); - virtual int cycle(); - virtual void encoder_cycle(); - virtual void encoder(SrsPithyPrint* pithy_print); - static void* encoder_thread(void* arg); + virtual void encoder(); }; #endif diff --git a/trunk/src/core/srs_core_error.hpp b/trunk/src/core/srs_core_error.hpp index a5d5348f0..47004397e 100644 --- a/trunk/src/core/srs_core_error.hpp +++ b/trunk/src/core/srs_core_error.hpp @@ -37,8 +37,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define ERROR_ST_OPEN_SOCKET 102 #define ERROR_ST_CREATE_LISTEN_THREAD 103 #define ERROR_ST_CREATE_CYCLE_THREAD 104 -#define ERROR_ST_CREATE_FORWARD_THREAD 105 -#define ERROR_ST_CONNECT 106 +#define ERROR_ST_CONNECT 105 #define ERROR_SOCKET_CREATE 200 #define ERROR_SOCKET_SETREUSE 201 diff --git a/trunk/src/core/srs_core_forward.cpp b/trunk/src/core/srs_core_forward.cpp old mode 100755 new mode 100644 index ebca3f667..39059121b --- a/trunk/src/core/srs_core_forward.cpp +++ b/trunk/src/core/srs_core_forward.cpp @@ -1,387 +1,329 @@ -/* -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 - -#define SRS_PULSE_TIMEOUT_MS 100 -#define SRS_FORWARDER_SLEEP_MS 2000 -#define SRS_SEND_TIMEOUT_US 3000000L -#define SRS_RECV_TIMEOUT_US SRS_SEND_TIMEOUT_US - -SrsForwarder::SrsForwarder() -{ - client = NULL; - stfd = NULL; - stream_id = 0; - - tid = NULL; - loop = false; -} - -SrsForwarder::~SrsForwarder() -{ - on_unpublish(); - - std::vector::iterator it; - for (it = msgs.begin(); it != msgs.end(); ++it) { - SrsSharedPtrMessage* msg = *it; - srs_freep(msg); - } - msgs.clear(); -} - -int SrsForwarder::on_publish(SrsRequest* req, std::string forward_server) -{ - int ret = ERROR_SUCCESS; - - // forward app - app = req->app; - - stream_name = req->stream; - server = forward_server; - std::string s_port = RTMP_DEFAULT_PORTS; - port = RTMP_DEFAULT_PORT; - - size_t pos = forward_server.find(":"); - if (pos != std::string::npos) { - s_port = forward_server.substr(pos + 1); - server = forward_server.substr(0, pos); - } - // discovery vhost - std::string vhost = req->vhost; - srs_vhost_resolve(vhost, s_port); - port = ::atoi(s_port.c_str()); - - // generate tcUrl - tc_url = "rtmp://"; - tc_url += vhost; - tc_url += "/"; - tc_url += req->app; - - // dead loop check - std::string source_ep = req->vhost; - source_ep += ":"; - source_ep += req->port; - - std::string dest_ep = vhost; - dest_ep += ":"; - dest_ep += s_port; - - if (source_ep == dest_ep) { - ret = ERROR_SYSTEM_FORWARD_LOOP; - srs_warn("farder loop detected. src=%s, dest=%s, ret=%d", - source_ep.c_str(), dest_ep.c_str(), ret); - return ret; - } - srs_trace("start forward %s to %s, stream: %s/%s", - source_ep.c_str(), dest_ep.c_str(), tc_url.c_str(), - stream_name.c_str()); - - // TODO: seems bug when republish and reforward. - - // start forward - if ((ret = open_socket()) != ERROR_SUCCESS) { - return ret; - } - - srs_assert(!tid); - if((tid = st_thread_create(forward_thread, this, 1, 0)) == NULL){ - ret = ERROR_ST_CREATE_FORWARD_THREAD; - srs_error("st_thread_create failed. ret=%d", ret); - return ret; - } - - return ret; -} - -void SrsForwarder::on_unpublish() -{ - if (tid) { - loop = false; - st_thread_interrupt(tid); - st_thread_join(tid, NULL); - tid = NULL; - } - - if (stfd) { - int fd = st_netfd_fileno(stfd); - st_netfd_close(stfd); - stfd = NULL; - - // st does not close it sometimes, - // close it manually. - close(fd); - } - - srs_freep(client); -} - -int SrsForwarder::on_meta_data(SrsSharedPtrMessage* metadata) -{ - int ret = ERROR_SUCCESS; - - msgs.push_back(metadata); - - return ret; -} - -int SrsForwarder::on_audio(SrsSharedPtrMessage* msg) -{ - int ret = ERROR_SUCCESS; - - // TODO: FIXME: must drop the msgs when server failed. - msgs.push_back(msg); - - return ret; -} - -int SrsForwarder::on_video(SrsSharedPtrMessage* msg) -{ - int ret = ERROR_SUCCESS; - - // TODO: FIXME: must drop the msgs when server failed. - msgs.push_back(msg); - - return ret; -} - -int SrsForwarder::open_socket() -{ - int ret = ERROR_SUCCESS; - - srs_trace("forward stream=%s, tcUrl=%s to server=%s, port=%d", - stream_name.c_str(), tc_url.c_str(), server.c_str(), port); - - int sock = socket(AF_INET, SOCK_STREAM, 0); - if(sock == -1){ - ret = ERROR_SOCKET_CREATE; - srs_error("create socket error. ret=%d", ret); - return ret; - } - - stfd = st_netfd_open_socket(sock); - if(stfd == NULL){ - ret = ERROR_ST_OPEN_SOCKET; - srs_error("st_netfd_open_socket failed. ret=%d", ret); - return ret; - } - - srs_freep(client); - client = new SrsRtmpClient(stfd); - - return ret; -} - -int SrsForwarder::connect_server() -{ - int ret = ERROR_SUCCESS; - - std::string ip = srs_dns_resolve(server); - if (ip.empty()) { - ret = ERROR_SYSTEM_IP_INVALID; - srs_error("dns resolve server error, ip empty. ret=%d", ret); - return ret; - } - - sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = inet_addr(ip.c_str()); - - if (st_connect(stfd, (const struct sockaddr*)&addr, sizeof(sockaddr_in), ST_UTIME_NO_TIMEOUT) == -1){ - ret = ERROR_ST_CONNECT; - srs_error("connect to server error. ip=%s, port=%d, ret=%d", ip.c_str(), port, ret); - return ret; - } - srs_trace("connect to server success. server=%s, ip=%s, port=%d", server.c_str(), ip.c_str(), port); - - return ret; -} - -int SrsForwarder::cycle() -{ - int ret = ERROR_SUCCESS; - - client->set_recv_timeout(SRS_RECV_TIMEOUT_US); - client->set_send_timeout(SRS_SEND_TIMEOUT_US); - - if ((ret = connect_server()) != ERROR_SUCCESS) { - return ret; - } - srs_assert(client); - - if ((ret = client->handshake()) != ERROR_SUCCESS) { - srs_error("handshake with server failed. ret=%d", ret); - return ret; - } - if ((ret = client->connect_app(app, tc_url)) != ERROR_SUCCESS) { - srs_error("connect with server failed, tcUrl=%s. ret=%d", tc_url.c_str(), ret); - return ret; - } - if ((ret = client->create_stream(stream_id)) != ERROR_SUCCESS) { - srs_error("connect with server failed, stream_id=%d. ret=%d", stream_id, ret); - return ret; - } - - // TODO: FIXME: need to cache the metadata and sequence header when reconnect. - - if ((ret = client->publish(stream_name, stream_id)) != ERROR_SUCCESS) { - srs_error("connect with server failed, stream_name=%s, stream_id=%d. ret=%d", - stream_name.c_str(), stream_id, ret); - return ret; - } - - if ((ret = forward()) != ERROR_SUCCESS) { - return ret; - } - - return ret; -} - -int SrsForwarder::forward() -{ - int ret = ERROR_SUCCESS; - - client->set_recv_timeout(SRS_PULSE_TIMEOUT_MS * 1000); - - SrsPithyPrint pithy_print(SRS_STAGE_FORWARDER); - - while (loop) { - pithy_print.elapse(SRS_PULSE_TIMEOUT_MS); - - // switch to other st-threads. - st_usleep(0); - - // read from client. - if (true) { - SrsCommonMessage* msg = NULL; - ret = client->recv_message(&msg); - - srs_verbose("play loop recv message. ret=%d", ret); - if (ret != ERROR_SUCCESS && ret != ERROR_SOCKET_TIMEOUT) { - srs_error("recv server control message failed. ret=%d", ret); - return ret; - } - } - - // ignore when no messages. - int count = (int)msgs.size(); - if (msgs.empty()) { - continue; - } - - // reportable - if (pithy_print.can_print()) { - srs_trace("-> time=%"PRId64", msgs=%d, obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d", - pithy_print.get_age(), count, client->get_send_bytes(), client->get_recv_bytes(), client->get_send_kbps(), client->get_recv_kbps()); - } - - // all msgs to forward. - int i = 0; - for (i = 0; i < count; i++) { - SrsSharedPtrMessage* msg = msgs[i]; - msgs[i] = NULL; - - // we erased the sendout messages, the msg must not be NULL. - srs_assert(msg); - - ret = client->send_message(msg); - if (ret != ERROR_SUCCESS) { - srs_error("forwarder send message to server failed. ret=%d", ret); - - // convert the index to count when error. - i++; - - break; - } - } - - // clear sendout mesages. - if (i < count) { - srs_warn("clear forwarded msg, total=%d, forwarded=%d, ret=%d", count, i, ret); - } else { - srs_info("clear forwarded msg, total=%d, forwarded=%d, ret=%d", count, i, ret); - } - msgs.erase(msgs.begin(), msgs.begin() + i); - - if (ret != ERROR_SUCCESS) { - break; - } - } - - return ret; -} - -void SrsForwarder::forward_cycle() -{ - int ret = ERROR_SUCCESS; - - log_context->generate_id(); - srs_trace("forward cycle start"); - - while (loop) { - if ((ret = cycle()) != ERROR_SUCCESS) { - srs_warn("forward cycle failed, ignored and retry, ret=%d", ret); - } else { - srs_info("forward cycle success, retry"); - } - - if (!loop) { - break; - } - - st_usleep(SRS_FORWARDER_SLEEP_MS * 1000); - - if ((ret = open_socket()) != ERROR_SUCCESS) { - srs_warn("forward cycle reopen failed, ignored and retry, ret=%d", ret); - } else { - srs_info("forward cycle reopen success"); - } - } - srs_trace("forward cycle finished"); -} - -void* SrsForwarder::forward_thread(void* arg) -{ - SrsForwarder* obj = (SrsForwarder*)arg; - srs_assert(obj != NULL); - - obj->loop = true; - obj->forward_cycle(); - - return NULL; -} - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define SRS_PULSE_TIMEOUT_MS 100 +#define SRS_FORWARDER_SLEEP_MS 2000 +#define SRS_SEND_TIMEOUT_US 3000000L +#define SRS_RECV_TIMEOUT_US SRS_SEND_TIMEOUT_US + +SrsForwarder::SrsForwarder() +{ + client = NULL; + stfd = NULL; + stream_id = 0; + + pthread = new SrsThread(this, SRS_FORWARDER_SLEEP_MS); +} + +SrsForwarder::~SrsForwarder() +{ + on_unpublish(); + + std::vector::iterator it; + for (it = msgs.begin(); it != msgs.end(); ++it) { + SrsSharedPtrMessage* msg = *it; + srs_freep(msg); + } + msgs.clear(); + + srs_freep(pthread); +} + +int SrsForwarder::on_publish(SrsRequest* req, std::string forward_server) +{ + int ret = ERROR_SUCCESS; + + // forward app + app = req->app; + + stream_name = req->stream; + server = forward_server; + std::string s_port = RTMP_DEFAULT_PORTS; + port = RTMP_DEFAULT_PORT; + + size_t pos = forward_server.find(":"); + if (pos != std::string::npos) { + s_port = forward_server.substr(pos + 1); + server = forward_server.substr(0, pos); + } + // discovery vhost + std::string vhost = req->vhost; + srs_vhost_resolve(vhost, s_port); + port = ::atoi(s_port.c_str()); + + // generate tcUrl + tc_url = "rtmp://"; + tc_url += vhost; + tc_url += "/"; + tc_url += req->app; + + // dead loop check + std::string source_ep = req->vhost; + source_ep += ":"; + source_ep += req->port; + + std::string dest_ep = vhost; + dest_ep += ":"; + dest_ep += s_port; + + if (source_ep == dest_ep) { + ret = ERROR_SYSTEM_FORWARD_LOOP; + srs_warn("farder loop detected. src=%s, dest=%s, ret=%d", + source_ep.c_str(), dest_ep.c_str(), ret); + return ret; + } + srs_trace("start forward %s to %s, stream: %s/%s", + source_ep.c_str(), dest_ep.c_str(), tc_url.c_str(), + stream_name.c_str()); + + if ((ret = pthread->start()) != ERROR_SUCCESS) { + srs_error("start srs thread failed. ret=%d", ret); + return ret; + } + + return ret; +} + +void SrsForwarder::on_unpublish() +{ + pthread->stop(); + + close_underlayer_socket(); + + srs_freep(client); +} + +int SrsForwarder::on_meta_data(SrsSharedPtrMessage* metadata) +{ + int ret = ERROR_SUCCESS; + + msgs.push_back(metadata); + + return ret; +} + +int SrsForwarder::on_audio(SrsSharedPtrMessage* msg) +{ + int ret = ERROR_SUCCESS; + + // TODO: FIXME: must drop the msgs when server failed. + msgs.push_back(msg); + + return ret; +} + +int SrsForwarder::on_video(SrsSharedPtrMessage* msg) +{ + int ret = ERROR_SUCCESS; + + // TODO: FIXME: must drop the msgs when server failed. + msgs.push_back(msg); + + return ret; +} + +int SrsForwarder::cycle() +{ + int ret = ERROR_SUCCESS; + + if ((ret = connect_server()) != ERROR_SUCCESS) { + return ret; + } + srs_assert(client); + + client->set_recv_timeout(SRS_RECV_TIMEOUT_US); + client->set_send_timeout(SRS_SEND_TIMEOUT_US); + + if ((ret = client->handshake()) != ERROR_SUCCESS) { + srs_error("handshake with server failed. ret=%d", ret); + return ret; + } + if ((ret = client->connect_app(app, tc_url)) != ERROR_SUCCESS) { + srs_error("connect with server failed, tcUrl=%s. ret=%d", tc_url.c_str(), ret); + return ret; + } + if ((ret = client->create_stream(stream_id)) != ERROR_SUCCESS) { + srs_error("connect with server failed, stream_id=%d. ret=%d", stream_id, ret); + return ret; + } + + // TODO: FIXME: need to cache the metadata and sequence header when reconnect. + + if ((ret = client->publish(stream_name, stream_id)) != ERROR_SUCCESS) { + srs_error("connect with server failed, stream_name=%s, stream_id=%d. ret=%d", + stream_name.c_str(), stream_id, ret); + return ret; + } + + if ((ret = forward()) != ERROR_SUCCESS) { + return ret; + } + + return ret; +} + +void SrsForwarder::close_underlayer_socket() +{ + srs_close_stfd(stfd); +} + +int SrsForwarder::connect_server() +{ + int ret = ERROR_SUCCESS; + + // reopen + close_underlayer_socket(); + + // open socket. + srs_trace("forward stream=%s, tcUrl=%s to server=%s, port=%d", + stream_name.c_str(), tc_url.c_str(), server.c_str(), port); + + int sock = socket(AF_INET, SOCK_STREAM, 0); + if(sock == -1){ + ret = ERROR_SOCKET_CREATE; + srs_error("create socket error. ret=%d", ret); + return ret; + } + + srs_assert(!stfd); + stfd = st_netfd_open_socket(sock); + if(stfd == NULL){ + ret = ERROR_ST_OPEN_SOCKET; + srs_error("st_netfd_open_socket failed. ret=%d", ret); + return ret; + } + + srs_freep(client); + client = new SrsRtmpClient(stfd); + + // connect to server. + std::string ip = srs_dns_resolve(server); + if (ip.empty()) { + ret = ERROR_SYSTEM_IP_INVALID; + srs_error("dns resolve server error, ip empty. ret=%d", ret); + return ret; + } + + sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = inet_addr(ip.c_str()); + + if (st_connect(stfd, (const struct sockaddr*)&addr, sizeof(sockaddr_in), ST_UTIME_NO_TIMEOUT) == -1){ + ret = ERROR_ST_CONNECT; + srs_error("connect to server error. ip=%s, port=%d, ret=%d", ip.c_str(), port, ret); + return ret; + } + srs_trace("connect to server success. server=%s, ip=%s, port=%d", server.c_str(), ip.c_str(), port); + + return ret; +} + +int SrsForwarder::forward() +{ + int ret = ERROR_SUCCESS; + + client->set_recv_timeout(SRS_PULSE_TIMEOUT_MS * 1000); + + SrsPithyPrint pithy_print(SRS_STAGE_FORWARDER); + + while (true) { + // switch to other st-threads. + st_usleep(0); + + // read from client. + if (true) { + SrsCommonMessage* msg = NULL; + ret = client->recv_message(&msg); + + srs_verbose("play loop recv message. ret=%d", ret); + if (ret != ERROR_SUCCESS && ret != ERROR_SOCKET_TIMEOUT) { + srs_error("recv server control message failed. ret=%d", ret); + return ret; + } + } + + // ignore when no messages. + int count = (int)msgs.size(); + if (msgs.empty()) { + continue; + } + + // pithy print + pithy_print.elapse(SRS_PULSE_TIMEOUT_MS); + if (pithy_print.can_print()) { + srs_trace("-> time=%"PRId64", msgs=%d, obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d", + pithy_print.get_age(), count, client->get_send_bytes(), client->get_recv_bytes(), client->get_send_kbps(), client->get_recv_kbps()); + } + + // all msgs to forward. + int i = 0; + for (i = 0; i < count; i++) { + SrsSharedPtrMessage* msg = msgs[i]; + msgs[i] = NULL; + + // we erased the sendout messages, the msg must not be NULL. + srs_assert(msg); + + ret = client->send_message(msg); + if (ret != ERROR_SUCCESS) { + srs_error("forwarder send message to server failed. ret=%d", ret); + + // convert the index to count when error. + i++; + + break; + } + } + + // clear sendout mesages. + if (i < count) { + srs_warn("clear forwarded msg, total=%d, forwarded=%d, ret=%d", count, i, ret); + } else { + srs_info("clear forwarded msg, total=%d, forwarded=%d, ret=%d", count, i, ret); + } + msgs.erase(msgs.begin(), msgs.begin() + i); + + if (ret != ERROR_SUCCESS) { + break; + } + } + + return ret; +} + diff --git a/trunk/src/core/srs_core_forward.hpp b/trunk/src/core/srs_core_forward.hpp index 405ea94a7..01bb7251b 100644 --- a/trunk/src/core/srs_core_forward.hpp +++ b/trunk/src/core/srs_core_forward.hpp @@ -32,7 +32,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include -#include +#include class SrsSharedPtrMessage; class SrsOnMetaDataPacket; @@ -42,7 +42,7 @@ class SrsRequest; /** * forward the stream to other servers. */ -class SrsForwarder +class SrsForwarder : public ISrsThreadHandler { private: std::string app; @@ -53,8 +53,7 @@ private: int port; private: st_netfd_t stfd; - st_thread_t tid; - bool loop; + SrsThread* pthread; private: SrsRtmpClient* client; std::vector msgs; @@ -67,14 +66,13 @@ public: virtual int on_meta_data(SrsSharedPtrMessage* metadata); virtual int on_audio(SrsSharedPtrMessage* msg); virtual int on_video(SrsSharedPtrMessage* msg); -private: - virtual int open_socket(); - virtual int connect_server(); -private: +// interface ISrsThreadHandler. +public: virtual int cycle(); +private: + virtual void close_underlayer_socket(); + virtual int connect_server(); virtual int forward(); - virtual void forward_cycle(); - static void* forward_thread(void* arg); }; #endif diff --git a/trunk/src/core/srs_core_http.cpp b/trunk/src/core/srs_core_http.cpp index 461d5cd2d..867f3635f 100644 --- a/trunk/src/core/srs_core_http.cpp +++ b/trunk/src/core/srs_core_http.cpp @@ -184,15 +184,7 @@ void SrsHttpClient::disconnect() { connected = false; - if (stfd) { - int fd = st_netfd_fileno(stfd); - st_netfd_close(stfd); - stfd = NULL; - - // st does not close it sometimes, - // close it manually. - ::close(fd); - } + srs_close_stfd(stfd); } int SrsHttpClient::connect(SrsHttpUri* uri) diff --git a/trunk/src/core/srs_core_http.hpp b/trunk/src/core/srs_core_http.hpp index 0848dd66b..0989717bb 100644 --- a/trunk/src/core/srs_core_http.hpp +++ b/trunk/src/core/srs_core_http.hpp @@ -36,7 +36,6 @@ class SrsSocket; #include -#include #include #define SRS_HTTP_HEADER_BUFFER 1024 diff --git a/trunk/src/core/srs_core_log.cpp b/trunk/src/core/srs_core_log.cpp index 3b74f3006..27aa5ae0a 100644 --- a/trunk/src/core/srs_core_log.cpp +++ b/trunk/src/core/srs_core_log.cpp @@ -29,8 +29,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include -#include - ILogContext::ILogContext() { } diff --git a/trunk/src/core/srs_core_protocol.hpp b/trunk/src/core/srs_core_protocol.hpp index fe52d5872..9cda280ca 100644 --- a/trunk/src/core/srs_core_protocol.hpp +++ b/trunk/src/core/srs_core_protocol.hpp @@ -33,8 +33,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include -#include - #include #include diff --git a/trunk/src/core/srs_core_rtmp.cpp b/trunk/src/core/srs_core_rtmp.cpp old mode 100755 new mode 100644 index 9c5e63d50..0fccf4de3 --- a/trunk/src/core/srs_core_rtmp.cpp +++ b/trunk/src/core/srs_core_rtmp.cpp @@ -1,1127 +1,1127 @@ -/* -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 - -/** -* the signature for packets to client. -*/ -#define RTMP_SIG_FMS_VER "3,5,3,888" -#define RTMP_SIG_AMF0_VER 0 -#define RTMP_SIG_CLIENT_ID "ASAICiss" - -/** -* onStatus consts. -*/ -#define StatusLevel "level" -#define StatusCode "code" -#define StatusDescription "description" -#define StatusDetails "details" -#define StatusClientId "clientid" -// status value -#define StatusLevelStatus "status" -// code value -#define StatusCodeConnectSuccess "NetConnection.Connect.Success" -#define StatusCodeStreamReset "NetStream.Play.Reset" -#define StatusCodeStreamStart "NetStream.Play.Start" -#define StatusCodeStreamPause "NetStream.Pause.Notify" -#define StatusCodeStreamUnpause "NetStream.Unpause.Notify" -#define StatusCodePublishStart "NetStream.Publish.Start" -#define StatusCodeDataStart "NetStream.Data.Start" -#define StatusCodeUnpublishSuccess "NetStream.Unpublish.Success" - -// FMLE -#define RTMP_AMF0_COMMAND_ON_FC_PUBLISH "onFCPublish" -#define RTMP_AMF0_COMMAND_ON_FC_UNPUBLISH "onFCUnpublish" - -// default stream id for response the createStream request. -#define SRS_DEFAULT_SID 1 - -SrsRequest::SrsRequest() -{ - objectEncoding = RTMP_SIG_AMF0_VER; -} - -SrsRequest::~SrsRequest() -{ -} - -int SrsRequest::discovery_app() -{ - int ret = ERROR_SUCCESS; - - size_t pos = std::string::npos; - std::string url = tcUrl; - - if ((pos = url.find("://")) != std::string::npos) { - schema = url.substr(0, pos); - url = url.substr(schema.length() + 3); - srs_verbose("discovery schema=%s", schema.c_str()); - } - - if ((pos = url.find("/")) != std::string::npos) { - vhost = url.substr(0, pos); - url = url.substr(vhost.length() + 1); - srs_verbose("discovery vhost=%s", vhost.c_str()); - } - - port = RTMP_DEFAULT_PORTS; - if ((pos = vhost.find(":")) != std::string::npos) { - port = vhost.substr(pos + 1); - vhost = vhost.substr(0, pos); - srs_verbose("discovery vhost=%s, port=%s", vhost.c_str(), port.c_str()); - } - - app = url; - srs_vhost_resolve(vhost, app); - - // resolve the vhost from config - SrsConfDirective* parsed_vhost = config->get_vhost(vhost); - if (parsed_vhost) { - vhost = parsed_vhost->arg0(); - } - - // TODO: discovery the params of vhost. - - srs_info("discovery app success. schema=%s, vhost=%s, port=%s, app=%s", - schema.c_str(), vhost.c_str(), port.c_str(), app.c_str()); - - if (schema.empty() || vhost.empty() || port.empty() || app.empty()) { - ret = ERROR_RTMP_REQ_TCURL; - srs_error("discovery tcUrl failed. " - "tcUrl=%s, schema=%s, vhost=%s, port=%s, app=%s, ret=%d", - tcUrl.c_str(), schema.c_str(), vhost.c_str(), port.c_str(), app.c_str(), ret); - return ret; - } - - strip(); - - return ret; -} - -std::string SrsRequest::get_stream_url() -{ - std::string url = ""; - - url += vhost; - url += "/"; - url += app; - url += "/"; - url += stream; - - return url; -} - -void SrsRequest::strip() -{ - trim(vhost, "/ \n\r\t"); - trim(app, "/ \n\r\t"); - trim(stream, "/ \n\r\t"); -} - -std::string& SrsRequest::trim(std::string& str, std::string chs) -{ - for (int i = 0; i < (int)chs.length(); i++) { - char ch = chs.at(i); - - for (std::string::iterator it = str.begin(); it != str.end();) { - if (ch == *it) { - it = str.erase(it); - } else { - ++it; - } - } - } - - return str; -} - -SrsResponse::SrsResponse() -{ - stream_id = SRS_DEFAULT_SID; -} - -SrsResponse::~SrsResponse() -{ -} - -SrsRtmpClient::SrsRtmpClient(st_netfd_t _stfd) -{ - stfd = _stfd; - protocol = new SrsProtocol(stfd); -} - -SrsRtmpClient::~SrsRtmpClient() -{ - srs_freep(protocol); -} - -void SrsRtmpClient::set_recv_timeout(int64_t timeout_us) -{ - protocol->set_recv_timeout(timeout_us); -} - -void SrsRtmpClient::set_send_timeout(int64_t timeout_us) -{ - protocol->set_send_timeout(timeout_us); -} - -int64_t SrsRtmpClient::get_recv_bytes() -{ - return protocol->get_recv_bytes(); -} - -int64_t SrsRtmpClient::get_send_bytes() -{ - return protocol->get_send_bytes(); -} - -int SrsRtmpClient::get_recv_kbps() -{ - return protocol->get_recv_kbps(); -} - -int SrsRtmpClient::get_send_kbps() -{ - return protocol->get_send_kbps(); -} - -int SrsRtmpClient::recv_message(SrsCommonMessage** pmsg) -{ - return protocol->recv_message(pmsg); -} - -int SrsRtmpClient::send_message(ISrsMessage* msg) -{ - return protocol->send_message(msg); -} - -int SrsRtmpClient::handshake() -{ - int ret = ERROR_SUCCESS; - - SrsSocket skt(stfd); - - SrsComplexHandshake complex_hs; - SrsSimpleHandshake simple_hs; - if ((ret = simple_hs.handshake_with_server(skt, complex_hs)) != ERROR_SUCCESS) { - return ret; - } - - return ret; -} - -int SrsRtmpClient::connect_app(std::string app, std::string tc_url) -{ - int ret = ERROR_SUCCESS; - - // Connect(vhost, app) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsConnectAppPacket* pkt = new SrsConnectAppPacket(); - msg->set_packet(pkt, 0); - - pkt->command_object = new SrsAmf0Object(); - pkt->command_object->set("app", new SrsAmf0String(app.c_str())); - pkt->command_object->set("swfUrl", new SrsAmf0String()); - pkt->command_object->set("tcUrl", new SrsAmf0String(tc_url.c_str())); - pkt->command_object->set("fpad", new SrsAmf0Boolean(false)); - pkt->command_object->set("capabilities", new SrsAmf0Number(239)); - pkt->command_object->set("audioCodecs", new SrsAmf0Number(3575)); - pkt->command_object->set("videoCodecs", new SrsAmf0Number(252)); - pkt->command_object->set("videoFunction", new SrsAmf0Number(1)); - pkt->command_object->set("pageUrl", new SrsAmf0String()); - pkt->command_object->set("objectEncoding", new SrsAmf0Number(0)); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - return ret; - } - } - - // Set Window Acknowledgement size(2500000) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsSetWindowAckSizePacket* pkt = new SrsSetWindowAckSizePacket(); - - pkt->ackowledgement_window_size = 2500000; - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - return ret; - } - } - - // expect connect _result - SrsCommonMessage* msg = NULL; - SrsConnectAppResPacket* pkt = NULL; - if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { - srs_error("expect connect app response message failed. ret=%d", ret); - return ret; - } - SrsAutoFree(SrsCommonMessage, msg, false); - srs_info("get connect app response message"); - - return ret; -} - -int SrsRtmpClient::create_stream(int& stream_id) -{ - int ret = ERROR_SUCCESS; - - // CreateStream - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsCreateStreamPacket* pkt = new SrsCreateStreamPacket(); - - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - return ret; - } - } - - // CreateStream _result. - if (true) { - SrsCommonMessage* msg = NULL; - SrsCreateStreamResPacket* pkt = NULL; - if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { - srs_error("expect create stream response message failed. ret=%d", ret); - return ret; - } - SrsAutoFree(SrsCommonMessage, msg, false); - srs_info("get create stream response message"); - - stream_id = (int)pkt->stream_id; - } - - return ret; -} - -int SrsRtmpClient::play(std::string stream, int stream_id) -{ - int ret = ERROR_SUCCESS; - - // Play(stream) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsPlayPacket* pkt = new SrsPlayPacket(); - - pkt->stream_name = stream; - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send play stream failed. " - "stream=%s, stream_id=%d, ret=%d", - stream.c_str(), stream_id, ret); - return ret; - } - } - - // SetBufferLength(1000ms) - int buffer_length_ms = 1000; - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsUserControlPacket* pkt = new SrsUserControlPacket(); - - pkt->event_type = SrcPCUCSetBufferLength; - pkt->event_data = stream_id; - pkt->extra_data = buffer_length_ms; - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send set buffer length failed. " - "stream=%s, stream_id=%d, bufferLength=%d, ret=%d", - stream.c_str(), stream_id, buffer_length_ms, ret); - return ret; - } - } - - return ret; -} - -int SrsRtmpClient::publish(std::string stream, int stream_id) -{ - int ret = ERROR_SUCCESS; - - // publish(stream) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsPublishPacket* pkt = new SrsPublishPacket(); - - pkt->stream_name = stream; - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send publish message failed. " - "stream=%s, stream_id=%d, ret=%d", - stream.c_str(), stream_id, ret); - return ret; - } - } - - return ret; -} - -SrsRtmp::SrsRtmp(st_netfd_t client_stfd) -{ - protocol = new SrsProtocol(client_stfd); - stfd = client_stfd; -} - -SrsRtmp::~SrsRtmp() -{ - srs_freep(protocol); -} - -SrsProtocol* SrsRtmp::get_protocol() -{ - return protocol; -} - -void SrsRtmp::set_recv_timeout(int64_t timeout_us) -{ - protocol->set_recv_timeout(timeout_us); -} - -int64_t SrsRtmp::get_recv_timeout() -{ - return protocol->get_recv_timeout(); -} - -void SrsRtmp::set_send_timeout(int64_t timeout_us) -{ - protocol->set_send_timeout(timeout_us); -} - -int64_t SrsRtmp::get_recv_bytes() -{ - return protocol->get_recv_bytes(); -} - -int64_t SrsRtmp::get_send_bytes() -{ - return protocol->get_send_bytes(); -} - -int SrsRtmp::get_recv_kbps() -{ - return protocol->get_recv_kbps(); -} - -int SrsRtmp::get_send_kbps() -{ - return protocol->get_send_kbps(); -} - -int SrsRtmp::recv_message(SrsCommonMessage** pmsg) -{ - return protocol->recv_message(pmsg); -} - -int SrsRtmp::send_message(ISrsMessage* msg) -{ - return protocol->send_message(msg); -} - -int SrsRtmp::handshake() -{ - int ret = ERROR_SUCCESS; - - SrsSocket skt(stfd); - - SrsComplexHandshake complex_hs; - SrsSimpleHandshake simple_hs; - if ((ret = simple_hs.handshake_with_client(skt, complex_hs)) != ERROR_SUCCESS) { - return ret; - } - - return ret; -} - -int SrsRtmp::connect_app(SrsRequest* req) -{ - int ret = ERROR_SUCCESS; - - SrsCommonMessage* msg = NULL; - SrsConnectAppPacket* pkt = NULL; - if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { - srs_error("expect connect app message failed. ret=%d", ret); - return ret; - } - SrsAutoFree(SrsCommonMessage, msg, false); - srs_info("get connect app message"); - - SrsAmf0Any* prop = NULL; - - if ((prop = pkt->command_object->ensure_property_string("tcUrl")) == NULL) { - ret = ERROR_RTMP_REQ_CONNECT; - srs_error("invalid request, must specifies the tcUrl. ret=%d", ret); - return ret; - } - req->tcUrl = srs_amf0_convert(prop)->value; - - if ((prop = pkt->command_object->ensure_property_string("pageUrl")) != NULL) { - req->pageUrl = srs_amf0_convert(prop)->value; - } - - if ((prop = pkt->command_object->ensure_property_string("swfUrl")) != NULL) { - req->swfUrl = srs_amf0_convert(prop)->value; - } - - if ((prop = pkt->command_object->ensure_property_number("objectEncoding")) != NULL) { - req->objectEncoding = srs_amf0_convert(prop)->value; - } - - srs_info("get connect app message params success."); - - return req->discovery_app(); -} - -int SrsRtmp::set_window_ack_size(int ack_size) -{ - int ret = ERROR_SUCCESS; - - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsSetWindowAckSizePacket* pkt = new SrsSetWindowAckSizePacket(); - - pkt->ackowledgement_window_size = ack_size; - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send ack size message failed. ret=%d", ret); - return ret; - } - srs_info("send ack size message success. ack_size=%d", ack_size); - - return ret; -} - -int SrsRtmp::set_peer_bandwidth(int bandwidth, int type) -{ - int ret = ERROR_SUCCESS; - - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsSetPeerBandwidthPacket* pkt = new SrsSetPeerBandwidthPacket(); - - pkt->bandwidth = bandwidth; - pkt->type = type; - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send set bandwidth message failed. ret=%d", ret); - return ret; - } - srs_info("send set bandwidth message " - "success. bandwidth=%d, type=%d", bandwidth, type); - - return ret; -} - -int SrsRtmp::response_connect_app(SrsRequest* req) -{ - int ret = ERROR_SUCCESS; - - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsConnectAppResPacket* pkt = new SrsConnectAppResPacket(); - - pkt->props->set("fmsVer", new SrsAmf0String("FMS/"RTMP_SIG_FMS_VER)); - pkt->props->set("capabilities", new SrsAmf0Number(127)); - pkt->props->set("mode", new SrsAmf0Number(1)); - - pkt->info->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); - pkt->info->set(StatusCode, new SrsAmf0String(StatusCodeConnectSuccess)); - pkt->info->set(StatusDescription, new SrsAmf0String("Connection succeeded")); - pkt->info->set("objectEncoding", new SrsAmf0Number(req->objectEncoding)); - SrsASrsAmf0EcmaArray* data = new SrsASrsAmf0EcmaArray(); - pkt->info->set("data", data); - - data->set("srs_version", new SrsAmf0String(RTMP_SIG_FMS_VER)); - data->set("srs_server", new SrsAmf0String(RTMP_SIG_SRS_NAME)); - data->set("srs_license", new SrsAmf0String(RTMP_SIG_SRS_LICENSE)); - data->set("srs_role", new SrsAmf0String(RTMP_SIG_SRS_ROLE)); - data->set("srs_url", new SrsAmf0String(RTMP_SIG_SRS_URL)); - data->set("srs_version", new SrsAmf0String(RTMP_SIG_SRS_VERSION)); - data->set("srs_site", new SrsAmf0String(RTMP_SIG_SRS_WEB)); - data->set("srs_email", new SrsAmf0String(RTMP_SIG_SRS_EMAIL)); - data->set("srs_copyright", new SrsAmf0String(RTMP_SIG_SRS_COPYRIGHT)); - - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send connect app response message failed. ret=%d", ret); - return ret; - } - srs_info("send connect app response message success."); - - return ret; -} - -int SrsRtmp::on_bw_done() -{ - int ret = ERROR_SUCCESS; - - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnBWDonePacket* pkt = new SrsOnBWDonePacket(); - - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onBWDone message failed. ret=%d", ret); - return ret; - } - srs_info("send onBWDone message success."); - - return ret; -} - -int SrsRtmp::identify_client(int stream_id, SrsClientType& type, std::string& stream_name) -{ - type = SrsClientUnknown; - int ret = ERROR_SUCCESS; - - while (true) { - SrsCommonMessage* msg = NULL; - if ((ret = protocol->recv_message(&msg)) != ERROR_SUCCESS) { - srs_error("recv identify client message failed. ret=%d", ret); - return ret; - } - - SrsAutoFree(SrsCommonMessage, msg, false); - - if (!msg->header.is_amf0_command() && !msg->header.is_amf3_command()) { - srs_trace("identify ignore messages except " - "AMF0/AMF3 command message. type=%#x", msg->header.message_type); - continue; - } - - if ((ret = msg->decode_packet(protocol)) != ERROR_SUCCESS) { - srs_error("identify decode message failed. ret=%d", ret); - return ret; - } - - SrsPacket* pkt = msg->get_packet(); - if (dynamic_cast(pkt)) { - srs_info("identify client by create stream, play or flash publish."); - return identify_create_stream_client( - dynamic_cast(pkt), stream_id, type, stream_name); - } - if (dynamic_cast(pkt)) { - srs_info("identify client by releaseStream, fmle publish."); - return identify_fmle_publish_client( - dynamic_cast(pkt), type, stream_name); - } - - srs_trace("ignore AMF0/AMF3 command message."); - } - - return ret; -} - -int SrsRtmp::set_chunk_size(int chunk_size) -{ - int ret = ERROR_SUCCESS; - - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsSetChunkSizePacket* pkt = new SrsSetChunkSizePacket(); - - pkt->chunk_size = chunk_size; - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send set chunk size message failed. ret=%d", ret); - return ret; - } - srs_info("send set chunk size message success. chunk_size=%d", chunk_size); - - return ret; -} - -int SrsRtmp::start_play(int stream_id) -{ - int ret = ERROR_SUCCESS; - - // StreamBegin - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsUserControlPacket* pkt = new SrsUserControlPacket(); - - pkt->event_type = SrcPCUCStreamBegin; - pkt->event_data = stream_id; - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send PCUC(StreamBegin) message failed. ret=%d", ret); - return ret; - } - srs_info("send PCUC(StreamBegin) message success."); - } - - // onStatus(NetStream.Play.Reset) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); - pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamReset)); - pkt->data->set(StatusDescription, new SrsAmf0String("Playing and resetting stream.")); - pkt->data->set(StatusDetails, new SrsAmf0String("stream")); - pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID)); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onStatus(NetStream.Play.Reset) message failed. ret=%d", ret); - return ret; - } - srs_info("send onStatus(NetStream.Play.Reset) message success."); - } - - // onStatus(NetStream.Play.Start) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); - pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamStart)); - pkt->data->set(StatusDescription, new SrsAmf0String("Started playing stream.")); - pkt->data->set(StatusDetails, new SrsAmf0String("stream")); - pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID)); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onStatus(NetStream.Play.Reset) message failed. ret=%d", ret); - return ret; - } - srs_info("send onStatus(NetStream.Play.Reset) message success."); - } - - // |RtmpSampleAccess(false, false) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsSampleAccessPacket* pkt = new SrsSampleAccessPacket(); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send |RtmpSampleAccess(false, false) message failed. ret=%d", ret); - return ret; - } - srs_info("send |RtmpSampleAccess(false, false) message success."); - } - - // onStatus(NetStream.Data.Start) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnStatusDataPacket* pkt = new SrsOnStatusDataPacket(); - - pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeDataStart)); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onStatus(NetStream.Data.Start) message failed. ret=%d", ret); - return ret; - } - srs_info("send onStatus(NetStream.Data.Start) message success."); - } - - srs_info("start play success."); - - return ret; -} - -int SrsRtmp::on_play_client_pause(int stream_id, bool is_pause) -{ - int ret = ERROR_SUCCESS; - - if (is_pause) { - // onStatus(NetStream.Pause.Notify) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); - pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamPause)); - pkt->data->set(StatusDescription, new SrsAmf0String("Paused stream.")); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onStatus(NetStream.Pause.Notify) message failed. ret=%d", ret); - return ret; - } - srs_info("send onStatus(NetStream.Pause.Notify) message success."); - } - // StreamEOF - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsUserControlPacket* pkt = new SrsUserControlPacket(); - - pkt->event_type = SrcPCUCStreamEOF; - pkt->event_data = stream_id; - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send PCUC(StreamEOF) message failed. ret=%d", ret); - return ret; - } - srs_info("send PCUC(StreamEOF) message success."); - } - } else { - // onStatus(NetStream.Unpause.Notify) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); - pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamUnpause)); - pkt->data->set(StatusDescription, new SrsAmf0String("Unpaused stream.")); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onStatus(NetStream.Unpause.Notify) message failed. ret=%d", ret); - return ret; - } - srs_info("send onStatus(NetStream.Unpause.Notify) message success."); - } - // StreanBegin - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsUserControlPacket* pkt = new SrsUserControlPacket(); - - pkt->event_type = SrcPCUCStreamBegin; - pkt->event_data = stream_id; - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send PCUC(StreanBegin) message failed. ret=%d", ret); - return ret; - } - srs_info("send PCUC(StreanBegin) message success."); - } - } - - return ret; -} - -int SrsRtmp::start_fmle_publish(int stream_id) -{ - int ret = ERROR_SUCCESS; - - // FCPublish - double fc_publish_tid = 0; - if (true) { - SrsCommonMessage* msg = NULL; - SrsFMLEStartPacket* pkt = NULL; - if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { - srs_error("recv FCPublish message failed. ret=%d", ret); - return ret; - } - srs_info("recv FCPublish request message success."); - - SrsAutoFree(SrsCommonMessage, msg, false); - fc_publish_tid = pkt->transaction_id; - } - // FCPublish response - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(fc_publish_tid); - - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send FCPublish response message failed. ret=%d", ret); - return ret; - } - srs_info("send FCPublish response message success."); - } - - // createStream - double create_stream_tid = 0; - if (true) { - SrsCommonMessage* msg = NULL; - SrsCreateStreamPacket* pkt = NULL; - if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { - srs_error("recv createStream message failed. ret=%d", ret); - return ret; - } - srs_info("recv createStream request message success."); - - SrsAutoFree(SrsCommonMessage, msg, false); - create_stream_tid = pkt->transaction_id; - } - // createStream response - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsCreateStreamResPacket* pkt = new SrsCreateStreamResPacket(create_stream_tid, stream_id); - - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send createStream response message failed. ret=%d", ret); - return ret; - } - srs_info("send createStream response message success."); - } - - // publish - if (true) { - SrsCommonMessage* msg = NULL; - SrsPublishPacket* pkt = NULL; - if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { - srs_error("recv publish message failed. ret=%d", ret); - return ret; - } - srs_info("recv publish request message success."); - - SrsAutoFree(SrsCommonMessage, msg, false); - } - // publish response onFCPublish(NetStream.Publish.Start) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->command_name = RTMP_AMF0_COMMAND_ON_FC_PUBLISH; - pkt->data->set(StatusCode, new SrsAmf0String(StatusCodePublishStart)); - pkt->data->set(StatusDescription, new SrsAmf0String("Started publishing stream.")); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onFCPublish(NetStream.Publish.Start) message failed. ret=%d", ret); - return ret; - } - srs_info("send onFCPublish(NetStream.Publish.Start) message success."); - } - // publish response onStatus(NetStream.Publish.Start) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); - pkt->data->set(StatusCode, new SrsAmf0String(StatusCodePublishStart)); - pkt->data->set(StatusDescription, new SrsAmf0String("Started publishing stream.")); - pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID)); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onStatus(NetStream.Publish.Start) message failed. ret=%d", ret); - return ret; - } - srs_info("send onStatus(NetStream.Publish.Start) message success."); - } - - srs_info("FMLE publish success."); - - return ret; -} - -int SrsRtmp::fmle_unpublish(int stream_id, double unpublish_tid) -{ - int ret = ERROR_SUCCESS; - - // publish response onFCUnpublish(NetStream.unpublish.Success) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->command_name = RTMP_AMF0_COMMAND_ON_FC_UNPUBLISH; - pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeUnpublishSuccess)); - pkt->data->set(StatusDescription, new SrsAmf0String("Stop publishing stream.")); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onFCUnpublish(NetStream.unpublish.Success) message failed. ret=%d", ret); - return ret; - } - srs_info("send onFCUnpublish(NetStream.unpublish.Success) message success."); - } - // FCUnpublish response - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(unpublish_tid); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send FCUnpublish response message failed. ret=%d", ret); - return ret; - } - srs_info("send FCUnpublish response message success."); - } - // publish response onStatus(NetStream.Unpublish.Success) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); - pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeUnpublishSuccess)); - pkt->data->set(StatusDescription, new SrsAmf0String("Stream is now unpublished")); - pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID)); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onStatus(NetStream.Unpublish.Success) message failed. ret=%d", ret); - return ret; - } - srs_info("send onStatus(NetStream.Unpublish.Success) message success."); - } - - srs_info("FMLE unpublish success."); - - return ret; -} - -int SrsRtmp::start_flash_publish(int stream_id) -{ - int ret = ERROR_SUCCESS; - - // publish response onStatus(NetStream.Publish.Start) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); - pkt->data->set(StatusCode, new SrsAmf0String(StatusCodePublishStart)); - pkt->data->set(StatusDescription, new SrsAmf0String("Started publishing stream.")); - pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID)); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onStatus(NetStream.Publish.Start) message failed. ret=%d", ret); - return ret; - } - srs_info("send onStatus(NetStream.Publish.Start) message success."); - } - - srs_info("flash publish success."); - - return ret; -} - -int SrsRtmp::identify_create_stream_client(SrsCreateStreamPacket* req, int stream_id, SrsClientType& type, std::string& stream_name) -{ - int ret = ERROR_SUCCESS; - - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsCreateStreamResPacket* pkt = new SrsCreateStreamResPacket(req->transaction_id, stream_id); - - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send createStream response message failed. ret=%d", ret); - return ret; - } - srs_info("send createStream response message success."); - } - - while (true) { - SrsCommonMessage* msg = NULL; - if ((ret = protocol->recv_message(&msg)) != ERROR_SUCCESS) { - srs_error("recv identify client message failed. ret=%d", ret); - return ret; - } - - SrsAutoFree(SrsCommonMessage, msg, false); - - if (!msg->header.is_amf0_command() && !msg->header.is_amf3_command()) { - srs_trace("identify ignore messages except " - "AMF0/AMF3 command message. type=%#x", msg->header.message_type); - continue; - } - - if ((ret = msg->decode_packet(protocol)) != ERROR_SUCCESS) { - srs_error("identify decode message failed. ret=%d", ret); - return ret; - } - - SrsPacket* pkt = msg->get_packet(); - if (dynamic_cast(pkt)) { - SrsPlayPacket* play = dynamic_cast(pkt); - type = SrsClientPlay; - stream_name = play->stream_name; - srs_trace("identity client type=play, stream_name=%s", stream_name.c_str()); - return ret; - } - if (dynamic_cast(pkt)) { - srs_info("identify client by publish, falsh publish."); - return identify_flash_publish_client( - dynamic_cast(pkt), type, stream_name); - } - - srs_trace("ignore AMF0/AMF3 command message."); - } - - return ret; -} - -int SrsRtmp::identify_fmle_publish_client(SrsFMLEStartPacket* req, SrsClientType& type, std::string& stream_name) -{ - int ret = ERROR_SUCCESS; - - type = SrsClientFMLEPublish; - stream_name = req->stream_name; - - // releaseStream response - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(req->transaction_id); - - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send releaseStream response message failed. ret=%d", ret); - return ret; - } - srs_info("send releaseStream response message success."); - } - - return ret; -} - -int SrsRtmp::identify_flash_publish_client(SrsPublishPacket* req, SrsClientType& type, std::string& stream_name) -{ - int ret = ERROR_SUCCESS; - - type = SrsClientFlashPublish; - stream_name = req->stream_name; - - 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 + +/** +* the signature for packets to client. +*/ +#define RTMP_SIG_FMS_VER "3,5,3,888" +#define RTMP_SIG_AMF0_VER 0 +#define RTMP_SIG_CLIENT_ID "ASAICiss" + +/** +* onStatus consts. +*/ +#define StatusLevel "level" +#define StatusCode "code" +#define StatusDescription "description" +#define StatusDetails "details" +#define StatusClientId "clientid" +// status value +#define StatusLevelStatus "status" +// code value +#define StatusCodeConnectSuccess "NetConnection.Connect.Success" +#define StatusCodeStreamReset "NetStream.Play.Reset" +#define StatusCodeStreamStart "NetStream.Play.Start" +#define StatusCodeStreamPause "NetStream.Pause.Notify" +#define StatusCodeStreamUnpause "NetStream.Unpause.Notify" +#define StatusCodePublishStart "NetStream.Publish.Start" +#define StatusCodeDataStart "NetStream.Data.Start" +#define StatusCodeUnpublishSuccess "NetStream.Unpublish.Success" + +// FMLE +#define RTMP_AMF0_COMMAND_ON_FC_PUBLISH "onFCPublish" +#define RTMP_AMF0_COMMAND_ON_FC_UNPUBLISH "onFCUnpublish" + +// default stream id for response the createStream request. +#define SRS_DEFAULT_SID 1 + +SrsRequest::SrsRequest() +{ + objectEncoding = RTMP_SIG_AMF0_VER; +} + +SrsRequest::~SrsRequest() +{ +} + +int SrsRequest::discovery_app() +{ + int ret = ERROR_SUCCESS; + + size_t pos = std::string::npos; + std::string url = tcUrl; + + if ((pos = url.find("://")) != std::string::npos) { + schema = url.substr(0, pos); + url = url.substr(schema.length() + 3); + srs_verbose("discovery schema=%s", schema.c_str()); + } + + if ((pos = url.find("/")) != std::string::npos) { + vhost = url.substr(0, pos); + url = url.substr(vhost.length() + 1); + srs_verbose("discovery vhost=%s", vhost.c_str()); + } + + port = RTMP_DEFAULT_PORTS; + if ((pos = vhost.find(":")) != std::string::npos) { + port = vhost.substr(pos + 1); + vhost = vhost.substr(0, pos); + srs_verbose("discovery vhost=%s, port=%s", vhost.c_str(), port.c_str()); + } + + app = url; + srs_vhost_resolve(vhost, app); + + // resolve the vhost from config + SrsConfDirective* parsed_vhost = config->get_vhost(vhost); + if (parsed_vhost) { + vhost = parsed_vhost->arg0(); + } + + // TODO: discovery the params of vhost. + + srs_info("discovery app success. schema=%s, vhost=%s, port=%s, app=%s", + schema.c_str(), vhost.c_str(), port.c_str(), app.c_str()); + + if (schema.empty() || vhost.empty() || port.empty() || app.empty()) { + ret = ERROR_RTMP_REQ_TCURL; + srs_error("discovery tcUrl failed. " + "tcUrl=%s, schema=%s, vhost=%s, port=%s, app=%s, ret=%d", + tcUrl.c_str(), schema.c_str(), vhost.c_str(), port.c_str(), app.c_str(), ret); + return ret; + } + + strip(); + + return ret; +} + +std::string SrsRequest::get_stream_url() +{ + std::string url = ""; + + url += vhost; + url += "/"; + url += app; + url += "/"; + url += stream; + + return url; +} + +void SrsRequest::strip() +{ + trim(vhost, "/ \n\r\t"); + trim(app, "/ \n\r\t"); + trim(stream, "/ \n\r\t"); +} + +std::string& SrsRequest::trim(std::string& str, std::string chs) +{ + for (int i = 0; i < (int)chs.length(); i++) { + char ch = chs.at(i); + + for (std::string::iterator it = str.begin(); it != str.end();) { + if (ch == *it) { + it = str.erase(it); + } else { + ++it; + } + } + } + + return str; +} + +SrsResponse::SrsResponse() +{ + stream_id = SRS_DEFAULT_SID; +} + +SrsResponse::~SrsResponse() +{ +} + +SrsRtmpClient::SrsRtmpClient(st_netfd_t _stfd) +{ + stfd = _stfd; + protocol = new SrsProtocol(stfd); +} + +SrsRtmpClient::~SrsRtmpClient() +{ + srs_freep(protocol); +} + +void SrsRtmpClient::set_recv_timeout(int64_t timeout_us) +{ + protocol->set_recv_timeout(timeout_us); +} + +void SrsRtmpClient::set_send_timeout(int64_t timeout_us) +{ + protocol->set_send_timeout(timeout_us); +} + +int64_t SrsRtmpClient::get_recv_bytes() +{ + return protocol->get_recv_bytes(); +} + +int64_t SrsRtmpClient::get_send_bytes() +{ + return protocol->get_send_bytes(); +} + +int SrsRtmpClient::get_recv_kbps() +{ + return protocol->get_recv_kbps(); +} + +int SrsRtmpClient::get_send_kbps() +{ + return protocol->get_send_kbps(); +} + +int SrsRtmpClient::recv_message(SrsCommonMessage** pmsg) +{ + return protocol->recv_message(pmsg); +} + +int SrsRtmpClient::send_message(ISrsMessage* msg) +{ + return protocol->send_message(msg); +} + +int SrsRtmpClient::handshake() +{ + int ret = ERROR_SUCCESS; + + SrsSocket skt(stfd); + + SrsComplexHandshake complex_hs; + SrsSimpleHandshake simple_hs; + if ((ret = simple_hs.handshake_with_server(skt, complex_hs)) != ERROR_SUCCESS) { + return ret; + } + + return ret; +} + +int SrsRtmpClient::connect_app(std::string app, std::string tc_url) +{ + int ret = ERROR_SUCCESS; + + // Connect(vhost, app) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsConnectAppPacket* pkt = new SrsConnectAppPacket(); + msg->set_packet(pkt, 0); + + pkt->command_object = new SrsAmf0Object(); + pkt->command_object->set("app", new SrsAmf0String(app.c_str())); + pkt->command_object->set("swfUrl", new SrsAmf0String()); + pkt->command_object->set("tcUrl", new SrsAmf0String(tc_url.c_str())); + pkt->command_object->set("fpad", new SrsAmf0Boolean(false)); + pkt->command_object->set("capabilities", new SrsAmf0Number(239)); + pkt->command_object->set("audioCodecs", new SrsAmf0Number(3575)); + pkt->command_object->set("videoCodecs", new SrsAmf0Number(252)); + pkt->command_object->set("videoFunction", new SrsAmf0Number(1)); + pkt->command_object->set("pageUrl", new SrsAmf0String()); + pkt->command_object->set("objectEncoding", new SrsAmf0Number(0)); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + return ret; + } + } + + // Set Window Acknowledgement size(2500000) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsSetWindowAckSizePacket* pkt = new SrsSetWindowAckSizePacket(); + + pkt->ackowledgement_window_size = 2500000; + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + return ret; + } + } + + // expect connect _result + SrsCommonMessage* msg = NULL; + SrsConnectAppResPacket* pkt = NULL; + if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { + srs_error("expect connect app response message failed. ret=%d", ret); + return ret; + } + SrsAutoFree(SrsCommonMessage, msg, false); + srs_info("get connect app response message"); + + return ret; +} + +int SrsRtmpClient::create_stream(int& stream_id) +{ + int ret = ERROR_SUCCESS; + + // CreateStream + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsCreateStreamPacket* pkt = new SrsCreateStreamPacket(); + + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + return ret; + } + } + + // CreateStream _result. + if (true) { + SrsCommonMessage* msg = NULL; + SrsCreateStreamResPacket* pkt = NULL; + if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { + srs_error("expect create stream response message failed. ret=%d", ret); + return ret; + } + SrsAutoFree(SrsCommonMessage, msg, false); + srs_info("get create stream response message"); + + stream_id = (int)pkt->stream_id; + } + + return ret; +} + +int SrsRtmpClient::play(std::string stream, int stream_id) +{ + int ret = ERROR_SUCCESS; + + // Play(stream) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsPlayPacket* pkt = new SrsPlayPacket(); + + pkt->stream_name = stream; + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send play stream failed. " + "stream=%s, stream_id=%d, ret=%d", + stream.c_str(), stream_id, ret); + return ret; + } + } + + // SetBufferLength(1000ms) + int buffer_length_ms = 1000; + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsUserControlPacket* pkt = new SrsUserControlPacket(); + + pkt->event_type = SrcPCUCSetBufferLength; + pkt->event_data = stream_id; + pkt->extra_data = buffer_length_ms; + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send set buffer length failed. " + "stream=%s, stream_id=%d, bufferLength=%d, ret=%d", + stream.c_str(), stream_id, buffer_length_ms, ret); + return ret; + } + } + + return ret; +} + +int SrsRtmpClient::publish(std::string stream, int stream_id) +{ + int ret = ERROR_SUCCESS; + + // publish(stream) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsPublishPacket* pkt = new SrsPublishPacket(); + + pkt->stream_name = stream; + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send publish message failed. " + "stream=%s, stream_id=%d, ret=%d", + stream.c_str(), stream_id, ret); + return ret; + } + } + + return ret; +} + +SrsRtmp::SrsRtmp(st_netfd_t client_stfd) +{ + protocol = new SrsProtocol(client_stfd); + stfd = client_stfd; +} + +SrsRtmp::~SrsRtmp() +{ + srs_freep(protocol); +} + +SrsProtocol* SrsRtmp::get_protocol() +{ + return protocol; +} + +void SrsRtmp::set_recv_timeout(int64_t timeout_us) +{ + protocol->set_recv_timeout(timeout_us); +} + +int64_t SrsRtmp::get_recv_timeout() +{ + return protocol->get_recv_timeout(); +} + +void SrsRtmp::set_send_timeout(int64_t timeout_us) +{ + protocol->set_send_timeout(timeout_us); +} + +int64_t SrsRtmp::get_recv_bytes() +{ + return protocol->get_recv_bytes(); +} + +int64_t SrsRtmp::get_send_bytes() +{ + return protocol->get_send_bytes(); +} + +int SrsRtmp::get_recv_kbps() +{ + return protocol->get_recv_kbps(); +} + +int SrsRtmp::get_send_kbps() +{ + return protocol->get_send_kbps(); +} + +int SrsRtmp::recv_message(SrsCommonMessage** pmsg) +{ + return protocol->recv_message(pmsg); +} + +int SrsRtmp::send_message(ISrsMessage* msg) +{ + return protocol->send_message(msg); +} + +int SrsRtmp::handshake() +{ + int ret = ERROR_SUCCESS; + + SrsSocket skt(stfd); + + SrsComplexHandshake complex_hs; + SrsSimpleHandshake simple_hs; + if ((ret = simple_hs.handshake_with_client(skt, complex_hs)) != ERROR_SUCCESS) { + return ret; + } + + return ret; +} + +int SrsRtmp::connect_app(SrsRequest* req) +{ + int ret = ERROR_SUCCESS; + + SrsCommonMessage* msg = NULL; + SrsConnectAppPacket* pkt = NULL; + if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { + srs_error("expect connect app message failed. ret=%d", ret); + return ret; + } + SrsAutoFree(SrsCommonMessage, msg, false); + srs_info("get connect app message"); + + SrsAmf0Any* prop = NULL; + + if ((prop = pkt->command_object->ensure_property_string("tcUrl")) == NULL) { + ret = ERROR_RTMP_REQ_CONNECT; + srs_error("invalid request, must specifies the tcUrl. ret=%d", ret); + return ret; + } + req->tcUrl = srs_amf0_convert(prop)->value; + + if ((prop = pkt->command_object->ensure_property_string("pageUrl")) != NULL) { + req->pageUrl = srs_amf0_convert(prop)->value; + } + + if ((prop = pkt->command_object->ensure_property_string("swfUrl")) != NULL) { + req->swfUrl = srs_amf0_convert(prop)->value; + } + + if ((prop = pkt->command_object->ensure_property_number("objectEncoding")) != NULL) { + req->objectEncoding = srs_amf0_convert(prop)->value; + } + + srs_info("get connect app message params success."); + + return req->discovery_app(); +} + +int SrsRtmp::set_window_ack_size(int ack_size) +{ + int ret = ERROR_SUCCESS; + + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsSetWindowAckSizePacket* pkt = new SrsSetWindowAckSizePacket(); + + pkt->ackowledgement_window_size = ack_size; + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send ack size message failed. ret=%d", ret); + return ret; + } + srs_info("send ack size message success. ack_size=%d", ack_size); + + return ret; +} + +int SrsRtmp::set_peer_bandwidth(int bandwidth, int type) +{ + int ret = ERROR_SUCCESS; + + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsSetPeerBandwidthPacket* pkt = new SrsSetPeerBandwidthPacket(); + + pkt->bandwidth = bandwidth; + pkt->type = type; + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send set bandwidth message failed. ret=%d", ret); + return ret; + } + srs_info("send set bandwidth message " + "success. bandwidth=%d, type=%d", bandwidth, type); + + return ret; +} + +int SrsRtmp::response_connect_app(SrsRequest* req) +{ + int ret = ERROR_SUCCESS; + + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsConnectAppResPacket* pkt = new SrsConnectAppResPacket(); + + pkt->props->set("fmsVer", new SrsAmf0String("FMS/"RTMP_SIG_FMS_VER)); + pkt->props->set("capabilities", new SrsAmf0Number(127)); + pkt->props->set("mode", new SrsAmf0Number(1)); + + pkt->info->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); + pkt->info->set(StatusCode, new SrsAmf0String(StatusCodeConnectSuccess)); + pkt->info->set(StatusDescription, new SrsAmf0String("Connection succeeded")); + pkt->info->set("objectEncoding", new SrsAmf0Number(req->objectEncoding)); + SrsASrsAmf0EcmaArray* data = new SrsASrsAmf0EcmaArray(); + pkt->info->set("data", data); + + data->set("srs_version", new SrsAmf0String(RTMP_SIG_FMS_VER)); + data->set("srs_server", new SrsAmf0String(RTMP_SIG_SRS_NAME)); + data->set("srs_license", new SrsAmf0String(RTMP_SIG_SRS_LICENSE)); + data->set("srs_role", new SrsAmf0String(RTMP_SIG_SRS_ROLE)); + data->set("srs_url", new SrsAmf0String(RTMP_SIG_SRS_URL)); + data->set("srs_version", new SrsAmf0String(RTMP_SIG_SRS_VERSION)); + data->set("srs_site", new SrsAmf0String(RTMP_SIG_SRS_WEB)); + data->set("srs_email", new SrsAmf0String(RTMP_SIG_SRS_EMAIL)); + data->set("srs_copyright", new SrsAmf0String(RTMP_SIG_SRS_COPYRIGHT)); + + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send connect app response message failed. ret=%d", ret); + return ret; + } + srs_info("send connect app response message success."); + + return ret; +} + +int SrsRtmp::on_bw_done() +{ + int ret = ERROR_SUCCESS; + + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnBWDonePacket* pkt = new SrsOnBWDonePacket(); + + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onBWDone message failed. ret=%d", ret); + return ret; + } + srs_info("send onBWDone message success."); + + return ret; +} + +int SrsRtmp::identify_client(int stream_id, SrsClientType& type, std::string& stream_name) +{ + type = SrsClientUnknown; + int ret = ERROR_SUCCESS; + + while (true) { + SrsCommonMessage* msg = NULL; + if ((ret = protocol->recv_message(&msg)) != ERROR_SUCCESS) { + srs_error("recv identify client message failed. ret=%d", ret); + return ret; + } + + SrsAutoFree(SrsCommonMessage, msg, false); + + if (!msg->header.is_amf0_command() && !msg->header.is_amf3_command()) { + srs_trace("identify ignore messages except " + "AMF0/AMF3 command message. type=%#x", msg->header.message_type); + continue; + } + + if ((ret = msg->decode_packet(protocol)) != ERROR_SUCCESS) { + srs_error("identify decode message failed. ret=%d", ret); + return ret; + } + + SrsPacket* pkt = msg->get_packet(); + if (dynamic_cast(pkt)) { + srs_info("identify client by create stream, play or flash publish."); + return identify_create_stream_client( + dynamic_cast(pkt), stream_id, type, stream_name); + } + if (dynamic_cast(pkt)) { + srs_info("identify client by releaseStream, fmle publish."); + return identify_fmle_publish_client( + dynamic_cast(pkt), type, stream_name); + } + + srs_trace("ignore AMF0/AMF3 command message."); + } + + return ret; +} + +int SrsRtmp::set_chunk_size(int chunk_size) +{ + int ret = ERROR_SUCCESS; + + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsSetChunkSizePacket* pkt = new SrsSetChunkSizePacket(); + + pkt->chunk_size = chunk_size; + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send set chunk size message failed. ret=%d", ret); + return ret; + } + srs_info("send set chunk size message success. chunk_size=%d", chunk_size); + + return ret; +} + +int SrsRtmp::start_play(int stream_id) +{ + int ret = ERROR_SUCCESS; + + // StreamBegin + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsUserControlPacket* pkt = new SrsUserControlPacket(); + + pkt->event_type = SrcPCUCStreamBegin; + pkt->event_data = stream_id; + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send PCUC(StreamBegin) message failed. ret=%d", ret); + return ret; + } + srs_info("send PCUC(StreamBegin) message success."); + } + + // onStatus(NetStream.Play.Reset) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); + pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamReset)); + pkt->data->set(StatusDescription, new SrsAmf0String("Playing and resetting stream.")); + pkt->data->set(StatusDetails, new SrsAmf0String("stream")); + pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID)); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onStatus(NetStream.Play.Reset) message failed. ret=%d", ret); + return ret; + } + srs_info("send onStatus(NetStream.Play.Reset) message success."); + } + + // onStatus(NetStream.Play.Start) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); + pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamStart)); + pkt->data->set(StatusDescription, new SrsAmf0String("Started playing stream.")); + pkt->data->set(StatusDetails, new SrsAmf0String("stream")); + pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID)); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onStatus(NetStream.Play.Reset) message failed. ret=%d", ret); + return ret; + } + srs_info("send onStatus(NetStream.Play.Reset) message success."); + } + + // |RtmpSampleAccess(false, false) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsSampleAccessPacket* pkt = new SrsSampleAccessPacket(); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send |RtmpSampleAccess(false, false) message failed. ret=%d", ret); + return ret; + } + srs_info("send |RtmpSampleAccess(false, false) message success."); + } + + // onStatus(NetStream.Data.Start) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnStatusDataPacket* pkt = new SrsOnStatusDataPacket(); + + pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeDataStart)); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onStatus(NetStream.Data.Start) message failed. ret=%d", ret); + return ret; + } + srs_info("send onStatus(NetStream.Data.Start) message success."); + } + + srs_info("start play success."); + + return ret; +} + +int SrsRtmp::on_play_client_pause(int stream_id, bool is_pause) +{ + int ret = ERROR_SUCCESS; + + if (is_pause) { + // onStatus(NetStream.Pause.Notify) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); + pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamPause)); + pkt->data->set(StatusDescription, new SrsAmf0String("Paused stream.")); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onStatus(NetStream.Pause.Notify) message failed. ret=%d", ret); + return ret; + } + srs_info("send onStatus(NetStream.Pause.Notify) message success."); + } + // StreamEOF + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsUserControlPacket* pkt = new SrsUserControlPacket(); + + pkt->event_type = SrcPCUCStreamEOF; + pkt->event_data = stream_id; + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send PCUC(StreamEOF) message failed. ret=%d", ret); + return ret; + } + srs_info("send PCUC(StreamEOF) message success."); + } + } else { + // onStatus(NetStream.Unpause.Notify) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); + pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamUnpause)); + pkt->data->set(StatusDescription, new SrsAmf0String("Unpaused stream.")); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onStatus(NetStream.Unpause.Notify) message failed. ret=%d", ret); + return ret; + } + srs_info("send onStatus(NetStream.Unpause.Notify) message success."); + } + // StreanBegin + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsUserControlPacket* pkt = new SrsUserControlPacket(); + + pkt->event_type = SrcPCUCStreamBegin; + pkt->event_data = stream_id; + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send PCUC(StreanBegin) message failed. ret=%d", ret); + return ret; + } + srs_info("send PCUC(StreanBegin) message success."); + } + } + + return ret; +} + +int SrsRtmp::start_fmle_publish(int stream_id) +{ + int ret = ERROR_SUCCESS; + + // FCPublish + double fc_publish_tid = 0; + if (true) { + SrsCommonMessage* msg = NULL; + SrsFMLEStartPacket* pkt = NULL; + if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { + srs_error("recv FCPublish message failed. ret=%d", ret); + return ret; + } + srs_info("recv FCPublish request message success."); + + SrsAutoFree(SrsCommonMessage, msg, false); + fc_publish_tid = pkt->transaction_id; + } + // FCPublish response + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(fc_publish_tid); + + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send FCPublish response message failed. ret=%d", ret); + return ret; + } + srs_info("send FCPublish response message success."); + } + + // createStream + double create_stream_tid = 0; + if (true) { + SrsCommonMessage* msg = NULL; + SrsCreateStreamPacket* pkt = NULL; + if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { + srs_error("recv createStream message failed. ret=%d", ret); + return ret; + } + srs_info("recv createStream request message success."); + + SrsAutoFree(SrsCommonMessage, msg, false); + create_stream_tid = pkt->transaction_id; + } + // createStream response + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsCreateStreamResPacket* pkt = new SrsCreateStreamResPacket(create_stream_tid, stream_id); + + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send createStream response message failed. ret=%d", ret); + return ret; + } + srs_info("send createStream response message success."); + } + + // publish + if (true) { + SrsCommonMessage* msg = NULL; + SrsPublishPacket* pkt = NULL; + if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { + srs_error("recv publish message failed. ret=%d", ret); + return ret; + } + srs_info("recv publish request message success."); + + SrsAutoFree(SrsCommonMessage, msg, false); + } + // publish response onFCPublish(NetStream.Publish.Start) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->command_name = RTMP_AMF0_COMMAND_ON_FC_PUBLISH; + pkt->data->set(StatusCode, new SrsAmf0String(StatusCodePublishStart)); + pkt->data->set(StatusDescription, new SrsAmf0String("Started publishing stream.")); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onFCPublish(NetStream.Publish.Start) message failed. ret=%d", ret); + return ret; + } + srs_info("send onFCPublish(NetStream.Publish.Start) message success."); + } + // publish response onStatus(NetStream.Publish.Start) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); + pkt->data->set(StatusCode, new SrsAmf0String(StatusCodePublishStart)); + pkt->data->set(StatusDescription, new SrsAmf0String("Started publishing stream.")); + pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID)); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onStatus(NetStream.Publish.Start) message failed. ret=%d", ret); + return ret; + } + srs_info("send onStatus(NetStream.Publish.Start) message success."); + } + + srs_info("FMLE publish success."); + + return ret; +} + +int SrsRtmp::fmle_unpublish(int stream_id, double unpublish_tid) +{ + int ret = ERROR_SUCCESS; + + // publish response onFCUnpublish(NetStream.unpublish.Success) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->command_name = RTMP_AMF0_COMMAND_ON_FC_UNPUBLISH; + pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeUnpublishSuccess)); + pkt->data->set(StatusDescription, new SrsAmf0String("Stop publishing stream.")); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onFCUnpublish(NetStream.unpublish.Success) message failed. ret=%d", ret); + return ret; + } + srs_info("send onFCUnpublish(NetStream.unpublish.Success) message success."); + } + // FCUnpublish response + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(unpublish_tid); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send FCUnpublish response message failed. ret=%d", ret); + return ret; + } + srs_info("send FCUnpublish response message success."); + } + // publish response onStatus(NetStream.Unpublish.Success) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); + pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeUnpublishSuccess)); + pkt->data->set(StatusDescription, new SrsAmf0String("Stream is now unpublished")); + pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID)); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onStatus(NetStream.Unpublish.Success) message failed. ret=%d", ret); + return ret; + } + srs_info("send onStatus(NetStream.Unpublish.Success) message success."); + } + + srs_info("FMLE unpublish success."); + + return ret; +} + +int SrsRtmp::start_flash_publish(int stream_id) +{ + int ret = ERROR_SUCCESS; + + // publish response onStatus(NetStream.Publish.Start) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); + pkt->data->set(StatusCode, new SrsAmf0String(StatusCodePublishStart)); + pkt->data->set(StatusDescription, new SrsAmf0String("Started publishing stream.")); + pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID)); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onStatus(NetStream.Publish.Start) message failed. ret=%d", ret); + return ret; + } + srs_info("send onStatus(NetStream.Publish.Start) message success."); + } + + srs_info("flash publish success."); + + return ret; +} + +int SrsRtmp::identify_create_stream_client(SrsCreateStreamPacket* req, int stream_id, SrsClientType& type, std::string& stream_name) +{ + int ret = ERROR_SUCCESS; + + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsCreateStreamResPacket* pkt = new SrsCreateStreamResPacket(req->transaction_id, stream_id); + + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send createStream response message failed. ret=%d", ret); + return ret; + } + srs_info("send createStream response message success."); + } + + while (true) { + SrsCommonMessage* msg = NULL; + if ((ret = protocol->recv_message(&msg)) != ERROR_SUCCESS) { + srs_error("recv identify client message failed. ret=%d", ret); + return ret; + } + + SrsAutoFree(SrsCommonMessage, msg, false); + + if (!msg->header.is_amf0_command() && !msg->header.is_amf3_command()) { + srs_trace("identify ignore messages except " + "AMF0/AMF3 command message. type=%#x", msg->header.message_type); + continue; + } + + if ((ret = msg->decode_packet(protocol)) != ERROR_SUCCESS) { + srs_error("identify decode message failed. ret=%d", ret); + return ret; + } + + SrsPacket* pkt = msg->get_packet(); + if (dynamic_cast(pkt)) { + SrsPlayPacket* play = dynamic_cast(pkt); + type = SrsClientPlay; + stream_name = play->stream_name; + srs_trace("identity client type=play, stream_name=%s", stream_name.c_str()); + return ret; + } + if (dynamic_cast(pkt)) { + srs_info("identify client by publish, falsh publish."); + return identify_flash_publish_client( + dynamic_cast(pkt), type, stream_name); + } + + srs_trace("ignore AMF0/AMF3 command message."); + } + + return ret; +} + +int SrsRtmp::identify_fmle_publish_client(SrsFMLEStartPacket* req, SrsClientType& type, std::string& stream_name) +{ + int ret = ERROR_SUCCESS; + + type = SrsClientFMLEPublish; + stream_name = req->stream_name; + + // releaseStream response + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(req->transaction_id); + + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send releaseStream response message failed. ret=%d", ret); + return ret; + } + srs_info("send releaseStream response message success."); + } + + return ret; +} + +int SrsRtmp::identify_flash_publish_client(SrsPublishPacket* req, SrsClientType& type, std::string& stream_name) +{ + int ret = ERROR_SUCCESS; + + type = SrsClientFlashPublish; + stream_name = req->stream_name; + + return ret; +} + diff --git a/trunk/src/core/srs_core_rtmp.hpp b/trunk/src/core/srs_core_rtmp.hpp index 3c18eb479..56fc95112 100644 --- a/trunk/src/core/srs_core_rtmp.hpp +++ b/trunk/src/core/srs_core_rtmp.hpp @@ -32,8 +32,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include -#include - class SrsProtocol; class ISrsMessage; class SrsCommonMessage; diff --git a/trunk/src/core/srs_core_server.cpp b/trunk/src/core/srs_core_server.cpp index ab6e22102..4833673f4 100644 --- a/trunk/src/core/srs_core_server.cpp +++ b/trunk/src/core/srs_core_server.cpp @@ -30,8 +30,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include -#include - #include #include #include @@ -48,24 +46,16 @@ SrsListener::SrsListener(SrsServer* _server, SrsListenerType _type) port = 0; server = _server; type = _type; - - tid = NULL; - loop = false; + + pthread = new SrsThread(this, 0); } SrsListener::~SrsListener() { - if (stfd) { - st_netfd_close(stfd); - stfd = NULL; - } + srs_close_stfd(stfd); - if (tid) { - loop = false; - st_thread_interrupt(tid); - st_thread_join(tid, NULL); - tid = NULL; - } + pthread->stop(); + srs_freep(pthread); // st does not close it sometimes, // close it manually. @@ -118,8 +108,7 @@ int SrsListener::listen(int _port) } srs_verbose("st open socket success. fd=%d", fd); - if ((tid = st_thread_create(listen_thread, this, 1, 0)) == NULL) { - ret = ERROR_ST_CREATE_LISTEN_THREAD; + if ((ret = pthread->start()) != ERROR_SUCCESS) { srs_error("st_thread_create listen thread error. ret=%d", ret); return ret; } @@ -130,41 +119,32 @@ int SrsListener::listen(int _port) return ret; } -void SrsListener::listen_cycle() +void SrsListener::on_enter_loop() +{ + srs_trace("listen cycle start, port=%d, type=%d, fd=%d", port, type, fd); +} + +int SrsListener::cycle() { int ret = ERROR_SUCCESS; - log_context->generate_id(); - srs_trace("listen cycle start, port=%d, type=%d, fd=%d", port, type, fd); + st_netfd_t client_stfd = st_accept(stfd, NULL, NULL, ST_UTIME_NO_TIMEOUT); + + if(client_stfd == NULL){ + // ignore error. + srs_warn("ignore accept thread stoppped for accept client error"); + return ret; + } + srs_verbose("get a client. fd=%d", st_netfd_fileno(client_stfd)); - while (loop) { - st_netfd_t client_stfd = st_accept(stfd, NULL, NULL, ST_UTIME_NO_TIMEOUT); - - if(client_stfd == NULL){ - // ignore error. - srs_warn("ignore accept thread stoppped for accept client error"); - continue; - } - srs_verbose("get a client. fd=%d", st_netfd_fileno(client_stfd)); - - if ((ret = server->accept_client(type, client_stfd)) != ERROR_SUCCESS) { - srs_warn("accept client error. ret=%d", ret); - continue; - } - - srs_verbose("accept client finished. conns=%d, ret=%d", (int)conns.size(), ret); + if ((ret = server->accept_client(type, client_stfd)) != ERROR_SUCCESS) { + srs_warn("accept client error. ret=%d", ret); + return ret; } -} - -void* SrsListener::listen_thread(void* arg) -{ - SrsListener* obj = (SrsListener*)arg; - srs_assert(obj != NULL); - obj->loop = true; - obj->listen_cycle(); + srs_verbose("accept client finished. conns=%d, ret=%d", (int)conns.size(), ret); - return NULL; + return ret; } SrsServer::SrsServer() @@ -312,8 +292,7 @@ int SrsServer::accept_client(SrsListenerType type, st_netfd_t client_stfd) srs_error("exceed the max connections, drop client: " "clients=%d, max=%d, fd=%d", (int)conns.size(), max_connections, fd); - st_netfd_close(client_stfd); - ::close(fd); + srs_close_stfd(client_stfd); return ret; } diff --git a/trunk/src/core/srs_core_server.hpp b/trunk/src/core/srs_core_server.hpp index 1d4bf5c9f..fcc9d61b8 100644 --- a/trunk/src/core/srs_core_server.hpp +++ b/trunk/src/core/srs_core_server.hpp @@ -32,9 +32,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include -#include - #include +#include class SrsServer; class SrsConnection; @@ -45,7 +44,7 @@ enum SrsListenerType SrsListenerApi }; -class SrsListener +class SrsListener : public ISrsThreadHandler { public: SrsListenerType type; @@ -54,16 +53,16 @@ private: st_netfd_t stfd; int port; SrsServer* server; - st_thread_t tid; - bool loop; + SrsThread* pthread; public: SrsListener(SrsServer* _server, SrsListenerType _type); virtual ~SrsListener(); public: virtual int listen(int port); -private: - virtual void listen_cycle(); - static void* listen_thread(void* arg); +// interface ISrsThreadHandler. +public: + virtual void on_enter_loop(); + virtual int cycle(); }; class SrsServer : public SrsReloadHandler diff --git a/trunk/src/core/srs_core_socket.hpp b/trunk/src/core/srs_core_socket.hpp index 74887fc12..42e8cf752 100644 --- a/trunk/src/core/srs_core_socket.hpp +++ b/trunk/src/core/srs_core_socket.hpp @@ -30,8 +30,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include -#include - /** * the socket provides TCP socket over st, * that is, the sync socket mechanism. diff --git a/trunk/src/core/srs_core_source.cpp b/trunk/src/core/srs_core_source.cpp old mode 100755 new mode 100644 index 8071cd6e9..e95f81be8 --- a/trunk/src/core/srs_core_source.cpp +++ b/trunk/src/core/srs_core_source.cpp @@ -1,750 +1,752 @@ -/* -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 - -#define CONST_MAX_JITTER_MS 500 -#define DEFAULT_FRAME_TIME_MS 10 -#define PAUSED_SHRINK_SIZE 250 - -SrsRtmpJitter::SrsRtmpJitter() -{ - last_pkt_correct_time = last_pkt_time = 0; -} - -SrsRtmpJitter::~SrsRtmpJitter() -{ -} - -int SrsRtmpJitter::correct(SrsSharedPtrMessage* msg, int tba, int tbv, int64_t* corrected_time) -{ - int ret = ERROR_SUCCESS; - - int sample_rate = tba; - int frame_rate = tbv; - - /** - * we use a very simple time jitter detect/correct algorithm: - * 1. delta: ensure the delta is positive and valid, - * we set the delta to DEFAULT_FRAME_TIME_MS, - * if the delta of time is nagative or greater than CONST_MAX_JITTER_MS. - * 2. last_pkt_time: specifies the original packet time, - * is used to detect next jitter. - * 3. last_pkt_correct_time: simply add the positive delta, - * and enforce the time monotonically. - */ - u_int32_t time = msg->header.timestamp; - int32_t delta = time - last_pkt_time; - - // if jitter detected, reset the delta. - if (delta < 0 || delta > CONST_MAX_JITTER_MS) { - // calc the right diff by audio sample rate - if (msg->header.is_audio() && sample_rate > 0) { - delta = (int32_t)(delta * 1000.0 / sample_rate); - } else if (msg->header.is_video() && frame_rate > 0) { - delta = (int32_t)(delta * 1.0 / frame_rate); - } else { - delta = DEFAULT_FRAME_TIME_MS; - } - - // sometimes, the time is absolute time, so correct it again. - if (delta < 0 || delta > CONST_MAX_JITTER_MS) { - delta = DEFAULT_FRAME_TIME_MS; - } - - srs_info("jitter detected, last_pts=%d, pts=%d, diff=%d, last_time=%d, time=%d, diff=%d", - last_pkt_time, time, time - last_pkt_time, last_pkt_correct_time, last_pkt_correct_time + delta, delta); - } else { - srs_verbose("timestamp no jitter. time=%d, last_pkt=%d, correct_to=%d", - time, last_pkt_time, last_pkt_correct_time + delta); - } - - last_pkt_correct_time = srs_max(0, last_pkt_correct_time + delta); - - if (corrected_time) { - *corrected_time = last_pkt_correct_time; - } - msg->header.timestamp = last_pkt_correct_time; - - last_pkt_time = time; - - return ret; -} - -int SrsRtmpJitter::get_time() -{ - return (int)last_pkt_correct_time; -} - -SrsConsumer::SrsConsumer(SrsSource* _source) -{ - source = _source; - paused = false; - jitter = new SrsRtmpJitter(); -} - -SrsConsumer::~SrsConsumer() -{ - clear(); - - source->on_consumer_destroy(this); - srs_freep(jitter); -} - -int SrsConsumer::get_time() -{ - return jitter->get_time(); -} - -int SrsConsumer::enqueue(SrsSharedPtrMessage* msg, int tba, int tbv) -{ - int ret = ERROR_SUCCESS; - - if ((ret = jitter->correct(msg, tba, tbv)) != ERROR_SUCCESS) { - srs_freep(msg); - return ret; - } - - // TODO: check the queue size and drop packets if overflow. - msgs.push_back(msg); - - return ret; -} - -int SrsConsumer::get_packets(int max_count, SrsSharedPtrMessage**& pmsgs, int& count) -{ - int ret = ERROR_SUCCESS; - - if (msgs.empty()) { - return ret; - } - - if (paused) { - if ((int)msgs.size() >= PAUSED_SHRINK_SIZE) { - shrink(); - } - return ret; - } - - if (max_count == 0) { - count = (int)msgs.size(); - } else { - count = srs_min(max_count, (int)msgs.size()); - } - - pmsgs = new SrsSharedPtrMessage*[count]; - - for (int i = 0; i < count; i++) { - pmsgs[i] = msgs[i]; - } - - if (count == (int)msgs.size()) { - msgs.clear(); - } else { - msgs.erase(msgs.begin(), msgs.begin() + count); - } - - return ret; -} - -int SrsConsumer::on_play_client_pause(bool is_pause) -{ - int ret = ERROR_SUCCESS; - - srs_trace("stream consumer change pause state %d=>%d", paused, is_pause); - paused = is_pause; - - return ret; -} - -void SrsConsumer::shrink() -{ - int i = 0; - std::vector::iterator it; - - // issue the last video iframe. - bool has_video = false; - int frame_to_remove = 0; - std::vector::iterator iframe = msgs.end(); - for (i = 0, it = msgs.begin(); it != msgs.end(); ++it, i++) { - SrsSharedPtrMessage* msg = *it; - if (msg->header.is_video()) { - has_video = true; - if (SrsCodec::video_is_keyframe(msg->payload, msg->size)) { - iframe = it; - frame_to_remove = i + 1; - } - } - } - - // last iframe is the first elem, ignore it. - if (iframe == msgs.begin()) { - return; - } - - // recalc the frame to remove - if (iframe == msgs.end()) { - frame_to_remove = 0; - } - if (!has_video) { - frame_to_remove = (int)msgs.size(); - } - - srs_trace("shrink the cache queue, has_video=%d, has_iframe=%d, size=%d, removed=%d", - has_video, iframe != msgs.end(), (int)msgs.size(), frame_to_remove); - - // if no video, remove all audio. - if (!has_video) { - clear(); - return; - } - - // if exists video Iframe, remove the frames before it. - if (iframe != msgs.end()) { - for (it = msgs.begin(); it != iframe; ++it) { - SrsSharedPtrMessage* msg = *it; - srs_freep(msg); - } - msgs.erase(msgs.begin(), iframe); - } -} - -void SrsConsumer::clear() -{ - std::vector::iterator it; - for (it = msgs.begin(); it != msgs.end(); ++it) { - SrsSharedPtrMessage* msg = *it; - srs_freep(msg); - } - msgs.clear(); -} - -SrsGopCache::SrsGopCache() -{ - cached_video_count = 0; - enable_gop_cache = true; -} - -SrsGopCache::~SrsGopCache() -{ - clear(); -} - -void SrsGopCache::set(bool enabled) -{ - enable_gop_cache = enabled; - - if (!enabled) { - srs_info("disable gop cache, clear %d packets.", (int)gop_cache.size()); - clear(); - return; - } - - srs_info("enable gop cache"); -} - -int SrsGopCache::cache(SrsSharedPtrMessage* msg) -{ - int ret = ERROR_SUCCESS; - - if (!enable_gop_cache) { - srs_verbose("gop cache is disabled."); - return ret; - } - - // got video, update the video count if acceptable - if (msg->header.is_video()) { - cached_video_count++; - } - - // no acceptable video or pure audio, disable the cache. - if (cached_video_count == 0) { - srs_verbose("ignore any frame util got a h264 video frame."); - return ret; - } - - // clear gop cache when got key frame - if (msg->header.is_video() && SrsCodec::video_is_keyframe(msg->payload, msg->size)) { - srs_info("clear gop cache when got keyframe. vcount=%d, count=%d", - cached_video_count, (int)gop_cache.size()); - - clear(); - - // curent msg is video frame, so we set to 1. - cached_video_count = 1; - } - - // cache the frame. - gop_cache.push_back(msg->copy()); - - return ret; -} - -void SrsGopCache::clear() -{ - std::vector::iterator it; - for (it = gop_cache.begin(); it != gop_cache.end(); ++it) { - SrsSharedPtrMessage* msg = *it; - srs_freep(msg); - } - gop_cache.clear(); - - cached_video_count = 0; -} - -int SrsGopCache::dump(SrsConsumer* consumer, int tba, int tbv) -{ - int ret = ERROR_SUCCESS; - - std::vector::iterator it; - for (it = gop_cache.begin(); it != gop_cache.end(); ++it) { - SrsSharedPtrMessage* msg = *it; - if ((ret = consumer->enqueue(msg->copy(), tba, tbv)) != ERROR_SUCCESS) { - srs_error("dispatch cached gop failed. ret=%d", ret); - return ret; - } - } - srs_trace("dispatch cached gop success. count=%d, duration=%d", (int)gop_cache.size(), consumer->get_time()); - - return ret; -} - -std::map SrsSource::pool; - -SrsSource* SrsSource::find(std::string stream_url) -{ - if (pool.find(stream_url) == pool.end()) { - pool[stream_url] = new SrsSource(stream_url); - srs_verbose("create new source for url=%s", stream_url.c_str()); - } - - return pool[stream_url]; -} - -SrsSource::SrsSource(std::string _stream_url) -{ - stream_url = _stream_url; - -#ifdef SRS_HLS - hls = new SrsHls(); -#endif -#ifdef SRS_FFMPEG - encoder = new SrsEncoder(); -#endif - - cache_metadata = cache_sh_video = cache_sh_audio = NULL; - - frame_rate = sample_rate = 0; - _can_publish = true; - - gop_cache = new SrsGopCache(); -} - -SrsSource::~SrsSource() -{ - if (true) { - std::vector::iterator it; - for (it = consumers.begin(); it != consumers.end(); ++it) { - SrsConsumer* consumer = *it; - srs_freep(consumer); - } - consumers.clear(); - } - - if (true) { - std::vector::iterator it; - for (it = forwarders.begin(); it != forwarders.end(); ++it) { - SrsForwarder* forwarder = *it; - srs_freep(forwarder); - } - forwarders.clear(); - } - - srs_freep(cache_metadata); - srs_freep(cache_sh_video); - srs_freep(cache_sh_audio); - - srs_freep(gop_cache); - -#ifdef SRS_HLS - srs_freep(hls); -#endif -#ifdef SRS_FFMPEG - srs_freep(encoder); -#endif -} - -bool SrsSource::can_publish() -{ - return _can_publish; -} - -int SrsSource::on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* metadata) -{ - int ret = ERROR_SUCCESS; - -#ifdef SRS_HLS - if ((ret = hls->on_meta_data(metadata)) != ERROR_SUCCESS) { - srs_error("hls process onMetaData message failed. ret=%d", ret); - return ret; - } -#endif - - metadata->metadata->set("server", new SrsAmf0String( - RTMP_SIG_SRS_KEY" "RTMP_SIG_SRS_VERSION" ("RTMP_SIG_SRS_URL_SHORT")")); - - SrsAmf0Any* prop = NULL; - if ((prop = metadata->metadata->get_property("audiosamplerate")) != NULL) { - if (prop->is_number()) { - sample_rate = (int)(srs_amf0_convert(prop)->value); - } - } - if ((prop = metadata->metadata->get_property("framerate")) != NULL) { - if (prop->is_number()) { - frame_rate = (int)(srs_amf0_convert(prop)->value); - } - } - - // encode the metadata to payload - int size = metadata->get_payload_length(); - if (size <= 0) { - srs_warn("ignore the invalid metadata. size=%d", size); - return ret; - } - srs_verbose("get metadata size success."); - - char* payload = new char[size]; - memset(payload, 0, size); - if ((ret = metadata->encode(size, payload)) != ERROR_SUCCESS) { - srs_error("encode metadata error. ret=%d", ret); - srs_freepa(payload); - return ret; - } - srs_verbose("encode metadata success."); - - // create a shared ptr message. - srs_freep(cache_metadata); - cache_metadata = new SrsSharedPtrMessage(); - - // dump message to shared ptr message. - if ((ret = cache_metadata->initialize(msg, payload, size)) != ERROR_SUCCESS) { - srs_error("initialize the cache metadata failed. ret=%d", ret); - return ret; - } - srs_verbose("initialize shared ptr metadata success."); - - // copy to all consumer - if (true) { - std::vector::iterator it; - for (it = consumers.begin(); it != consumers.end(); ++it) { - SrsConsumer* consumer = *it; - if ((ret = consumer->enqueue(cache_metadata->copy(), sample_rate, frame_rate)) != ERROR_SUCCESS) { - srs_error("dispatch the metadata failed. ret=%d", ret); - return ret; - } - } - srs_trace("dispatch metadata success."); - } - - // copy to all forwarders - if (true) { - std::vector::iterator it; - for (it = forwarders.begin(); it != forwarders.end(); ++it) { - SrsForwarder* forwarder = *it; - if ((ret = forwarder->on_meta_data(cache_metadata->copy())) != ERROR_SUCCESS) { - srs_error("forwarder process onMetaData message failed. ret=%d", ret); - return ret; - } - } - } - - return ret; -} - -int SrsSource::on_audio(SrsCommonMessage* audio) -{ - int ret = ERROR_SUCCESS; - - SrsSharedPtrMessage* msg = new SrsSharedPtrMessage(); - SrsAutoFree(SrsSharedPtrMessage, msg, false); - if ((ret = msg->initialize(audio)) != ERROR_SUCCESS) { - srs_error("initialize the audio failed. ret=%d", ret); - return ret; - } - srs_verbose("initialize shared ptr audio success."); - -#ifdef SRS_HLS - if ((ret = hls->on_audio(msg->copy())) != ERROR_SUCCESS) { - srs_warn("hls process audio message failed, ignore and disable hls. ret=%d", ret); - - // unpublish, ignore ret. - hls->on_unpublish(); - - // ignore. - ret = ERROR_SUCCESS; - } -#endif - - // copy to all consumer - if (true) { - std::vector::iterator it; - for (it = consumers.begin(); it != consumers.end(); ++it) { - SrsConsumer* consumer = *it; - if ((ret = consumer->enqueue(msg->copy(), sample_rate, frame_rate)) != ERROR_SUCCESS) { - srs_error("dispatch the audio failed. ret=%d", ret); - return ret; - } - } - srs_info("dispatch audio success."); - } - - // copy to all forwarders. - if (true) { - std::vector::iterator it; - for (it = forwarders.begin(); it != forwarders.end(); ++it) { - SrsForwarder* forwarder = *it; - if ((ret = forwarder->on_audio(msg->copy())) != ERROR_SUCCESS) { - srs_error("forwarder process audio message failed. ret=%d", ret); - return ret; - } - } - } - - // cache the sequence header if h264 - if (SrsCodec::audio_is_sequence_header(msg->payload, msg->size)) { - srs_freep(cache_sh_audio); - cache_sh_audio = msg->copy(); - srs_trace("update audio sequence header success. size=%d", msg->header.payload_length); - return ret; - } - - // cache the last gop packets - if ((ret = gop_cache->cache(msg)) != ERROR_SUCCESS) { - srs_error("shrink gop cache failed. ret=%d", ret); - return ret; - } - srs_verbose("cache gop success."); - - return ret; -} - -int SrsSource::on_video(SrsCommonMessage* video) -{ - int ret = ERROR_SUCCESS; - - SrsSharedPtrMessage* msg = new SrsSharedPtrMessage(); - SrsAutoFree(SrsSharedPtrMessage, msg, false); - if ((ret = msg->initialize(video)) != ERROR_SUCCESS) { - srs_error("initialize the video failed. ret=%d", ret); - return ret; - } - srs_verbose("initialize shared ptr video success."); - -#ifdef SRS_HLS - if ((ret = hls->on_video(msg->copy())) != ERROR_SUCCESS) { - srs_warn("hls process video message failed, ignore and disable hls. ret=%d", ret); - - // unpublish, ignore ret. - hls->on_unpublish(); - - // ignore. - ret = ERROR_SUCCESS; - } -#endif - - // copy to all consumer - if (true) { - std::vector::iterator it; - for (it = consumers.begin(); it != consumers.end(); ++it) { - SrsConsumer* consumer = *it; - if ((ret = consumer->enqueue(msg->copy(), sample_rate, frame_rate)) != ERROR_SUCCESS) { - srs_error("dispatch the video failed. ret=%d", ret); - return ret; - } - } - srs_info("dispatch video success."); - } - - // copy to all forwarders. - if (true) { - std::vector::iterator it; - for (it = forwarders.begin(); it != forwarders.end(); ++it) { - SrsForwarder* forwarder = *it; - if ((ret = forwarder->on_video(msg->copy())) != ERROR_SUCCESS) { - srs_error("forwarder process video message failed. ret=%d", ret); - return ret; - } - } - } - - // cache the sequence header if h264 - if (SrsCodec::video_is_sequence_header(msg->payload, msg->size)) { - srs_freep(cache_sh_video); - cache_sh_video = msg->copy(); - srs_trace("update video sequence header success. size=%d", msg->header.payload_length); - return ret; - } - - // cache the last gop packets - if ((ret = gop_cache->cache(msg)) != ERROR_SUCCESS) { - srs_error("shrink gop cache failed. ret=%d", ret); - return ret; - } - srs_verbose("cache gop success."); - - return ret; -} - -int SrsSource::on_publish(SrsRequest* req) -{ - int ret = ERROR_SUCCESS; - - _can_publish = false; - - // TODO: support reload. - - // create forwarders - SrsConfDirective* conf = config->get_forward(req->vhost); - for (int i = 0; conf && i < (int)conf->args.size(); i++) { - std::string forward_server = conf->args.at(i); - - SrsForwarder* forwarder = new SrsForwarder(); - forwarders.push_back(forwarder); - - if ((ret = forwarder->on_publish(req, forward_server)) != ERROR_SUCCESS) { - srs_error("start forwarder failed. " - "vhost=%s, app=%s, stream=%s, forward-to=%s", - req->vhost.c_str(), req->app.c_str(), req->stream.c_str(), - forward_server.c_str()); - return ret; - } - } - -#ifdef SRS_FFMPEG - if ((ret = encoder->on_publish(req)) != ERROR_SUCCESS) { - return ret; - } -#endif - -#ifdef SRS_HLS - if ((ret = hls->on_publish(req)) != ERROR_SUCCESS) { - return ret; - } -#endif - - return ret; -} - -void SrsSource::on_unpublish() -{ - // close all forwarders - std::vector::iterator it; - for (it = forwarders.begin(); it != forwarders.end(); ++it) { - SrsForwarder* forwarder = *it; - forwarder->on_unpublish(); - srs_freep(forwarder); - } - forwarders.clear(); - -#ifdef SRS_FFMPEG - encoder->on_unpublish(); -#endif - - // TODO: HLS should continue previous sequence and stream. -#ifdef SRS_HLS - hls->on_unpublish(); -#endif - - gop_cache->clear(); - - srs_freep(cache_metadata); - frame_rate = sample_rate = 0; - - srs_freep(cache_sh_video); - srs_freep(cache_sh_audio); - - srs_trace("clear cache/metadata/sequence-headers when unpublish."); - - _can_publish = true; -} - - int SrsSource::create_consumer(SrsConsumer*& consumer) -{ - int ret = ERROR_SUCCESS; - - consumer = new SrsConsumer(this); - consumers.push_back(consumer); - - if (cache_metadata && (ret = consumer->enqueue(cache_metadata->copy(), sample_rate, frame_rate)) != ERROR_SUCCESS) { - srs_error("dispatch metadata failed. ret=%d", ret); - return ret; - } - srs_info("dispatch metadata success"); - - if (cache_sh_video && (ret = consumer->enqueue(cache_sh_video->copy(), sample_rate, frame_rate)) != ERROR_SUCCESS) { - srs_error("dispatch video sequence header failed. ret=%d", ret); - return ret; - } - srs_info("dispatch video sequence header success"); - - if (cache_sh_audio && (ret = consumer->enqueue(cache_sh_audio->copy(), sample_rate, frame_rate)) != ERROR_SUCCESS) { - srs_error("dispatch audio sequence header failed. ret=%d", ret); - return ret; - } - srs_info("dispatch audio sequence header success"); - - if ((ret = gop_cache->dump(consumer, sample_rate, frame_rate)) != ERROR_SUCCESS) { - return ret; - } - - return ret; -} - -void SrsSource::on_consumer_destroy(SrsConsumer* consumer) -{ - std::vector::iterator it; - it = std::find(consumers.begin(), consumers.end(), consumer); - if (it != consumers.end()) { - consumers.erase(it); - } - srs_info("handle consumer destroy success."); -} - -void SrsSource::set_cache(bool enabled) -{ - gop_cache->set(enabled); -} - +/* +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 + +#define CONST_MAX_JITTER_MS 500 +#define DEFAULT_FRAME_TIME_MS 10 +#define PAUSED_SHRINK_SIZE 250 + +SrsRtmpJitter::SrsRtmpJitter() +{ + last_pkt_correct_time = last_pkt_time = 0; +} + +SrsRtmpJitter::~SrsRtmpJitter() +{ +} + +int SrsRtmpJitter::correct(SrsSharedPtrMessage* msg, int tba, int tbv, int64_t* corrected_time) +{ + int ret = ERROR_SUCCESS; + + int sample_rate = tba; + int frame_rate = tbv; + + /** + * we use a very simple time jitter detect/correct algorithm: + * 1. delta: ensure the delta is positive and valid, + * we set the delta to DEFAULT_FRAME_TIME_MS, + * if the delta of time is nagative or greater than CONST_MAX_JITTER_MS. + * 2. last_pkt_time: specifies the original packet time, + * is used to detect next jitter. + * 3. last_pkt_correct_time: simply add the positive delta, + * and enforce the time monotonically. + */ + u_int32_t time = msg->header.timestamp; + int32_t delta = time - last_pkt_time; + + // if jitter detected, reset the delta. + if (delta < 0 || delta > CONST_MAX_JITTER_MS) { + // calc the right diff by audio sample rate + if (msg->header.is_audio() && sample_rate > 0) { + delta = (int32_t)(delta * 1000.0 / sample_rate); + } else if (msg->header.is_video() && frame_rate > 0) { + delta = (int32_t)(delta * 1.0 / frame_rate); + } else { + delta = DEFAULT_FRAME_TIME_MS; + } + + // sometimes, the time is absolute time, so correct it again. + if (delta < 0 || delta > CONST_MAX_JITTER_MS) { + delta = DEFAULT_FRAME_TIME_MS; + } + + srs_info("jitter detected, last_pts=%d, pts=%d, diff=%d, last_time=%d, time=%d, diff=%d", + last_pkt_time, time, time - last_pkt_time, last_pkt_correct_time, last_pkt_correct_time + delta, delta); + } else { + srs_verbose("timestamp no jitter. time=%d, last_pkt=%d, correct_to=%d", + time, last_pkt_time, last_pkt_correct_time + delta); + } + + last_pkt_correct_time = srs_max(0, last_pkt_correct_time + delta); + + if (corrected_time) { + *corrected_time = last_pkt_correct_time; + } + msg->header.timestamp = last_pkt_correct_time; + + last_pkt_time = time; + + return ret; +} + +int SrsRtmpJitter::get_time() +{ + return (int)last_pkt_correct_time; +} + +SrsConsumer::SrsConsumer(SrsSource* _source) +{ + source = _source; + paused = false; + jitter = new SrsRtmpJitter(); +} + +SrsConsumer::~SrsConsumer() +{ + clear(); + + source->on_consumer_destroy(this); + srs_freep(jitter); +} + +int SrsConsumer::get_time() +{ + return jitter->get_time(); +} + +int SrsConsumer::enqueue(SrsSharedPtrMessage* msg, int tba, int tbv) +{ + int ret = ERROR_SUCCESS; + + if ((ret = jitter->correct(msg, tba, tbv)) != ERROR_SUCCESS) { + srs_freep(msg); + return ret; + } + + // TODO: check the queue size and drop packets if overflow. + msgs.push_back(msg); + + return ret; +} + +int SrsConsumer::get_packets(int max_count, SrsSharedPtrMessage**& pmsgs, int& count) +{ + int ret = ERROR_SUCCESS; + + if (msgs.empty()) { + return ret; + } + + if (paused) { + if ((int)msgs.size() >= PAUSED_SHRINK_SIZE) { + shrink(); + } + return ret; + } + + if (max_count == 0) { + count = (int)msgs.size(); + } else { + count = srs_min(max_count, (int)msgs.size()); + } + + pmsgs = new SrsSharedPtrMessage*[count]; + + for (int i = 0; i < count; i++) { + pmsgs[i] = msgs[i]; + } + + if (count == (int)msgs.size()) { + msgs.clear(); + } else { + msgs.erase(msgs.begin(), msgs.begin() + count); + } + + return ret; +} + +int SrsConsumer::on_play_client_pause(bool is_pause) +{ + int ret = ERROR_SUCCESS; + + srs_trace("stream consumer change pause state %d=>%d", paused, is_pause); + paused = is_pause; + + return ret; +} + +void SrsConsumer::shrink() +{ + int i = 0; + std::vector::iterator it; + + // issue the last video iframe. + bool has_video = false; + int frame_to_remove = 0; + std::vector::iterator iframe = msgs.end(); + for (i = 0, it = msgs.begin(); it != msgs.end(); ++it, i++) { + SrsSharedPtrMessage* msg = *it; + if (msg->header.is_video()) { + has_video = true; + if (SrsCodec::video_is_keyframe(msg->payload, msg->size)) { + iframe = it; + frame_to_remove = i + 1; + } + } + } + + // last iframe is the first elem, ignore it. + if (iframe == msgs.begin()) { + return; + } + + // recalc the frame to remove + if (iframe == msgs.end()) { + frame_to_remove = 0; + } + if (!has_video) { + frame_to_remove = (int)msgs.size(); + } + + srs_trace("shrink the cache queue, has_video=%d, has_iframe=%d, size=%d, removed=%d", + has_video, iframe != msgs.end(), (int)msgs.size(), frame_to_remove); + + // if no video, remove all audio. + if (!has_video) { + clear(); + return; + } + + // if exists video Iframe, remove the frames before it. + if (iframe != msgs.end()) { + for (it = msgs.begin(); it != iframe; ++it) { + SrsSharedPtrMessage* msg = *it; + srs_freep(msg); + } + msgs.erase(msgs.begin(), iframe); + } +} + +void SrsConsumer::clear() +{ + std::vector::iterator it; + for (it = msgs.begin(); it != msgs.end(); ++it) { + SrsSharedPtrMessage* msg = *it; + srs_freep(msg); + } + msgs.clear(); +} + +SrsGopCache::SrsGopCache() +{ + cached_video_count = 0; + enable_gop_cache = true; +} + +SrsGopCache::~SrsGopCache() +{ + clear(); +} + +void SrsGopCache::set(bool enabled) +{ + enable_gop_cache = enabled; + + if (!enabled) { + srs_info("disable gop cache, clear %d packets.", (int)gop_cache.size()); + clear(); + return; + } + + srs_info("enable gop cache"); +} + +int SrsGopCache::cache(SrsSharedPtrMessage* msg) +{ + int ret = ERROR_SUCCESS; + + if (!enable_gop_cache) { + srs_verbose("gop cache is disabled."); + return ret; + } + + // got video, update the video count if acceptable + if (msg->header.is_video()) { + cached_video_count++; + } + + // no acceptable video or pure audio, disable the cache. + if (cached_video_count == 0) { + srs_verbose("ignore any frame util got a h264 video frame."); + return ret; + } + + // clear gop cache when got key frame + if (msg->header.is_video() && SrsCodec::video_is_keyframe(msg->payload, msg->size)) { + srs_info("clear gop cache when got keyframe. vcount=%d, count=%d", + cached_video_count, (int)gop_cache.size()); + + clear(); + + // curent msg is video frame, so we set to 1. + cached_video_count = 1; + } + + // cache the frame. + gop_cache.push_back(msg->copy()); + + return ret; +} + +void SrsGopCache::clear() +{ + std::vector::iterator it; + for (it = gop_cache.begin(); it != gop_cache.end(); ++it) { + SrsSharedPtrMessage* msg = *it; + srs_freep(msg); + } + gop_cache.clear(); + + cached_video_count = 0; +} + +int SrsGopCache::dump(SrsConsumer* consumer, int tba, int tbv) +{ + int ret = ERROR_SUCCESS; + + std::vector::iterator it; + for (it = gop_cache.begin(); it != gop_cache.end(); ++it) { + SrsSharedPtrMessage* msg = *it; + if ((ret = consumer->enqueue(msg->copy(), tba, tbv)) != ERROR_SUCCESS) { + srs_error("dispatch cached gop failed. ret=%d", ret); + return ret; + } + } + srs_trace("dispatch cached gop success. count=%d, duration=%d", (int)gop_cache.size(), consumer->get_time()); + + return ret; +} + +std::map SrsSource::pool; + +SrsSource* SrsSource::find(std::string stream_url) +{ + if (pool.find(stream_url) == pool.end()) { + pool[stream_url] = new SrsSource(stream_url); + srs_verbose("create new source for url=%s", stream_url.c_str()); + } + + return pool[stream_url]; +} + +SrsSource::SrsSource(std::string _stream_url) +{ + stream_url = _stream_url; + +#ifdef SRS_HLS + hls = new SrsHls(); +#endif +#ifdef SRS_FFMPEG + encoder = new SrsEncoder(); +#endif + + cache_metadata = cache_sh_video = cache_sh_audio = NULL; + + frame_rate = sample_rate = 0; + _can_publish = true; + + gop_cache = new SrsGopCache(); +} + +SrsSource::~SrsSource() +{ + if (true) { + std::vector::iterator it; + for (it = consumers.begin(); it != consumers.end(); ++it) { + SrsConsumer* consumer = *it; + srs_freep(consumer); + } + consumers.clear(); + } + + if (true) { + std::vector::iterator it; + for (it = forwarders.begin(); it != forwarders.end(); ++it) { + SrsForwarder* forwarder = *it; + srs_freep(forwarder); + } + forwarders.clear(); + } + + srs_freep(cache_metadata); + srs_freep(cache_sh_video); + srs_freep(cache_sh_audio); + + srs_freep(gop_cache); + +#ifdef SRS_HLS + srs_freep(hls); +#endif +#ifdef SRS_FFMPEG + srs_freep(encoder); +#endif +} + +bool SrsSource::can_publish() +{ + return _can_publish; +} + +int SrsSource::on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* metadata) +{ + int ret = ERROR_SUCCESS; + +#ifdef SRS_HLS + if ((ret = hls->on_meta_data(metadata)) != ERROR_SUCCESS) { + srs_error("hls process onMetaData message failed. ret=%d", ret); + return ret; + } +#endif + + metadata->metadata->set("server", new SrsAmf0String( + RTMP_SIG_SRS_KEY" "RTMP_SIG_SRS_VERSION" ("RTMP_SIG_SRS_URL_SHORT")")); + metadata->metadata->set("contributor", + new SrsAmf0String(RTMP_SIG_SRS_CONTRIBUTOR)); + + SrsAmf0Any* prop = NULL; + if ((prop = metadata->metadata->get_property("audiosamplerate")) != NULL) { + if (prop->is_number()) { + sample_rate = (int)(srs_amf0_convert(prop)->value); + } + } + if ((prop = metadata->metadata->get_property("framerate")) != NULL) { + if (prop->is_number()) { + frame_rate = (int)(srs_amf0_convert(prop)->value); + } + } + + // encode the metadata to payload + int size = metadata->get_payload_length(); + if (size <= 0) { + srs_warn("ignore the invalid metadata. size=%d", size); + return ret; + } + srs_verbose("get metadata size success."); + + char* payload = new char[size]; + memset(payload, 0, size); + if ((ret = metadata->encode(size, payload)) != ERROR_SUCCESS) { + srs_error("encode metadata error. ret=%d", ret); + srs_freepa(payload); + return ret; + } + srs_verbose("encode metadata success."); + + // create a shared ptr message. + srs_freep(cache_metadata); + cache_metadata = new SrsSharedPtrMessage(); + + // dump message to shared ptr message. + if ((ret = cache_metadata->initialize(msg, payload, size)) != ERROR_SUCCESS) { + srs_error("initialize the cache metadata failed. ret=%d", ret); + return ret; + } + srs_verbose("initialize shared ptr metadata success."); + + // copy to all consumer + if (true) { + std::vector::iterator it; + for (it = consumers.begin(); it != consumers.end(); ++it) { + SrsConsumer* consumer = *it; + if ((ret = consumer->enqueue(cache_metadata->copy(), sample_rate, frame_rate)) != ERROR_SUCCESS) { + srs_error("dispatch the metadata failed. ret=%d", ret); + return ret; + } + } + srs_trace("dispatch metadata success."); + } + + // copy to all forwarders + if (true) { + std::vector::iterator it; + for (it = forwarders.begin(); it != forwarders.end(); ++it) { + SrsForwarder* forwarder = *it; + if ((ret = forwarder->on_meta_data(cache_metadata->copy())) != ERROR_SUCCESS) { + srs_error("forwarder process onMetaData message failed. ret=%d", ret); + return ret; + } + } + } + + return ret; +} + +int SrsSource::on_audio(SrsCommonMessage* audio) +{ + int ret = ERROR_SUCCESS; + + SrsSharedPtrMessage* msg = new SrsSharedPtrMessage(); + SrsAutoFree(SrsSharedPtrMessage, msg, false); + if ((ret = msg->initialize(audio)) != ERROR_SUCCESS) { + srs_error("initialize the audio failed. ret=%d", ret); + return ret; + } + srs_verbose("initialize shared ptr audio success."); + +#ifdef SRS_HLS + if ((ret = hls->on_audio(msg->copy())) != ERROR_SUCCESS) { + srs_warn("hls process audio message failed, ignore and disable hls. ret=%d", ret); + + // unpublish, ignore ret. + hls->on_unpublish(); + + // ignore. + ret = ERROR_SUCCESS; + } +#endif + + // copy to all consumer + if (true) { + std::vector::iterator it; + for (it = consumers.begin(); it != consumers.end(); ++it) { + SrsConsumer* consumer = *it; + if ((ret = consumer->enqueue(msg->copy(), sample_rate, frame_rate)) != ERROR_SUCCESS) { + srs_error("dispatch the audio failed. ret=%d", ret); + return ret; + } + } + srs_info("dispatch audio success."); + } + + // copy to all forwarders. + if (true) { + std::vector::iterator it; + for (it = forwarders.begin(); it != forwarders.end(); ++it) { + SrsForwarder* forwarder = *it; + if ((ret = forwarder->on_audio(msg->copy())) != ERROR_SUCCESS) { + srs_error("forwarder process audio message failed. ret=%d", ret); + return ret; + } + } + } + + // cache the sequence header if h264 + if (SrsCodec::audio_is_sequence_header(msg->payload, msg->size)) { + srs_freep(cache_sh_audio); + cache_sh_audio = msg->copy(); + srs_trace("update audio sequence header success. size=%d", msg->header.payload_length); + return ret; + } + + // cache the last gop packets + if ((ret = gop_cache->cache(msg)) != ERROR_SUCCESS) { + srs_error("shrink gop cache failed. ret=%d", ret); + return ret; + } + srs_verbose("cache gop success."); + + return ret; +} + +int SrsSource::on_video(SrsCommonMessage* video) +{ + int ret = ERROR_SUCCESS; + + SrsSharedPtrMessage* msg = new SrsSharedPtrMessage(); + SrsAutoFree(SrsSharedPtrMessage, msg, false); + if ((ret = msg->initialize(video)) != ERROR_SUCCESS) { + srs_error("initialize the video failed. ret=%d", ret); + return ret; + } + srs_verbose("initialize shared ptr video success."); + +#ifdef SRS_HLS + if ((ret = hls->on_video(msg->copy())) != ERROR_SUCCESS) { + srs_warn("hls process video message failed, ignore and disable hls. ret=%d", ret); + + // unpublish, ignore ret. + hls->on_unpublish(); + + // ignore. + ret = ERROR_SUCCESS; + } +#endif + + // copy to all consumer + if (true) { + std::vector::iterator it; + for (it = consumers.begin(); it != consumers.end(); ++it) { + SrsConsumer* consumer = *it; + if ((ret = consumer->enqueue(msg->copy(), sample_rate, frame_rate)) != ERROR_SUCCESS) { + srs_error("dispatch the video failed. ret=%d", ret); + return ret; + } + } + srs_info("dispatch video success."); + } + + // copy to all forwarders. + if (true) { + std::vector::iterator it; + for (it = forwarders.begin(); it != forwarders.end(); ++it) { + SrsForwarder* forwarder = *it; + if ((ret = forwarder->on_video(msg->copy())) != ERROR_SUCCESS) { + srs_error("forwarder process video message failed. ret=%d", ret); + return ret; + } + } + } + + // cache the sequence header if h264 + if (SrsCodec::video_is_sequence_header(msg->payload, msg->size)) { + srs_freep(cache_sh_video); + cache_sh_video = msg->copy(); + srs_trace("update video sequence header success. size=%d", msg->header.payload_length); + return ret; + } + + // cache the last gop packets + if ((ret = gop_cache->cache(msg)) != ERROR_SUCCESS) { + srs_error("shrink gop cache failed. ret=%d", ret); + return ret; + } + srs_verbose("cache gop success."); + + return ret; +} + +int SrsSource::on_publish(SrsRequest* req) +{ + int ret = ERROR_SUCCESS; + + _can_publish = false; + + // TODO: support reload. + + // create forwarders + SrsConfDirective* conf = config->get_forward(req->vhost); + for (int i = 0; conf && i < (int)conf->args.size(); i++) { + std::string forward_server = conf->args.at(i); + + SrsForwarder* forwarder = new SrsForwarder(); + forwarders.push_back(forwarder); + + if ((ret = forwarder->on_publish(req, forward_server)) != ERROR_SUCCESS) { + srs_error("start forwarder failed. " + "vhost=%s, app=%s, stream=%s, forward-to=%s", + req->vhost.c_str(), req->app.c_str(), req->stream.c_str(), + forward_server.c_str()); + return ret; + } + } + +#ifdef SRS_FFMPEG + if ((ret = encoder->on_publish(req)) != ERROR_SUCCESS) { + return ret; + } +#endif + +#ifdef SRS_HLS + if ((ret = hls->on_publish(req)) != ERROR_SUCCESS) { + return ret; + } +#endif + + return ret; +} + +void SrsSource::on_unpublish() +{ + // close all forwarders + std::vector::iterator it; + for (it = forwarders.begin(); it != forwarders.end(); ++it) { + SrsForwarder* forwarder = *it; + forwarder->on_unpublish(); + srs_freep(forwarder); + } + forwarders.clear(); + +#ifdef SRS_FFMPEG + encoder->on_unpublish(); +#endif + + // TODO: HLS should continue previous sequence and stream. +#ifdef SRS_HLS + hls->on_unpublish(); +#endif + + gop_cache->clear(); + + srs_freep(cache_metadata); + frame_rate = sample_rate = 0; + + srs_freep(cache_sh_video); + srs_freep(cache_sh_audio); + + srs_trace("clear cache/metadata/sequence-headers when unpublish."); + + _can_publish = true; +} + + int SrsSource::create_consumer(SrsConsumer*& consumer) +{ + int ret = ERROR_SUCCESS; + + consumer = new SrsConsumer(this); + consumers.push_back(consumer); + + if (cache_metadata && (ret = consumer->enqueue(cache_metadata->copy(), sample_rate, frame_rate)) != ERROR_SUCCESS) { + srs_error("dispatch metadata failed. ret=%d", ret); + return ret; + } + srs_info("dispatch metadata success"); + + if (cache_sh_video && (ret = consumer->enqueue(cache_sh_video->copy(), sample_rate, frame_rate)) != ERROR_SUCCESS) { + srs_error("dispatch video sequence header failed. ret=%d", ret); + return ret; + } + srs_info("dispatch video sequence header success"); + + if (cache_sh_audio && (ret = consumer->enqueue(cache_sh_audio->copy(), sample_rate, frame_rate)) != ERROR_SUCCESS) { + srs_error("dispatch audio sequence header failed. ret=%d", ret); + return ret; + } + srs_info("dispatch audio sequence header success"); + + if ((ret = gop_cache->dump(consumer, sample_rate, frame_rate)) != ERROR_SUCCESS) { + return ret; + } + + return ret; +} + +void SrsSource::on_consumer_destroy(SrsConsumer* consumer) +{ + std::vector::iterator it; + it = std::find(consumers.begin(), consumers.end(), consumer); + if (it != consumers.end()) { + consumers.erase(it); + } + srs_info("handle consumer destroy success."); +} + +void SrsSource::set_cache(bool enabled) +{ + gop_cache->set(enabled); +} + diff --git a/trunk/src/core/srs_core_thread.cpp b/trunk/src/core/srs_core_thread.cpp new file mode 100644 index 000000000..fbbe4b300 --- /dev/null +++ b/trunk/src/core/srs_core_thread.cpp @@ -0,0 +1,156 @@ +/* +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 + +ISrsThreadHandler::ISrsThreadHandler() +{ +} + +ISrsThreadHandler::~ISrsThreadHandler() +{ +} + +void ISrsThreadHandler::on_enter_loop() +{ +} + +int ISrsThreadHandler::on_before_cycle() +{ + int ret = ERROR_SUCCESS; + return ret; +} + +int ISrsThreadHandler::on_end_cycle() +{ + int ret = ERROR_SUCCESS; + return ret; +} + +void ISrsThreadHandler::on_leave_loop() +{ +} + +SrsThread::SrsThread(ISrsThreadHandler* thread_handler, int64_t interval_ms) +{ + handler = thread_handler; + cycle_interval_milliseconds = interval_ms; + + tid = NULL; + loop = false; +} + +SrsThread::~SrsThread() +{ + stop(); +} + +int SrsThread::start() +{ + int ret = ERROR_SUCCESS; + + if(tid) { + srs_info("thread already running."); + return ret; + } + + if((tid = st_thread_create(thread_fun, this, 1, 0)) == NULL){ + ret = ERROR_ST_CREATE_CYCLE_THREAD; + srs_error("st_thread_create failed. ret=%d", ret); + return ret; + } + + return ret; +} + +void SrsThread::stop() +{ + if (tid) { + loop = false; + + // the interrupt will cause the socket to read/write error, + // which will terminate the cycle thread. + st_thread_interrupt(tid); + + // wait the thread to exit. + st_thread_join(tid, NULL); + + tid = NULL; + } +} + +void SrsThread::thread_cycle() +{ + int ret = ERROR_SUCCESS; + + srs_assert(handler); + + log_context->generate_id(); + srs_trace("thread cycle start"); + + handler->on_end_cycle(); + + loop = true; + while (loop) { + if ((ret = handler->on_before_cycle()) != ERROR_SUCCESS) { + srs_warn("thread on before cycle failed, ignored and retry, ret=%d", ret); + goto failed; + } + srs_info("thread on before cycle success"); + + if ((ret = handler->cycle()) != ERROR_SUCCESS) { + srs_warn("thread cycle failed, ignored and retry, ret=%d", ret); + goto failed; + } + srs_info("thread cycle success"); + + if ((ret = handler->on_end_cycle()) != ERROR_SUCCESS) { + srs_warn("thread on end cycle failed, ignored and retry, ret=%d", ret); + goto failed; + } + srs_info("thread on end cycle success"); + +failed: + if (!loop) { + break; + } + + st_usleep(cycle_interval_milliseconds * 1000); + } + + handler->on_leave_loop(); + srs_trace("thread cycle finished"); +} + +void* SrsThread::thread_fun(void* arg) +{ + SrsThread* obj = (SrsThread*)arg; + srs_assert(obj); + + obj->thread_cycle(); + + return NULL; +} \ No newline at end of file diff --git a/trunk/src/core/srs_core_thread.hpp b/trunk/src/core/srs_core_thread.hpp new file mode 100644 index 000000000..fb3ef8136 --- /dev/null +++ b/trunk/src/core/srs_core_thread.hpp @@ -0,0 +1,98 @@ +/* +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_THREAD_HPP +#define SRS_CORE_THREAD_HPP + +/* +#include +*/ +#include + +/** +* the handler for the thread, callback interface. +* the thread model defines as: +* handler->on_enter_loop() +* while loop: +* handler->on_before_cycle() +* handler->cycle() +* handler->on_end_cycle() +* if !loop then break for user stop thread. +* sleep(CycleIntervalMilliseconds) +* handler->on_leave_loop() +* when stop, the thread will interrupt the st_thread, +* which will cause the socket to return error and +* terminate the cycle thread. +*/ +class ISrsThreadHandler +{ +public: + ISrsThreadHandler(); + virtual ~ISrsThreadHandler(); +public: + virtual void on_enter_loop(); + virtual int on_before_cycle(); + virtual int cycle() = 0; + virtual int on_end_cycle(); + virtual void on_leave_loop(); +}; + +/** +* provides servies from st_thread_t, +* for common thread usage. +*/ +class SrsThread +{ +private: + st_thread_t tid; + bool loop; +private: + ISrsThreadHandler* handler; + int64_t cycle_interval_milliseconds; +public: + /** + * initialize the thread. + * @param thread_handler, the cycle handler for the thread. + * @param interval_ms, the sleep interval when cycle finished. + */ + SrsThread(ISrsThreadHandler* thread_handler, int64_t interval_ms); + virtual ~SrsThread(); +public: + /** + * start the thread, invoke the cycle of handler util + * user stop the thread. + * @remark ignore any error of cycle of handler. + * @remark user can start multiple times, ignore if already started. + */ + virtual int start(); + /** + * stop the thread, wait for the thread to terminate. + * @remark user can stop multiple times, ignore if already stopped. + */ + virtual void stop(); +private: + virtual void thread_cycle(); + static void* thread_fun(void* arg); +}; + +#endif diff --git a/trunk/src/srs/srs.upp b/trunk/src/srs/srs.upp index 7286f909c..53f5277c1 100755 --- a/trunk/src/srs/srs.upp +++ b/trunk/src/srs/srs.upp @@ -1,60 +1,62 @@ file - main readonly separator, - ..\main\srs_main_server.cpp, - auto readonly separator, - ..\..\objs\srs_auto_headers.hpp, - core readonly separator, - ..\core\srs_core.hpp, - ..\core\srs_core.cpp, - ..\core\srs_core_error.hpp, - ..\core\srs_core_error.cpp, - ..\core\srs_core_autofree.hpp, - ..\core\srs_core_autofree.cpp, - ..\core\srs_core_server.hpp, - ..\core\srs_core_server.cpp, - ..\core\srs_core_reload.hpp, - ..\core\srs_core_reload.cpp, - ..\core\srs_core_config.hpp, - ..\core\srs_core_config.cpp, - ..\core\srs_core_refer.hpp, - ..\core\srs_core_refer.cpp, - ..\core\srs_core_conn.hpp, - ..\core\srs_core_conn.cpp, - ..\core\srs_core_client.hpp, - ..\core\srs_core_client.cpp, - ..\core\srs_core_http.hpp, - ..\core\srs_core_http.cpp, - ..\core\srs_core_source.hpp, - ..\core\srs_core_source.cpp, - ..\core\srs_core_forward.hpp, - ..\core\srs_core_forward.cpp, - ..\core\srs_core_encoder.hpp, - ..\core\srs_core_encoder.cpp, - ..\core\srs_core_hls.hpp, - ..\core\srs_core_hls.cpp, - ..\core\srs_core_codec.hpp, - ..\core\srs_core_codec.cpp, - ..\core\srs_core_rtmp.hpp, - ..\core\srs_core_rtmp.cpp, - ..\core\srs_core_handshake.hpp, - ..\core\srs_core_handshake.cpp, - ..\core\srs_core_protocol.hpp, - ..\core\srs_core_protocol.cpp, - ..\core\srs_core_amf0.hpp, - ..\core\srs_core_amf0.cpp, - ..\core\srs_core_stream.hpp, - ..\core\srs_core_stream.cpp, - ..\core\srs_core_socket.hpp, - ..\core\srs_core_socket.cpp, - ..\core\srs_core_buffer.hpp, - ..\core\srs_core_buffer.cpp, - ..\core\srs_core_pithy_print.hpp, - ..\core\srs_core_pithy_print.cpp, - ..\core\srs_core_log.hpp, - ..\core\srs_core_log.cpp, - research readonly separator, - ..\..\research\ts_info.cc; + main readonly separator, + ..\main\srs_main_server.cpp, + auto readonly separator, + ..\..\objs\srs_auto_headers.hpp, + core readonly separator, + ..\core\srs_core.hpp, + ..\core\srs_core.cpp, + ..\core\srs_core_amf0.hpp, + ..\core\srs_core_amf0.cpp, + ..\core\srs_core_autofree.hpp, + ..\core\srs_core_autofree.cpp, + ..\core\srs_core_buffer.hpp, + ..\core\srs_core_buffer.cpp, + ..\core\srs_core_client.hpp, + ..\core\srs_core_client.cpp, + ..\core\srs_core_codec.hpp, + ..\core\srs_core_codec.cpp, + ..\core\srs_core_config.hpp, + ..\core\srs_core_config.cpp, + ..\core\srs_core_conn.hpp, + ..\core\srs_core_conn.cpp, + ..\core\srs_core_encoder.hpp, + ..\core\srs_core_encoder.cpp, + ..\core\srs_core_error.hpp, + ..\core\srs_core_error.cpp, + ..\core\srs_core_forward.hpp, + ..\core\srs_core_forward.cpp, + ..\core\srs_core_handshake.hpp, + ..\core\srs_core_handshake.cpp, + ..\core\srs_core_hls.hpp, + ..\core\srs_core_hls.cpp, + ..\core\srs_core_http.hpp, + ..\core\srs_core_http.cpp, + ..\core\srs_core_log.hpp, + ..\core\srs_core_log.cpp, + ..\core\srs_core_pithy_print.hpp, + ..\core\srs_core_pithy_print.cpp, + ..\core\srs_core_protocol.hpp, + ..\core\srs_core_protocol.cpp, + ..\core\srs_core_refer.hpp, + ..\core\srs_core_refer.cpp, + ..\core\srs_core_reload.hpp, + ..\core\srs_core_reload.cpp, + ..\core\srs_core_rtmp.hpp, + ..\core\srs_core_rtmp.cpp, + ..\core\srs_core_thread.hpp, + ..\core\srs_core_thread.cpp, + ..\core\srs_core_server.hpp, + ..\core\srs_core_server.cpp, + ..\core\srs_core_stream.hpp, + ..\core\srs_core_stream.cpp, + ..\core\srs_core_socket.hpp, + ..\core\srs_core_socket.cpp, + ..\core\srs_core_source.hpp, + ..\core\srs_core_source.cpp, + research readonly separator, + ..\..\research\ts_info.cc; mainconfig - "" = "MAIN"; + "" = "MAIN";