From 20d1732ced9209097a6cd1f350ead83f3b7272a8 Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 27 Dec 2013 17:03:12 +0800 Subject: [PATCH 01/26] merge from wenjie, the bandwidth test feature. --- README.md | 1 + trunk/conf/srs.conf | 2 +- trunk/configure | 20 +- trunk/research/players/js/srs.bandwidth.js | 27 + trunk/research/players/srs_bwt.html | 127 ++- .../players/srs_bwt/.actionScriptProperties | 40 + trunk/research/players/srs_bwt/.project | 17 + .../players/srs_bwt/release/srs_bwt.swf | Bin 0 -> 2603 bytes .../srs_bwt/src/SrsClass/SrsElapsedTimer.as | 24 + .../srs_bwt/src/SrsClass/SrsSettings.as | 27 + trunk/research/players/srs_bwt/src/srs_bwt.as | 227 +++++ trunk/src/core/srs_core_bandwidth.cpp | 16 +- trunk/src/core/srs_core_protocol.cpp | 2 +- trunk/src/core/srs_core_rtmp.hpp | 460 +++++----- trunk/src/main/srs_main_bandcheck.cpp | 832 ++++++++++++++++++ trunk/src/srs/srs.upp | 1 + 16 files changed, 1577 insertions(+), 246 deletions(-) create mode 100755 trunk/research/players/js/srs.bandwidth.js create mode 100755 trunk/research/players/srs_bwt/.actionScriptProperties create mode 100755 trunk/research/players/srs_bwt/.project create mode 100755 trunk/research/players/srs_bwt/release/srs_bwt.swf create mode 100755 trunk/research/players/srs_bwt/src/SrsClass/SrsElapsedTimer.as create mode 100755 trunk/research/players/srs_bwt/src/SrsClass/SrsSettings.as create mode 100755 trunk/research/players/srs_bwt/src/srs_bwt.as mode change 100644 => 100755 trunk/src/core/srs_core_bandwidth.cpp mode change 100644 => 100755 trunk/src/core/srs_core_protocol.cpp mode change 100644 => 100755 trunk/src/core/srs_core_rtmp.hpp create mode 100755 trunk/src/main/srs_main_bandcheck.cpp diff --git a/README.md b/README.md index b0bb23c7d..7fb4df96f 100755 --- a/README.md +++ b/README.md @@ -278,6 +278,7 @@ usr sys idl wai hiq siq| read writ| recv send| in out | int csw * nginx v1.5.0: 139524 lines
### History +* v1.0, 2013-12-27, merge from wenjie, the bandwidth test feature. * v0.9, 2013-12-25, [v0.9](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.9) released. 20926 lines. * v0.9, 2013-12-25, fix the bitrate bug(in Bps), use enhanced microphone. * v0.9, 2013-12-22, demo video meeting or chat(srs+cherrypy+jquery+bootstrap). diff --git a/trunk/conf/srs.conf b/trunk/conf/srs.conf index 18482fbf8..232c1238e 100755 --- a/trunk/conf/srs.conf +++ b/trunk/conf/srs.conf @@ -223,7 +223,7 @@ vhost bandcheck.srs.com { enabled on; # the key for server to valid, # if invalid key, server disconnect and abort the bandwidth check. - key 35c9b402c12a7246868752e2878f7e0e; + key "35c9b402c12a7246868752e2878f7e0e"; # the interval in seconds for bandwidth check, # server donot allow new test request. # default: 30 diff --git a/trunk/configure b/trunk/configure index ba8b1b4fa..5be8cc473 100755 --- a/trunk/configure +++ b/trunk/configure @@ -47,22 +47,27 @@ echo "" >> $SRS_AUTO_HEADERS_H echo "generate Makefile" cat << END > ${SRS_MAKEFILE} -.PHONY: default help clean server _prepare_dir -default: server +.PHONY: default help clean server bandwidth _prepare_dir +default: server bandwidth help: - @echo "Usage: make ||" + @echo "Usage: make |||" @echo " help display this help menu" @echo " clean cleanup project" @echo " server build the srs(simple rtmp server) over st(state-threads)" + @echo " bandwidth build the bandwidth test client tool." clean: - (rm -f Makefile; cd ${SRS_OBJS}; rm -rf srs Makefile *.hpp src st_*_load) + (rm -f Makefile; cd ${SRS_OBJS}; rm -rf srs bandwidth Makefile *.hpp src st_*_load) server: _prepare_dir @echo "build the srs(simple rtmp server) over st(state-threads)" \$(MAKE) -f ${SRS_OBJS}/${SRS_MAKEFILE} srs +bandwidth: _prepare_dir + @echo "build the bandwidth test client tool" + \$(MAKE) -f ${SRS_OBJS}/${SRS_MAKEFILE} bandwidth + # the ./configure will generate it. _prepare_dir: @mkdir -p ${SRS_OBJS} @@ -87,7 +92,7 @@ GCC = g++ LINK = \$(GCC) AR = ar -.PHONY: default srs +.PHONY: default srs bandwidth default: @@ -124,12 +129,12 @@ CORE_OBJS="${MODULE_OBJS[@]}" MODULE_ID="MAIN" MODULE_DEPENDS=("CORE") ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS}) -MODULE_FILES=("srs_main_server") +MODULE_FILES=("srs_main_server" "srs_main_bandcheck") MODULE_DIR="src/main" . auto/modules.sh MAIN_OBJS="${MODULE_OBJS[@].o}" # all main entrances -MAIN_ENTRANCES=("srs_main_server") +MAIN_ENTRANCES=("srs_main_server" "srs_main_bandcheck") # srs(simple rtmp server) over st(state-threads) ModuleLibFiles=(${LibSTfile}) @@ -143,6 +148,7 @@ else LINK_OPTIONS="-ldl" fi BUILD_KEY="srs" APP_MAIN="srs_main_server" APP_NAME="srs" SO_PATH="" . auto/apps.sh +BUILD_KEY="bandwidth" APP_MAIN="srs_main_bandcheck" APP_NAME="bandwidth" SO_PATH="" . auto/apps.sh echo 'configure ok! ' diff --git a/trunk/research/players/js/srs.bandwidth.js b/trunk/research/players/js/srs.bandwidth.js new file mode 100755 index 000000000..79c51e2f8 --- /dev/null +++ b/trunk/research/players/js/srs.bandwidth.js @@ -0,0 +1,27 @@ +// for bw to init url +// url: scheme://host:port/path?query#fragment +function srs_init_bwt(rtmp_url, hls_url) { + update_nav(); + + if (rtmp_url) { + //var query = parse_query_string(); + var search_filed = String(window.location.search).replace(" ", "").split("?")[1]; + $(rtmp_url).val("rtmp://" + window.location.host + ":" + 1935 + "/app?" + search_filed); + } + if (hls_url) { + $(hls_url).val(build_default_hls_url()); + } +} + +function srs_bwt_check_url(url) { + if (url.indexOf("key") != -1 && url.indexOf("vhost") != -1) { + return true; + } + + return false; +} + +function srs_bwt_build_default_url() { + var url_default = "rtmp://" + window.location.host + ":" + 1935 + "/app?key=35c9b402c12a7246868752e2878f7e0e&vhost=bandcheck.srs.com"; + return url_default; +} \ No newline at end of file diff --git a/trunk/research/players/srs_bwt.html b/trunk/research/players/srs_bwt.html index e12b64c58..3f68ea6b5 100755 --- a/trunk/research/players/srs_bwt.html +++ b/trunk/research/players/srs_bwt.html @@ -13,15 +13,112 @@ + @@ -44,6 +141,34 @@
+ +
+ URL: + + +
+ + +

SRS Team © 2013

diff --git a/trunk/research/players/srs_bwt/.actionScriptProperties b/trunk/research/players/srs_bwt/.actionScriptProperties new file mode 100755 index 000000000..cd87f23d7 --- /dev/null +++ b/trunk/research/players/srs_bwt/.actionScriptProperties @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/trunk/research/players/srs_bwt/.project b/trunk/research/players/srs_bwt/.project new file mode 100755 index 000000000..240a737f9 --- /dev/null +++ b/trunk/research/players/srs_bwt/.project @@ -0,0 +1,17 @@ + + + srs_bwt + + + + + + com.adobe.flexbuilder.project.flexbuilder + + + + + + com.adobe.flexbuilder.project.actionscriptnature + + diff --git a/trunk/research/players/srs_bwt/release/srs_bwt.swf b/trunk/research/players/srs_bwt/release/srs_bwt.swf new file mode 100755 index 0000000000000000000000000000000000000000..3e89783117da7113a3b86e0611bfe262224ed0c3 GIT binary patch literal 2603 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 0){ + kbps = bytes_delta * 8.0 / duration_delta; // b/ms == kbps + } + kbps = (int(kbps * 10))/10.0; + + flash.utils.setTimeout(stopPlayTest, 0); + } + + private function stopPlayTest():void{ + connection.call("onSrsBandCheckStoppedPlayBytes", null); + } + + public function onSrsBandCheckStartPublishBytes(evt:Object):void{ + var duration_ms:Number = evt.duration_ms; + var interval_ms:Number = evt.interval_ms; + + connection.call("onSrsBandCheckStartingPublishBytes", null); + updateState("测试上行带宽(" + server_ip + ")"); + + flash.utils.setTimeout(publisher, 0); + } + + private function publisher():void{ + if (stop_pub) { + return; + } + + var data:Array = new Array(); + + var data_size:int = 100; + for(var i:int; i < data_size; i++){ + data.push("SrS band check data from client's publishing......"); + } + data_size += 100; + connection.call("onSrsBandCheckPublishing", null, data); + + flash.utils.setTimeout(publisher, 0); + } + + public function onSrsBandCheckStopPublishBytes(evt:Object):void{ + var duration_ms:Number = evt.duration_ms; + var interval_ms:Number = evt.interval_ms; + var duration_delta:Number = evt.duration_delta; + var bytes_delta:Number = evt.bytes_delta; + + var kbps:Number = 0; + if(duration_delta > 0){ + kbps = bytes_delta * 8.0 / duration_delta; // b/ms == kbps + } + kbps = (int(kbps * 10))/10.0; + + stopPublishTest(); + } + + private function stopPublishTest():void{ + if(connection.connected){ + connection.call("onSrsBandCheckStoppedPublishBytes", null); + } + stop_pub = true; + + value_progressbar = max_progressbar; + updateProgess(value_progressbar, max_progressbar); + updatePlayProgressTimer.stop(); + } + + public function onSrsBandCheckFinished(evt:Object):void{ + var code:Number = evt.code; + var start_time:Number = evt.start_time; + var end_time:Number = evt.end_time; + var play_kbps:Number = evt.play_kbps; + var publish_kbps:Number = evt.publish_kbps; + var play_bytes:Number = evt.play_bytes; + var play_time:Number = evt.play_time; + var publish_bytes:Number = evt.publish_bytes; + var publish_time:Number = evt.publish_time; + + updateState("检测结束: 服务器: " + server_ip + " 上行: " + publish_kbps + " kbps" + " 下行: " + play_kbps + " kbps" + + " 测试时间: " + (end_time-start_time)/1000 + " 秒"); + connection.call("finalClientPacket", null); + } + + public function onBWDone():void{ + // do nothing + } + + // update progressBar's value + private function updateProgess(value:Number, maxValue:Number):void{ + flash.external.ExternalInterface.call(this.js_update_progress, value * 100 / maxValue + "%"); + trace(value + "-" + maxValue + "-" + value * 100 / maxValue + "%"); + } + + // update checking status + private function updateState(text:String):void{ + flash.external.ExternalInterface.call(this.js_update_status, text); + trace(text); + } + } +} \ No newline at end of file diff --git a/trunk/src/core/srs_core_bandwidth.cpp b/trunk/src/core/srs_core_bandwidth.cpp old mode 100644 new mode 100755 index b5b1f1109..4e3a5dd6e --- a/trunk/src/core/srs_core_bandwidth.cpp +++ b/trunk/src/core/srs_core_bandwidth.cpp @@ -24,6 +24,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +#include using namespace std; @@ -260,21 +261,24 @@ int SrsBandwidth::check_play( int64_t current_time = srs_get_system_time_ms(); int size = 1024; // TODO: FIXME: magic number char random_data[size]; - memset(random_data, 0x01, size); + memset(random_data, 'A', size); int interval = 0; + int data_count = 1; while ( (srs_get_system_time_ms() - current_time) < duration_ms ) { st_usleep(interval); // TODO: FIXME: use shared ptr message. SrsBandwidthPacket* pkt = SrsBandwidthPacket::create_playing(); - // TODO: FIXME: magic number - for (int i = 0; i < 100; ++i) { - char buf[32]; // TODO: FIXME: magic number - sprintf(buf, "%d", i); - pkt->data->set(buf, new SrsAmf0String(random_data)); + // TODO: FIXME: magic number + for (int i = 0; i < data_count; ++i) { + std::stringstream seq; + seq << i; + std::string play_data = "SrS band check data from server's playing......"; + pkt->data->set(seq.str(), new SrsAmf0String(play_data.c_str())); } + data_count += 2; // TODO: FIXME: get length from the rtmp protocol stack. play_bytes += pkt->get_payload_length(); diff --git a/trunk/src/core/srs_core_protocol.cpp b/trunk/src/core/srs_core_protocol.cpp old mode 100644 new mode 100755 index 2cbe75b5a..1160711bd --- a/trunk/src/core/srs_core_protocol.cpp +++ b/trunk/src/core/srs_core_protocol.cpp @@ -2789,7 +2789,7 @@ SrsBandwidthPacket* SrsBandwidthPacket::create_start_play() SrsBandwidthPacket* SrsBandwidthPacket::create_playing() { SrsBandwidthPacket* pkt = new SrsBandwidthPacket(); - return pkt->set_command(SRS_BW_CHECK_STARTING_PLAY); + return pkt->set_command(SRS_BW_CHECK_PLAYING); } SrsBandwidthPacket* SrsBandwidthPacket::create_stop_play() diff --git a/trunk/src/core/srs_core_rtmp.hpp b/trunk/src/core/srs_core_rtmp.hpp old mode 100644 new mode 100755 index 459e33436..6726a36e4 --- a/trunk/src/core/srs_core_rtmp.hpp +++ b/trunk/src/core/srs_core_rtmp.hpp @@ -1,231 +1,231 @@ -/* -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_RTMP_HPP -#define SRS_CORE_RTMP_HPP - -/* -#include -*/ - -#include - -#include - -class SrsProtocol; -class ISrsMessage; -class SrsCommonMessage; -class SrsCreateStreamPacket; -class SrsFMLEStartPacket; -class SrsPublishPacket; -class SrsSharedPtrMessage; -class SrsOnMetaDataPacket; - -/** -* the original request from client. -*/ -struct SrsRequest -{ - /** - * tcUrl: rtmp://request_vhost:port/app/stream - * support pass vhost in query string, such as: - * rtmp://ip:port/app?vhost=request_vhost/stream - * rtmp://ip:port/app...vhost...request_vhost/stream - */ - std::string tcUrl; - std::string pageUrl; - std::string swfUrl; - double objectEncoding; - - std::string schema; - std::string vhost; - std::string port; - std::string app; - std::string stream; - - SrsRequest(); - virtual ~SrsRequest(); - - /** - * deep copy the request, for source to use it to support reload, - * for when initialize the source, the request is valid, - * when reload it, the request maybe invalid, so need to copy it. - */ - virtual SrsRequest* copy(); - - /** - * disconvery vhost/app from tcUrl. - */ - virtual int discovery_app(); - virtual std::string get_stream_url(); - virtual void strip(); -private: - std::string& trim(std::string& str, std::string chs); -}; - -/** -* the response to client. -*/ -struct SrsResponse -{ - int stream_id; - - SrsResponse(); - virtual ~SrsResponse(); -}; - -/** -* the rtmp client type. -*/ -enum SrsClientType -{ - SrsClientUnknown, - SrsClientPlay, - SrsClientFMLEPublish, - SrsClientFlashPublish, -}; - -/** -* implements the client role protocol. -*/ -class SrsRtmpClient -{ -private: - SrsProtocol* protocol; - st_netfd_t stfd; -public: - SrsRtmpClient(st_netfd_t _stfd); - virtual ~SrsRtmpClient(); -public: - virtual void set_recv_timeout(int64_t timeout_us); - virtual void set_send_timeout(int64_t timeout_us); - virtual int64_t get_recv_bytes(); - virtual int64_t get_send_bytes(); - virtual int get_recv_kbps(); - virtual int get_send_kbps(); - virtual int recv_message(SrsCommonMessage** pmsg); - virtual int send_message(ISrsMessage* msg); -public: - virtual int handshake(); - virtual int connect_app(std::string app, std::string tc_url); - virtual int create_stream(int& stream_id); - virtual int play(std::string stream, int stream_id); - virtual int publish(std::string stream, int stream_id); -}; - -/** -* the rtmp provices rtmp-command-protocol services, -* a high level protocol, media stream oriented services, -* such as connect to vhost/app, play stream, get audio/video data. -*/ -class SrsRtmp -{ -private: - SrsProtocol* protocol; - st_netfd_t stfd; -public: - SrsRtmp(st_netfd_t client_stfd); - virtual ~SrsRtmp(); -public: - virtual SrsProtocol* get_protocol(); - virtual void set_recv_timeout(int64_t timeout_us); - virtual int64_t get_recv_timeout(); - virtual void set_send_timeout(int64_t timeout_us); - virtual int64_t get_send_timeout(); - virtual int64_t get_recv_bytes(); - virtual int64_t get_send_bytes(); - virtual int get_recv_kbps(); - virtual int get_send_kbps(); - virtual int recv_message(SrsCommonMessage** pmsg); - virtual int send_message(ISrsMessage* msg); -public: - virtual int handshake(); - virtual int connect_app(SrsRequest* req); - virtual int set_window_ack_size(int ack_size); - /** - * @type: The sender can mark this message hard (0), soft (1), or dynamic (2) - * using the Limit type field. - */ - virtual int set_peer_bandwidth(int bandwidth, int type); - /** - * @param server_ip the ip of server. - */ - virtual int response_connect_app(SrsRequest* req, const char* server_ip = NULL); - virtual void response_connect_reject(SrsRequest* req, const char* desc); - virtual int on_bw_done(); - /** - * recv some message to identify the client. - * @stream_id, client will createStream to play or publish by flash, - * the stream_id used to response the createStream request. - * @type, output the client type. - */ - virtual int identify_client(int stream_id, SrsClientType& type, std::string& stream_name); - /** - * set the chunk size when client type identified. - */ - virtual int set_chunk_size(int chunk_size); - /** - * when client type is play, response with packets: - * StreamBegin, - * onStatus(NetStream.Play.Reset), onStatus(NetStream.Play.Start)., - * |RtmpSampleAccess(false, false), - * onStatus(NetStream.Data.Start). - */ - virtual int start_play(int stream_id); - /** - * when client(type is play) send pause message, - * if is_pause, response the following packets: - * onStatus(NetStream.Pause.Notify) - * StreamEOF - * if not is_pause, response the following packets: - * onStatus(NetStream.Unpause.Notify) - * StreamBegin - */ - virtual int on_play_client_pause(int stream_id, bool is_pause); - /** - * when client type is publish, response with packets: - * releaseStream response - * FCPublish - * FCPublish response - * createStream response - * onFCPublish(NetStream.Publish.Start) - * onStatus(NetStream.Publish.Start) - */ - virtual int start_fmle_publish(int stream_id); - /** - * process the FMLE unpublish event. - * @unpublish_tid the unpublish request transaction id. - */ - virtual int fmle_unpublish(int stream_id, double unpublish_tid); - /** - * when client type is publish, response with packets: - * onStatus(NetStream.Publish.Start) - */ - virtual int start_flash_publish(int stream_id); -private: - virtual int identify_create_stream_client(SrsCreateStreamPacket* req, int stream_id, SrsClientType& type, std::string& stream_name); - virtual int identify_fmle_publish_client(SrsFMLEStartPacket* req, SrsClientType& type, std::string& stream_name); - virtual int identify_flash_publish_client(SrsPublishPacket* req, SrsClientType& type, std::string& stream_name); -}; - +/* +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_RTMP_HPP +#define SRS_CORE_RTMP_HPP + +/* +#include +*/ + +#include + +#include + +class SrsProtocol; +class ISrsMessage; +class SrsCommonMessage; +class SrsCreateStreamPacket; +class SrsFMLEStartPacket; +class SrsPublishPacket; +class SrsSharedPtrMessage; +class SrsOnMetaDataPacket; + +/** +* the original request from client. +*/ +struct SrsRequest +{ + /** + * tcUrl: rtmp://request_vhost:port/app/stream + * support pass vhost in query string, such as: + * rtmp://ip:port/app?vhost=request_vhost/stream + * rtmp://ip:port/app...vhost...request_vhost/stream + */ + std::string tcUrl; + std::string pageUrl; + std::string swfUrl; + double objectEncoding; + + std::string schema; + std::string vhost; + std::string port; + std::string app; + std::string stream; + + SrsRequest(); + virtual ~SrsRequest(); + + /** + * deep copy the request, for source to use it to support reload, + * for when initialize the source, the request is valid, + * when reload it, the request maybe invalid, so need to copy it. + */ + virtual SrsRequest* copy(); + + /** + * disconvery vhost/app from tcUrl. + */ + virtual int discovery_app(); + virtual std::string get_stream_url(); + virtual void strip(); +private: + std::string& trim(std::string& str, std::string chs); +}; + +/** +* the response to client. +*/ +struct SrsResponse +{ + int stream_id; + + SrsResponse(); + virtual ~SrsResponse(); +}; + +/** +* the rtmp client type. +*/ +enum SrsClientType +{ + SrsClientUnknown, + SrsClientPlay, + SrsClientFMLEPublish, + SrsClientFlashPublish, +}; + +/** +* implements the client role protocol. +*/ +class SrsRtmpClient +{ +protected: + SrsProtocol* protocol; + st_netfd_t stfd; +public: + SrsRtmpClient(st_netfd_t _stfd); + virtual ~SrsRtmpClient(); +public: + virtual void set_recv_timeout(int64_t timeout_us); + virtual void set_send_timeout(int64_t timeout_us); + virtual int64_t get_recv_bytes(); + virtual int64_t get_send_bytes(); + virtual int get_recv_kbps(); + virtual int get_send_kbps(); + virtual int recv_message(SrsCommonMessage** pmsg); + virtual int send_message(ISrsMessage* msg); +public: + virtual int handshake(); + virtual int connect_app(std::string app, std::string tc_url); + virtual int create_stream(int& stream_id); + virtual int play(std::string stream, int stream_id); + virtual int publish(std::string stream, int stream_id); +}; + +/** +* the rtmp provices rtmp-command-protocol services, +* a high level protocol, media stream oriented services, +* such as connect to vhost/app, play stream, get audio/video data. +*/ +class SrsRtmp +{ +private: + SrsProtocol* protocol; + st_netfd_t stfd; +public: + SrsRtmp(st_netfd_t client_stfd); + virtual ~SrsRtmp(); +public: + virtual SrsProtocol* get_protocol(); + virtual void set_recv_timeout(int64_t timeout_us); + virtual int64_t get_recv_timeout(); + virtual void set_send_timeout(int64_t timeout_us); + virtual int64_t get_send_timeout(); + virtual int64_t get_recv_bytes(); + virtual int64_t get_send_bytes(); + virtual int get_recv_kbps(); + virtual int get_send_kbps(); + virtual int recv_message(SrsCommonMessage** pmsg); + virtual int send_message(ISrsMessage* msg); +public: + virtual int handshake(); + virtual int connect_app(SrsRequest* req); + virtual int set_window_ack_size(int ack_size); + /** + * @type: The sender can mark this message hard (0), soft (1), or dynamic (2) + * using the Limit type field. + */ + virtual int set_peer_bandwidth(int bandwidth, int type); + /** + * @param server_ip the ip of server. + */ + virtual int response_connect_app(SrsRequest* req, const char* server_ip = NULL); + virtual void response_connect_reject(SrsRequest* req, const char* desc); + virtual int on_bw_done(); + /** + * recv some message to identify the client. + * @stream_id, client will createStream to play or publish by flash, + * the stream_id used to response the createStream request. + * @type, output the client type. + */ + virtual int identify_client(int stream_id, SrsClientType& type, std::string& stream_name); + /** + * set the chunk size when client type identified. + */ + virtual int set_chunk_size(int chunk_size); + /** + * when client type is play, response with packets: + * StreamBegin, + * onStatus(NetStream.Play.Reset), onStatus(NetStream.Play.Start)., + * |RtmpSampleAccess(false, false), + * onStatus(NetStream.Data.Start). + */ + virtual int start_play(int stream_id); + /** + * when client(type is play) send pause message, + * if is_pause, response the following packets: + * onStatus(NetStream.Pause.Notify) + * StreamEOF + * if not is_pause, response the following packets: + * onStatus(NetStream.Unpause.Notify) + * StreamBegin + */ + virtual int on_play_client_pause(int stream_id, bool is_pause); + /** + * when client type is publish, response with packets: + * releaseStream response + * FCPublish + * FCPublish response + * createStream response + * onFCPublish(NetStream.Publish.Start) + * onStatus(NetStream.Publish.Start) + */ + virtual int start_fmle_publish(int stream_id); + /** + * process the FMLE unpublish event. + * @unpublish_tid the unpublish request transaction id. + */ + virtual int fmle_unpublish(int stream_id, double unpublish_tid); + /** + * when client type is publish, response with packets: + * onStatus(NetStream.Publish.Start) + */ + virtual int start_flash_publish(int stream_id); +private: + virtual int identify_create_stream_client(SrsCreateStreamPacket* req, int stream_id, SrsClientType& type, std::string& stream_name); + virtual int identify_fmle_publish_client(SrsFMLEStartPacket* req, SrsClientType& type, std::string& stream_name); + virtual int identify_flash_publish_client(SrsPublishPacket* req, SrsClientType& type, std::string& stream_name); +}; + #endif \ No newline at end of file diff --git a/trunk/src/main/srs_main_bandcheck.cpp b/trunk/src/main/srs_main_bandcheck.cpp new file mode 100755 index 000000000..f0342753d --- /dev/null +++ b/trunk/src/main/srs_main_bandcheck.cpp @@ -0,0 +1,832 @@ +/* +The MIT License (MIT) + +Copyright (c) 2013 wenjiegit + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +// server play control +#define SRS_BW_CHECK_START_PLAY "onSrsBandCheckStartPlayBytes" +#define SRS_BW_CHECK_STARTING_PLAY "onSrsBandCheckStartingPlayBytes" +#define SRS_BW_CHECK_STOP_PLAY "onSrsBandCheckStopPlayBytes" +#define SRS_BW_CHECK_STOPPED_PLAY "onSrsBandCheckStoppedPlayBytes" + +// server publish control +#define SRS_BW_CHECK_START_PUBLISH "onSrsBandCheckStartPublishBytes" +#define SRS_BW_CHECK_STARTING_PUBLISH "onSrsBandCheckStartingPublishBytes" +#define SRS_BW_CHECK_STOP_PUBLISH "onSrsBandCheckStopPublishBytes" +#define SRS_BW_CHECK_STOPPED_PUBLISH "onSrsBandCheckStoppedPublishBytes" + +// EOF control. +#define SRS_BW_CHECK_FINISHED "onSrsBandCheckFinished" +#define SRS_BW_CHECK_FLASH_FINAL "finalClientPacket" + +// client only +#define SRS_BW_CHECK_PLAYING "onSrsBandCheckPlaying" +#define SRS_BW_CHECK_PUBLISHING "onSrsBandCheckPublishing" + +/** +* @brief class of Linux version band check client +* check play and publish speed. +*/ +class SrsBandCheckClient : public SrsRtmpClient +{ +public: + SrsBandCheckClient(st_netfd_t _stfd); + ~SrsBandCheckClient(); + +public: + /** + * @brief test play + * + */ + int check_play(); + /** + * @brief test publish + * + */ + int check_publish(); + +private: + /** + * @brief just return success. + */ + int create_stream(int& stream_id); + /** + * @brief just return success. + */ + int play(std::string stream, int stream_id); + /** + * @brief just return success. + */ + int publish(std::string stream, int stream_id); + +private: + int expect_start_play(); + int send_starting_play(); + int expect_stop_play(); + int send_stopped_play(); + int expect_start_pub(); + int send_starting_pub(); + int send_pub_data(); + int expect_stop_pub(); + /** + * @brief expect result. + * because the core module has no method to decode this packet + * so we must get the internal data and decode it here. + */ + int expect_finished(); + int send_stopped_pub(); + /** + * @brief notify server the check procedure is over. + */ + int send_final(); +}; + +/** +* @brief class of band check +* used to check band width with a client @param bandCheck_Client +*/ +class SrsBandCheck +{ +public: + SrsBandCheck(); + ~SrsBandCheck(); + +public: + /** + * @brief band check method + * + * connect to server------>rtmp handshake------>rtmp connect------>play------>publish + * @retval ERROR_SUCCESS when success. + */ + int check(const std::string& app, const std::string& tcUrl); + + /** + * @brief set the address and port of test server + * + * @param server server address, domain or ip + * @param server listened port ,default is 1935 + */ + void set_server(const std::string& server, int port = 1935); + +private: + int connect_server(); + +private: + SrsBandCheckClient* bandCheck_Client; + std::string server_address; + int server_port; +}; + +/** +* @brief init st lib +*/ +static int init_st(); +static void print_help(char** argv); +static void print_version(); + +/** +* @brief get user option +* @internal ip Mandatory arguments +* @internal key Mandatory arguments +* @internal port default 1935 +* @internal vhost default bandcheck.srs.com +*/ +static int get_opt(int argc ,char* argv[]); + +/** +* global var. +*/ +static struct option long_options[] = +{ + {"ip", required_argument, 0, 'i'}, + {"port", optional_argument, 0, 'p'}, + {"key", required_argument, 0, 'k'}, + {"vhost", optional_argument, 0, 'v'}, + {"help", no_argument, 0, 'h'}, + {"version", no_argument, 0, 'V'}, +}; + +static const char* short_options = "i:p::k:v::hV"; + +static std::string g_ip; +static int g_port = 1935; +static std::string g_key; +static std::string g_vhost = "bandcheck.srs.com"; + +#define BUILD_VERSION "srs band check 0.1" + +int main(int argc ,char* argv[]) +{ + int ret = ERROR_SUCCESS; + + if (argc <= 1) { + print_help(argv); + exit(1); + } + + if ((ret = get_opt(argc, argv)) != ERROR_SUCCESS) { + return -1; + } + + // check param + if (g_ip.empty()) { + printf("ip address should not be empty.\n"); + return -1; + } + + if (g_key.empty()) { + printf("test key should not be empty.\n"); + return -1; + } + + if ((ret = init_st()) != ERROR_SUCCESS) { + srs_error("band check init failed. ret=%d", ret); + return ret; + } + + std::string app = "app?key=" + g_key + "&vhost=" + g_vhost; + + char tcUrl_buffer[1024] = {0}; + sprintf(tcUrl_buffer, "rtmp://%s:%d/%s", g_ip.c_str(), g_port, app.c_str()); + std::string tcUrl = tcUrl_buffer; + + SrsBandCheck band_check; + band_check.set_server(g_ip, g_port); + if ((ret = band_check.check(app, tcUrl)) != ERROR_SUCCESS) { + srs_error("band check failed. address=%s ret=%d", "xx.com", ret); + return -1; + } + + return 0; +} + +SrsBandCheckClient::SrsBandCheckClient(st_netfd_t _stfd) + : SrsRtmpClient(_stfd) +{ +} + +SrsBandCheckClient::~SrsBandCheckClient() +{ +} + +int SrsBandCheckClient::check_play() +{ + int ret = ERROR_SUCCESS; + + if ((ret = expect_start_play()) != ERROR_SUCCESS) { + srs_error("expect_start_play failed. ret=%d", ret); + return ret; + } + + if ((ret = send_starting_play()) != ERROR_SUCCESS) { + srs_error("send starting play failed. ret=%d", ret); + return ret; + } + + if ((ret = expect_stop_play()) != ERROR_SUCCESS) { + srs_error("expect stop play failed. ret=%d", ret); + return ret; + } + + if ((ret = send_stopped_play()) != ERROR_SUCCESS) { + srs_error("send stopped play failed. ret=%d", ret); + return ret; + } + + return ret; +} + +int SrsBandCheckClient::check_publish() +{ + int ret = ERROR_SUCCESS; + + if ((ret = expect_start_pub()) != ERROR_SUCCESS) { + srs_error("expect start pub failed. ret=%d", ret); + return ret; + } + + if ((ret = send_starting_pub())!= ERROR_SUCCESS) { + srs_error("send starting pub failed. ret=%d", ret); + return ret; + } + + if ((ret = send_pub_data()) != ERROR_SUCCESS) { + srs_error("publish data failed. ret=%d", ret); + return ret; + } + + if ((ret = send_stopped_pub()) != ERROR_SUCCESS) { + srs_error("send stopped pub failed. ret=%d", ret); + return ret; + } + + if ((ret = expect_finished()) != ERROR_SUCCESS) { + srs_error("expect finished msg failed. ret=%d", ret); + return ret; + } + + if ((ret = send_final()) != ERROR_SUCCESS) { + srs_error("send final msg failed. ret=%d", ret); + return ret; + } + + return ret; +} + +int SrsBandCheckClient::create_stream(int &stream_id) +{ + return ERROR_SUCCESS; +} + +int SrsBandCheckClient::play(std::string stream, int stream_id) +{ + return ERROR_SUCCESS; +} + +int SrsBandCheckClient::publish(std::string stream, int stream_id) +{ + return ERROR_SUCCESS; +} + +int SrsBandCheckClient::expect_start_play() +{ + int ret = ERROR_SUCCESS; + + // expect connect _result + SrsCommonMessage* msg = NULL; + SrsBandwidthPacket* pkt = NULL; + if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { + srs_error("expect bandcheck start play message failed. ret=%d", ret); + return ret; + } + SrsAutoFree(SrsCommonMessage, msg, false); + srs_info("get bandcheck start play message"); + + if (pkt->command_name != SRS_BW_CHECK_START_PLAY) { + srs_error("pkt error. expect=%s, actual=%s", SRS_BW_CHECK_START_PLAY, pkt->command_name.c_str()); + return -1; + } + + return ret; +} + +int SrsBandCheckClient::send_starting_play() +{ + int ret = ERROR_SUCCESS; + + SrsCommonMessage* msg = new SrsCommonMessage; + SrsBandwidthPacket* pkt = new SrsBandwidthPacket; + pkt->command_name = SRS_BW_CHECK_STARTING_PLAY; + msg->set_packet(pkt, 0); + + if ((ret = send_message(msg)) != ERROR_SUCCESS) { + srs_error("send starting play msg failed. ret=%d", ret); + return ret; + } + + return ret; +} + +int SrsBandCheckClient::expect_stop_play() +{ + int ret = ERROR_SUCCESS; + + while (true) { + SrsCommonMessage* msg = NULL; + SrsBandwidthPacket* pkt = NULL; + if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { + srs_error("expect stop play message failed. ret=%d", ret); + return ret; + } + SrsAutoFree(SrsCommonMessage, msg, false); + srs_info("get bandcheck stop play message"); + + if (pkt->command_name == SRS_BW_CHECK_STOP_PLAY) { + break; + } + } + + return ret; +} + +int SrsBandCheckClient::send_stopped_play() +{ + int ret = ERROR_SUCCESS; + + SrsCommonMessage* msg = new SrsCommonMessage; + SrsBandwidthPacket* pkt = new SrsBandwidthPacket; + pkt->command_name = SRS_BW_CHECK_STOPPED_PLAY; + msg->set_packet(pkt, 0); + + if ((ret = send_message(msg)) != ERROR_SUCCESS) { + srs_error("send stopped play msg failed. ret=%d", ret); + return ret; + } + + return ret; +} + +int SrsBandCheckClient::expect_start_pub() +{ + int ret = ERROR_SUCCESS; + + while (true) { + SrsCommonMessage* msg = NULL; + SrsBandwidthPacket* pkt = NULL; + if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { + srs_error("expect start pub message failed. ret=%d", ret); + return ret; + } + SrsAutoFree(SrsCommonMessage, msg, false); + srs_info("get bandcheck start pub message"); + + if (pkt->command_name == SRS_BW_CHECK_START_PUBLISH) { + break; + } + } + + return ret; +} + +int SrsBandCheckClient::send_starting_pub() +{ + int ret = ERROR_SUCCESS; + + SrsCommonMessage* msg = new SrsCommonMessage; + SrsBandwidthPacket* pkt = new SrsBandwidthPacket; + pkt->command_name = SRS_BW_CHECK_STARTING_PUBLISH; + msg->set_packet(pkt, 0); + + if ((ret = send_message(msg)) != ERROR_SUCCESS) { + srs_error("send starting play msg failed. ret=%d", ret); + return ret; + } + srs_info("send starting play msg success."); + + return ret; +} + +int SrsBandCheckClient::send_pub_data() +{ + int ret = ERROR_SUCCESS; + + int data_count = 100; + while (true) { + SrsCommonMessage* msg = new SrsCommonMessage; + SrsBandwidthPacket* pkt = new SrsBandwidthPacket; + pkt->command_name = SRS_BW_CHECK_PUBLISHING; + msg->set_packet(pkt, 0); + + for (int i = 0; i < data_count; ++i) { + std::stringstream seq; + seq << i; + std::string play_data = "SrS band check data from client's publishing......"; + pkt->data->set(seq.str(), new SrsAmf0String(play_data.c_str())); + } + data_count += 100; + + if ((ret = send_message(msg)) != ERROR_SUCCESS) { + srs_error("send publish message failed.ret=%d", ret); + return ret; + } + + if ((ret = expect_stop_pub()) == ERROR_SUCCESS) { + break; + } + } + + return ret; +} + +int SrsBandCheckClient::expect_stop_pub() +{ + int ret = ERROR_SUCCESS; + + while (true) { + if ((ret = st_netfd_poll(stfd, POLLIN, 1000)) == ERROR_SUCCESS) { + SrsCommonMessage* msg = 0; + if ((ret = recv_message(&msg)) != ERROR_SUCCESS) + { + srs_error("recv message failed while expect stop pub. ret=%d", ret); + return ret; + } + + if ((ret = msg->decode_packet(protocol)) != ERROR_SUCCESS) { + srs_error("decode packet error while expect stop pub. ret=%d", ret); + return ret; + } + + SrsBandwidthPacket* pkt = dynamic_cast(msg->get_packet()); + if (pkt && pkt->command_name == SRS_BW_CHECK_STOP_PUBLISH) { + + return ret; + } + } else { + break; + } + } + + return ret; +} + +int SrsBandCheckClient::expect_finished() +{ + int ret = ERROR_SUCCESS; + + while (true) { + SrsCommonMessage* msg = NULL; + SrsBandwidthPacket* pkt = NULL; + if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { + srs_error("expect finished message failed. ret=%d", ret); + return ret; + } + SrsAutoFree(SrsCommonMessage, msg, false); + srs_info("get bandcheck finished message"); + + if (pkt->command_name == SRS_BW_CHECK_FINISHED) { + SrsStream *stream = new SrsStream; + SrsAutoFree(SrsStream, stream, false); + + if ((ret = stream->initialize((char*)msg->payload, msg->size)) != ERROR_SUCCESS) { + srs_error("initialize stream error. ret=%d", ret); + return ret; + } + + std::string command_name; + if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) { + srs_error("amfo read string error. ret=%d", ret); + return ret; + } + + double action_id; + if ((ret = srs_amf0_read_number(stream, action_id)) != ERROR_SUCCESS) { + srs_error("amfo read number error. ret=%d", ret); + return ret; + } + + if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) { + srs_error("amfo read number error. ret=%d", ret); + return ret; + } + + SrsAmf0Object* object; + if ((ret = srs_amf0_read_object(stream, object)) != ERROR_SUCCESS) { + srs_error("amfo read object error. ret=%d", ret); + return ret; + } + + int64_t start_time = 0; + int64_t end_time = 0; + + SrsAmf0Any* start_time_any = object->get_property("start_time"); + if (start_time_any && start_time_any->is_number()) { + SrsAmf0Number* start_time_number = dynamic_cast (start_time_any); + if (start_time_number) { + start_time = start_time_number->value; + } + } + + SrsAmf0Any* end_time_any = object->get_property("end_time"); + if (end_time_any && end_time_any->is_number()) { + SrsAmf0Number* end_time_number = dynamic_cast (end_time_any); + if (end_time_number) { + end_time = end_time_number->value; + } + } + + int play_kbps = 0; + int pub_kbps = 0; + SrsAmf0Any* play_kbp_any = object->get_property("play_kbps"); + if (play_kbp_any && play_kbp_any->is_number()) { + SrsAmf0Number* play_kbps_number = dynamic_cast (play_kbp_any); + if (play_kbps_number) { + play_kbps = play_kbps_number->value; + } + } + + SrsAmf0Any* pub_kbp_any = object->get_property("publish_kbps"); + if (pub_kbp_any && pub_kbp_any->is_number()) { + SrsAmf0Number* pub_kbps_number = dynamic_cast (pub_kbp_any); + if (pub_kbps_number) { + pub_kbps = pub_kbps_number->value; + } + } + + float time_elapsed; + if (end_time - start_time > 0) { + time_elapsed = (end_time - start_time) / 1000.00; + } + + srs_trace("result: play %d kbps, publish %d kbps, check time %.4f S\n" + , play_kbps, pub_kbps, time_elapsed); + + break; + } + } + + return ret; +} + +int SrsBandCheckClient::send_stopped_pub() +{ + int ret = ERROR_SUCCESS; + + SrsCommonMessage* msg = new SrsCommonMessage; + SrsBandwidthPacket* pkt = new SrsBandwidthPacket; + pkt->command_name = SRS_BW_CHECK_STOPPED_PUBLISH; + msg->set_packet(pkt, 0); + + if ((ret = send_message(msg)) != ERROR_SUCCESS) { + srs_error("send stopped pub msg failed. ret=%d", ret); + return ret; + } + srs_info("send stopped pub msg success."); + + return ret; +} + +int SrsBandCheckClient::send_final() +{ + int ret = ERROR_SUCCESS; + + SrsCommonMessage* msg = new SrsCommonMessage; + SrsBandwidthPacket* pkt = new SrsBandwidthPacket; + pkt->command_name = SRS_BW_CHECK_FLASH_FINAL; + msg->set_packet(pkt, 0); + + if ((ret = send_message(msg)) != ERROR_SUCCESS) { + srs_error("send final msg failed. ret=%d", ret); + return ret; + } + srs_info("send final msg success."); + + return ret; +} + +SrsBandCheck::SrsBandCheck() + : bandCheck_Client(0) +{ +} + +SrsBandCheck::~SrsBandCheck() +{ + if (bandCheck_Client) { + srs_freep(bandCheck_Client); + } +} + +int SrsBandCheck::check(const std::string &app, const std::string &tcUrl) +{ + int ret = ERROR_SUCCESS; + + if ((ret = connect_server()) != ERROR_SUCCESS) { + srs_error("connect to server failed. ret = %d", ret); + return ret; + } + + if ((ret = bandCheck_Client->handshake()) != ERROR_SUCCESS) { + srs_error("handshake failed. ret = %d", ret); + return ret; + } + + if ((ret = bandCheck_Client->connect_app(app, tcUrl)) != ERROR_SUCCESS) { + srs_error("handshake failed. ret = %d", ret); + return ret; + } + + if ((ret = bandCheck_Client->check_play()) != ERROR_SUCCESS) { + srs_error("band check play failed."); + return ret; + } + + if ((ret = bandCheck_Client->check_publish()) != ERROR_SUCCESS) { + srs_error("band check publish failed."); + return ret; + } + + return ret; +} + +void SrsBandCheck::set_server(const std::string &server, int port) +{ + server_address = server; + server_port = port; +} + +int SrsBandCheck::connect_server() +{ + int ret = ERROR_SUCCESS; + + 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; + } + + st_netfd_t 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; + } + + bandCheck_Client = new SrsBandCheckClient(stfd); + + // connect to server. + std::string ip = srs_dns_resolve(server_address); + 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(server_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(), server_port, ret); + return ret; + } + srs_trace("connect to server success. server=%s, ip=%s, port=%d", server_address.c_str(), ip.c_str(), server_port); + + return ret; +} + +int init_st() +{ + int ret = ERROR_SUCCESS; + + if (st_set_eventsys(ST_EVENTSYS_ALT) == -1) { + ret = ERROR_ST_SET_EPOLL; + srs_error("st_set_eventsys use linux epoll failed. ret=%d", ret); + return ret; + } + + if(st_init() != 0){ + ret = ERROR_ST_INITIALIZE; + srs_error("st_init failed. ret=%d", ret); + return ret; + } + + return ret; +} + +void print_help(char** argv) +{ + printf( + "Usage: %s [OPTION]...\n" + "test band width from client to rtmp server.\n" + "Mandatory arguments to long options are mandatory for short options too.\n" + " -i, --ip the ip or domain that to test\n" + " -p, --port the port that server listen \n" + " -k, --key the key used to test \n" + " -v, --vhost the vhost used to test \n" + " -V, --version output version information and exit \n" + " -h, --help display this help and exit \n" + "\n" + "For example:\n" + " %s -i 192.168.1.248 -p 1935 -v bandcheck.srs.com -k 35c9b402c12a7246868752e2878f7e0e" + "\n\n" + "Exit status:\n" + "0 if OK,\n" + "other if error occured, and the detail should be printed.\n" + "\n\n" + "srs home page: \n", + argv[0], argv[0]); +} + +void print_version() +{ + const char *version = "" + "srs_bandcheck "BUILD_VERSION"\n" + "Copyright (C) 2013 wenjiegit.\n" + "License MIT\n" + "This is free software: you are free to change and redistribute it.\n" + "There is NO WARRANTY, to the extent permitted by law.\n" + "\n" + "Written by wenjie.\n"; + + printf("%s", version); +} + +int get_opt(int argc, char *argv[]) +{ + int ret = ERROR_SUCCESS; + + int c; + while ((c = getopt_long (argc, argv, short_options, long_options, NULL)) != -1) { + switch (c) { + case 'i': + if (optarg) { + g_ip = optarg; + } + break; + case 'p': + if (optarg) { + g_port = atoi(optarg); + } + break; + case 'k': + if (optarg) { + g_key = optarg; + } + break; + case 'v': + if (optarg) { + g_vhost = optarg; + } + break; + case 'V': + print_version(); + exit(0); + break; + case 'h': + print_help(argv); + exit(0); + break; + default: + printf("see --help or -h\n"); + ret = -1; + } + } + + return ret; +} diff --git a/trunk/src/srs/srs.upp b/trunk/src/srs/srs.upp index 06e6aca70..25a8495bf 100755 --- a/trunk/src/srs/srs.upp +++ b/trunk/src/srs/srs.upp @@ -1,6 +1,7 @@ file main readonly separator, ..\main\srs_main_server.cpp, + ..\main\srs_main_bandcheck.cpp, auto readonly separator, ..\..\objs\srs_auto_headers.hpp, core readonly separator, From 19f9342034ce1cbc5e60fef75d3a8e24d9d8c57b Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 27 Dec 2013 18:49:34 +0800 Subject: [PATCH 02/26] refine bandwidth test --- trunk/research/players/js/srs.bandwidth.js | 161 +++++++++++++--- trunk/research/players/js/srs.page.js | 21 +++ trunk/research/players/js/srs.player.js | 2 +- trunk/research/players/js/srs.publisher.js | 2 +- trunk/research/players/srs_bwt.html | 174 +++++++----------- .../players/srs_bwt/.actionScriptProperties | 8 +- .../players/srs_bwt/release/srs_bwt.swf | Bin 2603 -> 4656 bytes trunk/research/players/srs_bwt/src/srs_bwt.as | 125 ++++++++++--- 8 files changed, 322 insertions(+), 171 deletions(-) diff --git a/trunk/research/players/js/srs.bandwidth.js b/trunk/research/players/js/srs.bandwidth.js index 79c51e2f8..1b63b2ac8 100755 --- a/trunk/research/players/js/srs.bandwidth.js +++ b/trunk/research/players/js/srs.bandwidth.js @@ -1,27 +1,136 @@ -// for bw to init url -// url: scheme://host:port/path?query#fragment -function srs_init_bwt(rtmp_url, hls_url) { - update_nav(); - - if (rtmp_url) { - //var query = parse_query_string(); - var search_filed = String(window.location.search).replace(" ", "").split("?")[1]; - $(rtmp_url).val("rtmp://" + window.location.host + ":" + 1935 + "/app?" + search_filed); - } - if (hls_url) { - $(hls_url).val(build_default_hls_url()); - } -} - -function srs_bwt_check_url(url) { - if (url.indexOf("key") != -1 && url.indexOf("vhost") != -1) { - return true; - } - - return false; -} - -function srs_bwt_build_default_url() { - var url_default = "rtmp://" + window.location.host + ":" + 1935 + "/app?key=35c9b402c12a7246868752e2878f7e0e&vhost=bandcheck.srs.com"; - return url_default; +/** +* the SrsBandwidth object. +* @param container the html container id. +* @param width a float value specifies the width of bandwidth. +* @param height a float value specifies the height of bandwidth. +* @param private_object [optional] an object that used as private object, +* for example, the logic chat object which owner this bandwidth. +*/ +function SrsBandwidth(container, width, height, private_object) { + if (!SrsBandwidth.__id) { + SrsBandwidth.__id = 100; + } + if (!SrsBandwidth.__bandwidths) { + SrsBandwidth.__bandwidths = []; + } + + SrsBandwidth.__bandwidths.push(this); + + this.private_object = private_object; + this.container = container; + this.width = width; + this.height = height; + this.id = SrsBandwidth.__id++; + this.stream_url = null; + this.callbackObj = null; + + // the callback set data. + this.percent = 0; + this.status = ""; +} +/** +* user can set some callback, then start the bandwidth. +* @param url the bandwidth test url. +* callbacks: +* on_bandwidth_ready():void, when srs bandwidth ready, user can play. +* on_update_progress(percent:Number):void, when srs bandwidth update the progress. +* percent:Number 100 means 100%. +* on_update_status(status:String):void, when srs bandwidth update the status. +* status:String the human readable status text. +*/ +SrsBandwidth.prototype.start = function(url) { + if (url) { + this.stream_url = url; + } + + // embed the flash. + var flashvars = {}; + flashvars.id = this.id; + flashvars.on_bandwidth_ready = "__srs_on_bandwidth_ready"; + flashvars.on_update_progress = "__srs_on_update_progress"; + flashvars.on_update_status = "__srs_on_update_status"; + + var params = {}; + params.wmode = "opaque"; + params.allowFullScreen = "true"; + params.allowScriptAccess = "always"; + + var attributes = {}; + + var self = this; + + swfobject.embedSWF( + "srs_bwt/release/srs_bwt.swf?_version="+srs_get_version_code(), + this.container, + this.width, this.height, + "11.1.0", "js/AdobeFlashbandwidthInstall.swf", + flashvars, params, attributes, + function(callbackObj){ + self.callbackObj = callbackObj; + } + ); + + return this; +} +/** +* play the stream. +* @param stream_url the url of stream, rtmp or http. +* @param volume the volume, 0 is mute, 1 is 100%, 2 is 200%. +*/ +SrsBandwidth.prototype.check_bandwidth = function(url) { + this.stop(); + SrsBandwidth.__bandwidths.push(this); + + if (url) { + this.stream_url = url; + } + + this.callbackObj.ref.__check_bandwidth(this.stream_url); +} +SrsBandwidth.prototype.stop = function(url) { + for (var i = 0; i < SrsBandwidth.__bandwidths.length; i++) { + var bandwidth = SrsBandwidth.__bandwidths[i]; + + if (bandwidth.id != this.id) { + continue; + } + + SrsBandwidth.__bandwidths.splice(i, 1); + break; + } + + this.callbackObj.ref.__stop(); +} +SrsBandwidth.prototype.on_bandwidth_ready = function() { +} +SrsBandwidth.prototype.on_update_progress = function(percent) { +} +SrsBandwidth.prototype.on_update_status = function(status) { +} +function __srs_find_bandwidth(id) { + for (var i = 0; i < SrsBandwidth.__bandwidths.length; i++) { + var bandwidth = SrsBandwidth.__bandwidths[i]; + + if (bandwidth.id != id) { + continue; + } + + return bandwidth; + } + + throw new Error("bandwidth not found. id=" + id); +} +function __srs_on_bandwidth_ready(id) { + var bandwidth = __srs_find_bandwidth(id); + bandwidth.on_bandwidth_ready(); +} +function __srs_on_update_progress(id, percent) { + var bandwidth = __srs_find_bandwidth(id); + bandwidth.percent = percent; + bandwidth.on_update_progress(percent); +} +function __srs_on_update_status(id, status) { + var bandwidth = __srs_find_bandwidth(id); + bandwidth.status = status; + bandwidth.on_update_status(status); } \ No newline at end of file diff --git a/trunk/research/players/js/srs.page.js b/trunk/research/players/js/srs.page.js index 68ce4bd6c..9fd0a5780 100755 --- a/trunk/research/players/js/srs.page.js +++ b/trunk/research/players/js/srs.page.js @@ -80,6 +80,18 @@ function build_default_publish_rtmp_url() { return "rtmp://" + server + ":" + port + "/" + app + "...vhost..." + vhost + "/" + stream; } } +// for the bandwidth tool to init page +function build_default_bandwidth_rtmp_url() { + var query = parse_query_string(); + + var server = (query.server == undefined)? window.location.hostname:query.server; + var port = (query.port == undefined)? 1935:query.port; + var vhost = (query.vhost == undefined)? "bandcheck.srs.com":query.vhost; + var app = (query.app == undefined)? "app":query.app; + var key = (query.key == undefined)? "35c9b402c12a7246868752e2878f7e0e":query.key; + + return "rtmp://" + server + ":" + port + "/" + app + "?key=" + key + "&vhost=" + vhost; +} /** @param server the ip of server. default to window.location.hostname @@ -139,6 +151,15 @@ function srs_init_publish(rtmp_url) { $(rtmp_url).val(build_default_publish_rtmp_url()); } } +// for bw to init url +// url: scheme://host:port/path?query#fragment +function srs_init_bwt(rtmp_url, hls_url) { + update_nav(); + + if (rtmp_url) { + $(rtmp_url).val(build_default_bandwidth_rtmp_url()); + } +} // check whether can republish function srs_can_republish() { diff --git a/trunk/research/players/js/srs.player.js b/trunk/research/players/js/srs.player.js index 3d9da3bb5..dad0ba927 100755 --- a/trunk/research/players/js/srs.player.js +++ b/trunk/research/players/js/srs.player.js @@ -63,7 +63,7 @@ SrsPlayer.prototype.start = function(url) { "srs_player/release/srs_player.swf?_version="+srs_get_version_code(), this.container, this.width, this.height, - "11.1", "js/AdobeFlashPlayerInstall.swf", + "11.1.0", "js/AdobeFlashPlayerInstall.swf", flashvars, params, attributes, function(callbackObj){ self.callbackObj = callbackObj; diff --git a/trunk/research/players/js/srs.publisher.js b/trunk/research/players/js/srs.publisher.js index 50363e7d5..25f29bfed 100755 --- a/trunk/research/players/js/srs.publisher.js +++ b/trunk/research/players/js/srs.publisher.js @@ -73,7 +73,7 @@ SrsPublisher.prototype.start = function() { "srs_publisher/release/srs_publisher.swf?_version="+srs_get_version_code(), this.container, this.width, this.height, - "11.1", "js/AdobeFlashPlayerInstall.swf", + "11.1.0", "js/AdobeFlashPlayerInstall.swf", flashvars, params, attributes, function(callbackObj){ self.callbackObj = callbackObj; diff --git a/trunk/research/players/srs_bwt.html b/trunk/research/players/srs_bwt.html index 3f68ea6b5..dc2593adb 100755 --- a/trunk/research/players/srs_bwt.html +++ b/trunk/research/players/srs_bwt.html @@ -12,113 +12,60 @@ - - + @@ -141,38 +88,43 @@
- +
+ + Usage: 点击“开始测速”即可测带宽,最大可测试带宽由服务器限制 +
URL: - + -
- +
-
+
+
+
diff --git a/trunk/research/players/srs_bwt/.actionScriptProperties b/trunk/research/players/srs_bwt/.actionScriptProperties index cd87f23d7..f95be3da9 100755 --- a/trunk/research/players/srs_bwt/.actionScriptProperties +++ b/trunk/research/players/srs_bwt/.actionScriptProperties @@ -1,6 +1,6 @@ - + @@ -17,13 +17,13 @@ - - + + - + diff --git a/trunk/research/players/srs_bwt/release/srs_bwt.swf b/trunk/research/players/srs_bwt/release/srs_bwt.swf index 3e89783117da7113a3b86e0611bfe262224ed0c3..c540d38254800b6a1fb64878bd4e947d503966ea 100755 GIT binary patch literal 4656 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 literal 2603 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 Date: Fri, 27 Dec 2013 18:50:43 +0800 Subject: [PATCH 03/26] change ui version to 1.19 --- trunk/research/players/js/srs.page.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trunk/research/players/js/srs.page.js b/trunk/research/players/js/srs.page.js index 9fd0a5780..9dc9e3825 100755 --- a/trunk/research/players/js/srs.page.js +++ b/trunk/research/players/js/srs.page.js @@ -10,7 +10,7 @@ function srs_get_player_width() { return srs_get_player_modal() - 30; } function srs_get_player_height() { return srs_get_player_width() * 9 / 19; } // to query the swf anti cache. -function srs_get_version_code() { return "1.17"; } +function srs_get_version_code() { return "1.19"; } // get the default vhost for players. function srs_get_player_vhost() { return "players"; } // the api server port, for chat room. From 62f1f239917c8e272cd4043090bd8c93a6888c44 Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 27 Dec 2013 18:52:09 +0800 Subject: [PATCH 04/26] fix bug of bandwidth test, donot use vhost in query --- trunk/research/players/js/srs.page.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trunk/research/players/js/srs.page.js b/trunk/research/players/js/srs.page.js index 9dc9e3825..62e28a52c 100755 --- a/trunk/research/players/js/srs.page.js +++ b/trunk/research/players/js/srs.page.js @@ -86,7 +86,7 @@ function build_default_bandwidth_rtmp_url() { var server = (query.server == undefined)? window.location.hostname:query.server; var port = (query.port == undefined)? 1935:query.port; - var vhost = (query.vhost == undefined)? "bandcheck.srs.com":query.vhost; + var vhost = "bandcheck.srs.com"; var app = (query.app == undefined)? "app":query.app; var key = (query.key == undefined)? "35c9b402c12a7246868752e2878f7e0e":query.key; From 89ac091c35799aa933957615113f3106bfdadf18 Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 27 Dec 2013 18:54:34 +0800 Subject: [PATCH 05/26] fix bug of bandwidth test, donot use vhost in query --- .../players/srs_bwt/release/srs_bwt.swf | Bin 4656 -> 4664 bytes trunk/research/players/srs_bwt/src/srs_bwt.as | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/trunk/research/players/srs_bwt/release/srs_bwt.swf b/trunk/research/players/srs_bwt/release/srs_bwt.swf index c540d38254800b6a1fb64878bd4e947d503966ea..5035f521aa47a119a3d05b93515861137cc65a5e 100755 GIT binary patch literal 4664 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 literal 4656 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 diff --git a/trunk/research/players/srs_bwt/src/srs_bwt.as b/trunk/research/players/srs_bwt/src/srs_bwt.as index 4e21cef83..0051edf6d 100755 --- a/trunk/research/players/srs_bwt/src/srs_bwt.as +++ b/trunk/research/players/srs_bwt/src/srs_bwt.as @@ -203,7 +203,7 @@ package kbps = (int(kbps * 10))/10.0; flash.utils.setTimeout(stopPlayTest, 0); - updateState("下行带宽测试完毕:" + kbps + "kbps,开始测试上行带宽。"); + updateState("下行带宽测试完毕,服务器: " + server_ip + "," + kbps + "kbps,开始测试上行带宽。"); } private function stopPlayTest():void{ From df349da7d93af5dccea565c63df143b515f6c04c Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 27 Dec 2013 18:55:10 +0800 Subject: [PATCH 06/26] fix bug of bandwidth test, donot use vhost in query --- .../players/srs_bwt/release/srs_bwt.swf | Bin 4664 -> 4669 bytes trunk/research/players/srs_bwt/src/srs_bwt.as | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/trunk/research/players/srs_bwt/release/srs_bwt.swf b/trunk/research/players/srs_bwt/release/srs_bwt.swf index 5035f521aa47a119a3d05b93515861137cc65a5e..bbba9cfd0cf93da074d38f7a673fbbd3f95b4b7f 100755 GIT binary patch literal 4669 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 diff --git a/trunk/research/players/srs_bwt/src/srs_bwt.as b/trunk/research/players/srs_bwt/src/srs_bwt.as index 0051edf6d..c4bdc0944 100755 --- a/trunk/research/players/srs_bwt/src/srs_bwt.as +++ b/trunk/research/players/srs_bwt/src/srs_bwt.as @@ -203,7 +203,7 @@ package kbps = (int(kbps * 10))/10.0; flash.utils.setTimeout(stopPlayTest, 0); - updateState("下行带宽测试完毕,服务器: " + server_ip + "," + kbps + "kbps,开始测试上行带宽。"); + updateState("下行带宽测试完毕,服务器: " + server_ip + ", " + kbps + "kbps,开始测试上行带宽。"); } private function stopPlayTest():void{ From 5537f9b066202593b6b94f59819e0294ecb9b033 Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 27 Dec 2013 18:55:47 +0800 Subject: [PATCH 07/26] fix bug of bandwidth test, donot use vhost in query --- .../players/srs_bwt/release/srs_bwt.swf | Bin 4669 -> 4664 bytes trunk/research/players/srs_bwt/src/srs_bwt.as | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/trunk/research/players/srs_bwt/release/srs_bwt.swf b/trunk/research/players/srs_bwt/release/srs_bwt.swf index bbba9cfd0cf93da074d38f7a673fbbd3f95b4b7f..0ecee07d5b7e20a556e234e852e812d81063848a 100755 GIT binary patch literal 4664 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 Date: Fri, 27 Dec 2013 18:57:36 +0800 Subject: [PATCH 08/26] fix bug of bandwidth test, donot use vhost in query --- .../players/srs_bwt/release/srs_bwt.swf | Bin 4664 -> 4666 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/trunk/research/players/srs_bwt/release/srs_bwt.swf b/trunk/research/players/srs_bwt/release/srs_bwt.swf index 0ecee07d5b7e20a556e234e852e812d81063848a..5ead12bea1c1efca27b02d65b9d0c5ab1ea836c5 100755 GIT binary patch literal 4666 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 literal 4664 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 Date: Wed, 1 Jan 2014 09:49:54 +0800 Subject: [PATCH 09/26] refine the utility of js. --- LICENSE | 2 +- trunk/research/players/js/srs.utility.js | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index f37c3072c..85fff8a0e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/research/players/js/srs.utility.js b/trunk/research/players/js/srs.utility.js index e3886cc77..60562e209 100755 --- a/trunk/research/players/js/srs.utility.js +++ b/trunk/research/players/js/srs.utility.js @@ -13,6 +13,21 @@ function padding(number, length, prefix) { /** * parse the query string to object. +* parse the url location object as: host(hostname:http_port), pathname(dir/filename) +* for example, url http://192.168.1.168:1980/ui/players.html?vhost=player.vhost.com&app=test&stream=livestream +* parsed to object: +{ + host : "192.168.1.168:1980", + hostname : "192.168.1.168", + http_port : 1980, + pathname : "/ui/players.html", + dir : "/ui", + filename : "/players.html", + + vhost : "player.vhost.com", + app : "test", + stream : "livestream" +} */ function parse_query_string(){ var obj = {}; From bb3c88fef7fc04f7f667b891d6231aacbdec9342 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 1 Jan 2014 10:37:12 +0800 Subject: [PATCH 10/26] change copyright from 2013 to 2013-2014. --- trunk/research/api-server/server.py | 4 +- trunk/research/ffempty/ffempty.cc | 2 +- trunk/research/hls/ts_info.cc | 2 +- trunk/src/core/srs_core.cpp | 2 +- trunk/src/core/srs_core.hpp | 4 +- trunk/src/core/srs_core_amf0.cpp | 2 +- trunk/src/core/srs_core_amf0.hpp | 2 +- trunk/src/core/srs_core_autofree.cpp | 2 +- trunk/src/core/srs_core_autofree.hpp | 2 +- trunk/src/core/srs_core_bandwidth.cpp | 2 +- trunk/src/core/srs_core_bandwidth.hpp | 2 +- trunk/src/core/srs_core_buffer.cpp | 2 +- trunk/src/core/srs_core_buffer.hpp | 2 +- trunk/src/core/srs_core_client.cpp | 2 +- trunk/src/core/srs_core_client.hpp | 2 +- trunk/src/core/srs_core_codec.cpp | 2 +- trunk/src/core/srs_core_codec.hpp | 2 +- trunk/src/core/srs_core_config.cpp | 4 +- trunk/src/core/srs_core_config.hpp | 2 +- trunk/src/core/srs_core_conn.cpp | 2 +- trunk/src/core/srs_core_conn.hpp | 2 +- trunk/src/core/srs_core_encoder.cpp | 2 +- trunk/src/core/srs_core_encoder.hpp | 2 +- trunk/src/core/srs_core_error.cpp | 2 +- trunk/src/core/srs_core_error.hpp | 2 +- trunk/src/core/srs_core_forward.cpp | 2 +- trunk/src/core/srs_core_forward.hpp | 2 +- trunk/src/core/srs_core_handshake.cpp | 2 +- trunk/src/core/srs_core_handshake.hpp | 2 +- trunk/src/core/srs_core_hls.cpp | 2 +- trunk/src/core/srs_core_hls.hpp | 2 +- trunk/src/core/srs_core_http.cpp | 2 +- trunk/src/core/srs_core_http.hpp | 2 +- trunk/src/core/srs_core_log.cpp | 2 +- trunk/src/core/srs_core_log.hpp | 2 +- trunk/src/core/srs_core_pithy_print.cpp | 2 +- trunk/src/core/srs_core_pithy_print.hpp | 2 +- trunk/src/core/srs_core_protocol.cpp | 2 +- trunk/src/core/srs_core_protocol.hpp | 2 +- trunk/src/core/srs_core_refer.cpp | 2 +- trunk/src/core/srs_core_refer.hpp | 2 +- trunk/src/core/srs_core_reload.cpp | 2 +- trunk/src/core/srs_core_reload.hpp | 2 +- trunk/src/core/srs_core_rtmp.cpp | 2 +- trunk/src/core/srs_core_rtmp.hpp | 460 +++---- trunk/src/core/srs_core_server.cpp | 2 +- trunk/src/core/srs_core_server.hpp | 2 +- trunk/src/core/srs_core_socket.cpp | 2 +- trunk/src/core/srs_core_socket.hpp | 2 +- trunk/src/core/srs_core_source.cpp | 2 +- trunk/src/core/srs_core_source.hpp | 2 +- trunk/src/core/srs_core_stream.cpp | 2 +- trunk/src/core/srs_core_stream.hpp | 2 +- trunk/src/core/srs_core_thread.cpp | 2 +- trunk/src/core/srs_core_thread.hpp | 2 +- trunk/src/main/srs_main_bandcheck.cpp | 1664 +++++++++++------------ trunk/src/main/srs_main_server.cpp | 2 +- 57 files changed, 1120 insertions(+), 1120 deletions(-) mode change 100755 => 100644 trunk/src/core/srs_core_bandwidth.cpp mode change 100755 => 100644 trunk/src/core/srs_core_protocol.cpp mode change 100755 => 100644 trunk/src/core/srs_core_rtmp.hpp mode change 100755 => 100644 trunk/src/main/srs_main_bandcheck.cpp diff --git a/trunk/research/api-server/server.py b/trunk/research/api-server/server.py index 01b7bf929..5f73022bd 100755 --- a/trunk/research/api-server/server.py +++ b/trunk/research/api-server/server.py @@ -2,7 +2,7 @@ ''' The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in @@ -463,7 +463,7 @@ if __name__ != "__main__": # check the user options if len(sys.argv) <= 1: - print "SRS api callback server, Copyright (c) 2013 winlin" + print "SRS api callback server, Copyright (c) 2013-2014 winlin" print "Usage: python %s "%(sys.argv[0]) print " port: the port to listen at." print "For example:" diff --git a/trunk/research/ffempty/ffempty.cc b/trunk/research/ffempty/ffempty.cc index 8def35f25..8df9c7d1b 100644 --- a/trunk/research/ffempty/ffempty.cc +++ b/trunk/research/ffempty/ffempty.cc @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/research/hls/ts_info.cc b/trunk/research/hls/ts_info.cc index dfe682428..8273b073e 100644 --- a/trunk/research/hls/ts_info.cc +++ b/trunk/research/hls/ts_info.cc @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core.cpp b/trunk/src/core/srs_core.cpp index 8ef6cceca..05a80f45a 100644 --- a/trunk/src/core/srs_core.cpp +++ b/trunk/src/core/srs_core.cpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index ce3bfadc5..64f79f2f1 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in @@ -78,7 +78,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define RTMP_SIG_SRS_WEB "http://blog.csdn.net/win_lin" #define RTMP_SIG_SRS_EMAIL "winterserver@126.com" #define RTMP_SIG_SRS_LICENSE "The MIT License (MIT)" -#define RTMP_SIG_SRS_COPYRIGHT "Copyright (c) 2013 winlin" +#define RTMP_SIG_SRS_COPYRIGHT "Copyright (c) 2013-2014 winlin" #define RTMP_SIG_SRS_CONTRIBUTOR "winlin,wenjiegit" // compare diff --git a/trunk/src/core/srs_core_amf0.cpp b/trunk/src/core/srs_core_amf0.cpp index b6b8a2dc5..940bf6f1e 100644 --- a/trunk/src/core/srs_core_amf0.cpp +++ b/trunk/src/core/srs_core_amf0.cpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_amf0.hpp b/trunk/src/core/srs_core_amf0.hpp index 68ff63784..219011351 100644 --- a/trunk/src/core/srs_core_amf0.hpp +++ b/trunk/src/core/srs_core_amf0.hpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_autofree.cpp b/trunk/src/core/srs_core_autofree.cpp index ba391709a..cbd317870 100644 --- a/trunk/src/core/srs_core_autofree.cpp +++ b/trunk/src/core/srs_core_autofree.cpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_autofree.hpp b/trunk/src/core/srs_core_autofree.hpp index 73035b783..513a4dcf5 100644 --- a/trunk/src/core/srs_core_autofree.hpp +++ b/trunk/src/core/srs_core_autofree.hpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_bandwidth.cpp b/trunk/src/core/srs_core_bandwidth.cpp old mode 100755 new mode 100644 index 4e3a5dd6e..fd833bd11 --- a/trunk/src/core/srs_core_bandwidth.cpp +++ b/trunk/src/core/srs_core_bandwidth.cpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 wenjiegit +Copyright (c) 2013-2014 wenjiegit 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 diff --git a/trunk/src/core/srs_core_bandwidth.hpp b/trunk/src/core/srs_core_bandwidth.hpp index ef66a86f2..0f375bdc5 100644 --- a/trunk/src/core/srs_core_bandwidth.hpp +++ b/trunk/src/core/srs_core_bandwidth.hpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 wenjiegit +Copyright (c) 2013-2014 wenjiegit 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 diff --git a/trunk/src/core/srs_core_buffer.cpp b/trunk/src/core/srs_core_buffer.cpp index 11d760a2c..31572b6c5 100644 --- a/trunk/src/core/srs_core_buffer.cpp +++ b/trunk/src/core/srs_core_buffer.cpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_buffer.hpp b/trunk/src/core/srs_core_buffer.hpp index a36f5f65b..8d66cdcbd 100644 --- a/trunk/src/core/srs_core_buffer.hpp +++ b/trunk/src/core/srs_core_buffer.hpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_client.cpp b/trunk/src/core/srs_core_client.cpp index e1901c248..7fb4ef5c6 100644 --- a/trunk/src/core/srs_core_client.cpp +++ b/trunk/src/core/srs_core_client.cpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_client.hpp b/trunk/src/core/srs_core_client.hpp index f8aaee87c..3d93f4123 100644 --- a/trunk/src/core/srs_core_client.hpp +++ b/trunk/src/core/srs_core_client.hpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_codec.cpp b/trunk/src/core/srs_core_codec.cpp index af1b91519..323d7621e 100644 --- a/trunk/src/core/srs_core_codec.cpp +++ b/trunk/src/core/srs_core_codec.cpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_codec.hpp b/trunk/src/core/srs_core_codec.hpp index 18cc32a5a..5e700dafe 100644 --- a/trunk/src/core/srs_core_codec.hpp +++ b/trunk/src/core/srs_core_codec.hpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_config.cpp b/trunk/src/core/srs_core_config.cpp index ed83a46d6..ea44b8a25 100644 --- a/trunk/src/core/srs_core_config.cpp +++ b/trunk/src/core/srs_core_config.cpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in @@ -736,7 +736,7 @@ int SrsConfig::parse_argv(int& i, char** argv) void SrsConfig::print_help(char** argv) { printf(RTMP_SIG_SRS_NAME" "RTMP_SIG_SRS_VERSION - " Copyright (c) 2013 winlin\n" + " Copyright (c) 2013-2014 winlin\n" "Contributors: "RTMP_SIG_SRS_CONTRIBUTOR"\n" "Build: "SRS_BUILD_DATE" Configuration: "SRS_CONFIGURE"\n" "Usage: %s [-h?vV] [-c ]\n" diff --git a/trunk/src/core/srs_core_config.hpp b/trunk/src/core/srs_core_config.hpp index 3c7dd3b65..3cd693275 100644 --- a/trunk/src/core/srs_core_config.hpp +++ b/trunk/src/core/srs_core_config.hpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_conn.cpp b/trunk/src/core/srs_core_conn.cpp index e148db79a..02c758891 100644 --- a/trunk/src/core/srs_core_conn.cpp +++ b/trunk/src/core/srs_core_conn.cpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_conn.hpp b/trunk/src/core/srs_core_conn.hpp index 4689eb7d7..8ac78f8d3 100644 --- a/trunk/src/core/srs_core_conn.hpp +++ b/trunk/src/core/srs_core_conn.hpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_encoder.cpp b/trunk/src/core/srs_core_encoder.cpp index f2b041ac6..c56509796 100644 --- a/trunk/src/core/srs_core_encoder.cpp +++ b/trunk/src/core/srs_core_encoder.cpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_encoder.hpp b/trunk/src/core/srs_core_encoder.hpp index 4ecfd59c9..eb34572d1 100644 --- a/trunk/src/core/srs_core_encoder.hpp +++ b/trunk/src/core/srs_core_encoder.hpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_error.cpp b/trunk/src/core/srs_core_error.cpp index b9ea1d673..3163d0036 100644 --- a/trunk/src/core/srs_core_error.cpp +++ b/trunk/src/core/srs_core_error.cpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_error.hpp b/trunk/src/core/srs_core_error.hpp index 15af170a3..587491579 100644 --- a/trunk/src/core/srs_core_error.hpp +++ b/trunk/src/core/srs_core_error.hpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_forward.cpp b/trunk/src/core/srs_core_forward.cpp index 73e398343..97e8b75e8 100644 --- a/trunk/src/core/srs_core_forward.cpp +++ b/trunk/src/core/srs_core_forward.cpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_forward.hpp b/trunk/src/core/srs_core_forward.hpp index a2e832cb5..34bfbfb95 100644 --- a/trunk/src/core/srs_core_forward.hpp +++ b/trunk/src/core/srs_core_forward.hpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_handshake.cpp b/trunk/src/core/srs_core_handshake.cpp index 44c3e4a01..32239ee03 100644 --- a/trunk/src/core/srs_core_handshake.cpp +++ b/trunk/src/core/srs_core_handshake.cpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_handshake.hpp b/trunk/src/core/srs_core_handshake.hpp index e34135a7c..06686fc1e 100644 --- a/trunk/src/core/srs_core_handshake.hpp +++ b/trunk/src/core/srs_core_handshake.hpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_hls.cpp b/trunk/src/core/srs_core_hls.cpp index b7182cad5..8d0b14d81 100644 --- a/trunk/src/core/srs_core_hls.cpp +++ b/trunk/src/core/srs_core_hls.cpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_hls.hpp b/trunk/src/core/srs_core_hls.hpp index 9474909f3..746d6e3c0 100644 --- a/trunk/src/core/srs_core_hls.hpp +++ b/trunk/src/core/srs_core_hls.hpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_http.cpp b/trunk/src/core/srs_core_http.cpp index 867f3635f..9f350e493 100644 --- a/trunk/src/core/srs_core_http.cpp +++ b/trunk/src/core/srs_core_http.cpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_http.hpp b/trunk/src/core/srs_core_http.hpp index 0989717bb..67a05088d 100644 --- a/trunk/src/core/srs_core_http.hpp +++ b/trunk/src/core/srs_core_http.hpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_log.cpp b/trunk/src/core/srs_core_log.cpp index 27aa5ae0a..64f0c9294 100644 --- a/trunk/src/core/srs_core_log.cpp +++ b/trunk/src/core/srs_core_log.cpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_log.hpp b/trunk/src/core/srs_core_log.hpp index e97c51bdc..ffeca8ec2 100644 --- a/trunk/src/core/srs_core_log.hpp +++ b/trunk/src/core/srs_core_log.hpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_pithy_print.cpp b/trunk/src/core/srs_core_pithy_print.cpp index abb568596..e375c4006 100644 --- a/trunk/src/core/srs_core_pithy_print.cpp +++ b/trunk/src/core/srs_core_pithy_print.cpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_pithy_print.hpp b/trunk/src/core/srs_core_pithy_print.hpp index 9a86c0e8d..c402f5a3c 100644 --- a/trunk/src/core/srs_core_pithy_print.hpp +++ b/trunk/src/core/srs_core_pithy_print.hpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_protocol.cpp b/trunk/src/core/srs_core_protocol.cpp old mode 100755 new mode 100644 index 1160711bd..3d3d6138b --- a/trunk/src/core/srs_core_protocol.cpp +++ b/trunk/src/core/srs_core_protocol.cpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_protocol.hpp b/trunk/src/core/srs_core_protocol.hpp index 490d7cb94..089251709 100644 --- a/trunk/src/core/srs_core_protocol.hpp +++ b/trunk/src/core/srs_core_protocol.hpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_refer.cpp b/trunk/src/core/srs_core_refer.cpp index fbe70b559..c7ff7ac1a 100644 --- a/trunk/src/core/srs_core_refer.cpp +++ b/trunk/src/core/srs_core_refer.cpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_refer.hpp b/trunk/src/core/srs_core_refer.hpp index 17067e297..9b4e27b9c 100644 --- a/trunk/src/core/srs_core_refer.hpp +++ b/trunk/src/core/srs_core_refer.hpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_reload.cpp b/trunk/src/core/srs_core_reload.cpp index f6feee15e..d4cd85f58 100644 --- a/trunk/src/core/srs_core_reload.cpp +++ b/trunk/src/core/srs_core_reload.cpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_reload.hpp b/trunk/src/core/srs_core_reload.hpp index 3d8f3e8b9..78f6effa6 100644 --- a/trunk/src/core/srs_core_reload.hpp +++ b/trunk/src/core/srs_core_reload.hpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_rtmp.cpp b/trunk/src/core/srs_core_rtmp.cpp index 4fce14050..331b41b09 100644 --- a/trunk/src/core/srs_core_rtmp.cpp +++ b/trunk/src/core/srs_core_rtmp.cpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_rtmp.hpp b/trunk/src/core/srs_core_rtmp.hpp old mode 100755 new mode 100644 index 6726a36e4..54b0948af --- a/trunk/src/core/srs_core_rtmp.hpp +++ b/trunk/src/core/srs_core_rtmp.hpp @@ -1,231 +1,231 @@ -/* -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_RTMP_HPP -#define SRS_CORE_RTMP_HPP - -/* -#include -*/ - -#include - -#include - -class SrsProtocol; -class ISrsMessage; -class SrsCommonMessage; -class SrsCreateStreamPacket; -class SrsFMLEStartPacket; -class SrsPublishPacket; -class SrsSharedPtrMessage; -class SrsOnMetaDataPacket; - -/** -* the original request from client. -*/ -struct SrsRequest -{ - /** - * tcUrl: rtmp://request_vhost:port/app/stream - * support pass vhost in query string, such as: - * rtmp://ip:port/app?vhost=request_vhost/stream - * rtmp://ip:port/app...vhost...request_vhost/stream - */ - std::string tcUrl; - std::string pageUrl; - std::string swfUrl; - double objectEncoding; - - std::string schema; - std::string vhost; - std::string port; - std::string app; - std::string stream; - - SrsRequest(); - virtual ~SrsRequest(); - - /** - * deep copy the request, for source to use it to support reload, - * for when initialize the source, the request is valid, - * when reload it, the request maybe invalid, so need to copy it. - */ - virtual SrsRequest* copy(); - - /** - * disconvery vhost/app from tcUrl. - */ - virtual int discovery_app(); - virtual std::string get_stream_url(); - virtual void strip(); -private: - std::string& trim(std::string& str, std::string chs); -}; - -/** -* the response to client. -*/ -struct SrsResponse -{ - int stream_id; - - SrsResponse(); - virtual ~SrsResponse(); -}; - -/** -* the rtmp client type. -*/ -enum SrsClientType -{ - SrsClientUnknown, - SrsClientPlay, - SrsClientFMLEPublish, - SrsClientFlashPublish, -}; - -/** -* implements the client role protocol. -*/ -class SrsRtmpClient -{ -protected: - SrsProtocol* protocol; - st_netfd_t stfd; -public: - SrsRtmpClient(st_netfd_t _stfd); - virtual ~SrsRtmpClient(); -public: - virtual void set_recv_timeout(int64_t timeout_us); - virtual void set_send_timeout(int64_t timeout_us); - virtual int64_t get_recv_bytes(); - virtual int64_t get_send_bytes(); - virtual int get_recv_kbps(); - virtual int get_send_kbps(); - virtual int recv_message(SrsCommonMessage** pmsg); - virtual int send_message(ISrsMessage* msg); -public: - virtual int handshake(); - virtual int connect_app(std::string app, std::string tc_url); - virtual int create_stream(int& stream_id); - virtual int play(std::string stream, int stream_id); - virtual int publish(std::string stream, int stream_id); -}; - -/** -* the rtmp provices rtmp-command-protocol services, -* a high level protocol, media stream oriented services, -* such as connect to vhost/app, play stream, get audio/video data. -*/ -class SrsRtmp -{ -private: - SrsProtocol* protocol; - st_netfd_t stfd; -public: - SrsRtmp(st_netfd_t client_stfd); - virtual ~SrsRtmp(); -public: - virtual SrsProtocol* get_protocol(); - virtual void set_recv_timeout(int64_t timeout_us); - virtual int64_t get_recv_timeout(); - virtual void set_send_timeout(int64_t timeout_us); - virtual int64_t get_send_timeout(); - virtual int64_t get_recv_bytes(); - virtual int64_t get_send_bytes(); - virtual int get_recv_kbps(); - virtual int get_send_kbps(); - virtual int recv_message(SrsCommonMessage** pmsg); - virtual int send_message(ISrsMessage* msg); -public: - virtual int handshake(); - virtual int connect_app(SrsRequest* req); - virtual int set_window_ack_size(int ack_size); - /** - * @type: The sender can mark this message hard (0), soft (1), or dynamic (2) - * using the Limit type field. - */ - virtual int set_peer_bandwidth(int bandwidth, int type); - /** - * @param server_ip the ip of server. - */ - virtual int response_connect_app(SrsRequest* req, const char* server_ip = NULL); - virtual void response_connect_reject(SrsRequest* req, const char* desc); - virtual int on_bw_done(); - /** - * recv some message to identify the client. - * @stream_id, client will createStream to play or publish by flash, - * the stream_id used to response the createStream request. - * @type, output the client type. - */ - virtual int identify_client(int stream_id, SrsClientType& type, std::string& stream_name); - /** - * set the chunk size when client type identified. - */ - virtual int set_chunk_size(int chunk_size); - /** - * when client type is play, response with packets: - * StreamBegin, - * onStatus(NetStream.Play.Reset), onStatus(NetStream.Play.Start)., - * |RtmpSampleAccess(false, false), - * onStatus(NetStream.Data.Start). - */ - virtual int start_play(int stream_id); - /** - * when client(type is play) send pause message, - * if is_pause, response the following packets: - * onStatus(NetStream.Pause.Notify) - * StreamEOF - * if not is_pause, response the following packets: - * onStatus(NetStream.Unpause.Notify) - * StreamBegin - */ - virtual int on_play_client_pause(int stream_id, bool is_pause); - /** - * when client type is publish, response with packets: - * releaseStream response - * FCPublish - * FCPublish response - * createStream response - * onFCPublish(NetStream.Publish.Start) - * onStatus(NetStream.Publish.Start) - */ - virtual int start_fmle_publish(int stream_id); - /** - * process the FMLE unpublish event. - * @unpublish_tid the unpublish request transaction id. - */ - virtual int fmle_unpublish(int stream_id, double unpublish_tid); - /** - * when client type is publish, response with packets: - * onStatus(NetStream.Publish.Start) - */ - virtual int start_flash_publish(int stream_id); -private: - virtual int identify_create_stream_client(SrsCreateStreamPacket* req, int stream_id, SrsClientType& type, std::string& stream_name); - virtual int identify_fmle_publish_client(SrsFMLEStartPacket* req, SrsClientType& type, std::string& stream_name); - virtual int identify_flash_publish_client(SrsPublishPacket* req, SrsClientType& type, std::string& stream_name); -}; - +/* +The MIT License (MIT) + +Copyright (c) 2013-2014 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef SRS_CORE_RTMP_HPP +#define SRS_CORE_RTMP_HPP + +/* +#include +*/ + +#include + +#include + +class SrsProtocol; +class ISrsMessage; +class SrsCommonMessage; +class SrsCreateStreamPacket; +class SrsFMLEStartPacket; +class SrsPublishPacket; +class SrsSharedPtrMessage; +class SrsOnMetaDataPacket; + +/** +* the original request from client. +*/ +struct SrsRequest +{ + /** + * tcUrl: rtmp://request_vhost:port/app/stream + * support pass vhost in query string, such as: + * rtmp://ip:port/app?vhost=request_vhost/stream + * rtmp://ip:port/app...vhost...request_vhost/stream + */ + std::string tcUrl; + std::string pageUrl; + std::string swfUrl; + double objectEncoding; + + std::string schema; + std::string vhost; + std::string port; + std::string app; + std::string stream; + + SrsRequest(); + virtual ~SrsRequest(); + + /** + * deep copy the request, for source to use it to support reload, + * for when initialize the source, the request is valid, + * when reload it, the request maybe invalid, so need to copy it. + */ + virtual SrsRequest* copy(); + + /** + * disconvery vhost/app from tcUrl. + */ + virtual int discovery_app(); + virtual std::string get_stream_url(); + virtual void strip(); +private: + std::string& trim(std::string& str, std::string chs); +}; + +/** +* the response to client. +*/ +struct SrsResponse +{ + int stream_id; + + SrsResponse(); + virtual ~SrsResponse(); +}; + +/** +* the rtmp client type. +*/ +enum SrsClientType +{ + SrsClientUnknown, + SrsClientPlay, + SrsClientFMLEPublish, + SrsClientFlashPublish, +}; + +/** +* implements the client role protocol. +*/ +class SrsRtmpClient +{ +protected: + SrsProtocol* protocol; + st_netfd_t stfd; +public: + SrsRtmpClient(st_netfd_t _stfd); + virtual ~SrsRtmpClient(); +public: + virtual void set_recv_timeout(int64_t timeout_us); + virtual void set_send_timeout(int64_t timeout_us); + virtual int64_t get_recv_bytes(); + virtual int64_t get_send_bytes(); + virtual int get_recv_kbps(); + virtual int get_send_kbps(); + virtual int recv_message(SrsCommonMessage** pmsg); + virtual int send_message(ISrsMessage* msg); +public: + virtual int handshake(); + virtual int connect_app(std::string app, std::string tc_url); + virtual int create_stream(int& stream_id); + virtual int play(std::string stream, int stream_id); + virtual int publish(std::string stream, int stream_id); +}; + +/** +* the rtmp provices rtmp-command-protocol services, +* a high level protocol, media stream oriented services, +* such as connect to vhost/app, play stream, get audio/video data. +*/ +class SrsRtmp +{ +private: + SrsProtocol* protocol; + st_netfd_t stfd; +public: + SrsRtmp(st_netfd_t client_stfd); + virtual ~SrsRtmp(); +public: + virtual SrsProtocol* get_protocol(); + virtual void set_recv_timeout(int64_t timeout_us); + virtual int64_t get_recv_timeout(); + virtual void set_send_timeout(int64_t timeout_us); + virtual int64_t get_send_timeout(); + virtual int64_t get_recv_bytes(); + virtual int64_t get_send_bytes(); + virtual int get_recv_kbps(); + virtual int get_send_kbps(); + virtual int recv_message(SrsCommonMessage** pmsg); + virtual int send_message(ISrsMessage* msg); +public: + virtual int handshake(); + virtual int connect_app(SrsRequest* req); + virtual int set_window_ack_size(int ack_size); + /** + * @type: The sender can mark this message hard (0), soft (1), or dynamic (2) + * using the Limit type field. + */ + virtual int set_peer_bandwidth(int bandwidth, int type); + /** + * @param server_ip the ip of server. + */ + virtual int response_connect_app(SrsRequest* req, const char* server_ip = NULL); + virtual void response_connect_reject(SrsRequest* req, const char* desc); + virtual int on_bw_done(); + /** + * recv some message to identify the client. + * @stream_id, client will createStream to play or publish by flash, + * the stream_id used to response the createStream request. + * @type, output the client type. + */ + virtual int identify_client(int stream_id, SrsClientType& type, std::string& stream_name); + /** + * set the chunk size when client type identified. + */ + virtual int set_chunk_size(int chunk_size); + /** + * when client type is play, response with packets: + * StreamBegin, + * onStatus(NetStream.Play.Reset), onStatus(NetStream.Play.Start)., + * |RtmpSampleAccess(false, false), + * onStatus(NetStream.Data.Start). + */ + virtual int start_play(int stream_id); + /** + * when client(type is play) send pause message, + * if is_pause, response the following packets: + * onStatus(NetStream.Pause.Notify) + * StreamEOF + * if not is_pause, response the following packets: + * onStatus(NetStream.Unpause.Notify) + * StreamBegin + */ + virtual int on_play_client_pause(int stream_id, bool is_pause); + /** + * when client type is publish, response with packets: + * releaseStream response + * FCPublish + * FCPublish response + * createStream response + * onFCPublish(NetStream.Publish.Start) + * onStatus(NetStream.Publish.Start) + */ + virtual int start_fmle_publish(int stream_id); + /** + * process the FMLE unpublish event. + * @unpublish_tid the unpublish request transaction id. + */ + virtual int fmle_unpublish(int stream_id, double unpublish_tid); + /** + * when client type is publish, response with packets: + * onStatus(NetStream.Publish.Start) + */ + virtual int start_flash_publish(int stream_id); +private: + virtual int identify_create_stream_client(SrsCreateStreamPacket* req, int stream_id, SrsClientType& type, std::string& stream_name); + virtual int identify_fmle_publish_client(SrsFMLEStartPacket* req, SrsClientType& type, std::string& stream_name); + virtual int identify_flash_publish_client(SrsPublishPacket* req, SrsClientType& type, std::string& stream_name); +}; + #endif \ No newline at end of file diff --git a/trunk/src/core/srs_core_server.cpp b/trunk/src/core/srs_core_server.cpp index 4833673f4..69c9687c0 100644 --- a/trunk/src/core/srs_core_server.cpp +++ b/trunk/src/core/srs_core_server.cpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_server.hpp b/trunk/src/core/srs_core_server.hpp index 79664657d..13599b465 100644 --- a/trunk/src/core/srs_core_server.hpp +++ b/trunk/src/core/srs_core_server.hpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_socket.cpp b/trunk/src/core/srs_core_socket.cpp index 365b3ed77..7628c3c70 100644 --- a/trunk/src/core/srs_core_socket.cpp +++ b/trunk/src/core/srs_core_socket.cpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_socket.hpp b/trunk/src/core/srs_core_socket.hpp index f355dbfb1..8b25c39dd 100644 --- a/trunk/src/core/srs_core_socket.hpp +++ b/trunk/src/core/srs_core_socket.hpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_source.cpp b/trunk/src/core/srs_core_source.cpp index 5cca79543..dc5bc1240 100644 --- a/trunk/src/core/srs_core_source.cpp +++ b/trunk/src/core/srs_core_source.cpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_source.hpp b/trunk/src/core/srs_core_source.hpp index 405c51e53..3f8d62796 100644 --- a/trunk/src/core/srs_core_source.hpp +++ b/trunk/src/core/srs_core_source.hpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_stream.cpp b/trunk/src/core/srs_core_stream.cpp index 6530ee40d..a22581c07 100644 --- a/trunk/src/core/srs_core_stream.cpp +++ b/trunk/src/core/srs_core_stream.cpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_stream.hpp b/trunk/src/core/srs_core_stream.hpp index 7bbc4d5a1..df7cc27f9 100644 --- a/trunk/src/core/srs_core_stream.hpp +++ b/trunk/src/core/srs_core_stream.hpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_thread.cpp b/trunk/src/core/srs_core_thread.cpp index fbbe4b300..b5670cccd 100644 --- a/trunk/src/core/srs_core_thread.cpp +++ b/trunk/src/core/srs_core_thread.cpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_thread.hpp b/trunk/src/core/srs_core_thread.hpp index fb3ef8136..8e649f760 100644 --- a/trunk/src/core/srs_core_thread.hpp +++ b/trunk/src/core/srs_core_thread.hpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/main/srs_main_bandcheck.cpp b/trunk/src/main/srs_main_bandcheck.cpp old mode 100755 new mode 100644 index f0342753d..107722d1c --- a/trunk/src/main/srs_main_bandcheck.cpp +++ b/trunk/src/main/srs_main_bandcheck.cpp @@ -1,832 +1,832 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 wenjiegit - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -// server play control -#define SRS_BW_CHECK_START_PLAY "onSrsBandCheckStartPlayBytes" -#define SRS_BW_CHECK_STARTING_PLAY "onSrsBandCheckStartingPlayBytes" -#define SRS_BW_CHECK_STOP_PLAY "onSrsBandCheckStopPlayBytes" -#define SRS_BW_CHECK_STOPPED_PLAY "onSrsBandCheckStoppedPlayBytes" - -// server publish control -#define SRS_BW_CHECK_START_PUBLISH "onSrsBandCheckStartPublishBytes" -#define SRS_BW_CHECK_STARTING_PUBLISH "onSrsBandCheckStartingPublishBytes" -#define SRS_BW_CHECK_STOP_PUBLISH "onSrsBandCheckStopPublishBytes" -#define SRS_BW_CHECK_STOPPED_PUBLISH "onSrsBandCheckStoppedPublishBytes" - -// EOF control. -#define SRS_BW_CHECK_FINISHED "onSrsBandCheckFinished" -#define SRS_BW_CHECK_FLASH_FINAL "finalClientPacket" - -// client only -#define SRS_BW_CHECK_PLAYING "onSrsBandCheckPlaying" -#define SRS_BW_CHECK_PUBLISHING "onSrsBandCheckPublishing" - -/** -* @brief class of Linux version band check client -* check play and publish speed. -*/ -class SrsBandCheckClient : public SrsRtmpClient -{ -public: - SrsBandCheckClient(st_netfd_t _stfd); - ~SrsBandCheckClient(); - -public: - /** - * @brief test play - * - */ - int check_play(); - /** - * @brief test publish - * - */ - int check_publish(); - -private: - /** - * @brief just return success. - */ - int create_stream(int& stream_id); - /** - * @brief just return success. - */ - int play(std::string stream, int stream_id); - /** - * @brief just return success. - */ - int publish(std::string stream, int stream_id); - -private: - int expect_start_play(); - int send_starting_play(); - int expect_stop_play(); - int send_stopped_play(); - int expect_start_pub(); - int send_starting_pub(); - int send_pub_data(); - int expect_stop_pub(); - /** - * @brief expect result. - * because the core module has no method to decode this packet - * so we must get the internal data and decode it here. - */ - int expect_finished(); - int send_stopped_pub(); - /** - * @brief notify server the check procedure is over. - */ - int send_final(); -}; - -/** -* @brief class of band check -* used to check band width with a client @param bandCheck_Client -*/ -class SrsBandCheck -{ -public: - SrsBandCheck(); - ~SrsBandCheck(); - -public: - /** - * @brief band check method - * - * connect to server------>rtmp handshake------>rtmp connect------>play------>publish - * @retval ERROR_SUCCESS when success. - */ - int check(const std::string& app, const std::string& tcUrl); - - /** - * @brief set the address and port of test server - * - * @param server server address, domain or ip - * @param server listened port ,default is 1935 - */ - void set_server(const std::string& server, int port = 1935); - -private: - int connect_server(); - -private: - SrsBandCheckClient* bandCheck_Client; - std::string server_address; - int server_port; -}; - -/** -* @brief init st lib -*/ -static int init_st(); -static void print_help(char** argv); -static void print_version(); - -/** -* @brief get user option -* @internal ip Mandatory arguments -* @internal key Mandatory arguments -* @internal port default 1935 -* @internal vhost default bandcheck.srs.com -*/ -static int get_opt(int argc ,char* argv[]); - -/** -* global var. -*/ -static struct option long_options[] = -{ - {"ip", required_argument, 0, 'i'}, - {"port", optional_argument, 0, 'p'}, - {"key", required_argument, 0, 'k'}, - {"vhost", optional_argument, 0, 'v'}, - {"help", no_argument, 0, 'h'}, - {"version", no_argument, 0, 'V'}, -}; - -static const char* short_options = "i:p::k:v::hV"; - -static std::string g_ip; -static int g_port = 1935; -static std::string g_key; -static std::string g_vhost = "bandcheck.srs.com"; - -#define BUILD_VERSION "srs band check 0.1" - -int main(int argc ,char* argv[]) -{ - int ret = ERROR_SUCCESS; - - if (argc <= 1) { - print_help(argv); - exit(1); - } - - if ((ret = get_opt(argc, argv)) != ERROR_SUCCESS) { - return -1; - } - - // check param - if (g_ip.empty()) { - printf("ip address should not be empty.\n"); - return -1; - } - - if (g_key.empty()) { - printf("test key should not be empty.\n"); - return -1; - } - - if ((ret = init_st()) != ERROR_SUCCESS) { - srs_error("band check init failed. ret=%d", ret); - return ret; - } - - std::string app = "app?key=" + g_key + "&vhost=" + g_vhost; - - char tcUrl_buffer[1024] = {0}; - sprintf(tcUrl_buffer, "rtmp://%s:%d/%s", g_ip.c_str(), g_port, app.c_str()); - std::string tcUrl = tcUrl_buffer; - - SrsBandCheck band_check; - band_check.set_server(g_ip, g_port); - if ((ret = band_check.check(app, tcUrl)) != ERROR_SUCCESS) { - srs_error("band check failed. address=%s ret=%d", "xx.com", ret); - return -1; - } - - return 0; -} - -SrsBandCheckClient::SrsBandCheckClient(st_netfd_t _stfd) - : SrsRtmpClient(_stfd) -{ -} - -SrsBandCheckClient::~SrsBandCheckClient() -{ -} - -int SrsBandCheckClient::check_play() -{ - int ret = ERROR_SUCCESS; - - if ((ret = expect_start_play()) != ERROR_SUCCESS) { - srs_error("expect_start_play failed. ret=%d", ret); - return ret; - } - - if ((ret = send_starting_play()) != ERROR_SUCCESS) { - srs_error("send starting play failed. ret=%d", ret); - return ret; - } - - if ((ret = expect_stop_play()) != ERROR_SUCCESS) { - srs_error("expect stop play failed. ret=%d", ret); - return ret; - } - - if ((ret = send_stopped_play()) != ERROR_SUCCESS) { - srs_error("send stopped play failed. ret=%d", ret); - return ret; - } - - return ret; -} - -int SrsBandCheckClient::check_publish() -{ - int ret = ERROR_SUCCESS; - - if ((ret = expect_start_pub()) != ERROR_SUCCESS) { - srs_error("expect start pub failed. ret=%d", ret); - return ret; - } - - if ((ret = send_starting_pub())!= ERROR_SUCCESS) { - srs_error("send starting pub failed. ret=%d", ret); - return ret; - } - - if ((ret = send_pub_data()) != ERROR_SUCCESS) { - srs_error("publish data failed. ret=%d", ret); - return ret; - } - - if ((ret = send_stopped_pub()) != ERROR_SUCCESS) { - srs_error("send stopped pub failed. ret=%d", ret); - return ret; - } - - if ((ret = expect_finished()) != ERROR_SUCCESS) { - srs_error("expect finished msg failed. ret=%d", ret); - return ret; - } - - if ((ret = send_final()) != ERROR_SUCCESS) { - srs_error("send final msg failed. ret=%d", ret); - return ret; - } - - return ret; -} - -int SrsBandCheckClient::create_stream(int &stream_id) -{ - return ERROR_SUCCESS; -} - -int SrsBandCheckClient::play(std::string stream, int stream_id) -{ - return ERROR_SUCCESS; -} - -int SrsBandCheckClient::publish(std::string stream, int stream_id) -{ - return ERROR_SUCCESS; -} - -int SrsBandCheckClient::expect_start_play() -{ - int ret = ERROR_SUCCESS; - - // expect connect _result - SrsCommonMessage* msg = NULL; - SrsBandwidthPacket* pkt = NULL; - if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { - srs_error("expect bandcheck start play message failed. ret=%d", ret); - return ret; - } - SrsAutoFree(SrsCommonMessage, msg, false); - srs_info("get bandcheck start play message"); - - if (pkt->command_name != SRS_BW_CHECK_START_PLAY) { - srs_error("pkt error. expect=%s, actual=%s", SRS_BW_CHECK_START_PLAY, pkt->command_name.c_str()); - return -1; - } - - return ret; -} - -int SrsBandCheckClient::send_starting_play() -{ - int ret = ERROR_SUCCESS; - - SrsCommonMessage* msg = new SrsCommonMessage; - SrsBandwidthPacket* pkt = new SrsBandwidthPacket; - pkt->command_name = SRS_BW_CHECK_STARTING_PLAY; - msg->set_packet(pkt, 0); - - if ((ret = send_message(msg)) != ERROR_SUCCESS) { - srs_error("send starting play msg failed. ret=%d", ret); - return ret; - } - - return ret; -} - -int SrsBandCheckClient::expect_stop_play() -{ - int ret = ERROR_SUCCESS; - - while (true) { - SrsCommonMessage* msg = NULL; - SrsBandwidthPacket* pkt = NULL; - if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { - srs_error("expect stop play message failed. ret=%d", ret); - return ret; - } - SrsAutoFree(SrsCommonMessage, msg, false); - srs_info("get bandcheck stop play message"); - - if (pkt->command_name == SRS_BW_CHECK_STOP_PLAY) { - break; - } - } - - return ret; -} - -int SrsBandCheckClient::send_stopped_play() -{ - int ret = ERROR_SUCCESS; - - SrsCommonMessage* msg = new SrsCommonMessage; - SrsBandwidthPacket* pkt = new SrsBandwidthPacket; - pkt->command_name = SRS_BW_CHECK_STOPPED_PLAY; - msg->set_packet(pkt, 0); - - if ((ret = send_message(msg)) != ERROR_SUCCESS) { - srs_error("send stopped play msg failed. ret=%d", ret); - return ret; - } - - return ret; -} - -int SrsBandCheckClient::expect_start_pub() -{ - int ret = ERROR_SUCCESS; - - while (true) { - SrsCommonMessage* msg = NULL; - SrsBandwidthPacket* pkt = NULL; - if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { - srs_error("expect start pub message failed. ret=%d", ret); - return ret; - } - SrsAutoFree(SrsCommonMessage, msg, false); - srs_info("get bandcheck start pub message"); - - if (pkt->command_name == SRS_BW_CHECK_START_PUBLISH) { - break; - } - } - - return ret; -} - -int SrsBandCheckClient::send_starting_pub() -{ - int ret = ERROR_SUCCESS; - - SrsCommonMessage* msg = new SrsCommonMessage; - SrsBandwidthPacket* pkt = new SrsBandwidthPacket; - pkt->command_name = SRS_BW_CHECK_STARTING_PUBLISH; - msg->set_packet(pkt, 0); - - if ((ret = send_message(msg)) != ERROR_SUCCESS) { - srs_error("send starting play msg failed. ret=%d", ret); - return ret; - } - srs_info("send starting play msg success."); - - return ret; -} - -int SrsBandCheckClient::send_pub_data() -{ - int ret = ERROR_SUCCESS; - - int data_count = 100; - while (true) { - SrsCommonMessage* msg = new SrsCommonMessage; - SrsBandwidthPacket* pkt = new SrsBandwidthPacket; - pkt->command_name = SRS_BW_CHECK_PUBLISHING; - msg->set_packet(pkt, 0); - - for (int i = 0; i < data_count; ++i) { - std::stringstream seq; - seq << i; - std::string play_data = "SrS band check data from client's publishing......"; - pkt->data->set(seq.str(), new SrsAmf0String(play_data.c_str())); - } - data_count += 100; - - if ((ret = send_message(msg)) != ERROR_SUCCESS) { - srs_error("send publish message failed.ret=%d", ret); - return ret; - } - - if ((ret = expect_stop_pub()) == ERROR_SUCCESS) { - break; - } - } - - return ret; -} - -int SrsBandCheckClient::expect_stop_pub() -{ - int ret = ERROR_SUCCESS; - - while (true) { - if ((ret = st_netfd_poll(stfd, POLLIN, 1000)) == ERROR_SUCCESS) { - SrsCommonMessage* msg = 0; - if ((ret = recv_message(&msg)) != ERROR_SUCCESS) - { - srs_error("recv message failed while expect stop pub. ret=%d", ret); - return ret; - } - - if ((ret = msg->decode_packet(protocol)) != ERROR_SUCCESS) { - srs_error("decode packet error while expect stop pub. ret=%d", ret); - return ret; - } - - SrsBandwidthPacket* pkt = dynamic_cast(msg->get_packet()); - if (pkt && pkt->command_name == SRS_BW_CHECK_STOP_PUBLISH) { - - return ret; - } - } else { - break; - } - } - - return ret; -} - -int SrsBandCheckClient::expect_finished() -{ - int ret = ERROR_SUCCESS; - - while (true) { - SrsCommonMessage* msg = NULL; - SrsBandwidthPacket* pkt = NULL; - if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { - srs_error("expect finished message failed. ret=%d", ret); - return ret; - } - SrsAutoFree(SrsCommonMessage, msg, false); - srs_info("get bandcheck finished message"); - - if (pkt->command_name == SRS_BW_CHECK_FINISHED) { - SrsStream *stream = new SrsStream; - SrsAutoFree(SrsStream, stream, false); - - if ((ret = stream->initialize((char*)msg->payload, msg->size)) != ERROR_SUCCESS) { - srs_error("initialize stream error. ret=%d", ret); - return ret; - } - - std::string command_name; - if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) { - srs_error("amfo read string error. ret=%d", ret); - return ret; - } - - double action_id; - if ((ret = srs_amf0_read_number(stream, action_id)) != ERROR_SUCCESS) { - srs_error("amfo read number error. ret=%d", ret); - return ret; - } - - if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) { - srs_error("amfo read number error. ret=%d", ret); - return ret; - } - - SrsAmf0Object* object; - if ((ret = srs_amf0_read_object(stream, object)) != ERROR_SUCCESS) { - srs_error("amfo read object error. ret=%d", ret); - return ret; - } - - int64_t start_time = 0; - int64_t end_time = 0; - - SrsAmf0Any* start_time_any = object->get_property("start_time"); - if (start_time_any && start_time_any->is_number()) { - SrsAmf0Number* start_time_number = dynamic_cast (start_time_any); - if (start_time_number) { - start_time = start_time_number->value; - } - } - - SrsAmf0Any* end_time_any = object->get_property("end_time"); - if (end_time_any && end_time_any->is_number()) { - SrsAmf0Number* end_time_number = dynamic_cast (end_time_any); - if (end_time_number) { - end_time = end_time_number->value; - } - } - - int play_kbps = 0; - int pub_kbps = 0; - SrsAmf0Any* play_kbp_any = object->get_property("play_kbps"); - if (play_kbp_any && play_kbp_any->is_number()) { - SrsAmf0Number* play_kbps_number = dynamic_cast (play_kbp_any); - if (play_kbps_number) { - play_kbps = play_kbps_number->value; - } - } - - SrsAmf0Any* pub_kbp_any = object->get_property("publish_kbps"); - if (pub_kbp_any && pub_kbp_any->is_number()) { - SrsAmf0Number* pub_kbps_number = dynamic_cast (pub_kbp_any); - if (pub_kbps_number) { - pub_kbps = pub_kbps_number->value; - } - } - - float time_elapsed; - if (end_time - start_time > 0) { - time_elapsed = (end_time - start_time) / 1000.00; - } - - srs_trace("result: play %d kbps, publish %d kbps, check time %.4f S\n" - , play_kbps, pub_kbps, time_elapsed); - - break; - } - } - - return ret; -} - -int SrsBandCheckClient::send_stopped_pub() -{ - int ret = ERROR_SUCCESS; - - SrsCommonMessage* msg = new SrsCommonMessage; - SrsBandwidthPacket* pkt = new SrsBandwidthPacket; - pkt->command_name = SRS_BW_CHECK_STOPPED_PUBLISH; - msg->set_packet(pkt, 0); - - if ((ret = send_message(msg)) != ERROR_SUCCESS) { - srs_error("send stopped pub msg failed. ret=%d", ret); - return ret; - } - srs_info("send stopped pub msg success."); - - return ret; -} - -int SrsBandCheckClient::send_final() -{ - int ret = ERROR_SUCCESS; - - SrsCommonMessage* msg = new SrsCommonMessage; - SrsBandwidthPacket* pkt = new SrsBandwidthPacket; - pkt->command_name = SRS_BW_CHECK_FLASH_FINAL; - msg->set_packet(pkt, 0); - - if ((ret = send_message(msg)) != ERROR_SUCCESS) { - srs_error("send final msg failed. ret=%d", ret); - return ret; - } - srs_info("send final msg success."); - - return ret; -} - -SrsBandCheck::SrsBandCheck() - : bandCheck_Client(0) -{ -} - -SrsBandCheck::~SrsBandCheck() -{ - if (bandCheck_Client) { - srs_freep(bandCheck_Client); - } -} - -int SrsBandCheck::check(const std::string &app, const std::string &tcUrl) -{ - int ret = ERROR_SUCCESS; - - if ((ret = connect_server()) != ERROR_SUCCESS) { - srs_error("connect to server failed. ret = %d", ret); - return ret; - } - - if ((ret = bandCheck_Client->handshake()) != ERROR_SUCCESS) { - srs_error("handshake failed. ret = %d", ret); - return ret; - } - - if ((ret = bandCheck_Client->connect_app(app, tcUrl)) != ERROR_SUCCESS) { - srs_error("handshake failed. ret = %d", ret); - return ret; - } - - if ((ret = bandCheck_Client->check_play()) != ERROR_SUCCESS) { - srs_error("band check play failed."); - return ret; - } - - if ((ret = bandCheck_Client->check_publish()) != ERROR_SUCCESS) { - srs_error("band check publish failed."); - return ret; - } - - return ret; -} - -void SrsBandCheck::set_server(const std::string &server, int port) -{ - server_address = server; - server_port = port; -} - -int SrsBandCheck::connect_server() -{ - int ret = ERROR_SUCCESS; - - 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; - } - - st_netfd_t 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; - } - - bandCheck_Client = new SrsBandCheckClient(stfd); - - // connect to server. - std::string ip = srs_dns_resolve(server_address); - 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(server_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(), server_port, ret); - return ret; - } - srs_trace("connect to server success. server=%s, ip=%s, port=%d", server_address.c_str(), ip.c_str(), server_port); - - return ret; -} - -int init_st() -{ - int ret = ERROR_SUCCESS; - - if (st_set_eventsys(ST_EVENTSYS_ALT) == -1) { - ret = ERROR_ST_SET_EPOLL; - srs_error("st_set_eventsys use linux epoll failed. ret=%d", ret); - return ret; - } - - if(st_init() != 0){ - ret = ERROR_ST_INITIALIZE; - srs_error("st_init failed. ret=%d", ret); - return ret; - } - - return ret; -} - -void print_help(char** argv) -{ - printf( - "Usage: %s [OPTION]...\n" - "test band width from client to rtmp server.\n" - "Mandatory arguments to long options are mandatory for short options too.\n" - " -i, --ip the ip or domain that to test\n" - " -p, --port the port that server listen \n" - " -k, --key the key used to test \n" - " -v, --vhost the vhost used to test \n" - " -V, --version output version information and exit \n" - " -h, --help display this help and exit \n" - "\n" - "For example:\n" - " %s -i 192.168.1.248 -p 1935 -v bandcheck.srs.com -k 35c9b402c12a7246868752e2878f7e0e" - "\n\n" - "Exit status:\n" - "0 if OK,\n" - "other if error occured, and the detail should be printed.\n" - "\n\n" - "srs home page: \n", - argv[0], argv[0]); -} - -void print_version() -{ - const char *version = "" - "srs_bandcheck "BUILD_VERSION"\n" - "Copyright (C) 2013 wenjiegit.\n" - "License MIT\n" - "This is free software: you are free to change and redistribute it.\n" - "There is NO WARRANTY, to the extent permitted by law.\n" - "\n" - "Written by wenjie.\n"; - - printf("%s", version); -} - -int get_opt(int argc, char *argv[]) -{ - int ret = ERROR_SUCCESS; - - int c; - while ((c = getopt_long (argc, argv, short_options, long_options, NULL)) != -1) { - switch (c) { - case 'i': - if (optarg) { - g_ip = optarg; - } - break; - case 'p': - if (optarg) { - g_port = atoi(optarg); - } - break; - case 'k': - if (optarg) { - g_key = optarg; - } - break; - case 'v': - if (optarg) { - g_vhost = optarg; - } - break; - case 'V': - print_version(); - exit(0); - break; - case 'h': - print_help(argv); - exit(0); - break; - default: - printf("see --help or -h\n"); - ret = -1; - } - } - - return ret; -} +/* +The MIT License (MIT) + +Copyright (c) 2013-2014 wenjiegit + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +// server play control +#define SRS_BW_CHECK_START_PLAY "onSrsBandCheckStartPlayBytes" +#define SRS_BW_CHECK_STARTING_PLAY "onSrsBandCheckStartingPlayBytes" +#define SRS_BW_CHECK_STOP_PLAY "onSrsBandCheckStopPlayBytes" +#define SRS_BW_CHECK_STOPPED_PLAY "onSrsBandCheckStoppedPlayBytes" + +// server publish control +#define SRS_BW_CHECK_START_PUBLISH "onSrsBandCheckStartPublishBytes" +#define SRS_BW_CHECK_STARTING_PUBLISH "onSrsBandCheckStartingPublishBytes" +#define SRS_BW_CHECK_STOP_PUBLISH "onSrsBandCheckStopPublishBytes" +#define SRS_BW_CHECK_STOPPED_PUBLISH "onSrsBandCheckStoppedPublishBytes" + +// EOF control. +#define SRS_BW_CHECK_FINISHED "onSrsBandCheckFinished" +#define SRS_BW_CHECK_FLASH_FINAL "finalClientPacket" + +// client only +#define SRS_BW_CHECK_PLAYING "onSrsBandCheckPlaying" +#define SRS_BW_CHECK_PUBLISHING "onSrsBandCheckPublishing" + +/** +* @brief class of Linux version band check client +* check play and publish speed. +*/ +class SrsBandCheckClient : public SrsRtmpClient +{ +public: + SrsBandCheckClient(st_netfd_t _stfd); + ~SrsBandCheckClient(); + +public: + /** + * @brief test play + * + */ + int check_play(); + /** + * @brief test publish + * + */ + int check_publish(); + +private: + /** + * @brief just return success. + */ + int create_stream(int& stream_id); + /** + * @brief just return success. + */ + int play(std::string stream, int stream_id); + /** + * @brief just return success. + */ + int publish(std::string stream, int stream_id); + +private: + int expect_start_play(); + int send_starting_play(); + int expect_stop_play(); + int send_stopped_play(); + int expect_start_pub(); + int send_starting_pub(); + int send_pub_data(); + int expect_stop_pub(); + /** + * @brief expect result. + * because the core module has no method to decode this packet + * so we must get the internal data and decode it here. + */ + int expect_finished(); + int send_stopped_pub(); + /** + * @brief notify server the check procedure is over. + */ + int send_final(); +}; + +/** +* @brief class of band check +* used to check band width with a client @param bandCheck_Client +*/ +class SrsBandCheck +{ +public: + SrsBandCheck(); + ~SrsBandCheck(); + +public: + /** + * @brief band check method + * + * connect to server------>rtmp handshake------>rtmp connect------>play------>publish + * @retval ERROR_SUCCESS when success. + */ + int check(const std::string& app, const std::string& tcUrl); + + /** + * @brief set the address and port of test server + * + * @param server server address, domain or ip + * @param server listened port ,default is 1935 + */ + void set_server(const std::string& server, int port = 1935); + +private: + int connect_server(); + +private: + SrsBandCheckClient* bandCheck_Client; + std::string server_address; + int server_port; +}; + +/** +* @brief init st lib +*/ +static int init_st(); +static void print_help(char** argv); +static void print_version(); + +/** +* @brief get user option +* @internal ip Mandatory arguments +* @internal key Mandatory arguments +* @internal port default 1935 +* @internal vhost default bandcheck.srs.com +*/ +static int get_opt(int argc ,char* argv[]); + +/** +* global var. +*/ +static struct option long_options[] = +{ + {"ip", required_argument, 0, 'i'}, + {"port", optional_argument, 0, 'p'}, + {"key", required_argument, 0, 'k'}, + {"vhost", optional_argument, 0, 'v'}, + {"help", no_argument, 0, 'h'}, + {"version", no_argument, 0, 'V'}, +}; + +static const char* short_options = "i:p::k:v::hV"; + +static std::string g_ip; +static int g_port = 1935; +static std::string g_key; +static std::string g_vhost = "bandcheck.srs.com"; + +#define BUILD_VERSION "srs band check 0.1" + +int main(int argc ,char* argv[]) +{ + int ret = ERROR_SUCCESS; + + if (argc <= 1) { + print_help(argv); + exit(1); + } + + if ((ret = get_opt(argc, argv)) != ERROR_SUCCESS) { + return -1; + } + + // check param + if (g_ip.empty()) { + printf("ip address should not be empty.\n"); + return -1; + } + + if (g_key.empty()) { + printf("test key should not be empty.\n"); + return -1; + } + + if ((ret = init_st()) != ERROR_SUCCESS) { + srs_error("band check init failed. ret=%d", ret); + return ret; + } + + std::string app = "app?key=" + g_key + "&vhost=" + g_vhost; + + char tcUrl_buffer[1024] = {0}; + sprintf(tcUrl_buffer, "rtmp://%s:%d/%s", g_ip.c_str(), g_port, app.c_str()); + std::string tcUrl = tcUrl_buffer; + + SrsBandCheck band_check; + band_check.set_server(g_ip, g_port); + if ((ret = band_check.check(app, tcUrl)) != ERROR_SUCCESS) { + srs_error("band check failed. address=%s ret=%d", "xx.com", ret); + return -1; + } + + return 0; +} + +SrsBandCheckClient::SrsBandCheckClient(st_netfd_t _stfd) + : SrsRtmpClient(_stfd) +{ +} + +SrsBandCheckClient::~SrsBandCheckClient() +{ +} + +int SrsBandCheckClient::check_play() +{ + int ret = ERROR_SUCCESS; + + if ((ret = expect_start_play()) != ERROR_SUCCESS) { + srs_error("expect_start_play failed. ret=%d", ret); + return ret; + } + + if ((ret = send_starting_play()) != ERROR_SUCCESS) { + srs_error("send starting play failed. ret=%d", ret); + return ret; + } + + if ((ret = expect_stop_play()) != ERROR_SUCCESS) { + srs_error("expect stop play failed. ret=%d", ret); + return ret; + } + + if ((ret = send_stopped_play()) != ERROR_SUCCESS) { + srs_error("send stopped play failed. ret=%d", ret); + return ret; + } + + return ret; +} + +int SrsBandCheckClient::check_publish() +{ + int ret = ERROR_SUCCESS; + + if ((ret = expect_start_pub()) != ERROR_SUCCESS) { + srs_error("expect start pub failed. ret=%d", ret); + return ret; + } + + if ((ret = send_starting_pub())!= ERROR_SUCCESS) { + srs_error("send starting pub failed. ret=%d", ret); + return ret; + } + + if ((ret = send_pub_data()) != ERROR_SUCCESS) { + srs_error("publish data failed. ret=%d", ret); + return ret; + } + + if ((ret = send_stopped_pub()) != ERROR_SUCCESS) { + srs_error("send stopped pub failed. ret=%d", ret); + return ret; + } + + if ((ret = expect_finished()) != ERROR_SUCCESS) { + srs_error("expect finished msg failed. ret=%d", ret); + return ret; + } + + if ((ret = send_final()) != ERROR_SUCCESS) { + srs_error("send final msg failed. ret=%d", ret); + return ret; + } + + return ret; +} + +int SrsBandCheckClient::create_stream(int &stream_id) +{ + return ERROR_SUCCESS; +} + +int SrsBandCheckClient::play(std::string stream, int stream_id) +{ + return ERROR_SUCCESS; +} + +int SrsBandCheckClient::publish(std::string stream, int stream_id) +{ + return ERROR_SUCCESS; +} + +int SrsBandCheckClient::expect_start_play() +{ + int ret = ERROR_SUCCESS; + + // expect connect _result + SrsCommonMessage* msg = NULL; + SrsBandwidthPacket* pkt = NULL; + if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { + srs_error("expect bandcheck start play message failed. ret=%d", ret); + return ret; + } + SrsAutoFree(SrsCommonMessage, msg, false); + srs_info("get bandcheck start play message"); + + if (pkt->command_name != SRS_BW_CHECK_START_PLAY) { + srs_error("pkt error. expect=%s, actual=%s", SRS_BW_CHECK_START_PLAY, pkt->command_name.c_str()); + return -1; + } + + return ret; +} + +int SrsBandCheckClient::send_starting_play() +{ + int ret = ERROR_SUCCESS; + + SrsCommonMessage* msg = new SrsCommonMessage; + SrsBandwidthPacket* pkt = new SrsBandwidthPacket; + pkt->command_name = SRS_BW_CHECK_STARTING_PLAY; + msg->set_packet(pkt, 0); + + if ((ret = send_message(msg)) != ERROR_SUCCESS) { + srs_error("send starting play msg failed. ret=%d", ret); + return ret; + } + + return ret; +} + +int SrsBandCheckClient::expect_stop_play() +{ + int ret = ERROR_SUCCESS; + + while (true) { + SrsCommonMessage* msg = NULL; + SrsBandwidthPacket* pkt = NULL; + if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { + srs_error("expect stop play message failed. ret=%d", ret); + return ret; + } + SrsAutoFree(SrsCommonMessage, msg, false); + srs_info("get bandcheck stop play message"); + + if (pkt->command_name == SRS_BW_CHECK_STOP_PLAY) { + break; + } + } + + return ret; +} + +int SrsBandCheckClient::send_stopped_play() +{ + int ret = ERROR_SUCCESS; + + SrsCommonMessage* msg = new SrsCommonMessage; + SrsBandwidthPacket* pkt = new SrsBandwidthPacket; + pkt->command_name = SRS_BW_CHECK_STOPPED_PLAY; + msg->set_packet(pkt, 0); + + if ((ret = send_message(msg)) != ERROR_SUCCESS) { + srs_error("send stopped play msg failed. ret=%d", ret); + return ret; + } + + return ret; +} + +int SrsBandCheckClient::expect_start_pub() +{ + int ret = ERROR_SUCCESS; + + while (true) { + SrsCommonMessage* msg = NULL; + SrsBandwidthPacket* pkt = NULL; + if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { + srs_error("expect start pub message failed. ret=%d", ret); + return ret; + } + SrsAutoFree(SrsCommonMessage, msg, false); + srs_info("get bandcheck start pub message"); + + if (pkt->command_name == SRS_BW_CHECK_START_PUBLISH) { + break; + } + } + + return ret; +} + +int SrsBandCheckClient::send_starting_pub() +{ + int ret = ERROR_SUCCESS; + + SrsCommonMessage* msg = new SrsCommonMessage; + SrsBandwidthPacket* pkt = new SrsBandwidthPacket; + pkt->command_name = SRS_BW_CHECK_STARTING_PUBLISH; + msg->set_packet(pkt, 0); + + if ((ret = send_message(msg)) != ERROR_SUCCESS) { + srs_error("send starting play msg failed. ret=%d", ret); + return ret; + } + srs_info("send starting play msg success."); + + return ret; +} + +int SrsBandCheckClient::send_pub_data() +{ + int ret = ERROR_SUCCESS; + + int data_count = 100; + while (true) { + SrsCommonMessage* msg = new SrsCommonMessage; + SrsBandwidthPacket* pkt = new SrsBandwidthPacket; + pkt->command_name = SRS_BW_CHECK_PUBLISHING; + msg->set_packet(pkt, 0); + + for (int i = 0; i < data_count; ++i) { + std::stringstream seq; + seq << i; + std::string play_data = "SrS band check data from client's publishing......"; + pkt->data->set(seq.str(), new SrsAmf0String(play_data.c_str())); + } + data_count += 100; + + if ((ret = send_message(msg)) != ERROR_SUCCESS) { + srs_error("send publish message failed.ret=%d", ret); + return ret; + } + + if ((ret = expect_stop_pub()) == ERROR_SUCCESS) { + break; + } + } + + return ret; +} + +int SrsBandCheckClient::expect_stop_pub() +{ + int ret = ERROR_SUCCESS; + + while (true) { + if ((ret = st_netfd_poll(stfd, POLLIN, 1000)) == ERROR_SUCCESS) { + SrsCommonMessage* msg = 0; + if ((ret = recv_message(&msg)) != ERROR_SUCCESS) + { + srs_error("recv message failed while expect stop pub. ret=%d", ret); + return ret; + } + + if ((ret = msg->decode_packet(protocol)) != ERROR_SUCCESS) { + srs_error("decode packet error while expect stop pub. ret=%d", ret); + return ret; + } + + SrsBandwidthPacket* pkt = dynamic_cast(msg->get_packet()); + if (pkt && pkt->command_name == SRS_BW_CHECK_STOP_PUBLISH) { + + return ret; + } + } else { + break; + } + } + + return ret; +} + +int SrsBandCheckClient::expect_finished() +{ + int ret = ERROR_SUCCESS; + + while (true) { + SrsCommonMessage* msg = NULL; + SrsBandwidthPacket* pkt = NULL; + if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { + srs_error("expect finished message failed. ret=%d", ret); + return ret; + } + SrsAutoFree(SrsCommonMessage, msg, false); + srs_info("get bandcheck finished message"); + + if (pkt->command_name == SRS_BW_CHECK_FINISHED) { + SrsStream *stream = new SrsStream; + SrsAutoFree(SrsStream, stream, false); + + if ((ret = stream->initialize((char*)msg->payload, msg->size)) != ERROR_SUCCESS) { + srs_error("initialize stream error. ret=%d", ret); + return ret; + } + + std::string command_name; + if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) { + srs_error("amfo read string error. ret=%d", ret); + return ret; + } + + double action_id; + if ((ret = srs_amf0_read_number(stream, action_id)) != ERROR_SUCCESS) { + srs_error("amfo read number error. ret=%d", ret); + return ret; + } + + if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) { + srs_error("amfo read number error. ret=%d", ret); + return ret; + } + + SrsAmf0Object* object; + if ((ret = srs_amf0_read_object(stream, object)) != ERROR_SUCCESS) { + srs_error("amfo read object error. ret=%d", ret); + return ret; + } + + int64_t start_time = 0; + int64_t end_time = 0; + + SrsAmf0Any* start_time_any = object->get_property("start_time"); + if (start_time_any && start_time_any->is_number()) { + SrsAmf0Number* start_time_number = dynamic_cast (start_time_any); + if (start_time_number) { + start_time = start_time_number->value; + } + } + + SrsAmf0Any* end_time_any = object->get_property("end_time"); + if (end_time_any && end_time_any->is_number()) { + SrsAmf0Number* end_time_number = dynamic_cast (end_time_any); + if (end_time_number) { + end_time = end_time_number->value; + } + } + + int play_kbps = 0; + int pub_kbps = 0; + SrsAmf0Any* play_kbp_any = object->get_property("play_kbps"); + if (play_kbp_any && play_kbp_any->is_number()) { + SrsAmf0Number* play_kbps_number = dynamic_cast (play_kbp_any); + if (play_kbps_number) { + play_kbps = play_kbps_number->value; + } + } + + SrsAmf0Any* pub_kbp_any = object->get_property("publish_kbps"); + if (pub_kbp_any && pub_kbp_any->is_number()) { + SrsAmf0Number* pub_kbps_number = dynamic_cast (pub_kbp_any); + if (pub_kbps_number) { + pub_kbps = pub_kbps_number->value; + } + } + + float time_elapsed; + if (end_time - start_time > 0) { + time_elapsed = (end_time - start_time) / 1000.00; + } + + srs_trace("result: play %d kbps, publish %d kbps, check time %.4f S\n" + , play_kbps, pub_kbps, time_elapsed); + + break; + } + } + + return ret; +} + +int SrsBandCheckClient::send_stopped_pub() +{ + int ret = ERROR_SUCCESS; + + SrsCommonMessage* msg = new SrsCommonMessage; + SrsBandwidthPacket* pkt = new SrsBandwidthPacket; + pkt->command_name = SRS_BW_CHECK_STOPPED_PUBLISH; + msg->set_packet(pkt, 0); + + if ((ret = send_message(msg)) != ERROR_SUCCESS) { + srs_error("send stopped pub msg failed. ret=%d", ret); + return ret; + } + srs_info("send stopped pub msg success."); + + return ret; +} + +int SrsBandCheckClient::send_final() +{ + int ret = ERROR_SUCCESS; + + SrsCommonMessage* msg = new SrsCommonMessage; + SrsBandwidthPacket* pkt = new SrsBandwidthPacket; + pkt->command_name = SRS_BW_CHECK_FLASH_FINAL; + msg->set_packet(pkt, 0); + + if ((ret = send_message(msg)) != ERROR_SUCCESS) { + srs_error("send final msg failed. ret=%d", ret); + return ret; + } + srs_info("send final msg success."); + + return ret; +} + +SrsBandCheck::SrsBandCheck() + : bandCheck_Client(0) +{ +} + +SrsBandCheck::~SrsBandCheck() +{ + if (bandCheck_Client) { + srs_freep(bandCheck_Client); + } +} + +int SrsBandCheck::check(const std::string &app, const std::string &tcUrl) +{ + int ret = ERROR_SUCCESS; + + if ((ret = connect_server()) != ERROR_SUCCESS) { + srs_error("connect to server failed. ret = %d", ret); + return ret; + } + + if ((ret = bandCheck_Client->handshake()) != ERROR_SUCCESS) { + srs_error("handshake failed. ret = %d", ret); + return ret; + } + + if ((ret = bandCheck_Client->connect_app(app, tcUrl)) != ERROR_SUCCESS) { + srs_error("handshake failed. ret = %d", ret); + return ret; + } + + if ((ret = bandCheck_Client->check_play()) != ERROR_SUCCESS) { + srs_error("band check play failed."); + return ret; + } + + if ((ret = bandCheck_Client->check_publish()) != ERROR_SUCCESS) { + srs_error("band check publish failed."); + return ret; + } + + return ret; +} + +void SrsBandCheck::set_server(const std::string &server, int port) +{ + server_address = server; + server_port = port; +} + +int SrsBandCheck::connect_server() +{ + int ret = ERROR_SUCCESS; + + 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; + } + + st_netfd_t 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; + } + + bandCheck_Client = new SrsBandCheckClient(stfd); + + // connect to server. + std::string ip = srs_dns_resolve(server_address); + 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(server_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(), server_port, ret); + return ret; + } + srs_trace("connect to server success. server=%s, ip=%s, port=%d", server_address.c_str(), ip.c_str(), server_port); + + return ret; +} + +int init_st() +{ + int ret = ERROR_SUCCESS; + + if (st_set_eventsys(ST_EVENTSYS_ALT) == -1) { + ret = ERROR_ST_SET_EPOLL; + srs_error("st_set_eventsys use linux epoll failed. ret=%d", ret); + return ret; + } + + if(st_init() != 0){ + ret = ERROR_ST_INITIALIZE; + srs_error("st_init failed. ret=%d", ret); + return ret; + } + + return ret; +} + +void print_help(char** argv) +{ + printf( + "Usage: %s [OPTION]...\n" + "test band width from client to rtmp server.\n" + "Mandatory arguments to long options are mandatory for short options too.\n" + " -i, --ip the ip or domain that to test\n" + " -p, --port the port that server listen \n" + " -k, --key the key used to test \n" + " -v, --vhost the vhost used to test \n" + " -V, --version output version information and exit \n" + " -h, --help display this help and exit \n" + "\n" + "For example:\n" + " %s -i 192.168.1.248 -p 1935 -v bandcheck.srs.com -k 35c9b402c12a7246868752e2878f7e0e" + "\n\n" + "Exit status:\n" + "0 if OK,\n" + "other if error occured, and the detail should be printed.\n" + "\n\n" + "srs home page: \n", + argv[0], argv[0]); +} + +void print_version() +{ + const char *version = "" + "srs_bandcheck "BUILD_VERSION"\n" + "Copyright (c) 2013-2014 wenjiegit.\n" + "License MIT\n" + "This is free software: you are free to change and redistribute it.\n" + "There is NO WARRANTY, to the extent permitted by law.\n" + "\n" + "Written by wenjie.\n"; + + printf("%s", version); +} + +int get_opt(int argc, char *argv[]) +{ + int ret = ERROR_SUCCESS; + + int c; + while ((c = getopt_long (argc, argv, short_options, long_options, NULL)) != -1) { + switch (c) { + case 'i': + if (optarg) { + g_ip = optarg; + } + break; + case 'p': + if (optarg) { + g_port = atoi(optarg); + } + break; + case 'k': + if (optarg) { + g_key = optarg; + } + break; + case 'v': + if (optarg) { + g_vhost = optarg; + } + break; + case 'V': + print_version(); + exit(0); + break; + case 'h': + print_help(argv); + exit(0); + break; + default: + printf("see --help or -h\n"); + ret = -1; + } + } + + return ret; +} diff --git a/trunk/src/main/srs_main_server.cpp b/trunk/src/main/srs_main_server.cpp index 94d6fc31e..e87527e36 100644 --- a/trunk/src/main/srs_main_server.cpp +++ b/trunk/src/main/srs_main_server.cpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013 winlin +Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in From 0b7c7240e638108c01cc5f5f455b1dfe2331ebb3 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 1 Jan 2014 11:21:52 +0800 Subject: [PATCH 11/26] refine the authors and license --- AUTHORS.txt | 4 + README.md | 78 +- trunk/src/core/srs_core.hpp | 2 +- trunk/src/core/srs_core_config.cpp | 3334 ++++++++++++++-------------- trunk/src/core/srs_core_rtmp.cpp | 2374 ++++++++++---------- trunk/src/core/srs_core_source.cpp | 2 +- 6 files changed, 2901 insertions(+), 2893 deletions(-) create mode 100644 AUTHORS.txt mode change 100755 => 100644 README.md mode change 100644 => 100755 trunk/src/core/srs_core_config.cpp mode change 100644 => 100755 trunk/src/core/srs_core_rtmp.cpp diff --git a/AUTHORS.txt b/AUTHORS.txt new file mode 100644 index 000000000..49c96d4cf --- /dev/null +++ b/AUTHORS.txt @@ -0,0 +1,4 @@ +Authors ordered by first contribution. + +* winlin +* wenjie diff --git a/README.md b/README.md old mode 100755 new mode 100644 index 7fb4df96f..9c3920568 --- a/README.md +++ b/README.md @@ -1,56 +1,60 @@ -simple-rtmp-server +Simple-RTMP-Server ================== -srs(simple rtmp origin live server) over state-threads.
-srs is a simple, high-performance, running in single process, origin live server.
-srs supports vhost, rtmp, HLS, transcoding, forward, http hooks.
-blog: [http://blog.csdn.net/win_lin](http://blog.csdn.net/win_lin)
-see also: [https://github.com/winlinvip/simple-rtmp-server](https://github.com/winlinvip/simple-rtmp-server)
-see also: [http://winlinvip.github.io/simple-rtmp-server](http://winlinvip.github.io/simple-rtmp-server)
+SRS(SIMPLE RTMP Server) over state-threads created in 2013.
+SRS is a simple, high-performance, running in single process, origin live server.
+SRS supports vhost, rtmp, HLS, transcoding, forward, http hooks.
+Blog: [http://blog.csdn.net/win_lin](http://blog.csdn.net/win_lin)
+See also: [https://github.com/winlinvip/simple-rtmp-server](https://github.com/winlinvip/simple-rtmp-server)
+See also: [http://winlinvip.github.io/simple-rtmp-server](http://winlinvip.github.io/simple-rtmp-server)
TencentQQ: [http://url.cn/WAHICw](http://url.cn/WAHICw) (Group: 212189142) -### Contributors -winlin([winterserver](#)): [http://blog.csdn.net/win_lin](http://blog.csdn.net/win_lin)
-wenjie([wenjiegit](https://github.com/wenjiegit/simple-rtmp-server)): [http://blog.chinaunix.net/uid/25006789.html](http://blog.chinaunix.net/uid/25006789.html)
-about the contributors:
-1. contribute important features to srs.
-2. the name of all contributors will send in the response of NetConnection.connect and metadata. +### AUTHORS +The PRIMARY AUTHORS are (and/or have been)(Authors ordered by first contribution):
+* winlin([winterserver](#)): [http://blog.csdn.net/win_lin](http://blog.csdn.net/win_lin)
+* wenjie([wenjiegit](https://github.com/wenjiegit/simple-rtmp-server)): [http://blog.chinaunix.net/uid/25006789.html](http://blog.chinaunix.net/uid/25006789.html)
+About the primary AUTHORS:
+* Contribute important features to SRS.
+* Names of all PRIMARY AUTHORS response in NetConnection.connect and metadata.
+And here is an inevitably incomplete list of MUCH-APPRECIATED CONTRIBUTORS --
+people who have submitted patches, reported bugs, added translations, helped
+answer newbie questions, and generally made SRS that much better: [AUTHORS.txt](https://github.com/winlinvip/simple-rtmp-server/blob/master/AUTHORS.txt) ### Usage(simple) -step -1: get srs
+step -1: get SRS
 git clone https://github.com/winlinvip/simple-rtmp-server &&
 cd simple-rtmp-server/trunk
 
-step 0: build srs system.
+step 0: build SRS system.
 bash scripts/build.sh
 
-step 1: start srs all demo features.
+step 1: start SRS all demo features.
 bash scripts/run.sh
 
-step 2: srs live show: [http://your-server-ip](http://your-server-ip)
-step 3: stop srs demo
+step 2: SRS live show: [http://your-server-ip](http://your-server-ip)
+step 3: stop SRS demo
 bash scripts/stop.sh
 
### Usage(detail) -step 0: get srs
+step 0: get SRS
 git clone https://github.com/winlinvip/simple-rtmp-server &&
 cd simple-rtmp-server/trunk
 
-step 1: build srs
+step 1: build SRS
 ./configure --with-ssl --with-hls --with-ffmpeg --with-http && make
 
-step 2: start srs
+step 2: start SRS
 ./objs/srs -c conf/srs.conf
 
-step 3(optinal): start srs listen at 19350 to forward to
+step 3(optinal): start SRS listen at 19350 to forward to
 ./objs/srs -c conf/srs.19350.conf
 
@@ -58,7 +62,7 @@ cd simple-rtmp-server/trunk
 sudo ./objs/nginx/sbin/nginx
 
-step 5(optinal): start http hooks for srs callback
+step 5(optinal): start http hooks for SRS callback
 python ./research/api-server/server.py 8085
 
@@ -135,7 +139,7 @@ killall -s SIGHUP srs System Architecture:
 +------------------------------------------------------+
-|             SRS(Simple Rtmp Server)                  |
+|             SRS(Simple RTMP Server)                  |
 +---------------+---------------+-----------+----------+
 |   API/hook    |   Transcoder  |    HLS    |   RTMP   |
 |  http-parser  |  FFMPEG/x264  |  NGINX/ts | protocol |
@@ -151,7 +155,7 @@ Stream Architecture:
         + Publish +              +  Deliver | 
         +---|-----+              +----|-----+ 
 +-----------+-------------------------+----------------+
-| Encoder   | SRS(Simple Rtmp Server) |     Client     |
+| Encoder   | SRS(Simple RTMP Server) |     Client     |
 +-----------+-------------------------+----------------+
 | (FMLE,    |   +-> RTMP protocol ----+-> Flash Player |
 | FFMPEG, --+-> +-> HLS/NGINX --------+-> m3u8 player  |
@@ -217,7 +221,7 @@ Supported operating systems and hardware:
 20. support http callback api hooks(for authentication and injection).
21. support bandwidth test api and flash client.
22. player, publisher(encoder), and demo pages(jquery+bootstrap).
-23. demo video meeting or chat(srs+cherrypy+jquery+bootstrap).
+23. demo video meeting or chat(SRS+cherrypy+jquery+bootstrap).
24. [plan] support network based cli and json result.
25. [plan] support adobe flash refer/token/swf verification.
26. [plan] support adobe amf3 codec.
@@ -265,15 +269,15 @@ usr sys idl wai hiq siq| read writ| recv send| in out | int csw * 2013-10-17, created.
### Compare -* srs v0.9: 20926 lines. player/encoder/chat demos. bandwidth test for encoder/CDN.
-* srs v0.8: 19186 lines. implements http hooks refer to [nginx-rtmp](https://github.com/arut/nginx-rtmp-module).
-* srs v0.7: 17605 lines. implements transcoding(FFMPEG) feature refer to [wowza](http://www.wowza.com).
-* srs v0.6: 16094 lines. important feature forward for CDN.
-* srs v0.5: 14449 lines. implements HLS feature refer to [nginx-rtmp](https://github.com/arut/nginx-rtmp-module).
-* srs v0.4: 12500 lines. important feature reload for CDN.
-* srs v0.3: 11773 lines. implements vhost feature refer to [FMS](http://www.adobe.com/products/adobe-media-server-family.html).
-* srs v0.2: 10125 lines. implements rtmp protocol stack refer to [nginx-rtmp](https://github.com/arut/nginx-rtmp-module).
-* srs v0.1: 8287 lines. base on state-threads.
+* SRS v0.9: 20926 lines. player/encoder/chat demos. bandwidth test for encoder/CDN.
+* SRS v0.8: 19186 lines. implements http hooks refer to [nginx-rtmp](https://github.com/arut/nginx-rtmp-module).
+* SRS v0.7: 17605 lines. implements transcoding(FFMPEG) feature refer to [wowza](http://www.wowza.com).
+* SRS v0.6: 16094 lines. important feature forward for CDN.
+* SRS v0.5: 14449 lines. implements HLS feature refer to [nginx-rtmp](https://github.com/arut/nginx-rtmp-module).
+* SRS v0.4: 12500 lines. important feature reload for CDN.
+* SRS v0.3: 11773 lines. implements vhost feature refer to [FMS](http://www.adobe.com/products/adobe-media-server-family.html).
+* SRS v0.2: 10125 lines. implements rtmp protocol stack refer to [nginx-rtmp](https://github.com/arut/nginx-rtmp-module).
+* SRS v0.1: 8287 lines. base on state-threads.
* nginx-rtmp v1.0.4: 26786 lines
* nginx v1.5.0: 139524 lines
@@ -281,7 +285,7 @@ usr sys idl wai hiq siq| read writ| recv send| in out | int csw * v1.0, 2013-12-27, merge from wenjie, the bandwidth test feature. * v0.9, 2013-12-25, [v0.9](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.9) released. 20926 lines. * v0.9, 2013-12-25, fix the bitrate bug(in Bps), use enhanced microphone. -* v0.9, 2013-12-22, demo video meeting or chat(srs+cherrypy+jquery+bootstrap). +* v0.9, 2013-12-22, demo video meeting or chat(SRS+cherrypy+jquery+bootstrap). * v0.9, 2013-12-22, merge from wenjie, support banwidth test. * v0.9, 2013-12-22, merge from wenjie: support set chunk size at vhost level * v0.9, 2013-12-21, add [players](http://demo.srs.com/players) for play and publish. @@ -298,7 +302,7 @@ usr sys idl wai hiq siq| read writ| recv send| in out | int csw * v0.8, 2013-12-08, support multiple http hooks for a event. * v0.8, 2013-12-07, support http callback hooks, on_connect. * v0.8, 2013-12-07, support network based cli and json result, add CherryPy 3.2.4. -* v0.8, 2013-12-07, update http/hls/rtmp load test tool [st_load](https://github.com/winlinvip/st-load), use srs rtmp sdk. +* v0.8, 2013-12-07, update http/hls/rtmp load test tool [st_load](https://github.com/winlinvip/st-load), use SRS rtmp sdk. * v0.8, 2013-12-06, support max_connections, drop if exceed. * v0.8, 2013-12-05, support log_dir, write ffmpeg log to file. * v0.8, 2013-12-05, fix the forward/hls/encoder bug. diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 64f79f2f1..8e6a8859d 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -79,7 +79,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define RTMP_SIG_SRS_EMAIL "winterserver@126.com" #define RTMP_SIG_SRS_LICENSE "The MIT License (MIT)" #define RTMP_SIG_SRS_COPYRIGHT "Copyright (c) 2013-2014 winlin" -#define RTMP_SIG_SRS_CONTRIBUTOR "winlin,wenjiegit" +#define RTMP_SIG_SRS_PRIMARY_AUTHROS "winlin,wenjiegit" // compare #define srs_min(a, b) (((a) < (b))? (a) : (b)) diff --git a/trunk/src/core/srs_core_config.cpp b/trunk/src/core/srs_core_config.cpp old mode 100644 new mode 100755 index ea44b8a25..f8223df68 --- a/trunk/src/core/srs_core_config.cpp +++ b/trunk/src/core/srs_core_config.cpp @@ -1,1667 +1,1667 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013-2014 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include - -#include -#include -#include -#include -// file operations. -#include -#include -#include -#include - -#include -#include -using namespace std; - -#include -#include -#include - -#define FILE_OFFSET(fd) lseek(fd, 0, SEEK_CUR) - -int64_t FILE_SIZE(int fd) -{ - int64_t pre = FILE_OFFSET(fd); - int64_t pos = lseek(fd, 0, SEEK_END); - lseek(fd, pre, SEEK_SET); - return pos; -} - -#define LF (char)0x0a -#define CR (char)0x0d - -bool is_common_space(char ch) -{ - return (ch == ' ' || ch == '\t' || ch == CR || ch == LF); -} - -class SrsFileBuffer -{ -private: - // last available position. - char* last; - // end of buffer. - char* end; - // start of buffer. - char* start; -public: - // current consumed position. - char* pos; - // current parsed line. - int line; - - SrsFileBuffer(); - virtual ~SrsFileBuffer(); - virtual int fullfill(const char* filename); - virtual bool empty(); -}; - -SrsFileBuffer::SrsFileBuffer() -{ - line = 0; - - pos = last = start = NULL; - end = start; -} - -SrsFileBuffer::~SrsFileBuffer() -{ - srs_freepa(start); -} - -int SrsFileBuffer::fullfill(const char* filename) -{ - int ret = ERROR_SUCCESS; - - int fd = -1; - int nread = 0; - int filesize = 0; - - if ((fd = ::open(filename, O_RDONLY, 0)) < 0) { - ret = ERROR_SYSTEM_CONFIG_INVALID; - srs_error("open conf file error. ret=%d", ret); - goto finish; - } - - if ((filesize = FILE_SIZE(fd) - FILE_OFFSET(fd)) <= 0) { - ret = ERROR_SYSTEM_CONFIG_EOF; - srs_error("read conf file error. ret=%d", ret); - goto finish; - } - - srs_freepa(start); - pos = last = start = new char[filesize]; - end = start + filesize; - - if ((nread = read(fd, start, filesize)) != filesize) { - ret = ERROR_SYSTEM_CONFIG_INVALID; - srs_error("read file read error. expect %d, actual %d bytes, ret=%d", - filesize, nread, ret); - goto finish; - } - - line = 1; - -finish: - if (fd > 0) { - ::close(fd); - } - - return ret; -} - -bool SrsFileBuffer::empty() -{ - return pos >= end; -} - -SrsConfDirective::SrsConfDirective() -{ -} - -SrsConfDirective::~SrsConfDirective() -{ - std::vector::iterator it; - for (it = directives.begin(); it != directives.end(); ++it) { - SrsConfDirective* directive = *it; - srs_freep(directive); - } - directives.clear(); -} - -string SrsConfDirective::arg0() -{ - if (args.size() > 0) { - return args.at(0); - } - - return ""; -} - -string SrsConfDirective::arg1() -{ - if (args.size() > 1) { - return args.at(1); - } - - return ""; -} - -string SrsConfDirective::arg2() -{ - if (args.size() > 2) { - return args.at(2); - } - - return ""; -} - -SrsConfDirective* SrsConfDirective::at(int index) -{ - return directives.at(index); -} - -SrsConfDirective* SrsConfDirective::get(string _name) -{ - std::vector::iterator it; - for (it = directives.begin(); it != directives.end(); ++it) { - SrsConfDirective* directive = *it; - if (directive->name == _name) { - return directive; - } - } - - return NULL; -} - -SrsConfDirective* SrsConfDirective::get(string _name, string _arg0) -{ - std::vector::iterator it; - for (it = directives.begin(); it != directives.end(); ++it) { - SrsConfDirective* directive = *it; - if (directive->name == _name && directive->arg0() == _arg0) { - return directive; - } - } - - return NULL; -} - -int SrsConfDirective::parse(const char* filename) -{ - int ret = ERROR_SUCCESS; - - SrsFileBuffer buffer; - - if ((ret = buffer.fullfill(filename)) != ERROR_SUCCESS) { - return ret; - } - - return parse_conf(&buffer, parse_file); -} - -// see: ngx_conf_parse -int SrsConfDirective::parse_conf(SrsFileBuffer* buffer, SrsDirectiveType type) -{ - int ret = ERROR_SUCCESS; - - while (true) { - std::vector args; - ret = read_token(buffer, args); - - /** - * ret maybe: - * ERROR_SYSTEM_CONFIG_INVALID error. - * ERROR_SYSTEM_CONFIG_DIRECTIVE directive terminated by ';' found - * ERROR_SYSTEM_CONFIG_BLOCK_START token terminated by '{' found - * ERROR_SYSTEM_CONFIG_BLOCK_END the '}' found - * ERROR_SYSTEM_CONFIG_EOF the config file is done - */ - if (ret == ERROR_SYSTEM_CONFIG_INVALID) { - return ret; - } - if (ret == ERROR_SYSTEM_CONFIG_BLOCK_END) { - if (type != parse_block) { - srs_error("line %d: unexpected \"}\"", buffer->line); - return ret; - } - return ERROR_SUCCESS; - } - if (ret == ERROR_SYSTEM_CONFIG_EOF) { - if (type == parse_block) { - srs_error("line %d: unexpected end of file, expecting \"}\"", buffer->line); - return ret; - } - return ERROR_SUCCESS; - } - - if (args.empty()) { - srs_error("line %d: empty directive.", buffer->line); - return ret; - } - - // build directive tree. - SrsConfDirective* directive = new SrsConfDirective(); - - directive->conf_line = buffer->line; - directive->name = args[0]; - args.erase(args.begin()); - directive->args.swap(args); - - directives.push_back(directive); - - if (ret == ERROR_SYSTEM_CONFIG_BLOCK_START) { - if ((ret = directive->parse_conf(buffer, parse_block)) != ERROR_SUCCESS) { - return ret; - } - } - } - - return ret; -} - -// see: ngx_conf_read_token -int SrsConfDirective::read_token(SrsFileBuffer* buffer, std::vector& args) -{ - int ret = ERROR_SUCCESS; - - char* pstart = buffer->pos; - int startline = buffer->line; - - bool sharp_comment = false; - - bool d_quoted = false; - bool s_quoted = false; - - bool need_space = false; - bool last_space = true; - - while (true) { - if (buffer->empty()) { - ret = ERROR_SYSTEM_CONFIG_EOF; - - if (!args.empty() || !last_space) { - srs_error("line %d: unexpected end of file, expecting ; or \"}\"", buffer->line); - return ERROR_SYSTEM_CONFIG_INVALID; - } - srs_error("end of file. ret=%d", ret); - - return ret; - } - - char ch = *buffer->pos++; - - if (ch == LF) { - buffer->line++; - sharp_comment = false; - } - - if (sharp_comment) { - continue; - } - - if (need_space) { - if (is_common_space(ch)) { - last_space = true; - need_space = false; - continue; - } - if (ch == ';') { - return ERROR_SYSTEM_CONFIG_DIRECTIVE; - } - if (ch == '{') { - return ERROR_SYSTEM_CONFIG_BLOCK_START; - } - srs_error("line %d: unexpected '%c'", buffer->line, ch); - return ERROR_SYSTEM_CONFIG_INVALID; - } - - // last charecter is space. - if (last_space) { - if (is_common_space(ch)) { - continue; - } - pstart = buffer->pos - 1; - startline = buffer->line; - switch (ch) { - case ';': - if (args.size() == 0) { - srs_error("line %d: unexpected ';'", buffer->line); - return ERROR_SYSTEM_CONFIG_INVALID; - } - return ERROR_SYSTEM_CONFIG_DIRECTIVE; - case '{': - if (args.size() == 0) { - srs_error("line %d: unexpected '{'", buffer->line); - return ERROR_SYSTEM_CONFIG_INVALID; - } - return ERROR_SYSTEM_CONFIG_BLOCK_START; - case '}': - if (args.size() != 0) { - srs_error("line %d: unexpected '}'", buffer->line); - return ERROR_SYSTEM_CONFIG_INVALID; - } - return ERROR_SYSTEM_CONFIG_BLOCK_END; - case '#': - sharp_comment = 1; - continue; - case '"': - pstart++; - d_quoted = true; - last_space = 0; - continue; - case '\'': - pstart++; - s_quoted = true; - last_space = 0; - continue; - default: - last_space = 0; - continue; - } - } else { - // last charecter is not space - bool found = false; - if (d_quoted) { - if (ch == '"') { - d_quoted = false; - need_space = true; - found = true; - } - } else if (s_quoted) { - if (ch == '\'') { - s_quoted = false; - need_space = true; - found = true; - } - } else if (is_common_space(ch) || ch == ';' || ch == '{') { - last_space = true; - found = 1; - } - - if (found) { - int len = buffer->pos - pstart; - char* word = new char[len]; - memcpy(word, pstart, len); - word[len - 1] = 0; - - string word_str = word; - if (!word_str.empty()) { - args.push_back(word_str); - } - srs_freepa(word); - - if (ch == ';') { - return ERROR_SYSTEM_CONFIG_DIRECTIVE; - } - if (ch == '{') { - return ERROR_SYSTEM_CONFIG_BLOCK_START; - } - } - } - } - - return ret; -} - -SrsConfig* config = new SrsConfig(); - -SrsConfig::SrsConfig() -{ - show_help = false; - show_version = false; - - root = new SrsConfDirective(); - root->conf_line = 0; - root->name = "root"; -} - -SrsConfig::~SrsConfig() -{ - srs_freep(root); -} - -int SrsConfig::reload() -{ - int ret = ERROR_SUCCESS; - - SrsConfig conf; - if ((ret = conf.parse_file(config_file.c_str())) != ERROR_SUCCESS) { - srs_error("config reloader parse file failed. ret=%d", ret); - return ret; - } - srs_info("config reloader parse file success."); - - // store current root to old_root, - // and reap the root from conf to current root. - SrsConfDirective* old_root = root; - SrsAutoFree(SrsConfDirective, old_root, false); - - root = conf.root; - conf.root = NULL; - - // merge config. - std::vector::iterator it; - - // merge config: listen - if (!srs_directive_equals(root->get("listen"), old_root->get("listen"))) { - for (it = subscribes.begin(); it != subscribes.end(); ++it) { - ISrsReloadHandler* subscribe = *it; - if ((ret = subscribe->on_reload_listen()) != ERROR_SUCCESS) { - srs_error("notify subscribes reload listen failed. ret=%d", ret); - return ret; - } - } - srs_trace("reload listen success."); - } - - // merge config: pithy_print - if (!srs_directive_equals(root->get("pithy_print"), old_root->get("pithy_print"))) { - for (it = subscribes.begin(); it != subscribes.end(); ++it) { - ISrsReloadHandler* subscribe = *it; - if ((ret = subscribe->on_reload_pithy_print()) != ERROR_SUCCESS) { - srs_error("notify subscribes pithy_print listen failed. ret=%d", ret); - return ret; - } - } - srs_trace("reload pithy_print success."); - } - - // merge config: vhost added, directly supported. - - // merge config: vhost removed/disabled/modified. - for (int i = 0; i < (int)old_root->directives.size(); i++) { - SrsConfDirective* old_vhost = old_root->at(i); - // only process vhost directives. - if (old_vhost->name != "vhost") { - continue; - } - - std::string vhost = old_vhost->arg0(); - - SrsConfDirective* new_vhost = root->get("vhost", vhost); - // ignore if absolutely equal - if (new_vhost && srs_directive_equals(old_vhost, new_vhost)) { - srs_trace("vhost %s absolutely equal, ignore.", vhost.c_str()); - continue; - } - // ignore if enable the new vhost when old vhost is disabled. - if (get_vhost_enabled(new_vhost) && !get_vhost_enabled(old_vhost)) { - srs_trace("vhost %s disabled=>enabled, ignore.", vhost.c_str()); - continue; - } - // ignore if both old and new vhost are disabled. - if (!get_vhost_enabled(new_vhost) && !get_vhost_enabled(old_vhost)) { - srs_trace("vhost %s disabled=>disabled, ignore.", vhost.c_str()); - continue; - } - - // merge config: vhost removed/disabled. - if (!get_vhost_enabled(new_vhost) && get_vhost_enabled(old_vhost)) { - srs_trace("vhost %s disabled, reload it.", vhost.c_str()); - for (it = subscribes.begin(); it != subscribes.end(); ++it) { - ISrsReloadHandler* subscribe = *it; - if ((ret = subscribe->on_reload_vhost_removed(vhost)) != ERROR_SUCCESS) { - srs_error("notify subscribes pithy_print remove " - "vhost %s failed. ret=%d", vhost.c_str(), ret); - return ret; - } - } - srs_trace("reload remove vhost %s success.", vhost.c_str()); - } - - // merge config: vhost modified. - srs_trace("vhost %s modified, reload its detail.", vhost.c_str()); - if (get_vhost_enabled(new_vhost) && get_vhost_enabled(old_vhost)) { - // gop_cache - if (!srs_directive_equals(new_vhost->get("gop_cache"), old_vhost->get("gop_cache"))) { - for (it = subscribes.begin(); it != subscribes.end(); ++it) { - ISrsReloadHandler* subscribe = *it; - if ((ret = subscribe->on_reload_gop_cache(vhost)) != ERROR_SUCCESS) { - srs_error("vhost %s notify subscribes gop_cache failed. ret=%d", vhost.c_str(), ret); - return ret; - } - } - srs_trace("vhost %s reload gop_cache success.", vhost.c_str()); - } - // queue_length - if (!srs_directive_equals(new_vhost->get("queue_length"), old_vhost->get("queue_length"))) { - for (it = subscribes.begin(); it != subscribes.end(); ++it) { - ISrsReloadHandler* subscribe = *it; - if ((ret = subscribe->on_reload_queue_length(vhost)) != ERROR_SUCCESS) { - srs_error("vhost %s notify subscribes queue_length failed. ret=%d", vhost.c_str(), ret); - return ret; - } - } - srs_trace("vhost %s reload queue_length success.", vhost.c_str()); - } - // forward - if (!srs_directive_equals(new_vhost->get("forward"), old_vhost->get("forward"))) { - for (it = subscribes.begin(); it != subscribes.end(); ++it) { - ISrsReloadHandler* subscribe = *it; - if ((ret = subscribe->on_reload_forward(vhost)) != ERROR_SUCCESS) { - srs_error("vhost %s notify subscribes forward failed. ret=%d", vhost.c_str(), ret); - return ret; - } - } - srs_trace("vhost %s reload forward success.", vhost.c_str()); - } - // hls - if (!srs_directive_equals(new_vhost->get("hls"), old_vhost->get("hls"))) { - for (it = subscribes.begin(); it != subscribes.end(); ++it) { - ISrsReloadHandler* subscribe = *it; - if ((ret = subscribe->on_reload_hls(vhost)) != ERROR_SUCCESS) { - srs_error("vhost %s notify subscribes hls failed. ret=%d", vhost.c_str(), ret); - return ret; - } - } - srs_trace("vhost %s reload hls success.", vhost.c_str()); - } - // transcode - if (!srs_directive_equals(new_vhost->get("transcode"), old_vhost->get("transcode"))) { - for (it = subscribes.begin(); it != subscribes.end(); ++it) { - ISrsReloadHandler* subscribe = *it; - if ((ret = subscribe->on_reload_transcode(vhost)) != ERROR_SUCCESS) { - srs_error("vhost %s notify subscribes transcode failed. ret=%d", vhost.c_str(), ret); - return ret; - } - } - srs_trace("vhost %s reload transcode success.", vhost.c_str()); - } - // TODO: suppor reload hls/forward/ffmpeg/http - continue; - } - srs_warn("invalid reload path, enabled old: %d, new: %d", - get_vhost_enabled(old_vhost), get_vhost_enabled(new_vhost)); - } - - return ret; -} - -void SrsConfig::subscribe(ISrsReloadHandler* handler) -{ - std::vector::iterator it; - - it = std::find(subscribes.begin(), subscribes.end(), handler); - if (it != subscribes.end()) { - return; - } - - subscribes.push_back(handler); -} - -void SrsConfig::unsubscribe(ISrsReloadHandler* handler) -{ - std::vector::iterator it; - - it = std::find(subscribes.begin(), subscribes.end(), handler); - if (it == subscribes.end()) { - return; - } - - subscribes.erase(it); -} - -// see: ngx_get_options -int SrsConfig::parse_options(int argc, char** argv) -{ - int ret = ERROR_SUCCESS; - - for (int i = 1; i < argc; i++) { - if ((ret = parse_argv(i, argv)) != ERROR_SUCCESS) { - return ret; - } - } - - if (show_help) { - print_help(argv); - } - - if (show_version) { - printf("%s\n", RTMP_SIG_SRS_VERSION); - } - - if (show_help || show_version) { - exit(0); - } - - if (config_file.empty()) { - ret = ERROR_SYSTEM_CONFIG_INVALID; - srs_error("config file not specified, see help: %s -h, ret=%d", argv[0], ret); - return ret; - } - - return parse_file(config_file.c_str()); -} - -int SrsConfig::parse_file(const char* filename) -{ - int ret = ERROR_SUCCESS; - - config_file = filename; - - if (config_file.empty()) { - return ERROR_SYSTEM_CONFIG_INVALID; - } - - if ((ret = root->parse(config_file.c_str())) != ERROR_SUCCESS) { - return ret; - } - - SrsConfDirective* conf = NULL; - if ((conf = get_listen()) == NULL || conf->args.size() == 0) { - ret = ERROR_SYSTEM_CONFIG_INVALID; - srs_error("line %d: conf error, " - "directive \"listen\" is empty, ret=%d", (conf? conf->conf_line:0), ret); - return ret; - } - - // TODO: check the hls. - // TODO: check forward. - // TODO: check ffmpeg. - // TODO: check http. - - return ret; -} - -int SrsConfig::parse_argv(int& i, char** argv) -{ - int ret = ERROR_SUCCESS; - - char* p = argv[i]; - - if (*p++ != '-') { - ret = ERROR_SYSTEM_CONFIG_INVALID; - srs_error("invalid options(index=%d, value=%s), " - "must starts with -, see help: %s -h, ret=%d", i, argv[i], argv[0], ret); - return ret; - } - - while (*p) { - switch (*p++) { - case '?': - case 'h': - show_help = true; - break; - case 'v': - case 'V': - show_version = true; - break; - case 'c': - if (*p) { - config_file = p; - return ret; - } - if (argv[++i]) { - config_file = argv[i]; - return ret; - } - ret = ERROR_SYSTEM_CONFIG_INVALID; - srs_error("option \"-c\" requires parameter, ret=%d", ret); - return ret; - default: - ret = ERROR_SYSTEM_CONFIG_INVALID; - srs_error("invalid option: \"%c\", see help: %s -h, ret=%d", *(p - 1), argv[0], ret); - return ret; - } - } - - return ret; -} - -void SrsConfig::print_help(char** argv) -{ - printf(RTMP_SIG_SRS_NAME" "RTMP_SIG_SRS_VERSION - " Copyright (c) 2013-2014 winlin\n" - "Contributors: "RTMP_SIG_SRS_CONTRIBUTOR"\n" - "Build: "SRS_BUILD_DATE" Configuration: "SRS_CONFIGURE"\n" - "Usage: %s [-h?vV] [-c ]\n" - "\n" - "Options:\n" - " -?-h : show help\n" - " -v-V : show version and exit\n" - " -c filename : set configuration file\n" - "\n" - RTMP_SIG_SRS_WEB"\n" - RTMP_SIG_SRS_URL"\n" - "Email: "RTMP_SIG_SRS_EMAIL"\n" - "\n", - argv[0]); -} - -SrsConfDirective* SrsConfig::get_vhost(string vhost) -{ - srs_assert(root); - - for (int i = 0; i < (int)root->directives.size(); i++) { - SrsConfDirective* conf = root->at(i); - - if (conf->name != "vhost") { - continue; - } - - if (conf->arg0() == vhost) { - return conf; - } - } - - if (vhost != RTMP_VHOST_DEFAULT) { - return get_vhost(RTMP_VHOST_DEFAULT); - } - - return NULL; -} - -SrsConfDirective* SrsConfig::get_vhost_on_connect(string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - conf = conf->get("http_hooks"); - if (!conf) { - return NULL; - } - - SrsConfDirective* enabled = conf->get("enabled"); - if (!enabled || enabled->arg0() != "on") { - return NULL; - } - - return conf->get("on_connect"); -} - -SrsConfDirective* SrsConfig::get_vhost_on_close(string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - conf = conf->get("http_hooks"); - if (!conf) { - return NULL; - } - - SrsConfDirective* enabled = conf->get("enabled"); - if (!enabled || enabled->arg0() != "on") { - return NULL; - } - - return conf->get("on_close"); -} - -SrsConfDirective* SrsConfig::get_vhost_on_publish(string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - conf = conf->get("http_hooks"); - if (!conf) { - return NULL; - } - - SrsConfDirective* enabled = conf->get("enabled"); - if (!enabled || enabled->arg0() != "on") { - return NULL; - } - - return conf->get("on_publish"); -} - -SrsConfDirective* SrsConfig::get_vhost_on_unpublish(string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - conf = conf->get("http_hooks"); - if (!conf) { - return NULL; - } - - SrsConfDirective* enabled = conf->get("enabled"); - if (!enabled || enabled->arg0() != "on") { - return NULL; - } - - return conf->get("on_unpublish"); -} - -SrsConfDirective* SrsConfig::get_vhost_on_play(string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - conf = conf->get("http_hooks"); - if (!conf) { - return NULL; - } - - SrsConfDirective* enabled = conf->get("enabled"); - if (!enabled || enabled->arg0() != "on") { - return NULL; - } - - return conf->get("on_play"); -} - -SrsConfDirective* SrsConfig::get_vhost_on_stop(string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - conf = conf->get("http_hooks"); - if (!conf) { - return NULL; - } - - SrsConfDirective* enabled = conf->get("enabled"); - if (!enabled || enabled->arg0() != "on") { - return NULL; - } - - return conf->get("on_stop"); -} - -bool SrsConfig::get_vhost_enabled(string vhost) -{ - SrsConfDirective* vhost_conf = get_vhost(vhost); - - return get_vhost_enabled(vhost_conf); -} - -bool SrsConfig::get_vhost_enabled(SrsConfDirective* vhost) -{ - if (!vhost) { - return false; - } - - SrsConfDirective* conf = vhost->get("enabled"); - if (!conf) { - return true; - } - - if (conf->arg0() == "off") { - return false; - } - - return true; -} - -SrsConfDirective* SrsConfig::get_transcode(string vhost, string scope) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - SrsConfDirective* transcode = conf->get("transcode"); - if (!transcode) { - return NULL; - } - - if (transcode->arg0() == scope) { - return transcode; - } - - return NULL; -} - -bool SrsConfig::get_transcode_enabled(SrsConfDirective* transcode) -{ - if (!transcode) { - return false; - } - - SrsConfDirective* conf = transcode->get("enabled"); - if (!conf || conf->arg0() != "on") { - return false; - } - - return true; -} - -string SrsConfig::get_transcode_ffmpeg(SrsConfDirective* transcode) -{ - if (!transcode) { - return ""; - } - - SrsConfDirective* conf = transcode->get("ffmpeg"); - if (!conf) { - return ""; - } - - return conf->arg0(); -} - -void SrsConfig::get_transcode_engines(SrsConfDirective* transcode, std::vector& engines) -{ - if (!transcode) { - return; - } - - for (int i = 0; i < (int)transcode->directives.size(); i++) { - SrsConfDirective* conf = transcode->directives[i]; - - if (conf->name == "engine") { - engines.push_back(conf); - } - } - - return; -} - -bool SrsConfig::get_engine_enabled(SrsConfDirective* engine) -{ - if (!engine) { - return false; - } - - SrsConfDirective* conf = engine->get("enabled"); - if (!conf || conf->arg0() != "on") { - return false; - } - - return true; -} - -string SrsConfig::get_engine_vcodec(SrsConfDirective* engine) -{ - if (!engine) { - return ""; - } - - SrsConfDirective* conf = engine->get("vcodec"); - if (!conf) { - return ""; - } - - return conf->arg0(); -} - -int SrsConfig::get_engine_vbitrate(SrsConfDirective* engine) -{ - if (!engine) { - return 0; - } - - SrsConfDirective* conf = engine->get("vbitrate"); - if (!conf) { - return 0; - } - - return ::atoi(conf->arg0().c_str()); -} - -double SrsConfig::get_engine_vfps(SrsConfDirective* engine) -{ - if (!engine) { - return 0; - } - - SrsConfDirective* conf = engine->get("vfps"); - if (!conf) { - return 0; - } - - return ::atof(conf->arg0().c_str()); -} - -int SrsConfig::get_engine_vwidth(SrsConfDirective* engine) -{ - if (!engine) { - return 0; - } - - SrsConfDirective* conf = engine->get("vwidth"); - if (!conf) { - return 0; - } - - return ::atoi(conf->arg0().c_str()); -} - -int SrsConfig::get_engine_vheight(SrsConfDirective* engine) -{ - if (!engine) { - return 0; - } - - SrsConfDirective* conf = engine->get("vheight"); - if (!conf) { - return 0; - } - - return ::atoi(conf->arg0().c_str()); -} - -int SrsConfig::get_engine_vthreads(SrsConfDirective* engine) -{ - if (!engine) { - return 0; - } - - SrsConfDirective* conf = engine->get("vthreads"); - if (!conf) { - return 0; - } - - return ::atoi(conf->arg0().c_str()); -} - -string SrsConfig::get_engine_vprofile(SrsConfDirective* engine) -{ - if (!engine) { - return ""; - } - - SrsConfDirective* conf = engine->get("vprofile"); - if (!conf) { - return ""; - } - - return conf->arg0(); -} - -string SrsConfig::get_engine_vpreset(SrsConfDirective* engine) -{ - if (!engine) { - return ""; - } - - SrsConfDirective* conf = engine->get("vpreset"); - if (!conf) { - return ""; - } - - return conf->arg0(); -} - -void SrsConfig::get_engine_vparams(SrsConfDirective* engine, std::vector& vparams) -{ - if (!engine) { - return; - } - - SrsConfDirective* conf = engine->get("vparams"); - if (!conf) { - return; - } - - for (int i = 0; i < (int)conf->directives.size(); i++) { - SrsConfDirective* p = conf->directives[i]; - if (!p) { - continue; - } - - vparams.push_back("-" + p->name); - vparams.push_back(p->arg0()); - } -} - -void SrsConfig::get_engine_vfilter(SrsConfDirective* engine, std::vector& vfilter) -{ - if (!engine) { - return; - } - - SrsConfDirective* conf = engine->get("vfilter"); - if (!conf) { - return; - } - - for (int i = 0; i < (int)conf->directives.size(); i++) { - SrsConfDirective* p = conf->directives[i]; - if (!p) { - continue; - } - - vfilter.push_back("-" + p->name); - vfilter.push_back(p->arg0()); - } -} - -string SrsConfig::get_engine_acodec(SrsConfDirective* engine) -{ - if (!engine) { - return ""; - } - - SrsConfDirective* conf = engine->get("acodec"); - if (!conf) { - return ""; - } - - return conf->arg0(); -} - -int SrsConfig::get_engine_abitrate(SrsConfDirective* engine) -{ - if (!engine) { - return 0; - } - - SrsConfDirective* conf = engine->get("abitrate"); - if (!conf) { - return 0; - } - - return ::atoi(conf->arg0().c_str()); -} - -int SrsConfig::get_engine_asample_rate(SrsConfDirective* engine) -{ - if (!engine) { - return 0; - } - - SrsConfDirective* conf = engine->get("asample_rate"); - if (!conf) { - return 0; - } - - return ::atoi(conf->arg0().c_str()); -} - -int SrsConfig::get_engine_achannels(SrsConfDirective* engine) -{ - if (!engine) { - return 0; - } - - SrsConfDirective* conf = engine->get("achannels"); - if (!conf) { - return 0; - } - - return ::atoi(conf->arg0().c_str()); -} - -void SrsConfig::get_engine_aparams(SrsConfDirective* engine, std::vector& aparams) -{ - if (!engine) { - return; - } - - SrsConfDirective* conf = engine->get("aparams"); - if (!conf) { - return; - } - - for (int i = 0; i < (int)conf->directives.size(); i++) { - SrsConfDirective* p = conf->directives[i]; - if (!p) { - continue; - } - - aparams.push_back("-" + p->name); - aparams.push_back(p->arg0()); - } -} - -string SrsConfig::get_engine_output(SrsConfDirective* engine) -{ - if (!engine) { - return ""; - } - - SrsConfDirective* conf = engine->get("output"); - if (!conf) { - return ""; - } - - return conf->arg0(); -} - -string SrsConfig::get_log_dir() -{ - srs_assert(root); - - SrsConfDirective* conf = root->get("log_dir"); - if (!conf || conf->arg0().empty()) { - return "./objs/logs"; - } - - return conf->arg0(); -} - -int SrsConfig::get_max_connections() -{ - srs_assert(root); - - SrsConfDirective* conf = root->get("max_connections"); - if (!conf || conf->arg0().empty()) { - return 2000; - } - - return ::atoi(conf->arg0().c_str()); -} - -bool SrsConfig::get_gop_cache(string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return true; - } - - conf = conf->get("gop_cache"); - if (conf && conf->arg0() == "off") { - return false; - } - - return true; -} - -double SrsConfig::get_queue_length(string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return SRS_CONF_DEFAULT_QUEUE_LENGTH; - } - - conf = conf->get("queue_length"); - if (!conf || conf->arg0().empty()) { - return SRS_CONF_DEFAULT_QUEUE_LENGTH; - } - - return ::atoi(conf->arg0().c_str()); -} - -SrsConfDirective* SrsConfig::get_forward(string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("forward"); -} - -SrsConfDirective* SrsConfig::get_hls(string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("hls"); -} - -bool SrsConfig::get_hls_enabled(string vhost) -{ - SrsConfDirective* hls = get_hls(vhost); - - if (!hls) { - return false; - } - - SrsConfDirective* conf = hls->get("enabled"); - - if (!conf) { - return false; - } - - if (conf->arg0() == "on") { - return true; - } - - return false; -} - -string SrsConfig::get_hls_path(string vhost) -{ - SrsConfDirective* hls = get_hls(vhost); - - if (!hls) { - return SRS_CONF_DEFAULT_HLS_PATH; - } - - SrsConfDirective* conf = hls->get("hls_path"); - - if (!conf) { - return SRS_CONF_DEFAULT_HLS_PATH; - } - - return conf->arg0(); -} - -double SrsConfig::get_hls_fragment(string vhost) -{ - SrsConfDirective* hls = get_hls(vhost); - - if (!hls) { - return SRS_CONF_DEFAULT_HLS_FRAGMENT; - } - - SrsConfDirective* conf = hls->get("hls_fragment"); - - if (!conf) { - return SRS_CONF_DEFAULT_HLS_FRAGMENT; - } - - return ::atof(conf->arg0().c_str()); -} - -double SrsConfig::get_hls_window(string vhost) -{ - SrsConfDirective* hls = get_hls(vhost); - - if (!hls) { - return SRS_CONF_DEFAULT_HLS_WINDOW; - } - - SrsConfDirective* conf = hls->get("hls_window"); - - if (!conf) { - return SRS_CONF_DEFAULT_HLS_WINDOW; - } - - return ::atof(conf->arg0().c_str()); -} - -SrsConfDirective* SrsConfig::get_refer(string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("refer"); -} - -SrsConfDirective* SrsConfig::get_refer_play(string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("refer_play"); -} - -SrsConfDirective* SrsConfig::get_refer_publish(string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("refer_publish"); -} - -SrsConfDirective* SrsConfig::get_listen() -{ - return root->get("listen"); -} - -int SrsConfig::get_chunk_size(const std::string &vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return SRS_CONF_DEFAULT_CHUNK_SIZE; - } - - conf = conf->get("chunk_size"); - if (!conf) { - // vhost does not specify the chunk size, - // use the global instead. - conf = root->get("chunk_size"); - if (!conf) { - return SRS_CONF_DEFAULT_CHUNK_SIZE; - } - - return ::atoi(conf->arg0().c_str()); - } - - return ::atoi(conf->arg0().c_str()); -} - -int SrsConfig::get_pithy_print_publish() -{ - SrsConfDirective* pithy = root->get("pithy_print"); - if (!pithy) { - return SRS_STAGE_PUBLISH_USER_INTERVAL_MS; - } - - pithy = pithy->get("publish"); - if (!pithy) { - return SRS_STAGE_PUBLISH_USER_INTERVAL_MS; - } - - return ::atoi(pithy->arg0().c_str()); -} - -int SrsConfig::get_pithy_print_forwarder() -{ - SrsConfDirective* pithy = root->get("pithy_print"); - if (!pithy) { - return SRS_STAGE_FORWARDER_INTERVAL_MS; - } - - pithy = pithy->get("forwarder"); - if (!pithy) { - return SRS_STAGE_FORWARDER_INTERVAL_MS; - } - - return ::atoi(pithy->arg0().c_str()); -} - -int SrsConfig::get_pithy_print_hls() -{ - SrsConfDirective* pithy = root->get("pithy_print"); - if (!pithy) { - return SRS_STAGE_HLS_INTERVAL_MS; - } - - pithy = pithy->get("hls"); - if (!pithy) { - return SRS_STAGE_HLS_INTERVAL_MS; - } - - return ::atoi(pithy->arg0().c_str()); -} - -bool SrsConfig::get_bw_check_enabled(const string &vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return false; - } - - conf = conf->get("bandcheck"); - if (!conf) { - return false; - } - - conf = conf->get("enabled"); - if (!conf || conf->arg0() != "on") { - return false; - } - - return true; -} - -string SrsConfig::get_bw_check_key(const string &vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return ""; - } - - conf = conf->get("bandcheck"); - if (!conf) { - return ""; - } - - conf = conf->get("key"); - if (!conf) { - return ""; - } - - return conf->arg0(); -} - -int SrsConfig::get_bw_check_interval_ms(const string &vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return SRS_CONF_DEFAULT_BANDWIDTH_INTERVAL; - } - - conf = conf->get("bandcheck"); - if (!conf) { - return SRS_CONF_DEFAULT_BANDWIDTH_INTERVAL; - } - - conf = conf->get("interval_ms"); - if (!conf) { - return SRS_CONF_DEFAULT_BANDWIDTH_INTERVAL; - } - - return ::atoi(conf->arg0().c_str()) * 1000; -} - -int SrsConfig::get_bw_check_limit_kbps(const string &vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return SRS_CONF_DEFAULT_BANDWIDTH_LIMIT_KBPS; - } - - conf = conf->get("bandcheck"); - if (!conf) { - return SRS_CONF_DEFAULT_BANDWIDTH_LIMIT_KBPS; - } - - conf = conf->get("limit_kbps"); - if (!conf) { - return SRS_CONF_DEFAULT_BANDWIDTH_LIMIT_KBPS; - } - - return ::atoi(conf->arg0().c_str()); -} - -int SrsConfig::get_pithy_print_encoder() -{ - SrsConfDirective* pithy = root->get("encoder"); - if (!pithy) { - return SRS_STAGE_ENCODER_INTERVAL_MS; - } - - pithy = pithy->get("forwarder"); - if (!pithy) { - return SRS_STAGE_ENCODER_INTERVAL_MS; - } - - return ::atoi(pithy->arg0().c_str()); -} - -int SrsConfig::get_pithy_print_play() -{ - SrsConfDirective* pithy = root->get("pithy_print"); - if (!pithy) { - return SRS_STAGE_PLAY_USER_INTERVAL_MS; - } - - pithy = pithy->get("play"); - if (!pithy) { - return SRS_STAGE_PLAY_USER_INTERVAL_MS; - } - - return ::atoi(pithy->arg0().c_str()); -} - -bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b) -{ - // both NULL, equal. - if (!a && !b) { - return true; - } - - if (!a || !b) { - return false; - } - - if (a->name != b->name) { - return false; - } - - if (a->args.size() != b->args.size()) { - return false; - } - - for (int i = 0; i < (int)a->args.size(); i++) { - if (a->args.at(i) != b->args.at(i)) { - return false; - } - } - - if (a->directives.size() != b->directives.size()) { - return false; - } - - for (int i = 0; i < (int)a->directives.size(); i++) { - SrsConfDirective* a0 = a->at(i); - SrsConfDirective* b0 = b->at(i); - - if (!srs_directive_equals(a0, b0)) { - return false; - } - } - - return true; -} - +/* +The MIT License (MIT) + +Copyright (c) 2013-2014 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include +#include +#include +#include +// file operations. +#include +#include +#include +#include + +#include +#include +using namespace std; + +#include +#include +#include + +#define FILE_OFFSET(fd) lseek(fd, 0, SEEK_CUR) + +int64_t FILE_SIZE(int fd) +{ + int64_t pre = FILE_OFFSET(fd); + int64_t pos = lseek(fd, 0, SEEK_END); + lseek(fd, pre, SEEK_SET); + return pos; +} + +#define LF (char)0x0a +#define CR (char)0x0d + +bool is_common_space(char ch) +{ + return (ch == ' ' || ch == '\t' || ch == CR || ch == LF); +} + +class SrsFileBuffer +{ +private: + // last available position. + char* last; + // end of buffer. + char* end; + // start of buffer. + char* start; +public: + // current consumed position. + char* pos; + // current parsed line. + int line; + + SrsFileBuffer(); + virtual ~SrsFileBuffer(); + virtual int fullfill(const char* filename); + virtual bool empty(); +}; + +SrsFileBuffer::SrsFileBuffer() +{ + line = 0; + + pos = last = start = NULL; + end = start; +} + +SrsFileBuffer::~SrsFileBuffer() +{ + srs_freepa(start); +} + +int SrsFileBuffer::fullfill(const char* filename) +{ + int ret = ERROR_SUCCESS; + + int fd = -1; + int nread = 0; + int filesize = 0; + + if ((fd = ::open(filename, O_RDONLY, 0)) < 0) { + ret = ERROR_SYSTEM_CONFIG_INVALID; + srs_error("open conf file error. ret=%d", ret); + goto finish; + } + + if ((filesize = FILE_SIZE(fd) - FILE_OFFSET(fd)) <= 0) { + ret = ERROR_SYSTEM_CONFIG_EOF; + srs_error("read conf file error. ret=%d", ret); + goto finish; + } + + srs_freepa(start); + pos = last = start = new char[filesize]; + end = start + filesize; + + if ((nread = read(fd, start, filesize)) != filesize) { + ret = ERROR_SYSTEM_CONFIG_INVALID; + srs_error("read file read error. expect %d, actual %d bytes, ret=%d", + filesize, nread, ret); + goto finish; + } + + line = 1; + +finish: + if (fd > 0) { + ::close(fd); + } + + return ret; +} + +bool SrsFileBuffer::empty() +{ + return pos >= end; +} + +SrsConfDirective::SrsConfDirective() +{ +} + +SrsConfDirective::~SrsConfDirective() +{ + std::vector::iterator it; + for (it = directives.begin(); it != directives.end(); ++it) { + SrsConfDirective* directive = *it; + srs_freep(directive); + } + directives.clear(); +} + +string SrsConfDirective::arg0() +{ + if (args.size() > 0) { + return args.at(0); + } + + return ""; +} + +string SrsConfDirective::arg1() +{ + if (args.size() > 1) { + return args.at(1); + } + + return ""; +} + +string SrsConfDirective::arg2() +{ + if (args.size() > 2) { + return args.at(2); + } + + return ""; +} + +SrsConfDirective* SrsConfDirective::at(int index) +{ + return directives.at(index); +} + +SrsConfDirective* SrsConfDirective::get(string _name) +{ + std::vector::iterator it; + for (it = directives.begin(); it != directives.end(); ++it) { + SrsConfDirective* directive = *it; + if (directive->name == _name) { + return directive; + } + } + + return NULL; +} + +SrsConfDirective* SrsConfDirective::get(string _name, string _arg0) +{ + std::vector::iterator it; + for (it = directives.begin(); it != directives.end(); ++it) { + SrsConfDirective* directive = *it; + if (directive->name == _name && directive->arg0() == _arg0) { + return directive; + } + } + + return NULL; +} + +int SrsConfDirective::parse(const char* filename) +{ + int ret = ERROR_SUCCESS; + + SrsFileBuffer buffer; + + if ((ret = buffer.fullfill(filename)) != ERROR_SUCCESS) { + return ret; + } + + return parse_conf(&buffer, parse_file); +} + +// see: ngx_conf_parse +int SrsConfDirective::parse_conf(SrsFileBuffer* buffer, SrsDirectiveType type) +{ + int ret = ERROR_SUCCESS; + + while (true) { + std::vector args; + ret = read_token(buffer, args); + + /** + * ret maybe: + * ERROR_SYSTEM_CONFIG_INVALID error. + * ERROR_SYSTEM_CONFIG_DIRECTIVE directive terminated by ';' found + * ERROR_SYSTEM_CONFIG_BLOCK_START token terminated by '{' found + * ERROR_SYSTEM_CONFIG_BLOCK_END the '}' found + * ERROR_SYSTEM_CONFIG_EOF the config file is done + */ + if (ret == ERROR_SYSTEM_CONFIG_INVALID) { + return ret; + } + if (ret == ERROR_SYSTEM_CONFIG_BLOCK_END) { + if (type != parse_block) { + srs_error("line %d: unexpected \"}\"", buffer->line); + return ret; + } + return ERROR_SUCCESS; + } + if (ret == ERROR_SYSTEM_CONFIG_EOF) { + if (type == parse_block) { + srs_error("line %d: unexpected end of file, expecting \"}\"", buffer->line); + return ret; + } + return ERROR_SUCCESS; + } + + if (args.empty()) { + srs_error("line %d: empty directive.", buffer->line); + return ret; + } + + // build directive tree. + SrsConfDirective* directive = new SrsConfDirective(); + + directive->conf_line = buffer->line; + directive->name = args[0]; + args.erase(args.begin()); + directive->args.swap(args); + + directives.push_back(directive); + + if (ret == ERROR_SYSTEM_CONFIG_BLOCK_START) { + if ((ret = directive->parse_conf(buffer, parse_block)) != ERROR_SUCCESS) { + return ret; + } + } + } + + return ret; +} + +// see: ngx_conf_read_token +int SrsConfDirective::read_token(SrsFileBuffer* buffer, std::vector& args) +{ + int ret = ERROR_SUCCESS; + + char* pstart = buffer->pos; + int startline = buffer->line; + + bool sharp_comment = false; + + bool d_quoted = false; + bool s_quoted = false; + + bool need_space = false; + bool last_space = true; + + while (true) { + if (buffer->empty()) { + ret = ERROR_SYSTEM_CONFIG_EOF; + + if (!args.empty() || !last_space) { + srs_error("line %d: unexpected end of file, expecting ; or \"}\"", buffer->line); + return ERROR_SYSTEM_CONFIG_INVALID; + } + srs_error("end of file. ret=%d", ret); + + return ret; + } + + char ch = *buffer->pos++; + + if (ch == LF) { + buffer->line++; + sharp_comment = false; + } + + if (sharp_comment) { + continue; + } + + if (need_space) { + if (is_common_space(ch)) { + last_space = true; + need_space = false; + continue; + } + if (ch == ';') { + return ERROR_SYSTEM_CONFIG_DIRECTIVE; + } + if (ch == '{') { + return ERROR_SYSTEM_CONFIG_BLOCK_START; + } + srs_error("line %d: unexpected '%c'", buffer->line, ch); + return ERROR_SYSTEM_CONFIG_INVALID; + } + + // last charecter is space. + if (last_space) { + if (is_common_space(ch)) { + continue; + } + pstart = buffer->pos - 1; + startline = buffer->line; + switch (ch) { + case ';': + if (args.size() == 0) { + srs_error("line %d: unexpected ';'", buffer->line); + return ERROR_SYSTEM_CONFIG_INVALID; + } + return ERROR_SYSTEM_CONFIG_DIRECTIVE; + case '{': + if (args.size() == 0) { + srs_error("line %d: unexpected '{'", buffer->line); + return ERROR_SYSTEM_CONFIG_INVALID; + } + return ERROR_SYSTEM_CONFIG_BLOCK_START; + case '}': + if (args.size() != 0) { + srs_error("line %d: unexpected '}'", buffer->line); + return ERROR_SYSTEM_CONFIG_INVALID; + } + return ERROR_SYSTEM_CONFIG_BLOCK_END; + case '#': + sharp_comment = 1; + continue; + case '"': + pstart++; + d_quoted = true; + last_space = 0; + continue; + case '\'': + pstart++; + s_quoted = true; + last_space = 0; + continue; + default: + last_space = 0; + continue; + } + } else { + // last charecter is not space + bool found = false; + if (d_quoted) { + if (ch == '"') { + d_quoted = false; + need_space = true; + found = true; + } + } else if (s_quoted) { + if (ch == '\'') { + s_quoted = false; + need_space = true; + found = true; + } + } else if (is_common_space(ch) || ch == ';' || ch == '{') { + last_space = true; + found = 1; + } + + if (found) { + int len = buffer->pos - pstart; + char* word = new char[len]; + memcpy(word, pstart, len); + word[len - 1] = 0; + + string word_str = word; + if (!word_str.empty()) { + args.push_back(word_str); + } + srs_freepa(word); + + if (ch == ';') { + return ERROR_SYSTEM_CONFIG_DIRECTIVE; + } + if (ch == '{') { + return ERROR_SYSTEM_CONFIG_BLOCK_START; + } + } + } + } + + return ret; +} + +SrsConfig* config = new SrsConfig(); + +SrsConfig::SrsConfig() +{ + show_help = false; + show_version = false; + + root = new SrsConfDirective(); + root->conf_line = 0; + root->name = "root"; +} + +SrsConfig::~SrsConfig() +{ + srs_freep(root); +} + +int SrsConfig::reload() +{ + int ret = ERROR_SUCCESS; + + SrsConfig conf; + if ((ret = conf.parse_file(config_file.c_str())) != ERROR_SUCCESS) { + srs_error("config reloader parse file failed. ret=%d", ret); + return ret; + } + srs_info("config reloader parse file success."); + + // store current root to old_root, + // and reap the root from conf to current root. + SrsConfDirective* old_root = root; + SrsAutoFree(SrsConfDirective, old_root, false); + + root = conf.root; + conf.root = NULL; + + // merge config. + std::vector::iterator it; + + // merge config: listen + if (!srs_directive_equals(root->get("listen"), old_root->get("listen"))) { + for (it = subscribes.begin(); it != subscribes.end(); ++it) { + ISrsReloadHandler* subscribe = *it; + if ((ret = subscribe->on_reload_listen()) != ERROR_SUCCESS) { + srs_error("notify subscribes reload listen failed. ret=%d", ret); + return ret; + } + } + srs_trace("reload listen success."); + } + + // merge config: pithy_print + if (!srs_directive_equals(root->get("pithy_print"), old_root->get("pithy_print"))) { + for (it = subscribes.begin(); it != subscribes.end(); ++it) { + ISrsReloadHandler* subscribe = *it; + if ((ret = subscribe->on_reload_pithy_print()) != ERROR_SUCCESS) { + srs_error("notify subscribes pithy_print listen failed. ret=%d", ret); + return ret; + } + } + srs_trace("reload pithy_print success."); + } + + // merge config: vhost added, directly supported. + + // merge config: vhost removed/disabled/modified. + for (int i = 0; i < (int)old_root->directives.size(); i++) { + SrsConfDirective* old_vhost = old_root->at(i); + // only process vhost directives. + if (old_vhost->name != "vhost") { + continue; + } + + std::string vhost = old_vhost->arg0(); + + SrsConfDirective* new_vhost = root->get("vhost", vhost); + // ignore if absolutely equal + if (new_vhost && srs_directive_equals(old_vhost, new_vhost)) { + srs_trace("vhost %s absolutely equal, ignore.", vhost.c_str()); + continue; + } + // ignore if enable the new vhost when old vhost is disabled. + if (get_vhost_enabled(new_vhost) && !get_vhost_enabled(old_vhost)) { + srs_trace("vhost %s disabled=>enabled, ignore.", vhost.c_str()); + continue; + } + // ignore if both old and new vhost are disabled. + if (!get_vhost_enabled(new_vhost) && !get_vhost_enabled(old_vhost)) { + srs_trace("vhost %s disabled=>disabled, ignore.", vhost.c_str()); + continue; + } + + // merge config: vhost removed/disabled. + if (!get_vhost_enabled(new_vhost) && get_vhost_enabled(old_vhost)) { + srs_trace("vhost %s disabled, reload it.", vhost.c_str()); + for (it = subscribes.begin(); it != subscribes.end(); ++it) { + ISrsReloadHandler* subscribe = *it; + if ((ret = subscribe->on_reload_vhost_removed(vhost)) != ERROR_SUCCESS) { + srs_error("notify subscribes pithy_print remove " + "vhost %s failed. ret=%d", vhost.c_str(), ret); + return ret; + } + } + srs_trace("reload remove vhost %s success.", vhost.c_str()); + } + + // merge config: vhost modified. + srs_trace("vhost %s modified, reload its detail.", vhost.c_str()); + if (get_vhost_enabled(new_vhost) && get_vhost_enabled(old_vhost)) { + // gop_cache + if (!srs_directive_equals(new_vhost->get("gop_cache"), old_vhost->get("gop_cache"))) { + for (it = subscribes.begin(); it != subscribes.end(); ++it) { + ISrsReloadHandler* subscribe = *it; + if ((ret = subscribe->on_reload_gop_cache(vhost)) != ERROR_SUCCESS) { + srs_error("vhost %s notify subscribes gop_cache failed. ret=%d", vhost.c_str(), ret); + return ret; + } + } + srs_trace("vhost %s reload gop_cache success.", vhost.c_str()); + } + // queue_length + if (!srs_directive_equals(new_vhost->get("queue_length"), old_vhost->get("queue_length"))) { + for (it = subscribes.begin(); it != subscribes.end(); ++it) { + ISrsReloadHandler* subscribe = *it; + if ((ret = subscribe->on_reload_queue_length(vhost)) != ERROR_SUCCESS) { + srs_error("vhost %s notify subscribes queue_length failed. ret=%d", vhost.c_str(), ret); + return ret; + } + } + srs_trace("vhost %s reload queue_length success.", vhost.c_str()); + } + // forward + if (!srs_directive_equals(new_vhost->get("forward"), old_vhost->get("forward"))) { + for (it = subscribes.begin(); it != subscribes.end(); ++it) { + ISrsReloadHandler* subscribe = *it; + if ((ret = subscribe->on_reload_forward(vhost)) != ERROR_SUCCESS) { + srs_error("vhost %s notify subscribes forward failed. ret=%d", vhost.c_str(), ret); + return ret; + } + } + srs_trace("vhost %s reload forward success.", vhost.c_str()); + } + // hls + if (!srs_directive_equals(new_vhost->get("hls"), old_vhost->get("hls"))) { + for (it = subscribes.begin(); it != subscribes.end(); ++it) { + ISrsReloadHandler* subscribe = *it; + if ((ret = subscribe->on_reload_hls(vhost)) != ERROR_SUCCESS) { + srs_error("vhost %s notify subscribes hls failed. ret=%d", vhost.c_str(), ret); + return ret; + } + } + srs_trace("vhost %s reload hls success.", vhost.c_str()); + } + // transcode + if (!srs_directive_equals(new_vhost->get("transcode"), old_vhost->get("transcode"))) { + for (it = subscribes.begin(); it != subscribes.end(); ++it) { + ISrsReloadHandler* subscribe = *it; + if ((ret = subscribe->on_reload_transcode(vhost)) != ERROR_SUCCESS) { + srs_error("vhost %s notify subscribes transcode failed. ret=%d", vhost.c_str(), ret); + return ret; + } + } + srs_trace("vhost %s reload transcode success.", vhost.c_str()); + } + // TODO: suppor reload hls/forward/ffmpeg/http + continue; + } + srs_warn("invalid reload path, enabled old: %d, new: %d", + get_vhost_enabled(old_vhost), get_vhost_enabled(new_vhost)); + } + + return ret; +} + +void SrsConfig::subscribe(ISrsReloadHandler* handler) +{ + std::vector::iterator it; + + it = std::find(subscribes.begin(), subscribes.end(), handler); + if (it != subscribes.end()) { + return; + } + + subscribes.push_back(handler); +} + +void SrsConfig::unsubscribe(ISrsReloadHandler* handler) +{ + std::vector::iterator it; + + it = std::find(subscribes.begin(), subscribes.end(), handler); + if (it == subscribes.end()) { + return; + } + + subscribes.erase(it); +} + +// see: ngx_get_options +int SrsConfig::parse_options(int argc, char** argv) +{ + int ret = ERROR_SUCCESS; + + for (int i = 1; i < argc; i++) { + if ((ret = parse_argv(i, argv)) != ERROR_SUCCESS) { + return ret; + } + } + + if (show_help) { + print_help(argv); + } + + if (show_version) { + printf("%s\n", RTMP_SIG_SRS_VERSION); + } + + if (show_help || show_version) { + exit(0); + } + + if (config_file.empty()) { + ret = ERROR_SYSTEM_CONFIG_INVALID; + srs_error("config file not specified, see help: %s -h, ret=%d", argv[0], ret); + return ret; + } + + return parse_file(config_file.c_str()); +} + +int SrsConfig::parse_file(const char* filename) +{ + int ret = ERROR_SUCCESS; + + config_file = filename; + + if (config_file.empty()) { + return ERROR_SYSTEM_CONFIG_INVALID; + } + + if ((ret = root->parse(config_file.c_str())) != ERROR_SUCCESS) { + return ret; + } + + SrsConfDirective* conf = NULL; + if ((conf = get_listen()) == NULL || conf->args.size() == 0) { + ret = ERROR_SYSTEM_CONFIG_INVALID; + srs_error("line %d: conf error, " + "directive \"listen\" is empty, ret=%d", (conf? conf->conf_line:0), ret); + return ret; + } + + // TODO: check the hls. + // TODO: check forward. + // TODO: check ffmpeg. + // TODO: check http. + + return ret; +} + +int SrsConfig::parse_argv(int& i, char** argv) +{ + int ret = ERROR_SUCCESS; + + char* p = argv[i]; + + if (*p++ != '-') { + ret = ERROR_SYSTEM_CONFIG_INVALID; + srs_error("invalid options(index=%d, value=%s), " + "must starts with -, see help: %s -h, ret=%d", i, argv[i], argv[0], ret); + return ret; + } + + while (*p) { + switch (*p++) { + case '?': + case 'h': + show_help = true; + break; + case 'v': + case 'V': + show_version = true; + break; + case 'c': + if (*p) { + config_file = p; + return ret; + } + if (argv[++i]) { + config_file = argv[i]; + return ret; + } + ret = ERROR_SYSTEM_CONFIG_INVALID; + srs_error("option \"-c\" requires parameter, ret=%d", ret); + return ret; + default: + ret = ERROR_SYSTEM_CONFIG_INVALID; + srs_error("invalid option: \"%c\", see help: %s -h, ret=%d", *(p - 1), argv[0], ret); + return ret; + } + } + + return ret; +} + +void SrsConfig::print_help(char** argv) +{ + printf( + RTMP_SIG_SRS_NAME" "RTMP_SIG_SRS_VERSION" "RTMP_SIG_SRS_COPYRIGHT"\n" + "Primary Authors: "RTMP_SIG_SRS_PRIMARY_AUTHROS"\n" + "Build: "SRS_BUILD_DATE" Configuration: "SRS_CONFIGURE"\n" + "Usage: %s [-h?vV] [-c ]\n" + "\n" + "Options:\n" + " -?-h : show help\n" + " -v-V : show version and exit\n" + " -c filename : set configuration file\n" + "\n" + RTMP_SIG_SRS_WEB"\n" + RTMP_SIG_SRS_URL"\n" + "Email: "RTMP_SIG_SRS_EMAIL"\n" + "\n", + argv[0]); +} + +SrsConfDirective* SrsConfig::get_vhost(string vhost) +{ + srs_assert(root); + + for (int i = 0; i < (int)root->directives.size(); i++) { + SrsConfDirective* conf = root->at(i); + + if (conf->name != "vhost") { + continue; + } + + if (conf->arg0() == vhost) { + return conf; + } + } + + if (vhost != RTMP_VHOST_DEFAULT) { + return get_vhost(RTMP_VHOST_DEFAULT); + } + + return NULL; +} + +SrsConfDirective* SrsConfig::get_vhost_on_connect(string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + conf = conf->get("http_hooks"); + if (!conf) { + return NULL; + } + + SrsConfDirective* enabled = conf->get("enabled"); + if (!enabled || enabled->arg0() != "on") { + return NULL; + } + + return conf->get("on_connect"); +} + +SrsConfDirective* SrsConfig::get_vhost_on_close(string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + conf = conf->get("http_hooks"); + if (!conf) { + return NULL; + } + + SrsConfDirective* enabled = conf->get("enabled"); + if (!enabled || enabled->arg0() != "on") { + return NULL; + } + + return conf->get("on_close"); +} + +SrsConfDirective* SrsConfig::get_vhost_on_publish(string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + conf = conf->get("http_hooks"); + if (!conf) { + return NULL; + } + + SrsConfDirective* enabled = conf->get("enabled"); + if (!enabled || enabled->arg0() != "on") { + return NULL; + } + + return conf->get("on_publish"); +} + +SrsConfDirective* SrsConfig::get_vhost_on_unpublish(string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + conf = conf->get("http_hooks"); + if (!conf) { + return NULL; + } + + SrsConfDirective* enabled = conf->get("enabled"); + if (!enabled || enabled->arg0() != "on") { + return NULL; + } + + return conf->get("on_unpublish"); +} + +SrsConfDirective* SrsConfig::get_vhost_on_play(string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + conf = conf->get("http_hooks"); + if (!conf) { + return NULL; + } + + SrsConfDirective* enabled = conf->get("enabled"); + if (!enabled || enabled->arg0() != "on") { + return NULL; + } + + return conf->get("on_play"); +} + +SrsConfDirective* SrsConfig::get_vhost_on_stop(string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + conf = conf->get("http_hooks"); + if (!conf) { + return NULL; + } + + SrsConfDirective* enabled = conf->get("enabled"); + if (!enabled || enabled->arg0() != "on") { + return NULL; + } + + return conf->get("on_stop"); +} + +bool SrsConfig::get_vhost_enabled(string vhost) +{ + SrsConfDirective* vhost_conf = get_vhost(vhost); + + return get_vhost_enabled(vhost_conf); +} + +bool SrsConfig::get_vhost_enabled(SrsConfDirective* vhost) +{ + if (!vhost) { + return false; + } + + SrsConfDirective* conf = vhost->get("enabled"); + if (!conf) { + return true; + } + + if (conf->arg0() == "off") { + return false; + } + + return true; +} + +SrsConfDirective* SrsConfig::get_transcode(string vhost, string scope) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + SrsConfDirective* transcode = conf->get("transcode"); + if (!transcode) { + return NULL; + } + + if (transcode->arg0() == scope) { + return transcode; + } + + return NULL; +} + +bool SrsConfig::get_transcode_enabled(SrsConfDirective* transcode) +{ + if (!transcode) { + return false; + } + + SrsConfDirective* conf = transcode->get("enabled"); + if (!conf || conf->arg0() != "on") { + return false; + } + + return true; +} + +string SrsConfig::get_transcode_ffmpeg(SrsConfDirective* transcode) +{ + if (!transcode) { + return ""; + } + + SrsConfDirective* conf = transcode->get("ffmpeg"); + if (!conf) { + return ""; + } + + return conf->arg0(); +} + +void SrsConfig::get_transcode_engines(SrsConfDirective* transcode, std::vector& engines) +{ + if (!transcode) { + return; + } + + for (int i = 0; i < (int)transcode->directives.size(); i++) { + SrsConfDirective* conf = transcode->directives[i]; + + if (conf->name == "engine") { + engines.push_back(conf); + } + } + + return; +} + +bool SrsConfig::get_engine_enabled(SrsConfDirective* engine) +{ + if (!engine) { + return false; + } + + SrsConfDirective* conf = engine->get("enabled"); + if (!conf || conf->arg0() != "on") { + return false; + } + + return true; +} + +string SrsConfig::get_engine_vcodec(SrsConfDirective* engine) +{ + if (!engine) { + return ""; + } + + SrsConfDirective* conf = engine->get("vcodec"); + if (!conf) { + return ""; + } + + return conf->arg0(); +} + +int SrsConfig::get_engine_vbitrate(SrsConfDirective* engine) +{ + if (!engine) { + return 0; + } + + SrsConfDirective* conf = engine->get("vbitrate"); + if (!conf) { + return 0; + } + + return ::atoi(conf->arg0().c_str()); +} + +double SrsConfig::get_engine_vfps(SrsConfDirective* engine) +{ + if (!engine) { + return 0; + } + + SrsConfDirective* conf = engine->get("vfps"); + if (!conf) { + return 0; + } + + return ::atof(conf->arg0().c_str()); +} + +int SrsConfig::get_engine_vwidth(SrsConfDirective* engine) +{ + if (!engine) { + return 0; + } + + SrsConfDirective* conf = engine->get("vwidth"); + if (!conf) { + return 0; + } + + return ::atoi(conf->arg0().c_str()); +} + +int SrsConfig::get_engine_vheight(SrsConfDirective* engine) +{ + if (!engine) { + return 0; + } + + SrsConfDirective* conf = engine->get("vheight"); + if (!conf) { + return 0; + } + + return ::atoi(conf->arg0().c_str()); +} + +int SrsConfig::get_engine_vthreads(SrsConfDirective* engine) +{ + if (!engine) { + return 0; + } + + SrsConfDirective* conf = engine->get("vthreads"); + if (!conf) { + return 0; + } + + return ::atoi(conf->arg0().c_str()); +} + +string SrsConfig::get_engine_vprofile(SrsConfDirective* engine) +{ + if (!engine) { + return ""; + } + + SrsConfDirective* conf = engine->get("vprofile"); + if (!conf) { + return ""; + } + + return conf->arg0(); +} + +string SrsConfig::get_engine_vpreset(SrsConfDirective* engine) +{ + if (!engine) { + return ""; + } + + SrsConfDirective* conf = engine->get("vpreset"); + if (!conf) { + return ""; + } + + return conf->arg0(); +} + +void SrsConfig::get_engine_vparams(SrsConfDirective* engine, std::vector& vparams) +{ + if (!engine) { + return; + } + + SrsConfDirective* conf = engine->get("vparams"); + if (!conf) { + return; + } + + for (int i = 0; i < (int)conf->directives.size(); i++) { + SrsConfDirective* p = conf->directives[i]; + if (!p) { + continue; + } + + vparams.push_back("-" + p->name); + vparams.push_back(p->arg0()); + } +} + +void SrsConfig::get_engine_vfilter(SrsConfDirective* engine, std::vector& vfilter) +{ + if (!engine) { + return; + } + + SrsConfDirective* conf = engine->get("vfilter"); + if (!conf) { + return; + } + + for (int i = 0; i < (int)conf->directives.size(); i++) { + SrsConfDirective* p = conf->directives[i]; + if (!p) { + continue; + } + + vfilter.push_back("-" + p->name); + vfilter.push_back(p->arg0()); + } +} + +string SrsConfig::get_engine_acodec(SrsConfDirective* engine) +{ + if (!engine) { + return ""; + } + + SrsConfDirective* conf = engine->get("acodec"); + if (!conf) { + return ""; + } + + return conf->arg0(); +} + +int SrsConfig::get_engine_abitrate(SrsConfDirective* engine) +{ + if (!engine) { + return 0; + } + + SrsConfDirective* conf = engine->get("abitrate"); + if (!conf) { + return 0; + } + + return ::atoi(conf->arg0().c_str()); +} + +int SrsConfig::get_engine_asample_rate(SrsConfDirective* engine) +{ + if (!engine) { + return 0; + } + + SrsConfDirective* conf = engine->get("asample_rate"); + if (!conf) { + return 0; + } + + return ::atoi(conf->arg0().c_str()); +} + +int SrsConfig::get_engine_achannels(SrsConfDirective* engine) +{ + if (!engine) { + return 0; + } + + SrsConfDirective* conf = engine->get("achannels"); + if (!conf) { + return 0; + } + + return ::atoi(conf->arg0().c_str()); +} + +void SrsConfig::get_engine_aparams(SrsConfDirective* engine, std::vector& aparams) +{ + if (!engine) { + return; + } + + SrsConfDirective* conf = engine->get("aparams"); + if (!conf) { + return; + } + + for (int i = 0; i < (int)conf->directives.size(); i++) { + SrsConfDirective* p = conf->directives[i]; + if (!p) { + continue; + } + + aparams.push_back("-" + p->name); + aparams.push_back(p->arg0()); + } +} + +string SrsConfig::get_engine_output(SrsConfDirective* engine) +{ + if (!engine) { + return ""; + } + + SrsConfDirective* conf = engine->get("output"); + if (!conf) { + return ""; + } + + return conf->arg0(); +} + +string SrsConfig::get_log_dir() +{ + srs_assert(root); + + SrsConfDirective* conf = root->get("log_dir"); + if (!conf || conf->arg0().empty()) { + return "./objs/logs"; + } + + return conf->arg0(); +} + +int SrsConfig::get_max_connections() +{ + srs_assert(root); + + SrsConfDirective* conf = root->get("max_connections"); + if (!conf || conf->arg0().empty()) { + return 2000; + } + + return ::atoi(conf->arg0().c_str()); +} + +bool SrsConfig::get_gop_cache(string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return true; + } + + conf = conf->get("gop_cache"); + if (conf && conf->arg0() == "off") { + return false; + } + + return true; +} + +double SrsConfig::get_queue_length(string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return SRS_CONF_DEFAULT_QUEUE_LENGTH; + } + + conf = conf->get("queue_length"); + if (!conf || conf->arg0().empty()) { + return SRS_CONF_DEFAULT_QUEUE_LENGTH; + } + + return ::atoi(conf->arg0().c_str()); +} + +SrsConfDirective* SrsConfig::get_forward(string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("forward"); +} + +SrsConfDirective* SrsConfig::get_hls(string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("hls"); +} + +bool SrsConfig::get_hls_enabled(string vhost) +{ + SrsConfDirective* hls = get_hls(vhost); + + if (!hls) { + return false; + } + + SrsConfDirective* conf = hls->get("enabled"); + + if (!conf) { + return false; + } + + if (conf->arg0() == "on") { + return true; + } + + return false; +} + +string SrsConfig::get_hls_path(string vhost) +{ + SrsConfDirective* hls = get_hls(vhost); + + if (!hls) { + return SRS_CONF_DEFAULT_HLS_PATH; + } + + SrsConfDirective* conf = hls->get("hls_path"); + + if (!conf) { + return SRS_CONF_DEFAULT_HLS_PATH; + } + + return conf->arg0(); +} + +double SrsConfig::get_hls_fragment(string vhost) +{ + SrsConfDirective* hls = get_hls(vhost); + + if (!hls) { + return SRS_CONF_DEFAULT_HLS_FRAGMENT; + } + + SrsConfDirective* conf = hls->get("hls_fragment"); + + if (!conf) { + return SRS_CONF_DEFAULT_HLS_FRAGMENT; + } + + return ::atof(conf->arg0().c_str()); +} + +double SrsConfig::get_hls_window(string vhost) +{ + SrsConfDirective* hls = get_hls(vhost); + + if (!hls) { + return SRS_CONF_DEFAULT_HLS_WINDOW; + } + + SrsConfDirective* conf = hls->get("hls_window"); + + if (!conf) { + return SRS_CONF_DEFAULT_HLS_WINDOW; + } + + return ::atof(conf->arg0().c_str()); +} + +SrsConfDirective* SrsConfig::get_refer(string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("refer"); +} + +SrsConfDirective* SrsConfig::get_refer_play(string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("refer_play"); +} + +SrsConfDirective* SrsConfig::get_refer_publish(string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("refer_publish"); +} + +SrsConfDirective* SrsConfig::get_listen() +{ + return root->get("listen"); +} + +int SrsConfig::get_chunk_size(const std::string &vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return SRS_CONF_DEFAULT_CHUNK_SIZE; + } + + conf = conf->get("chunk_size"); + if (!conf) { + // vhost does not specify the chunk size, + // use the global instead. + conf = root->get("chunk_size"); + if (!conf) { + return SRS_CONF_DEFAULT_CHUNK_SIZE; + } + + return ::atoi(conf->arg0().c_str()); + } + + return ::atoi(conf->arg0().c_str()); +} + +int SrsConfig::get_pithy_print_publish() +{ + SrsConfDirective* pithy = root->get("pithy_print"); + if (!pithy) { + return SRS_STAGE_PUBLISH_USER_INTERVAL_MS; + } + + pithy = pithy->get("publish"); + if (!pithy) { + return SRS_STAGE_PUBLISH_USER_INTERVAL_MS; + } + + return ::atoi(pithy->arg0().c_str()); +} + +int SrsConfig::get_pithy_print_forwarder() +{ + SrsConfDirective* pithy = root->get("pithy_print"); + if (!pithy) { + return SRS_STAGE_FORWARDER_INTERVAL_MS; + } + + pithy = pithy->get("forwarder"); + if (!pithy) { + return SRS_STAGE_FORWARDER_INTERVAL_MS; + } + + return ::atoi(pithy->arg0().c_str()); +} + +int SrsConfig::get_pithy_print_hls() +{ + SrsConfDirective* pithy = root->get("pithy_print"); + if (!pithy) { + return SRS_STAGE_HLS_INTERVAL_MS; + } + + pithy = pithy->get("hls"); + if (!pithy) { + return SRS_STAGE_HLS_INTERVAL_MS; + } + + return ::atoi(pithy->arg0().c_str()); +} + +bool SrsConfig::get_bw_check_enabled(const string &vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return false; + } + + conf = conf->get("bandcheck"); + if (!conf) { + return false; + } + + conf = conf->get("enabled"); + if (!conf || conf->arg0() != "on") { + return false; + } + + return true; +} + +string SrsConfig::get_bw_check_key(const string &vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return ""; + } + + conf = conf->get("bandcheck"); + if (!conf) { + return ""; + } + + conf = conf->get("key"); + if (!conf) { + return ""; + } + + return conf->arg0(); +} + +int SrsConfig::get_bw_check_interval_ms(const string &vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return SRS_CONF_DEFAULT_BANDWIDTH_INTERVAL; + } + + conf = conf->get("bandcheck"); + if (!conf) { + return SRS_CONF_DEFAULT_BANDWIDTH_INTERVAL; + } + + conf = conf->get("interval_ms"); + if (!conf) { + return SRS_CONF_DEFAULT_BANDWIDTH_INTERVAL; + } + + return ::atoi(conf->arg0().c_str()) * 1000; +} + +int SrsConfig::get_bw_check_limit_kbps(const string &vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return SRS_CONF_DEFAULT_BANDWIDTH_LIMIT_KBPS; + } + + conf = conf->get("bandcheck"); + if (!conf) { + return SRS_CONF_DEFAULT_BANDWIDTH_LIMIT_KBPS; + } + + conf = conf->get("limit_kbps"); + if (!conf) { + return SRS_CONF_DEFAULT_BANDWIDTH_LIMIT_KBPS; + } + + return ::atoi(conf->arg0().c_str()); +} + +int SrsConfig::get_pithy_print_encoder() +{ + SrsConfDirective* pithy = root->get("encoder"); + if (!pithy) { + return SRS_STAGE_ENCODER_INTERVAL_MS; + } + + pithy = pithy->get("forwarder"); + if (!pithy) { + return SRS_STAGE_ENCODER_INTERVAL_MS; + } + + return ::atoi(pithy->arg0().c_str()); +} + +int SrsConfig::get_pithy_print_play() +{ + SrsConfDirective* pithy = root->get("pithy_print"); + if (!pithy) { + return SRS_STAGE_PLAY_USER_INTERVAL_MS; + } + + pithy = pithy->get("play"); + if (!pithy) { + return SRS_STAGE_PLAY_USER_INTERVAL_MS; + } + + return ::atoi(pithy->arg0().c_str()); +} + +bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b) +{ + // both NULL, equal. + if (!a && !b) { + return true; + } + + if (!a || !b) { + return false; + } + + if (a->name != b->name) { + return false; + } + + if (a->args.size() != b->args.size()) { + return false; + } + + for (int i = 0; i < (int)a->args.size(); i++) { + if (a->args.at(i) != b->args.at(i)) { + return false; + } + } + + if (a->directives.size() != b->directives.size()) { + return false; + } + + for (int i = 0; i < (int)a->directives.size(); i++) { + SrsConfDirective* a0 = a->at(i); + SrsConfDirective* b0 = b->at(i); + + if (!srs_directive_equals(a0, b0)) { + return false; + } + } + + return true; +} + diff --git a/trunk/src/core/srs_core_rtmp.cpp b/trunk/src/core/srs_core_rtmp.cpp old mode 100644 new mode 100755 index 331b41b09..11ead12ef --- a/trunk/src/core/srs_core_rtmp.cpp +++ b/trunk/src/core/srs_core_rtmp.cpp @@ -1,1187 +1,1187 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013-2014 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace std; - -/** -* 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" -// status error -#define StatusLevelError "error" -// code value -#define StatusCodeConnectSuccess "NetConnection.Connect.Success" -#define StatusCodeConnectRejected "NetConnection.Connect.Rejected" -#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() -{ -} - -SrsRequest* SrsRequest::copy() -{ - SrsRequest* cp = new SrsRequest(); - - cp->app = app; - cp->objectEncoding = objectEncoding; - cp->pageUrl = pageUrl; - cp->port = port; - cp->schema = schema; - cp->stream = stream; - cp->swfUrl = swfUrl; - cp->tcUrl = tcUrl; - cp->vhost = vhost; - - return cp; -} - -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); - strip(); - - // 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; -} - -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(string& str, 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); - - skt.set_recv_timeout(protocol->get_recv_timeout()); - skt.set_send_timeout(protocol->get_send_timeout()); - - 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(string app, 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(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(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_send_timeout() -{ - return protocol->get_send_timeout(); -} - -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); - - skt.set_recv_timeout(protocol->get_recv_timeout()); - skt.set_send_timeout(protocol->get_send_timeout()); - - 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, const char* server_ip) -{ - 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_KEY" "RTMP_SIG_SRS_VERSION" ("RTMP_SIG_SRS_URL_SHORT")")); - 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)); - data->set("srs_contributor", new SrsAmf0String(RTMP_SIG_SRS_CONTRIBUTOR)); - - if (server_ip) { - data->set("srs_server_ip", new SrsAmf0String(server_ip)); - } - - 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; -} - -void SrsRtmp::response_connect_reject(SrsRequest *req, const char* desc) -{ - int ret = ERROR_SUCCESS; - - SrsConnectAppResPacket* pkt = new SrsConnectAppResPacket(); - pkt->command_name = "_error"; - pkt->props->set(StatusLevel, new SrsAmf0String(StatusLevelError)); - pkt->props->set(StatusCode, new SrsAmf0String(StatusCodeConnectRejected)); - pkt->props->set(StatusDescription, new SrsAmf0String(desc)); - //pkt->props->set("objectEncoding", new SrsAmf0Number(req->objectEncoding)); - - SrsCommonMessage* msg = (new SrsCommonMessage())->set_packet(pkt, 0); - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send connect app response rejected message failed. ret=%d", ret); - return; - } - srs_info("send connect app response rejected message success."); - - return; -} - -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, 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, 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, string& stream_name) -{ - int ret = ERROR_SUCCESS; - - type = SrsClientFlashPublish; - stream_name = req->stream_name; - - return ret; -} - +/* +The MIT License (MIT) + +Copyright (c) 2013-2014 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +/** +* 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" +// status error +#define StatusLevelError "error" +// code value +#define StatusCodeConnectSuccess "NetConnection.Connect.Success" +#define StatusCodeConnectRejected "NetConnection.Connect.Rejected" +#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() +{ +} + +SrsRequest* SrsRequest::copy() +{ + SrsRequest* cp = new SrsRequest(); + + cp->app = app; + cp->objectEncoding = objectEncoding; + cp->pageUrl = pageUrl; + cp->port = port; + cp->schema = schema; + cp->stream = stream; + cp->swfUrl = swfUrl; + cp->tcUrl = tcUrl; + cp->vhost = vhost; + + return cp; +} + +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); + strip(); + + // 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; +} + +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(string& str, 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); + + skt.set_recv_timeout(protocol->get_recv_timeout()); + skt.set_send_timeout(protocol->get_send_timeout()); + + 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(string app, 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(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(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_send_timeout() +{ + return protocol->get_send_timeout(); +} + +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); + + skt.set_recv_timeout(protocol->get_recv_timeout()); + skt.set_send_timeout(protocol->get_send_timeout()); + + 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, const char* server_ip) +{ + 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_KEY" "RTMP_SIG_SRS_VERSION" ("RTMP_SIG_SRS_URL_SHORT")")); + 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)); + data->set("srs_primary_authors", new SrsAmf0String(RTMP_SIG_SRS_PRIMARY_AUTHROS)); + + if (server_ip) { + data->set("srs_server_ip", new SrsAmf0String(server_ip)); + } + + 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; +} + +void SrsRtmp::response_connect_reject(SrsRequest *req, const char* desc) +{ + int ret = ERROR_SUCCESS; + + SrsConnectAppResPacket* pkt = new SrsConnectAppResPacket(); + pkt->command_name = "_error"; + pkt->props->set(StatusLevel, new SrsAmf0String(StatusLevelError)); + pkt->props->set(StatusCode, new SrsAmf0String(StatusCodeConnectRejected)); + pkt->props->set(StatusDescription, new SrsAmf0String(desc)); + //pkt->props->set("objectEncoding", new SrsAmf0Number(req->objectEncoding)); + + SrsCommonMessage* msg = (new SrsCommonMessage())->set_packet(pkt, 0); + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send connect app response rejected message failed. ret=%d", ret); + return; + } + srs_info("send connect app response rejected message success."); + + return; +} + +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, 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, 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, string& stream_name) +{ + int ret = ERROR_SUCCESS; + + type = SrsClientFlashPublish; + stream_name = req->stream_name; + + return ret; +} + diff --git a/trunk/src/core/srs_core_source.cpp b/trunk/src/core/srs_core_source.cpp index dc5bc1240..a86c0cc97 100644 --- a/trunk/src/core/srs_core_source.cpp +++ b/trunk/src/core/srs_core_source.cpp @@ -642,7 +642,7 @@ int SrsSource::on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* metadata 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)); + new SrsAmf0String(RTMP_SIG_SRS_PRIMARY_AUTHROS)); SrsAmf0Any* prop = NULL; if ((prop = metadata->metadata->get_property("audiosamplerate")) != NULL) { From dba732968280fb3d2752427e52088700e675dee9 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 1 Jan 2014 11:28:43 +0800 Subject: [PATCH 12/26] refine readme. --- README.md | 122 +- trunk/src/core/srs_core.hpp | 2 +- trunk/src/core/srs_core_config.cpp | 3333 ++++++++++++++-------------- trunk/src/core/srs_core_rtmp.cpp | 2373 ++++++++++---------- 4 files changed, 2915 insertions(+), 2915 deletions(-) mode change 100644 => 100755 README.md mode change 100755 => 100644 trunk/src/core/srs_core_config.cpp mode change 100755 => 100644 trunk/src/core/srs_core_rtmp.cpp diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 9c3920568..0ad829cb0 --- a/README.md +++ b/README.md @@ -21,52 +21,52 @@ people who have submitted patches, reported bugs, added translations, helped
step -1: get SRS
+Step -1: get SRS
 git clone https://github.com/winlinvip/simple-rtmp-server &&
 cd simple-rtmp-server/trunk
 
-step 0: build SRS system.
+Step 0: build SRS system.
 bash scripts/build.sh
 
-step 1: start SRS all demo features.
+Step 1: start SRS all demo features.
 bash scripts/run.sh
 
-step 2: SRS live show: [http://your-server-ip](http://your-server-ip)
-step 3: stop SRS demo
+Step 2: SRS live show: [http://your-server-ip](http://your-server-ip)
+Step 3: stop SRS demo
 bash scripts/stop.sh
 
### Usage(detail) -step 0: get SRS
+Step 0: get SRS
 git clone https://github.com/winlinvip/simple-rtmp-server &&
 cd simple-rtmp-server/trunk
 
-step 1: build SRS
+Step 1: build SRS
 ./configure --with-ssl --with-hls --with-ffmpeg --with-http && make
 
-step 2: start SRS
+Step 2: start SRS
 ./objs/srs -c conf/srs.conf
 
-step 3(optinal): start SRS listen at 19350 to forward to
+Step 3(optinal): start SRS listen at 19350 to forward to
 ./objs/srs -c conf/srs.19350.conf
 
-step 4(optinal): start nginx for HLS
+Step 4(optinal): start nginx for HLS
 sudo ./objs/nginx/sbin/nginx
 
-step 5(optinal): start http hooks for SRS callback
+Step 5(optinal): start http hooks for SRS callback
 python ./research/api-server/server.py 8085
 
-step 6: publish demo live stream
+Step 6: publish demo live stream
 FMS URL: rtmp://127.0.0.1/live?vhost=demo.srs.com
 Stream:  livestream
@@ -78,7 +78,7 @@ FFMPEG to publish the default demo stream:
         sleep 1; \
     done
 
-step 7: publish players live stream
+Step 7: publish players live stream
 FMS URL: rtmp://127.0.0.1/live?vhost=players
 Stream:  livestream
@@ -90,7 +90,7 @@ FFMPEG to publish the players demo stream:
         sleep 1; \
     done
 
-step 8: add server ip to client hosts as demo.
+Step 8: add server ip to client hosts as demo.
 # edit the folowing file:
 # linux: /etc/hosts
@@ -98,14 +98,14 @@ FFMPEG to publish the players demo stream:
 # where server ip is 192.168.2.111
 192.168.2.111 demo.srs.com
 
-step 9: play live stream.
+Step 9: play live stream.
 players: http://demo.srs.com/players
 rtmp url: rtmp://demo.srs.com/live/livestream
 m3u8 url: http://demo.srs.com/live/livestream.m3u8
 for android: http://demo.srs.com/live/livestream.html
 
-step 10(optinal): play live stream auto transcoded
+Step 10(optinal): play live stream auto transcoded
 rtmp url: rtmp://demo.srs.com/live/livestream_ld
 m3u8 url: http://demo.srs.com/live/livestream_ld.m3u8
@@ -114,7 +114,7 @@ rtmp url: rtmp://demo.srs.com/live/livestream_sd
 m3u8 url: http://demo.srs.com/live/livestream_sd.m3u8
 for android: http://demo.srs.com/live/livestream_sd.html
 
-step 11(optinal): play live stream auto forwarded, the hls dir change to /forward
+Step 11(optinal): play live stream auto forwarded, the hls dir change to /forward
 rtmp url: rtmp://demo.srs.com:19350/live/livestream
 m3u8 url: http://demo.srs.com/forward/live/livestream.m3u8
@@ -126,7 +126,7 @@ rtmp url: rtmp://demo.srs.com:19350/live/livestream_sd
 m3u8 url: http://demo.srs.com/forward/live/livestream_sd.m3u8
 for android: http://demo.srs.com/forward/live/livestream_sd.html
 
-step 12(optinal): modify the config and reload it (all features support reload)
+Step 12(optinal): modify the config and reload it (all features support reload)
 killall -1 srs
 
@@ -190,7 +190,7 @@ Bandwidth Test Workflow: | final(2)-----------------> | | <END> | -@see: class SrsBandwidth comments. +@See: class SrsBandwidth comments.
### System Requirements @@ -199,37 +199,38 @@ Supported operating systems and hardware: * All handware. ### Summary -1. simple: also stable enough.
-2. high-performance: single-thread, async socket, event/st-thread driven.
-3. no edge server, origin server only.
-4. no vod streaming, live streaming only.
-5. no multiple processes, single process only.
-6. support vhost, support \_\_defaultVhost\_\_.
-7. support adobe rtmp live streaming.
-8. support apple hls(m3u8) live streaming.
-9. support reload config to enable changes.
-10. support cache last gop for flash player to fast startup.
-11. support listen at multiple ports.
-12. support long time(>4.6hours) publish/play.
-13. high performace, 1800 connections(500kbps), 900Mbps, CPU 90.2%, 41MB
-14. support forward publish stream to build active-standby cluster.
-15. support broadcast by forward the stream to other servers(origin/edge).
-16. support live stream transcoding by ffmpeg.
-17. support live stream forward(acopy/vcopy) by ffmpeg.
-18. support ffmpeg filters(logo/overlay/crop), x264 params.
-19. support audio transcode only, speex/mp3 to aac
-20. support http callback api hooks(for authentication and injection).
-21. support bandwidth test api and flash client.
-22. player, publisher(encoder), and demo pages(jquery+bootstrap).
-23. demo video meeting or chat(SRS+cherrypy+jquery+bootstrap).
-24. [plan] support network based cli and json result.
-25. [plan] support adobe flash refer/token/swf verification.
-26. [plan] support adobe amf3 codec.
-27. [plan] support dvr(record live to vod file)
-28. [plan] support FMS edge protocol
-29. [plan] support encryption: RTMPE/RTMPS, HLS DRM
-30. [plan] support RTMPT, http to tranverse firewalls
-31. [plan] support file source, transcoding file to live stream
+1. Simple: also stable enough.
+2. High-performance: single-thread, async socket, event/st-thread driven.
+3. NO edge server, origin server only.
+4. NO vod streaming, live streaming only.
+5. NO multiple processes, single process only.
+6. Support vhost, support \_\_defaultVhost\_\_.
+7. Support adobe rtmp live streaming.
+8. Support apple hls(m3u8) live streaming.
+9. Support reload config to enable changes.
+10. Support cache last gop for flash player to fast startup.
+11. Support listen at multiple ports.
+12. Support long time(>4.6hours) publish/play.
+13. High performace, 1800 connections(500kbps), 900Mbps, CPU 90.2%, 41MB
+14. Support forward publish stream to build active-standby cluster.
+15. Support broadcast by forward the stream to other servers(origin/edge).
+16. Support live stream transcoding by ffmpeg.
+17. Support live stream forward(acopy/vcopy) by ffmpeg.
+18. Support ffmpeg filters(logo/overlay/crop), x264 params.
+19. Support audio transcode only, speex/mp3 to aac
+20. Support http callback api hooks(for authentication and injection).
+21. Support bandwidth test api and flash client.
+22. Player, publisher(encoder), and demo pages(jquery+bootstrap).
+23. Demo video meeting or chat(SRS+cherrypy+jquery+bootstrap).
+24. [dev] Full documents in wiki, in chineses.
+25. [plan] Support network based cli and json result.
+26. [plan] Support adobe flash refer/token/swf verification.
+27. [plan] Support adobe amf3 codec.
+28. [plan] Support dvr(record live to vod file)
+29. [plan] Support FMS edge protocol
+30. [plan] Support encryption: RTMPE/RTMPS, HLS DRM
+31. [plan] Support RTMPT, http to tranverse firewalls
+32. [plan] Support file source, transcoding file to live stream
### Performance 1. 300 connections, 150Mbps, 500kbps, CPU 18.8%, 5956KB. @@ -239,6 +240,7 @@ Supported operating systems and hardware: 5. 1500 connections, 750Mbps, 500kbps, CPU 81.9%, 28MB. 6. 1800 connections, 900Mbps, 500kbps, CPU 90.2%, 41MB.
+[winlin@dev6 srs]$ dstat
 ----total-cpu-usage---- -dsk/total- ---net/lo-- ---paging-- ---system--
 usr sys idl wai hiq siq| read  writ| recv  send|  in   out | int   csw 
  58   9  32   0   0   1|   0  4168k| 277M  277M|   0     0 |  29k   25k
@@ -257,16 +259,16 @@ usr sys idl wai hiq siq| read  writ| recv  send|  in   out | int   csw
 
### Releases -* 2013-12-25, [release v0.9](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.9), support bandwidth test, add player/encoder/chat demos. 20926 lines.
-* 2013-12-08, [release v0.8](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.8), support http hooks callback, update [st_load](https://github.com/winlinvip/st-load). 19186 lines.
-* 2013-12-03, [release v0.7](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.7), support live stream transcoding. 17605 lines.
-* 2013-11-29, [release v0.6](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.6), support forward stream to origin/edge. 16094 lines.
-* 2013-11-26, [release v0.5](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.5), support HLS(m3u8), fragment and window. 14449 lines.
-* 2013-11-10, [release v0.4](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.4), support reload config, pause, longtime publish/play. 12500 lines.
-* 2013-11-04, [release v0.3](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.3), support vhost, refer, gop cache, listen multiple ports. 11773 lines.
-* 2013-10-25, [release v0.2](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.2), support rtmp flash publish, h264, time jitter correct. 10125 lines.
-* 2013-10-23, [release v0.1](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.1), support rtmp FMLE/FFMPEG publish, vp6. 8287 lines.
-* 2013-10-17, created.
+* 2013-12-25, [Release v0.9](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.9), support bandwidth test, add player/encoder/chat demos. 20926 lines.
+* 2013-12-08, [Release v0.8](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.8), support http hooks callback, update [st_load](https://github.com/winlinvip/st-load). 19186 lines.
+* 2013-12-03, [Release v0.7](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.7), support live stream transcoding. 17605 lines.
+* 2013-11-29, [Release v0.6](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.6), support forward stream to origin/edge. 16094 lines.
+* 2013-11-26, [Release v0.5](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.5), support HLS(m3u8), fragment and window. 14449 lines.
+* 2013-11-10, [Release v0.4](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.4), support reload config, pause, longtime publish/play. 12500 lines.
+* 2013-11-04, [Release v0.3](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.3), support vhost, refer, gop cache, listen multiple ports. 11773 lines.
+* 2013-10-25, [Release v0.2](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.2), support rtmp flash publish, h264, time jitter correct. 10125 lines.
+* 2013-10-23, [Release v0.1](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.1), support rtmp FMLE/FFMPEG publish, vp6. 8287 lines.
+* 2013-10-17, Created.
### Compare * SRS v0.9: 20926 lines. player/encoder/chat demos. bandwidth test for encoder/CDN.
diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 8e6a8859d..b42d35e70 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -76,7 +76,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define RTMP_SIG_SRS_URL "https://"RTMP_SIG_SRS_URL_SHORT #define RTMP_SIG_SRS_URL_SHORT "github.com/winlinvip/simple-rtmp-server" #define RTMP_SIG_SRS_WEB "http://blog.csdn.net/win_lin" -#define RTMP_SIG_SRS_EMAIL "winterserver@126.com" +#define RTMP_SIG_SRS_EMAIL "winlin@vip.126.com" #define RTMP_SIG_SRS_LICENSE "The MIT License (MIT)" #define RTMP_SIG_SRS_COPYRIGHT "Copyright (c) 2013-2014 winlin" #define RTMP_SIG_SRS_PRIMARY_AUTHROS "winlin,wenjiegit" diff --git a/trunk/src/core/srs_core_config.cpp b/trunk/src/core/srs_core_config.cpp old mode 100755 new mode 100644 index f8223df68..812bb7062 --- a/trunk/src/core/srs_core_config.cpp +++ b/trunk/src/core/srs_core_config.cpp @@ -1,1667 +1,1666 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013-2014 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include - -#include -#include -#include -#include -// file operations. -#include -#include -#include -#include - -#include -#include -using namespace std; - -#include -#include -#include - -#define FILE_OFFSET(fd) lseek(fd, 0, SEEK_CUR) - -int64_t FILE_SIZE(int fd) -{ - int64_t pre = FILE_OFFSET(fd); - int64_t pos = lseek(fd, 0, SEEK_END); - lseek(fd, pre, SEEK_SET); - return pos; -} - -#define LF (char)0x0a -#define CR (char)0x0d - -bool is_common_space(char ch) -{ - return (ch == ' ' || ch == '\t' || ch == CR || ch == LF); -} - -class SrsFileBuffer -{ -private: - // last available position. - char* last; - // end of buffer. - char* end; - // start of buffer. - char* start; -public: - // current consumed position. - char* pos; - // current parsed line. - int line; - - SrsFileBuffer(); - virtual ~SrsFileBuffer(); - virtual int fullfill(const char* filename); - virtual bool empty(); -}; - -SrsFileBuffer::SrsFileBuffer() -{ - line = 0; - - pos = last = start = NULL; - end = start; -} - -SrsFileBuffer::~SrsFileBuffer() -{ - srs_freepa(start); -} - -int SrsFileBuffer::fullfill(const char* filename) -{ - int ret = ERROR_SUCCESS; - - int fd = -1; - int nread = 0; - int filesize = 0; - - if ((fd = ::open(filename, O_RDONLY, 0)) < 0) { - ret = ERROR_SYSTEM_CONFIG_INVALID; - srs_error("open conf file error. ret=%d", ret); - goto finish; - } - - if ((filesize = FILE_SIZE(fd) - FILE_OFFSET(fd)) <= 0) { - ret = ERROR_SYSTEM_CONFIG_EOF; - srs_error("read conf file error. ret=%d", ret); - goto finish; - } - - srs_freepa(start); - pos = last = start = new char[filesize]; - end = start + filesize; - - if ((nread = read(fd, start, filesize)) != filesize) { - ret = ERROR_SYSTEM_CONFIG_INVALID; - srs_error("read file read error. expect %d, actual %d bytes, ret=%d", - filesize, nread, ret); - goto finish; - } - - line = 1; - -finish: - if (fd > 0) { - ::close(fd); - } - - return ret; -} - -bool SrsFileBuffer::empty() -{ - return pos >= end; -} - -SrsConfDirective::SrsConfDirective() -{ -} - -SrsConfDirective::~SrsConfDirective() -{ - std::vector::iterator it; - for (it = directives.begin(); it != directives.end(); ++it) { - SrsConfDirective* directive = *it; - srs_freep(directive); - } - directives.clear(); -} - -string SrsConfDirective::arg0() -{ - if (args.size() > 0) { - return args.at(0); - } - - return ""; -} - -string SrsConfDirective::arg1() -{ - if (args.size() > 1) { - return args.at(1); - } - - return ""; -} - -string SrsConfDirective::arg2() -{ - if (args.size() > 2) { - return args.at(2); - } - - return ""; -} - -SrsConfDirective* SrsConfDirective::at(int index) -{ - return directives.at(index); -} - -SrsConfDirective* SrsConfDirective::get(string _name) -{ - std::vector::iterator it; - for (it = directives.begin(); it != directives.end(); ++it) { - SrsConfDirective* directive = *it; - if (directive->name == _name) { - return directive; - } - } - - return NULL; -} - -SrsConfDirective* SrsConfDirective::get(string _name, string _arg0) -{ - std::vector::iterator it; - for (it = directives.begin(); it != directives.end(); ++it) { - SrsConfDirective* directive = *it; - if (directive->name == _name && directive->arg0() == _arg0) { - return directive; - } - } - - return NULL; -} - -int SrsConfDirective::parse(const char* filename) -{ - int ret = ERROR_SUCCESS; - - SrsFileBuffer buffer; - - if ((ret = buffer.fullfill(filename)) != ERROR_SUCCESS) { - return ret; - } - - return parse_conf(&buffer, parse_file); -} - -// see: ngx_conf_parse -int SrsConfDirective::parse_conf(SrsFileBuffer* buffer, SrsDirectiveType type) -{ - int ret = ERROR_SUCCESS; - - while (true) { - std::vector args; - ret = read_token(buffer, args); - - /** - * ret maybe: - * ERROR_SYSTEM_CONFIG_INVALID error. - * ERROR_SYSTEM_CONFIG_DIRECTIVE directive terminated by ';' found - * ERROR_SYSTEM_CONFIG_BLOCK_START token terminated by '{' found - * ERROR_SYSTEM_CONFIG_BLOCK_END the '}' found - * ERROR_SYSTEM_CONFIG_EOF the config file is done - */ - if (ret == ERROR_SYSTEM_CONFIG_INVALID) { - return ret; - } - if (ret == ERROR_SYSTEM_CONFIG_BLOCK_END) { - if (type != parse_block) { - srs_error("line %d: unexpected \"}\"", buffer->line); - return ret; - } - return ERROR_SUCCESS; - } - if (ret == ERROR_SYSTEM_CONFIG_EOF) { - if (type == parse_block) { - srs_error("line %d: unexpected end of file, expecting \"}\"", buffer->line); - return ret; - } - return ERROR_SUCCESS; - } - - if (args.empty()) { - srs_error("line %d: empty directive.", buffer->line); - return ret; - } - - // build directive tree. - SrsConfDirective* directive = new SrsConfDirective(); - - directive->conf_line = buffer->line; - directive->name = args[0]; - args.erase(args.begin()); - directive->args.swap(args); - - directives.push_back(directive); - - if (ret == ERROR_SYSTEM_CONFIG_BLOCK_START) { - if ((ret = directive->parse_conf(buffer, parse_block)) != ERROR_SUCCESS) { - return ret; - } - } - } - - return ret; -} - -// see: ngx_conf_read_token -int SrsConfDirective::read_token(SrsFileBuffer* buffer, std::vector& args) -{ - int ret = ERROR_SUCCESS; - - char* pstart = buffer->pos; - int startline = buffer->line; - - bool sharp_comment = false; - - bool d_quoted = false; - bool s_quoted = false; - - bool need_space = false; - bool last_space = true; - - while (true) { - if (buffer->empty()) { - ret = ERROR_SYSTEM_CONFIG_EOF; - - if (!args.empty() || !last_space) { - srs_error("line %d: unexpected end of file, expecting ; or \"}\"", buffer->line); - return ERROR_SYSTEM_CONFIG_INVALID; - } - srs_error("end of file. ret=%d", ret); - - return ret; - } - - char ch = *buffer->pos++; - - if (ch == LF) { - buffer->line++; - sharp_comment = false; - } - - if (sharp_comment) { - continue; - } - - if (need_space) { - if (is_common_space(ch)) { - last_space = true; - need_space = false; - continue; - } - if (ch == ';') { - return ERROR_SYSTEM_CONFIG_DIRECTIVE; - } - if (ch == '{') { - return ERROR_SYSTEM_CONFIG_BLOCK_START; - } - srs_error("line %d: unexpected '%c'", buffer->line, ch); - return ERROR_SYSTEM_CONFIG_INVALID; - } - - // last charecter is space. - if (last_space) { - if (is_common_space(ch)) { - continue; - } - pstart = buffer->pos - 1; - startline = buffer->line; - switch (ch) { - case ';': - if (args.size() == 0) { - srs_error("line %d: unexpected ';'", buffer->line); - return ERROR_SYSTEM_CONFIG_INVALID; - } - return ERROR_SYSTEM_CONFIG_DIRECTIVE; - case '{': - if (args.size() == 0) { - srs_error("line %d: unexpected '{'", buffer->line); - return ERROR_SYSTEM_CONFIG_INVALID; - } - return ERROR_SYSTEM_CONFIG_BLOCK_START; - case '}': - if (args.size() != 0) { - srs_error("line %d: unexpected '}'", buffer->line); - return ERROR_SYSTEM_CONFIG_INVALID; - } - return ERROR_SYSTEM_CONFIG_BLOCK_END; - case '#': - sharp_comment = 1; - continue; - case '"': - pstart++; - d_quoted = true; - last_space = 0; - continue; - case '\'': - pstart++; - s_quoted = true; - last_space = 0; - continue; - default: - last_space = 0; - continue; - } - } else { - // last charecter is not space - bool found = false; - if (d_quoted) { - if (ch == '"') { - d_quoted = false; - need_space = true; - found = true; - } - } else if (s_quoted) { - if (ch == '\'') { - s_quoted = false; - need_space = true; - found = true; - } - } else if (is_common_space(ch) || ch == ';' || ch == '{') { - last_space = true; - found = 1; - } - - if (found) { - int len = buffer->pos - pstart; - char* word = new char[len]; - memcpy(word, pstart, len); - word[len - 1] = 0; - - string word_str = word; - if (!word_str.empty()) { - args.push_back(word_str); - } - srs_freepa(word); - - if (ch == ';') { - return ERROR_SYSTEM_CONFIG_DIRECTIVE; - } - if (ch == '{') { - return ERROR_SYSTEM_CONFIG_BLOCK_START; - } - } - } - } - - return ret; -} - -SrsConfig* config = new SrsConfig(); - -SrsConfig::SrsConfig() -{ - show_help = false; - show_version = false; - - root = new SrsConfDirective(); - root->conf_line = 0; - root->name = "root"; -} - -SrsConfig::~SrsConfig() -{ - srs_freep(root); -} - -int SrsConfig::reload() -{ - int ret = ERROR_SUCCESS; - - SrsConfig conf; - if ((ret = conf.parse_file(config_file.c_str())) != ERROR_SUCCESS) { - srs_error("config reloader parse file failed. ret=%d", ret); - return ret; - } - srs_info("config reloader parse file success."); - - // store current root to old_root, - // and reap the root from conf to current root. - SrsConfDirective* old_root = root; - SrsAutoFree(SrsConfDirective, old_root, false); - - root = conf.root; - conf.root = NULL; - - // merge config. - std::vector::iterator it; - - // merge config: listen - if (!srs_directive_equals(root->get("listen"), old_root->get("listen"))) { - for (it = subscribes.begin(); it != subscribes.end(); ++it) { - ISrsReloadHandler* subscribe = *it; - if ((ret = subscribe->on_reload_listen()) != ERROR_SUCCESS) { - srs_error("notify subscribes reload listen failed. ret=%d", ret); - return ret; - } - } - srs_trace("reload listen success."); - } - - // merge config: pithy_print - if (!srs_directive_equals(root->get("pithy_print"), old_root->get("pithy_print"))) { - for (it = subscribes.begin(); it != subscribes.end(); ++it) { - ISrsReloadHandler* subscribe = *it; - if ((ret = subscribe->on_reload_pithy_print()) != ERROR_SUCCESS) { - srs_error("notify subscribes pithy_print listen failed. ret=%d", ret); - return ret; - } - } - srs_trace("reload pithy_print success."); - } - - // merge config: vhost added, directly supported. - - // merge config: vhost removed/disabled/modified. - for (int i = 0; i < (int)old_root->directives.size(); i++) { - SrsConfDirective* old_vhost = old_root->at(i); - // only process vhost directives. - if (old_vhost->name != "vhost") { - continue; - } - - std::string vhost = old_vhost->arg0(); - - SrsConfDirective* new_vhost = root->get("vhost", vhost); - // ignore if absolutely equal - if (new_vhost && srs_directive_equals(old_vhost, new_vhost)) { - srs_trace("vhost %s absolutely equal, ignore.", vhost.c_str()); - continue; - } - // ignore if enable the new vhost when old vhost is disabled. - if (get_vhost_enabled(new_vhost) && !get_vhost_enabled(old_vhost)) { - srs_trace("vhost %s disabled=>enabled, ignore.", vhost.c_str()); - continue; - } - // ignore if both old and new vhost are disabled. - if (!get_vhost_enabled(new_vhost) && !get_vhost_enabled(old_vhost)) { - srs_trace("vhost %s disabled=>disabled, ignore.", vhost.c_str()); - continue; - } - - // merge config: vhost removed/disabled. - if (!get_vhost_enabled(new_vhost) && get_vhost_enabled(old_vhost)) { - srs_trace("vhost %s disabled, reload it.", vhost.c_str()); - for (it = subscribes.begin(); it != subscribes.end(); ++it) { - ISrsReloadHandler* subscribe = *it; - if ((ret = subscribe->on_reload_vhost_removed(vhost)) != ERROR_SUCCESS) { - srs_error("notify subscribes pithy_print remove " - "vhost %s failed. ret=%d", vhost.c_str(), ret); - return ret; - } - } - srs_trace("reload remove vhost %s success.", vhost.c_str()); - } - - // merge config: vhost modified. - srs_trace("vhost %s modified, reload its detail.", vhost.c_str()); - if (get_vhost_enabled(new_vhost) && get_vhost_enabled(old_vhost)) { - // gop_cache - if (!srs_directive_equals(new_vhost->get("gop_cache"), old_vhost->get("gop_cache"))) { - for (it = subscribes.begin(); it != subscribes.end(); ++it) { - ISrsReloadHandler* subscribe = *it; - if ((ret = subscribe->on_reload_gop_cache(vhost)) != ERROR_SUCCESS) { - srs_error("vhost %s notify subscribes gop_cache failed. ret=%d", vhost.c_str(), ret); - return ret; - } - } - srs_trace("vhost %s reload gop_cache success.", vhost.c_str()); - } - // queue_length - if (!srs_directive_equals(new_vhost->get("queue_length"), old_vhost->get("queue_length"))) { - for (it = subscribes.begin(); it != subscribes.end(); ++it) { - ISrsReloadHandler* subscribe = *it; - if ((ret = subscribe->on_reload_queue_length(vhost)) != ERROR_SUCCESS) { - srs_error("vhost %s notify subscribes queue_length failed. ret=%d", vhost.c_str(), ret); - return ret; - } - } - srs_trace("vhost %s reload queue_length success.", vhost.c_str()); - } - // forward - if (!srs_directive_equals(new_vhost->get("forward"), old_vhost->get("forward"))) { - for (it = subscribes.begin(); it != subscribes.end(); ++it) { - ISrsReloadHandler* subscribe = *it; - if ((ret = subscribe->on_reload_forward(vhost)) != ERROR_SUCCESS) { - srs_error("vhost %s notify subscribes forward failed. ret=%d", vhost.c_str(), ret); - return ret; - } - } - srs_trace("vhost %s reload forward success.", vhost.c_str()); - } - // hls - if (!srs_directive_equals(new_vhost->get("hls"), old_vhost->get("hls"))) { - for (it = subscribes.begin(); it != subscribes.end(); ++it) { - ISrsReloadHandler* subscribe = *it; - if ((ret = subscribe->on_reload_hls(vhost)) != ERROR_SUCCESS) { - srs_error("vhost %s notify subscribes hls failed. ret=%d", vhost.c_str(), ret); - return ret; - } - } - srs_trace("vhost %s reload hls success.", vhost.c_str()); - } - // transcode - if (!srs_directive_equals(new_vhost->get("transcode"), old_vhost->get("transcode"))) { - for (it = subscribes.begin(); it != subscribes.end(); ++it) { - ISrsReloadHandler* subscribe = *it; - if ((ret = subscribe->on_reload_transcode(vhost)) != ERROR_SUCCESS) { - srs_error("vhost %s notify subscribes transcode failed. ret=%d", vhost.c_str(), ret); - return ret; - } - } - srs_trace("vhost %s reload transcode success.", vhost.c_str()); - } - // TODO: suppor reload hls/forward/ffmpeg/http - continue; - } - srs_warn("invalid reload path, enabled old: %d, new: %d", - get_vhost_enabled(old_vhost), get_vhost_enabled(new_vhost)); - } - - return ret; -} - -void SrsConfig::subscribe(ISrsReloadHandler* handler) -{ - std::vector::iterator it; - - it = std::find(subscribes.begin(), subscribes.end(), handler); - if (it != subscribes.end()) { - return; - } - - subscribes.push_back(handler); -} - -void SrsConfig::unsubscribe(ISrsReloadHandler* handler) -{ - std::vector::iterator it; - - it = std::find(subscribes.begin(), subscribes.end(), handler); - if (it == subscribes.end()) { - return; - } - - subscribes.erase(it); -} - -// see: ngx_get_options -int SrsConfig::parse_options(int argc, char** argv) -{ - int ret = ERROR_SUCCESS; - - for (int i = 1; i < argc; i++) { - if ((ret = parse_argv(i, argv)) != ERROR_SUCCESS) { - return ret; - } - } - - if (show_help) { - print_help(argv); - } - - if (show_version) { - printf("%s\n", RTMP_SIG_SRS_VERSION); - } - - if (show_help || show_version) { - exit(0); - } - - if (config_file.empty()) { - ret = ERROR_SYSTEM_CONFIG_INVALID; - srs_error("config file not specified, see help: %s -h, ret=%d", argv[0], ret); - return ret; - } - - return parse_file(config_file.c_str()); -} - -int SrsConfig::parse_file(const char* filename) -{ - int ret = ERROR_SUCCESS; - - config_file = filename; - - if (config_file.empty()) { - return ERROR_SYSTEM_CONFIG_INVALID; - } - - if ((ret = root->parse(config_file.c_str())) != ERROR_SUCCESS) { - return ret; - } - - SrsConfDirective* conf = NULL; - if ((conf = get_listen()) == NULL || conf->args.size() == 0) { - ret = ERROR_SYSTEM_CONFIG_INVALID; - srs_error("line %d: conf error, " - "directive \"listen\" is empty, ret=%d", (conf? conf->conf_line:0), ret); - return ret; - } - - // TODO: check the hls. - // TODO: check forward. - // TODO: check ffmpeg. - // TODO: check http. - - return ret; -} - -int SrsConfig::parse_argv(int& i, char** argv) -{ - int ret = ERROR_SUCCESS; - - char* p = argv[i]; - - if (*p++ != '-') { - ret = ERROR_SYSTEM_CONFIG_INVALID; - srs_error("invalid options(index=%d, value=%s), " - "must starts with -, see help: %s -h, ret=%d", i, argv[i], argv[0], ret); - return ret; - } - - while (*p) { - switch (*p++) { - case '?': - case 'h': - show_help = true; - break; - case 'v': - case 'V': - show_version = true; - break; - case 'c': - if (*p) { - config_file = p; - return ret; - } - if (argv[++i]) { - config_file = argv[i]; - return ret; - } - ret = ERROR_SYSTEM_CONFIG_INVALID; - srs_error("option \"-c\" requires parameter, ret=%d", ret); - return ret; - default: - ret = ERROR_SYSTEM_CONFIG_INVALID; - srs_error("invalid option: \"%c\", see help: %s -h, ret=%d", *(p - 1), argv[0], ret); - return ret; - } - } - - return ret; -} - -void SrsConfig::print_help(char** argv) -{ - printf( - RTMP_SIG_SRS_NAME" "RTMP_SIG_SRS_VERSION" "RTMP_SIG_SRS_COPYRIGHT"\n" - "Primary Authors: "RTMP_SIG_SRS_PRIMARY_AUTHROS"\n" - "Build: "SRS_BUILD_DATE" Configuration: "SRS_CONFIGURE"\n" - "Usage: %s [-h?vV] [-c ]\n" - "\n" - "Options:\n" - " -?-h : show help\n" - " -v-V : show version and exit\n" - " -c filename : set configuration file\n" - "\n" - RTMP_SIG_SRS_WEB"\n" - RTMP_SIG_SRS_URL"\n" - "Email: "RTMP_SIG_SRS_EMAIL"\n" - "\n", - argv[0]); -} - -SrsConfDirective* SrsConfig::get_vhost(string vhost) -{ - srs_assert(root); - - for (int i = 0; i < (int)root->directives.size(); i++) { - SrsConfDirective* conf = root->at(i); - - if (conf->name != "vhost") { - continue; - } - - if (conf->arg0() == vhost) { - return conf; - } - } - - if (vhost != RTMP_VHOST_DEFAULT) { - return get_vhost(RTMP_VHOST_DEFAULT); - } - - return NULL; -} - -SrsConfDirective* SrsConfig::get_vhost_on_connect(string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - conf = conf->get("http_hooks"); - if (!conf) { - return NULL; - } - - SrsConfDirective* enabled = conf->get("enabled"); - if (!enabled || enabled->arg0() != "on") { - return NULL; - } - - return conf->get("on_connect"); -} - -SrsConfDirective* SrsConfig::get_vhost_on_close(string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - conf = conf->get("http_hooks"); - if (!conf) { - return NULL; - } - - SrsConfDirective* enabled = conf->get("enabled"); - if (!enabled || enabled->arg0() != "on") { - return NULL; - } - - return conf->get("on_close"); -} - -SrsConfDirective* SrsConfig::get_vhost_on_publish(string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - conf = conf->get("http_hooks"); - if (!conf) { - return NULL; - } - - SrsConfDirective* enabled = conf->get("enabled"); - if (!enabled || enabled->arg0() != "on") { - return NULL; - } - - return conf->get("on_publish"); -} - -SrsConfDirective* SrsConfig::get_vhost_on_unpublish(string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - conf = conf->get("http_hooks"); - if (!conf) { - return NULL; - } - - SrsConfDirective* enabled = conf->get("enabled"); - if (!enabled || enabled->arg0() != "on") { - return NULL; - } - - return conf->get("on_unpublish"); -} - -SrsConfDirective* SrsConfig::get_vhost_on_play(string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - conf = conf->get("http_hooks"); - if (!conf) { - return NULL; - } - - SrsConfDirective* enabled = conf->get("enabled"); - if (!enabled || enabled->arg0() != "on") { - return NULL; - } - - return conf->get("on_play"); -} - -SrsConfDirective* SrsConfig::get_vhost_on_stop(string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - conf = conf->get("http_hooks"); - if (!conf) { - return NULL; - } - - SrsConfDirective* enabled = conf->get("enabled"); - if (!enabled || enabled->arg0() != "on") { - return NULL; - } - - return conf->get("on_stop"); -} - -bool SrsConfig::get_vhost_enabled(string vhost) -{ - SrsConfDirective* vhost_conf = get_vhost(vhost); - - return get_vhost_enabled(vhost_conf); -} - -bool SrsConfig::get_vhost_enabled(SrsConfDirective* vhost) -{ - if (!vhost) { - return false; - } - - SrsConfDirective* conf = vhost->get("enabled"); - if (!conf) { - return true; - } - - if (conf->arg0() == "off") { - return false; - } - - return true; -} - -SrsConfDirective* SrsConfig::get_transcode(string vhost, string scope) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - SrsConfDirective* transcode = conf->get("transcode"); - if (!transcode) { - return NULL; - } - - if (transcode->arg0() == scope) { - return transcode; - } - - return NULL; -} - -bool SrsConfig::get_transcode_enabled(SrsConfDirective* transcode) -{ - if (!transcode) { - return false; - } - - SrsConfDirective* conf = transcode->get("enabled"); - if (!conf || conf->arg0() != "on") { - return false; - } - - return true; -} - -string SrsConfig::get_transcode_ffmpeg(SrsConfDirective* transcode) -{ - if (!transcode) { - return ""; - } - - SrsConfDirective* conf = transcode->get("ffmpeg"); - if (!conf) { - return ""; - } - - return conf->arg0(); -} - -void SrsConfig::get_transcode_engines(SrsConfDirective* transcode, std::vector& engines) -{ - if (!transcode) { - return; - } - - for (int i = 0; i < (int)transcode->directives.size(); i++) { - SrsConfDirective* conf = transcode->directives[i]; - - if (conf->name == "engine") { - engines.push_back(conf); - } - } - - return; -} - -bool SrsConfig::get_engine_enabled(SrsConfDirective* engine) -{ - if (!engine) { - return false; - } - - SrsConfDirective* conf = engine->get("enabled"); - if (!conf || conf->arg0() != "on") { - return false; - } - - return true; -} - -string SrsConfig::get_engine_vcodec(SrsConfDirective* engine) -{ - if (!engine) { - return ""; - } - - SrsConfDirective* conf = engine->get("vcodec"); - if (!conf) { - return ""; - } - - return conf->arg0(); -} - -int SrsConfig::get_engine_vbitrate(SrsConfDirective* engine) -{ - if (!engine) { - return 0; - } - - SrsConfDirective* conf = engine->get("vbitrate"); - if (!conf) { - return 0; - } - - return ::atoi(conf->arg0().c_str()); -} - -double SrsConfig::get_engine_vfps(SrsConfDirective* engine) -{ - if (!engine) { - return 0; - } - - SrsConfDirective* conf = engine->get("vfps"); - if (!conf) { - return 0; - } - - return ::atof(conf->arg0().c_str()); -} - -int SrsConfig::get_engine_vwidth(SrsConfDirective* engine) -{ - if (!engine) { - return 0; - } - - SrsConfDirective* conf = engine->get("vwidth"); - if (!conf) { - return 0; - } - - return ::atoi(conf->arg0().c_str()); -} - -int SrsConfig::get_engine_vheight(SrsConfDirective* engine) -{ - if (!engine) { - return 0; - } - - SrsConfDirective* conf = engine->get("vheight"); - if (!conf) { - return 0; - } - - return ::atoi(conf->arg0().c_str()); -} - -int SrsConfig::get_engine_vthreads(SrsConfDirective* engine) -{ - if (!engine) { - return 0; - } - - SrsConfDirective* conf = engine->get("vthreads"); - if (!conf) { - return 0; - } - - return ::atoi(conf->arg0().c_str()); -} - -string SrsConfig::get_engine_vprofile(SrsConfDirective* engine) -{ - if (!engine) { - return ""; - } - - SrsConfDirective* conf = engine->get("vprofile"); - if (!conf) { - return ""; - } - - return conf->arg0(); -} - -string SrsConfig::get_engine_vpreset(SrsConfDirective* engine) -{ - if (!engine) { - return ""; - } - - SrsConfDirective* conf = engine->get("vpreset"); - if (!conf) { - return ""; - } - - return conf->arg0(); -} - -void SrsConfig::get_engine_vparams(SrsConfDirective* engine, std::vector& vparams) -{ - if (!engine) { - return; - } - - SrsConfDirective* conf = engine->get("vparams"); - if (!conf) { - return; - } - - for (int i = 0; i < (int)conf->directives.size(); i++) { - SrsConfDirective* p = conf->directives[i]; - if (!p) { - continue; - } - - vparams.push_back("-" + p->name); - vparams.push_back(p->arg0()); - } -} - -void SrsConfig::get_engine_vfilter(SrsConfDirective* engine, std::vector& vfilter) -{ - if (!engine) { - return; - } - - SrsConfDirective* conf = engine->get("vfilter"); - if (!conf) { - return; - } - - for (int i = 0; i < (int)conf->directives.size(); i++) { - SrsConfDirective* p = conf->directives[i]; - if (!p) { - continue; - } - - vfilter.push_back("-" + p->name); - vfilter.push_back(p->arg0()); - } -} - -string SrsConfig::get_engine_acodec(SrsConfDirective* engine) -{ - if (!engine) { - return ""; - } - - SrsConfDirective* conf = engine->get("acodec"); - if (!conf) { - return ""; - } - - return conf->arg0(); -} - -int SrsConfig::get_engine_abitrate(SrsConfDirective* engine) -{ - if (!engine) { - return 0; - } - - SrsConfDirective* conf = engine->get("abitrate"); - if (!conf) { - return 0; - } - - return ::atoi(conf->arg0().c_str()); -} - -int SrsConfig::get_engine_asample_rate(SrsConfDirective* engine) -{ - if (!engine) { - return 0; - } - - SrsConfDirective* conf = engine->get("asample_rate"); - if (!conf) { - return 0; - } - - return ::atoi(conf->arg0().c_str()); -} - -int SrsConfig::get_engine_achannels(SrsConfDirective* engine) -{ - if (!engine) { - return 0; - } - - SrsConfDirective* conf = engine->get("achannels"); - if (!conf) { - return 0; - } - - return ::atoi(conf->arg0().c_str()); -} - -void SrsConfig::get_engine_aparams(SrsConfDirective* engine, std::vector& aparams) -{ - if (!engine) { - return; - } - - SrsConfDirective* conf = engine->get("aparams"); - if (!conf) { - return; - } - - for (int i = 0; i < (int)conf->directives.size(); i++) { - SrsConfDirective* p = conf->directives[i]; - if (!p) { - continue; - } - - aparams.push_back("-" + p->name); - aparams.push_back(p->arg0()); - } -} - -string SrsConfig::get_engine_output(SrsConfDirective* engine) -{ - if (!engine) { - return ""; - } - - SrsConfDirective* conf = engine->get("output"); - if (!conf) { - return ""; - } - - return conf->arg0(); -} - -string SrsConfig::get_log_dir() -{ - srs_assert(root); - - SrsConfDirective* conf = root->get("log_dir"); - if (!conf || conf->arg0().empty()) { - return "./objs/logs"; - } - - return conf->arg0(); -} - -int SrsConfig::get_max_connections() -{ - srs_assert(root); - - SrsConfDirective* conf = root->get("max_connections"); - if (!conf || conf->arg0().empty()) { - return 2000; - } - - return ::atoi(conf->arg0().c_str()); -} - -bool SrsConfig::get_gop_cache(string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return true; - } - - conf = conf->get("gop_cache"); - if (conf && conf->arg0() == "off") { - return false; - } - - return true; -} - -double SrsConfig::get_queue_length(string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return SRS_CONF_DEFAULT_QUEUE_LENGTH; - } - - conf = conf->get("queue_length"); - if (!conf || conf->arg0().empty()) { - return SRS_CONF_DEFAULT_QUEUE_LENGTH; - } - - return ::atoi(conf->arg0().c_str()); -} - -SrsConfDirective* SrsConfig::get_forward(string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("forward"); -} - -SrsConfDirective* SrsConfig::get_hls(string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("hls"); -} - -bool SrsConfig::get_hls_enabled(string vhost) -{ - SrsConfDirective* hls = get_hls(vhost); - - if (!hls) { - return false; - } - - SrsConfDirective* conf = hls->get("enabled"); - - if (!conf) { - return false; - } - - if (conf->arg0() == "on") { - return true; - } - - return false; -} - -string SrsConfig::get_hls_path(string vhost) -{ - SrsConfDirective* hls = get_hls(vhost); - - if (!hls) { - return SRS_CONF_DEFAULT_HLS_PATH; - } - - SrsConfDirective* conf = hls->get("hls_path"); - - if (!conf) { - return SRS_CONF_DEFAULT_HLS_PATH; - } - - return conf->arg0(); -} - -double SrsConfig::get_hls_fragment(string vhost) -{ - SrsConfDirective* hls = get_hls(vhost); - - if (!hls) { - return SRS_CONF_DEFAULT_HLS_FRAGMENT; - } - - SrsConfDirective* conf = hls->get("hls_fragment"); - - if (!conf) { - return SRS_CONF_DEFAULT_HLS_FRAGMENT; - } - - return ::atof(conf->arg0().c_str()); -} - -double SrsConfig::get_hls_window(string vhost) -{ - SrsConfDirective* hls = get_hls(vhost); - - if (!hls) { - return SRS_CONF_DEFAULT_HLS_WINDOW; - } - - SrsConfDirective* conf = hls->get("hls_window"); - - if (!conf) { - return SRS_CONF_DEFAULT_HLS_WINDOW; - } - - return ::atof(conf->arg0().c_str()); -} - -SrsConfDirective* SrsConfig::get_refer(string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("refer"); -} - -SrsConfDirective* SrsConfig::get_refer_play(string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("refer_play"); -} - -SrsConfDirective* SrsConfig::get_refer_publish(string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("refer_publish"); -} - -SrsConfDirective* SrsConfig::get_listen() -{ - return root->get("listen"); -} - -int SrsConfig::get_chunk_size(const std::string &vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return SRS_CONF_DEFAULT_CHUNK_SIZE; - } - - conf = conf->get("chunk_size"); - if (!conf) { - // vhost does not specify the chunk size, - // use the global instead. - conf = root->get("chunk_size"); - if (!conf) { - return SRS_CONF_DEFAULT_CHUNK_SIZE; - } - - return ::atoi(conf->arg0().c_str()); - } - - return ::atoi(conf->arg0().c_str()); -} - -int SrsConfig::get_pithy_print_publish() -{ - SrsConfDirective* pithy = root->get("pithy_print"); - if (!pithy) { - return SRS_STAGE_PUBLISH_USER_INTERVAL_MS; - } - - pithy = pithy->get("publish"); - if (!pithy) { - return SRS_STAGE_PUBLISH_USER_INTERVAL_MS; - } - - return ::atoi(pithy->arg0().c_str()); -} - -int SrsConfig::get_pithy_print_forwarder() -{ - SrsConfDirective* pithy = root->get("pithy_print"); - if (!pithy) { - return SRS_STAGE_FORWARDER_INTERVAL_MS; - } - - pithy = pithy->get("forwarder"); - if (!pithy) { - return SRS_STAGE_FORWARDER_INTERVAL_MS; - } - - return ::atoi(pithy->arg0().c_str()); -} - -int SrsConfig::get_pithy_print_hls() -{ - SrsConfDirective* pithy = root->get("pithy_print"); - if (!pithy) { - return SRS_STAGE_HLS_INTERVAL_MS; - } - - pithy = pithy->get("hls"); - if (!pithy) { - return SRS_STAGE_HLS_INTERVAL_MS; - } - - return ::atoi(pithy->arg0().c_str()); -} - -bool SrsConfig::get_bw_check_enabled(const string &vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return false; - } - - conf = conf->get("bandcheck"); - if (!conf) { - return false; - } - - conf = conf->get("enabled"); - if (!conf || conf->arg0() != "on") { - return false; - } - - return true; -} - -string SrsConfig::get_bw_check_key(const string &vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return ""; - } - - conf = conf->get("bandcheck"); - if (!conf) { - return ""; - } - - conf = conf->get("key"); - if (!conf) { - return ""; - } - - return conf->arg0(); -} - -int SrsConfig::get_bw_check_interval_ms(const string &vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return SRS_CONF_DEFAULT_BANDWIDTH_INTERVAL; - } - - conf = conf->get("bandcheck"); - if (!conf) { - return SRS_CONF_DEFAULT_BANDWIDTH_INTERVAL; - } - - conf = conf->get("interval_ms"); - if (!conf) { - return SRS_CONF_DEFAULT_BANDWIDTH_INTERVAL; - } - - return ::atoi(conf->arg0().c_str()) * 1000; -} - -int SrsConfig::get_bw_check_limit_kbps(const string &vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return SRS_CONF_DEFAULT_BANDWIDTH_LIMIT_KBPS; - } - - conf = conf->get("bandcheck"); - if (!conf) { - return SRS_CONF_DEFAULT_BANDWIDTH_LIMIT_KBPS; - } - - conf = conf->get("limit_kbps"); - if (!conf) { - return SRS_CONF_DEFAULT_BANDWIDTH_LIMIT_KBPS; - } - - return ::atoi(conf->arg0().c_str()); -} - -int SrsConfig::get_pithy_print_encoder() -{ - SrsConfDirective* pithy = root->get("encoder"); - if (!pithy) { - return SRS_STAGE_ENCODER_INTERVAL_MS; - } - - pithy = pithy->get("forwarder"); - if (!pithy) { - return SRS_STAGE_ENCODER_INTERVAL_MS; - } - - return ::atoi(pithy->arg0().c_str()); -} - -int SrsConfig::get_pithy_print_play() -{ - SrsConfDirective* pithy = root->get("pithy_print"); - if (!pithy) { - return SRS_STAGE_PLAY_USER_INTERVAL_MS; - } - - pithy = pithy->get("play"); - if (!pithy) { - return SRS_STAGE_PLAY_USER_INTERVAL_MS; - } - - return ::atoi(pithy->arg0().c_str()); -} - -bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b) -{ - // both NULL, equal. - if (!a && !b) { - return true; - } - - if (!a || !b) { - return false; - } - - if (a->name != b->name) { - return false; - } - - if (a->args.size() != b->args.size()) { - return false; - } - - for (int i = 0; i < (int)a->args.size(); i++) { - if (a->args.at(i) != b->args.at(i)) { - return false; - } - } - - if (a->directives.size() != b->directives.size()) { - return false; - } - - for (int i = 0; i < (int)a->directives.size(); i++) { - SrsConfDirective* a0 = a->at(i); - SrsConfDirective* b0 = b->at(i); - - if (!srs_directive_equals(a0, b0)) { - return false; - } - } - - return true; -} - +/* +The MIT License (MIT) + +Copyright (c) 2013-2014 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include +#include +#include +#include +// file operations. +#include +#include +#include +#include + +#include +#include +using namespace std; + +#include +#include +#include + +#define FILE_OFFSET(fd) lseek(fd, 0, SEEK_CUR) + +int64_t FILE_SIZE(int fd) +{ + int64_t pre = FILE_OFFSET(fd); + int64_t pos = lseek(fd, 0, SEEK_END); + lseek(fd, pre, SEEK_SET); + return pos; +} + +#define LF (char)0x0a +#define CR (char)0x0d + +bool is_common_space(char ch) +{ + return (ch == ' ' || ch == '\t' || ch == CR || ch == LF); +} + +class SrsFileBuffer +{ +private: + // last available position. + char* last; + // end of buffer. + char* end; + // start of buffer. + char* start; +public: + // current consumed position. + char* pos; + // current parsed line. + int line; + + SrsFileBuffer(); + virtual ~SrsFileBuffer(); + virtual int fullfill(const char* filename); + virtual bool empty(); +}; + +SrsFileBuffer::SrsFileBuffer() +{ + line = 0; + + pos = last = start = NULL; + end = start; +} + +SrsFileBuffer::~SrsFileBuffer() +{ + srs_freepa(start); +} + +int SrsFileBuffer::fullfill(const char* filename) +{ + int ret = ERROR_SUCCESS; + + int fd = -1; + int nread = 0; + int filesize = 0; + + if ((fd = ::open(filename, O_RDONLY, 0)) < 0) { + ret = ERROR_SYSTEM_CONFIG_INVALID; + srs_error("open conf file error. ret=%d", ret); + goto finish; + } + + if ((filesize = FILE_SIZE(fd) - FILE_OFFSET(fd)) <= 0) { + ret = ERROR_SYSTEM_CONFIG_EOF; + srs_error("read conf file error. ret=%d", ret); + goto finish; + } + + srs_freepa(start); + pos = last = start = new char[filesize]; + end = start + filesize; + + if ((nread = read(fd, start, filesize)) != filesize) { + ret = ERROR_SYSTEM_CONFIG_INVALID; + srs_error("read file read error. expect %d, actual %d bytes, ret=%d", + filesize, nread, ret); + goto finish; + } + + line = 1; + +finish: + if (fd > 0) { + ::close(fd); + } + + return ret; +} + +bool SrsFileBuffer::empty() +{ + return pos >= end; +} + +SrsConfDirective::SrsConfDirective() +{ +} + +SrsConfDirective::~SrsConfDirective() +{ + std::vector::iterator it; + for (it = directives.begin(); it != directives.end(); ++it) { + SrsConfDirective* directive = *it; + srs_freep(directive); + } + directives.clear(); +} + +string SrsConfDirective::arg0() +{ + if (args.size() > 0) { + return args.at(0); + } + + return ""; +} + +string SrsConfDirective::arg1() +{ + if (args.size() > 1) { + return args.at(1); + } + + return ""; +} + +string SrsConfDirective::arg2() +{ + if (args.size() > 2) { + return args.at(2); + } + + return ""; +} + +SrsConfDirective* SrsConfDirective::at(int index) +{ + return directives.at(index); +} + +SrsConfDirective* SrsConfDirective::get(string _name) +{ + std::vector::iterator it; + for (it = directives.begin(); it != directives.end(); ++it) { + SrsConfDirective* directive = *it; + if (directive->name == _name) { + return directive; + } + } + + return NULL; +} + +SrsConfDirective* SrsConfDirective::get(string _name, string _arg0) +{ + std::vector::iterator it; + for (it = directives.begin(); it != directives.end(); ++it) { + SrsConfDirective* directive = *it; + if (directive->name == _name && directive->arg0() == _arg0) { + return directive; + } + } + + return NULL; +} + +int SrsConfDirective::parse(const char* filename) +{ + int ret = ERROR_SUCCESS; + + SrsFileBuffer buffer; + + if ((ret = buffer.fullfill(filename)) != ERROR_SUCCESS) { + return ret; + } + + return parse_conf(&buffer, parse_file); +} + +// see: ngx_conf_parse +int SrsConfDirective::parse_conf(SrsFileBuffer* buffer, SrsDirectiveType type) +{ + int ret = ERROR_SUCCESS; + + while (true) { + std::vector args; + ret = read_token(buffer, args); + + /** + * ret maybe: + * ERROR_SYSTEM_CONFIG_INVALID error. + * ERROR_SYSTEM_CONFIG_DIRECTIVE directive terminated by ';' found + * ERROR_SYSTEM_CONFIG_BLOCK_START token terminated by '{' found + * ERROR_SYSTEM_CONFIG_BLOCK_END the '}' found + * ERROR_SYSTEM_CONFIG_EOF the config file is done + */ + if (ret == ERROR_SYSTEM_CONFIG_INVALID) { + return ret; + } + if (ret == ERROR_SYSTEM_CONFIG_BLOCK_END) { + if (type != parse_block) { + srs_error("line %d: unexpected \"}\"", buffer->line); + return ret; + } + return ERROR_SUCCESS; + } + if (ret == ERROR_SYSTEM_CONFIG_EOF) { + if (type == parse_block) { + srs_error("line %d: unexpected end of file, expecting \"}\"", buffer->line); + return ret; + } + return ERROR_SUCCESS; + } + + if (args.empty()) { + srs_error("line %d: empty directive.", buffer->line); + return ret; + } + + // build directive tree. + SrsConfDirective* directive = new SrsConfDirective(); + + directive->conf_line = buffer->line; + directive->name = args[0]; + args.erase(args.begin()); + directive->args.swap(args); + + directives.push_back(directive); + + if (ret == ERROR_SYSTEM_CONFIG_BLOCK_START) { + if ((ret = directive->parse_conf(buffer, parse_block)) != ERROR_SUCCESS) { + return ret; + } + } + } + + return ret; +} + +// see: ngx_conf_read_token +int SrsConfDirective::read_token(SrsFileBuffer* buffer, std::vector& args) +{ + int ret = ERROR_SUCCESS; + + char* pstart = buffer->pos; + int startline = buffer->line; + + bool sharp_comment = false; + + bool d_quoted = false; + bool s_quoted = false; + + bool need_space = false; + bool last_space = true; + + while (true) { + if (buffer->empty()) { + ret = ERROR_SYSTEM_CONFIG_EOF; + + if (!args.empty() || !last_space) { + srs_error("line %d: unexpected end of file, expecting ; or \"}\"", buffer->line); + return ERROR_SYSTEM_CONFIG_INVALID; + } + srs_error("end of file. ret=%d", ret); + + return ret; + } + + char ch = *buffer->pos++; + + if (ch == LF) { + buffer->line++; + sharp_comment = false; + } + + if (sharp_comment) { + continue; + } + + if (need_space) { + if (is_common_space(ch)) { + last_space = true; + need_space = false; + continue; + } + if (ch == ';') { + return ERROR_SYSTEM_CONFIG_DIRECTIVE; + } + if (ch == '{') { + return ERROR_SYSTEM_CONFIG_BLOCK_START; + } + srs_error("line %d: unexpected '%c'", buffer->line, ch); + return ERROR_SYSTEM_CONFIG_INVALID; + } + + // last charecter is space. + if (last_space) { + if (is_common_space(ch)) { + continue; + } + pstart = buffer->pos - 1; + startline = buffer->line; + switch (ch) { + case ';': + if (args.size() == 0) { + srs_error("line %d: unexpected ';'", buffer->line); + return ERROR_SYSTEM_CONFIG_INVALID; + } + return ERROR_SYSTEM_CONFIG_DIRECTIVE; + case '{': + if (args.size() == 0) { + srs_error("line %d: unexpected '{'", buffer->line); + return ERROR_SYSTEM_CONFIG_INVALID; + } + return ERROR_SYSTEM_CONFIG_BLOCK_START; + case '}': + if (args.size() != 0) { + srs_error("line %d: unexpected '}'", buffer->line); + return ERROR_SYSTEM_CONFIG_INVALID; + } + return ERROR_SYSTEM_CONFIG_BLOCK_END; + case '#': + sharp_comment = 1; + continue; + case '"': + pstart++; + d_quoted = true; + last_space = 0; + continue; + case '\'': + pstart++; + s_quoted = true; + last_space = 0; + continue; + default: + last_space = 0; + continue; + } + } else { + // last charecter is not space + bool found = false; + if (d_quoted) { + if (ch == '"') { + d_quoted = false; + need_space = true; + found = true; + } + } else if (s_quoted) { + if (ch == '\'') { + s_quoted = false; + need_space = true; + found = true; + } + } else if (is_common_space(ch) || ch == ';' || ch == '{') { + last_space = true; + found = 1; + } + + if (found) { + int len = buffer->pos - pstart; + char* word = new char[len]; + memcpy(word, pstart, len); + word[len - 1] = 0; + + string word_str = word; + if (!word_str.empty()) { + args.push_back(word_str); + } + srs_freepa(word); + + if (ch == ';') { + return ERROR_SYSTEM_CONFIG_DIRECTIVE; + } + if (ch == '{') { + return ERROR_SYSTEM_CONFIG_BLOCK_START; + } + } + } + } + + return ret; +} + +SrsConfig* config = new SrsConfig(); + +SrsConfig::SrsConfig() +{ + show_help = false; + show_version = false; + + root = new SrsConfDirective(); + root->conf_line = 0; + root->name = "root"; +} + +SrsConfig::~SrsConfig() +{ + srs_freep(root); +} + +int SrsConfig::reload() +{ + int ret = ERROR_SUCCESS; + + SrsConfig conf; + if ((ret = conf.parse_file(config_file.c_str())) != ERROR_SUCCESS) { + srs_error("config reloader parse file failed. ret=%d", ret); + return ret; + } + srs_info("config reloader parse file success."); + + // store current root to old_root, + // and reap the root from conf to current root. + SrsConfDirective* old_root = root; + SrsAutoFree(SrsConfDirective, old_root, false); + + root = conf.root; + conf.root = NULL; + + // merge config. + std::vector::iterator it; + + // merge config: listen + if (!srs_directive_equals(root->get("listen"), old_root->get("listen"))) { + for (it = subscribes.begin(); it != subscribes.end(); ++it) { + ISrsReloadHandler* subscribe = *it; + if ((ret = subscribe->on_reload_listen()) != ERROR_SUCCESS) { + srs_error("notify subscribes reload listen failed. ret=%d", ret); + return ret; + } + } + srs_trace("reload listen success."); + } + + // merge config: pithy_print + if (!srs_directive_equals(root->get("pithy_print"), old_root->get("pithy_print"))) { + for (it = subscribes.begin(); it != subscribes.end(); ++it) { + ISrsReloadHandler* subscribe = *it; + if ((ret = subscribe->on_reload_pithy_print()) != ERROR_SUCCESS) { + srs_error("notify subscribes pithy_print listen failed. ret=%d", ret); + return ret; + } + } + srs_trace("reload pithy_print success."); + } + + // merge config: vhost added, directly supported. + + // merge config: vhost removed/disabled/modified. + for (int i = 0; i < (int)old_root->directives.size(); i++) { + SrsConfDirective* old_vhost = old_root->at(i); + // only process vhost directives. + if (old_vhost->name != "vhost") { + continue; + } + + std::string vhost = old_vhost->arg0(); + + SrsConfDirective* new_vhost = root->get("vhost", vhost); + // ignore if absolutely equal + if (new_vhost && srs_directive_equals(old_vhost, new_vhost)) { + srs_trace("vhost %s absolutely equal, ignore.", vhost.c_str()); + continue; + } + // ignore if enable the new vhost when old vhost is disabled. + if (get_vhost_enabled(new_vhost) && !get_vhost_enabled(old_vhost)) { + srs_trace("vhost %s disabled=>enabled, ignore.", vhost.c_str()); + continue; + } + // ignore if both old and new vhost are disabled. + if (!get_vhost_enabled(new_vhost) && !get_vhost_enabled(old_vhost)) { + srs_trace("vhost %s disabled=>disabled, ignore.", vhost.c_str()); + continue; + } + + // merge config: vhost removed/disabled. + if (!get_vhost_enabled(new_vhost) && get_vhost_enabled(old_vhost)) { + srs_trace("vhost %s disabled, reload it.", vhost.c_str()); + for (it = subscribes.begin(); it != subscribes.end(); ++it) { + ISrsReloadHandler* subscribe = *it; + if ((ret = subscribe->on_reload_vhost_removed(vhost)) != ERROR_SUCCESS) { + srs_error("notify subscribes pithy_print remove " + "vhost %s failed. ret=%d", vhost.c_str(), ret); + return ret; + } + } + srs_trace("reload remove vhost %s success.", vhost.c_str()); + } + + // merge config: vhost modified. + srs_trace("vhost %s modified, reload its detail.", vhost.c_str()); + if (get_vhost_enabled(new_vhost) && get_vhost_enabled(old_vhost)) { + // gop_cache + if (!srs_directive_equals(new_vhost->get("gop_cache"), old_vhost->get("gop_cache"))) { + for (it = subscribes.begin(); it != subscribes.end(); ++it) { + ISrsReloadHandler* subscribe = *it; + if ((ret = subscribe->on_reload_gop_cache(vhost)) != ERROR_SUCCESS) { + srs_error("vhost %s notify subscribes gop_cache failed. ret=%d", vhost.c_str(), ret); + return ret; + } + } + srs_trace("vhost %s reload gop_cache success.", vhost.c_str()); + } + // queue_length + if (!srs_directive_equals(new_vhost->get("queue_length"), old_vhost->get("queue_length"))) { + for (it = subscribes.begin(); it != subscribes.end(); ++it) { + ISrsReloadHandler* subscribe = *it; + if ((ret = subscribe->on_reload_queue_length(vhost)) != ERROR_SUCCESS) { + srs_error("vhost %s notify subscribes queue_length failed. ret=%d", vhost.c_str(), ret); + return ret; + } + } + srs_trace("vhost %s reload queue_length success.", vhost.c_str()); + } + // forward + if (!srs_directive_equals(new_vhost->get("forward"), old_vhost->get("forward"))) { + for (it = subscribes.begin(); it != subscribes.end(); ++it) { + ISrsReloadHandler* subscribe = *it; + if ((ret = subscribe->on_reload_forward(vhost)) != ERROR_SUCCESS) { + srs_error("vhost %s notify subscribes forward failed. ret=%d", vhost.c_str(), ret); + return ret; + } + } + srs_trace("vhost %s reload forward success.", vhost.c_str()); + } + // hls + if (!srs_directive_equals(new_vhost->get("hls"), old_vhost->get("hls"))) { + for (it = subscribes.begin(); it != subscribes.end(); ++it) { + ISrsReloadHandler* subscribe = *it; + if ((ret = subscribe->on_reload_hls(vhost)) != ERROR_SUCCESS) { + srs_error("vhost %s notify subscribes hls failed. ret=%d", vhost.c_str(), ret); + return ret; + } + } + srs_trace("vhost %s reload hls success.", vhost.c_str()); + } + // transcode + if (!srs_directive_equals(new_vhost->get("transcode"), old_vhost->get("transcode"))) { + for (it = subscribes.begin(); it != subscribes.end(); ++it) { + ISrsReloadHandler* subscribe = *it; + if ((ret = subscribe->on_reload_transcode(vhost)) != ERROR_SUCCESS) { + srs_error("vhost %s notify subscribes transcode failed. ret=%d", vhost.c_str(), ret); + return ret; + } + } + srs_trace("vhost %s reload transcode success.", vhost.c_str()); + } + // TODO: suppor reload hls/forward/ffmpeg/http + continue; + } + srs_warn("invalid reload path, enabled old: %d, new: %d", + get_vhost_enabled(old_vhost), get_vhost_enabled(new_vhost)); + } + + return ret; +} + +void SrsConfig::subscribe(ISrsReloadHandler* handler) +{ + std::vector::iterator it; + + it = std::find(subscribes.begin(), subscribes.end(), handler); + if (it != subscribes.end()) { + return; + } + + subscribes.push_back(handler); +} + +void SrsConfig::unsubscribe(ISrsReloadHandler* handler) +{ + std::vector::iterator it; + + it = std::find(subscribes.begin(), subscribes.end(), handler); + if (it == subscribes.end()) { + return; + } + + subscribes.erase(it); +} + +// see: ngx_get_options +int SrsConfig::parse_options(int argc, char** argv) +{ + int ret = ERROR_SUCCESS; + + for (int i = 1; i < argc; i++) { + if ((ret = parse_argv(i, argv)) != ERROR_SUCCESS) { + return ret; + } + } + + if (show_help) { + print_help(argv); + } + + if (show_version) { + printf("%s\n", RTMP_SIG_SRS_VERSION); + } + + if (show_help || show_version) { + exit(0); + } + + if (config_file.empty()) { + ret = ERROR_SYSTEM_CONFIG_INVALID; + srs_error("config file not specified, see help: %s -h, ret=%d", argv[0], ret); + return ret; + } + + return parse_file(config_file.c_str()); +} + +int SrsConfig::parse_file(const char* filename) +{ + int ret = ERROR_SUCCESS; + + config_file = filename; + + if (config_file.empty()) { + return ERROR_SYSTEM_CONFIG_INVALID; + } + + if ((ret = root->parse(config_file.c_str())) != ERROR_SUCCESS) { + return ret; + } + + SrsConfDirective* conf = NULL; + if ((conf = get_listen()) == NULL || conf->args.size() == 0) { + ret = ERROR_SYSTEM_CONFIG_INVALID; + srs_error("line %d: conf error, " + "directive \"listen\" is empty, ret=%d", (conf? conf->conf_line:0), ret); + return ret; + } + + // TODO: check the hls. + // TODO: check forward. + // TODO: check ffmpeg. + // TODO: check http. + + return ret; +} + +int SrsConfig::parse_argv(int& i, char** argv) +{ + int ret = ERROR_SUCCESS; + + char* p = argv[i]; + + if (*p++ != '-') { + ret = ERROR_SYSTEM_CONFIG_INVALID; + srs_error("invalid options(index=%d, value=%s), " + "must starts with -, see help: %s -h, ret=%d", i, argv[i], argv[0], ret); + return ret; + } + + while (*p) { + switch (*p++) { + case '?': + case 'h': + show_help = true; + break; + case 'v': + case 'V': + show_version = true; + break; + case 'c': + if (*p) { + config_file = p; + return ret; + } + if (argv[++i]) { + config_file = argv[i]; + return ret; + } + ret = ERROR_SYSTEM_CONFIG_INVALID; + srs_error("option \"-c\" requires parameter, ret=%d", ret); + return ret; + default: + ret = ERROR_SYSTEM_CONFIG_INVALID; + srs_error("invalid option: \"%c\", see help: %s -h, ret=%d", *(p - 1), argv[0], ret); + return ret; + } + } + + return ret; +} + +void SrsConfig::print_help(char** argv) +{ + printf( + RTMP_SIG_SRS_NAME" "RTMP_SIG_SRS_VERSION" "RTMP_SIG_SRS_COPYRIGHT"\n" + "Primary Authors: "RTMP_SIG_SRS_PRIMARY_AUTHROS"\n" + "Build: "SRS_BUILD_DATE" Configuration: "SRS_CONFIGURE"\n" + "Usage: %s [-h?vV] [-c ]\n" + "\n" + "Options:\n" + " -?-h : show help\n" + " -v-V : show version and exit\n" + " -c filename : set configuration file\n" + "\n" + RTMP_SIG_SRS_WEB"\n" + RTMP_SIG_SRS_URL"\n" + "Email: "RTMP_SIG_SRS_EMAIL"\n" + "\n", + argv[0]); +} + +SrsConfDirective* SrsConfig::get_vhost(string vhost) +{ + srs_assert(root); + + for (int i = 0; i < (int)root->directives.size(); i++) { + SrsConfDirective* conf = root->at(i); + + if (conf->name != "vhost") { + continue; + } + + if (conf->arg0() == vhost) { + return conf; + } + } + + if (vhost != RTMP_VHOST_DEFAULT) { + return get_vhost(RTMP_VHOST_DEFAULT); + } + + return NULL; +} + +SrsConfDirective* SrsConfig::get_vhost_on_connect(string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + conf = conf->get("http_hooks"); + if (!conf) { + return NULL; + } + + SrsConfDirective* enabled = conf->get("enabled"); + if (!enabled || enabled->arg0() != "on") { + return NULL; + } + + return conf->get("on_connect"); +} + +SrsConfDirective* SrsConfig::get_vhost_on_close(string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + conf = conf->get("http_hooks"); + if (!conf) { + return NULL; + } + + SrsConfDirective* enabled = conf->get("enabled"); + if (!enabled || enabled->arg0() != "on") { + return NULL; + } + + return conf->get("on_close"); +} + +SrsConfDirective* SrsConfig::get_vhost_on_publish(string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + conf = conf->get("http_hooks"); + if (!conf) { + return NULL; + } + + SrsConfDirective* enabled = conf->get("enabled"); + if (!enabled || enabled->arg0() != "on") { + return NULL; + } + + return conf->get("on_publish"); +} + +SrsConfDirective* SrsConfig::get_vhost_on_unpublish(string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + conf = conf->get("http_hooks"); + if (!conf) { + return NULL; + } + + SrsConfDirective* enabled = conf->get("enabled"); + if (!enabled || enabled->arg0() != "on") { + return NULL; + } + + return conf->get("on_unpublish"); +} + +SrsConfDirective* SrsConfig::get_vhost_on_play(string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + conf = conf->get("http_hooks"); + if (!conf) { + return NULL; + } + + SrsConfDirective* enabled = conf->get("enabled"); + if (!enabled || enabled->arg0() != "on") { + return NULL; + } + + return conf->get("on_play"); +} + +SrsConfDirective* SrsConfig::get_vhost_on_stop(string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + conf = conf->get("http_hooks"); + if (!conf) { + return NULL; + } + + SrsConfDirective* enabled = conf->get("enabled"); + if (!enabled || enabled->arg0() != "on") { + return NULL; + } + + return conf->get("on_stop"); +} + +bool SrsConfig::get_vhost_enabled(string vhost) +{ + SrsConfDirective* vhost_conf = get_vhost(vhost); + + return get_vhost_enabled(vhost_conf); +} + +bool SrsConfig::get_vhost_enabled(SrsConfDirective* vhost) +{ + if (!vhost) { + return false; + } + + SrsConfDirective* conf = vhost->get("enabled"); + if (!conf) { + return true; + } + + if (conf->arg0() == "off") { + return false; + } + + return true; +} + +SrsConfDirective* SrsConfig::get_transcode(string vhost, string scope) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + SrsConfDirective* transcode = conf->get("transcode"); + if (!transcode) { + return NULL; + } + + if (transcode->arg0() == scope) { + return transcode; + } + + return NULL; +} + +bool SrsConfig::get_transcode_enabled(SrsConfDirective* transcode) +{ + if (!transcode) { + return false; + } + + SrsConfDirective* conf = transcode->get("enabled"); + if (!conf || conf->arg0() != "on") { + return false; + } + + return true; +} + +string SrsConfig::get_transcode_ffmpeg(SrsConfDirective* transcode) +{ + if (!transcode) { + return ""; + } + + SrsConfDirective* conf = transcode->get("ffmpeg"); + if (!conf) { + return ""; + } + + return conf->arg0(); +} + +void SrsConfig::get_transcode_engines(SrsConfDirective* transcode, std::vector& engines) +{ + if (!transcode) { + return; + } + + for (int i = 0; i < (int)transcode->directives.size(); i++) { + SrsConfDirective* conf = transcode->directives[i]; + + if (conf->name == "engine") { + engines.push_back(conf); + } + } + + return; +} + +bool SrsConfig::get_engine_enabled(SrsConfDirective* engine) +{ + if (!engine) { + return false; + } + + SrsConfDirective* conf = engine->get("enabled"); + if (!conf || conf->arg0() != "on") { + return false; + } + + return true; +} + +string SrsConfig::get_engine_vcodec(SrsConfDirective* engine) +{ + if (!engine) { + return ""; + } + + SrsConfDirective* conf = engine->get("vcodec"); + if (!conf) { + return ""; + } + + return conf->arg0(); +} + +int SrsConfig::get_engine_vbitrate(SrsConfDirective* engine) +{ + if (!engine) { + return 0; + } + + SrsConfDirective* conf = engine->get("vbitrate"); + if (!conf) { + return 0; + } + + return ::atoi(conf->arg0().c_str()); +} + +double SrsConfig::get_engine_vfps(SrsConfDirective* engine) +{ + if (!engine) { + return 0; + } + + SrsConfDirective* conf = engine->get("vfps"); + if (!conf) { + return 0; + } + + return ::atof(conf->arg0().c_str()); +} + +int SrsConfig::get_engine_vwidth(SrsConfDirective* engine) +{ + if (!engine) { + return 0; + } + + SrsConfDirective* conf = engine->get("vwidth"); + if (!conf) { + return 0; + } + + return ::atoi(conf->arg0().c_str()); +} + +int SrsConfig::get_engine_vheight(SrsConfDirective* engine) +{ + if (!engine) { + return 0; + } + + SrsConfDirective* conf = engine->get("vheight"); + if (!conf) { + return 0; + } + + return ::atoi(conf->arg0().c_str()); +} + +int SrsConfig::get_engine_vthreads(SrsConfDirective* engine) +{ + if (!engine) { + return 0; + } + + SrsConfDirective* conf = engine->get("vthreads"); + if (!conf) { + return 0; + } + + return ::atoi(conf->arg0().c_str()); +} + +string SrsConfig::get_engine_vprofile(SrsConfDirective* engine) +{ + if (!engine) { + return ""; + } + + SrsConfDirective* conf = engine->get("vprofile"); + if (!conf) { + return ""; + } + + return conf->arg0(); +} + +string SrsConfig::get_engine_vpreset(SrsConfDirective* engine) +{ + if (!engine) { + return ""; + } + + SrsConfDirective* conf = engine->get("vpreset"); + if (!conf) { + return ""; + } + + return conf->arg0(); +} + +void SrsConfig::get_engine_vparams(SrsConfDirective* engine, std::vector& vparams) +{ + if (!engine) { + return; + } + + SrsConfDirective* conf = engine->get("vparams"); + if (!conf) { + return; + } + + for (int i = 0; i < (int)conf->directives.size(); i++) { + SrsConfDirective* p = conf->directives[i]; + if (!p) { + continue; + } + + vparams.push_back("-" + p->name); + vparams.push_back(p->arg0()); + } +} + +void SrsConfig::get_engine_vfilter(SrsConfDirective* engine, std::vector& vfilter) +{ + if (!engine) { + return; + } + + SrsConfDirective* conf = engine->get("vfilter"); + if (!conf) { + return; + } + + for (int i = 0; i < (int)conf->directives.size(); i++) { + SrsConfDirective* p = conf->directives[i]; + if (!p) { + continue; + } + + vfilter.push_back("-" + p->name); + vfilter.push_back(p->arg0()); + } +} + +string SrsConfig::get_engine_acodec(SrsConfDirective* engine) +{ + if (!engine) { + return ""; + } + + SrsConfDirective* conf = engine->get("acodec"); + if (!conf) { + return ""; + } + + return conf->arg0(); +} + +int SrsConfig::get_engine_abitrate(SrsConfDirective* engine) +{ + if (!engine) { + return 0; + } + + SrsConfDirective* conf = engine->get("abitrate"); + if (!conf) { + return 0; + } + + return ::atoi(conf->arg0().c_str()); +} + +int SrsConfig::get_engine_asample_rate(SrsConfDirective* engine) +{ + if (!engine) { + return 0; + } + + SrsConfDirective* conf = engine->get("asample_rate"); + if (!conf) { + return 0; + } + + return ::atoi(conf->arg0().c_str()); +} + +int SrsConfig::get_engine_achannels(SrsConfDirective* engine) +{ + if (!engine) { + return 0; + } + + SrsConfDirective* conf = engine->get("achannels"); + if (!conf) { + return 0; + } + + return ::atoi(conf->arg0().c_str()); +} + +void SrsConfig::get_engine_aparams(SrsConfDirective* engine, std::vector& aparams) +{ + if (!engine) { + return; + } + + SrsConfDirective* conf = engine->get("aparams"); + if (!conf) { + return; + } + + for (int i = 0; i < (int)conf->directives.size(); i++) { + SrsConfDirective* p = conf->directives[i]; + if (!p) { + continue; + } + + aparams.push_back("-" + p->name); + aparams.push_back(p->arg0()); + } +} + +string SrsConfig::get_engine_output(SrsConfDirective* engine) +{ + if (!engine) { + return ""; + } + + SrsConfDirective* conf = engine->get("output"); + if (!conf) { + return ""; + } + + return conf->arg0(); +} + +string SrsConfig::get_log_dir() +{ + srs_assert(root); + + SrsConfDirective* conf = root->get("log_dir"); + if (!conf || conf->arg0().empty()) { + return "./objs/logs"; + } + + return conf->arg0(); +} + +int SrsConfig::get_max_connections() +{ + srs_assert(root); + + SrsConfDirective* conf = root->get("max_connections"); + if (!conf || conf->arg0().empty()) { + return 2000; + } + + return ::atoi(conf->arg0().c_str()); +} + +bool SrsConfig::get_gop_cache(string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return true; + } + + conf = conf->get("gop_cache"); + if (conf && conf->arg0() == "off") { + return false; + } + + return true; +} + +double SrsConfig::get_queue_length(string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return SRS_CONF_DEFAULT_QUEUE_LENGTH; + } + + conf = conf->get("queue_length"); + if (!conf || conf->arg0().empty()) { + return SRS_CONF_DEFAULT_QUEUE_LENGTH; + } + + return ::atoi(conf->arg0().c_str()); +} + +SrsConfDirective* SrsConfig::get_forward(string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("forward"); +} + +SrsConfDirective* SrsConfig::get_hls(string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("hls"); +} + +bool SrsConfig::get_hls_enabled(string vhost) +{ + SrsConfDirective* hls = get_hls(vhost); + + if (!hls) { + return false; + } + + SrsConfDirective* conf = hls->get("enabled"); + + if (!conf) { + return false; + } + + if (conf->arg0() == "on") { + return true; + } + + return false; +} + +string SrsConfig::get_hls_path(string vhost) +{ + SrsConfDirective* hls = get_hls(vhost); + + if (!hls) { + return SRS_CONF_DEFAULT_HLS_PATH; + } + + SrsConfDirective* conf = hls->get("hls_path"); + + if (!conf) { + return SRS_CONF_DEFAULT_HLS_PATH; + } + + return conf->arg0(); +} + +double SrsConfig::get_hls_fragment(string vhost) +{ + SrsConfDirective* hls = get_hls(vhost); + + if (!hls) { + return SRS_CONF_DEFAULT_HLS_FRAGMENT; + } + + SrsConfDirective* conf = hls->get("hls_fragment"); + + if (!conf) { + return SRS_CONF_DEFAULT_HLS_FRAGMENT; + } + + return ::atof(conf->arg0().c_str()); +} + +double SrsConfig::get_hls_window(string vhost) +{ + SrsConfDirective* hls = get_hls(vhost); + + if (!hls) { + return SRS_CONF_DEFAULT_HLS_WINDOW; + } + + SrsConfDirective* conf = hls->get("hls_window"); + + if (!conf) { + return SRS_CONF_DEFAULT_HLS_WINDOW; + } + + return ::atof(conf->arg0().c_str()); +} + +SrsConfDirective* SrsConfig::get_refer(string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("refer"); +} + +SrsConfDirective* SrsConfig::get_refer_play(string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("refer_play"); +} + +SrsConfDirective* SrsConfig::get_refer_publish(string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("refer_publish"); +} + +SrsConfDirective* SrsConfig::get_listen() +{ + return root->get("listen"); +} + +int SrsConfig::get_chunk_size(const std::string &vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return SRS_CONF_DEFAULT_CHUNK_SIZE; + } + + conf = conf->get("chunk_size"); + if (!conf) { + // vhost does not specify the chunk size, + // use the global instead. + conf = root->get("chunk_size"); + if (!conf) { + return SRS_CONF_DEFAULT_CHUNK_SIZE; + } + + return ::atoi(conf->arg0().c_str()); + } + + return ::atoi(conf->arg0().c_str()); +} + +int SrsConfig::get_pithy_print_publish() +{ + SrsConfDirective* pithy = root->get("pithy_print"); + if (!pithy) { + return SRS_STAGE_PUBLISH_USER_INTERVAL_MS; + } + + pithy = pithy->get("publish"); + if (!pithy) { + return SRS_STAGE_PUBLISH_USER_INTERVAL_MS; + } + + return ::atoi(pithy->arg0().c_str()); +} + +int SrsConfig::get_pithy_print_forwarder() +{ + SrsConfDirective* pithy = root->get("pithy_print"); + if (!pithy) { + return SRS_STAGE_FORWARDER_INTERVAL_MS; + } + + pithy = pithy->get("forwarder"); + if (!pithy) { + return SRS_STAGE_FORWARDER_INTERVAL_MS; + } + + return ::atoi(pithy->arg0().c_str()); +} + +int SrsConfig::get_pithy_print_hls() +{ + SrsConfDirective* pithy = root->get("pithy_print"); + if (!pithy) { + return SRS_STAGE_HLS_INTERVAL_MS; + } + + pithy = pithy->get("hls"); + if (!pithy) { + return SRS_STAGE_HLS_INTERVAL_MS; + } + + return ::atoi(pithy->arg0().c_str()); +} + +bool SrsConfig::get_bw_check_enabled(const string &vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return false; + } + + conf = conf->get("bandcheck"); + if (!conf) { + return false; + } + + conf = conf->get("enabled"); + if (!conf || conf->arg0() != "on") { + return false; + } + + return true; +} + +string SrsConfig::get_bw_check_key(const string &vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return ""; + } + + conf = conf->get("bandcheck"); + if (!conf) { + return ""; + } + + conf = conf->get("key"); + if (!conf) { + return ""; + } + + return conf->arg0(); +} + +int SrsConfig::get_bw_check_interval_ms(const string &vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return SRS_CONF_DEFAULT_BANDWIDTH_INTERVAL; + } + + conf = conf->get("bandcheck"); + if (!conf) { + return SRS_CONF_DEFAULT_BANDWIDTH_INTERVAL; + } + + conf = conf->get("interval_ms"); + if (!conf) { + return SRS_CONF_DEFAULT_BANDWIDTH_INTERVAL; + } + + return ::atoi(conf->arg0().c_str()) * 1000; +} + +int SrsConfig::get_bw_check_limit_kbps(const string &vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return SRS_CONF_DEFAULT_BANDWIDTH_LIMIT_KBPS; + } + + conf = conf->get("bandcheck"); + if (!conf) { + return SRS_CONF_DEFAULT_BANDWIDTH_LIMIT_KBPS; + } + + conf = conf->get("limit_kbps"); + if (!conf) { + return SRS_CONF_DEFAULT_BANDWIDTH_LIMIT_KBPS; + } + + return ::atoi(conf->arg0().c_str()); +} + +int SrsConfig::get_pithy_print_encoder() +{ + SrsConfDirective* pithy = root->get("encoder"); + if (!pithy) { + return SRS_STAGE_ENCODER_INTERVAL_MS; + } + + pithy = pithy->get("forwarder"); + if (!pithy) { + return SRS_STAGE_ENCODER_INTERVAL_MS; + } + + return ::atoi(pithy->arg0().c_str()); +} + +int SrsConfig::get_pithy_print_play() +{ + SrsConfDirective* pithy = root->get("pithy_print"); + if (!pithy) { + return SRS_STAGE_PLAY_USER_INTERVAL_MS; + } + + pithy = pithy->get("play"); + if (!pithy) { + return SRS_STAGE_PLAY_USER_INTERVAL_MS; + } + + return ::atoi(pithy->arg0().c_str()); +} + +bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b) +{ + // both NULL, equal. + if (!a && !b) { + return true; + } + + if (!a || !b) { + return false; + } + + if (a->name != b->name) { + return false; + } + + if (a->args.size() != b->args.size()) { + return false; + } + + for (int i = 0; i < (int)a->args.size(); i++) { + if (a->args.at(i) != b->args.at(i)) { + return false; + } + } + + if (a->directives.size() != b->directives.size()) { + return false; + } + + for (int i = 0; i < (int)a->directives.size(); i++) { + SrsConfDirective* a0 = a->at(i); + SrsConfDirective* b0 = b->at(i); + + if (!srs_directive_equals(a0, b0)) { + return false; + } + } + + return true; +} diff --git a/trunk/src/core/srs_core_rtmp.cpp b/trunk/src/core/srs_core_rtmp.cpp old mode 100755 new mode 100644 index 11ead12ef..4164436d2 --- a/trunk/src/core/srs_core_rtmp.cpp +++ b/trunk/src/core/srs_core_rtmp.cpp @@ -1,1187 +1,1186 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013-2014 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace std; - -/** -* 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" -// status error -#define StatusLevelError "error" -// code value -#define StatusCodeConnectSuccess "NetConnection.Connect.Success" -#define StatusCodeConnectRejected "NetConnection.Connect.Rejected" -#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() -{ -} - -SrsRequest* SrsRequest::copy() -{ - SrsRequest* cp = new SrsRequest(); - - cp->app = app; - cp->objectEncoding = objectEncoding; - cp->pageUrl = pageUrl; - cp->port = port; - cp->schema = schema; - cp->stream = stream; - cp->swfUrl = swfUrl; - cp->tcUrl = tcUrl; - cp->vhost = vhost; - - return cp; -} - -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); - strip(); - - // 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; -} - -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(string& str, 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); - - skt.set_recv_timeout(protocol->get_recv_timeout()); - skt.set_send_timeout(protocol->get_send_timeout()); - - 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(string app, 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(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(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_send_timeout() -{ - return protocol->get_send_timeout(); -} - -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); - - skt.set_recv_timeout(protocol->get_recv_timeout()); - skt.set_send_timeout(protocol->get_send_timeout()); - - 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, const char* server_ip) -{ - 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_KEY" "RTMP_SIG_SRS_VERSION" ("RTMP_SIG_SRS_URL_SHORT")")); - 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)); - data->set("srs_primary_authors", new SrsAmf0String(RTMP_SIG_SRS_PRIMARY_AUTHROS)); - - if (server_ip) { - data->set("srs_server_ip", new SrsAmf0String(server_ip)); - } - - 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; -} - -void SrsRtmp::response_connect_reject(SrsRequest *req, const char* desc) -{ - int ret = ERROR_SUCCESS; - - SrsConnectAppResPacket* pkt = new SrsConnectAppResPacket(); - pkt->command_name = "_error"; - pkt->props->set(StatusLevel, new SrsAmf0String(StatusLevelError)); - pkt->props->set(StatusCode, new SrsAmf0String(StatusCodeConnectRejected)); - pkt->props->set(StatusDescription, new SrsAmf0String(desc)); - //pkt->props->set("objectEncoding", new SrsAmf0Number(req->objectEncoding)); - - SrsCommonMessage* msg = (new SrsCommonMessage())->set_packet(pkt, 0); - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send connect app response rejected message failed. ret=%d", ret); - return; - } - srs_info("send connect app response rejected message success."); - - return; -} - -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, 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, 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, string& stream_name) -{ - int ret = ERROR_SUCCESS; - - type = SrsClientFlashPublish; - stream_name = req->stream_name; - - return ret; -} - +/* +The MIT License (MIT) + +Copyright (c) 2013-2014 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +/** +* 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" +// status error +#define StatusLevelError "error" +// code value +#define StatusCodeConnectSuccess "NetConnection.Connect.Success" +#define StatusCodeConnectRejected "NetConnection.Connect.Rejected" +#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() +{ +} + +SrsRequest* SrsRequest::copy() +{ + SrsRequest* cp = new SrsRequest(); + + cp->app = app; + cp->objectEncoding = objectEncoding; + cp->pageUrl = pageUrl; + cp->port = port; + cp->schema = schema; + cp->stream = stream; + cp->swfUrl = swfUrl; + cp->tcUrl = tcUrl; + cp->vhost = vhost; + + return cp; +} + +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); + strip(); + + // 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; +} + +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(string& str, 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); + + skt.set_recv_timeout(protocol->get_recv_timeout()); + skt.set_send_timeout(protocol->get_send_timeout()); + + 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(string app, 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(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(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_send_timeout() +{ + return protocol->get_send_timeout(); +} + +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); + + skt.set_recv_timeout(protocol->get_recv_timeout()); + skt.set_send_timeout(protocol->get_send_timeout()); + + 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, const char* server_ip) +{ + 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_KEY" "RTMP_SIG_SRS_VERSION" ("RTMP_SIG_SRS_URL_SHORT")")); + 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)); + data->set("srs_primary_authors", new SrsAmf0String(RTMP_SIG_SRS_PRIMARY_AUTHROS)); + + if (server_ip) { + data->set("srs_server_ip", new SrsAmf0String(server_ip)); + } + + 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; +} + +void SrsRtmp::response_connect_reject(SrsRequest *req, const char* desc) +{ + int ret = ERROR_SUCCESS; + + SrsConnectAppResPacket* pkt = new SrsConnectAppResPacket(); + pkt->command_name = "_error"; + pkt->props->set(StatusLevel, new SrsAmf0String(StatusLevelError)); + pkt->props->set(StatusCode, new SrsAmf0String(StatusCodeConnectRejected)); + pkt->props->set(StatusDescription, new SrsAmf0String(desc)); + //pkt->props->set("objectEncoding", new SrsAmf0Number(req->objectEncoding)); + + SrsCommonMessage* msg = (new SrsCommonMessage())->set_packet(pkt, 0); + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send connect app response rejected message failed. ret=%d", ret); + return; + } + srs_info("send connect app response rejected message success."); + + return; +} + +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, 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, 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, string& stream_name) +{ + int ret = ERROR_SUCCESS; + + type = SrsClientFlashPublish; + stream_name = req->stream_name; + + return ret; +} From 11e465ae9e0e929a04ce5059f6ed13608bd7a60d Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 1 Jan 2014 11:32:09 +0800 Subject: [PATCH 13/26] refine readme. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 0ad829cb0..7592491a8 100755 --- a/README.md +++ b/README.md @@ -13,9 +13,11 @@ TencentQQ: [http://url.cn/WAHICw](http://url.cn/WAHICw) (Group: 212189142) The PRIMARY AUTHORS are (and/or have been)(Authors ordered by first contribution):
* winlin([winterserver](#)): [http://blog.csdn.net/win_lin](http://blog.csdn.net/win_lin)
* wenjie([wenjiegit](https://github.com/wenjiegit/simple-rtmp-server)): [http://blog.chinaunix.net/uid/25006789.html](http://blog.chinaunix.net/uid/25006789.html)
+ About the primary AUTHORS:
* Contribute important features to SRS.
* Names of all PRIMARY AUTHORS response in NetConnection.connect and metadata.
+ And here is an inevitably incomplete list of MUCH-APPRECIATED CONTRIBUTORS --
people who have submitted patches, reported bugs, added translations, helped
answer newbie questions, and generally made SRS that much better: [AUTHORS.txt](https://github.com/winlinvip/simple-rtmp-server/blob/master/AUTHORS.txt) From 273bdb224202f6dc03cd177d2933109e49accbb2 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 1 Jan 2014 11:38:48 +0800 Subject: [PATCH 14/26] update flash client. --- .../players/srs_bwt/release/srs_bwt.swf | Bin 4666 -> 4675 bytes trunk/research/players/srs_bwt/src/srs_bwt.as | 4 ++-- .../players/srs_player/release/srs_player.swf | Bin 5332 -> 5355 bytes .../players/srs_player/src/srs_player.as | 4 ++-- .../srs_publisher/release/srs_publisher.swf | Bin 5203 -> 5205 bytes .../srs_publisher/src/srs_publisher.as | 4 ++-- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/trunk/research/players/srs_bwt/release/srs_bwt.swf b/trunk/research/players/srs_bwt/release/srs_bwt.swf index 5ead12bea1c1efca27b02d65b9d0c5ab1ea836c5..e60ceaf69b3e014a8c809a17a2226590672e6f6c 100755 GIT binary patch literal 4675 zcmV-J61?q0S5pp?9smG%0gX8ebQ{%`b7w|+8u=%G<2XNsI0*s8mj53@uwq-m&d2vCX(m!4OR8q%*pw2(0xd}h5XvtQaFPZJZDG4WVV4xjZ=vndD#?;;x82fR>X7c) zJs#TA-Lq%+toOb*Bik~eXYKReeeb^ezxTa)GaBW@f8n^|XF0AKxSrC59LMb||0!_X zx^zVC+}`7Nj}FHZS|`q~ULDo-RA+tt-o1P4_BPce)4S>$IyyS)y^Zyajcbu&tu~g> z<FMd4oB}S{ zv5EF$HSG4DHpqwebKv@uOrhJ(pO zP>w``dNRluT{vO9n`5|~P|_uq=z0qZ%7|_{G9srn`|#KnC6Or}q)G?Fa$MPxj3`B> z-bhTN#yYm5%5LB0UP;RlI2pY!9@~|03~d_-ZtnFD*5 zV4-lHNkwow1aYtK!kW=2)jX7ImI`yOXEfI=jfp6rZYiTW&8@>UE)#P_V-aNo4QOAY z4@*ZY2xB~x!)#nKhl3)j2DcBoCtrVd^2Hxrc=f==mmZmX?d0S=uZgLQ7A*+RN_T3C zJ`@{P@JKAM6zQ?JRyJFmXd82PRjv>*CnM-#=Pb$G0v1P-u}Ep}D26{F$5|RwIjj_0 zA-3?6JR-;9awx9YbWD>{y<2l9lDeB|^0@W%m^+R-(siZ5ASNNaXEyi?&>*bi_(H==<1yU6#dwn7rom*f@^EhL z9isbqXvd%d^uN1s)ga5 ztY>;`Lw#F%w-+Z93`ILk6g^_n`s(?v&(4msG;yFOxut zY`%0_9us4D28eh{Dk4_3ETJpZTqVtpVNFSoQ2!X{beB_kSRKQ1dMqes^k_1zxdzO+ z&zyF;t1rIw^o2twFFbYs&xVkkE*X|bXU+eRuRi1!UlojOuGtN05NXT&YJG!b1#t;I7&MwGZN7lfzW2^W2wO9g}JEY4snZ<-ZN-qOj}qsa+LX^Sp4Q% zY#n+^Wce2fX)x!|F3X9`DW9j($xfx9j;EYMN#LCb2T}^FBS_O-Xqn0;PKIVBWl~Vu zq8tUo&(PDQg%?hYV@ZAX#={qmKGW%*DfUjc*D+fu;XWDGl{K@?cU0K z3c83zDYmw@HZICvVs(bgY$J)SZB1UWt+Au!QYYNp5)<2+y)9x}Ye&Z=_I4g&djmSr z-qPAum9IFLH?h6FtzGPB>1Yr;+S^+3DQ`yE*5LIvx7*E|Y0131Vo~0;vh)j=(5k$3 zd>ol~*TuQah4H;?e)>dAL%Yz@)>>hMDxu0=RZvyoaNAZauvbG3)YR6jtXWmFy5{n# zhN_OL^;OsLg&^38Lvj`r78RGI2+tEV2Fr2h;Tg- zZXm*qM7W6vHxpqe5duUA65&=!1}Oy6GLRx5si-|B?ZG}y1dV`BAVXk88U<+#q&q;` z2Sm6Nm+!*1AKM{phY|NJkRAh&9>?|swj7w{t)3`WBXfdA7c9{ zwvVu7v7NzZWR+HvwtN53un=Z2T}AKg!0>;+S7`>#g5~@#o+>fO21i@$Un- z>LvODZ0Fp63Gf5>D`4Pd6TD)AS55Gm30^nB-ewRLf=2%)8mYevW~=;GEP6l4>MeF!*(xxu% zRuI6s*@OiSAIC%CN*^ac(G@;Ugkq16vq4FtkF$fT)5j4g4f{9;ltp};1m%j4b3(SM_GnoJ#~CfzfL*n# zB&$0762HdfKwMka?RUT$qXT()QIwo705V^b^-h6{yJiYXp?eC-_-#fPiU~MXr&=Hv z8a_HHQj64Lxy0zssxGxuEgLUCXY`)&EcI8wqh(H%?GIFXF6*xZ!RX7LQLE$yMnA%8 zd7-fhVU4`V*o<(oyu{cRSlYjo8((_f7&z-+#;q}~JFB__%RMct`j_+m%h0~<*|YKr zV<=FYRae$>{#t&Gu>(yOsJpkI#W$n7I|HlL)#~Msmerv0Kwyo>=U;=Yx0p;3#uVIW zgeaM3Lsq@QeZm13YHxdW~Ue&8MWYtC*Wh050)BYwjM9T)6`@80xn1=;0vhz70M68p;|e!|!0m?KtvZjWl1+x>5Z; zWEm+d#f5R8-0N}WPQax$<1Y}=GQtvaHy!$~!L@qxbXGQx3et$U>Gs~k54O?X%uhX9im?q3eq14=O-pd4zq zC*tpck+KD7)q`*bDHMiQdnb^RW%aN{sLl{R%m{axg#H=AMgptPfy=IJ^keLG|r9ZXygeks4Fq>&tu?e(4Pn6yO5>X>F1)GjPKF$ zrmVWzzXdDk`;^5YV6wI&%gYwa%P<9_@Cx>0{HtKRf$eyX?k4HBfr>JBJ(3nz`{H7)OO)%b~3r>MZ&KK!<@V2$`Hq-eI z)A=s;cL?uczfXK0`*E;+0LD9(8Sh$VyoYcg6Bdp4ad2HGY%@N9S}qf|8y`YQtmTS} zG5$Y+kge8nJS3i=3qOOv_K>~SdECDpYMl)A5zsw6Xl?wTqc^mw`zAbh!$inF;kgG) z&wfG8E}&<^LbUocT5X(Rarg$9fjCPE3TY_&klt&h?CQ8Sz ze?Z96_D3ABwEf9S+keca?LYI<_FrF2+kZpWe?6Wy|3l3xp=sHIR{bwc%QSUEy`hYz zf1IbK`iK?ZyZG#?=nms<9=E_|+za>6!OUK`7irDo?HfD_b%_O%bCUXcA8+oY`}ir4 zVL$ed0e=9&;~*SF@C1nWBRB%KLkOM(`vVA`0`ee&qu_W5!P6ie=8gUQX>|i`qyq?V z#Eo(4J6crH;`15-oPGp59@B^maxC*ZsaIO0dG|t#j}%MPY12< zpY=HCo%3Ooqd(Z-Urlw6qH{jQHUFPqUvk`6&Ev*jW5gc(nka>oU9kimU7jPIuV}t$ z1iwE}29bU|aDOS6VtQk}vVS?9^}&^W8~?Mf=?&gyVhI%*8(MQ;YK5lOmYL7U^S>`! zzsvmDmq?+Zse^Cru;D8tUsX{JJO~B!wGeECcM#r5_(HB|h5^Q*fWUm&S zDB;be0LMIMD}v$@Y^B)B7eH|}!i9Kul|;O(@_04Je>=TJ1EIHQntu@-J;*AEHV!}# zg{&8A1NDox>dytkf#oLAo2}Ejeep&CuJb%E+Lq&b5j_Y9S8Nos%h@=5Y?Pi>g)F@z zAG;Rsj8srL5>}v~${86|C__Xmghg1uf`OOafTT)Z+F_kW9f)HOkMg-cA9kUO{|7x= FLJCvM7v2B> literal 4666 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 diff --git a/trunk/research/players/srs_bwt/src/srs_bwt.as b/trunk/research/players/srs_bwt/src/srs_bwt.as index 1ae01d639..bb12e965d 100755 --- a/trunk/research/players/srs_bwt/src/srs_bwt.as +++ b/trunk/research/players/srs_bwt/src/srs_bwt.as @@ -143,8 +143,8 @@ package if (evt.info.data.hasOwnProperty("srs_server")) { customItems.push(new ContextMenuItem("Server: " + evt.info.data.srs_server)); } - if (evt.info.data.hasOwnProperty("srs_contributor")) { - customItems.push(new ContextMenuItem("Contributor: " + evt.info.data.srs_contributor)); + if (evt.info.data.hasOwnProperty("srs_primary_authors")) { + customItems.push(new ContextMenuItem("PrimaryAuthors: " + evt.info.data.srs_primary_authors)); } contextMenu.customItems = customItems; } diff --git a/trunk/research/players/srs_player/release/srs_player.swf b/trunk/research/players/srs_player/release/srs_player.swf index bb8c116471eaee436676ad569e19f8fc6a3e3698..264bef415b3deacd48b07ce3349443fd8e229fa4 100755 GIT binary patch literal 5355 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 literal 5332 zcmV;_6f5gPS5pp=A^-q*0exBtcpSyC?wXn1(XLKQvV1|jfUuE~C9MwYz*f9iT4Uo2 zOTLJg^=NlSyY?Q+&a7lXf@OovPmV$l?6mv$`BV>-&1DtNO3%>gww1mh8Pu`2}MopJHq-v94M37-Rb?ULnSoXQD>O z=B}V`Z!(cGJHV}29J8!}j{5qcp`p5=rn+>dzuw>8-d^9(Sl`&V6o{qfaLUs5E=`#i zE$$Qsx^y#=i4R!ubjrsKYJKUfwPNw&ywzxA(&#`olMp7Ok$OF$C-szN*8A)HFcgh+ z80kz>vpThbfkZr_adY*1mzuG3WY3T`s4q1Vni*SOKLzAER@_SHoq=e&Pxl2A`d(kN zFEFJq&<=!yqf?G{PFtqNwd*43Wc@%U9nD7IlL1pgdAbTWF_7&`#LbwV>CC3~q|!qv zftr$XOeCXgR(d94LdM4vTB<**_3NG88-(^GFMQN2y|YV?_!^hh`WhSjO^zE}y1br8 zcUrXb5^P{6D=Ev)+puxzra({6wvC&+SgsViJmaUU>`i$&TP2-7efnyzj3GCg;(p+2 z2X)^SUtYQjIq{0u%$VVUgf^^a*b9}-KE@zr(}ZQLj>#hnscGqRCc)~1n02Y_K zdj>LbOD}eYqj8f@Q5s37tV}u)Hq3BRGxxYm3-NVz4qr%KD(?5!MH_j9HPF`w>XQ`%?wI8#jg5bq9N08fQzyNKZsd=(Sy9v zQ^ICpaLc-N0B!E>-Y_?7>KX2}a5roKi6aPP0X$z7izlMqzN}@XQ{1+9eHw6K!?kMN znpIa;OlU`N&h~@{<54|bxC#-}_gd@qRJI@=`gkD^YhG9u1MKo_JYlU#twGF8C6i8D z-b7l9>X|huBkdi~G8*s9G0ogKl-iU@59k?d_=5bYn>ulUo~HW7 zhK6PJecW_Bjt$=ZxthpE^WX`M739P!(Le%`hCr&N!JL zMk$C6mqU7{Xo{c6``I&46Vhq6ME=jTB}86vV4Mx^U9-M>bLmuC3jjY38Ov+x7Du8u zmrnsOsqnF8D!3?v@j(=vjw#gi)1eHe5T3ZTjUe@R5u?XiW|c|1dHeV^dW^a zMSWSr&@=1wRKFFw@SHvfT~{EJ(S{2ql1WbY?N%bF32xMnK@`~t&RIGs&U5Ls3cv-P zo{YIk+yVtX0^H$K2C`;M6opJ2^(CDt<~sR)hi^_e+>d~XdLkki#&oCyGW>K8#Z!rR zs)omo@2cH+yS0gke8P}r$|jXEJ2*8k7ribGB}?eGN%gjoM@Wov=IBIY}OU!? zs2O2iIQlc($zp7`*ikW+*Qu-<@cfvp)!d!xh60g>@YO9kLk@{m;ACekpOyA;m=EJ9 zZneIl75Kzf6vL6J4Jk;cF5lLbPU$Mb&o{|sMYd!T=gJ&z)GLy28((vw=w2aKyyPqi z_y3g5vZ>nU=$1|igk87u=Bo&Yvja#MeOl#hJnJ+w>f@P(@N5r&Giqo4>?^!rC}9)| zDLf>FV{+IWLW|K}8a~@OKZ}0_{TXc_7LS;oKD|Gl3dR$O0>06k5nokLMY-VYp*m|i z@S7*ipYKuKh^ONG93jIJ1M)!Hl-pXH<@V-fioc=3k55ax;`g^SD*ndiR(#r;6@OC; z|FnZ`Zf{ZiEzS5WYihuUkG3{Jx3#TF@wYWKDvb?IexE?U)IosFO-34 zTh`j_^0&7xYbrhCU0FQi2|DZF9;{U}#*w_vJNLYaxI}PSpBWsb5y6mK+gmDKROPPn zR7q9ERn-McT@7=jxip{V`{y^#Z<^mczojZz^#D~pNKz3gYJtaFSX5k6TJ{lA!AdTb zR8=le$*U@bs;fvnOUl`>VSROQ*KQl%<$RC%td%u|*5s%wF&oTt+Hs`4IHS*W^v zs&au!7pm@yROMn-S)?k9Rpk;@sZo_Bs#>cmb)HKx(K1gfzHOd%d^P?suETe zO?CHsbSz-EXAh~0p;A&+GAfxWS*kLqDtlFVSXHi7l@F-O^(q~}#5VwV5cFoWThPA^ z?L%mHfWH&;!)Qm)zYBDfJW+J-CDnBwc|JzusU^?HK|VpGmLDax;uxu(hk*Pf=wZ-L zk>}GGQyvEY2pl_({$rqzqkR_m&q4kKWKV)V1^P7FGiaa3G|z%Q2YI>bF;vfgLh=Q) zFQR=3vwej;U*(p+2E&Bur6mrPuXm`jZzb!IWO9v=({XQhsCtz{3wj-(XN$vocdlgn36={x zS27*6Xva#H0WF3BAEOd^CANY}T`QTwsLT^!63uD|Fqz7i2ADz>;Q(_{Wo3Z5Y4*GT zQ>m&gzzS$idw_XpZbyK5Y2IZ4R!H-g2Urm;XbrGpIbZ=lz$51xRzob4$GiD0+3v_S*p0a@EJk)y zE+k$2n6y@+m~6M@Ldsf&#a#Afxi!pQo-^E|<&})tD{>*#P>q6|;R$-Bnz91O-kb9U zJyc@{U|Eu7kM}Kt>D9T0Q&i4YpP~xdYF{x;uTZM7*TQ0`$S5+3wG#VE&XgLZMwvFt zUeB3wqui*-8I^nvdt)wVxPr4W!zOeVX;t=BprJX&9Aj?Im?tnjxzPNz^O?55?#&qs zg6FXsduz@(-*}I)aKv}g-acM?Y48FXE%(CT9ia(^dLvb{4mZd{~Q+ab`4wMF(d zpo_Ij>@aAJw#3$Qh8()oxYVf48B4`HeUJp}U_BbD&lwHXELaZ@b*x5dd`y@OHP@~QHe&%PM=L6{(yQ$Zht(n&Ex~25V+s}#Xcep_*o`@(O|V(P zV(smM9TaR&&gc;AkYKmujLQVOSFqc1#&W@4%h{Z+r(iffW~@32NU#f$y%&Hz+y)2S=KvC8M)xG(qZC?TEa+3T zpnX5(f~y(Niw9`DHl?kykAbGOZu_B$X*{s0Wa9uYSo+6@TE)A?vWP4+W1W?V5^UIc~D!>2XO zIZ)4`zZN<6Jl9ht=!K#8LSF9$vR_2x-Nu#9K7EC6%#hfauVSCRA;#8))*I`Mipp}B zewj-Izd>79RszYlCM6ZxI{OtqO#yC5uMgAYFpWJ1KH_oi@rgxH(d_5DO|lKpFJd+aLvH}jjC*xNviD1334F^A z@a~wl&Aw3rx>&+WcL9A1(Dtnu+c8+q<8+&ZLbD@yHL~fjl=Epj?Ask1voLrE?*`w8 z<=x50I_)D6>>MoTV|Vec7`vN~?X*YvJSB|0UN`1>lyjxBR9M)-E8^t?tF(^C{yIZqI(r6AD6Z*If;e-2hnj!XE2sV{v0sVPG3^8 zpU7(&TJ6Uv*hhoq=L7d7mrDE~PUstv$>a9)l*%WjRGyhoNldClCsm%6_zC4UaYnx| zp?^*7{Zye~x-Uwr?H8nLIzxJBr?GRbPO(n=MJd#2bcT#jztO+eAT4Hp#gX7o>DceD z3T6w_cH3VQU1N7BY=pHv_Dc?Im$A!8tSsfF9dZ4x6iOP&+D9pvqH0!Ifl0sT;2;jB zsiy1_h{oU<#|i9bC^$gD_Y-1oj|(jPmpRscRk~(JD6=-hVms`UQfP;d&cgS zCE5Xgryl2Q$l0#4{C52UjwfhxyUMUPZ*Z6|cmw74>X(4Pg=&URZGNvLtlj<<2SVK~ zu=bmXpF7EDD>IqY!?H~7VR5ExXZ_5zo!y8R_f}S}pvv6Oov}rkiLke%+9%2WBTVfu ztXkCbw?%hwi!|bU)qV#I0l+UMexm;(jX_`>B<&#C?~1{vCkOxL3?3rw5ZR}NnVXP< zC0ubthT;z6CZTvh=KY%|#%^%NZWelnkZw_V{2qKV-MdtL-DQb3MLl-viuc^Q1irdKK=sd*yMQ3tm$1 zlgErhgrSc@bh@$-qK^UOHg4g<`(@mN0(TU+g9O!M5IML91@2ZZ{4~0~wXaYm$_WqU zBl#g7%SZF(oM3)d&Yi%$dl0+1g$#A>`6qb3?{)Pw#e-*zB)ILbQE$7CufBU#jo;Y5enak4#*U3c zslfiF02BcFE74O>zJ4QmE}oOt1J%-l+F`PPD`aji8x*n+k^Or)*XQbUi`w-EzVIp+ zuK4S)?zhG6JVESju^@U|w*LeJw8=y8U^AXJg~RI|*;hcZ4MPVL}0U7kwA- zOQGT5|CX1)!lcIE^D;;0AKZmG5`H7PJjdT;^H9=#hOZ%X7a4a^L+EZY?gqJsjC(*v m$ruGu3`Kmlv-8DI>-Fo&4#8A*@?VIwfN4aS_=D3FkYWET=30g{kw*<4G;@&Ro?_cwq&A4&z^o& znMx*7W)E_`8)B9<(bLp4IXT%l+18lOj5oD(cXu~6w>Gu5ZbXZX=5)%^rZ%R`vo~zz z0R6fd$;2nDcsivpKrNikTD==K6i}m)1<;9XCcz=2ktRK%C-szNHnlXiAW$^YW27@l z&DyF>OeEqFjlnfdZ8T%)$kmftPTy!GG&8oPY0;4RSaB<%Zw*A#VO>!Z`jpbH1Qz|d zwPV7Jql2`C5jSIcW@|Qebt*lX;#P}N zW)sQinw4H^QIs)#LQ9QjwQ+sxzzFwV;CYOirEeY9Qc8D$4ccNgzElq53Ph0GQ z1ov+%DXYk?9NE2bG%z-HVE3MWk}t7#GG_12|c0ab2nqWeRaI#w;0 z|MbE>Oo^AgX2uLHOo+TtEv+L26=bQrf;19|M0I04M#%lNbg6dpo}SB5a(PBKbuAN# zT|SY}rgao7F+(o0dGiucqh|WDrk)AqBI&3earzqXyG#og%{7q2tSRBi(5SP%Sr+K; zALtM5-5na+8`v>WYfhV%o(!c^AuSpWS?LgWtU!kQ?u?^4rDs+z8MtW)C+Im#=40fv zj0usdjGj#A^dI_>nbyZvEtS*WN-*iV|2O( z6>pa=HPrd+&ot!HWlMY~wM?q|OgkoYO~#{E%*_*c(jC*|<1x$OV(vcNpN!^nqEG74 zxF(P5mLqi~vz8w9jOmdquHkecyPZ5PElNkVl%9}A_YCb1^j%Vdb#Zb`w{X|S&8kp{ zv7tm7Gb$9}19?r|+8a;mSc(f{X03R_tXw=CI03#i{v|t$H87sTa?`VDc={<4bLn_g za)vN{$#?{lE0#{_=CXk)+{BcY;G1b^5xuOiQ3biI<+ON03nz4!g`2IfxXM&gX-jcp zP^`?flE85@jdG~CdF2=)&L~mjeHcj}@?kA?CX8{VaO$QjkjZG%-dzi^ zt;pzBCXNZCICD}8;n!C-C|WA2oHmPkXb{B5qN)JS^Tn?)d9TN> zaJKiXzlq0fgm}twk71yx@xmr4ETsxa>;EPIGprcVEp*qlWREkr7{|g;#-*B-HTg;L zX{mH-I+@Ozb*HXZs&Tea0XeF`!vhaBC!B-Dun33mN<$&UMB>6l0lOX-&T0HMD?lN zhTF`BaV}D!f_j~KjiB|i;$bVO>YCjlUh$-mQ|qenrp;Jv`3e@SEBh5^9*Q_TwtGZI zJk0U9F^vi*bj8CfCPw0m>bZDC=c`bP<#F*4S36m-IK2?6S2^3mM!`ESb=pZ*puUL3 zib6#phxnA4XYCoD&dMm2OYC@6t_|=_R{Hf+T#w>$5b*@gPF$_C&!P)lwkAFop5-glNXE)nUqoFUYthg`Ln^7Wa%-2PMV^u4j`i z?8}+^=P881t{y&2?+U+i^%)COkK;A;)^1ifXuY!<4GKTXy?82}!`$okD;Qj_*RK@9 zVclz-yy{)frnjG!tTA5qic40G?~cN(cd|XI;$tkesY07GH%mepdmgiQOJOViPI_=) zr(V*?q|YgTTwPgSt1cd+Y$ox+9K+S}ur~x#&`1oXmxn^pj5ZkxComyH>|HUQ zVX0e+QE)3>6W=wEx4pVN;~8xt7LS;6SRaq4)OaG{VFT>JOIFfTQ6|{{3aduT>u}w?}6KPZEZfz5~yF0{|=8iV81^)Ir? zw6`ML!4w^xXxG_-9-W=7VoMkE*u;!Cb)c-fr5QhH(%RhGBDQukw~6h|o4UmI?(R;p zqqV(V?AX-N>1t{2>gaN{u%TQnt=(;Hu9mL0R_t%;Z0mA6KLg77b7S#gUb^HBzwB)< z{@Nw_zTyvsHO1LS@CWsoOMqc^w+NfsTWVZTOKUx~m9;B9XSvQ6r_F7s3)Gc{rudk%Eto#EYfhd7XLYW{*RF=RiQJ+LBBypKUt0Y=2 zi8T_fmFRNGwL+q+B)VFnYb3f>qVJLDI?26WqG!tr;+!x0@w-uO#BY<_jNcZy6~AqA zJAON)icX1cl4!TItVde5MWVeD-73KaQsp)Ywo9~Ms@fsZA&Cx4uuGyN5{ydpB8l#i zD#j#wK%!SjG$hf8d*G1tApw=utwTQS6&3C5;yo+SuI_oVnyuMm40Qk)pPj&Xlt6!BSUaPj`1s%}3$j zVYmy9zziG$()e*8LgP?Z&F5g|6Yw~SkgKctDdWm#02=Fl1vBLc*=@rN^J3$!djsG% z@LS;Ie;x9F4*8u!e(#V!IOLBG`IAHb?2tDd@)w8v)gf;=WQKC+8ys@bA%`4tqeEsL za+5=DcF19e+~Sa19desPZg(hUN*&UUC9wvifwyT*xUcpHf!iafZD3?cO%M)j?LJ>R}aQo+(0S{P@y7q39xJjQnvt=?f{`swJ|^hs2&Iq5o*Q*1Pe@S zfXJ}CJwUv$Vl+T}uyR*`l)$R<1EdsIAB6xZgEjbTBnEapBh>EB4-va3ZwSXLs|m3;=Yygl8m_$IRwdd{;Xzq%UQyl9 zU|)bpR1jtFtAJ1g`Q}qlN(N3r8H}8Qa(acWp62YK4fY_y24zDwyqeD*VoZrqVw7rS z_J#Ri`EWVWD(szkqe5Lq8tjq0QE5~e)iX6O+oSV-MXiP7m0leFqTq6W-SBb{?7jJU zV}-WT-iLIRw%Xp0bd9#wK7jN++B*AEq>8rQz6|ME+S&FM!8)T(J%`NHy<&$>8s}Ex zv9hlWZZI|k>y3J&K|K#=70I7e&qt~U{dr?!9Z~%lxq(v^T!QR9#-^HJQyn0=Q=r16 zoeVY^O-A$a%2kM#3byz!R9nzL?f6Hz|HKa4WM+PxUaQ)Mh!$se=Z$vG=2&P($4uvU z?a5%5(Z#wi+f#_P_r#ok-`xD1fBziEy_PxVjn#Q$<%xN9lh|Nihrx*=2Dcewn*kQw zC1eIp!KHZU?Hd3=E<=#Z5riWg1P5X*gHSgK4fY{G2v-r}#sb8R9O6c>XBotmr$gLS zfLONxaWjK(6YzT77{#-UlLYGXtz}8^L$+!^VkqGg#%5!S6UR@Okw+AKCHIJ^e+~|u z_cF$=!*ie$Wu`MJ=;TBagD54>f&DylH*}JKMT9S)A>;kym>s$Eax6a@3MN}~m4|Rc zUOUP}9uP%Bd7d*aJ z^hhmu^euS22*C=Yf|Tl^E%cqr{}La<$J#~l9_1P6kuq54gA0CVEou)}_7 z(b(d~-zggBneh%|hd;-SPoVL47mX*YL_~fWh2MuO27^PvVPklB2x5cw4_FqX{|th=m_=~J7zyq+cB{KEBVL8zC>LI2Tol}6>`_Ni_;U!3 z8DqIh{A<4b3kM3->Rw!;UqW!7vCmPx=BN-(-H)nYVUWQC#sSv+w}5&vsF%QOlG?xK zcDP7Xy%ga@n1bWHkg!Ygi5ju(NlDs94u~a#V8G7Knh#RxzPH-K? zlu>57l5R%ol9CRXH9E{EsSYPGi6AT{Mf|#wu2XOr$WctGy8swD0+f*%5EwZG;!&{g zW{Jiy6ENq$8|H#n7*`-8sv+d>UEp1e=jZ7d`yQ+$pLzt0qOpKU?Mar0*S^V&?gb- z3GTuU7}oy==5CyutE|IFo@4-qpN0H3fDhsNjAJNI@p((yKKp6jb8BPvXCWVq4abN! zY<~{&#xPIMXIamAy++xTzvz_$s1SJ5&RT$iOwcH_HURg#^6z}!c_V# zW{>9pfgW--q1_}wgl@8%h&5ynan+EEh`Wo75UGwNiKmBnDPD$`;DCR?tg`=zK!dqT zw#t8EU4^IjpPepC=QmkbP2uVN3*;4T(0&UA!@0_3=rKdF%XK$U)D7E*D3&;%;Fi5} ztOU+dR-8WpWEP)#j{`7r2q+_;1mOshPhkpD`zAVnypo;go9PbwHaf3VGv;<=j?g-f z84>NHw64~Sxa@lL&i`Z~KOds=VNpxlb4XpFNS+7R zr;&Uf+@C@61(2RX@|MX}BaThFvOVM}z$hdLmq+ z;YIvC+(Aj51x&*qW7iXw!*2B^z|#CHWW9BsN6&dpdL-;qZ)R@4W_w!79%xTf+|$=_ zPwQX7?If?`e*6sJO=_RQgguN`;CkEvoA(u7v)`b2tPg{F3mfV;ESC?1{X3cu-U`FF z0*N8_dv1P$oBx6L&(&@N`;RnuJ01x1y`A~KiJG%d7XU`Ux z^~@S^K2%2^)$RiOrlJv--|5JYfPGlNDWdV&>QS~>w+X?!dE9kGy_*Sc7jX5;*qd($ zF5XdmII~-W92MBD0Y?S<9t6Gzv#Ac$W6a@Lktg^ryBF|bb}yHx#{rAQLkzB*;u~De zHzVLmycBDUn&#jGU_1cL!3V*35XnPeJcQ(7Fdjy7KN$BH$;D(X{};QDRg&e1R$2A- P{|MiPzY_l+e#+V8YeO4! literal 5203 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 NZ^hpd{|bgN)~e~~Jud(N diff --git a/trunk/research/players/srs_publisher/src/srs_publisher.as b/trunk/research/players/srs_publisher/src/srs_publisher.as index d9141b4ce..d91641493 100755 --- a/trunk/research/players/srs_publisher/src/srs_publisher.as +++ b/trunk/research/players/srs_publisher/src/srs_publisher.as @@ -192,8 +192,8 @@ package if (evt.info.data.hasOwnProperty("srs_server")) { customItems.push(new ContextMenuItem("Server: " + evt.info.data.srs_server)); } - if (evt.info.data.hasOwnProperty("srs_contributor")) { - customItems.push(new ContextMenuItem("Contributor: " + evt.info.data.srs_contributor)); + if (evt.info.data.hasOwnProperty("srs_primary_authors")) { + customItems.push(new ContextMenuItem("PrimaryAuthors: " + evt.info.data.srs_primary_authors)); } contextMenu.customItems = customItems; } From fabdf9507e0d9b318952cd705272cfd87affbec7 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 1 Jan 2014 13:58:58 +0800 Subject: [PATCH 15/26] fix bug of play stream of nginx, remove the start slash of streamName. --- .../players/srs_bwt/release/srs_bwt.swf | Bin 4675 -> 4674 bytes .../players/srs_player/release/srs_player.swf | Bin 5355 -> 5360 bytes .../players/srs_player/src/srs_player.as | 2 +- .../srs_publisher/release/srs_publisher.swf | Bin 5205 -> 5205 bytes 4 files changed, 1 insertion(+), 1 deletion(-) diff --git a/trunk/research/players/srs_bwt/release/srs_bwt.swf b/trunk/research/players/srs_bwt/release/srs_bwt.swf index e60ceaf69b3e014a8c809a17a2226590672e6f6c..a76afa2c7146557a2a6f4bb0924840c578d054a8 100755 GIT binary patch delta 4529 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 diff --git a/trunk/research/players/srs_player/release/srs_player.swf b/trunk/research/players/srs_player/release/srs_player.swf index 264bef415b3deacd48b07ce3349443fd8e229fa4..7361e298ba6c11ad14bb71d01788c6764a418eac 100755 GIT binary patch delta 5359 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ŎWMRK;A>&1^gmtIQYNiC9p8A@%OyU(fJ2=VTOd?imu7= zciB5ERCS->YY5#>#{JX~dVq`vKprIHL6E~_90pMgMSQlhi^Wgu5Z|{MuvA)sU%Ds% NcY@_${|`p{aKS+XnFs&? delta 5343 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 Pe}r$tUx|MO5w6)yw;Up< literal 5205 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 P{|MiPzY_l+e#+V8YeO4! From 99b9fa0c251a27bb3a9e724de67578111066c17f Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 1 Jan 2014 16:27:45 +0800 Subject: [PATCH 16/26] fix the forwarder dead when st_thread interrupt at st_usleep, check thread->can_loop(). --- trunk/src/core/srs_core_forward.cpp | 2 +- trunk/src/core/srs_core_thread.cpp | 5 +++++ trunk/src/core/srs_core_thread.hpp | 23 +++++++++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/trunk/src/core/srs_core_forward.cpp b/trunk/src/core/srs_core_forward.cpp index 97e8b75e8..60a3541a7 100644 --- a/trunk/src/core/srs_core_forward.cpp +++ b/trunk/src/core/srs_core_forward.cpp @@ -290,7 +290,7 @@ int SrsForwarder::forward() SrsPithyPrint pithy_print(SRS_STAGE_FORWARDER); - while (true) { + while (pthread->can_loop()) { // switch to other st-threads. st_usleep(0); diff --git a/trunk/src/core/srs_core_thread.cpp b/trunk/src/core/srs_core_thread.cpp index b5670cccd..61e4fb5b8 100644 --- a/trunk/src/core/srs_core_thread.cpp +++ b/trunk/src/core/srs_core_thread.cpp @@ -102,6 +102,11 @@ void SrsThread::stop() } } +bool SrsThread::can_loop() +{ + return loop; +} + void SrsThread::thread_cycle() { int ret = ERROR_SUCCESS; diff --git a/trunk/src/core/srs_core_thread.hpp b/trunk/src/core/srs_core_thread.hpp index 8e649f760..940ba050c 100644 --- a/trunk/src/core/srs_core_thread.hpp +++ b/trunk/src/core/srs_core_thread.hpp @@ -43,6 +43,23 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * when stop, the thread will interrupt the st_thread, * which will cause the socket to return error and * terminate the cycle thread. +* +* when thread interrupt, the socket maybe not got EINT, +* espectially on st_usleep(), so the cycle must check the loop, +* when handler->cycle() has loop itself, for example: +* handler->cycle() is: +* while (true): +* st_usleep(0); +* if (read_from_socket(skt) < 0) break; +* if thread stop when read_from_socket, it's ok, the loop will break, +* but when thread stop interrupt the s_usleep(0), then the loop is +* death loop. +* in a word, the handler->cycle() must: +* handler->cycle() is: +* while (pthread->can_loop()): +* st_usleep(0); +* if (read_from_socket(skt) < 0) break; +* check the loop, then it works. */ class ISrsThreadHandler { @@ -90,6 +107,12 @@ public: * @remark user can stop multiple times, ignore if already stopped. */ virtual void stop(); + /** + * whether the thread should loop, + * used for handler->cycle() which has a loop method, + * to check this method, break if false. + */ + virtual bool can_loop(); private: virtual void thread_cycle(); static void* thread_fun(void* arg); From 50c15fec6ccc1d0f972f2e3b47587b41364d9701 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 1 Jan 2014 17:35:29 +0800 Subject: [PATCH 17/26] update readme, refine the performance section. add wiki performance check guide. --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index 7592491a8..771d817d6 100755 --- a/README.md +++ b/README.md @@ -249,16 +249,13 @@ usr sys idl wai hiq siq| read writ| recv send| in out | int csw 61 8 30 0 0 1| 0 1168k| 336M 336M| 0 0 | 29k 24k 63 8 27 0 0 1| 0 2240k| 124M 124M| 0 0 | 32k 33k 62 8 28 0 0 1| 0 1632k| 110M 110M| 0 0 | 31k 33k - 67 9 23 0 0 2| 0 1604k| 130M 130M| 0 0 | 33k 32k - 63 9 27 0 0 2| 0 1496k| 145M 145M| 0 0 | 32k 32k - 61 9 29 0 0 1| 0 1112k| 132M 132M| 0 0 | 32k 33k - 63 9 27 0 0 2| 0 1220k| 145M 145M| 0 0 | 32k 33k 53 7 40 0 0 1| 0 1360k| 115M 115M| 0 0 | 24k 26k 51 7 41 0 0 1| 0 1184k| 146M 146M| 0 0 | 24k 27k 39 6 54 0 0 1| 0 1284k| 105M 105M| 0 0 | 22k 28k 41 6 52 0 0 1| 0 1264k| 116M 116M| 0 0 | 25k 28k 48 6 45 0 0 1| 0 1272k| 143M 143M| 0 0 | 27k 27k +See also: [Performance Test Guide](https://github.com/winlinvip/simple-rtmp-server/wiki/%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95%E6%8C%87%E5%8D%97#%E6%80%A7%E8%83%BD%E5%AF%B9%E6%AF%94) ### Releases * 2013-12-25, [Release v0.9](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.9), support bandwidth test, add player/encoder/chat demos. 20926 lines.
From 5cc2de3a5d4d2502ae72bb51b3d7305e11cf0984 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 1 Jan 2014 17:35:59 +0800 Subject: [PATCH 18/26] update readme, refine the performance section. add wiki performance check guide. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 771d817d6..7b8d5ccb8 100755 --- a/README.md +++ b/README.md @@ -241,6 +241,7 @@ Supported operating systems and hardware: 4. 1200 connections, 600Mbps, 500kbps, CPU 72.4%, 15MB. 5. 1500 connections, 750Mbps, 500kbps, CPU 81.9%, 28MB. 6. 1800 connections, 900Mbps, 500kbps, CPU 90.2%, 41MB. +
 [winlin@dev6 srs]$ dstat
 ----total-cpu-usage---- -dsk/total- ---net/lo-- ---paging-- ---system--

From a9a6a7cdcdb2478bd69e70171cf28da929616c0b Mon Sep 17 00:00:00 2001
From: winlin 
Date: Wed, 1 Jan 2014 17:37:54 +0800
Subject: [PATCH 19/26] update readme, refine the performance section. add wiki
 performance check guide.

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 7b8d5ccb8..d0fbec335 100755
--- a/README.md
+++ b/README.md
@@ -256,7 +256,7 @@ usr sys idl wai hiq siq| read  writ| recv  send|  in   out | int   csw
  41   6  52   0   0   1|   0  1264k| 116M  116M|   0     0 |  25k   28k
  48   6  45   0   0   1|   0  1272k| 143M  143M|   0     0 |  27k   27k
 
-See also: [Performance Test Guide](https://github.com/winlinvip/simple-rtmp-server/wiki/%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95%E6%8C%87%E5%8D%97#%E6%80%A7%E8%83%BD%E5%AF%B9%E6%AF%94) +See also: [Performance Test Guide](https://github.com/winlinvip/simple-rtmp-server/wiki/Performance) ### Releases * 2013-12-25, [Release v0.9](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.9), support bandwidth test, add player/encoder/chat demos. 20926 lines.
From 3dbb2132d322473b5ad87bc6fab48319b9fac0ff Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 1 Jan 2014 19:58:33 +0800 Subject: [PATCH 20/26] change server timeout longer, recv timeout from 5s to 30s --- trunk/src/core/srs_core_client.cpp | 11 +++------- trunk/src/core/srs_core_encoder.cpp | 7 +++---- trunk/src/core/srs_core_forward.cpp | 11 +++------- trunk/src/core/srs_core_protocol.cpp | 2 -- trunk/src/core/srs_core_protocol.hpp | 31 ++++++++++++++++++++++++++++ trunk/src/core/srs_core_thread.cpp | 6 +++--- trunk/src/core/srs_core_thread.hpp | 6 +++--- 7 files changed, 46 insertions(+), 28 deletions(-) diff --git a/trunk/src/core/srs_core_client.cpp b/trunk/src/core/srs_core_client.cpp index 7fb4ef5c6..3d5dc6c4d 100644 --- a/trunk/src/core/srs_core_client.cpp +++ b/trunk/src/core/srs_core_client.cpp @@ -42,11 +42,6 @@ using namespace std; #include #include -#define SRS_PULSE_TIMEOUT_MS 100 -#define SRS_SEND_TIMEOUT_US 5000000L -#define SRS_RECV_TIMEOUT_US SRS_SEND_TIMEOUT_US -#define SRS_STREAM_BUSY_SLEEP_MS 2000 - SrsClient::SrsClient(SrsServer* srs_server, st_netfd_t client_stfd) : SrsConnection(srs_server, client_stfd) { @@ -198,7 +193,7 @@ int SrsClient::service_cycle() srs_warn("stream %s is already publishing. ret=%d", req->get_stream_url().c_str(), ret); // to delay request - st_usleep(SRS_STREAM_BUSY_SLEEP_MS * 1000); + st_usleep(SRS_STREAM_BUSY_SLEEP_US); return ret; } @@ -324,12 +319,12 @@ int SrsClient::playing(SrsSource* source) SrsAutoFree(SrsConsumer, consumer, false); srs_verbose("consumer created success."); - rtmp->set_recv_timeout(SRS_PULSE_TIMEOUT_MS * 1000); + rtmp->set_recv_timeout(SRS_PULSE_TIMEOUT_US); SrsPithyPrint pithy_print(SRS_STAGE_PLAY_USER); while (true) { - pithy_print.elapse(SRS_PULSE_TIMEOUT_MS); + pithy_print.elapse(SRS_PULSE_TIMEOUT_US / 1000); // switch to other st-threads. st_usleep(0); diff --git a/trunk/src/core/srs_core_encoder.cpp b/trunk/src/core/srs_core_encoder.cpp index c56509796..95cf523ac 100644 --- a/trunk/src/core/srs_core_encoder.cpp +++ b/trunk/src/core/srs_core_encoder.cpp @@ -37,11 +37,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #include +#include #ifdef SRS_FFMPEG -#define SRS_ENCODER_SLEEP_MS 2000 - #define SRS_ENCODER_COPY "copy" #define SRS_ENCODER_VCODEC "libx264" #define SRS_ENCODER_ACODEC "libaacplus" @@ -483,7 +482,7 @@ void SrsFFMPEG::stop() SrsEncoder::SrsEncoder() { - pthread = new SrsThread(this, SRS_ENCODER_SLEEP_MS); + pthread = new SrsThread(this, SRS_ENCODER_SLEEP_US); pithy_print = new SrsPithyPrint(SRS_STAGE_ENCODER); } @@ -549,7 +548,7 @@ int SrsEncoder::cycle() // pithy print encoder(); - pithy_print->elapse(SRS_ENCODER_SLEEP_MS); + pithy_print->elapse(SRS_ENCODER_SLEEP_US / 1000); return ret; } diff --git a/trunk/src/core/srs_core_forward.cpp b/trunk/src/core/srs_core_forward.cpp index 60a3541a7..98c545399 100644 --- a/trunk/src/core/srs_core_forward.cpp +++ b/trunk/src/core/srs_core_forward.cpp @@ -38,11 +38,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #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(SrsSource* _source) { source = _source; @@ -51,7 +46,7 @@ SrsForwarder::SrsForwarder(SrsSource* _source) stfd = NULL; stream_id = 0; - pthread = new SrsThread(this, SRS_FORWARDER_SLEEP_MS); + pthread = new SrsThread(this, SRS_FORWARDER_SLEEP_US); queue = new SrsMessageQueue(); jitter = new SrsRtmpJitter(); } @@ -286,7 +281,7 @@ int SrsForwarder::forward() { int ret = ERROR_SUCCESS; - client->set_recv_timeout(SRS_PULSE_TIMEOUT_MS * 1000); + client->set_recv_timeout(SRS_PULSE_TIMEOUT_US); SrsPithyPrint pithy_print(SRS_STAGE_FORWARDER); @@ -322,7 +317,7 @@ int SrsForwarder::forward() SrsAutoFree(SrsSharedPtrMessage*, msgs, true); // pithy print - pithy_print.elapse(SRS_PULSE_TIMEOUT_MS); + pithy_print.elapse(SRS_PULSE_TIMEOUT_US / 1000); 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()); diff --git a/trunk/src/core/srs_core_protocol.cpp b/trunk/src/core/srs_core_protocol.cpp index 3d3d6138b..4de050c75 100644 --- a/trunk/src/core/srs_core_protocol.cpp +++ b/trunk/src/core/srs_core_protocol.cpp @@ -282,8 +282,6 @@ messages. /**************************************************************************** ***************************************************************************** ****************************************************************************/ -// when got a messae header, increase recv timeout to got an entire message. -#define SRS_MIN_RECV_TIMEOUT_US 3000 SrsProtocol::AckWindowSize::AckWindowSize() { diff --git a/trunk/src/core/srs_core_protocol.hpp b/trunk/src/core/srs_core_protocol.hpp index 089251709..ca6602c67 100644 --- a/trunk/src/core/srs_core_protocol.hpp +++ b/trunk/src/core/srs_core_protocol.hpp @@ -36,6 +36,37 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +// the following is the timeout for rtmp protocol, +// to avoid death connection. + +// when got a messae header, there must be some data, +// increase recv timeout to got an entire message. +#define SRS_MIN_RECV_TIMEOUT_US 60*1000*1000L + +// the timeout to wait for client control message, +// if timeout, we generally ignore and send the data to client, +// generally, it's the pulse time for data seding. +#define SRS_PULSE_TIMEOUT_US 200*1000L + +// the timeout to wait client data, +// if timeout, close the connection. +#define SRS_SEND_TIMEOUT_US 30*1000*1000L + +// the timeout to send data to client, +// if timeout, close the connection. +#define SRS_RECV_TIMEOUT_US 30*1000*1000L + +// when stream is busy, for example, streaming is already +// publishing, when a new client to request to publish, +// sleep a while and close the connection. +#define SRS_STREAM_BUSY_SLEEP_US 3*1000*1000L + +// when error, forwarder sleep for a while and retry. +#define SRS_FORWARDER_SLEEP_US 3*1000*1000L + +// when error, encoder sleep for a while and retry. +#define SRS_ENCODER_SLEEP_US 3*1000*1000L + class SrsSocket; class SrsBuffer; class SrsPacket; diff --git a/trunk/src/core/srs_core_thread.cpp b/trunk/src/core/srs_core_thread.cpp index 61e4fb5b8..b6742f29c 100644 --- a/trunk/src/core/srs_core_thread.cpp +++ b/trunk/src/core/srs_core_thread.cpp @@ -54,10 +54,10 @@ void ISrsThreadHandler::on_leave_loop() { } -SrsThread::SrsThread(ISrsThreadHandler* thread_handler, int64_t interval_ms) +SrsThread::SrsThread(ISrsThreadHandler* thread_handler, int64_t interval_us) { handler = thread_handler; - cycle_interval_milliseconds = interval_ms; + cycle_interval_us = interval_us; tid = NULL; loop = false; @@ -143,7 +143,7 @@ failed: break; } - st_usleep(cycle_interval_milliseconds * 1000); + st_usleep(cycle_interval_us); } handler->on_leave_loop(); diff --git a/trunk/src/core/srs_core_thread.hpp b/trunk/src/core/srs_core_thread.hpp index 940ba050c..3210dcb56 100644 --- a/trunk/src/core/srs_core_thread.hpp +++ b/trunk/src/core/srs_core_thread.hpp @@ -85,14 +85,14 @@ private: bool loop; private: ISrsThreadHandler* handler; - int64_t cycle_interval_milliseconds; + int64_t cycle_interval_us; public: /** * initialize the thread. * @param thread_handler, the cycle handler for the thread. - * @param interval_ms, the sleep interval when cycle finished. + * @param interval_us, the sleep interval when cycle finished. */ - SrsThread(ISrsThreadHandler* thread_handler, int64_t interval_ms); + SrsThread(ISrsThreadHandler* thread_handler, int64_t interval_us); virtual ~SrsThread(); public: /** From 4955425be960778aad04bca47caed7b1e33627c5 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 1 Jan 2014 20:14:46 +0800 Subject: [PATCH 21/26] fix the listen backlog bug, change from 10 to 512 --- trunk/src/core/srs_core_server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trunk/src/core/srs_core_server.cpp b/trunk/src/core/srs_core_server.cpp index 69c9687c0..a10e6da80 100644 --- a/trunk/src/core/srs_core_server.cpp +++ b/trunk/src/core/srs_core_server.cpp @@ -35,7 +35,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include -#define SERVER_LISTEN_BACKLOG 10 +#define SERVER_LISTEN_BACKLOG 512 #define SRS_TIME_RESOLUTION_MS 500 SrsListener::SrsListener(SrsServer* _server, SrsListenerType _type) From 4d631d749ff77c79b2f3d33456c4de2ae05b6952 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 1 Jan 2014 21:07:01 +0800 Subject: [PATCH 22/26] fix ts_info centos5 build bug --- trunk/research/hls/ts_info.cc | 38 +++++++++++++++++------------------ trunk/src/srs/srs.upp | 2 +- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/trunk/research/hls/ts_info.cc b/trunk/research/hls/ts_info.cc index 8273b073e..7cc1abe2f 100644 --- a/trunk/research/hls/ts_info.cc +++ b/trunk/research/hls/ts_info.cc @@ -417,38 +417,38 @@ public: */ enum TSPESStreamId { - PES_program_stream_map = 0b10111100, // 0xbc - PES_private_stream_1 = 0b10111101, // 0xbd - PES_padding_stream = 0b10111110, // 0xbe - PES_private_stream_2 = 0b10111111, // 0xbf + PES_program_stream_map = 0xbc, // 0b10111100 + PES_private_stream_1 = 0xbd, // 0b10111101 + PES_padding_stream = 0xbe, // 0b10111110 + PES_private_stream_2 = 0xbf, // 0b10111111 // 110x xxxx // ISO/IEC 13818-3 or ISO/IEC 11172-3 or ISO/IEC 13818-7 or ISO/IEC // 14496-3 audio stream number x xxxx // (stream_id>>5)&0x07 == PES_audio_prefix - PES_audio_prefix = 0b110, + PES_audio_prefix = 0x06, // 0b110 // 1110 xxxx // ITU-T Rec. H.262 | ISO/IEC 13818-2 or ISO/IEC 11172-2 or ISO/IEC // 14496-2 video stream number xxxx // (stream_id>>4)&0x0f == PES_audio_prefix - PES_video_prefix = 0b1110, + PES_video_prefix = 0x0e, // 0b1110 - PES_ECM_stream = 0b11110000, // 0xf0 - PES_EMM_stream = 0b11110001, // 0xf1 - PES_DSMCC_stream = 0b11110010, // 0xf2 - PES_13522_stream = 0b11110011, // 0xf3 - PES_H_222_1_type_A = 0b11110100, // 0xf4 - PES_H_222_1_type_B = 0b11110101, // 0xf5 - PES_H_222_1_type_C = 0b11110110, // 0xf6 - PES_H_222_1_type_D = 0b11110111, // 0xf7 - PES_H_222_1_type_E = 0b11111000, // 0xf8 - PES_ancillary_stream = 0b11111001, // 0xf9 - PES_SL_packetized_stream = 0b11111010, // 0xfa - PES_FlexMux_stream = 0b11111011, // 0xfb + PES_ECM_stream = 0xf0, // 0b11110000 + PES_EMM_stream = 0xf1, // 0b11110001 + PES_DSMCC_stream = 0xf2, // 0b11110010 + PES_13522_stream = 0xf3, // 0b11110011 + PES_H_222_1_type_A = 0xf4, // 0b11110100 + PES_H_222_1_type_B = 0xf5, // 0b11110101 + PES_H_222_1_type_C = 0xf6, // 0b11110110 + PES_H_222_1_type_D = 0xf7, // 0b11110111 + PES_H_222_1_type_E = 0xf8, // 0b11111000 + PES_ancillary_stream = 0xf9, // 0b11111001 + PES_SL_packetized_stream = 0xfa, // 0b11111010 + PES_FlexMux_stream = 0xfb, // 0b11111011 // reserved data stream // 1111 1100 … 1111 1110 - PES_program_stream_directory= 0b11111111, // 0xff + PES_program_stream_directory= 0xff, // 0b11111111 }; diff --git a/trunk/src/srs/srs.upp b/trunk/src/srs/srs.upp index 25a8495bf..fca1a86ca 100755 --- a/trunk/src/srs/srs.upp +++ b/trunk/src/srs/srs.upp @@ -58,7 +58,7 @@ file ..\core\srs_core_source.hpp, ..\core\srs_core_source.cpp, research readonly separator, - ..\..\research\ts_info.cc; + ..\..\research\hls\ts_info.cc; mainconfig "" = "MAIN"; From 993c5daec702af70321e89b5c9060a7e72a74056 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 1 Jan 2014 21:39:06 +0800 Subject: [PATCH 23/26] chunk size default to 60000, high performance. set chunk size when forward --- trunk/conf/srs.conf | 6 +++--- trunk/src/core/srs_core_config.hpp | 3 ++- trunk/src/core/srs_core_rtmp.cpp | 32 ++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/trunk/conf/srs.conf b/trunk/conf/srs.conf index 232c1238e..6ae16e04c 100755 --- a/trunk/conf/srs.conf +++ b/trunk/conf/srs.conf @@ -4,8 +4,8 @@ listen 1935; # some client does not support chunk size change, # however, most clients supports it and it can improve # performance about 10%. -# default: 4096 -chunk_size 65000; +# default: 60000 +chunk_size 60000; # the logs dir. # if enabled ffmpeg, each stracoding stream will create a log file. # default: ./objs/logs @@ -26,7 +26,7 @@ vhost __defaultVhost__ { # for which cannot identify the required vhost. # for default demo. vhost demo.srs.com { - chunk_size 4096; + chunk_size 60000; enabled on; gop_cache on; queue_length 30; diff --git a/trunk/src/core/srs_core_config.hpp b/trunk/src/core/srs_core_config.hpp index 3cd693275..4bbe122eb 100644 --- a/trunk/src/core/srs_core_config.hpp +++ b/trunk/src/core/srs_core_config.hpp @@ -57,7 +57,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // the interval in seconds for bandwidth check #define SRS_CONF_DEFAULT_BANDWIDTH_LIMIT_KBPS 1000 -#define SRS_CONF_DEFAULT_CHUNK_SIZE 4096 +// the default chunk size for system. +#define SRS_CONF_DEFAULT_CHUNK_SIZE 60000 #define SRS_STAGE_PLAY_USER_INTERVAL_MS 1300 #define SRS_STAGE_PUBLISH_USER_INTERVAL_MS 1100 diff --git a/trunk/src/core/srs_core_rtmp.cpp b/trunk/src/core/srs_core_rtmp.cpp index 4164436d2..bf67fb9fc 100644 --- a/trunk/src/core/srs_core_rtmp.cpp +++ b/trunk/src/core/srs_core_rtmp.cpp @@ -391,12 +391,44 @@ int SrsRtmpClient::play(string stream, int stream_id) } } + // SetChunkSize + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsSetChunkSizePacket* pkt = new SrsSetChunkSizePacket(); + + pkt->chunk_size = SRS_CONF_DEFAULT_CHUNK_SIZE; + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send set chunk size failed. " + "stream=%s, chunk_size=%d, ret=%d", + stream.c_str(), SRS_CONF_DEFAULT_CHUNK_SIZE, ret); + return ret; + } + } + return ret; } int SrsRtmpClient::publish(string stream, int stream_id) { int ret = ERROR_SUCCESS; + + // SetChunkSize + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsSetChunkSizePacket* pkt = new SrsSetChunkSizePacket(); + + pkt->chunk_size = SRS_CONF_DEFAULT_CHUNK_SIZE; + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send set chunk size failed. " + "stream=%s, chunk_size=%d, ret=%d", + stream.c_str(), SRS_CONF_DEFAULT_CHUNK_SIZE, ret); + return ret; + } + } // publish(stream) if (true) { From 82961e28d665c8b2f041583ca24165cce2102b75 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 1 Jan 2014 21:44:48 +0800 Subject: [PATCH 24/26] change listen(512), chunk-size(60000), to improve performance. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d0fbec335..b8b822b5a 100755 --- a/README.md +++ b/README.md @@ -284,6 +284,7 @@ See also: [Performance Test Guide](https://github.com/winlinvip/simple-rtmp-serv * nginx v1.5.0: 139524 lines
### History +* v1.0, 2014-01-01, change listen(512), chunk-size(60000), to improve performance. * v1.0, 2013-12-27, merge from wenjie, the bandwidth test feature. * v0.9, 2013-12-25, [v0.9](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.9) released. 20926 lines. * v0.9, 2013-12-25, fix the bitrate bug(in Bps), use enhanced microphone. From a0757c36bcb209535c846432caf13cc555d0c8c6 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 1 Jan 2014 23:15:28 +0800 Subject: [PATCH 25/26] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b8b822b5a..7e89335f0 100755 --- a/README.md +++ b/README.md @@ -214,7 +214,7 @@ Supported operating systems and hardware: 11. Support listen at multiple ports.
12. Support long time(>4.6hours) publish/play.
13. High performace, 1800 connections(500kbps), 900Mbps, CPU 90.2%, 41MB
-14. Support forward publish stream to build active-standby cluster.
+14. Support forward publish stream to build active-standby [cluster](https://github.com/winlinvip/simple-rtmp-server/wiki/Cluster).
15. Support broadcast by forward the stream to other servers(origin/edge).
16. Support live stream transcoding by ffmpeg.
17. Support live stream forward(acopy/vcopy) by ffmpeg.
From 539ffca6505c97d8c3e078f20eb18fc0dd1f0c88 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 1 Jan 2014 23:51:39 +0800 Subject: [PATCH 26/26] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7e89335f0..e877a82f1 100755 --- a/README.md +++ b/README.md @@ -202,7 +202,7 @@ Supported operating systems and hardware: ### Summary 1. Simple: also stable enough.
-2. High-performance: single-thread, async socket, event/st-thread driven.
+2. [High-performance](https://github.com/winlinvip/simple-rtmp-server/wiki/Performance): single-thread, async socket, event/st-thread driven.
3. NO edge server, origin server only.
4. NO vod streaming, live streaming only.
5. NO multiple processes, single process only.