diff --git a/trunk/configure b/trunk/configure index 874fc4145..f4833f6c2 100755 --- a/trunk/configure +++ b/trunk/configure @@ -55,27 +55,6 @@ SRS_BUILD_SUMMARY="_srs_build_summary.sh" SrsUtestMakeEntry="@echo -e \"ignore utest for it's disabled\"" if [[ $SRS_UTEST == YES ]]; then SrsUtestMakeEntry="\$(MAKE)\$(JOBS) -C ${SRS_OBJS}/${SRS_PLATFORM}/utest"; fi -##################################################################################### -# finger out modules to install. -# where srs module is a dir which contains a config file. -SRS_MODULES=() -__mfiles=$(find $SRS_WORKDIR/modules -name "config") && for __mfile in $__mfiles; do - SRS_MODULES+=("`dirname $__mfile`") -done - -# variables for makefile for all modules. -__mphonys="" && __mdefaults="" && __mcleanups="" -# add each modules for application -for SRS_MODULE in ${SRS_MODULES[*]}; do - echo "install module at: $SRS_MODULE" - . $SRS_WORKDIR/auto/reset_module.sh && . $SRS_MODULE/config - if [[ 0 -ne ${#SRS_MODULE_MAIN[@]} ]]; then - __mphonys="$__mphonys $SRS_MODULE_NAME" - __mdefaults="$__mdefaults $SRS_MODULE_NAME" - __mcleanups="$__mcleanups $SRS_MODULE_NAME" - fi -done - # generate extra phony for each modules. cat << END > ${SRS_OBJS}/Makefile @@ -319,7 +298,7 @@ MODULE_FILES=("srs_app_server" "srs_app_conn" "srs_app_rtmp_conn" "srs_app_sourc "srs_app_mpegts_udp" "srs_app_listener" "srs_app_async_call" "srs_app_caster_flv" "srs_app_latest_version" "srs_app_uuid" "srs_app_process" "srs_app_ng_exec" "srs_app_hourglass" "srs_app_dash" "srs_app_fragment" "srs_app_dvr" - "srs_app_coworkers" "srs_app_hybrid" "srs_app_circuit_breaker" + "srs_app_coworkers" "srs_app_circuit_breaker" "srs_app_stream_token") if [[ $SRS_SRT == YES ]]; then MODULE_FILES+=("srs_app_srt_server" "srs_app_srt_listener" "srs_app_srt_conn" "srs_app_srt_utility" "srs_app_srt_source") @@ -339,11 +318,6 @@ fi DEFINES="" # add each modules for app -for SRS_MODULE in ${SRS_MODULES[*]}; do - . $SRS_WORKDIR/auto/reset_module.sh && . $SRS_MODULE/config - MODULE_FILES+=("${SRS_MODULE_APP[*]}") - DEFINES="${DEFINES} ${SRS_MODULE_DEFINES}" -done APP_INCS="src/app"; MODULE_DIR=${APP_INCS} . $SRS_WORKDIR/auto/modules.sh APP_OBJS="${MODULE_OBJS[@]}" # @@ -357,24 +331,6 @@ fi MODULE_FILES=("srs_main_server") SERVER_INCS="src/main"; MODULE_DIR=${SERVER_INCS} . $SRS_WORKDIR/auto/modules.sh SERVER_OBJS="${MODULE_OBJS[@]}" -# -#Main Module, for app from modules. -MODULE_ID="MAIN" -MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "APP") -ModuleLibIncs=(${SRS_OBJS} ${LibGperfRoot} ${LibSSLRoot} ${LibSrtpRoot}) -if [[ $SRS_FFMPEG_FIT == YES ]]; then - ModuleLibIncs+=("${LibFfmpegRoot[*]}") -fi -MODULE_FILES=() -DEFINES="" -# add each modules for main -for SRS_MODULE in ${SRS_MODULES[*]}; do - . $SRS_WORKDIR/auto/reset_module.sh && . $SRS_MODULE/config - MODULE_FILES+=("${SRS_MODULE_MAIN[*]}") - DEFINES="${DEFINES} ${SRS_MODULE_DEFINES}" -done -MAIN_INCS="src/main"; MODULE_DIR=${MAIN_INCS} . $SRS_WORKDIR/auto/modules.sh -MAIN_OBJS="${MODULE_OBJS[@]}" ##################################################################################### # Binaries, main entrances, link the module and its depends modules, @@ -382,10 +338,6 @@ MAIN_OBJS="${MODULE_OBJS[@]}" # # all main entrances MAIN_ENTRANCES=("srs_main_server") -for SRS_MODULE in ${SRS_MODULES[*]}; do - . $SRS_WORKDIR/auto/reset_module.sh && . $SRS_MODULE/config - MAIN_ENTRANCES+=("${SRS_MODULE_MAIN[*]}") -done # # all depends libraries ModuleLibFiles=(${LibSTfile} ${LibSSLfile} ${LibGperfFile} ${LibSrtpFile}) @@ -419,13 +371,6 @@ fi if [[ $SRS_SRT == YES ]]; then ModuleLibFiles+=("${LibSRTfile[*]}") fi -# -for SRS_MODULE in ${SRS_MODULES[*]}; do - . $SRS_WORKDIR/auto/reset_module.sh && . $SRS_MODULE/config - # no SRS_MODULE_MAIN - if [[ 0 -eq ${#SRS_MODULE_MAIN[@]} ]]; then continue; fi - BUILD_KEY="$SRS_MODULE_NAME" APP_MAIN="${SRS_MODULE_MAIN[0]}" APP_NAME="$SRS_MODULE_NAME" . $SRS_WORKDIR/auto/apps.sh -done # For utest on mac. # @see https://github.com/protocolbuffers/protobuf/issues/51#issuecomment-111044468 if [[ $SRS_OSX == YES ]]; then @@ -480,7 +425,7 @@ mv ${SRS_MAKEFILE} ${SRS_MAKEFILE}.bk # generate phony header cat << END > ${SRS_MAKEFILE} .PHONY: default all _default install help clean destroy server utest _prepare_dir $__mphonys -.PHONY: clean_srs clean_modules clean_openssl clean_srtp2 clean_opus clean_ffmpeg clean_st +.PHONY: clean_srs clean_openssl clean_srtp2 clean_opus clean_ffmpeg clean_st .PHONY: st ffmpeg CC = ${SRS_TOOL_CC} @@ -512,7 +457,7 @@ default: server all: _default -_default: server utest $__mdefaults +_default: server utest help: @echo "Usage: make ||||||" @@ -541,7 +486,7 @@ doclean: (cd ${SRS_OBJS} && rm -rf src/* include lib) (mkdir -p ${SRS_OBJS}/utest && cd ${SRS_OBJS}/utest && rm -rf *.o *.a) -clean: clean_srs clean_modules +clean: clean_srs destroy: (cd ${SRS_OBJS} && rm -rf ${SRS_PLATFORM}) @@ -549,9 +494,6 @@ destroy: clean_srs: @(cd ${SRS_OBJS} && rm -rf srs srs_utest src/* utest/*) -clean_modules: - @(cd ${SRS_OBJS} && rm -rf $__mdefaults) - clean_openssl: @rm -rf ${SRS_OBJS}/${SRS_PLATFORM}/3rdparty/openssl @echo "Please rebuild openssl by: ./configure" @@ -595,17 +537,6 @@ server: _prepare_dir END -# generate all modules entry -for SRS_MODULE in ${SRS_MODULES[*]}; do - . $SRS_WORKDIR/auto/reset_module.sh && . $SRS_MODULE/config - cat << END >> ${SRS_MAKEFILE} -$SRS_MODULE_NAME: _prepare_dir server - @echo "Build the $SRS_MODULE_NAME over SRS" - \$(MAKE)\$(JOBS) -f ${SRS_OBJS}/Makefile $SRS_MODULE_NAME - -END -done - # install entry cat << END >> ${SRS_MAKEFILE} uninstall: @@ -817,11 +748,6 @@ else echo -e "${GREEN}Note: The sanitizer is disabled.${BLACK}" fi -# add each modules for application -for SRS_MODULE in ${SRS_MODULES[*]}; do - echo -e "${GREEN}Enable module: $SRS_MODULE${BLACK}" -done - ##################################################################################### # Do cleanup when configure done. ##################################################################################### diff --git a/trunk/doc/CHANGELOG.md b/trunk/doc/CHANGELOG.md index 873295cdc..273e53de6 100644 --- a/trunk/doc/CHANGELOG.md +++ b/trunk/doc/CHANGELOG.md @@ -7,6 +7,7 @@ The changelog for SRS. ## SRS 7.0 Changelog +* v7.0, 2025-08-31, Merge [#4459](https://github.com/ossrs/srs/pull/4459): AI: Merge SRT and RTC servers into unified SrsServer. v7.0.68 (#4459) * v7.0, 2025-08-29, Merge [#4457](https://github.com/ossrs/srs/pull/4457): Support IPv6 for all protocols: RTMP, HTTP/HTTPS, WebRTC, SRT, RTSP. v7.0.67 (#4457) * v7.0, 2025-08-28, Merge [#4456](https://github.com/ossrs/srs/pull/4456): AI: Remove cloud CLS and APM. v7.0.66 (#4456) * v7.0, 2025-08-27, Merge [#4455](https://github.com/ossrs/srs/pull/4455): Gather utility functions to kernel or protocol. v7.0.65 (#4455) diff --git a/trunk/modules/hls-ingester/config b/trunk/modules/hls-ingester/config deleted file mode 100644 index 57c803ba8..000000000 --- a/trunk/modules/hls-ingester/config +++ /dev/null @@ -1,6 +0,0 @@ - -# The module to ingest hls to replace ffmpeg with better behavior. -SRS_MODULE_NAME=("srs_hls_ingester") -SRS_MODULE_MAIN=("srs_main_ingest_hls") -SRS_MODULE_APP=() -SRS_MODULE_DEFINES="" diff --git a/trunk/modules/mp4-parser/config b/trunk/modules/mp4-parser/config deleted file mode 100644 index 5b2719545..000000000 --- a/trunk/modules/mp4-parser/config +++ /dev/null @@ -1,6 +0,0 @@ - -# The module to parse mp4 file. -SRS_MODULE_NAME=("srs_mp4_parser") -SRS_MODULE_MAIN=("srs_main_mp4_parser") -SRS_MODULE_APP=() -SRS_MODULE_DEFINES="" diff --git a/trunk/modules/readme.txt b/trunk/modules/readme.txt deleted file mode 100644 index b0f161ab6..000000000 --- a/trunk/modules/readme.txt +++ /dev/null @@ -1,20 +0,0 @@ - -SRS Application Module Rules(SRS应用模块规则) -1. Each module in its isolate home directory(一个模块一个目录). -2. There is a config file in home(目录下放一个config文件). -3. All variables in configure are available(所有的configure中的变量模块中可以使用). - -The Variables in config(模块中需要定义变量,例如): -1. SRS_MODULE_NAME:The application binary name, optional. (模块名称,用来做Makefile的phony以及执行binary文件名。模块的二进制输出。为空时没有独立的二进制。) -2. SRS_MODULE_MAIN:The source file in src/main directory, optional. (模块的main函数所在的cpp文件,在src/main目录。模块在main的文件。可以为空。) -3. SRS_MODULE_APP:The source file in src/app directory, optional. (模块在src/app目录的源文件列表。模块在app的文件。可以为空。) -4. SRS_MODULE_DEFINES: The extra defined macros, optional. (模块编译时的额外宏定义。在app和main模块加入。可以为空。) - -For example(下面是一个RTMFP服务器实例): -SRS_MODULE_NAME=("srs_rtmfpd") -SRS_MODULE_MAIN=("srs_main_rtmfpd") -SRS_MODULE_APP=("srs_app_rtfmpd") -SRS_MODULE_DEFINES="-DRTMFPD" - -Winlin, 2015.3 - diff --git a/trunk/src/app/srs_app_circuit_breaker.cpp b/trunk/src/app/srs_app_circuit_breaker.cpp index a65cc1e99..520362eef 100644 --- a/trunk/src/app/srs_app_circuit_breaker.cpp +++ b/trunk/src/app/srs_app_circuit_breaker.cpp @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include #include @@ -17,6 +17,8 @@ extern SrsPps *_srs_pps_snack2; extern SrsPps *_srs_pps_snack3; extern SrsPps *_srs_pps_snack4; +extern SrsServer *_srs_server; + using namespace std; ISrsCircuitBreaker::ISrsCircuitBreaker() @@ -60,7 +62,7 @@ srs_error_t SrsCircuitBreaker::initialize() // Update the water level for circuit breaker. // @see SrsCircuitBreaker::on_timer() - _srs_hybrid->timer1s()->subscribe(this); + _srs_server->timer1s()->subscribe(this); srs_trace("CircuitBreaker: enabled=%d, high=%dx%d, critical=%dx%d, dying=%dx%d", enabled_, high_pulse_, high_threshold_, critical_pulse_, critical_threshold_, diff --git a/trunk/src/app/srs_app_gb28181.cpp b/trunk/src/app/srs_app_gb28181.cpp index b32f995f6..fa4424391 100644 --- a/trunk/src/app/srs_app_gb28181.cpp +++ b/trunk/src/app/srs_app_gb28181.cpp @@ -408,11 +408,12 @@ std::string SrsGbSession::desc() return "GBS"; } -SrsGbListener::SrsGbListener() +SrsGbListener::SrsGbListener(ISrsHttpServeMux *http_api_mux) { conf_ = NULL; sip_listener_ = new SrsTcpListener(this); media_listener_ = new SrsTcpListener(this); + http_api_mux_ = http_api_mux; } SrsGbListener::~SrsGbListener() @@ -469,10 +470,7 @@ srs_error_t SrsGbListener::listen_api() { srs_error_t err = srs_success; - // TODO: FIXME: Fetch api from hybrid manager, not from SRS. - ISrsHttpServeMux *http_api_mux = _srs_hybrid->srs()->instance()->api_server(); - - if ((err = http_api_mux->handle("/gb/v1/publish/", new SrsGoApiGbPublish(conf_))) != srs_success) { + if ((err = http_api_mux_->handle("/gb/v1/publish/", new SrsGoApiGbPublish(conf_))) != srs_success) { return srs_error_wrap(err, "handle publish"); } diff --git a/trunk/src/app/srs_app_gb28181.hpp b/trunk/src/app/srs_app_gb28181.hpp index 67bfd6c50..080e5a70f 100644 --- a/trunk/src/app/srs_app_gb28181.hpp +++ b/trunk/src/app/srs_app_gb28181.hpp @@ -40,6 +40,7 @@ class SrsRawHEVCStream; class SrsSharedPtrMessage; class SrsPithyPrint; class SrsRawAacStream; +class ISrsHttpServeMux; // The state machine for GB session. // init: @@ -218,9 +219,10 @@ private: SrsConfDirective *conf_; SrsTcpListener *media_listener_; SrsTcpListener *sip_listener_; + ISrsHttpServeMux *http_api_mux_; public: - SrsGbListener(); + SrsGbListener(ISrsHttpServeMux *http_api_mux); virtual ~SrsGbListener(); public: diff --git a/trunk/src/app/srs_app_http_api.cpp b/trunk/src/app/srs_app_http_api.cpp index 066aa2762..02bc0ef62 100644 --- a/trunk/src/app/srs_app_http_api.cpp +++ b/trunk/src/app/srs_app_http_api.cpp @@ -1225,7 +1225,7 @@ srs_error_t SrsGoApiSignal::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessag signo = SRS_SIGNAL_ASSERT_ABORT; } - _srs_hybrid->srs()->instance()->on_signal(signo); + _srs_server->on_signal(signo); // By default, response the json style response. SrsUniquePtr obj(SrsJsonAny::object()); diff --git a/trunk/src/app/srs_app_http_static.cpp b/trunk/src/app/srs_app_http_static.cpp index 16b7e7fc2..da5e6505f 100644 --- a/trunk/src/app/srs_app_http_static.cpp +++ b/trunk/src/app/srs_app_http_static.cpp @@ -16,7 +16,6 @@ using namespace std; #include #include -#include #include #include #include @@ -61,13 +60,13 @@ void SrsHlsVirtualConn::expire() SrsHlsStream::SrsHlsStream() { - _srs_hybrid->timer5s()->subscribe(this); + _srs_server->timer5s()->subscribe(this); security_ = new SrsSecurity(); } SrsHlsStream::~SrsHlsStream() { - _srs_hybrid->timer5s()->unsubscribe(this); + _srs_server->timer5s()->unsubscribe(this); std::map::iterator it; for (it = map_ctx_info_.begin(); it != map_ctx_info_.end(); ++it) { diff --git a/trunk/src/app/srs_app_hybrid.cpp b/trunk/src/app/srs_app_hybrid.cpp deleted file mode 100644 index 04f281e73..000000000 --- a/trunk/src/app/srs_app_hybrid.cpp +++ /dev/null @@ -1,797 +0,0 @@ -// -// Copyright (c) 2013-2025 The SRS Authors -// -// SPDX-License-Identifier: MIT -// - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#ifdef SRS_SRT -#include -#endif -#ifdef SRS_GB28181 -#include -#endif -#ifdef SRS_RTSP -#include -#endif -#include - -#include -#include -#include -#include -#include - -using namespace std; - -SrsAsyncCallWorker *_srs_dvr_async = NULL; - -extern SrsPps *_srs_pps_cids_get; -extern SrsPps *_srs_pps_cids_set; - -extern SrsPps *_srs_pps_timer; -extern SrsPps *_srs_pps_pub; -extern SrsPps *_srs_pps_conn; -extern SrsPps *_srs_pps_dispose; - -#if defined(SRS_DEBUG) && defined(SRS_DEBUG_STATS) -extern __thread unsigned long long _st_stat_recvfrom; -extern __thread unsigned long long _st_stat_recvfrom_eagain; -extern __thread unsigned long long _st_stat_sendto; -extern __thread unsigned long long _st_stat_sendto_eagain; -SrsPps *_srs_pps_recvfrom = NULL; -SrsPps *_srs_pps_recvfrom_eagain = NULL; -SrsPps *_srs_pps_sendto = NULL; -SrsPps *_srs_pps_sendto_eagain = NULL; - -extern __thread unsigned long long _st_stat_read; -extern __thread unsigned long long _st_stat_read_eagain; -extern __thread unsigned long long _st_stat_readv; -extern __thread unsigned long long _st_stat_readv_eagain; -extern __thread unsigned long long _st_stat_writev; -extern __thread unsigned long long _st_stat_writev_eagain; -SrsPps *_srs_pps_read = NULL; -SrsPps *_srs_pps_read_eagain = NULL; -SrsPps *_srs_pps_readv = NULL; -SrsPps *_srs_pps_readv_eagain = NULL; -SrsPps *_srs_pps_writev = NULL; -SrsPps *_srs_pps_writev_eagain = NULL; - -extern __thread unsigned long long _st_stat_recvmsg; -extern __thread unsigned long long _st_stat_recvmsg_eagain; -extern __thread unsigned long long _st_stat_sendmsg; -extern __thread unsigned long long _st_stat_sendmsg_eagain; -SrsPps *_srs_pps_recvmsg = NULL; -SrsPps *_srs_pps_recvmsg_eagain = NULL; -SrsPps *_srs_pps_sendmsg = NULL; -SrsPps *_srs_pps_sendmsg_eagain = NULL; - -extern __thread unsigned long long _st_stat_epoll; -extern __thread unsigned long long _st_stat_epoll_zero; -extern __thread unsigned long long _st_stat_epoll_shake; -extern __thread unsigned long long _st_stat_epoll_spin; -SrsPps *_srs_pps_epoll = NULL; -SrsPps *_srs_pps_epoll_zero = NULL; -SrsPps *_srs_pps_epoll_shake = NULL; -SrsPps *_srs_pps_epoll_spin = NULL; - -extern __thread unsigned long long _st_stat_sched_15ms; -extern __thread unsigned long long _st_stat_sched_20ms; -extern __thread unsigned long long _st_stat_sched_25ms; -extern __thread unsigned long long _st_stat_sched_30ms; -extern __thread unsigned long long _st_stat_sched_35ms; -extern __thread unsigned long long _st_stat_sched_40ms; -extern __thread unsigned long long _st_stat_sched_80ms; -extern __thread unsigned long long _st_stat_sched_160ms; -extern __thread unsigned long long _st_stat_sched_s; -SrsPps *_srs_pps_sched_15ms = NULL; -SrsPps *_srs_pps_sched_20ms = NULL; -SrsPps *_srs_pps_sched_25ms = NULL; -SrsPps *_srs_pps_sched_30ms = NULL; -SrsPps *_srs_pps_sched_35ms = NULL; -SrsPps *_srs_pps_sched_40ms = NULL; -SrsPps *_srs_pps_sched_80ms = NULL; -SrsPps *_srs_pps_sched_160ms = NULL; -SrsPps *_srs_pps_sched_s = NULL; -#endif - -SrsPps *_srs_pps_clock_15ms = NULL; -SrsPps *_srs_pps_clock_20ms = NULL; -SrsPps *_srs_pps_clock_25ms = NULL; -SrsPps *_srs_pps_clock_30ms = NULL; -SrsPps *_srs_pps_clock_35ms = NULL; -SrsPps *_srs_pps_clock_40ms = NULL; -SrsPps *_srs_pps_clock_80ms = NULL; -SrsPps *_srs_pps_clock_160ms = NULL; -SrsPps *_srs_pps_timer_s = NULL; - -#if defined(SRS_DEBUG) && defined(SRS_DEBUG_STATS) -extern __thread int _st_active_count; -extern __thread int _st_num_free_stacks; -extern __thread unsigned long long _st_stat_thread_run; -extern __thread unsigned long long _st_stat_thread_idle; -extern __thread unsigned long long _st_stat_thread_yield; -extern __thread unsigned long long _st_stat_thread_yield2; -SrsPps *_srs_pps_thread_run = NULL; -SrsPps *_srs_pps_thread_idle = NULL; -SrsPps *_srs_pps_thread_yield = NULL; -SrsPps *_srs_pps_thread_yield2 = NULL; -#endif - -extern SrsPps *_srs_pps_objs_rtps; -extern SrsPps *_srs_pps_objs_rraw; -extern SrsPps *_srs_pps_objs_rfua; -extern SrsPps *_srs_pps_objs_rbuf; -extern SrsPps *_srs_pps_objs_msgs; -extern SrsPps *_srs_pps_objs_rothers; - -extern ISrsLog *_srs_log; -extern ISrsContext *_srs_context; -extern SrsConfig *_srs_config; - -extern SrsStageManager *_srs_stages; - -extern SrsRtcBlackhole *_srs_blackhole; -extern SrsResourceManager *_srs_rtc_manager; - -extern SrsDtlsCertificate *_srs_rtc_dtls_certificate; - -SrsPps *_srs_pps_aloss2 = NULL; - -extern SrsPps *_srs_pps_ids; -extern SrsPps *_srs_pps_fids; -extern SrsPps *_srs_pps_fids_level0; -extern SrsPps *_srs_pps_dispose; - -extern SrsPps *_srs_pps_timer; - -extern SrsPps *_srs_pps_snack; -extern SrsPps *_srs_pps_snack2; -extern SrsPps *_srs_pps_snack3; -extern SrsPps *_srs_pps_snack4; -extern SrsPps *_srs_pps_sanack; -extern SrsPps *_srs_pps_svnack; - -extern SrsPps *_srs_pps_rnack; -extern SrsPps *_srs_pps_rnack2; -extern SrsPps *_srs_pps_rhnack; -extern SrsPps *_srs_pps_rmnack; - -#if defined(SRS_DEBUG) && defined(SRS_DEBUG_STATS) -extern SrsPps *_srs_pps_recvfrom; -extern SrsPps *_srs_pps_recvfrom_eagain; -extern SrsPps *_srs_pps_sendto; -extern SrsPps *_srs_pps_sendto_eagain; - -extern SrsPps *_srs_pps_read; -extern SrsPps *_srs_pps_read_eagain; -extern SrsPps *_srs_pps_readv; -extern SrsPps *_srs_pps_readv_eagain; -extern SrsPps *_srs_pps_writev; -extern SrsPps *_srs_pps_writev_eagain; - -extern SrsPps *_srs_pps_recvmsg; -extern SrsPps *_srs_pps_recvmsg_eagain; -extern SrsPps *_srs_pps_sendmsg; -extern SrsPps *_srs_pps_sendmsg_eagain; - -extern SrsPps *_srs_pps_epoll; -extern SrsPps *_srs_pps_epoll_zero; -extern SrsPps *_srs_pps_epoll_shake; -extern SrsPps *_srs_pps_epoll_spin; - -extern SrsPps *_srs_pps_sched_15ms; -extern SrsPps *_srs_pps_sched_20ms; -extern SrsPps *_srs_pps_sched_25ms; -extern SrsPps *_srs_pps_sched_30ms; -extern SrsPps *_srs_pps_sched_35ms; -extern SrsPps *_srs_pps_sched_40ms; -extern SrsPps *_srs_pps_sched_80ms; -extern SrsPps *_srs_pps_sched_160ms; -extern SrsPps *_srs_pps_sched_s; -#endif - -extern SrsPps *_srs_pps_clock_15ms; -extern SrsPps *_srs_pps_clock_20ms; -extern SrsPps *_srs_pps_clock_25ms; -extern SrsPps *_srs_pps_clock_30ms; -extern SrsPps *_srs_pps_clock_35ms; -extern SrsPps *_srs_pps_clock_40ms; -extern SrsPps *_srs_pps_clock_80ms; -extern SrsPps *_srs_pps_clock_160ms; -extern SrsPps *_srs_pps_timer_s; - -#if defined(SRS_DEBUG) && defined(SRS_DEBUG_STATS) -extern SrsPps *_srs_pps_thread_run; -extern SrsPps *_srs_pps_thread_idle; -extern SrsPps *_srs_pps_thread_yield; -extern SrsPps *_srs_pps_thread_yield2; -#endif - -extern SrsPps *_srs_pps_rpkts; -extern SrsPps *_srs_pps_addrs; -extern SrsPps *_srs_pps_fast_addrs; - -extern SrsPps *_srs_pps_spkts; - -extern SrsPps *_srs_pps_sstuns; -extern SrsPps *_srs_pps_srtcps; -extern SrsPps *_srs_pps_srtps; - -extern SrsPps *_srs_pps_pli; -extern SrsPps *_srs_pps_twcc; -extern SrsPps *_srs_pps_rr; -extern SrsPps *_srs_pps_pub; -extern SrsPps *_srs_pps_conn; - -extern SrsPps *_srs_pps_rstuns; -extern SrsPps *_srs_pps_rrtps; -extern SrsPps *_srs_pps_rrtcps; - -extern SrsPps *_srs_pps_aloss2; - -extern SrsPps *_srs_pps_cids_get; -extern SrsPps *_srs_pps_cids_set; - -extern SrsPps *_srs_pps_objs_msgs; - -extern SrsPps *_srs_pps_objs_rtps; -extern SrsPps *_srs_pps_objs_rraw; -extern SrsPps *_srs_pps_objs_rfua; -extern SrsPps *_srs_pps_objs_rbuf; -extern SrsPps *_srs_pps_objs_rothers; - -extern srs_error_t _srs_reload_err; -extern SrsReloadState _srs_reload_state; -extern std::string _srs_reload_id; - -ISrsHybridServer::ISrsHybridServer() -{ -} - -ISrsHybridServer::~ISrsHybridServer() -{ -} - -SrsHybridServer::SrsHybridServer() -{ - // Create global shared timer. - timer20ms_ = new SrsFastTimer("hybrid", 20 * SRS_UTIME_MILLISECONDS); - timer100ms_ = new SrsFastTimer("hybrid", 100 * SRS_UTIME_MILLISECONDS); - timer1s_ = new SrsFastTimer("hybrid", 1 * SRS_UTIME_SECONDS); - timer5s_ = new SrsFastTimer("hybrid", 5 * SRS_UTIME_SECONDS); - - clock_monitor_ = new SrsClockWallMonitor(); - - pid_fd = -1; -} - -SrsHybridServer::~SrsHybridServer() -{ - // We must free servers first, because it may depend on the timers of hybrid server. - vector::iterator it; - for (it = servers.begin(); it != servers.end(); ++it) { - ISrsHybridServer *server = *it; - srs_freep(server); - } - servers.clear(); - - srs_freep(clock_monitor_); - - srs_freep(timer20ms_); - srs_freep(timer100ms_); - srs_freep(timer1s_); - srs_freep(timer5s_); - - if (pid_fd > 0) { - ::close(pid_fd); - pid_fd = -1; - } -} - -void SrsHybridServer::register_server(ISrsHybridServer *svr) -{ - servers.push_back(svr); -} - -srs_error_t SrsHybridServer::initialize() -{ - srs_error_t err = srs_success; - - if ((err = acquire_pid_file()) != srs_success) { - return srs_error_wrap(err, "acquire pid file"); - } - - srs_trace("Hybrid server initialized in single thread mode"); - - // Start the timer first. - if ((err = timer20ms_->start()) != srs_success) { - return srs_error_wrap(err, "start timer"); - } - - if ((err = timer100ms_->start()) != srs_success) { - return srs_error_wrap(err, "start timer"); - } - - if ((err = timer1s_->start()) != srs_success) { - return srs_error_wrap(err, "start timer"); - } - - if ((err = timer5s_->start()) != srs_success) { - return srs_error_wrap(err, "start timer"); - } - - // Start the DVR async call. - if ((err = _srs_dvr_async->start()) != srs_success) { - return srs_error_wrap(err, "dvr async"); - } - - // Register some timers. - timer20ms_->subscribe(clock_monitor_); - timer5s_->subscribe(this); - - // Initialize all hybrid servers. - vector::iterator it; - for (it = servers.begin(); it != servers.end(); ++it) { - ISrsHybridServer *server = *it; - - if ((err = server->initialize()) != srs_success) { - return srs_error_wrap(err, "init server"); - } - } - - return err; -} - -srs_error_t SrsHybridServer::run() -{ - srs_error_t err = srs_success; - - // Wait for all servers which need to do cleanup. - SrsWaitGroup wg; - - vector::iterator it; - for (it = servers.begin(); it != servers.end(); ++it) { - ISrsHybridServer *server = *it; - - if ((err = server->run(&wg)) != srs_success) { - return srs_error_wrap(err, "run server"); - } - } - - // Wait for all server to quit. - wg.wait(); - - return err; -} - -void SrsHybridServer::stop() -{ - vector::iterator it; - for (it = servers.begin(); it != servers.end(); ++it) { - ISrsHybridServer *server = *it; - server->stop(); - } -} - -SrsServerAdapter *SrsHybridServer::srs() -{ - for (vector::iterator it = servers.begin(); it != servers.end(); ++it) { - if (dynamic_cast(*it)) { - return dynamic_cast(*it); - } - } - return NULL; -} - -SrsFastTimer *SrsHybridServer::timer20ms() -{ - return timer20ms_; -} - -SrsFastTimer *SrsHybridServer::timer100ms() -{ - return timer100ms_; -} - -SrsFastTimer *SrsHybridServer::timer1s() -{ - return timer1s_; -} - -SrsFastTimer *SrsHybridServer::timer5s() -{ - return timer5s_; -} - -srs_error_t SrsHybridServer::on_timer(srs_utime_t interval) -{ - srs_error_t err = srs_success; - - // Show statistics for RTC server. - SrsProcSelfStat *u = srs_get_self_proc_stat(); - // Resident Set Size: number of pages the process has in real memory. - int memory = (int)(u->rss * 4 / 1024); - - static char buf[128]; - - string cid_desc; - _srs_pps_cids_get->update(); - _srs_pps_cids_set->update(); - if (_srs_pps_cids_get->r10s() || _srs_pps_cids_set->r10s()) { - snprintf(buf, sizeof(buf), ", cid=%d,%d", _srs_pps_cids_get->r10s(), _srs_pps_cids_set->r10s()); - cid_desc = buf; - } - - string timer_desc; - _srs_pps_timer->update(); - _srs_pps_pub->update(); - _srs_pps_conn->update(); - if (_srs_pps_timer->r10s() || _srs_pps_pub->r10s() || _srs_pps_conn->r10s()) { - snprintf(buf, sizeof(buf), ", timer=%d,%d,%d", _srs_pps_timer->r10s(), _srs_pps_pub->r10s(), _srs_pps_conn->r10s()); - timer_desc = buf; - } - - string free_desc; - _srs_pps_dispose->update(); - if (_srs_pps_dispose->r10s()) { - snprintf(buf, sizeof(buf), ", free=%d", _srs_pps_dispose->r10s()); - free_desc = buf; - } - - string recvfrom_desc; -#if defined(SRS_DEBUG) && defined(SRS_DEBUG_STATS) - _srs_pps_recvfrom->update(_st_stat_recvfrom); - _srs_pps_recvfrom_eagain->update(_st_stat_recvfrom_eagain); - _srs_pps_sendto->update(_st_stat_sendto); - _srs_pps_sendto_eagain->update(_st_stat_sendto_eagain); - if (_srs_pps_recvfrom->r10s() || _srs_pps_recvfrom_eagain->r10s() || _srs_pps_sendto->r10s() || _srs_pps_sendto_eagain->r10s()) { - snprintf(buf, sizeof(buf), ", udp=%d,%d,%d,%d", _srs_pps_recvfrom->r10s(), _srs_pps_recvfrom_eagain->r10s(), _srs_pps_sendto->r10s(), _srs_pps_sendto_eagain->r10s()); - recvfrom_desc = buf; - } -#endif - - string io_desc; -#if defined(SRS_DEBUG) && defined(SRS_DEBUG_STATS) - _srs_pps_read->update(_st_stat_read); - _srs_pps_read_eagain->update(_st_stat_read_eagain); - _srs_pps_readv->update(_st_stat_readv); - _srs_pps_readv_eagain->update(_st_stat_readv_eagain); - _srs_pps_writev->update(_st_stat_writev); - _srs_pps_writev_eagain->update(_st_stat_writev_eagain); - if (_srs_pps_read->r10s() || _srs_pps_read_eagain->r10s() || _srs_pps_readv->r10s() || _srs_pps_readv_eagain->r10s() || _srs_pps_writev->r10s() || _srs_pps_writev_eagain->r10s()) { - snprintf(buf, sizeof(buf), ", io=%d,%d,%d,%d,%d,%d", _srs_pps_read->r10s(), _srs_pps_read_eagain->r10s(), _srs_pps_readv->r10s(), _srs_pps_readv_eagain->r10s(), _srs_pps_writev->r10s(), _srs_pps_writev_eagain->r10s()); - io_desc = buf; - } -#endif - - string msg_desc; -#if defined(SRS_DEBUG) && defined(SRS_DEBUG_STATS) - _srs_pps_recvmsg->update(_st_stat_recvmsg); - _srs_pps_recvmsg_eagain->update(_st_stat_recvmsg_eagain); - _srs_pps_sendmsg->update(_st_stat_sendmsg); - _srs_pps_sendmsg_eagain->update(_st_stat_sendmsg_eagain); - if (_srs_pps_recvmsg->r10s() || _srs_pps_recvmsg_eagain->r10s() || _srs_pps_sendmsg->r10s() || _srs_pps_sendmsg_eagain->r10s()) { - snprintf(buf, sizeof(buf), ", msg=%d,%d,%d,%d", _srs_pps_recvmsg->r10s(), _srs_pps_recvmsg_eagain->r10s(), _srs_pps_sendmsg->r10s(), _srs_pps_sendmsg_eagain->r10s()); - msg_desc = buf; - } -#endif - - string epoll_desc; -#if defined(SRS_DEBUG) && defined(SRS_DEBUG_STATS) - _srs_pps_epoll->update(_st_stat_epoll); - _srs_pps_epoll_zero->update(_st_stat_epoll_zero); - _srs_pps_epoll_shake->update(_st_stat_epoll_shake); - _srs_pps_epoll_spin->update(_st_stat_epoll_spin); - if (_srs_pps_epoll->r10s() || _srs_pps_epoll_zero->r10s() || _srs_pps_epoll_shake->r10s() || _srs_pps_epoll_spin->r10s()) { - snprintf(buf, sizeof(buf), ", epoll=%d,%d,%d,%d", _srs_pps_epoll->r10s(), _srs_pps_epoll_zero->r10s(), _srs_pps_epoll_shake->r10s(), _srs_pps_epoll_spin->r10s()); - epoll_desc = buf; - } -#endif - - string sched_desc; -#if defined(SRS_DEBUG) && defined(SRS_DEBUG_STATS) - _srs_pps_sched_160ms->update(_st_stat_sched_160ms); - _srs_pps_sched_s->update(_st_stat_sched_s); - _srs_pps_sched_15ms->update(_st_stat_sched_15ms); - _srs_pps_sched_20ms->update(_st_stat_sched_20ms); - _srs_pps_sched_25ms->update(_st_stat_sched_25ms); - _srs_pps_sched_30ms->update(_st_stat_sched_30ms); - _srs_pps_sched_35ms->update(_st_stat_sched_35ms); - _srs_pps_sched_40ms->update(_st_stat_sched_40ms); - _srs_pps_sched_80ms->update(_st_stat_sched_80ms); - if (_srs_pps_sched_160ms->r10s() || _srs_pps_sched_s->r10s() || _srs_pps_sched_15ms->r10s() || _srs_pps_sched_20ms->r10s() || _srs_pps_sched_25ms->r10s() || _srs_pps_sched_30ms->r10s() || _srs_pps_sched_35ms->r10s() || _srs_pps_sched_40ms->r10s() || _srs_pps_sched_80ms->r10s()) { - snprintf(buf, sizeof(buf), ", sched=%d,%d,%d,%d,%d,%d,%d,%d,%d", _srs_pps_sched_15ms->r10s(), _srs_pps_sched_20ms->r10s(), _srs_pps_sched_25ms->r10s(), _srs_pps_sched_30ms->r10s(), _srs_pps_sched_35ms->r10s(), _srs_pps_sched_40ms->r10s(), _srs_pps_sched_80ms->r10s(), _srs_pps_sched_160ms->r10s(), _srs_pps_sched_s->r10s()); - sched_desc = buf; - } -#endif - - string clock_desc; - _srs_pps_clock_15ms->update(); - _srs_pps_clock_20ms->update(); - _srs_pps_clock_25ms->update(); - _srs_pps_clock_30ms->update(); - _srs_pps_clock_35ms->update(); - _srs_pps_clock_40ms->update(); - _srs_pps_clock_80ms->update(); - _srs_pps_clock_160ms->update(); - _srs_pps_timer_s->update(); - if (_srs_pps_clock_15ms->r10s() || _srs_pps_timer_s->r10s() || _srs_pps_clock_20ms->r10s() || _srs_pps_clock_25ms->r10s() || _srs_pps_clock_30ms->r10s() || _srs_pps_clock_35ms->r10s() || _srs_pps_clock_40ms->r10s() || _srs_pps_clock_80ms->r10s() || _srs_pps_clock_160ms->r10s()) { - snprintf(buf, sizeof(buf), ", clock=%d,%d,%d,%d,%d,%d,%d,%d,%d", _srs_pps_clock_15ms->r10s(), _srs_pps_clock_20ms->r10s(), _srs_pps_clock_25ms->r10s(), _srs_pps_clock_30ms->r10s(), _srs_pps_clock_35ms->r10s(), _srs_pps_clock_40ms->r10s(), _srs_pps_clock_80ms->r10s(), _srs_pps_clock_160ms->r10s(), _srs_pps_timer_s->r10s()); - clock_desc = buf; - } - - string thread_desc; -#if defined(SRS_DEBUG) && defined(SRS_DEBUG_STATS) - _srs_pps_thread_run->update(_st_stat_thread_run); - _srs_pps_thread_idle->update(_st_stat_thread_idle); - _srs_pps_thread_yield->update(_st_stat_thread_yield); - _srs_pps_thread_yield2->update(_st_stat_thread_yield2); - if (_st_active_count > 0 || _st_num_free_stacks > 0 || _srs_pps_thread_run->r10s() || _srs_pps_thread_idle->r10s() || _srs_pps_thread_yield->r10s() || _srs_pps_thread_yield2->r10s()) { - snprintf(buf, sizeof(buf), ", co=%d,%d,%d, stk=%d, yield=%d,%d", _st_active_count, _srs_pps_thread_run->r10s(), _srs_pps_thread_idle->r10s(), _st_num_free_stacks, _srs_pps_thread_yield->r10s(), _srs_pps_thread_yield2->r10s()); - thread_desc = buf; - } -#endif - - string objs_desc; - _srs_pps_objs_rtps->update(); - _srs_pps_objs_rraw->update(); - _srs_pps_objs_rfua->update(); - _srs_pps_objs_rbuf->update(); - _srs_pps_objs_msgs->update(); - _srs_pps_objs_rothers->update(); - if (_srs_pps_objs_rtps->r10s() || _srs_pps_objs_rraw->r10s() || _srs_pps_objs_rfua->r10s() || _srs_pps_objs_rbuf->r10s() || _srs_pps_objs_msgs->r10s() || _srs_pps_objs_rothers->r10s()) { - snprintf(buf, sizeof(buf), ", objs=(pkt:%d,raw:%d,fua:%d,msg:%d,oth:%d,buf:%d)", - _srs_pps_objs_rtps->r10s(), _srs_pps_objs_rraw->r10s(), _srs_pps_objs_rfua->r10s(), - _srs_pps_objs_msgs->r10s(), _srs_pps_objs_rothers->r10s(), _srs_pps_objs_rbuf->r10s()); - objs_desc = buf; - } - - srs_trace("Hybrid cpu=%.2f%%,%dMB%s%s%s%s%s%s%s%s%s%s%s", - u->percent * 100, memory, - cid_desc.c_str(), timer_desc.c_str(), - recvfrom_desc.c_str(), io_desc.c_str(), msg_desc.c_str(), - epoll_desc.c_str(), sched_desc.c_str(), clock_desc.c_str(), - thread_desc.c_str(), free_desc.c_str(), objs_desc.c_str()); - - return err; -} - -srs_error_t SrsHybridServer::acquire_pid_file() -{ - std::string pid_file = _srs_config->get_pid_file(); - - // -rw-r--r-- - // 644 - int mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; - - int fd; - // open pid file - if ((fd = ::open(pid_file.c_str(), O_WRONLY | O_CREAT, mode)) == -1) { - return srs_error_new(ERROR_SYSTEM_PID_ACQUIRE, "open pid file=%s", pid_file.c_str()); - } - - // require write lock - struct flock lock; - - lock.l_type = F_WRLCK; // F_RDLCK, F_WRLCK, F_UNLCK - lock.l_start = 0; // type offset, relative to l_whence - lock.l_whence = SEEK_SET; // SEEK_SET, SEEK_CUR, SEEK_END - lock.l_len = 0; - - if (fcntl(fd, F_SETLK, &lock) == -1) { - if (errno == EACCES || errno == EAGAIN) { - ::close(fd); - srs_error("srs is already running!"); - return srs_error_new(ERROR_SYSTEM_PID_ALREADY_RUNNING, "srs is already running"); - } - return srs_error_new(ERROR_SYSTEM_PID_LOCK, "access to pid=%s", pid_file.c_str()); - } - - // truncate file - if (ftruncate(fd, 0) != 0) { - return srs_error_new(ERROR_SYSTEM_PID_TRUNCATE_FILE, "truncate pid file=%s", pid_file.c_str()); - } - - // write the pid - string pid = srs_strconv_format_int(getpid()); - if (write(fd, pid.c_str(), pid.length()) != (int)pid.length()) { - return srs_error_new(ERROR_SYSTEM_PID_WRITE_FILE, "write pid=%s to file=%s", pid.c_str(), pid_file.c_str()); - } - - // auto close when fork child process. - int val; - if ((val = fcntl(fd, F_GETFD, 0)) < 0) { - return srs_error_new(ERROR_SYSTEM_PID_GET_FILE_INFO, "fcntl fd=%d", fd); - } - val |= FD_CLOEXEC; - if (fcntl(fd, F_SETFD, val) < 0) { - return srs_error_new(ERROR_SYSTEM_PID_SET_FILE_INFO, "lock file=%s fd=%d", pid_file.c_str(), fd); - } - - srs_trace("write pid=%s to %s success!", pid.c_str(), pid_file.c_str()); - pid_fd = fd; - - return srs_success; -} - -srs_error_t srs_global_initialize() -{ - srs_error_t err = srs_success; - - // Root global objects. - _srs_log = new SrsFileLog(); - _srs_context = new SrsThreadContext(); - _srs_config = new SrsConfig(); - - // The clock wall object. - _srs_clock = new SrsWallClock(); - - // The pps cids depends by st init. - _srs_pps_cids_get = new SrsPps(); - _srs_pps_cids_set = new SrsPps(); - - // Initialize ST, which depends on pps cids. - if ((err = srs_st_init()) != srs_success) { - return srs_error_wrap(err, "initialize st failed"); - } - - // The global objects which depends on ST. - _srs_hybrid = new SrsHybridServer(); - _srs_sources = new SrsLiveSourceManager(); - _srs_stages = new SrsStageManager(); - _srs_circuit_breaker = new SrsCircuitBreaker(); - _srs_hooks = new SrsHttpHooks(); - -#ifdef SRS_SRT - _srs_srt_sources = new SrsSrtSourceManager(); -#endif - - _srs_rtc_sources = new SrsRtcSourceManager(); - _srs_blackhole = new SrsRtcBlackhole(); - - // Initialize stream publish token manager - _srs_stream_publish_tokens = new SrsStreamPublishTokenManager(); - - _srs_rtc_manager = new SrsResourceManager("RTC", true); - _srs_rtc_dtls_certificate = new SrsDtlsCertificate(); -#ifdef SRS_RTSP - _srs_rtsp_sources = new SrsRtspSourceManager(); - _srs_rtsp_manager = new SrsResourceManager("RTSP", true); -#endif -#ifdef SRS_GB28181 - _srs_gb_manager = new SrsResourceManager("GB", true); -#endif - - // Initialize global pps, which depends on _srs_clock - _srs_pps_ids = new SrsPps(); - _srs_pps_fids = new SrsPps(); - _srs_pps_fids_level0 = new SrsPps(); - _srs_pps_dispose = new SrsPps(); - - _srs_pps_timer = new SrsPps(); - _srs_pps_conn = new SrsPps(); - _srs_pps_pub = new SrsPps(); - - _srs_pps_snack = new SrsPps(); - _srs_pps_snack2 = new SrsPps(); - _srs_pps_snack3 = new SrsPps(); - _srs_pps_snack4 = new SrsPps(); - _srs_pps_sanack = new SrsPps(); - _srs_pps_svnack = new SrsPps(); - - _srs_pps_rnack = new SrsPps(); - _srs_pps_rnack2 = new SrsPps(); - _srs_pps_rhnack = new SrsPps(); - _srs_pps_rmnack = new SrsPps(); - -#if defined(SRS_DEBUG) && defined(SRS_DEBUG_STATS) - _srs_pps_recvfrom = new SrsPps(); - _srs_pps_recvfrom_eagain = new SrsPps(); - _srs_pps_sendto = new SrsPps(); - _srs_pps_sendto_eagain = new SrsPps(); - - _srs_pps_read = new SrsPps(); - _srs_pps_read_eagain = new SrsPps(); - _srs_pps_readv = new SrsPps(); - _srs_pps_readv_eagain = new SrsPps(); - _srs_pps_writev = new SrsPps(); - _srs_pps_writev_eagain = new SrsPps(); - - _srs_pps_recvmsg = new SrsPps(); - _srs_pps_recvmsg_eagain = new SrsPps(); - _srs_pps_sendmsg = new SrsPps(); - _srs_pps_sendmsg_eagain = new SrsPps(); - - _srs_pps_epoll = new SrsPps(); - _srs_pps_epoll_zero = new SrsPps(); - _srs_pps_epoll_shake = new SrsPps(); - _srs_pps_epoll_spin = new SrsPps(); - - _srs_pps_sched_15ms = new SrsPps(); - _srs_pps_sched_20ms = new SrsPps(); - _srs_pps_sched_25ms = new SrsPps(); - _srs_pps_sched_30ms = new SrsPps(); - _srs_pps_sched_35ms = new SrsPps(); - _srs_pps_sched_40ms = new SrsPps(); - _srs_pps_sched_80ms = new SrsPps(); - _srs_pps_sched_160ms = new SrsPps(); - _srs_pps_sched_s = new SrsPps(); -#endif - - _srs_pps_clock_15ms = new SrsPps(); - _srs_pps_clock_20ms = new SrsPps(); - _srs_pps_clock_25ms = new SrsPps(); - _srs_pps_clock_30ms = new SrsPps(); - _srs_pps_clock_35ms = new SrsPps(); - _srs_pps_clock_40ms = new SrsPps(); - _srs_pps_clock_80ms = new SrsPps(); - _srs_pps_clock_160ms = new SrsPps(); - _srs_pps_timer_s = new SrsPps(); - -#if defined(SRS_DEBUG) && defined(SRS_DEBUG_STATS) - _srs_pps_thread_run = new SrsPps(); - _srs_pps_thread_idle = new SrsPps(); - _srs_pps_thread_yield = new SrsPps(); - _srs_pps_thread_yield2 = new SrsPps(); -#endif - - _srs_pps_rpkts = new SrsPps(); - _srs_pps_addrs = new SrsPps(); - _srs_pps_fast_addrs = new SrsPps(); - - _srs_pps_spkts = new SrsPps(); - _srs_pps_objs_msgs = new SrsPps(); - - _srs_pps_sstuns = new SrsPps(); - _srs_pps_srtcps = new SrsPps(); - _srs_pps_srtps = new SrsPps(); - - _srs_pps_rstuns = new SrsPps(); - _srs_pps_rrtps = new SrsPps(); - _srs_pps_rrtcps = new SrsPps(); - - _srs_pps_aloss2 = new SrsPps(); - - _srs_pps_pli = new SrsPps(); - _srs_pps_twcc = new SrsPps(); - _srs_pps_rr = new SrsPps(); - - _srs_pps_objs_rtps = new SrsPps(); - _srs_pps_objs_rraw = new SrsPps(); - _srs_pps_objs_rfua = new SrsPps(); - _srs_pps_objs_rbuf = new SrsPps(); - _srs_pps_objs_rothers = new SrsPps(); - - // Create global async worker for DVR. - _srs_dvr_async = new SrsAsyncCallWorker(); - - _srs_reload_err = srs_success; - _srs_reload_state = SrsReloadStateInit; - _srs_reload_id = srs_rand_gen_str(7); - - return err; -} - -SrsHybridServer *_srs_hybrid = NULL; diff --git a/trunk/src/app/srs_app_hybrid.hpp b/trunk/src/app/srs_app_hybrid.hpp deleted file mode 100644 index 7ba8b0b95..000000000 --- a/trunk/src/app/srs_app_hybrid.hpp +++ /dev/null @@ -1,86 +0,0 @@ -// -// Copyright (c) 2013-2025 The SRS Authors -// -// SPDX-License-Identifier: MIT -// - -#ifndef SRS_APP_HYBRID_HPP -#define SRS_APP_HYBRID_HPP - -#include - -#include - -#include - -class SrsServer; -class SrsServerAdapter; -class SrsWaitGroup; - -// Initialize global shared variables cross all threads. -extern srs_error_t srs_global_initialize(); - -// The hibrid server interfaces, we could register many servers. -class ISrsHybridServer -{ -public: - ISrsHybridServer(); - virtual ~ISrsHybridServer(); - -public: - // Only ST initialized before each server, we could fork processes as such. - virtual srs_error_t initialize() = 0; - // Run each server, should never block except the SRS master server. - virtual srs_error_t run(SrsWaitGroup *wg) = 0; - // Stop each server, should do cleanup, for example, kill processes forked by server. - virtual void stop() = 0; -}; - -// The hybrid server manager. -class SrsHybridServer : public ISrsFastTimer -{ -private: - std::vector servers; - SrsFastTimer *timer20ms_; - SrsFastTimer *timer100ms_; - SrsFastTimer *timer1s_; - SrsFastTimer *timer5s_; - SrsClockWallMonitor *clock_monitor_; - -private: - // The pid file fd, lock the file write when server is running. - // @remark the init.d script should cleanup the pid file, when stop service, - // for the server never delete the file; when system startup, the pid in pid file - // maybe valid but the process is not SRS, the init.d script will never start server. - int pid_fd; - -public: - SrsHybridServer(); - virtual ~SrsHybridServer(); - -public: - virtual void register_server(ISrsHybridServer *svr); - -public: - virtual srs_error_t initialize(); - virtual srs_error_t run(); - virtual void stop(); - -public: - virtual SrsServerAdapter *srs(); - SrsFastTimer *timer20ms(); - SrsFastTimer *timer100ms(); - SrsFastTimer *timer1s(); - SrsFastTimer *timer5s(); - // interface ISrsFastTimer -private: - srs_error_t on_timer(srs_utime_t interval); - -private: - // Require the PID file for the whole process. - virtual srs_error_t acquire_pid_file(); -}; - -extern SrsHybridServer *_srs_hybrid; - -#endif diff --git a/trunk/src/app/srs_app_listener.cpp b/trunk/src/app/srs_app_listener.cpp index d591cf589..aa27a7214 100644 --- a/trunk/src/app/srs_app_listener.cpp +++ b/trunk/src/app/srs_app_listener.cpp @@ -197,22 +197,9 @@ srs_error_t SrsUdpListener::cycle() return srs_error_wrap(err, "udp listener"); } - int nread = 0; - sockaddr_storage from; - int nb_from = sizeof(from); - if ((nread = srs_recvfrom(lfd, buf, nb_buf, (sockaddr *)&from, &nb_from, SRS_UTIME_NO_TIMEOUT)) <= 0) { - return srs_error_new(ERROR_SOCKET_READ, "udp read, nread=%d", nread); - } - - // Drop UDP health check packet of Aliyun SLB. - // Healthcheck udp check - // @see https://help.aliyun.com/document_detail/27595.html - if (nread == 21 && buf[0] == 0x48 && buf[1] == 0x65 && buf[2] == 0x61 && buf[3] == 0x6c && buf[19] == 0x63 && buf[20] == 0x6b) { - continue; - } - - if ((err = handler->on_udp_packet((const sockaddr *)&from, nb_from, buf, nread)) != srs_success) { - return srs_error_wrap(err, "handle packet %d bytes", nread); + if ((err = do_cycle()) != srs_success) { + srs_warn("%s listener: Ignore error, %s", label_.c_str(), srs_error_desc(err).c_str()); + srs_freep(err); } if (SrsUdpPacketRecvCycleInterval > 0) { @@ -223,6 +210,31 @@ srs_error_t SrsUdpListener::cycle() return err; } +srs_error_t SrsUdpListener::do_cycle() +{ + srs_error_t err = srs_success; + + int nread = 0; + sockaddr_storage from; + int nb_from = sizeof(from); + if ((nread = srs_recvfrom(lfd, buf, nb_buf, (sockaddr *)&from, &nb_from, SRS_UTIME_NO_TIMEOUT)) <= 0) { + return srs_error_new(ERROR_SOCKET_READ, "udp read, nread=%d", nread); + } + + // Drop UDP health check packet of Aliyun SLB. + // Healthcheck udp check + // @see https://help.aliyun.com/document_detail/27595.html + if (nread == 21 && buf[0] == 0x48 && buf[1] == 0x65 && buf[2] == 0x61 && buf[3] == 0x6c && buf[19] == 0x63 && buf[20] == 0x6b) { + return err; + } + + if ((err = handler->on_udp_packet((const sockaddr *)&from, nb_from, buf, nread)) != srs_success) { + return srs_error_wrap(err, "handle packet %d bytes", nread); + } + + return err; +} + SrsTcpListener::SrsTcpListener(ISrsTcpHandler *h) { handler = h; @@ -304,18 +316,30 @@ srs_error_t SrsTcpListener::cycle() return srs_error_wrap(err, "tcp listener"); } - srs_netfd_t fd = srs_accept(lfd, NULL, NULL, SRS_UTIME_NO_TIMEOUT); - if (fd == NULL) { - return srs_error_new(ERROR_SOCKET_ACCEPT, "accept at fd=%d", srs_netfd_fileno(lfd)); + if ((err = do_cycle()) != srs_success) { + srs_warn("%s listener: Ignore error, %s", label_.c_str(), srs_error_desc(err).c_str()); + srs_freep(err); } + } - if ((err = srs_fd_closeexec(srs_netfd_fileno(fd))) != srs_success) { - return srs_error_wrap(err, "set closeexec"); - } + return err; +} - if ((err = handler->on_tcp_client(this, fd)) != srs_success) { - return srs_error_wrap(err, "handle fd=%d", srs_netfd_fileno(fd)); - } +srs_error_t SrsTcpListener::do_cycle() +{ + srs_error_t err = srs_success; + + srs_netfd_t fd = srs_accept(lfd, NULL, NULL, SRS_UTIME_NO_TIMEOUT); + if (fd == NULL) { + return srs_error_new(ERROR_SOCKET_ACCEPT, "accept at fd=%d", srs_netfd_fileno(lfd)); + } + + if ((err = srs_fd_closeexec(srs_netfd_fileno(fd))) != srs_success) { + return srs_error_wrap(err, "set closeexec"); + } + + if ((err = handler->on_tcp_client(this, fd)) != srs_success) { + return srs_error_wrap(err, "handle fd=%d", srs_netfd_fileno(fd)); } return err; diff --git a/trunk/src/app/srs_app_listener.hpp b/trunk/src/app/srs_app_listener.hpp index e26615189..8b0cf13fe 100644 --- a/trunk/src/app/srs_app_listener.hpp +++ b/trunk/src/app/srs_app_listener.hpp @@ -114,6 +114,9 @@ public: // Interface ISrsReusableThreadHandler. public: virtual srs_error_t cycle(); + +private: + srs_error_t do_cycle(); }; // Bind and listen tcp port, use handler to process the client. @@ -145,6 +148,9 @@ public: // Interface ISrsReusableThreadHandler. public: virtual srs_error_t cycle(); + +private: + srs_error_t do_cycle(); }; // Bind and listen tcp port, use handler to process the client. diff --git a/trunk/src/app/srs_app_rtc_api.cpp b/trunk/src/app/srs_app_rtc_api.cpp index 420ce12b8..8bb67fac0 100644 --- a/trunk/src/app/srs_app_rtc_api.cpp +++ b/trunk/src/app/srs_app_rtc_api.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -28,7 +29,7 @@ using namespace std; // To limit user to use too long password, to cause unknown issue. #define SRS_ICE_PWD_MAX 32 -SrsGoApiRtcPlay::SrsGoApiRtcPlay(SrsRtcServer *server) +SrsGoApiRtcPlay::SrsGoApiRtcPlay(SrsServer *server) { server_ = server; security_ = new SrsSecurity(); @@ -238,7 +239,7 @@ srs_error_t SrsGoApiRtcPlay::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessa // TODO: FIXME: When server enabled, but vhost disabled, should report error. SrsRtcConnection *session = NULL; - if ((err = server_->create_session(ruc, local_sdp, &session)) != srs_success) { + if ((err = server_->create_rtc_session(ruc, local_sdp, &session)) != srs_success) { return srs_error_wrap(err, "create session, dtls=%u, srtp=%u, eip=%s", ruc->dtls_, ruc->srtp_, ruc->eip_.c_str()); } @@ -325,7 +326,7 @@ srs_error_t SrsGoApiRtcPlay::http_hooks_on_play(ISrsRequest *req) return err; } -SrsGoApiRtcPublish::SrsGoApiRtcPublish(SrsRtcServer *server) +SrsGoApiRtcPublish::SrsGoApiRtcPublish(SrsServer *server) { server_ = server; security_ = new SrsSecurity(); @@ -504,7 +505,7 @@ srs_error_t SrsGoApiRtcPublish::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMe // TODO: FIXME: When server enabled, but vhost disabled, should report error. // We must do stat the client before hooks, because hooks depends on it. SrsRtcConnection *session = NULL; - if ((err = server_->create_session(ruc, local_sdp, &session)) != srs_success) { + if ((err = server_->create_rtc_session(ruc, local_sdp, &session)) != srs_success) { return srs_error_wrap(err, "create session"); } @@ -598,7 +599,7 @@ srs_error_t SrsGoApiRtcPublish::http_hooks_on_publish(ISrsRequest *req) return err; } -SrsGoApiRtcWhip::SrsGoApiRtcWhip(SrsRtcServer *server) +SrsGoApiRtcWhip::SrsGoApiRtcWhip(SrsServer *server) { server_ = server; publish_ = new SrsGoApiRtcPublish(server); @@ -627,7 +628,7 @@ srs_error_t SrsGoApiRtcWhip::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessa return srs_error_new(ERROR_RTC_INVALID_SESSION, "token empty"); } - SrsRtcConnection *session = server_->find_session_by_username(username); + SrsRtcConnection *session = server_->find_rtc_session_by_username(username); if (session && token != session->token()) { return srs_error_new(ERROR_RTC_INVALID_SESSION, "token %s not match", token.c_str()); } @@ -759,7 +760,7 @@ srs_error_t SrsGoApiRtcWhip::do_serve_http(ISrsHttpResponseWriter *w, ISrsHttpMe return err; } -SrsGoApiRtcNACK::SrsGoApiRtcNACK(SrsRtcServer *server) +SrsGoApiRtcNACK::SrsGoApiRtcNACK(SrsServer *server) { server_ = server; } @@ -802,7 +803,7 @@ srs_error_t SrsGoApiRtcNACK::do_serve_http(ISrsHttpResponseWriter *w, ISrsHttpMe return srs_error_new(ERROR_RTC_INVALID_PARAMS, "invalid drop=%s/%d", dropv.c_str(), drop); } - SrsRtcConnection *session = server_->find_session_by_username(username); + SrsRtcConnection *session = server_->find_rtc_session_by_username(username); if (!session) { return srs_error_new(ERROR_RTC_NO_SESSION, "no session username=%s", username.c_str()); } diff --git a/trunk/src/app/srs_app_rtc_api.hpp b/trunk/src/app/srs_app_rtc_api.hpp index bc9aea12e..45bfa35fd 100644 --- a/trunk/src/app/srs_app_rtc_api.hpp +++ b/trunk/src/app/srs_app_rtc_api.hpp @@ -11,7 +11,7 @@ #include #include -class SrsRtcServer; +class SrsServer; class ISrsRequest; class SrsSdp; class SrsRtcUserConfig; @@ -19,11 +19,11 @@ class SrsRtcUserConfig; class SrsGoApiRtcPlay : public ISrsHttpHandler { private: - SrsRtcServer *server_; + SrsServer *server_; SrsSecurity *security_; public: - SrsGoApiRtcPlay(SrsRtcServer *server); + SrsGoApiRtcPlay(SrsServer *server); virtual ~SrsGoApiRtcPlay(); public: @@ -45,11 +45,11 @@ private: class SrsGoApiRtcPublish : public ISrsHttpHandler { private: - SrsRtcServer *server_; + SrsServer *server_; SrsSecurity *security_; public: - SrsGoApiRtcPublish(SrsRtcServer *server); + SrsGoApiRtcPublish(SrsServer *server); virtual ~SrsGoApiRtcPublish(); public: @@ -72,12 +72,12 @@ private: class SrsGoApiRtcWhip : public ISrsHttpHandler { private: - SrsRtcServer *server_; + SrsServer *server_; SrsGoApiRtcPublish *publish_; SrsGoApiRtcPlay *play_; public: - SrsGoApiRtcWhip(SrsRtcServer *server); + SrsGoApiRtcWhip(SrsServer *server); virtual ~SrsGoApiRtcWhip(); public: @@ -90,10 +90,10 @@ private: class SrsGoApiRtcNACK : public ISrsHttpHandler { private: - SrsRtcServer *server_; + SrsServer *server_; public: - SrsGoApiRtcNACK(SrsRtcServer *server); + SrsGoApiRtcNACK(SrsServer *server); virtual ~SrsGoApiRtcNACK(); public: diff --git a/trunk/src/app/srs_app_rtc_conn.cpp b/trunk/src/app/srs_app_rtc_conn.cpp index b74731956..dad3fbfba 100644 --- a/trunk/src/app/srs_app_rtc_conn.cpp +++ b/trunk/src/app/srs_app_rtc_conn.cpp @@ -442,7 +442,7 @@ SrsRtcPlayStream::SrsRtcPlayStream(SrsRtcConnection *s, const SrsContextId &cid) SrsRtcPlayStream::~SrsRtcPlayStream() { if (req_) { - session_->server_->exec_async_work(new SrsRtcAsyncCallOnStop(cid_, req_)); + session_->server_->exec_rtc_async_work(new SrsRtcAsyncCallOnStop(cid_, req_)); } _srs_config->unsubscribe(this); @@ -927,12 +927,12 @@ srs_error_t SrsRtcPlayStream::do_request_keyframe(uint32_t ssrc, SrsContextId ci SrsRtcPublishRtcpTimer::SrsRtcPublishRtcpTimer(SrsRtcPublishStream *p) : p_(p) { - _srs_hybrid->timer1s()->subscribe(this); + _srs_server->timer1s()->subscribe(this); } SrsRtcPublishRtcpTimer::~SrsRtcPublishRtcpTimer() { - _srs_hybrid->timer1s()->unsubscribe(this); + _srs_server->timer1s()->unsubscribe(this); } srs_error_t SrsRtcPublishRtcpTimer::on_timer(srs_utime_t interval) @@ -963,12 +963,12 @@ srs_error_t SrsRtcPublishRtcpTimer::on_timer(srs_utime_t interval) SrsRtcPublishTwccTimer::SrsRtcPublishTwccTimer(SrsRtcPublishStream *p) : p_(p) { - _srs_hybrid->timer100ms()->subscribe(this); + _srs_server->timer100ms()->subscribe(this); } SrsRtcPublishTwccTimer::~SrsRtcPublishTwccTimer() { - _srs_hybrid->timer100ms()->unsubscribe(this); + _srs_server->timer100ms()->unsubscribe(this); } srs_error_t SrsRtcPublishTwccTimer::on_timer(srs_utime_t interval) @@ -1084,7 +1084,7 @@ SrsRtcPublishStream::SrsRtcPublishStream(SrsRtcConnection *session, const SrsCon SrsRtcPublishStream::~SrsRtcPublishStream() { if (req_) { - session_->server_->exec_async_work(new SrsRtcAsyncCallOnUnpublish(cid_, req_)); + session_->server_->exec_rtc_async_work(new SrsRtcAsyncCallOnUnpublish(cid_, req_)); } srs_freep(timer_rtcp_); @@ -1176,16 +1176,6 @@ srs_error_t SrsRtcPublishStream::initialize(ISrsRequest *r, SrsRtcSourceDescript track->set_nack_no_copy(nack_no_copy_); } - // Acquire stream publish token to prevent race conditions across all protocols. - SrsStreamPublishToken *publish_token_raw = NULL; - if ((err = _srs_stream_publish_tokens->acquire_token(req_, publish_token_raw)) != srs_success) { - return srs_error_wrap(err, "acquire stream publish token"); - } - SrsUniquePtr publish_token(publish_token_raw); - if (publish_token.get()) { - srs_trace("stream publish token acquired, type=rtc, url=%s", req_->get_stream_url().c_str()); - } - // Setup the publish stream in source to enable PLI as such. if ((err = _srs_rtc_sources->fetch_or_create(req_, source_)) != srs_success) { return srs_error_wrap(err, "create source"); @@ -1734,12 +1724,12 @@ void SrsRtcPublishStream::update_send_report_time(uint32_t ssrc, const SrsNtp &n SrsRtcConnectionNackTimer::SrsRtcConnectionNackTimer(SrsRtcConnection *p) : p_(p) { - _srs_hybrid->timer20ms()->subscribe(this); + _srs_server->timer20ms()->subscribe(this); } SrsRtcConnectionNackTimer::~SrsRtcConnectionNackTimer() { - _srs_hybrid->timer20ms()->unsubscribe(this); + _srs_server->timer20ms()->unsubscribe(this); } srs_error_t SrsRtcConnectionNackTimer::on_timer(srs_utime_t interval) @@ -1771,7 +1761,7 @@ srs_error_t SrsRtcConnectionNackTimer::on_timer(srs_utime_t interval) return err; } -SrsRtcConnection::SrsRtcConnection(SrsRtcServer *s, const SrsContextId &cid) +SrsRtcConnection::SrsRtcConnection(SrsServer *s, const SrsContextId &cid) { req_ = NULL; cid_ = cid; @@ -1795,12 +1785,12 @@ SrsRtcConnection::SrsRtcConnection(SrsRtcServer *s, const SrsContextId &cid) nack_enabled_ = false; timer_nack_ = new SrsRtcConnectionNackTimer(this); - _srs_rtc_manager->subscribe(this); + _srs_conn_manager->subscribe(this); } SrsRtcConnection::~SrsRtcConnection() { - _srs_rtc_manager->unsubscribe(this); + _srs_conn_manager->unsubscribe(this); srs_freep(timer_nack_); @@ -1912,7 +1902,7 @@ std::string SrsRtcConnection::desc() void SrsRtcConnection::expire() { // TODO: FIXME: Should set session to expired and remove it by heartbeat checking. Should not remove it directly. - _srs_rtc_manager->remove(this); + _srs_conn_manager->remove(this); } void SrsRtcConnection::switch_to_context() @@ -2255,7 +2245,7 @@ srs_error_t SrsRtcConnection::on_dtls_alert(std::string type, std::string desc) switch_to_context(); srs_trace("RTC: session destroy by DTLS alert(%s %s), username=%s", type.c_str(), desc.c_str(), username_.c_str()); - _srs_rtc_manager->remove(this); + _srs_conn_manager->remove(this); } return err; diff --git a/trunk/src/app/srs_app_rtc_conn.hpp b/trunk/src/app/srs_app_rtc_conn.hpp index ef40c9b9a..62a017d00 100644 --- a/trunk/src/app/srs_app_rtc_conn.hpp +++ b/trunk/src/app/srs_app_rtc_conn.hpp @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -34,7 +33,7 @@ class SrsUdpMuxSocket; class SrsLiveConsumer; class SrsStunPacket; -class SrsRtcServer; +class SrsServer; class SrsRtcConnection; class SrsSharedPtrMessage; class SrsRtcSource; @@ -270,6 +269,7 @@ public: // Interface ISrsRtcSourceChangeCallback public: void on_stream_change(SrsRtcSourceDescription *desc); + public: virtual const SrsContextId &context_id(); @@ -479,7 +479,7 @@ public: bool disposing_; private: - SrsRtcServer *server_; + SrsServer *server_; private: iovec *cache_iov_; @@ -529,7 +529,7 @@ private: bool nack_enabled_; public: - SrsRtcConnection(SrsRtcServer *s, const SrsContextId &cid); + SrsRtcConnection(SrsServer *s, const SrsContextId &cid); virtual ~SrsRtcConnection(); // interface ISrsDisposingHandler public: diff --git a/trunk/src/app/srs_app_rtc_network.cpp b/trunk/src/app/srs_app_rtc_network.cpp index 0330148ad..391ddbcc1 100644 --- a/trunk/src/app/srs_app_rtc_network.cpp +++ b/trunk/src/app/srs_app_rtc_network.cpp @@ -363,11 +363,11 @@ void SrsRtcUdpNetwork::update_sendonly_socket(SrsUdpMuxSocket *skt) // If no cache, build cache and setup the relations in connection. if (!addr_cache) { peer_addresses_[peer_id] = addr_cache = skt->copy_sendonly(); - _srs_rtc_manager->add_with_id(peer_id, conn_); + _srs_conn_manager->add_with_id(peer_id, conn_); uint64_t fast_id = skt->fast_id(); if (fast_id) { - _srs_rtc_manager->add_with_fast_id(fast_id, conn_); + _srs_conn_manager->add_with_fast_id(fast_id, conn_); } } @@ -888,7 +888,7 @@ srs_error_t SrsRtcTcpConn::handshake() } srs_assert(!session_); - SrsRtcConnection *session = dynamic_cast(_srs_rtc_manager->find_by_name(ping.get_username())); + SrsRtcConnection *session = dynamic_cast(_srs_conn_manager->find_by_name(ping.get_username())); // TODO: FIXME: For ICE trickle, we may get STUN packets before SDP answer, so maybe should response it. if (!session) { return srs_error_new(ERROR_RTC_TCP_STUN, "no session, stun username=%s", ping.get_username().c_str()); diff --git a/trunk/src/app/srs_app_rtc_server.cpp b/trunk/src/app/srs_app_rtc_server.cpp index bd9875872..aaba59f3c 100644 --- a/trunk/src/app/srs_app_rtc_server.cpp +++ b/trunk/src/app/srs_app_rtc_server.cpp @@ -32,9 +32,9 @@ using namespace std; #include extern SrsPps *_srs_pps_rpkts; -SrsPps *_srs_pps_rstuns = NULL; -SrsPps *_srs_pps_rrtps = NULL; -SrsPps *_srs_pps_rrtcps = NULL; +extern SrsPps *_srs_pps_rstuns; +extern SrsPps *_srs_pps_rrtps; +extern SrsPps *_srs_pps_rrtcps; extern SrsPps *_srs_pps_addrs; extern SrsPps *_srs_pps_fast_addrs; @@ -216,7 +216,7 @@ srs_error_t api_server_as_candidates(string api, set &candidate_ips) return err; } -static set discover_candidates(SrsRtcUserConfig *ruc) +set discover_candidates(SrsRtcUserConfig *ruc) { srs_error_t err = srs_success; @@ -306,540 +306,3 @@ SrsRtcUserConfig::~SrsRtcUserConfig() { srs_freep(req_); } - -SrsRtcServer::SrsRtcServer() -{ - async = new SrsAsyncCallWorker(); - - _srs_config->subscribe(this); -} - -SrsRtcServer::~SrsRtcServer() -{ - _srs_config->unsubscribe(this); - - if (true) { - vector::iterator it; - for (it = listeners.begin(); it != listeners.end(); ++it) { - SrsUdpMuxListener *listener = *it; - srs_freep(listener); - } - } - - async->stop(); - srs_freep(async); -} - -srs_error_t SrsRtcServer::initialize() -{ - srs_error_t err = srs_success; - - // The RTC server start a timer, do routines of RTC server. - // @see SrsRtcServer::on_timer() - _srs_hybrid->timer5s()->subscribe(this); - - // Initialize the black hole. - if ((err = _srs_blackhole->initialize()) != srs_success) { - return srs_error_wrap(err, "black hole"); - } - - async->start(); - - return err; -} - -srs_error_t SrsRtcServer::exec_async_work(ISrsAsyncCallTask *t) -{ - return async->execute(t); -} - -srs_error_t SrsRtcServer::listen_udp() -{ - srs_error_t err = srs_success; - - if (!_srs_config->get_rtc_server_enabled()) { - return err; - } - - // Check protocol setting - only create UDP listeners if protocol allows UDP - string protocol = _srs_config->get_rtc_server_protocol(); - if (protocol == "tcp") { - // When protocol is "tcp", don't create UDP listeners - return err; - } - - vector rtc_listens = _srs_config->get_rtc_server_listens(); - if (rtc_listens.empty()) { - return srs_error_new(ERROR_RTC_PORT, "empty rtc listen"); - } - - // There should be no listeners before listening. - srs_assert(listeners.empty()); - - // For each listen address, create multiple listeners based on reuseport setting - int nn_listeners = _srs_config->get_rtc_server_reuseport(); - for (int j = 0; j < (int)rtc_listens.size(); j++) { - string ip; - int port; - srs_net_split_for_listener(rtc_listens[j], ip, port); - - if (port <= 0) { - return srs_error_new(ERROR_RTC_PORT, "invalid port=%d", port); - } - - for (int i = 0; i < nn_listeners; i++) { - SrsUdpMuxListener *listener = new SrsUdpMuxListener(this, ip, port); - - if ((err = listener->listen()) != srs_success) { - srs_freep(listener); - return srs_error_wrap(err, "listen %s:%d", ip.c_str(), port); - } - - srs_trace("WebRTC listen at udp://%s:%d, fd=%d", ip.c_str(), port, listener->fd()); - listeners.push_back(listener); - } - } - - return err; -} - -srs_error_t SrsRtcServer::on_udp_packet(SrsUdpMuxSocket *skt) -{ - srs_error_t err = srs_success; - - SrsRtcConnection *session = NULL; - char *data = skt->data(); - int size = skt->size(); - bool is_rtp_or_rtcp = srs_is_rtp_or_rtcp((uint8_t *)data, size); - bool is_rtcp = srs_is_rtcp((uint8_t *)data, size); - - uint64_t fast_id = skt->fast_id(); - // Try fast id first, if not found, search by long peer id. - if (fast_id) { - session = (SrsRtcConnection *)_srs_rtc_manager->find_by_fast_id(fast_id); - } - if (!session) { - string peer_id = skt->peer_id(); - session = (SrsRtcConnection *)_srs_rtc_manager->find_by_id(peer_id); - } - - if (session) { - // When got any packet, the session is alive now. - session->alive(); - } - - // For STUN, the peer address may change. - if (!is_rtp_or_rtcp && srs_is_stun((uint8_t *)data, size)) { - ++_srs_pps_rstuns->sugar; - string peer_id = skt->peer_id(); - - // TODO: FIXME: Should support ICE renomination, to switch network between candidates. - SrsStunPacket ping; - if ((err = ping.decode(data, size)) != srs_success) { - return srs_error_wrap(err, "decode stun packet failed"); - } - if (!session) { - session = find_session_by_username(ping.get_username()); - } - if (session) { - session->switch_to_context(); - } - - srs_info("recv stun packet from %s, fast=%" PRId64 ", use-candidate=%d, ice-controlled=%d, ice-controlling=%d", - peer_id.c_str(), fast_id, ping.get_use_candidate(), ping.get_ice_controlled(), ping.get_ice_controlling()); - - // TODO: FIXME: For ICE trickle, we may get STUN packets before SDP answer, so maybe should response it. - if (!session) { - return srs_error_new(ERROR_RTC_STUN, "no session, stun username=%s, peer_id=%s, fast=%" PRId64, - ping.get_username().c_str(), peer_id.c_str(), fast_id); - } - - // For each binding request, update the UDP socket. - if (ping.is_binding_request()) { - session->udp()->update_sendonly_socket(skt); - } - - return session->udp()->on_stun(&ping, data, size); - } - - // For DTLS, RTCP or RTP, which does not support peer address changing. - if (!session) { - string peer_id = skt->peer_id(); - return srs_error_new(ERROR_RTC_STUN, "no session, peer_id=%s, fast=%" PRId64, peer_id.c_str(), fast_id); - } - - // Note that we don't(except error) switch to the context of session, for performance issue. - if (is_rtp_or_rtcp && !is_rtcp) { - ++_srs_pps_rrtps->sugar; - - err = session->udp()->on_rtp(data, size); - if (err != srs_success) { - session->switch_to_context(); - } - return err; - } - - session->switch_to_context(); - if (is_rtp_or_rtcp && is_rtcp) { - ++_srs_pps_rrtcps->sugar; - - return session->udp()->on_rtcp(data, size); - } - if (srs_is_dtls((uint8_t *)data, size)) { - ++_srs_pps_rstuns->sugar; - - return session->udp()->on_dtls(data, size); - } - return srs_error_new(ERROR_RTC_UDP, "unknown packet"); -} - -srs_error_t SrsRtcServer::listen_api() -{ - srs_error_t err = srs_success; - - // TODO: FIXME: Fetch api from hybrid manager, not from SRS. - ISrsHttpServeMux *http_api_mux = _srs_hybrid->srs()->instance()->api_server(); - - if ((err = http_api_mux->handle("/rtc/v1/play/", new SrsGoApiRtcPlay(this))) != srs_success) { - return srs_error_wrap(err, "handle play"); - } - - if ((err = http_api_mux->handle("/rtc/v1/publish/", new SrsGoApiRtcPublish(this))) != srs_success) { - return srs_error_wrap(err, "handle publish"); - } - - // Generally, WHIP is a publishing protocol, but it can be also used as playing. - // See https://datatracker.ietf.org/doc/draft-ietf-wish-whep/ - if ((err = http_api_mux->handle("/rtc/v1/whip/", new SrsGoApiRtcWhip(this))) != srs_success) { - return srs_error_wrap(err, "handle whip"); - } - - // We create another mount, to support play with the same query string as publish. - // See https://datatracker.ietf.org/doc/draft-murillo-whep/ - if ((err = http_api_mux->handle("/rtc/v1/whip-play/", new SrsGoApiRtcWhip(this))) != srs_success) { - return srs_error_wrap(err, "handle whep play"); - } - if ((err = http_api_mux->handle("/rtc/v1/whep/", new SrsGoApiRtcWhip(this))) != srs_success) { - return srs_error_wrap(err, "handle whep play"); - } - -#ifdef SRS_SIMULATOR - if ((err = http_api_mux->handle("/rtc/v1/nack/", new SrsGoApiRtcNACK(this))) != srs_success) { - return srs_error_wrap(err, "handle nack"); - } -#endif - - return err; -} - -srs_error_t SrsRtcServer::create_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection **psession) -{ - srs_error_t err = srs_success; - - SrsContextId cid = _srs_context->get_id(); - - ISrsRequest *req = ruc->req_; - - SrsSharedPtr source; - if ((err = _srs_rtc_sources->fetch_or_create(req, source)) != srs_success) { - return srs_error_wrap(err, "create source"); - } - - if (ruc->publish_ && !source->can_publish()) { - return srs_error_new(ERROR_RTC_SOURCE_BUSY, "stream %s busy", req->get_stream_url().c_str()); - } - - // TODO: FIXME: add do_create_session to error process. - SrsRtcConnection *session = new SrsRtcConnection(this, cid); - if ((err = do_create_session(ruc, local_sdp, session)) != srs_success) { - srs_freep(session); - return srs_error_wrap(err, "create session"); - } - - *psession = session; - - return err; -} - -srs_error_t SrsRtcServer::do_create_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection *session) -{ - srs_error_t err = srs_success; - - ISrsRequest *req = ruc->req_; - - // first add publisher/player for negotiate sdp media info - if (ruc->publish_) { - if ((err = session->add_publisher(ruc, local_sdp)) != srs_success) { - return srs_error_wrap(err, "add publisher"); - } - } else { - if ((err = session->add_player(ruc, local_sdp)) != srs_success) { - return srs_error_wrap(err, "add player"); - } - } - - // All tracks default as inactive, so we must enable them. - session->set_all_tracks_status(req->get_stream_url(), ruc->publish_, true); - - std::string local_pwd = ruc->req_->ice_pwd_.empty() ? srs_rand_gen_str(32) : ruc->req_->ice_pwd_; - std::string local_ufrag = ruc->req_->ice_ufrag_.empty() ? srs_rand_gen_str(8) : ruc->req_->ice_ufrag_; - // TODO: FIXME: Rename for a better name, it's not an username. - std::string username = ""; - while (true) { - username = local_ufrag + ":" + ruc->remote_sdp_.get_ice_ufrag(); - if (!_srs_rtc_manager->find_by_name(username)) { - break; - } - - // Username conflict, regenerate a new one. - local_ufrag = srs_rand_gen_str(8); - } - - local_sdp.set_ice_ufrag(local_ufrag); - local_sdp.set_ice_pwd(local_pwd); - local_sdp.set_fingerprint_algo("sha-256"); - local_sdp.set_fingerprint(_srs_rtc_dtls_certificate->get_fingerprint()); - - // We allows to mock the eip of server. - if (true) { - // TODO: Support multiple listen ports. - int udp_port = 0; - if (true) { - string udp_host; - string udp_hostport = _srs_config->get_rtc_server_listens().at(0); - srs_net_split_for_listener(udp_hostport, udp_host, udp_port); - } - - int tcp_port = 0; - if (true) { - string tcp_host; - string tcp_hostport = _srs_config->get_rtc_server_tcp_listens().at(0); - srs_net_split_for_listener(tcp_hostport, tcp_host, tcp_port); - } - - string protocol = _srs_config->get_rtc_server_protocol(); - - set candidates = discover_candidates(ruc); - for (set::iterator it = candidates.begin(); it != candidates.end(); ++it) { - string hostname; - int uport = udp_port; - srs_net_split_hostport(*it, hostname, uport); - int tport = tcp_port; - srs_net_split_hostport(*it, hostname, tport); - - if (protocol == "udp") { - local_sdp.add_candidate("udp", hostname, uport, "host"); - } else if (protocol == "tcp") { - local_sdp.add_candidate("tcp", hostname, tport, "host"); - } else { - local_sdp.add_candidate("udp", hostname, uport, "host"); - local_sdp.add_candidate("tcp", hostname, tport, "host"); - } - } - - vector v = vector(candidates.begin(), candidates.end()); - srs_trace("RTC: Use candidates %s, protocol=%s, tcp_port=%d, udp_port=%d", - srs_strings_join(v, ", ").c_str(), protocol.c_str(), tcp_port, udp_port); - } - - // Setup the negotiate DTLS by config. - local_sdp.session_negotiate_ = local_sdp.session_config_; - - // Setup the negotiate DTLS role. - if (ruc->remote_sdp_.get_dtls_role() == "active") { - local_sdp.session_negotiate_.dtls_role = "passive"; - } else if (ruc->remote_sdp_.get_dtls_role() == "passive") { - local_sdp.session_negotiate_.dtls_role = "active"; - } else if (ruc->remote_sdp_.get_dtls_role() == "actpass") { - local_sdp.session_negotiate_.dtls_role = local_sdp.session_config_.dtls_role; - } else { - // @see: https://tools.ietf.org/html/rfc4145#section-4.1 - // The default value of the setup attribute in an offer/answer exchange - // is 'active' in the offer and 'passive' in the answer. - local_sdp.session_negotiate_.dtls_role = "passive"; - } - local_sdp.set_dtls_role(local_sdp.session_negotiate_.dtls_role); - - session->set_remote_sdp(ruc->remote_sdp_); - // We must setup the local SDP, then initialize the session object. - session->set_local_sdp(local_sdp); - session->set_state_as_waiting_stun(); - - // Before session initialize, we must setup the local SDP. - if ((err = session->initialize(req, ruc->dtls_, ruc->srtp_, username)) != srs_success) { - return srs_error_wrap(err, "init"); - } - - // We allows username is optional, but it never empty here. - _srs_rtc_manager->add_with_name(username, session); - - return err; -} - -SrsRtcConnection *SrsRtcServer::find_session_by_username(const std::string &username) -{ - ISrsResource *conn = _srs_rtc_manager->find_by_name(username); - return dynamic_cast(conn); -} - -srs_error_t SrsRtcServer::on_timer(srs_utime_t interval) -{ - srs_error_t err = srs_success; - - // Alive RTC sessions, for stat. - int nn_rtc_conns = 0; - - // Check all sessions and dispose the dead sessions. - for (int i = 0; i < (int)_srs_rtc_manager->size(); i++) { - SrsRtcConnection *session = dynamic_cast(_srs_rtc_manager->at(i)); - // Ignore not session, or already disposing. - if (!session || session->disposing_) { - continue; - } - - // Update stat if session is alive. - if (session->is_alive()) { - nn_rtc_conns++; - SrsStatistic::instance()->kbps_add_delta(session->get_id().c_str(), session->delta()); - continue; - } - - SrsContextRestore(_srs_context->get_id()); - session->switch_to_context(); - - string username = session->username(); - srs_trace("RTC: session destroy by timeout, username=%s", username.c_str()); - - // Use manager to free session and notify other objects. - _srs_rtc_manager->remove(session); - } - - // Ignore stats if no RTC connections. - if (!nn_rtc_conns) { - return err; - } - static char buf[128]; - - string rpkts_desc; - _srs_pps_rpkts->update(); - _srs_pps_rrtps->update(); - _srs_pps_rstuns->update(); - _srs_pps_rrtcps->update(); - if (_srs_pps_rpkts->r10s() || _srs_pps_rrtps->r10s() || _srs_pps_rstuns->r10s() || _srs_pps_rrtcps->r10s()) { - snprintf(buf, sizeof(buf), ", rpkts=(%d,rtp:%d,stun:%d,rtcp:%d)", _srs_pps_rpkts->r10s(), _srs_pps_rrtps->r10s(), _srs_pps_rstuns->r10s(), _srs_pps_rrtcps->r10s()); - rpkts_desc = buf; - } - - string spkts_desc; - _srs_pps_spkts->update(); - _srs_pps_srtps->update(); - _srs_pps_sstuns->update(); - _srs_pps_srtcps->update(); - if (_srs_pps_spkts->r10s() || _srs_pps_srtps->r10s() || _srs_pps_sstuns->r10s() || _srs_pps_srtcps->r10s()) { - snprintf(buf, sizeof(buf), ", spkts=(%d,rtp:%d,stun:%d,rtcp:%d)", _srs_pps_spkts->r10s(), _srs_pps_srtps->r10s(), _srs_pps_sstuns->r10s(), _srs_pps_srtcps->r10s()); - spkts_desc = buf; - } - - string rtcp_desc; - _srs_pps_pli->update(); - _srs_pps_twcc->update(); - _srs_pps_rr->update(); - if (_srs_pps_pli->r10s() || _srs_pps_twcc->r10s() || _srs_pps_rr->r10s()) { - snprintf(buf, sizeof(buf), ", rtcp=(pli:%d,twcc:%d,rr:%d)", _srs_pps_pli->r10s(), _srs_pps_twcc->r10s(), _srs_pps_rr->r10s()); - rtcp_desc = buf; - } - - string snk_desc; - _srs_pps_snack->update(); - _srs_pps_snack2->update(); - _srs_pps_sanack->update(); - _srs_pps_svnack->update(); - if (_srs_pps_snack->r10s() || _srs_pps_sanack->r10s() || _srs_pps_svnack->r10s() || _srs_pps_snack2->r10s()) { - snprintf(buf, sizeof(buf), ", snk=(%d,a:%d,v:%d,h:%d)", _srs_pps_snack->r10s(), _srs_pps_sanack->r10s(), _srs_pps_svnack->r10s(), _srs_pps_snack2->r10s()); - snk_desc = buf; - } - - string rnk_desc; - _srs_pps_rnack->update(); - _srs_pps_rnack2->update(); - _srs_pps_rhnack->update(); - _srs_pps_rmnack->update(); - if (_srs_pps_rnack->r10s() || _srs_pps_rnack2->r10s() || _srs_pps_rhnack->r10s() || _srs_pps_rmnack->r10s()) { - snprintf(buf, sizeof(buf), ", rnk=(%d,%d,h:%d,m:%d)", _srs_pps_rnack->r10s(), _srs_pps_rnack2->r10s(), _srs_pps_rhnack->r10s(), _srs_pps_rmnack->r10s()); - rnk_desc = buf; - } - - string loss_desc; - SrsSnmpUdpStat *s = srs_get_udp_snmp_stat(); - if (s->rcv_buf_errors_delta || s->snd_buf_errors_delta) { - snprintf(buf, sizeof(buf), ", loss=(r:%d,s:%d)", s->rcv_buf_errors_delta, s->snd_buf_errors_delta); - loss_desc = buf; - } - - string fid_desc; - _srs_pps_ids->update(); - _srs_pps_fids->update(); - _srs_pps_fids_level0->update(); - _srs_pps_addrs->update(); - _srs_pps_fast_addrs->update(); - if (_srs_pps_ids->r10s(), _srs_pps_fids->r10s(), _srs_pps_fids_level0->r10s(), _srs_pps_addrs->r10s(), _srs_pps_fast_addrs->r10s()) { - snprintf(buf, sizeof(buf), ", fid=(id:%d,fid:%d,ffid:%d,addr:%d,faddr:%d)", _srs_pps_ids->r10s(), _srs_pps_fids->r10s(), _srs_pps_fids_level0->r10s(), _srs_pps_addrs->r10s(), _srs_pps_fast_addrs->r10s()); - fid_desc = buf; - } - - srs_trace("RTC: Server conns=%u%s%s%s%s%s%s%s", - nn_rtc_conns, - rpkts_desc.c_str(), spkts_desc.c_str(), rtcp_desc.c_str(), snk_desc.c_str(), rnk_desc.c_str(), loss_desc.c_str(), fid_desc.c_str()); - - return err; -} - -SrsRtcServerAdapter::SrsRtcServerAdapter() -{ - rtc = new SrsRtcServer(); -} - -SrsRtcServerAdapter::~SrsRtcServerAdapter() -{ - srs_freep(rtc); -} - -srs_error_t SrsRtcServerAdapter::initialize() -{ - srs_error_t err = srs_success; - - if ((err = _srs_rtc_dtls_certificate->initialize()) != srs_success) { - return srs_error_wrap(err, "rtc dtls certificate initialize"); - } - - if ((err = rtc->initialize()) != srs_success) { - return srs_error_wrap(err, "rtc server initialize"); - } - - return err; -} - -srs_error_t SrsRtcServerAdapter::run(SrsWaitGroup *wg) -{ - srs_error_t err = srs_success; - - if ((err = rtc->listen_udp()) != srs_success) { - return srs_error_wrap(err, "listen udp"); - } - - if ((err = rtc->listen_api()) != srs_success) { - return srs_error_wrap(err, "listen api"); - } - - if ((err = _srs_rtc_manager->start()) != srs_success) { - return srs_error_wrap(err, "start manager"); - } - - return err; -} - -void SrsRtcServerAdapter::stop() -{ -} - -SrsResourceManager *_srs_rtc_manager = NULL; diff --git a/trunk/src/app/srs_app_rtc_server.hpp b/trunk/src/app/srs_app_rtc_server.hpp index 4a87d00c6..887616932 100644 --- a/trunk/src/app/srs_app_rtc_server.hpp +++ b/trunk/src/app/srs_app_rtc_server.hpp @@ -11,12 +11,12 @@ #include #include -#include #include #include #include #include +#include #include class SrsRtcServer; @@ -26,7 +26,6 @@ class ISrsRequest; class SrsSdp; class SrsRtcSource; class SrsResourceManager; -class SrsWaitGroup; // The UDP black hole, for developer to use wireshark to catch plaintext packets. // For example, server receive UDP packets at udp://8000, and forward the plaintext packet to black hole, @@ -82,62 +81,11 @@ public: virtual ~SrsRtcUserConfig(); }; -// The RTC server instance, listen UDP port, handle UDP packet, manage RTC connections. -class SrsRtcServer : public ISrsUdpMuxHandler, public ISrsFastTimer, public ISrsReloadHandler -{ -private: - std::vector listeners; - SrsAsyncCallWorker *async; - -public: - SrsRtcServer(); - virtual ~SrsRtcServer(); - -public: - virtual srs_error_t initialize(); - -public: - srs_error_t exec_async_work(ISrsAsyncCallTask *t); - -public: - // TODO: FIXME: Support gracefully quit. - // TODO: FIXME: Support reload. - srs_error_t listen_udp(); - virtual srs_error_t on_udp_packet(SrsUdpMuxSocket *skt); - srs_error_t listen_api(); - -public: - // Peer start offering, we answer it. - srs_error_t create_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection **psession); - -private: - srs_error_t do_create_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection *session); - -public: - SrsRtcConnection *find_session_by_username(const std::string &ufrag); - // interface ISrsFastTimer -private: - srs_error_t on_timer(srs_utime_t interval); -}; - -// The RTC server adapter. -class SrsRtcServerAdapter : public ISrsHybridServer -{ -private: - SrsRtcServer *rtc; - -public: - SrsRtcServerAdapter(); - virtual ~SrsRtcServerAdapter(); - -public: - virtual srs_error_t initialize(); - virtual srs_error_t run(SrsWaitGroup *wg); - virtual void stop(); -}; +// Discover the candidates for RTC server. +extern std::set discover_candidates(SrsRtcUserConfig *ruc); // Manager for RTC connections. -extern SrsResourceManager *_srs_rtc_manager; +extern SrsResourceManager *_srs_conn_manager; // The dns resolve utility, return the resolved ip address. extern std::string srs_dns_resolve(std::string host, int &family); diff --git a/trunk/src/app/srs_app_rtc_source.cpp b/trunk/src/app/srs_app_rtc_source.cpp index 7e55c84a9..ccfa9b316 100644 --- a/trunk/src/app/srs_app_rtc_source.cpp +++ b/trunk/src/app/srs_app_rtc_source.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -53,6 +54,7 @@ SrsPps *_srs_pps_rhnack = NULL; SrsPps *_srs_pps_rmnack = NULL; extern SrsPps *_srs_pps_aloss2; +extern SrsServer *_srs_server; static const int kAudioChannel = 2; static const int kAudioSamplerate = 48000; @@ -689,7 +691,7 @@ srs_error_t SrsRtcSource::on_publish() pli_for_rtmp_ = _srs_config->get_rtc_pli_for_rtmp(req->vhost); // @see SrsRtcSource::on_timer() - _srs_hybrid->timer100ms()->subscribe(this); + _srs_server->timer100ms()->subscribe(this); } SrsStatistic *stat = SrsStatistic::instance(); @@ -723,7 +725,7 @@ void SrsRtcSource::on_unpublish() // free bridge resource if (bridge_) { // For SrsRtcSource::on_timer() - _srs_hybrid->timer100ms()->unsubscribe(this); + _srs_server->timer100ms()->unsubscribe(this); #ifdef SRS_FFMPEG_FIT frame_builder_->on_unpublish(); diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index 8561d6a75..0e04c4ee3 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -184,7 +184,7 @@ SrsRtmpConn::SrsRtmpConn(SrsServer *svr, SrsRtmpTransport *transport, string cip server = svr; transport_ = transport; - manager = svr; + manager = _srs_conn_manager; ip = cip; port = cport; create_time = srsu2ms(srs_time_now_cached()); diff --git a/trunk/src/app/srs_app_rtsp_source.cpp b/trunk/src/app/srs_app_rtsp_source.cpp index ad70b167c..b3c3aa832 100644 --- a/trunk/src/app/srs_app_rtsp_source.cpp +++ b/trunk/src/app/srs_app_rtsp_source.cpp @@ -8,7 +8,6 @@ #include #include -#include #include #include #include diff --git a/trunk/src/app/srs_app_server.cpp b/trunk/src/app/srs_app_server.cpp index 89edba798..4ea4be540 100644 --- a/trunk/src/app/srs_app_server.cpp +++ b/trunk/src/app/srs_app_server.cpp @@ -17,17 +17,26 @@ #endif using namespace std; +#include #include +#include #include #include #include #include #include #include +#include #include #include +#include #include +#include +#include +#include +#include #include +#include #include #include #include @@ -40,10 +49,13 @@ using namespace std; #include #include #include +#include #ifdef SRS_GB28181 #include #endif #ifdef SRS_SRT +#include +#include #include #endif #ifdef SRS_RTSP @@ -51,6 +63,379 @@ using namespace std; #include #endif +SrsServer *_srs_server = NULL; + +SrsAsyncCallWorker *_srs_dvr_async = NULL; + +SrsPps *_srs_pps_recvfrom = NULL; +SrsPps *_srs_pps_recvfrom_eagain = NULL; +SrsPps *_srs_pps_sendto = NULL; +SrsPps *_srs_pps_sendto_eagain = NULL; + +SrsPps *_srs_pps_read = NULL; +SrsPps *_srs_pps_read_eagain = NULL; +SrsPps *_srs_pps_readv = NULL; +SrsPps *_srs_pps_readv_eagain = NULL; +SrsPps *_srs_pps_writev = NULL; +SrsPps *_srs_pps_writev_eagain = NULL; + +SrsPps *_srs_pps_recvmsg = NULL; +SrsPps *_srs_pps_recvmsg_eagain = NULL; +SrsPps *_srs_pps_sendmsg = NULL; +SrsPps *_srs_pps_sendmsg_eagain = NULL; + +SrsPps *_srs_pps_clock_15ms = NULL; +SrsPps *_srs_pps_clock_20ms = NULL; +SrsPps *_srs_pps_clock_25ms = NULL; +SrsPps *_srs_pps_clock_30ms = NULL; +SrsPps *_srs_pps_clock_35ms = NULL; +SrsPps *_srs_pps_clock_40ms = NULL; +SrsPps *_srs_pps_clock_80ms = NULL; +SrsPps *_srs_pps_clock_160ms = NULL; +SrsPps *_srs_pps_timer_s = NULL; + +// External declarations for WebRTC functions and variables +extern bool srs_is_stun(const uint8_t *data, size_t size); +extern bool srs_is_dtls(const uint8_t *data, size_t len); +extern bool srs_is_rtp_or_rtcp(const uint8_t *data, size_t len); +extern bool srs_is_rtcp(const uint8_t *data, size_t len); + +extern SrsPps *_srs_pps_rpkts; +SrsPps *_srs_pps_rstuns = NULL; +SrsPps *_srs_pps_rrtps = NULL; +SrsPps *_srs_pps_rrtcps = NULL; +extern SrsPps *_srs_pps_addrs; +extern SrsPps *_srs_pps_fast_addrs; + +extern SrsPps *_srs_pps_spkts; +extern SrsPps *_srs_pps_sstuns; +extern SrsPps *_srs_pps_srtcps; +extern SrsPps *_srs_pps_srtps; + +extern SrsPps *_srs_pps_ids; +extern SrsPps *_srs_pps_fids; +extern SrsPps *_srs_pps_fids_level0; +extern SrsPps *_srs_pps_dispose; + +extern SrsPps *_srs_pps_timer; +extern SrsPps *_srs_pps_pub; +extern SrsPps *_srs_pps_conn; + +extern SrsPps *_srs_pps_cids_get; +extern SrsPps *_srs_pps_cids_set; + +extern SrsPps *_srs_pps_snack3; +extern SrsPps *_srs_pps_snack4; +extern SrsPps *_srs_pps_aloss2; + +extern SrsStageManager *_srs_stages; + +extern srs_error_t _srs_reload_err; +extern SrsReloadState _srs_reload_state; +extern std::string _srs_reload_id; + +// Clock and timing statistics +extern SrsPps *_srs_pps_clock_15ms; +extern SrsPps *_srs_pps_clock_20ms; +extern SrsPps *_srs_pps_clock_25ms; +extern SrsPps *_srs_pps_clock_30ms; +extern SrsPps *_srs_pps_clock_35ms; +extern SrsPps *_srs_pps_clock_40ms; +extern SrsPps *_srs_pps_clock_80ms; +extern SrsPps *_srs_pps_clock_160ms; +extern SrsPps *_srs_pps_timer_s; + +// Object statistics +extern SrsPps *_srs_pps_objs_rtps; +extern SrsPps *_srs_pps_objs_rraw; +extern SrsPps *_srs_pps_objs_rfua; +extern SrsPps *_srs_pps_objs_rbuf; +extern SrsPps *_srs_pps_objs_msgs; +extern SrsPps *_srs_pps_objs_rothers; + +SrsPps *_srs_pps_aloss2 = NULL; + +#if defined(SRS_DEBUG) && defined(SRS_DEBUG_STATS) +SrsPps *_srs_pps_thread_run = NULL; +SrsPps *_srs_pps_thread_idle = NULL; +SrsPps *_srs_pps_thread_yield = NULL; +SrsPps *_srs_pps_thread_yield2 = NULL; + +// Debug statistics for I/O operations +extern SrsPps *_srs_pps_recvfrom; +extern SrsPps *_srs_pps_recvfrom_eagain; +extern SrsPps *_srs_pps_sendto; +extern SrsPps *_srs_pps_sendto_eagain; + +extern SrsPps *_srs_pps_read; +extern SrsPps *_srs_pps_read_eagain; +extern SrsPps *_srs_pps_readv; +extern SrsPps *_srs_pps_readv_eagain; +extern SrsPps *_srs_pps_writev; +extern SrsPps *_srs_pps_writev_eagain; + +extern SrsPps *_srs_pps_recvmsg; +extern SrsPps *_srs_pps_recvmsg_eagain; +extern SrsPps *_srs_pps_sendmsg; +extern SrsPps *_srs_pps_sendmsg_eagain; + +extern SrsPps *_srs_pps_epoll; +extern SrsPps *_srs_pps_epoll_zero; +extern SrsPps *_srs_pps_epoll_shake; +extern SrsPps *_srs_pps_epoll_spin; + +extern SrsPps *_srs_pps_sched_160ms; +extern SrsPps *_srs_pps_sched_s; +extern SrsPps *_srs_pps_sched_15ms; +extern SrsPps *_srs_pps_sched_20ms; +extern SrsPps *_srs_pps_sched_25ms; +extern SrsPps *_srs_pps_sched_30ms; +extern SrsPps *_srs_pps_sched_35ms; +extern SrsPps *_srs_pps_sched_40ms; +extern SrsPps *_srs_pps_sched_80ms; + +extern SrsPps *_srs_pps_thread_run; +extern SrsPps *_srs_pps_thread_idle; +extern SrsPps *_srs_pps_thread_yield; +extern SrsPps *_srs_pps_thread_yield2; + +// External ST statistics +extern __thread unsigned long long _st_stat_recvfrom; +extern __thread unsigned long long _st_stat_recvfrom_eagain; +extern __thread unsigned long long _st_stat_sendto; +extern __thread unsigned long long _st_stat_sendto_eagain; + +extern __thread unsigned long long _st_stat_read; +extern __thread unsigned long long _st_stat_read_eagain; +extern __thread unsigned long long _st_stat_readv; +extern __thread unsigned long long _st_stat_readv_eagain; +extern __thread unsigned long long _st_stat_writev; +extern __thread unsigned long long _st_stat_writev_eagain; + +extern __thread unsigned long long _st_stat_recvmsg; +extern __thread unsigned long long _st_stat_recvmsg_eagain; +extern __thread unsigned long long _st_stat_sendmsg; +extern __thread unsigned long long _st_stat_sendmsg_eagain; + +extern __thread unsigned long long _st_stat_epoll; +extern __thread unsigned long long _st_stat_epoll_zero; +extern __thread unsigned long long _st_stat_epoll_shake; +extern __thread unsigned long long _st_stat_epoll_spin; + +extern __thread unsigned long long _st_stat_sched_15ms; +extern __thread unsigned long long _st_stat_sched_20ms; +extern __thread unsigned long long _st_stat_sched_25ms; +extern __thread unsigned long long _st_stat_sched_30ms; +extern __thread unsigned long long _st_stat_sched_35ms; +extern __thread unsigned long long _st_stat_sched_40ms; +extern __thread unsigned long long _st_stat_sched_80ms; +extern __thread unsigned long long _st_stat_sched_160ms; +extern __thread unsigned long long _st_stat_sched_s; + +extern __thread int _st_active_count; +extern __thread int _st_num_free_stacks; + +extern __thread unsigned long long _st_stat_thread_run; +extern __thread unsigned long long _st_stat_thread_idle; +extern __thread unsigned long long _st_stat_thread_yield; +extern __thread unsigned long long _st_stat_thread_yield2; +#endif + +extern SrsPps *_srs_pps_pli; +extern SrsPps *_srs_pps_twcc; +extern SrsPps *_srs_pps_rr; + +extern SrsPps *_srs_pps_snack; +extern SrsPps *_srs_pps_snack2; +extern SrsPps *_srs_pps_sanack; +extern SrsPps *_srs_pps_svnack; + +extern SrsPps *_srs_pps_rnack; +extern SrsPps *_srs_pps_rnack2; +extern SrsPps *_srs_pps_rhnack; +extern SrsPps *_srs_pps_rmnack; + +extern SrsPps *_srs_pps_sstuns; +extern SrsPps *_srs_pps_srtcps; +extern SrsPps *_srs_pps_srtps; + +SrsResourceManager *_srs_conn_manager = NULL; + +// External WebRTC global variables +extern SrsRtcBlackhole *_srs_blackhole; +extern SrsDtlsCertificate *_srs_rtc_dtls_certificate; + +srs_error_t srs_global_initialize() +{ + srs_error_t err = srs_success; + + // Root global objects. + _srs_log = new SrsFileLog(); + _srs_context = new SrsThreadContext(); + _srs_config = new SrsConfig(); + + // The clock wall object. + _srs_clock = new SrsWallClock(); + + // The pps cids depends by st init. + _srs_pps_cids_get = new SrsPps(); + _srs_pps_cids_set = new SrsPps(); + + // Initialize ST, which depends on pps cids. + if ((err = srs_st_init()) != srs_success) { + return srs_error_wrap(err, "initialize st failed"); + } + + // The global objects which depends on ST. + // Initialize _srs_stages first as it's needed by SrsServer constructor + _srs_stages = new SrsStageManager(); + _srs_sources = new SrsLiveSourceManager(); + _srs_circuit_breaker = new SrsCircuitBreaker(); + _srs_hooks = new SrsHttpHooks(); + +#ifdef SRS_SRT + _srs_srt_sources = new SrsSrtSourceManager(); +#endif + + _srs_rtc_sources = new SrsRtcSourceManager(); + _srs_blackhole = new SrsRtcBlackhole(); + + // Initialize stream publish token manager + _srs_stream_publish_tokens = new SrsStreamPublishTokenManager(); + + _srs_conn_manager = new SrsResourceManager("RTC", true); + _srs_rtc_dtls_certificate = new SrsDtlsCertificate(); +#ifdef SRS_RTSP + _srs_rtsp_sources = new SrsRtspSourceManager(); + _srs_rtsp_manager = new SrsResourceManager("RTSP", true); +#endif +#ifdef SRS_GB28181 + _srs_gb_manager = new SrsResourceManager("GB", true); +#endif + + // Initialize global pps, which depends on _srs_clock + _srs_pps_ids = new SrsPps(); + _srs_pps_fids = new SrsPps(); + _srs_pps_fids_level0 = new SrsPps(); + _srs_pps_dispose = new SrsPps(); + + _srs_pps_timer = new SrsPps(); + _srs_pps_conn = new SrsPps(); + _srs_pps_pub = new SrsPps(); + + _srs_pps_snack = new SrsPps(); + _srs_pps_snack2 = new SrsPps(); + _srs_pps_snack3 = new SrsPps(); + _srs_pps_snack4 = new SrsPps(); + _srs_pps_sanack = new SrsPps(); + _srs_pps_svnack = new SrsPps(); + + _srs_pps_rnack = new SrsPps(); + _srs_pps_rnack2 = new SrsPps(); + _srs_pps_rhnack = new SrsPps(); + _srs_pps_rmnack = new SrsPps(); + +#if defined(SRS_DEBUG) && defined(SRS_DEBUG_STATS) + _srs_pps_recvfrom = new SrsPps(); + _srs_pps_recvfrom_eagain = new SrsPps(); + _srs_pps_sendto = new SrsPps(); + _srs_pps_sendto_eagain = new SrsPps(); + + _srs_pps_read = new SrsPps(); + _srs_pps_read_eagain = new SrsPps(); + _srs_pps_readv = new SrsPps(); + _srs_pps_readv_eagain = new SrsPps(); + _srs_pps_writev = new SrsPps(); + _srs_pps_writev_eagain = new SrsPps(); + + _srs_pps_recvmsg = new SrsPps(); + _srs_pps_recvmsg_eagain = new SrsPps(); + _srs_pps_sendmsg = new SrsPps(); + _srs_pps_sendmsg_eagain = new SrsPps(); + + _srs_pps_epoll = new SrsPps(); + _srs_pps_epoll_zero = new SrsPps(); + _srs_pps_epoll_shake = new SrsPps(); + _srs_pps_epoll_spin = new SrsPps(); + + _srs_pps_sched_15ms = new SrsPps(); + _srs_pps_sched_20ms = new SrsPps(); + _srs_pps_sched_25ms = new SrsPps(); + _srs_pps_sched_30ms = new SrsPps(); + _srs_pps_sched_35ms = new SrsPps(); + _srs_pps_sched_40ms = new SrsPps(); + _srs_pps_sched_80ms = new SrsPps(); + _srs_pps_sched_160ms = new SrsPps(); + _srs_pps_sched_s = new SrsPps(); +#endif + + _srs_pps_clock_15ms = new SrsPps(); + _srs_pps_clock_20ms = new SrsPps(); + _srs_pps_clock_25ms = new SrsPps(); + _srs_pps_clock_30ms = new SrsPps(); + _srs_pps_clock_35ms = new SrsPps(); + _srs_pps_clock_40ms = new SrsPps(); + _srs_pps_clock_80ms = new SrsPps(); + _srs_pps_clock_160ms = new SrsPps(); + _srs_pps_timer_s = new SrsPps(); + +#if defined(SRS_DEBUG) && defined(SRS_DEBUG_STATS) + _srs_pps_thread_run = new SrsPps(); + _srs_pps_thread_idle = new SrsPps(); + _srs_pps_thread_yield = new SrsPps(); + _srs_pps_thread_yield2 = new SrsPps(); +#endif + + _srs_pps_rpkts = new SrsPps(); + _srs_pps_addrs = new SrsPps(); + _srs_pps_fast_addrs = new SrsPps(); + + _srs_pps_spkts = new SrsPps(); + _srs_pps_objs_msgs = new SrsPps(); + + _srs_pps_sstuns = new SrsPps(); + _srs_pps_srtcps = new SrsPps(); + _srs_pps_srtps = new SrsPps(); + + _srs_pps_rstuns = new SrsPps(); + _srs_pps_rrtps = new SrsPps(); + _srs_pps_rrtcps = new SrsPps(); + + _srs_pps_aloss2 = new SrsPps(); + + _srs_pps_pli = new SrsPps(); + _srs_pps_twcc = new SrsPps(); + _srs_pps_rr = new SrsPps(); + + _srs_pps_objs_rtps = new SrsPps(); + _srs_pps_objs_rraw = new SrsPps(); + _srs_pps_objs_rfua = new SrsPps(); + _srs_pps_objs_rbuf = new SrsPps(); + _srs_pps_objs_rothers = new SrsPps(); + + // Create global async worker for DVR. + _srs_dvr_async = new SrsAsyncCallWorker(); + + _srs_reload_err = srs_success; + _srs_reload_state = SrsReloadStateInit; + _srs_reload_id = srs_rand_gen_str(7); + + return err; +} + +ISrsSrtClientHandler::ISrsSrtClientHandler() +{ +} + +ISrsSrtClientHandler::~ISrsSrtClientHandler() +{ +} + +srs_error_t ISrsSrtClientHandler::accept_srt_client(srs_srt_t srt_fd) +{ + return srs_success; +} + SrsSignalManager *SrsSignalManager::instance = NULL; SrsSignalManager::SrsSignalManager(SrsServer *s) @@ -326,17 +711,18 @@ srs_error_t SrsInotifyWorker::cycle() SrsServer::SrsServer() { - signal_reload = false; - signal_persistence_config = false; - signal_gmc_stop = false; - signal_fast_quit = false; - signal_gracefully_quit = false; - pid_fd = -1; + signal_reload_ = false; + signal_persistence_config_ = false; + signal_gmc_stop_ = false; + signal_fast_quit_ = false; + signal_gracefully_quit_ = false; + pid_fd_ = -1; - signal_manager = new SrsSignalManager(this); - conn_manager = new SrsResourceManager("TCP", true); + signal_manager_ = new SrsSignalManager(this); latest_version_ = new SrsLatestVersion(); - ppid = ::getppid(); + ppid_ = ::getppid(); + + http_api_mux_ = new SrsHttpServeMux(); rtmp_listener_ = new SrsMultipleTcpListeners(this); rtmps_listener_ = new SrsMultipleTcpListeners(this); @@ -352,22 +738,26 @@ SrsServer::SrsServer() stream_caster_mpegts_ = new SrsUdpCasterListener(); exporter_listener_ = new SrsTcpListener(this); #ifdef SRS_GB28181 - stream_caster_gb28181_ = new SrsGbListener(); + stream_caster_gb28181_ = new SrsGbListener(http_api_mux_); #endif - // donot new object in constructor, - // for some global instance is not ready now, - // new these objects in initialize instead. - http_api_mux = new SrsHttpServeMux(); - http_server = new SrsHttpServer(this); + http_server_ = new SrsHttpServer(this); reuse_api_over_server_ = false; reuse_rtc_over_server_ = false; - http_heartbeat = new SrsHttpHeartbeat(); - ingester = new SrsIngester(); - trd_ = new SrsSTCoroutine("srs", this, _srs_context->get_id()); + http_heartbeat_ = new SrsHttpHeartbeat(); + ingester_ = new SrsIngester(); timer_ = NULL; - wg_ = NULL; + + // Initialize global shared timers moved from SrsHybridServer + timer20ms_ = new SrsFastTimer("server", 20 * SRS_UTIME_MILLISECONDS); + timer100ms_ = new SrsFastTimer("server", 100 * SRS_UTIME_MILLISECONDS); + timer1s_ = new SrsFastTimer("server", 1 * SRS_UTIME_SECONDS); + timer5s_ = new SrsFastTimer("server", 5 * SRS_UTIME_SECONDS); + clock_monitor_ = new SrsClockWallMonitor(); + + // Initialize WebRTC components + rtc_async_ = new SrsAsyncCallWorker(); } SrsServer::~SrsServer() @@ -377,28 +767,33 @@ SrsServer::~SrsServer() void SrsServer::destroy() { - srs_freep(trd_); srs_freep(timer_); + // Free global shared timers + srs_freep(timer20ms_); + srs_freep(timer100ms_); + srs_freep(timer1s_); + srs_freep(timer5s_); + srs_freep(clock_monitor_); + dispose(); // If api reuse the same port of server, they're the same object. if (!reuse_api_over_server_) { - srs_freep(http_api_mux); + srs_freep(http_api_mux_); } - srs_freep(http_server); + srs_freep(http_server_); - srs_freep(http_heartbeat); - srs_freep(ingester); + srs_freep(http_heartbeat_); + srs_freep(ingester_); - if (pid_fd > 0) { - ::close(pid_fd); - pid_fd = -1; + if (pid_fd_ > 0) { + ::close(pid_fd_); + pid_fd_ = -1; } - srs_freep(signal_manager); + srs_freep(signal_manager_); srs_freep(latest_version_); - srs_freep(conn_manager); srs_freep(rtmp_listener_); srs_freep(rtmps_listener_); srs_freep(api_listener_); @@ -415,6 +810,24 @@ void SrsServer::destroy() #ifdef SRS_GB28181 srs_freep(stream_caster_gb28181_); #endif +#ifdef SRS_SRT + close_srt_listeners(); +#endif + + // Cleanup WebRTC components + if (true) { + std::vector::iterator it; + for (it = rtc_listeners_.begin(); it != rtc_listeners_.end(); ++it) { + SrsUdpMuxListener *listener = *it; + srs_freep(listener); + } + rtc_listeners_.clear(); + } + + if (rtc_async_) { + rtc_async_->stop(); + srs_freep(rtc_async_); + } } void SrsServer::dispose() @@ -438,9 +851,12 @@ void SrsServer::dispose() #ifdef SRS_GB28181 stream_caster_gb28181_->close(); #endif +#ifdef SRS_SRT + close_srt_listeners(); +#endif // Fast stop to notify FFMPEG to quit, wait for a while then fast kill. - ingester->dispose(); + ingester_->dispose(); // dispose the source for hls and dvr. _srs_sources->dispose(); @@ -472,23 +888,26 @@ void SrsServer::gracefully_dispose() exporter_listener_->close(); #ifdef SRS_GB28181 stream_caster_gb28181_->close(); +#endif +#ifdef SRS_SRT + close_srt_listeners(); #endif srs_trace("listeners closed"); // Fast stop to notify FFMPEG to quit, wait for a while then fast kill. - ingester->stop(); + ingester_->stop(); srs_trace("ingesters stopped"); // Wait for connections to quit. // While gracefully quiting, user can requires SRS to fast quit. int wait_step = 1; - while (!conn_manager->empty() && !signal_fast_quit) { - for (int i = 0; i < wait_step && !conn_manager->empty() && !signal_fast_quit; i++) { + while (!_srs_conn_manager->empty() && !signal_fast_quit_) { + for (int i = 0; i < wait_step && !_srs_conn_manager->empty() && !signal_fast_quit_; i++) { srs_usleep(1000 * SRS_UTIME_MILLISECONDS); } wait_step = (wait_step * 2) % 33; - srs_trace("wait for %d conns to quit", (int)conn_manager->size()); + srs_trace("wait for %d conns to quit", (int)_srs_conn_manager->size()); } // dispose the source for hls and dvr. @@ -503,6 +922,39 @@ srs_error_t SrsServer::initialize() { srs_error_t err = srs_success; + srs_trace("SRS server initialized in single thread mode"); + + // Initialize the server. + if ((err = acquire_pid_file()) != srs_success) { + return srs_error_wrap(err, "init server"); + } + +#ifdef SRS_SRT + if ((err = srs_srt_log_initialize()) != srs_success) { + return srs_error_wrap(err, "srt log initialize"); + } + + _srt_eventloop = new SrsSrtEventLoop(); + + if ((err = _srt_eventloop->initialize()) != srs_success) { + return srs_error_wrap(err, "srt poller initialize"); + } + + if ((err = _srt_eventloop->start()) != srs_success) { + return srs_error_wrap(err, "srt poller start"); + } +#endif + + // Initialize WebRTC DTLS certificate + if ((err = _srs_rtc_dtls_certificate->initialize()) != srs_success) { + return srs_error_wrap(err, "rtc dtls certificate initialize"); + } + + // Start the DVR async call. + if ((err = _srs_dvr_async->start()) != srs_success) { + return srs_error_wrap(err, "dvr async"); + } + // for the main objects(server, config, log, context), // never subscribe handler in constructor, // instead, subscribe handler in initialize method. @@ -537,36 +989,190 @@ srs_error_t SrsServer::initialize() // Only init HTTP API when not reusing HTTP server. if (!reuse_api_over_server_) { - SrsHttpServeMux *api = dynamic_cast(http_api_mux); + SrsHttpServeMux *api = dynamic_cast(http_api_mux_); srs_assert(api); if ((err = api->initialize()) != srs_success) { return srs_error_wrap(err, "http api initialize"); } } else { - srs_freep(http_api_mux); - http_api_mux = http_server; + srs_freep(http_api_mux_); + http_api_mux_ = http_server_; } - if ((err = http_server->initialize()) != srs_success) { + if ((err = http_server_->initialize()) != srs_success) { return srs_error_wrap(err, "http server initialize"); } + // Initialize the black hole. + if ((err = _srs_blackhole->initialize()) != srs_success) { + return srs_error_wrap(err, "black hole"); + } + + // Start WebRTC async worker + rtc_async_->start(); + + // Start global shared timers + if ((err = timer20ms_->start()) != srs_success) { + return srs_error_wrap(err, "start timer20ms"); + } + + if ((err = timer100ms_->start()) != srs_success) { + return srs_error_wrap(err, "start timer100ms"); + } + + if ((err = timer1s_->start()) != srs_success) { + return srs_error_wrap(err, "start timer1s"); + } + + if ((err = timer5s_->start()) != srs_success) { + return srs_error_wrap(err, "start timer5s"); + } + + // Register clock monitor to 20ms timer and statistics reporting to 5s timer + timer20ms_->subscribe(clock_monitor_); + timer5s_->subscribe(this); + return err; } +srs_error_t SrsServer::acquire_pid_file() +{ + std::string pid_file = _srs_config->get_pid_file(); + + // -rw-r--r-- + // 644 + int mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + + int fd; + // open pid file + if ((fd = ::open(pid_file.c_str(), O_WRONLY | O_CREAT, mode)) == -1) { + return srs_error_new(ERROR_SYSTEM_PID_ACQUIRE, "open pid file=%s", pid_file.c_str()); + } + + // require write lock + struct flock lock; + + lock.l_type = F_WRLCK; // F_RDLCK, F_WRLCK, F_UNLCK + lock.l_start = 0; // type offset, relative to l_whence + lock.l_whence = SEEK_SET; // SEEK_SET, SEEK_CUR, SEEK_END + lock.l_len = 0; + + if (fcntl(fd, F_SETLK, &lock) == -1) { + if (errno == EACCES || errno == EAGAIN) { + ::close(fd); + srs_error("srs is already running!"); + return srs_error_new(ERROR_SYSTEM_PID_ALREADY_RUNNING, "srs is already running"); + } + return srs_error_new(ERROR_SYSTEM_PID_LOCK, "access to pid=%s", pid_file.c_str()); + } + + // truncate file + if (ftruncate(fd, 0) != 0) { + return srs_error_new(ERROR_SYSTEM_PID_TRUNCATE_FILE, "truncate pid file=%s", pid_file.c_str()); + } + + // write the pid + string pid = srs_strconv_format_int(getpid()); + if (write(fd, pid.c_str(), pid.length()) != (int)pid.length()) { + return srs_error_new(ERROR_SYSTEM_PID_WRITE_FILE, "write pid=%s to file=%s", pid.c_str(), pid_file.c_str()); + } + + // auto close when fork child process. + int val; + if ((val = fcntl(fd, F_GETFD, 0)) < 0) { + return srs_error_new(ERROR_SYSTEM_PID_GET_FILE_INFO, "fcntl fd=%d", fd); + } + val |= FD_CLOEXEC; + if (fcntl(fd, F_SETFD, val) < 0) { + return srs_error_new(ERROR_SYSTEM_PID_SET_FILE_INFO, "lock file=%s fd=%d", pid_file.c_str(), fd); + } + + srs_trace("write pid=%s to %s success!", pid.c_str(), pid_file.c_str()); + pid_fd_ = fd; + + return srs_success; +} + +srs_error_t SrsServer::run() +{ + srs_error_t err = srs_success; + + // Circuit breaker to protect server, which depends on server. + if ((err = _srs_circuit_breaker->initialize()) != srs_success) { + return srs_error_wrap(err, "init circuit breaker"); + } + + // Initialize the whole system, set hooks to handle server level events. + if ((err = initialize_st()) != srs_success) { + return srs_error_wrap(err, "initialize st"); + } + + if ((err = initialize_signal()) != srs_success) { + return srs_error_wrap(err, "initialize signal"); + } + + if ((err = listen()) != srs_success) { + return srs_error_wrap(err, "listen"); + } + + if ((err = register_signal()) != srs_success) { + return srs_error_wrap(err, "register signal"); + } + + if ((err = http_handle()) != srs_success) { + return srs_error_wrap(err, "http handle"); + } + + if ((err = ingest()) != srs_success) { + return srs_error_wrap(err, "ingest"); + } + + if ((err = _srs_sources->initialize()) != srs_success) { + return srs_error_wrap(err, "live sources"); + } + +#ifdef SRS_SRT + if ((err = _srs_srt_sources->initialize()) != srs_success) { + return srs_error_wrap(err, "srt sources"); + } +#endif + + if ((err = _srs_rtc_sources->initialize()) != srs_success) { + return srs_error_wrap(err, "rtc sources"); + } + +#ifdef SRS_RTSP + if ((err = _srs_rtsp_sources->initialize()) != srs_success) { + return srs_error_wrap(err, "rtsp sources"); + } +#endif + + if ((err = setup_ticks()) != srs_success) { + return srs_error_wrap(err, "tick"); + } + +#ifdef SRS_GB28181 + if ((err = _srs_gb_manager->start()) != srs_success) { + return srs_error_wrap(err, "start manager"); + } +#endif + + return cycle(); +} + srs_error_t SrsServer::initialize_st() { srs_error_t err = srs_success; // check asprocess. bool asprocess = _srs_config->get_asprocess(); - if (asprocess && ppid == 1) { - return srs_error_new(ERROR_SYSTEM_ASSERT_FAILED, "ppid=%d illegal for asprocess", ppid); + if (asprocess && ppid_ == 1) { + return srs_error_new(ERROR_SYSTEM_ASSERT_FAILED, "ppid=%d illegal for asprocess", ppid_); } srs_trace("server main cid=%s, pid=%d, ppid=%d, asprocess=%d", - _srs_context->get_id().c_str(), ::getpid(), ppid, asprocess); + _srs_context->get_id().c_str(), ::getpid(), ppid_, asprocess); return err; } @@ -575,7 +1181,7 @@ srs_error_t SrsServer::initialize_signal() { srs_error_t err = srs_success; - if ((err = signal_manager->initialize()) != srs_success) { + if ((err = signal_manager_->initialize()) != srs_success) { return srs_error_wrap(err, "init signal manager"); } @@ -713,7 +1319,19 @@ srs_error_t SrsServer::listen() } } - if ((err = conn_manager->start()) != srs_success) { +#ifdef SRS_SRT + // Listen MPEG-TS over SRT. + if ((err = listen_srt_mpegts()) != srs_success) { + return srs_error_wrap(err, "srt mpegts listen"); + } +#endif + + // Listen WebRTC UDP. + if ((err = listen_rtc_udp()) != srs_success) { + return srs_error_wrap(err, "rtc udp listen"); + } + + if ((err = _srs_conn_manager->start()) != srs_success) { return srs_error_wrap(err, "connection manager"); } @@ -724,7 +1342,7 @@ srs_error_t SrsServer::register_signal() { srs_error_t err = srs_success; - if ((err = signal_manager->start()) != srs_success) { + if ((err = signal_manager_->start()) != srs_success) { return srs_error_wrap(err, "signal manager start"); } @@ -737,109 +1355,114 @@ srs_error_t SrsServer::http_handle() // Ignore / and /api/v1/versions for already handled by HTTP server. if (!reuse_api_over_server_) { - if ((err = http_api_mux->handle("/", new SrsGoApiRoot())) != srs_success) { + if ((err = http_api_mux_->handle("/", new SrsGoApiRoot())) != srs_success) { return srs_error_wrap(err, "handle /"); } - if ((err = http_api_mux->handle("/api/v1/versions", new SrsGoApiVersion())) != srs_success) { + if ((err = http_api_mux_->handle("/api/v1/versions", new SrsGoApiVersion())) != srs_success) { return srs_error_wrap(err, "handle versions"); } } - if ((err = http_api_mux->handle("/api/", new SrsGoApiApi())) != srs_success) { + if ((err = http_api_mux_->handle("/api/", new SrsGoApiApi())) != srs_success) { return srs_error_wrap(err, "handle api"); } - if ((err = http_api_mux->handle("/api/v1/", new SrsGoApiV1())) != srs_success) { + if ((err = http_api_mux_->handle("/api/v1/", new SrsGoApiV1())) != srs_success) { return srs_error_wrap(err, "handle v1"); } - if ((err = http_api_mux->handle("/api/v1/summaries", new SrsGoApiSummaries())) != srs_success) { + if ((err = http_api_mux_->handle("/api/v1/summaries", new SrsGoApiSummaries())) != srs_success) { return srs_error_wrap(err, "handle summaries"); } - if ((err = http_api_mux->handle("/api/v1/rusages", new SrsGoApiRusages())) != srs_success) { + if ((err = http_api_mux_->handle("/api/v1/rusages", new SrsGoApiRusages())) != srs_success) { return srs_error_wrap(err, "handle rusages"); } - if ((err = http_api_mux->handle("/api/v1/self_proc_stats", new SrsGoApiSelfProcStats())) != srs_success) { + if ((err = http_api_mux_->handle("/api/v1/self_proc_stats", new SrsGoApiSelfProcStats())) != srs_success) { return srs_error_wrap(err, "handle self proc stats"); } - if ((err = http_api_mux->handle("/api/v1/system_proc_stats", new SrsGoApiSystemProcStats())) != srs_success) { + if ((err = http_api_mux_->handle("/api/v1/system_proc_stats", new SrsGoApiSystemProcStats())) != srs_success) { return srs_error_wrap(err, "handle system proc stats"); } - if ((err = http_api_mux->handle("/api/v1/meminfos", new SrsGoApiMemInfos())) != srs_success) { + if ((err = http_api_mux_->handle("/api/v1/meminfos", new SrsGoApiMemInfos())) != srs_success) { return srs_error_wrap(err, "handle meminfos"); } - if ((err = http_api_mux->handle("/api/v1/authors", new SrsGoApiAuthors())) != srs_success) { + if ((err = http_api_mux_->handle("/api/v1/authors", new SrsGoApiAuthors())) != srs_success) { return srs_error_wrap(err, "handle authors"); } - if ((err = http_api_mux->handle("/api/v1/features", new SrsGoApiFeatures())) != srs_success) { + if ((err = http_api_mux_->handle("/api/v1/features", new SrsGoApiFeatures())) != srs_success) { return srs_error_wrap(err, "handle features"); } - if ((err = http_api_mux->handle("/api/v1/vhosts/", new SrsGoApiVhosts())) != srs_success) { + if ((err = http_api_mux_->handle("/api/v1/vhosts/", new SrsGoApiVhosts())) != srs_success) { return srs_error_wrap(err, "handle vhosts"); } - if ((err = http_api_mux->handle("/api/v1/streams/", new SrsGoApiStreams())) != srs_success) { + if ((err = http_api_mux_->handle("/api/v1/streams/", new SrsGoApiStreams())) != srs_success) { return srs_error_wrap(err, "handle streams"); } - if ((err = http_api_mux->handle("/api/v1/clients/", new SrsGoApiClients())) != srs_success) { + if ((err = http_api_mux_->handle("/api/v1/clients/", new SrsGoApiClients())) != srs_success) { return srs_error_wrap(err, "handle clients"); } - if ((err = http_api_mux->handle("/api/v1/raw", new SrsGoApiRaw(this))) != srs_success) { + if ((err = http_api_mux_->handle("/api/v1/raw", new SrsGoApiRaw(this))) != srs_success) { return srs_error_wrap(err, "handle raw"); } - if ((err = http_api_mux->handle("/api/v1/clusters", new SrsGoApiClusters())) != srs_success) { + if ((err = http_api_mux_->handle("/api/v1/clusters", new SrsGoApiClusters())) != srs_success) { return srs_error_wrap(err, "handle clusters"); } // test the request info. - if ((err = http_api_mux->handle("/api/v1/tests/requests", new SrsGoApiRequests())) != srs_success) { + if ((err = http_api_mux_->handle("/api/v1/tests/requests", new SrsGoApiRequests())) != srs_success) { return srs_error_wrap(err, "handle tests requests"); } // test the error code response. - if ((err = http_api_mux->handle("/api/v1/tests/errors", new SrsGoApiError())) != srs_success) { + if ((err = http_api_mux_->handle("/api/v1/tests/errors", new SrsGoApiError())) != srs_success) { return srs_error_wrap(err, "handle tests errors"); } // test the redirect mechenism. - if ((err = http_api_mux->handle("/api/v1/tests/redirects", new SrsHttpRedirectHandler("/api/v1/tests/errors", SRS_CONSTS_HTTP_MovedPermanently))) != srs_success) { + if ((err = http_api_mux_->handle("/api/v1/tests/redirects", new SrsHttpRedirectHandler("/api/v1/tests/errors", SRS_CONSTS_HTTP_MovedPermanently))) != srs_success) { return srs_error_wrap(err, "handle tests redirects"); } // test the http vhost. - if ((err = http_api_mux->handle("error.srs.com/api/v1/tests/errors", new SrsGoApiError())) != srs_success) { + if ((err = http_api_mux_->handle("error.srs.com/api/v1/tests/errors", new SrsGoApiError())) != srs_success) { return srs_error_wrap(err, "handle tests errors for error.srs.com"); } #ifdef SRS_GPERF // The test api for get tcmalloc stats. // @see Memory Introspection in https://gperftools.github.io/gperftools/tcmalloc.html - if ((err = http_api_mux->handle("/api/v1/tcmalloc", new SrsGoApiTcmalloc())) != srs_success) { + if ((err = http_api_mux_->handle("/api/v1/tcmalloc", new SrsGoApiTcmalloc())) != srs_success) { return srs_error_wrap(err, "handle tcmalloc errors"); } #endif #ifdef SRS_VALGRIND // The test api for valgrind. See VALGRIND_DO_LEAK_CHECK in https://valgrind.org/docs/manual/mc-manual.html - if ((err = http_api_mux->handle("/api/v1/valgrind", new SrsGoApiValgrind())) != srs_success) { + if ((err = http_api_mux_->handle("/api/v1/valgrind", new SrsGoApiValgrind())) != srs_success) { return srs_error_wrap(err, "handle valgrind errors"); } #endif #ifdef SRS_SIGNAL_API // Simulate the signal by HTTP API, for debug signal issues in CLion. - if ((err = http_api_mux->handle("/api/v1/signal", new SrsGoApiSignal())) != srs_success) { + if ((err = http_api_mux_->handle("/api/v1/signal", new SrsGoApiSignal())) != srs_success) { return srs_error_wrap(err, "handle signal errors"); } #endif // metrics by prometheus - if ((err = http_api_mux->handle("/metrics", new SrsGoApiMetrics())) != srs_success) { + if ((err = http_api_mux_->handle("/metrics", new SrsGoApiMetrics())) != srs_success) { return srs_error_wrap(err, "handle tests errors"); } // TODO: FIXME: for console. // TODO: FIXME: support reload. std::string dir = _srs_config->get_http_stream_dir() + "/console"; - if ((err = http_api_mux->handle("/console/", new SrsHttpFileServer(dir))) != srs_success) { + if ((err = http_api_mux_->handle("/console/", new SrsHttpFileServer(dir))) != srs_success) { return srs_error_wrap(err, "handle console at %s", dir.c_str()); } srs_trace("http: api mount /console to %s", dir.c_str()); + // WebRTC API endpoints + if ((err = listen_rtc_api()) != srs_success) { + return srs_error_wrap(err, "rtc api"); + } + return err; } @@ -847,52 +1470,13 @@ srs_error_t SrsServer::ingest() { srs_error_t err = srs_success; - if ((err = ingester->start()) != srs_success) { + if ((err = ingester_->start()) != srs_success) { return srs_error_wrap(err, "ingest start"); } return err; } -srs_error_t SrsServer::start(SrsWaitGroup *wg) -{ - srs_error_t err = srs_success; - - if ((err = _srs_sources->initialize()) != srs_success) { - return srs_error_wrap(err, "live sources"); - } - -#ifdef SRS_SRT - if ((err = _srs_srt_sources->initialize()) != srs_success) { - return srs_error_wrap(err, "srt sources"); - } -#endif - - if ((err = _srs_rtc_sources->initialize()) != srs_success) { - return srs_error_wrap(err, "rtc sources"); - } - -#ifdef SRS_RTSP - if ((err = _srs_rtsp_sources->initialize()) != srs_success) { - return srs_error_wrap(err, "rtsp sources"); - } -#endif - - if ((err = trd_->start()) != srs_success) { - return srs_error_wrap(err, "start"); - } - - if ((err = setup_ticks()) != srs_success) { - return srs_error_wrap(err, "tick"); - } - - // OK, we start SRS server. - wg_ = wg; - wg->add(1); - - return err; -} - void SrsServer::stop() { #ifdef SRS_GPERF_MC @@ -911,13 +1495,13 @@ void SrsServer::stop() srs_warn("main cycle terminated, system quit normally."); // fast quit, do some essential cleanup. - if (signal_fast_quit) { + if (signal_fast_quit_) { dispose(); // TODO: FIXME: Rename to essential_dispose. srs_trace("srs disposed"); } // gracefully quit, do carefully cleanup. - if (signal_gracefully_quit) { + if (signal_gracefully_quit_) { gracefully_dispose(); srs_trace("srs gracefully quit"); } @@ -941,9 +1525,6 @@ srs_error_t SrsServer::cycle() srs_error("server err %s", srs_error_desc(err).c_str()); } - // OK, SRS server is done. - wg_->done(); - return err; } @@ -958,7 +1539,7 @@ void SrsServer::on_signal(int signo) if (signo == SRS_SIGNAL_RELOAD) { srs_trace("reload config, signo=%d", signo); - signal_reload = true; + signal_reload_ = true; return; } @@ -973,21 +1554,21 @@ void SrsServer::on_signal(int signo) #ifdef SRS_GPERF_MC if (signo == SRS_SIGNAL_REOPEN_LOG) { - signal_gmc_stop = true; + signal_gmc_stop_ = true; srs_warn("for gmc, the SIGUSR1 used as SIGINT, signo=%d", signo); return; } #endif if (signo == SRS_SIGNAL_PERSISTENCE_CONFIG) { - signal_persistence_config = true; + signal_persistence_config_ = true; return; } if (signo == SIGINT) { #ifdef SRS_GPERF_MC srs_trace("gmc is on, main cycle will terminate normally, signo=%d", signo); - signal_gmc_stop = true; + signal_gmc_stop_ = true; #endif } @@ -998,15 +1579,15 @@ void SrsServer::on_signal(int signo) signo = SRS_SIGNAL_GRACEFULLY_QUIT; } - if ((signo == SIGINT || signo == SRS_SIGNAL_FAST_QUIT) && !signal_fast_quit) { + if ((signo == SIGINT || signo == SRS_SIGNAL_FAST_QUIT) && !signal_fast_quit_) { srs_trace("sig=%d, user terminate program, fast quit", signo); - signal_fast_quit = true; + signal_fast_quit_ = true; return; } - if (signo == SRS_SIGNAL_GRACEFULLY_QUIT && !signal_gracefully_quit) { + if (signo == SRS_SIGNAL_GRACEFULLY_QUIT && !signal_gracefully_quit_) { srs_trace("sig=%d, user start gracefully quit", signo); - signal_gracefully_quit = true; + signal_gracefully_quit_ = true; return; } } @@ -1023,18 +1604,14 @@ srs_error_t SrsServer::do_cycle() bool asprocess = _srs_config->get_asprocess(); while (true) { - if ((err = trd_->pull()) != srs_success) { - return srs_error_wrap(err, "pull"); - } - // asprocess check. - if (asprocess && ::getppid() != ppid) { - return srs_error_new(ERROR_ASPROCESS_PPID, "asprocess ppid changed from %d to %d", ppid, ::getppid()); + if (asprocess && ::getppid() != ppid_) { + return srs_error_new(ERROR_ASPROCESS_PPID, "asprocess ppid changed from %d to %d", ppid_, ::getppid()); } // gracefully quit for SIGINT or SIGTERM or SIGQUIT. - if (signal_fast_quit || signal_gracefully_quit) { - srs_trace("cleanup for quit signal fast=%d, grace=%d", signal_fast_quit, signal_gracefully_quit); + if (signal_fast_quit_ || signal_gracefully_quit_) { + srs_trace("cleanup for quit signal fast=%d, grace=%d", signal_fast_quit_, signal_gracefully_quit_); return err; } @@ -1044,15 +1621,15 @@ srs_error_t SrsServer::do_cycle() // but, if gperf, use reload to ensure main return normally, // because directly exit will cause core-dump. #ifdef SRS_GPERF_MC - if (signal_gmc_stop) { + if (signal_gmc_stop_) { srs_warn("gmc got singal to stop server."); return err; } #endif // do persistence config to file. - if (signal_persistence_config) { - signal_persistence_config = false; + if (signal_persistence_config_) { + signal_persistence_config_ = false; srs_info("get signal to persistence config to file."); if ((err = _srs_config->persistence()) != srs_success) { @@ -1062,8 +1639,8 @@ srs_error_t SrsServer::do_cycle() } // do reload the config. - if (signal_reload) { - signal_reload = false; + if (signal_reload_) { + signal_reload_ = false; srs_trace("starting reload config."); SrsReloadState state = SrsReloadStateInit; @@ -1127,6 +1704,10 @@ srs_error_t SrsServer::setup_ticks() if ((err = timer_->tick(10, 9 * SRS_UTIME_SECONDS)) != srs_success) { return srs_error_wrap(err, "tick"); } + + if ((err = timer_->tick(11, 5 * SRS_UTIME_SECONDS)) != srs_success) { + return srs_error_wrap(err, "tick"); + } } if (_srs_config->get_heartbeat_enabled()) { @@ -1166,11 +1747,14 @@ srs_error_t SrsServer::notify(int event, srs_utime_t interval, srs_utime_t tick) resample_kbps(); break; case 9: - http_heartbeat->heartbeat(); + http_heartbeat_->heartbeat(); break; case 10: srs_update_udp_snmp_statistic(); break; + case 11: + srs_update_rtc_sessions(); + break; } return err; @@ -1181,8 +1765,8 @@ void SrsServer::resample_kbps() SrsStatistic *stat = SrsStatistic::instance(); // collect delta from all clients. - for (int i = 0; i < (int)conn_manager->size(); i++) { - ISrsResource *c = conn_manager->at(i); + for (int i = 0; i < (int)_srs_conn_manager->size(); i++) { + ISrsResource *c = _srs_conn_manager->at(i); SrsRtmpConn *rtmp = dynamic_cast(c); if (rtmp) { @@ -1210,6 +1794,20 @@ void SrsServer::resample_kbps() continue; } +#ifdef SRS_SRT + SrsMpegtsSrtConn *srt = dynamic_cast(c); + if (srt) { + stat->kbps_add_delta(c->get_id().c_str(), srt->delta()); + continue; + } +#endif + + SrsRtcConnection *rtc = dynamic_cast(c); + if (rtc) { + stat->kbps_add_delta(c->get_id().c_str(), rtc->delta()); + continue; + } + // Impossible path, because we only create these connections above. srs_assert(false); } @@ -1218,9 +1816,560 @@ void SrsServer::resample_kbps() stat->kbps_sample(); } -ISrsHttpServeMux *SrsServer::api_server() +#ifdef SRS_SRT +srs_error_t SrsServer::listen_srt_mpegts() { - return http_api_mux; + srs_error_t err = srs_success; + + if (!_srs_config->get_srt_enabled()) { + return err; + } + + // Close all listener for SRT if exists. + close_srt_listeners(); + + // Start listeners for SRT, support multiple addresses including IPv6. + vector srt_listens = _srs_config->get_srt_listens(); + for (int i = 0; i < (int)srt_listens.size(); i++) { + SrsSrtAcceptor *acceptor = new SrsSrtAcceptor(this); + + int port; + string ip; + srs_net_split_for_listener(srt_listens[i], ip, port); + + if ((err = acceptor->listen(ip, port)) != srs_success) { + srs_freep(acceptor); + srs_warn("srt listen %s:%d failed, err=%s", ip.c_str(), port, srs_error_desc(err).c_str()); + srs_error_reset(err); + continue; + } + + srt_acceptors_.push_back(acceptor); + } + + // Check if at least one listener succeeded + if (srt_acceptors_.empty()) { + return srs_error_new(ERROR_SOCKET_LISTEN, "no srt listeners available"); + } + + return err; +} + +void SrsServer::close_srt_listeners() +{ + std::vector::iterator it; + for (it = srt_acceptors_.begin(); it != srt_acceptors_.end();) { + SrsSrtAcceptor *acceptor = *it; + srs_freep(acceptor); + + it = srt_acceptors_.erase(it); + } +} + +srs_error_t SrsServer::accept_srt_client(srs_srt_t srt_fd) +{ + srs_error_t err = srs_success; + + ISrsResource *resource = NULL; + if ((err = srt_fd_to_resource(srt_fd, &resource)) != srs_success) { + // close fd on conn error, otherwise will lead to fd leak + srs_srt_close(srt_fd); + return srs_error_wrap(err, "srt fd to resource"); + } + srs_assert(resource); + + // directly enqueue, the cycle thread will remove the client. + _srs_conn_manager->add(resource); + + // Note that conn is managed by _srs_conn_manager, so we don't need to free it. + ISrsStartable *conn = dynamic_cast(resource); + if ((err = conn->start()) != srs_success) { + return srs_error_wrap(err, "start srt conn coroutine"); + } + + return err; +} + +srs_error_t SrsServer::srt_fd_to_resource(srs_srt_t srt_fd, ISrsResource **pr) +{ + srs_error_t err = srs_success; + + string ip = ""; + int port = 0; + if ((err = srs_srt_get_remote_ip_port(srt_fd, ip, port)) != srs_success) { + return srs_error_wrap(err, "get srt ip port"); + } + + // Security or system flow control check. + if ((err = on_before_connection("SRT", (int)srt_fd, ip, port)) != srs_success) { + return srs_error_wrap(err, "check"); + } + + // The context id may change during creating the bellow objects. + SrsContextRestore(_srs_context->get_id()); + + // Convert to SRT connection. + *pr = new SrsMpegtsSrtConn(_srs_conn_manager, srt_fd, ip, port); + + return err; +} +#endif + +srs_error_t SrsServer::listen_rtc_udp() +{ + srs_error_t err = srs_success; + + if (!_srs_config->get_rtc_server_enabled()) { + return err; + } + + // Check protocol setting - only create UDP listeners if protocol allows UDP + string protocol = _srs_config->get_rtc_server_protocol(); + if (protocol == "tcp") { + // When protocol is "tcp", don't create UDP listeners + return err; + } + + vector rtc_listens = _srs_config->get_rtc_server_listens(); + if (rtc_listens.empty()) { + return srs_error_new(ERROR_RTC_PORT, "empty rtc listen"); + } + + // There should be no listeners before listening. + srs_assert(rtc_listeners_.empty()); + + // For each listen address, create multiple listeners based on reuseport setting + int nn_listeners = _srs_config->get_rtc_server_reuseport(); + for (int j = 0; j < (int)rtc_listens.size(); j++) { + string ip; + int port; + srs_net_split_for_listener(rtc_listens[j], ip, port); + + if (port <= 0) { + return srs_error_new(ERROR_RTC_PORT, "invalid port=%d", port); + } + + for (int i = 0; i < nn_listeners; i++) { + SrsUdpMuxListener *listener = new SrsUdpMuxListener(this, ip, port); + + if ((err = listener->listen()) != srs_success) { + srs_freep(listener); + return srs_error_wrap(err, "listen %s:%d", ip.c_str(), port); + } + + srs_trace("WebRTC listen at udp://%s:%d, fd=%d", ip.c_str(), port, listener->fd()); + rtc_listeners_.push_back(listener); + } + } + + return err; +} + +srs_error_t SrsServer::on_udp_packet(SrsUdpMuxSocket *skt) +{ + srs_error_t err = srs_success; + + SrsRtcConnection *session = NULL; + char *data = skt->data(); + int size = skt->size(); + bool is_rtp_or_rtcp = srs_is_rtp_or_rtcp((uint8_t *)data, size); + bool is_rtcp = srs_is_rtcp((uint8_t *)data, size); + + uint64_t fast_id = skt->fast_id(); + // Try fast id first, if not found, search by long peer id. + if (fast_id) { + session = (SrsRtcConnection *)_srs_conn_manager->find_by_fast_id(fast_id); + } + if (!session) { + string peer_id = skt->peer_id(); + session = (SrsRtcConnection *)_srs_conn_manager->find_by_id(peer_id); + } + + if (session) { + // When got any packet, the session is alive now. + session->alive(); + } + + // For STUN, the peer address may change. + if (!is_rtp_or_rtcp && srs_is_stun((uint8_t *)data, size)) { + ++_srs_pps_rstuns->sugar; + string peer_id = skt->peer_id(); + + // TODO: FIXME: Should support ICE renomination, to switch network between candidates. + SrsStunPacket ping; + if ((err = ping.decode(data, size)) != srs_success) { + return srs_error_wrap(err, "decode stun packet failed"); + } + if (!session) { + session = find_rtc_session_by_username(ping.get_username()); + } + if (session) { + session->switch_to_context(); + } + + srs_info("recv stun packet from %s, fast=%" PRId64 ", use-candidate=%d, ice-controlled=%d, ice-controlling=%d", + peer_id.c_str(), fast_id, ping.get_use_candidate(), ping.get_ice_controlled(), ping.get_ice_controlling()); + + // TODO: FIXME: For ICE trickle, we may get STUN packets before SDP answer, so maybe should response it. + if (!session) { + return srs_error_new(ERROR_RTC_STUN, "no session, stun username=%s, peer_id=%s, fast=%" PRId64, + ping.get_username().c_str(), peer_id.c_str(), fast_id); + } + + // For each binding request, update the UDP socket. + if (ping.is_binding_request()) { + session->udp()->update_sendonly_socket(skt); + } + + return session->udp()->on_stun(&ping, data, size); + } + + // For DTLS, RTCP or RTP, which does not support peer address changing. + if (!session) { + string peer_id = skt->peer_id(); + return srs_error_new(ERROR_RTC_STUN, "no session, peer_id=%s, fast=%" PRId64, peer_id.c_str(), fast_id); + } + + // Note that we don't(except error) switch to the context of session, for performance issue. + if (is_rtp_or_rtcp && !is_rtcp) { + ++_srs_pps_rrtps->sugar; + + err = session->udp()->on_rtp(data, size); + if (err != srs_success) { + session->switch_to_context(); + } + return err; + } + + session->switch_to_context(); + if (is_rtp_or_rtcp && is_rtcp) { + ++_srs_pps_rrtcps->sugar; + + return session->udp()->on_rtcp(data, size); + } + if (srs_is_dtls((uint8_t *)data, size)) { + ++_srs_pps_rstuns->sugar; + + return session->udp()->on_dtls(data, size); + } + return srs_error_new(ERROR_RTC_UDP, "unknown packet"); +} + +srs_error_t SrsServer::listen_rtc_api() +{ + srs_error_t err = srs_success; + + if ((err = http_api_mux_->handle("/rtc/v1/play/", new SrsGoApiRtcPlay(this))) != srs_success) { + return srs_error_wrap(err, "handle play"); + } + + if ((err = http_api_mux_->handle("/rtc/v1/publish/", new SrsGoApiRtcPublish(this))) != srs_success) { + return srs_error_wrap(err, "handle publish"); + } + + // Generally, WHIP is a publishing protocol, but it can be also used as playing. + // See https://datatracker.ietf.org/doc/draft-ietf-wish-whep/ + if ((err = http_api_mux_->handle("/rtc/v1/whip/", new SrsGoApiRtcWhip(this))) != srs_success) { + return srs_error_wrap(err, "handle whip"); + } + + // We create another mount, to support play with the same query string as publish. + // See https://datatracker.ietf.org/doc/draft-murillo-whep/ + if ((err = http_api_mux_->handle("/rtc/v1/whip-play/", new SrsGoApiRtcWhip(this))) != srs_success) { + return srs_error_wrap(err, "handle whep play"); + } + if ((err = http_api_mux_->handle("/rtc/v1/whep/", new SrsGoApiRtcWhip(this))) != srs_success) { + return srs_error_wrap(err, "handle whep play"); + } + +#ifdef SRS_SIMULATOR + if ((err = http_api_mux_->handle("/rtc/v1/nack/", new SrsGoApiRtcNACK(this))) != srs_success) { + return srs_error_wrap(err, "handle nack"); + } +#endif + + return err; +} + +srs_error_t SrsServer::exec_rtc_async_work(ISrsAsyncCallTask *t) +{ + return rtc_async_->execute(t); +} + +SrsRtcConnection *SrsServer::find_rtc_session_by_username(const std::string &username) +{ + ISrsResource *conn = _srs_conn_manager->find_by_name(username); + return dynamic_cast(conn); +} + +srs_error_t SrsServer::create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection **psession) +{ + srs_error_t err = srs_success; + + ISrsRequest *req = ruc->req_; + + // Security or system flow control check. For WebRTC, use 0 as fd and port, because for + // the WebRTC HTTP API, it's not useful information. + if ((err = on_before_connection("RTC", (int)0, req->ip, 0)) != srs_success) { + return srs_error_wrap(err, "check"); + } + + // Acquire stream publish token to prevent race conditions across all protocols. + SrsStreamPublishToken *publish_token_raw = NULL; + if (ruc->publish_ && (err = _srs_stream_publish_tokens->acquire_token(req, publish_token_raw)) != srs_success) { + return srs_error_wrap(err, "acquire stream publish token"); + } + SrsUniquePtr publish_token(publish_token_raw); + if (publish_token.get()) { + srs_trace("stream publish token acquired, type=rtc, url=%s", req->get_stream_url().c_str()); + } + + SrsSharedPtr source; + if ((err = _srs_rtc_sources->fetch_or_create(req, source)) != srs_success) { + return srs_error_wrap(err, "create source"); + } + + if (ruc->publish_ && !source->can_publish()) { + return srs_error_new(ERROR_RTC_SOURCE_BUSY, "stream %s busy", req->get_stream_url().c_str()); + } + + // TODO: FIXME: add do_create_session to error process. + SrsContextId cid = _srs_context->get_id(); + SrsRtcConnection *session = new SrsRtcConnection(this, cid); + if ((err = do_create_rtc_session(ruc, local_sdp, session)) != srs_success) { + srs_freep(session); + return srs_error_wrap(err, "create session"); + } + + *psession = session; + + return err; +} + +srs_error_t SrsServer::do_create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection *session) +{ + srs_error_t err = srs_success; + + ISrsRequest *req = ruc->req_; + + // first add publisher/player for negotiate sdp media info + if (ruc->publish_) { + if ((err = session->add_publisher(ruc, local_sdp)) != srs_success) { + return srs_error_wrap(err, "add publisher"); + } + } else { + if ((err = session->add_player(ruc, local_sdp)) != srs_success) { + return srs_error_wrap(err, "add player"); + } + } + + // All tracks default as inactive, so we must enable them. + session->set_all_tracks_status(req->get_stream_url(), ruc->publish_, true); + + std::string local_pwd = ruc->req_->ice_pwd_.empty() ? srs_rand_gen_str(32) : ruc->req_->ice_pwd_; + std::string local_ufrag = ruc->req_->ice_ufrag_.empty() ? srs_rand_gen_str(8) : ruc->req_->ice_ufrag_; + // TODO: FIXME: Rename for a better name, it's not an username. + std::string username = ""; + while (true) { + username = local_ufrag + ":" + ruc->remote_sdp_.get_ice_ufrag(); + if (!_srs_conn_manager->find_by_name(username)) { + break; + } + + // Username conflict, regenerate a new one. + local_ufrag = srs_rand_gen_str(8); + } + + local_sdp.set_ice_ufrag(local_ufrag); + local_sdp.set_ice_pwd(local_pwd); + local_sdp.set_fingerprint_algo("sha-256"); + local_sdp.set_fingerprint(_srs_rtc_dtls_certificate->get_fingerprint()); + + // We allows to mock the eip of server. + if (true) { + // TODO: Support multiple listen ports. + int udp_port = 0; + if (true) { + string udp_host; + string udp_hostport = _srs_config->get_rtc_server_listens().at(0); + srs_net_split_for_listener(udp_hostport, udp_host, udp_port); + } + + int tcp_port = 0; + if (true) { + string tcp_host; + string tcp_hostport = _srs_config->get_rtc_server_tcp_listens().at(0); + srs_net_split_for_listener(tcp_hostport, tcp_host, tcp_port); + } + + string protocol = _srs_config->get_rtc_server_protocol(); + + set candidates = discover_candidates(ruc); + for (set::iterator it = candidates.begin(); it != candidates.end(); ++it) { + string hostname; + int uport = udp_port; + srs_net_split_hostport(*it, hostname, uport); + int tport = tcp_port; + srs_net_split_hostport(*it, hostname, tport); + + if (protocol == "udp") { + local_sdp.add_candidate("udp", hostname, uport, "host"); + } else if (protocol == "tcp") { + local_sdp.add_candidate("tcp", hostname, tport, "host"); + } else { + local_sdp.add_candidate("udp", hostname, uport, "host"); + local_sdp.add_candidate("tcp", hostname, tport, "host"); + } + } + + vector v = vector(candidates.begin(), candidates.end()); + srs_trace("RTC: Use candidates %s, protocol=%s, tcp_port=%d, udp_port=%d", + srs_strings_join(v, ", ").c_str(), protocol.c_str(), tcp_port, udp_port); + } + + // Setup the negotiate DTLS by config. + local_sdp.session_negotiate_ = local_sdp.session_config_; + + // Setup the negotiate DTLS role. + if (ruc->remote_sdp_.get_dtls_role() == "active") { + local_sdp.session_negotiate_.dtls_role = "passive"; + } else if (ruc->remote_sdp_.get_dtls_role() == "passive") { + local_sdp.session_negotiate_.dtls_role = "active"; + } else if (ruc->remote_sdp_.get_dtls_role() == "actpass") { + local_sdp.session_negotiate_.dtls_role = local_sdp.session_config_.dtls_role; + } else { + // @see: https://tools.ietf.org/html/rfc4145#section-4.1 + // The default value of the setup attribute in an offer/answer exchange + // is 'active' in the offer and 'passive' in the answer. + local_sdp.session_negotiate_.dtls_role = "passive"; + } + local_sdp.set_dtls_role(local_sdp.session_negotiate_.dtls_role); + + session->set_remote_sdp(ruc->remote_sdp_); + // We must setup the local SDP, then initialize the session object. + session->set_local_sdp(local_sdp); + session->set_state_as_waiting_stun(); + + // Before session initialize, we must setup the local SDP. + if ((err = session->initialize(req, ruc->dtls_, ruc->srtp_, username)) != srs_success) { + return srs_error_wrap(err, "init"); + } + + // We allows username is optional, but it never empty here. + _srs_conn_manager->add_with_name(username, session); + + return err; +} + +srs_error_t SrsServer::srs_update_rtc_sessions() +{ + srs_error_t err = srs_success; + + // Alive RTC sessions, for stat. + int nn_rtc_conns = 0; + + // Check all sessions and dispose the dead sessions. + for (int i = 0; i < (int)_srs_conn_manager->size(); i++) { + SrsRtcConnection *session = dynamic_cast(_srs_conn_manager->at(i)); + // Ignore not session, or already disposing. + if (!session || session->disposing_) { + continue; + } + + // Update stat if session is alive. + if (session->is_alive()) { + nn_rtc_conns++; + continue; + } + + SrsContextRestore(_srs_context->get_id()); + session->switch_to_context(); + + string username = session->username(); + srs_trace("RTC: session destroy by timeout, username=%s", username.c_str()); + + // Use manager to free session and notify other objects. + _srs_conn_manager->remove(session); + } + + // Ignore stats if no RTC connections. + if (!nn_rtc_conns) { + return err; + } + static char buf[128]; + + string rpkts_desc; + _srs_pps_rpkts->update(); + _srs_pps_rrtps->update(); + _srs_pps_rstuns->update(); + _srs_pps_rrtcps->update(); + if (_srs_pps_rpkts->r10s() || _srs_pps_rrtps->r10s() || _srs_pps_rstuns->r10s() || _srs_pps_rrtcps->r10s()) { + snprintf(buf, sizeof(buf), ", rpkts=(%d,rtp:%d,stun:%d,rtcp:%d)", _srs_pps_rpkts->r10s(), _srs_pps_rrtps->r10s(), _srs_pps_rstuns->r10s(), _srs_pps_rrtcps->r10s()); + rpkts_desc = buf; + } + + string spkts_desc; + _srs_pps_spkts->update(); + _srs_pps_srtps->update(); + _srs_pps_sstuns->update(); + _srs_pps_srtcps->update(); + if (_srs_pps_spkts->r10s() || _srs_pps_srtps->r10s() || _srs_pps_sstuns->r10s() || _srs_pps_srtcps->r10s()) { + snprintf(buf, sizeof(buf), ", spkts=(%d,rtp:%d,stun:%d,rtcp:%d)", _srs_pps_spkts->r10s(), _srs_pps_srtps->r10s(), _srs_pps_sstuns->r10s(), _srs_pps_srtcps->r10s()); + spkts_desc = buf; + } + + string rtcp_desc; + _srs_pps_pli->update(); + _srs_pps_twcc->update(); + _srs_pps_rr->update(); + if (_srs_pps_pli->r10s() || _srs_pps_twcc->r10s() || _srs_pps_rr->r10s()) { + snprintf(buf, sizeof(buf), ", rtcp=(pli:%d,twcc:%d,rr:%d)", _srs_pps_pli->r10s(), _srs_pps_twcc->r10s(), _srs_pps_rr->r10s()); + rtcp_desc = buf; + } + + string snk_desc; + _srs_pps_snack->update(); + _srs_pps_snack2->update(); + _srs_pps_sanack->update(); + _srs_pps_svnack->update(); + if (_srs_pps_snack->r10s() || _srs_pps_sanack->r10s() || _srs_pps_svnack->r10s() || _srs_pps_snack2->r10s()) { + snprintf(buf, sizeof(buf), ", snk=(%d,a:%d,v:%d,h:%d)", _srs_pps_snack->r10s(), _srs_pps_sanack->r10s(), _srs_pps_svnack->r10s(), _srs_pps_snack2->r10s()); + snk_desc = buf; + } + + string rnk_desc; + _srs_pps_rnack->update(); + _srs_pps_rnack2->update(); + _srs_pps_rhnack->update(); + _srs_pps_rmnack->update(); + if (_srs_pps_rnack->r10s() || _srs_pps_rnack2->r10s() || _srs_pps_rhnack->r10s() || _srs_pps_rmnack->r10s()) { + snprintf(buf, sizeof(buf), ", rnk=(%d,%d,h:%d,m:%d)", _srs_pps_rnack->r10s(), _srs_pps_rnack2->r10s(), _srs_pps_rhnack->r10s(), _srs_pps_rmnack->r10s()); + rnk_desc = buf; + } + + string loss_desc; + SrsSnmpUdpStat *s = srs_get_udp_snmp_stat(); + if (s->rcv_buf_errors_delta || s->snd_buf_errors_delta) { + snprintf(buf, sizeof(buf), ", loss=(r:%d,s:%d)", s->rcv_buf_errors_delta, s->snd_buf_errors_delta); + loss_desc = buf; + } + + string fid_desc; + _srs_pps_ids->update(); + _srs_pps_fids->update(); + _srs_pps_fids_level0->update(); + _srs_pps_addrs->update(); + _srs_pps_fast_addrs->update(); + if (_srs_pps_ids->r10s(), _srs_pps_fids->r10s(), _srs_pps_fids_level0->r10s(), _srs_pps_addrs->r10s(), _srs_pps_fast_addrs->r10s()) { + snprintf(buf, sizeof(buf), ", fid=(id:%d,fid:%d,ffid:%d,addr:%d,faddr:%d)", _srs_pps_ids->r10s(), _srs_pps_fids->r10s(), _srs_pps_fids_level0->r10s(), _srs_pps_addrs->r10s(), _srs_pps_fast_addrs->r10s()); + fid_desc = buf; + } + + srs_trace("RTC: Server conns=%u%s%s%s%s%s%s%s", + nn_rtc_conns, + rpkts_desc.c_str(), spkts_desc.c_str(), rtcp_desc.c_str(), snk_desc.c_str(), rnk_desc.c_str(), loss_desc.c_str(), fid_desc.c_str()); + + return err; } srs_error_t SrsServer::on_tcp_client(ISrsListener *listener, srs_netfd_t stfd) @@ -1249,10 +2398,23 @@ srs_error_t SrsServer::do_on_tcp_client(ISrsListener *listener, srs_netfd_t &stf } // Security or system flow control check. - if ((err = on_before_connection(stfd, ip, port)) != srs_success) { + if ((err = on_before_connection("TCP", fd, ip, port)) != srs_success) { return srs_error_wrap(err, "check"); } + // Set to close the fd when forking, to avoid fd leak when start a process. + // See https://github.com/ossrs/srs/issues/518 + if (true) { + int val; + if ((val = fcntl(fd, F_GETFD, 0)) < 0) { + return srs_error_new(ERROR_SYSTEM_PID_GET_FILE_INFO, "fnctl F_GETFD error! fd=%d", fd); + } + val |= FD_CLOEXEC; + if (fcntl(fd, F_SETFD, val) < 0) { + return srs_error_new(ERROR_SYSTEM_PID_SET_FILE_INFO, "fcntl F_SETFD error! fd=%d", fd); + } + } + // Covert handler to resource. ISrsResource *resource = NULL; @@ -1289,7 +2451,7 @@ srs_error_t SrsServer::do_on_tcp_client(ISrsListener *listener, srs_netfd_t &stf } else { string key = listener == https_listener_ ? _srs_config->get_https_stream_ssl_key() : ""; string cert = listener == https_listener_ ? _srs_config->get_https_stream_ssl_cert() : ""; - resource = new SrsHttpxConn(this, io, http_server, ip, port, key, cert); + resource = new SrsHttpxConn(_srs_conn_manager, io, http_server_, ip, port, key, cert); } } @@ -1304,20 +2466,20 @@ srs_error_t SrsServer::do_on_tcp_client(ISrsListener *listener, srs_netfd_t &stf } else if (listener == api_listener_ || listener == apis_listener_) { string key = listener == apis_listener_ ? _srs_config->get_https_api_ssl_key() : ""; string cert = listener == apis_listener_ ? _srs_config->get_https_api_ssl_cert() : ""; - resource = new SrsHttpxConn(this, new SrsTcpConnection(stfd2), http_api_mux, ip, port, key, cert); + resource = new SrsHttpxConn(_srs_conn_manager, new SrsTcpConnection(stfd2), http_api_mux_, ip, port, key, cert); } else if (listener == http_listener_ || listener == https_listener_) { string key = listener == https_listener_ ? _srs_config->get_https_stream_ssl_key() : ""; string cert = listener == https_listener_ ? _srs_config->get_https_stream_ssl_cert() : ""; - resource = new SrsHttpxConn(this, new SrsTcpConnection(stfd2), http_server, ip, port, key, cert); + resource = new SrsHttpxConn(_srs_conn_manager, new SrsTcpConnection(stfd2), http_server_, ip, port, key, cert); } else if (listener == webrtc_listener_) { resource = new SrsRtcTcpConn(new SrsTcpConnection(stfd2), ip, port); #ifdef SRS_RTSP } else if (listener == rtsp_listener_) { - resource = new SrsRtspConnection(this, new SrsTcpConnection(stfd2), ip, port); + resource = new SrsRtspConnection(_srs_conn_manager, new SrsTcpConnection(stfd2), ip, port); #endif } else if (listener == exporter_listener_) { // TODO: FIXME: Maybe should support https metrics. - resource = new SrsHttpxConn(this, new SrsTcpConnection(stfd2), http_api_mux, ip, port, "", ""); + resource = new SrsHttpxConn(_srs_conn_manager, new SrsTcpConnection(stfd2), http_api_mux_, ip, port, "", ""); } else { srs_close_stfd(stfd2); srs_warn("Close for invalid fd=%d, ip=%s:%d", fd, ip.c_str(), port); @@ -1329,7 +2491,7 @@ srs_error_t SrsServer::do_on_tcp_client(ISrsListener *listener, srs_netfd_t &stf SrsRtcTcpConn *raw_conn = dynamic_cast(resource); if (raw_conn) { SrsSharedResource *conn = new SrsSharedResource(raw_conn); - SrsExecutorCoroutine *executor = new SrsExecutorCoroutine(_srs_rtc_manager, conn, raw_conn, raw_conn); + SrsExecutorCoroutine *executor = new SrsExecutorCoroutine(_srs_conn_manager, conn, raw_conn, raw_conn); raw_conn->setup_owner(conn, executor, executor); if ((err = executor->start()) != srs_success) { srs_freep(executor); @@ -1340,7 +2502,7 @@ srs_error_t SrsServer::do_on_tcp_client(ISrsListener *listener, srs_netfd_t &stf // Use connection manager to manage all the resources. srs_assert(resource); - conn_manager->add(resource); + _srs_conn_manager->add(resource); // If connection is a resource to start, start a coroutine to handle it. // Note that conn is managed by conn_manager, so we don't need to free it. @@ -1353,47 +2515,26 @@ srs_error_t SrsServer::do_on_tcp_client(ISrsListener *listener, srs_netfd_t &stf return err; } -srs_error_t SrsServer::on_before_connection(srs_netfd_t &stfd, const std::string &ip, int port) +srs_error_t SrsServer::on_before_connection(const char *label, int fd, const std::string &ip, int port) { srs_error_t err = srs_success; - int fd = srs_netfd_fileno(stfd); - // Failed if exceed the connection limitation. int max_connections = _srs_config->get_max_connections(); - if ((int)conn_manager->size() >= max_connections) { - return srs_error_new(ERROR_EXCEED_CONNECTIONS, "drop fd=%d, ip=%s:%d, max=%d, cur=%d for exceed connection limits", - fd, ip.c_str(), port, max_connections, (int)conn_manager->size()); - } - - // Set to close the fd when forking, to avoid fd leak when start a process. - // See https://github.com/ossrs/srs/issues/518 - if (true) { - int val; - if ((val = fcntl(fd, F_GETFD, 0)) < 0) { - return srs_error_new(ERROR_SYSTEM_PID_GET_FILE_INFO, "fnctl F_GETFD error! fd=%d", fd); - } - val |= FD_CLOEXEC; - if (fcntl(fd, F_SETFD, val) < 0) { - return srs_error_new(ERROR_SYSTEM_PID_SET_FILE_INFO, "fcntl F_SETFD error! fd=%d", fd); - } + if ((int)_srs_conn_manager->size() >= max_connections) { + return srs_error_new(ERROR_EXCEED_CONNECTIONS, "drop %s fd=%d, ip=%s:%d, max=%d, cur=%d for exceed connection limits", + label, fd, ip.c_str(), port, max_connections, (int)_srs_conn_manager->size()); } return err; } -void SrsServer::remove(ISrsResource *c) -{ - // use manager to free it async. - conn_manager->remove(c); -} - srs_error_t SrsServer::on_publish(ISrsRequest *r) { srs_error_t err = srs_success; - if ((err = http_server->http_mount(r)) != srs_success) { + if ((err = http_server_->http_mount(r)) != srs_success) { return srs_error_wrap(err, "http mount"); } @@ -1407,80 +2548,180 @@ srs_error_t SrsServer::on_publish(ISrsRequest *r) void SrsServer::on_unpublish(ISrsRequest *r) { - http_server->http_unmount(r); + http_server_->http_unmount(r); SrsCoWorkers *coworkers = SrsCoWorkers::instance(); coworkers->on_unpublish(r); } -SrsServerAdapter::SrsServerAdapter() +SrsFastTimer *SrsServer::timer20ms() { - srs = new SrsServer(); + return timer20ms_; } -SrsServerAdapter::~SrsServerAdapter() +SrsFastTimer *SrsServer::timer100ms() { - srs_freep(srs); + return timer100ms_; } -srs_error_t SrsServerAdapter::initialize() +SrsFastTimer *SrsServer::timer1s() { - srs_error_t err = srs_success; - return err; + return timer1s_; } -srs_error_t SrsServerAdapter::run(SrsWaitGroup *wg) +SrsFastTimer *SrsServer::timer5s() +{ + return timer5s_; +} + +srs_error_t SrsServer::on_timer(srs_utime_t interval) { srs_error_t err = srs_success; - // Initialize the whole system, set hooks to handle server level events. - if ((err = srs->initialize()) != srs_success) { - return srs_error_wrap(err, "server initialize"); + // Show statistics for RTC server. + SrsProcSelfStat *u = srs_get_self_proc_stat(); + // Resident Set Size: number of pages the process has in real memory. + int memory = (int)(u->rss * 4 / 1024); + + static char buf[128]; + + string cid_desc; + _srs_pps_cids_get->update(); + _srs_pps_cids_set->update(); + if (_srs_pps_cids_get->r10s() || _srs_pps_cids_set->r10s()) { + snprintf(buf, sizeof(buf), ", cid=%d,%d", _srs_pps_cids_get->r10s(), _srs_pps_cids_set->r10s()); + cid_desc = buf; + } + string timer_desc; + _srs_pps_timer->update(); + _srs_pps_pub->update(); + _srs_pps_conn->update(); + if (_srs_pps_timer->r10s() || _srs_pps_pub->r10s() || _srs_pps_conn->r10s()) { + snprintf(buf, sizeof(buf), ", timer=%d,%d,%d", _srs_pps_timer->r10s(), _srs_pps_pub->r10s(), _srs_pps_conn->r10s()); + timer_desc = buf; } - if ((err = srs->initialize_st()) != srs_success) { - return srs_error_wrap(err, "initialize st"); + string free_desc; + _srs_pps_dispose->update(); + if (_srs_pps_dispose->r10s()) { + snprintf(buf, sizeof(buf), ", free=%d", _srs_pps_dispose->r10s()); + free_desc = buf; } - if ((err = srs->initialize_signal()) != srs_success) { - return srs_error_wrap(err, "initialize signal"); - } - - if ((err = srs->listen()) != srs_success) { - return srs_error_wrap(err, "listen"); - } - - if ((err = srs->register_signal()) != srs_success) { - return srs_error_wrap(err, "register signal"); - } - - if ((err = srs->http_handle()) != srs_success) { - return srs_error_wrap(err, "http handle"); - } - - if ((err = srs->ingest()) != srs_success) { - return srs_error_wrap(err, "ingest"); - } - - if ((err = srs->start(wg)) != srs_success) { - return srs_error_wrap(err, "start"); - } - -#ifdef SRS_GB28181 - if ((err = _srs_gb_manager->start()) != srs_success) { - return srs_error_wrap(err, "start manager"); + string recvfrom_desc; +#if defined(SRS_DEBUG) && defined(SRS_DEBUG_STATS) + _srs_pps_recvfrom->update(_st_stat_recvfrom); + _srs_pps_recvfrom_eagain->update(_st_stat_recvfrom_eagain); + _srs_pps_sendto->update(_st_stat_sendto); + _srs_pps_sendto_eagain->update(_st_stat_sendto_eagain); + if (_srs_pps_recvfrom->r10s() || _srs_pps_recvfrom_eagain->r10s() || _srs_pps_sendto->r10s() || _srs_pps_sendto_eagain->r10s()) { + snprintf(buf, sizeof(buf), ", udp=%d,%d,%d,%d", _srs_pps_recvfrom->r10s(), _srs_pps_recvfrom_eagain->r10s(), _srs_pps_sendto->r10s(), _srs_pps_sendto_eagain->r10s()); + recvfrom_desc = buf; } #endif + string io_desc; +#if defined(SRS_DEBUG) && defined(SRS_DEBUG_STATS) + _srs_pps_read->update(_st_stat_read); + _srs_pps_read_eagain->update(_st_stat_read_eagain); + _srs_pps_readv->update(_st_stat_readv); + _srs_pps_readv_eagain->update(_st_stat_readv_eagain); + _srs_pps_writev->update(_st_stat_writev); + _srs_pps_writev_eagain->update(_st_stat_writev_eagain); + if (_srs_pps_read->r10s() || _srs_pps_read_eagain->r10s() || _srs_pps_readv->r10s() || _srs_pps_readv_eagain->r10s() || _srs_pps_writev->r10s() || _srs_pps_writev_eagain->r10s()) { + snprintf(buf, sizeof(buf), ", io=%d,%d,%d,%d,%d,%d", _srs_pps_read->r10s(), _srs_pps_read_eagain->r10s(), _srs_pps_readv->r10s(), _srs_pps_readv_eagain->r10s(), _srs_pps_writev->r10s(), _srs_pps_writev_eagain->r10s()); + io_desc = buf; + } +#endif + + string msg_desc; +#if defined(SRS_DEBUG) && defined(SRS_DEBUG_STATS) + _srs_pps_recvmsg->update(_st_stat_recvmsg); + _srs_pps_recvmsg_eagain->update(_st_stat_recvmsg_eagain); + _srs_pps_sendmsg->update(_st_stat_sendmsg); + _srs_pps_sendmsg_eagain->update(_st_stat_sendmsg_eagain); + if (_srs_pps_recvmsg->r10s() || _srs_pps_recvmsg_eagain->r10s() || _srs_pps_sendmsg->r10s() || _srs_pps_sendmsg_eagain->r10s()) { + snprintf(buf, sizeof(buf), ", msg=%d,%d,%d,%d", _srs_pps_recvmsg->r10s(), _srs_pps_recvmsg_eagain->r10s(), _srs_pps_sendmsg->r10s(), _srs_pps_sendmsg_eagain->r10s()); + msg_desc = buf; + } +#endif + + string epoll_desc; +#if defined(SRS_DEBUG) && defined(SRS_DEBUG_STATS) + _srs_pps_epoll->update(_st_stat_epoll); + _srs_pps_epoll_zero->update(_st_stat_epoll_zero); + _srs_pps_epoll_shake->update(_st_stat_epoll_shake); + _srs_pps_epoll_spin->update(_st_stat_epoll_spin); + if (_srs_pps_epoll->r10s() || _srs_pps_epoll_zero->r10s() || _srs_pps_epoll_shake->r10s() || _srs_pps_epoll_spin->r10s()) { + snprintf(buf, sizeof(buf), ", epoll=%d,%d,%d,%d", _srs_pps_epoll->r10s(), _srs_pps_epoll_zero->r10s(), _srs_pps_epoll_shake->r10s(), _srs_pps_epoll_spin->r10s()); + epoll_desc = buf; + } +#endif + + string sched_desc; +#if defined(SRS_DEBUG) && defined(SRS_DEBUG_STATS) + _srs_pps_sched_160ms->update(_st_stat_sched_160ms); + _srs_pps_sched_s->update(_st_stat_sched_s); + _srs_pps_sched_15ms->update(_st_stat_sched_15ms); + _srs_pps_sched_20ms->update(_st_stat_sched_20ms); + _srs_pps_sched_25ms->update(_st_stat_sched_25ms); + _srs_pps_sched_30ms->update(_st_stat_sched_30ms); + _srs_pps_sched_35ms->update(_st_stat_sched_35ms); + _srs_pps_sched_40ms->update(_st_stat_sched_40ms); + _srs_pps_sched_80ms->update(_st_stat_sched_80ms); + if (_srs_pps_sched_160ms->r10s() || _srs_pps_sched_s->r10s() || _srs_pps_sched_15ms->r10s() || _srs_pps_sched_20ms->r10s() || _srs_pps_sched_25ms->r10s() || _srs_pps_sched_30ms->r10s() || _srs_pps_sched_35ms->r10s() || _srs_pps_sched_40ms->r10s() || _srs_pps_sched_80ms->r10s()) { + snprintf(buf, sizeof(buf), ", sched=%d,%d,%d,%d,%d,%d,%d,%d,%d", _srs_pps_sched_15ms->r10s(), _srs_pps_sched_20ms->r10s(), _srs_pps_sched_25ms->r10s(), _srs_pps_sched_30ms->r10s(), _srs_pps_sched_35ms->r10s(), _srs_pps_sched_40ms->r10s(), _srs_pps_sched_80ms->r10s(), _srs_pps_sched_160ms->r10s(), _srs_pps_sched_s->r10s()); + sched_desc = buf; + } +#endif + + string clock_desc; + _srs_pps_clock_15ms->update(); + _srs_pps_clock_20ms->update(); + _srs_pps_clock_25ms->update(); + _srs_pps_clock_30ms->update(); + _srs_pps_clock_35ms->update(); + _srs_pps_clock_40ms->update(); + _srs_pps_clock_80ms->update(); + _srs_pps_clock_160ms->update(); + _srs_pps_timer_s->update(); + if (_srs_pps_clock_15ms->r10s() || _srs_pps_timer_s->r10s() || _srs_pps_clock_20ms->r10s() || _srs_pps_clock_25ms->r10s() || _srs_pps_clock_30ms->r10s() || _srs_pps_clock_35ms->r10s() || _srs_pps_clock_40ms->r10s() || _srs_pps_clock_80ms->r10s() || _srs_pps_clock_160ms->r10s()) { + snprintf(buf, sizeof(buf), ", clock=%d,%d,%d,%d,%d,%d,%d,%d,%d", _srs_pps_clock_15ms->r10s(), _srs_pps_clock_20ms->r10s(), _srs_pps_clock_25ms->r10s(), _srs_pps_clock_30ms->r10s(), _srs_pps_clock_35ms->r10s(), _srs_pps_clock_40ms->r10s(), _srs_pps_clock_80ms->r10s(), _srs_pps_clock_160ms->r10s(), _srs_pps_timer_s->r10s()); + clock_desc = buf; + } + + string thread_desc; +#if defined(SRS_DEBUG) && defined(SRS_DEBUG_STATS) + _srs_pps_thread_run->update(_st_stat_thread_run); + _srs_pps_thread_idle->update(_st_stat_thread_idle); + _srs_pps_thread_yield->update(_st_stat_thread_yield); + _srs_pps_thread_yield2->update(_st_stat_thread_yield2); + if (_st_active_count > 0 || _st_num_free_stacks > 0 || _srs_pps_thread_run->r10s() || _srs_pps_thread_idle->r10s() || _srs_pps_thread_yield->r10s() || _srs_pps_thread_yield2->r10s()) { + snprintf(buf, sizeof(buf), ", co=%d,%d,%d, stk=%d, yield=%d,%d", _st_active_count, _srs_pps_thread_run->r10s(), _srs_pps_thread_idle->r10s(), _st_num_free_stacks, _srs_pps_thread_yield->r10s(), _srs_pps_thread_yield2->r10s()); + thread_desc = buf; + } +#endif + + string objs_desc; + _srs_pps_objs_rtps->update(); + _srs_pps_objs_rraw->update(); + _srs_pps_objs_rfua->update(); + _srs_pps_objs_rbuf->update(); + _srs_pps_objs_msgs->update(); + _srs_pps_objs_rothers->update(); + if (_srs_pps_objs_rtps->r10s() || _srs_pps_objs_rraw->r10s() || _srs_pps_objs_rfua->r10s() || _srs_pps_objs_rbuf->r10s() || _srs_pps_objs_msgs->r10s() || _srs_pps_objs_rothers->r10s()) { + snprintf(buf, sizeof(buf), ", objs=(pkt:%d,raw:%d,fua:%d,msg:%d,oth:%d,buf:%d)", + _srs_pps_objs_rtps->r10s(), _srs_pps_objs_rraw->r10s(), _srs_pps_objs_rfua->r10s(), + _srs_pps_objs_msgs->r10s(), _srs_pps_objs_rothers->r10s(), _srs_pps_objs_rbuf->r10s()); + objs_desc = buf; + } + + srs_trace("Hybrid cpu=%.2f%%,%dMB%s%s%s%s%s%s%s%s%s%s%s", + u->percent * 100, memory, + cid_desc.c_str(), timer_desc.c_str(), + recvfrom_desc.c_str(), io_desc.c_str(), msg_desc.c_str(), + epoll_desc.c_str(), sched_desc.c_str(), clock_desc.c_str(), + thread_desc.c_str(), free_desc.c_str(), objs_desc.c_str()); + return err; } - -void SrsServerAdapter::stop() -{ - srs->stop(); -} - -SrsServer *SrsServerAdapter::instance() -{ - return srs; -} diff --git a/trunk/src/app/srs_app_server.hpp b/trunk/src/app/srs_app_server.hpp index 1d4c63514..8f256fb11 100644 --- a/trunk/src/app/srs_app_server.hpp +++ b/trunk/src/app/srs_app_server.hpp @@ -15,13 +15,25 @@ #include #include #include -#include #include #include #include #include #include +#ifdef SRS_SRT +#include +#include +#endif + +class SrsAsyncCallWorker; +class SrsUdpMuxListener; +class SrsUdpMuxSocket; +class SrsRtcUserConfig; +class SrsSdp; +class SrsRtcConnection; +class ISrsAsyncCallTask; + class SrsServer; class ISrsHttpServeMux; class SrsHttpServer; @@ -36,13 +48,28 @@ class SrsTcpListener; class SrsAppCasterFlv; class SrsResourceManager; class SrsLatestVersion; -class SrsWaitGroup; class SrsMultipleTcpListeners; class SrsHttpFlvListener; class SrsUdpCasterListener; class SrsGbListener; class SrsRtmpTransport; class SrsRtmpsTransport; +class SrsSrtAcceptor; +class SrsSrtEventLoop; + +// Initialize global shared variables cross all threads. +extern srs_error_t srs_global_initialize(); + +// Interface for SRT client acceptance +class ISrsSrtClientHandler +{ +public: + ISrsSrtClientHandler(); + virtual ~ISrsSrtClientHandler(); + +public: + virtual srs_error_t accept_srt_client(srs_srt_t srt_fd); +}; // Convert signal to io, // @see: st-1.9/docs/notes.html @@ -97,29 +124,39 @@ public: virtual srs_error_t cycle(); }; -// TODO: FIXME: Rename to SrsLiveServer. // SRS RTMP server, initialize and listen, start connection service thread, destroy client. -class SrsServer : public ISrsReloadHandler, public ISrsLiveSourceHandler, public ISrsTcpHandler, public ISrsResourceManager, public ISrsCoroutineHandler, public ISrsHourGlass +class SrsServer : public ISrsReloadHandler, // Reload framework for permormance optimization. + public ISrsLiveSourceHandler, + public ISrsTcpHandler, + public ISrsHourGlass, + public ISrsSrtClientHandler, + public ISrsUdpMuxHandler, + public ISrsFastTimer { private: // TODO: FIXME: Extract an HttpApiServer. - ISrsHttpServeMux *http_api_mux; - SrsHttpServer *http_server; + ISrsHttpServeMux *http_api_mux_; + SrsHttpServer *http_server_; private: - SrsHttpHeartbeat *http_heartbeat; - SrsIngester *ingester; - SrsResourceManager *conn_manager; - SrsCoroutine *trd_; + SrsHttpHeartbeat *http_heartbeat_; + SrsIngester *ingester_; SrsHourGlass *timer_; - SrsWaitGroup *wg_; + +private: + // Global shared timers moved from SrsHybridServer + SrsFastTimer *timer20ms_; + SrsFastTimer *timer100ms_; + SrsFastTimer *timer1s_; + SrsFastTimer *timer5s_; + SrsClockWallMonitor *clock_monitor_; private: // The pid file fd, lock the file write when server is running. // @remark the init.d script should cleanup the pid file, when stop service, // for the server never delete the file; when system startup, the pid in pid file // maybe valid but the process is not SRS, the init.d script will never start server. - int pid_fd; + int pid_fd_; private: // If reusing, HTTP API use the same port of HTTP server. @@ -157,19 +194,28 @@ private: // Stream Caster for GB28181. SrsGbListener *stream_caster_gb28181_; #endif +#ifdef SRS_SRT + // SRT acceptors for MPEG-TS over SRT. + std::vector srt_acceptors_; +#endif + // WebRTC UDP listeners for RTC server functionality. + std::vector rtc_listeners_; + // WebRTC async call worker for non-blocking operations. + SrsAsyncCallWorker *rtc_async_; + private: // Signal manager which convert gignal to io message. - SrsSignalManager *signal_manager; + SrsSignalManager *signal_manager_; // To query the latest available version of SRS. SrsLatestVersion *latest_version_; // User send the signal, convert to variable. - bool signal_reload; - bool signal_persistence_config; - bool signal_gmc_stop; - bool signal_fast_quit; - bool signal_gracefully_quit; + bool signal_reload_; + bool signal_persistence_config_; + bool signal_gmc_stop_; + bool signal_fast_quit_; + bool signal_gracefully_quit_; // Parent pid for asprocess. - int ppid; + int ppid_; public: SrsServer(); @@ -186,11 +232,21 @@ private: // Close listener to stop accepting new connections, // then wait and quit when all connections finished. virtual void gracefully_dispose(); + // server startup workflow, @see run_master() public: // Initialize server with callback handler ch. // @remark user must free the handler. virtual srs_error_t initialize(); + +private: + // Require the PID file for the whole process. + virtual srs_error_t acquire_pid_file(); + +public: + srs_error_t run(); + +private: virtual srs_error_t initialize_st(); virtual srs_error_t initialize_signal(); virtual srs_error_t listen(); @@ -199,11 +255,12 @@ public: virtual srs_error_t ingest(); public: - virtual srs_error_t start(SrsWaitGroup *wg); void stop(); + // interface ISrsCoroutineHandler -public: +private: virtual srs_error_t cycle(); + // server utilities. public: // The callback for signal manager got a signal. @@ -226,6 +283,7 @@ private: // update the global static data, for instance, the current time, // the cpu/mem/network statistic. virtual srs_error_t do_cycle(); + // interface ISrsHourGlass private: virtual srs_error_t setup_ticks(); @@ -234,48 +292,65 @@ private: private: // Resample the server kbs. virtual void resample_kbps(); - // For internal only + +#ifdef SRS_SRT + // SRT-related methods + virtual srs_error_t listen_srt_mpegts(); + virtual void close_srt_listeners(); + virtual srs_error_t accept_srt_client(srs_srt_t srt_fd); + virtual srs_error_t srt_fd_to_resource(srs_srt_t srt_fd, ISrsResource **pr); +#endif + + // WebRTC-related methods + virtual srs_error_t listen_rtc_udp(); + + // Interface ISrsUdpMuxHandler public: - // TODO: FIXME: Fetch from hybrid server manager. - virtual ISrsHttpServeMux *api_server(); + virtual srs_error_t on_udp_packet(SrsUdpMuxSocket *skt); + +private: + virtual srs_error_t listen_rtc_api(); + +public: + virtual srs_error_t exec_rtc_async_work(ISrsAsyncCallTask *t); + virtual SrsRtcConnection *find_rtc_session_by_username(const std::string &ufrag); + virtual srs_error_t create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection **psession); + +private: + virtual srs_error_t do_create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection *session); + +private: + virtual srs_error_t srs_update_rtc_sessions(); + // Interface ISrsTcpHandler public: virtual srs_error_t on_tcp_client(ISrsListener *listener, srs_netfd_t stfd); private: virtual srs_error_t do_on_tcp_client(ISrsListener *listener, srs_netfd_t &stfd); - virtual srs_error_t on_before_connection(srs_netfd_t &stfd, const std::string &ip, int port); - // Interface ISrsResourceManager -public: - // A callback for connection to remove itself. - // When connection thread cycle terminated, callback this to delete connection. - // @see SrsTcpConnection.on_thread_stop(). - virtual void remove(ISrsResource *c); - // Interface ISrsReloadHandler. -public: + virtual srs_error_t on_before_connection(const char *label, int fd, const std::string &ip, int port); + // Interface ISrsLiveSourceHandler public: virtual srs_error_t on_publish(ISrsRequest *r); virtual void on_unpublish(ISrsRequest *r); -}; -// The SRS server adapter, the master server. -class SrsServerAdapter : public ISrsHybridServer -{ +public: + // Access to global shared timers + SrsFastTimer *timer20ms(); + SrsFastTimer *timer100ms(); + SrsFastTimer *timer1s(); + SrsFastTimer *timer5s(); + + // interface ISrsFastTimer for statistics reporting private: - SrsServer *srs; - -public: - SrsServerAdapter(); - virtual ~SrsServerAdapter(); - -public: - virtual srs_error_t initialize(); - virtual srs_error_t run(SrsWaitGroup *wg); - virtual void stop(); - -public: - virtual SrsServer *instance(); + virtual srs_error_t on_timer(srs_utime_t interval); }; +// @global main SRS server, for debugging +extern SrsServer *_srs_server; + +// Manager for RTC connections. +extern SrsResourceManager *_srs_conn_manager; + #endif diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index 859a9fc14..94afc9c26 100644 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -19,7 +19,6 @@ using namespace std; #include #include #include -#include #include #include #include @@ -2332,7 +2331,7 @@ srs_error_t SrsLiveSource::on_publish() } // notify the handler. - ISrsLiveSourceHandler *handler = _srs_hybrid->srs()->instance(); + ISrsLiveSourceHandler *handler = _srs_server; srs_assert(handler); if ((err = handler->on_publish(req)) != srs_success) { return srs_error_wrap(err, "handle publish"); @@ -2381,7 +2380,7 @@ void SrsLiveSource::on_unpublish() _source_id = SrsContextId(); // notify the handler. - ISrsLiveSourceHandler *handler = _srs_hybrid->srs()->instance(); + ISrsLiveSourceHandler *handler = _srs_server; srs_assert(handler); SrsStatistic *stat = SrsStatistic::instance(); diff --git a/trunk/src/app/srs_app_srt_conn.cpp b/trunk/src/app/srs_app_srt_conn.cpp index bdeea96a5..529fd7374 100644 --- a/trunk/src/app/srs_app_srt_conn.cpp +++ b/trunk/src/app/srs_app_srt_conn.cpp @@ -152,12 +152,12 @@ srs_error_t SrsSrtRecvThread::get_recv_err() return srs_error_copy(recv_err_); } -SrsMpegtsSrtConn::SrsMpegtsSrtConn(SrsSrtServer *srt_server, srs_srt_t srt_fd, std::string ip, int port) : srt_source_(new SrsSrtSource()) +SrsMpegtsSrtConn::SrsMpegtsSrtConn(ISrsResourceManager *resource_manager, srs_srt_t srt_fd, std::string ip, int port) : srt_source_(new SrsSrtSource()) { // Create a identify for this client. _srs_context->set_id(_srs_context->generate_id()); - srt_server_ = srt_server; + resource_manager_ = resource_manager; srt_fd_ = srt_fd; srt_conn_ = new SrsSrtConnection(srt_fd_); @@ -235,7 +235,7 @@ srs_error_t SrsMpegtsSrtConn::cycle() // Notify manager to remove it. // Note that we create this object, so we use manager to remove it. - srt_server_->remove(this); + resource_manager_->remove(this); // success. if (err == srs_success) { diff --git a/trunk/src/app/srs_app_srt_conn.hpp b/trunk/src/app/srs_app_srt_conn.hpp index 4d21bf154..7a92266e7 100644 --- a/trunk/src/app/srs_app_srt_conn.hpp +++ b/trunk/src/app/srs_app_srt_conn.hpp @@ -80,7 +80,7 @@ private: class SrsMpegtsSrtConn : public ISrsConnection, public ISrsStartable, public ISrsCoroutineHandler, public ISrsExpire { public: - SrsMpegtsSrtConn(SrsSrtServer *srt_server, srs_srt_t srt_fd, std::string ip, int port); + SrsMpegtsSrtConn(ISrsResourceManager *resource_manager, srs_srt_t srt_fd, std::string ip, int port); virtual ~SrsMpegtsSrtConn(); // Interface ISrsResource. public: @@ -125,7 +125,7 @@ private: void http_hooks_on_stop(); private: - SrsSrtServer *srt_server_; + ISrsResourceManager *resource_manager_; srs_srt_t srt_fd_; SrsSrtConnection *srt_conn_; SrsNetworkDelta *delta_; diff --git a/trunk/src/app/srs_app_srt_server.cpp b/trunk/src/app/srs_app_srt_server.cpp index 5913625d2..826c12e1b 100644 --- a/trunk/src/app/srs_app_srt_server.cpp +++ b/trunk/src/app/srs_app_srt_server.cpp @@ -19,10 +19,10 @@ using namespace std; SrsSrtEventLoop *_srt_eventloop = NULL; #endif -SrsSrtAcceptor::SrsSrtAcceptor(SrsSrtServer *srt_server) +SrsSrtAcceptor::SrsSrtAcceptor(ISrsSrtClientHandler *srt_handler) { port_ = 0; - srt_server_ = srt_server; + srt_handler_ = srt_handler; listener_ = NULL; } @@ -132,8 +132,8 @@ srs_error_t SrsSrtAcceptor::on_srt_client(srs_srt_t srt_fd) { srs_error_t err = srs_success; - // Notify srt server to accept srt client, and create new SrsSrtConn on it. - if ((err = srt_server_->accept_srt_client(srt_fd)) != srs_success) { + // Notify srt handler to accept srt client, and create new SrsSrtConn on it. + if ((err = srt_handler_->accept_srt_client(srt_fd)) != srs_success) { srs_warn("accept srt client failed, err is %s", srs_error_desc(err).c_str()); srs_freep(err); } @@ -141,254 +141,6 @@ srs_error_t SrsSrtAcceptor::on_srt_client(srs_srt_t srt_fd) return err; } -SrsSrtServer::SrsSrtServer() -{ - conn_manager_ = new SrsResourceManager("SRT", true); - timer_ = NULL; -} - -SrsSrtServer::~SrsSrtServer() -{ - srs_freep(conn_manager_); - srs_freep(timer_); -} - -srs_error_t SrsSrtServer::initialize() -{ - srs_error_t err = srs_success; - - if (!_srs_config->get_srt_enabled()) { - return err; - } - - if ((err = setup_ticks()) != srs_success) { - return srs_error_wrap(err, "tick"); - } - - return err; -} - -srs_error_t SrsSrtServer::listen() -{ - srs_error_t err = srs_success; - - // Listen mpegts over srt. - if ((err = listen_srt_mpegts()) != srs_success) { - return srs_error_wrap(err, "srt mpegts listen"); - } - - if ((err = conn_manager_->start()) != srs_success) { - return srs_error_wrap(err, "srt connection manager"); - } - - return err; -} - -srs_error_t SrsSrtServer::listen_srt_mpegts() -{ - srs_error_t err = srs_success; - - if (!_srs_config->get_srt_enabled()) { - return err; - } - - // Close all listener for SRT if exists. - close_listeners(); - - // Start listeners for SRT, support multiple addresses including IPv6. - vector srt_listens = _srs_config->get_srt_listens(); - for (int i = 0; i < (int)srt_listens.size(); i++) { - SrsSrtAcceptor *acceptor = new SrsSrtAcceptor(this); - - int port; - string ip; - srs_net_split_for_listener(srt_listens[i], ip, port); - - if ((err = acceptor->listen(ip, port)) != srs_success) { - srs_freep(acceptor); - srs_warn("srt listen %s:%d failed, err=%s", ip.c_str(), port, srs_error_desc(err).c_str()); - srs_error_reset(err); - continue; - } - - acceptors_.push_back(acceptor); - } - - // Check if at least one listener succeeded - if (acceptors_.empty()) { - return srs_error_new(ERROR_SOCKET_LISTEN, "no srt listeners available"); - } - - return err; -} - -void SrsSrtServer::close_listeners() -{ - std::vector::iterator it; - for (it = acceptors_.begin(); it != acceptors_.end();) { - SrsSrtAcceptor *acceptor = *it; - srs_freep(acceptor); - - it = acceptors_.erase(it); - } -} - -srs_error_t SrsSrtServer::accept_srt_client(srs_srt_t srt_fd) -{ - srs_error_t err = srs_success; - - ISrsResource *resource = NULL; - if ((err = fd_to_resource(srt_fd, &resource)) != srs_success) { - // close fd on conn error, otherwise will lead to fd leak -gs - // TODO: FIXME: Handle error. - srs_srt_close(srt_fd); - return srs_error_wrap(err, "srt fd to resource"); - } - srs_assert(resource); - - // directly enqueue, the cycle thread will remove the client. - conn_manager_->add(resource); - - // Note that conn is managed by conn_manager, so we don't need to free it. - ISrsStartable *conn = dynamic_cast(resource); - if ((err = conn->start()) != srs_success) { - return srs_error_wrap(err, "start srt conn coroutine"); - } - - return err; -} - -srs_error_t SrsSrtServer::fd_to_resource(srs_srt_t srt_fd, ISrsResource **pr) -{ - srs_error_t err = srs_success; - - string ip = ""; - int port = 0; - if ((err = srs_srt_get_remote_ip_port(srt_fd, ip, port)) != srs_success) { - return srs_error_wrap(err, "get srt ip port"); - } - - // TODO: FIXME: need to check max connection? - - // The context id may change during creating the bellow objects. - SrsContextRestore(_srs_context->get_id()); - - // Covert to SRT conection. - *pr = new SrsMpegtsSrtConn(this, srt_fd, ip, port); - - return err; -} - -void SrsSrtServer::remove(ISrsResource *c) -{ - // use manager to free it async. - conn_manager_->remove(c); -} - -srs_error_t SrsSrtServer::setup_ticks() -{ - srs_error_t err = srs_success; - - srs_freep(timer_); - timer_ = new SrsHourGlass("srt", this, 1 * SRS_UTIME_SECONDS); - - if (_srs_config->get_stats_enabled()) { - if ((err = timer_->tick(8, 3 * SRS_UTIME_SECONDS)) != srs_success) { - return srs_error_wrap(err, "tick"); - } - } - - if ((err = timer_->start()) != srs_success) { - return srs_error_wrap(err, "timer"); - } - - return err; -} - -srs_error_t SrsSrtServer::notify(int event, srs_utime_t interval, srs_utime_t tick) -{ - srs_error_t err = srs_success; - - switch (event) { - case 8: - resample_kbps(); - break; - } - - return err; -} - -void SrsSrtServer::resample_kbps() -{ - // collect delta from all clients. - for (int i = 0; i < (int)conn_manager_->size(); i++) { - ISrsResource *c = conn_manager_->at(i); - - SrsMpegtsSrtConn *conn = dynamic_cast(c); - srs_assert(conn); - - // add delta of connection to server kbps., - // for next sample() of server kbps can get the stat. - SrsStatistic::instance()->kbps_add_delta(c->get_id().c_str(), conn->delta()); - } -} - -SrsSrtServerAdapter::SrsSrtServerAdapter() -{ - srt_server_ = new SrsSrtServer(); -} - -SrsSrtServerAdapter::~SrsSrtServerAdapter() -{ - srs_freep(srt_server_); -} - -srs_error_t SrsSrtServerAdapter::initialize() -{ - srs_error_t err = srs_success; - - if ((err = srs_srt_log_initialize()) != srs_success) { - return srs_error_wrap(err, "srt log initialize"); - } - - _srt_eventloop = new SrsSrtEventLoop(); - - if ((err = _srt_eventloop->initialize()) != srs_success) { - return srs_error_wrap(err, "srt poller initialize"); - } - - if ((err = _srt_eventloop->start()) != srs_success) { - return srs_error_wrap(err, "srt poller start"); - } - - return err; -} - -srs_error_t SrsSrtServerAdapter::run(SrsWaitGroup *wg) -{ - srs_error_t err = srs_success; - - // Initialize the whole system, set hooks to handle server level events. - if ((err = srt_server_->initialize()) != srs_success) { - return srs_error_wrap(err, "srt server initialize"); - } - - if ((err = srt_server_->listen()) != srs_success) { - return srs_error_wrap(err, "srt listen"); - } - - return err; -} - -void SrsSrtServerAdapter::stop() -{ -} - -SrsSrtServer *SrsSrtServerAdapter::instance() -{ - return srt_server_; -} - SrsSrtEventLoop::SrsSrtEventLoop() { srt_poller_ = NULL; diff --git a/trunk/src/app/srs_app_srt_server.hpp b/trunk/src/app/srs_app_srt_server.hpp index 56ec7a3fc..3881a2e80 100644 --- a/trunk/src/app/srs_app_srt_server.hpp +++ b/trunk/src/app/srs_app_srt_server.hpp @@ -15,6 +15,7 @@ class SrsSrtServer; class SrsHourGlass; +class ISrsSrtClientHandler; // A common srt acceptor, for SRT server. class SrsSrtAcceptor : public ISrsSrtHandler @@ -22,13 +23,13 @@ class SrsSrtAcceptor : public ISrsSrtHandler private: std::string ip_; int port_; - SrsSrtServer *srt_server_; + ISrsSrtClientHandler *srt_handler_; private: SrsSrtListener *listener_; public: - SrsSrtAcceptor(SrsSrtServer *srt_server); + SrsSrtAcceptor(ISrsSrtClientHandler *srt_handler); virtual ~SrsSrtAcceptor(); public: @@ -41,70 +42,6 @@ public: virtual srs_error_t on_srt_client(srs_srt_t srt_fd); }; -// SRS SRT server, initialize and listen, start connection service thread, destroy client. -class SrsSrtServer : public ISrsResourceManager, public ISrsHourGlass -{ -private: - SrsResourceManager *conn_manager_; - SrsHourGlass *timer_; - -private: - std::vector acceptors_; - -public: - SrsSrtServer(); - virtual ~SrsSrtServer(); - -public: - virtual srs_error_t initialize(); - virtual srs_error_t listen(); - -private: - // listen at specified srt protocol. - virtual srs_error_t listen_srt_mpegts(); - // Close the listeners and remove the listen object from manager. - virtual void close_listeners(); - // For internal only -public: - // When listener got a fd, notice server to accept it. - // @param srt_fd, the client fd in srt boxed, the underlayer fd. - virtual srs_error_t accept_srt_client(srs_srt_t srt_fd); - -private: - virtual srs_error_t fd_to_resource(srs_srt_t srt_fd, ISrsResource **pr); - // Interface ISrsResourceManager -public: - // A callback for connection to remove itself. - // When connection thread cycle terminated, callback this to delete connection. - virtual void remove(ISrsResource *c); - // interface ISrsHourGlass -private: - virtual srs_error_t setup_ticks(); - virtual srs_error_t notify(int event, srs_utime_t interval, srs_utime_t tick); - -private: - virtual void resample_kbps(); -}; - -// The srt server adapter, the master server. -class SrsSrtServerAdapter : public ISrsHybridServer -{ -private: - SrsSrtServer *srt_server_; - -public: - SrsSrtServerAdapter(); - virtual ~SrsSrtServerAdapter(); - -public: - virtual srs_error_t initialize(); - virtual srs_error_t run(SrsWaitGroup *wg); - virtual void stop(); - -public: - virtual SrsSrtServer *instance(); -}; - // Start a coroutine to drive the SRT events with state-threads. class SrsSrtEventLoop : public ISrsCoroutineHandler { diff --git a/trunk/src/core/srs_core_version7.hpp b/trunk/src/core/srs_core_version7.hpp index 3d3c38b41..09ee946e4 100644 --- a/trunk/src/core/srs_core_version7.hpp +++ b/trunk/src/core/srs_core_version7.hpp @@ -9,6 +9,6 @@ #define VERSION_MAJOR 7 #define VERSION_MINOR 0 -#define VERSION_REVISION 67 +#define VERSION_REVISION 68 #endif \ No newline at end of file diff --git a/trunk/src/main/srs_main_ingest_hls.cpp b/trunk/src/main/srs_main_ingest_hls.cpp deleted file mode 100644 index 1f0251482..000000000 --- a/trunk/src/main/srs_main_ingest_hls.cpp +++ /dev/null @@ -1,1405 +0,0 @@ -// -// Copyright (c) 2013-2025 The SRS Authors -// -// SPDX-License-Identifier: MIT -// - -#include - -#include -#include -#include -#include -using namespace std; - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// pre-declare -srs_error_t proxy_hls2rtmp(std::string hls, std::string rtmp); - -// @global log and context. -ISrsLog *_srs_log = new SrsConsoleLog(SrsLogLevelTrace, false); -ISrsContext *_srs_context = new SrsThreadContext(); - -// @global config object for app module. -SrsConfig *_srs_config = new SrsConfig(); - -// @global Other variables. -bool _srs_in_docker = false; - -// Whether setup config by environment variables, see https://github.com/ossrs/srs/issues/2277 -bool _srs_config_by_env = false; - -// The binary name of SRS. -const char *_srs_binary = NULL; - -/** - * main entrance. - */ -int main(int argc, char **argv) -{ - // TODO: support both little and big endian. - srs_assert(srs_is_little_endian()); - - _srs_binary = argv[0]; - - // directly failed when compile limited. -#if defined(SRS_GPERF_MP) || defined(SRS_GPERF_MP) || defined(SRS_GPERF_MC) || defined(SRS_GPERF_MP) - srs_error("donot support gmc/gmp/gcp/gprof"); - exit(-1); -#endif - - srs_error_t err = srs_success; - if ((err = srs_global_initialize()) != srs_success) { - srs_freep(err); - srs_error("global init error"); - return -1; - } - - srs_trace("srs_ingest_hls base on %s, to ingest hls live to srs", RTMP_SIG_SRS_SERVER); - - // parse user options. - std::string in_hls_url, out_rtmp_url; - for (int opt = 0; opt < argc; opt++) { - srs_trace("argv[%d]=%s", opt, argv[opt]); - } - - // fill the options for mac - for (int opt = 0; opt < argc - 1; opt++) { - // ignore all options except -i and -y. - char *p = argv[opt]; - - // only accept -x - if (p[0] != '-' || p[1] == 0 || p[2] != 0) { - continue; - } - - // parse according the option name. - switch (p[1]) { - case 'i': - in_hls_url = argv[opt + 1]; - break; - case 'y': - out_rtmp_url = argv[opt + 1]; - break; - default: - break; - } - } - - if (in_hls_url.empty() || out_rtmp_url.empty()) { - printf("ingest hls live stream and publish to RTMP server\n" - "Usage: %s <-i in_hls_url> <-y out_rtmp_url>\n" - " in_hls_url input hls url, ingest from this m3u8.\n" - " out_rtmp_url output rtmp url, publish to this url.\n" - "For example:\n" - " %s -i http://127.0.0.1:8080/live/livestream.m3u8 -y rtmp://127.0.0.1/live/ingest_hls\n" - " %s -i http://ossrs.net/live/livestream.m3u8 -y rtmp://127.0.0.1/live/ingest_hls\n", - argv[0], argv[0], argv[0]); - exit(-1); - } - - srs_trace("input: %s", in_hls_url.c_str()); - srs_trace("output: %s", out_rtmp_url.c_str()); - - err = proxy_hls2rtmp(in_hls_url, out_rtmp_url); - - int ret = srs_error_code(err); - srs_freep(err); - return ret; -} - -class ISrsAacHandler -{ -public: - /** - * handle the aac frame, which in ADTS format(starts with FFFx). - * @param duration the duration in seconds of frames. - */ - virtual int on_aac_frame(char *frame, int frame_size, double duration) = 0; -}; - -// the context to ingest hls stream. -class SrsIngestHlsInput -{ -private: - struct SrsTsPiece { - double duration; - std::string url; - std::string body; - - // should skip this ts? - bool skip; - // already sent to rtmp server? - bool sent; - // whether ts piece is dirty, remove if not update. - bool dirty; - - SrsTsPiece() - { - skip = false; - sent = false; - dirty = false; - duration = 0.0; - } - - int fetch(std::string m3u8); - }; - -private: - SrsHttpUri *in_hls; - std::vector pieces; - srs_utime_t next_connect_time; - -private: - SrsTsContext *context; - -public: - SrsIngestHlsInput(SrsHttpUri *hls) - { - in_hls = hls; - next_connect_time = 0; - context = new SrsTsContext(); - } - virtual ~SrsIngestHlsInput() - { - srs_freep(context); - - std::vector::iterator it; - for (it = pieces.begin(); it != pieces.end(); ++it) { - SrsTsPiece *tp = *it; - srs_freep(tp); - } - pieces.clear(); - } - /** - * parse the input hls live m3u8 index. - */ - virtual int connect(); - /** - * parse the ts and use hanler to process the message. - */ - virtual int parse(ISrsTsHandler *ts, ISrsAacHandler *aac); - -private: - /** - * parse the ts pieces body. - */ - virtual int parseAac(ISrsAacHandler *handler, char *body, int nb_body, double duration); - virtual int parseTs(ISrsTsHandler *handler, char *body, int nb_body); - /** - * parse the m3u8 specified by url. - */ - virtual int parseM3u8(SrsHttpUri *url, double &td, double &duration); - /** - * find the ts piece by its url. - */ - virtual SrsTsPiece *find_ts(string url); - /** - * set all ts to dirty. - */ - virtual void dirty_all_ts(); - /** - * fetch all ts body. - */ - virtual int fetch_all_ts(bool fresh_m3u8); - /** - * remove all ts which is dirty. - */ - virtual void remove_dirty(); -}; - -int SrsIngestHlsInput::connect() -{ - int ret = ERROR_SUCCESS; - - srs_utime_t now = srs_time_now_realtime(); - if (now < next_connect_time) { - srs_trace("input hls wait for %dms", srsu2msi(next_connect_time - now)); - srs_usleep(next_connect_time - now); - } - - // set all ts to dirty. - dirty_all_ts(); - - bool fresh_m3u8 = pieces.empty(); - double td = 0.0; - double duration = 0.0; - if ((ret = parseM3u8(in_hls, td, duration)) != ERROR_SUCCESS) { - return ret; - } - - // fetch all ts. - if ((ret = fetch_all_ts(fresh_m3u8)) != ERROR_SUCCESS) { - srs_error("fetch all ts failed. ret=%d", ret); - return ret; - } - - // remove all dirty ts. - remove_dirty(); - - srs_trace("fetch m3u8 ok, td=%.2f, duration=%.2f, pieces=%d", td, duration, pieces.size()); - - return ret; -} - -int SrsIngestHlsInput::parse(ISrsTsHandler *ts, ISrsAacHandler *aac) -{ - int ret = ERROR_SUCCESS; - - for (int i = 0; i < (int)pieces.size(); i++) { - SrsTsPiece *tp = pieces.at(i); - - // sent only once. - if (tp->sent) { - continue; - } - tp->sent = true; - - if (tp->body.empty()) { - continue; - } - - srs_trace("proxy the ts to rtmp, ts=%s, duration=%.2f", tp->url.c_str(), tp->duration); - - if (srs_strings_ends_with(tp->url, ".ts")) { - if ((ret = parseTs(ts, (char *)tp->body.data(), (int)tp->body.length())) != ERROR_SUCCESS) { - return ret; - } - } else if (srs_strings_ends_with(tp->url, ".aac")) { - if ((ret = parseAac(aac, (char *)tp->body.data(), (int)tp->body.length(), tp->duration)) != ERROR_SUCCESS) { - return ret; - } - } else { - srs_warn("ignore unkown piece %s", tp->url.c_str()); - } - } - - return ret; -} - -int SrsIngestHlsInput::parseTs(ISrsTsHandler *handler, char *body, int nb_body) -{ - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - // use stream to parse ts packet. - int nb_packet = (int)nb_body / SRS_TS_PACKET_SIZE; - for (int i = 0; i < nb_packet; i++) { - char *p = (char *)body + (i * SRS_TS_PACKET_SIZE); - SrsUniquePtr stream(new SrsBuffer(p, SRS_TS_PACKET_SIZE)); - - // process each ts packet - if ((err = context->decode(stream.get(), handler)) != srs_success) { - // TODO: FIXME: Use error - ret = srs_error_code(err); - srs_freep(err); - srs_error("mpegts: ignore parse ts packet failed. ret=%d", ret); - return ret; - } - srs_info("mpegts: parse ts packet completed"); - } - srs_info("mpegts: parse udp packet completed"); - - return ret; -} - -int SrsIngestHlsInput::parseAac(ISrsAacHandler *handler, char *body, int nb_body, double duration) -{ - int ret = ERROR_SUCCESS; - - SrsUniquePtr stream(new SrsBuffer(body, nb_body)); - - // atleast 2bytes. - if (!stream->require(3)) { - ret = ERROR_AAC_BYTES_INVALID; - srs_error("invalid aac, atleast 3bytes. ret=%d", ret); - return ret; - } - - uint8_t id0 = (uint8_t)body[0]; - uint8_t id1 = (uint8_t)body[1]; - uint8_t id2 = (uint8_t)body[2]; - - // skip ID3. - if (id0 == 0x49 && id1 == 0x44 && id2 == 0x33) { - /*char id3[] = { - (char)0x49, (char)0x44, (char)0x33, // ID3 - (char)0x03, (char)0x00, // version - (char)0x00, // flags - (char)0x00, (char)0x00, (char)0x00, (char)0x0a, // size - - (char)0x00, (char)0x00, (char)0x00, (char)0x00, // FrameID - (char)0x00, (char)0x00, (char)0x00, (char)0x00, // FrameSize - (char)0x00, (char)0x00 // Flags - };*/ - // atleast 10 bytes. - if (!stream->require(10)) { - ret = ERROR_AAC_BYTES_INVALID; - srs_error("invalid aac ID3, atleast 10bytes. ret=%d", ret); - return ret; - } - - // ignore ID3 + version + flag. - stream->skip(6); - // read the size of ID3. - uint32_t nb_id3 = stream->read_4bytes(); - - // read body of ID3 - if (!stream->require(nb_id3)) { - ret = ERROR_AAC_BYTES_INVALID; - srs_error("invalid aac ID3 body, required %dbytes. ret=%d", nb_id3, ret); - return ret; - } - stream->skip(nb_id3); - } - - char *frame = body + stream->pos(); - int frame_size = nb_body - stream->pos(); - return handler->on_aac_frame(frame, frame_size, duration); -} - -int SrsIngestHlsInput::parseM3u8(SrsHttpUri *url, double &td, double &duration) -{ - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - SrsHttpClient client; - srs_trace("parse input hls %s", url->get_url().c_str()); - - if ((err = client.initialize(url->get_schema(), url->get_host(), url->get_port())) != srs_success) { - // TODO: FIXME: Use error - ret = srs_error_code(err); - srs_freep(err); - srs_error("connect to server failed. ret=%d", ret); - return ret; - } - - ISrsHttpMessage *msg_raw = NULL; - if ((err = client.get(url->get_path(), "", &msg_raw)) != srs_success) { - // TODO: FIXME: Use error - ret = srs_error_code(err); - srs_freep(err); - srs_error("HTTP GET %s failed. ret=%d", url->get_url().c_str(), ret); - return ret; - } - - srs_assert(msg_raw); - SrsUniquePtr msg(msg_raw); - - std::string body; - if ((err = msg->body_read_all(body)) != srs_success) { - // TODO: FIXME: Use error - ret = srs_error_code(err); - srs_freep(err); - srs_error("read m3u8 failed. ret=%d", ret); - return ret; - } - - if (body.empty()) { - srs_warn("ignore empty m3u8"); - return ret; - } - - std::string ptl; - while (!body.empty()) { - size_t pos = string::npos; - - std::string line; - if ((pos = body.find("\n")) != string::npos) { - line = body.substr(0, pos); - body = body.substr(pos + 1); - } else { - line = body; - body = ""; - } - - line = srs_strings_replace(line, "\r", ""); - line = srs_strings_replace(line, " ", ""); - - // #EXT-X-VERSION:3 - // the version must be 3.0 - if (srs_strings_starts_with(line, "#EXT-X-VERSION:")) { - if (!srs_strings_ends_with(line, ":3")) { - srs_warn("m3u8 3.0 required, actual is %s", line.c_str()); - } - continue; - } - - // #EXT-X-PLAYLIST-TYPE:VOD - // the playlist type, vod or nothing. - if (srs_strings_starts_with(line, "#EXT-X-PLAYLIST-TYPE:")) { - ptl = line; - continue; - } - - // #EXT-X-TARGETDURATION:12 - // the target duration is required. - if (srs_strings_starts_with(line, "#EXT-X-TARGETDURATION:")) { - td = ::atof(line.substr(string("#EXT-X-TARGETDURATION:").length()).c_str()); - } - - // #EXT-X-ENDLIST - // parse completed. - if (line == "#EXT-X-ENDLIST") { - break; - } - - // #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=73207,CODECS="mp4a.40.2" - if (srs_strings_starts_with(line, "#EXT-X-STREAM-INF:")) { - if ((pos = body.find("\n")) == string::npos) { - srs_warn("m3u8 entry unexpected eof, inf=%s", line.c_str()); - break; - } - - std::string m3u8_url = body.substr(0, pos); - body = body.substr(pos + 1); - - if (!srs_net_url_is_http(m3u8_url)) { - m3u8_url = srs_path_filepath_dir(url->get_url()) + "/" + m3u8_url; - } - srs_trace("parse sub m3u8, url=%s", m3u8_url.c_str()); - - if ((err = url->initialize(m3u8_url)) != srs_success) { - // TODO: FIXME: Use error - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - return parseM3u8(url, td, duration); - } - - // #EXTINF:11.401, - // livestream-5.ts - // parse each ts entry, expect current line is inf. - if (!srs_strings_starts_with(line, "#EXTINF:")) { - continue; - } - - // expect next line is url. - std::string ts_url; - if ((pos = body.find("\n")) != string::npos) { - ts_url = body.substr(0, pos); - body = body.substr(pos + 1); - } else { - srs_warn("ts entry unexpected eof, inf=%s", line.c_str()); - break; - } - - // parse the ts duration. - line = line.substr(string("#EXTINF:").length()); - if ((pos = line.find(",")) != string::npos) { - line = line.substr(0, pos); - } - - double ts_duration = ::atof(line.c_str()); - duration += ts_duration; - - SrsTsPiece *tp = find_ts(ts_url); - if (!tp) { - tp = new SrsTsPiece(); - tp->url = ts_url; - tp->duration = ts_duration; - pieces.push_back(tp); - } else { - tp->dirty = false; - } - } - - return ret; -} - -SrsIngestHlsInput::SrsTsPiece *SrsIngestHlsInput::find_ts(string url) -{ - std::vector::iterator it; - for (it = pieces.begin(); it != pieces.end(); ++it) { - SrsTsPiece *tp = *it; - if (tp->url == url) { - return tp; - } - } - return NULL; -} - -void SrsIngestHlsInput::dirty_all_ts() -{ - std::vector::iterator it; - for (it = pieces.begin(); it != pieces.end(); ++it) { - SrsTsPiece *tp = *it; - tp->dirty = true; - } -} - -int SrsIngestHlsInput::fetch_all_ts(bool fresh_m3u8) -{ - int ret = ERROR_SUCCESS; - - for (int i = 0; i < (int)pieces.size(); i++) { - SrsTsPiece *tp = pieces.at(i); - - // when skipped, ignore. - if (tp->skip) { - continue; - } - - // for the fresh m3u8, skip except the last one. - if (fresh_m3u8 && i != (int)pieces.size() - 1) { - tp->skip = true; - continue; - } - - if ((ret = tp->fetch(in_hls->get_url())) != ERROR_SUCCESS) { - srs_error("fetch ts %s for error. ret=%d", tp->url.c_str(), ret); - tp->skip = true; - return ret; - } - - // only wait for a duration of last piece. - if (i == (int)pieces.size() - 1) { - next_connect_time = srs_time_now_realtime() + tp->duration * SRS_UTIME_SECONDS; - } - } - - return ret; -} - -void SrsIngestHlsInput::remove_dirty() -{ - std::vector::iterator it; - for (it = pieces.begin(); it != pieces.end();) { - SrsTsPiece *tp = *it; - - if (tp->dirty) { - srs_trace("erase dirty ts, url=%s, duration=%.2f", tp->url.c_str(), tp->duration); - srs_freep(tp); - it = pieces.erase(it); - } else { - ++it; - } - } -} - -int SrsIngestHlsInput::SrsTsPiece::fetch(string m3u8) -{ - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - if (skip || sent || !body.empty()) { - return ret; - } - - SrsHttpClient client; - - std::string ts_url = url; - if (!srs_net_url_is_http(ts_url)) { - ts_url = srs_path_filepath_dir(m3u8) + "/" + url; - } - - SrsHttpUri uri; - if ((err = uri.initialize(ts_url)) != srs_success) { - // TODO: FIXME: Use error - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - // initialize the fresh http client. - if ((ret = client.initialize(uri.get_schema(), uri.get_host(), uri.get_port()) != srs_success)) { - return ret; - } - - ISrsHttpMessage *msg_raw = NULL; - if ((err = client.get(uri.get_path(), "", &msg_raw)) != srs_success) { - // TODO: FIXME: Use error - ret = srs_error_code(err); - srs_freep(err); - srs_error("HTTP GET %s failed. ret=%d", uri.get_url().c_str(), ret); - return ret; - } - - srs_assert(msg_raw); - SrsUniquePtr msg(msg_raw); - - if ((err = msg->body_read_all(body)) != srs_success) { - // TODO: FIXME: Use error - ret = srs_error_code(err); - srs_freep(err); - srs_error("read ts failed. ret=%d", ret); - return ret; - } - - srs_trace("fetch ts ok, duration=%.2f, url=%s, body=%dB", duration, url.c_str(), body.length()); - - return ret; -} - -// the context to output to rtmp server -class SrsIngestHlsOutput : public ISrsTsHandler, public ISrsAacHandler -{ -private: - SrsHttpUri *out_rtmp; - -private: - bool disconnected; - std::multimap queue; - int64_t raw_aac_dts; - -private: - ISrsRequest *req; - SrsBasicRtmpClient *sdk; - -private: - SrsRawH264Stream *avc; - std::string h264_sps; - bool h264_sps_changed; - std::string h264_pps; - bool h264_pps_changed; - bool h264_sps_pps_sent; - -private: - SrsRawAacStream *aac; - std::string aac_specific_config; - -public: - SrsIngestHlsOutput(SrsHttpUri *rtmp); - virtual ~SrsIngestHlsOutput(); - // Interface ISrsTsHandler -public: - virtual srs_error_t on_ts_message(SrsTsMessage *msg); - // Interface IAacHandler -public: - virtual int on_aac_frame(char *frame, int frame_size, double duration); - -private: - virtual int do_on_aac_frame(SrsBuffer *avs, double duration); - virtual int parse_message_queue(); - virtual int on_ts_video(SrsTsMessage *msg, SrsBuffer *avs); - virtual int write_h264_sps_pps(uint32_t dts, uint32_t pts); - virtual int write_h264_ipb_frame(std::string ibps, SrsVideoAvcFrameType frame_type, uint32_t dts, uint32_t pts); - virtual int on_ts_audio(SrsTsMessage *msg, SrsBuffer *avs); - virtual int write_audio_raw_frame(char *frame, int frame_size, SrsRawAacStreamCodec *codec, uint32_t dts); - -private: - virtual int rtmp_write_packet(char type, uint32_t timestamp, char *data, int size); - -public: - /** - * connect to output rtmp server. - */ - virtual int connect(); - /** - * flush the message queue when all ts parsed. - */ - virtual int flush_message_queue(); - -private: - // close the connected io and rtmp to ready to be re-connect. - virtual void close(); -}; - -SrsIngestHlsOutput::SrsIngestHlsOutput(SrsHttpUri *rtmp) -{ - out_rtmp = rtmp; - disconnected = false; - raw_aac_dts = srsu2ms(srs_time_now_realtime()); - - req = NULL; - sdk = NULL; - - avc = new SrsRawH264Stream(); - aac = new SrsRawAacStream(); - h264_sps_changed = false; - h264_pps_changed = false; - h264_sps_pps_sent = false; -} - -SrsIngestHlsOutput::~SrsIngestHlsOutput() -{ - close(); - - srs_freep(avc); - srs_freep(aac); - - std::multimap::iterator it; - for (it = queue.begin(); it != queue.end(); ++it) { - SrsTsMessage *msg = it->second; - srs_freep(msg); - } - queue.clear(); -} - -srs_error_t SrsIngestHlsOutput::on_ts_message(SrsTsMessage *msg) -{ - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - // about the bytes of msg, specified by elementary stream which indicates by PES_packet_data_byte and stream_id - // for example, when SrsTsStream of SrsTsChannel indicates stream_type is SrsTsStreamVideoMpeg4 and SrsTsStreamAudioMpeg4, - // the elementary stream can be mux in "2.11 Carriage of ISO/IEC 14496 data" in hls-mpeg-ts-iso13818-1.pdf, page 103 - // @remark, the most popular stream_id is 0xe0 for h.264 over mpegts, which indicates the stream_id is video and - // stream_number is 0, where I guess the elementary is specified in annexb format(ISO_IEC_14496-10-AVC-2003.pdf, page 211). - // because when audio stream_number is 0, the elementary is ADTS(ISO_IEC_14496-3-AAC-2001.pdf, page 75, 1.A.2.2 ADTS). - - // about the bytes of PES_packet_data_byte, defined in hls-mpeg-ts-iso13818-1.pdf, page 58 - // PES_packet_data_byte "C PES_packet_data_bytes shall be contiguous bytes of data from the elementary stream - // indicated by the packet¡¯s stream_id or PID. When the elementary stream data conforms to ITU-T - // Rec. H.262 | ISO/IEC 13818-2 or ISO/IEC 13818-3, the PES_packet_data_bytes shall be byte aligned to the bytes of this - // Recommendation | International Standard. The byte-order of the elementary stream shall be preserved. The number of - // PES_packet_data_bytes, N, is specified by the PES_packet_length field. N shall be equal to the value indicated in the - // PES_packet_length minus the number of bytes between the last byte of the PES_packet_length field and the first - // PES_packet_data_byte. - // - // In the case of a private_stream_1, private_stream_2, ECM_stream, or EMM_stream, the contents of the - // PES_packet_data_byte field are user definable and will not be specified by ITU-T | ISO/IEC in the future. - - // about the bytes of stream_id, define in hls-mpeg-ts-iso13818-1.pdf, page 49 - // stream_id "C In Program Streams, the stream_id specifies the type and number of the elementary stream as defined by the - // stream_id Table 2-18. In Transport Streams, the stream_id may be set to any valid value which correctly describes the - // elementary stream type as defined in Table 2-18. In Transport Streams, the elementary stream type is specified in the - // Program Specific Information as specified in 2.4.4. - - // about the stream_id table, define in Table 2-18 "C Stream_id assignments, hls-mpeg-ts-iso13818-1.pdf, page 52. - // - // 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 - // ((sid >> 5) & 0x07) == SrsTsPESStreamIdAudio - // - // 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) == SrsTsPESStreamIdVideo - - srs_info("<- " SRS_CONSTS_LOG_STREAM_CASTER " mpegts: got %s stream=%s, dts=%" PRId64 ", pts=%" PRId64 ", size=%d, us=%d, cc=%d, sid=%#x(%s-%d)", - (msg->channel->apply == SrsTsPidApplyVideo) ? "Video" : "Audio", srs_ts_stream2string(msg->channel->stream).c_str(), - msg->dts, msg->pts, msg->payload->length(), msg->packet->payload_unit_start_indicator, msg->continuity_counter, msg->sid, - msg->is_audio() ? "A" : msg->is_video() ? "V" - : "N", - msg->stream_number()); - - // When the audio SID is private stream 1, we use common audio. - // @see https://github.com/ossrs/srs/issues/740 - if (msg->channel->apply == SrsTsPidApplyAudio && msg->sid == SrsTsPESStreamIdPrivateStream1) { - msg->sid = SrsTsPESStreamIdAudioCommon; - } - - // when not audio/video, or not adts/annexb format, donot support. - if (msg->stream_number() != 0) { - return srs_error_new(ERROR_STREAM_CASTER_TS_ES, "ts: unsupported stream format, sid=%#x(%s-%d)", - msg->sid, msg->is_audio() ? "A" : msg->is_video() ? "V" - : "N", - msg->stream_number()); - } - - // check supported codec - if (msg->channel->stream != SrsTsStreamVideoH264 && msg->channel->stream != SrsTsStreamAudioAAC) { - return srs_error_new(ERROR_STREAM_CASTER_TS_CODEC, "ts: unsupported stream codec=%d", msg->channel->stream); - } - - // we must use queue to cache the msg, then parse it if possible. - queue.insert(std::make_pair(msg->dts, msg->detach())); - if ((ret = parse_message_queue()) != ERROR_SUCCESS) { - return srs_error_new(ret, "ts: parse message"); - } - - return err; -} - -int SrsIngestHlsOutput::on_aac_frame(char *frame, int frame_size, double duration) -{ - srs_trace("handle aac frames, size=%dB, duration=%.2f, dts=%" PRId64, frame_size, duration, raw_aac_dts); - - SrsBuffer stream(frame, frame_size); - return do_on_aac_frame(&stream, duration); -} - -int SrsIngestHlsOutput::do_on_aac_frame(SrsBuffer *avs, double duration) -{ - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - uint32_t duration_ms = (uint32_t)(duration * 1000); - - // ts tbn to flv tbn. - uint32_t dts = (uint32_t)raw_aac_dts; - raw_aac_dts += duration_ms; - - // got the next msg to calc the delta duration for each audio. - uint32_t max_dts = dts + duration_ms; - - // send each frame. - while (!avs->empty()) { - char *frame = NULL; - int frame_size = 0; - SrsRawAacStreamCodec codec; - if ((err = aac->adts_demux(avs, &frame, &frame_size, codec)) != srs_success) { - // TODO: FIXME: Use error - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - // ignore invalid frame, - // * atleast 1bytes for aac to decode the data. - if (frame_size <= 0) { - continue; - } - srs_info("mpegts: demux aac frame size=%d, dts=%d", frame_size, dts); - - // generate sh. - if (aac_specific_config.empty()) { - std::string sh; - if ((err = aac->mux_sequence_header(&codec, sh)) != srs_success) { - // TODO: FIXME: Use error - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - aac_specific_config = sh; - - codec.aac_packet_type = 0; - - if ((ret = write_audio_raw_frame((char *)sh.data(), (int)sh.length(), &codec, dts)) != ERROR_SUCCESS) { - return ret; - } - } - - // audio raw data. - codec.aac_packet_type = 1; - if ((ret = write_audio_raw_frame(frame, frame_size, &codec, dts)) != ERROR_SUCCESS) { - return ret; - } - - // calc the delta of dts, when previous frame output. - uint32_t delta = duration_ms / (avs->size() / frame_size); - dts = (uint32_t)(srs_min(max_dts, dts + delta)); - } - - return ret; -} - -int SrsIngestHlsOutput::parse_message_queue() -{ - int ret = ERROR_SUCCESS; - - if (queue.empty()) { - return ret; - } - - SrsTsMessage *first_ts_msg = queue.begin()->second; - SrsTsContext *context = first_ts_msg->channel->context; - bool cpa = context->is_pure_audio(); - - int nb_videos = 0; - if (!cpa) { - std::multimap::iterator it; - for (it = queue.begin(); it != queue.end(); ++it) { - SrsTsMessage *msg = it->second; - - // publish audio or video. - if (msg->channel->stream == SrsTsStreamVideoH264) { - nb_videos++; - } - } - - // always wait 2+ videos, to left one video in the queue. - // TODO: FIXME: support pure audio hls. - if (nb_videos <= 1) { - return ret; - } - } - - // parse messages util the last video. - while ((cpa && queue.size() > 1) || nb_videos > 1) { - srs_assert(!queue.empty()); - std::multimap::iterator it = queue.begin(); - - SrsUniquePtr msg(it->second); - queue.erase(it); - - if (msg->channel->stream == SrsTsStreamVideoH264) { - nb_videos--; - } - - // parse the stream. - SrsBuffer avs(msg->payload->bytes(), msg->payload->length()); - - // publish audio or video. - if (msg->channel->stream == SrsTsStreamVideoH264) { - if ((ret = on_ts_video(msg.get(), &avs)) != ERROR_SUCCESS) { - return ret; - } - } - if (msg->channel->stream == SrsTsStreamAudioAAC) { - if ((ret = on_ts_audio(msg.get(), &avs)) != ERROR_SUCCESS) { - return ret; - } - } - } - - return ret; -} - -int SrsIngestHlsOutput::flush_message_queue() -{ - int ret = ERROR_SUCCESS; - - // parse messages util the last video. - while (!queue.empty()) { - std::multimap::iterator it = queue.begin(); - - SrsUniquePtr msg(it->second); - queue.erase(it); - - // parse the stream. - SrsBuffer avs(msg->payload->bytes(), msg->payload->length()); - - // publish audio or video. - if (msg->channel->stream == SrsTsStreamVideoH264) { - if ((ret = on_ts_video(msg.get(), &avs)) != ERROR_SUCCESS) { - return ret; - } - } - if (msg->channel->stream == SrsTsStreamAudioAAC) { - if ((ret = on_ts_audio(msg.get(), &avs)) != ERROR_SUCCESS) { - return ret; - } - } - } - - return ret; -} - -int SrsIngestHlsOutput::on_ts_video(SrsTsMessage *msg, SrsBuffer *avs) -{ - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - // ts tbn to flv tbn. - uint32_t dts = (uint32_t)(msg->dts / 90); - uint32_t pts = (uint32_t)(msg->dts / 90); - - std::string ibps; - SrsVideoAvcFrameType frame_type = SrsVideoAvcFrameTypeInterFrame; - - // send each frame. - while (!avs->empty()) { - char *frame = NULL; - int frame_size = 0; - if ((err = avc->annexb_demux(avs, &frame, &frame_size)) != srs_success) { - // TODO: FIXME: Use error - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - // 5bits, 7.3.1 NAL unit syntax, - // ISO_IEC_14496-10-AVC-2003.pdf, page 44. - // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame - SrsAvcNaluType nal_unit_type = (SrsAvcNaluType)(frame[0] & 0x1f); - - // for IDR frame, the frame is keyframe. - if (nal_unit_type == SrsAvcNaluTypeIDR) { - frame_type = SrsVideoAvcFrameTypeKeyFrame; - } - - // ignore the nalu type aud(9) - if (nal_unit_type == SrsAvcNaluTypeAccessUnitDelimiter) { - continue; - } - - // for sps - if (avc->is_sps(frame, frame_size)) { - std::string sps; - if ((err = avc->sps_demux(frame, frame_size, sps)) != srs_success) { - // TODO: FIXME: Use error - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - if (h264_sps == sps) { - continue; - } - h264_sps_changed = true; - h264_sps = sps; - continue; - } - - // for pps - if (avc->is_pps(frame, frame_size)) { - std::string pps; - if ((err = avc->pps_demux(frame, frame_size, pps)) != srs_success) { - // TODO: FIXME: Use error - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - if (h264_pps == pps) { - continue; - } - h264_pps_changed = true; - h264_pps = pps; - continue; - } - - // ibp frame. - std::string ibp; - if ((err = avc->mux_ipb_frame(frame, frame_size, ibp)) != srs_success) { - // TODO: FIXME: Use error - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - ibps.append(ibp); - } - - if ((ret = write_h264_sps_pps(dts, pts)) != ERROR_SUCCESS) { - return ret; - } - - if ((ret = write_h264_ipb_frame(ibps, frame_type, dts, pts)) != ERROR_SUCCESS) { - // drop the ts message. - if (ret == ERROR_H264_DROP_BEFORE_SPS_PPS) { - return ERROR_SUCCESS; - } - return ret; - } - - return ret; -} - -int SrsIngestHlsOutput::write_h264_sps_pps(uint32_t dts, uint32_t pts) -{ - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - // when sps or pps changed, update the sequence header, - // for the pps maybe not changed while sps changed. - // so, we must check when each video ts message frame parsed. - if (h264_sps_pps_sent && !h264_sps_changed && !h264_pps_changed) { - return ret; - } - - // when not got sps/pps, wait. - if (h264_pps.empty() || h264_sps.empty()) { - return ret; - } - - // h264 raw to h264 packet. - std::string sh; - if ((err = avc->mux_sequence_header(h264_sps, h264_pps, sh)) != srs_success) { - // TODO: FIXME: Use error - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - // h264 packet to flv packet. - int8_t frame_type = SrsVideoAvcFrameTypeKeyFrame; - int8_t avc_packet_type = SrsVideoAvcFrameTraitSequenceHeader; - char *flv = NULL; - int nb_flv = 0; - if ((err = avc->mux_avc2flv(sh, frame_type, avc_packet_type, dts, pts, &flv, &nb_flv)) != srs_success) { - // TODO: FIXME: Use error - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - // the timestamp in rtmp message header is dts. - uint32_t timestamp = dts; - if ((ret = rtmp_write_packet(SrsFrameTypeVideo, timestamp, flv, nb_flv)) != ERROR_SUCCESS) { - return ret; - } - - // reset sps and pps. - h264_sps_changed = false; - h264_pps_changed = false; - h264_sps_pps_sent = true; - srs_trace("hls: h264 sps/pps sent, sps=%dB, pps=%dB", h264_sps.length(), h264_pps.length()); - - return ret; -} - -int SrsIngestHlsOutput::write_h264_ipb_frame(string ibps, SrsVideoAvcFrameType frame_type, uint32_t dts, uint32_t pts) -{ - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - // when sps or pps not sent, ignore the packet. - if (!h264_sps_pps_sent) { - return ERROR_H264_DROP_BEFORE_SPS_PPS; - } - - int8_t avc_packet_type = SrsVideoAvcFrameTraitNALU; - char *flv = NULL; - int nb_flv = 0; - if ((err = avc->mux_avc2flv(ibps, frame_type, avc_packet_type, dts, pts, &flv, &nb_flv)) != srs_success) { - // TODO: FIXME: Use error - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - // the timestamp in rtmp message header is dts. - uint32_t timestamp = dts; - return rtmp_write_packet(SrsFrameTypeVideo, timestamp, flv, nb_flv); -} - -int SrsIngestHlsOutput::on_ts_audio(SrsTsMessage *msg, SrsBuffer *avs) -{ - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - // ts tbn to flv tbn. - uint32_t dts = (uint32_t)(msg->dts / 90); - - // got the next msg to calc the delta duration for each audio. - uint32_t duration = 0; - if (!queue.empty()) { - SrsTsMessage *nm = queue.begin()->second; - duration = (uint32_t)(srs_max(0, nm->dts - msg->dts) / 90); - } - uint32_t max_dts = dts + duration; - - // send each frame. - while (!avs->empty()) { - char *frame = NULL; - int frame_size = 0; - SrsRawAacStreamCodec codec; - if ((err = aac->adts_demux(avs, &frame, &frame_size, codec)) != srs_success) { - // TODO: FIXME: Use error - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - // ignore invalid frame, - // * atleast 1bytes for aac to decode the data. - if (frame_size <= 0) { - continue; - } - srs_info("mpegts: demux aac frame size=%d, dts=%d", frame_size, dts); - - // generate sh. - if (aac_specific_config.empty()) { - std::string sh; - if ((err = aac->mux_sequence_header(&codec, sh)) != srs_success) { - // TODO: FIXME: Use error - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - aac_specific_config = sh; - - codec.aac_packet_type = 0; - - if ((ret = write_audio_raw_frame((char *)sh.data(), (int)sh.length(), &codec, dts)) != ERROR_SUCCESS) { - return ret; - } - } - - // audio raw data. - codec.aac_packet_type = 1; - if ((ret = write_audio_raw_frame(frame, frame_size, &codec, dts)) != ERROR_SUCCESS) { - return ret; - } - - // calc the delta of dts, when previous frame output. - uint32_t delta = duration / (msg->payload->length() / frame_size); - dts = (uint32_t)(srs_min(max_dts, dts + delta)); - } - - return ret; -} - -int SrsIngestHlsOutput::write_audio_raw_frame(char *frame, int frame_size, SrsRawAacStreamCodec *codec, uint32_t dts) -{ - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - char *data = NULL; - int size = 0; - if ((err = aac->mux_aac2flv(frame, frame_size, codec, dts, &data, &size)) != srs_success) { - // TODO: FIXME: Use error - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - return rtmp_write_packet(SrsFrameTypeAudio, dts, data, size); -} - -int SrsIngestHlsOutput::rtmp_write_packet(char type, uint32_t timestamp, char *data, int size) -{ - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - if ((ret = connect()) != ERROR_SUCCESS) { - return ret; - } - - SrsSharedPtrMessage *msg = NULL; - - if ((err = srs_rtmp_create_msg(type, timestamp, data, size, sdk->sid(), &msg)) != srs_success) { - // TODO: FIXME: Use error - ret = srs_error_code(err); - srs_freep(err); - srs_error("mpegts: create shared ptr msg failed. ret=%d", ret); - return ret; - } - srs_assert(msg); - - srs_info("RTMP type=%d, dts=%d, size=%d", type, timestamp, size); - - // send out encoded msg. - if ((err = sdk->send_and_free_message(msg)) != srs_success) { - // TODO: FIXME: Use error - ret = srs_error_code(err); - srs_freep(err); - close(); - srs_error("send RTMP type=%d, dts=%d, size=%d failed. ret=%d", type, timestamp, size, ret); - return ret; - } - - return ret; -} - -int SrsIngestHlsOutput::connect() -{ - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - // Ignore when connected. - if (sdk) { - return ret; - } - - std::string url = out_rtmp->get_url(); - srs_trace("connect output=%s", url.c_str()); - - // connect host. - srs_utime_t cto = SRS_CONSTS_RTMP_TIMEOUT; - srs_utime_t sto = SRS_CONSTS_RTMP_PULSE; - sdk = new SrsBasicRtmpClient(url, cto, sto); - - if ((err = sdk->connect()) != srs_success) { - // TODO: FIXME: Use error - ret = srs_error_code(err); - srs_freep(err); - close(); - srs_error("mpegts: connect %s failed, cto=%dms, sto=%dms. ret=%d", url.c_str(), srsu2msi(cto), srsu2msi(sto), ret); - return ret; - } - - // publish. - if ((err = sdk->publish(SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE)) != srs_success) { - // TODO: FIXME: Use error - ret = srs_error_code(err); - srs_freep(err); - close(); - srs_error("mpegts: publish %s failed. ret=%d", url.c_str(), ret); - return ret; - } - - return ret; -} - -void SrsIngestHlsOutput::close() -{ - h264_sps_pps_sent = false; - srs_freep(req); - srs_freep(sdk); -} - -// the context for ingest hls stream. -class SrsIngestHlsContext -{ -private: - SrsIngestHlsInput *ic; - SrsIngestHlsOutput *oc; - -public: - SrsIngestHlsContext(SrsHttpUri *hls, SrsHttpUri *rtmp) - { - ic = new SrsIngestHlsInput(hls); - oc = new SrsIngestHlsOutput(rtmp); - } - virtual ~SrsIngestHlsContext() - { - srs_freep(ic); - srs_freep(oc); - } - virtual int proxy() - { - int ret = ERROR_SUCCESS; - - if ((ret = ic->connect()) != ERROR_SUCCESS) { - srs_error("connect oc failed. ret=%d", ret); - return ret; - } - - if ((ret = oc->connect()) != ERROR_SUCCESS) { - srs_error("connect ic failed. ret=%d", ret); - return ret; - } - - if ((ret = ic->parse(oc, oc)) != ERROR_SUCCESS) { - srs_error("proxy ts to rtmp failed. ret=%d", ret); - return ret; - } - - if ((ret = oc->flush_message_queue()) != ERROR_SUCCESS) { - srs_error("flush oc message failed. ret=%d", ret); - return ret; - } - - return ret; - } -}; - -srs_error_t proxy_hls2rtmp(string hls, string rtmp) -{ - srs_error_t err = srs_success; - - // init st. - if ((err = srs_st_init()) != srs_success) { - return srs_error_wrap(err, "initialize st"); - } - - SrsHttpUri hls_uri, rtmp_uri; - if ((err = hls_uri.initialize(hls)) != srs_success) { - return srs_error_wrap(err, "hls parse uri=%s", hls.c_str()); - } - if ((err = rtmp_uri.initialize(rtmp)) != srs_success) { - return srs_error_wrap(err, "rtmp parse uri=%s", rtmp.c_str()); - } - - SrsIngestHlsContext context(&hls_uri, &rtmp_uri); - for (;;) { - int ret = ERROR_SUCCESS; - if ((ret = context.proxy()) != ERROR_SUCCESS) { - return srs_error_new(ret, "proxy hls to rtmp"); - } - } - - return err; -} diff --git a/trunk/src/main/srs_main_mp4_parser.cpp b/trunk/src/main/srs_main_mp4_parser.cpp deleted file mode 100644 index 6c97c320b..000000000 --- a/trunk/src/main/srs_main_mp4_parser.cpp +++ /dev/null @@ -1,132 +0,0 @@ -// -// Copyright (c) 2013-2025 The SRS Authors -// -// SPDX-License-Identifier: MIT -// - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -using namespace std; - -// @global log and context. -ISrsLog *_srs_log = new SrsConsoleLog(SrsLogLevelTrace, false); -ISrsContext *_srs_context = new SrsThreadContext(); - -// @global config object for app module. -SrsConfig *_srs_config = new SrsConfig(); - -// @global Other variables. -bool _srs_in_docker = false; - -// Whether setup config by environment variables, see https://github.com/ossrs/srs/issues/2277 -bool _srs_config_by_env = false; - -// The binary name of SRS. -const char *_srs_binary = NULL; - -extern SrsPps *_srs_pps_cids_get; - -srs_error_t parse(std::string mp4_file, bool verbose) -{ - srs_error_t err = srs_success; - - SrsFileReader fr; - if ((err = fr.open(mp4_file)) != srs_success) { - return srs_error_wrap(err, "open mp4 file %s", mp4_file.c_str()); - } - srs_trace("MP4 file open success"); - - SrsMp4BoxReader br; - if ((err = br.initialize(&fr)) != srs_success) { - return srs_error_wrap(err, "open box reader"); - } - srs_trace("MP4 box reader open success"); - - SrsUniquePtr stream(new SrsSimpleStream()); - - fprintf(stderr, "\n%s\n", mp4_file.c_str()); - while (true) { - SrsMp4Box *box_raw = NULL; - if ((err = br.read(stream.get(), &box_raw)) != srs_success) { - if (srs_error_code(err) == ERROR_SYSTEM_FILE_EOF) { - fprintf(stderr, "\n"); - } - return srs_error_wrap(err, "read box"); - } - - // Should use unique pointer after box is created. - SrsUniquePtr box(box_raw); - - SrsUniquePtr buffer(new SrsBuffer(stream->bytes(), stream->length())); - if ((err = box->decode(buffer.get())) != srs_success) { - return srs_error_wrap(err, "decode box"); - } - - if ((err = br.skip(box.get(), stream.get())) != srs_success) { - return srs_error_wrap(err, "skip box"); - } - - SrsMp4DumpContext ctx; - ctx.level = 1; - ctx.summary = !verbose; - - stringstream ss; - fprintf(stderr, "%s", box->dumps(ss, ctx).str().c_str()); - } - - return err; -} - -int main(int argc, char **argv) -{ - _srs_pps_cids_get = new SrsPps(); - - printf("SRS MP4 parser/%d.%d.%d, parse and show the mp4 boxes structure.\n", - VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION); - - _srs_binary = argv[0]; - - if (argc < 2) { - printf("Usage: %s [verbose]\n" - " mp4_file The MP4 file path to parse.\n" - " verbose Whether print verbose of box.\n" - "For example:\n" - " %s doc/source.200kbps.768x320.mp4\n" - " %s doc/source.200kbps.768x320.mp4 verbose\n", - argv[0], argv[0], argv[0]); - - exit(-1); - } - string mp4_file = argv[1]; - bool verbose = false; - if (argc > 2) { - verbose = true; - } - srs_trace("Parse MP4 file %s, verbose=%d", mp4_file.c_str(), verbose); - - srs_error_t err = parse(mp4_file, verbose); - int code = srs_error_code(err); - - if (code == ERROR_SYSTEM_FILE_EOF) { - srs_trace("Parse complete"); - } else { - srs_error("Parse error %s", srs_error_desc(err).c_str()); - } - - srs_freep(err); - return code; -} diff --git a/trunk/src/main/srs_main_server.cpp b/trunk/src/main/srs_main_server.cpp index ae0c0caad..07f88923f 100644 --- a/trunk/src/main/srs_main_server.cpp +++ b/trunk/src/main/srs_main_server.cpp @@ -33,7 +33,6 @@ using namespace std; #include #include -#include #include #include #include @@ -44,7 +43,6 @@ using namespace std; #include #include -#include #ifdef SRS_SRT #include @@ -53,7 +51,7 @@ using namespace std; // pre-declare srs_error_t run_directly_or_daemon(); -srs_error_t run_hybrid_server(); +srs_error_t run_srs_server(); void show_macro_features(); // @global log and context. @@ -66,9 +64,6 @@ SrsConfig *_srs_config = NULL; // @global version of srs, which can grep keyword "XCORE" extern const char *_srs_version; -// @global main SRS server, for debugging -SrsServer *_srs_server = NULL; - // Whether setup config by environment variables, see https://github.com/ossrs/srs/issues/2277 bool _srs_config_by_env = false; @@ -409,7 +404,7 @@ srs_error_t run_directly_or_daemon() // If not daemon, directly run hybrid server. if (!run_as_daemon) { - if ((err = run_hybrid_server()) != srs_success) { + if ((err = run_srs_server()) != srs_success) { return srs_error_wrap(err, "run server"); } return srs_success; @@ -446,43 +441,31 @@ srs_error_t run_directly_or_daemon() // son srs_trace("son(daemon) process running."); - if ((err = run_hybrid_server()) != srs_success) { + if ((err = run_srs_server()) != srs_success) { return srs_error_wrap(err, "daemon run server"); } return err; } -srs_error_t run_hybrid_server() +srs_error_t run_srs_server() { srs_error_t err = srs_success; - // Create servers and register them. - _srs_hybrid->register_server(new SrsServerAdapter()); - -#ifdef SRS_SRT - _srs_hybrid->register_server(new SrsSrtServerAdapter()); -#endif - - _srs_hybrid->register_server(new SrsRtcServerAdapter()); + _srs_server = new SrsServer(); // Do some system initialize. - if ((err = _srs_hybrid->initialize()) != srs_success) { + if ((err = _srs_server->initialize()) != srs_success) { return srs_error_wrap(err, "hybrid initialize"); } - // Circuit breaker to protect server, which depends on hybrid. - if ((err = _srs_circuit_breaker->initialize()) != srs_success) { - return srs_error_wrap(err, "init circuit breaker"); - } - // Should run util hybrid servers all done. - if ((err = _srs_hybrid->run()) != srs_success) { + if ((err = _srs_server->run()) != srs_success) { return srs_error_wrap(err, "hybrid run"); } // After all done, stop and cleanup. - _srs_hybrid->stop(); + _srs_server->stop(); return err; } diff --git a/trunk/src/protocol/srs_protocol_srt.hpp b/trunk/src/protocol/srs_protocol_srt.hpp index 2dd57a5ce..c6cff3c30 100644 --- a/trunk/src/protocol/srs_protocol_srt.hpp +++ b/trunk/src/protocol/srs_protocol_srt.hpp @@ -17,7 +17,6 @@ class SrsSrtSocket; extern srs_error_t srs_srt_log_initialize(); -typedef int srs_srt_t; extern srs_srt_t srs_srt_socket_invalid(); // Create srt socket only, with libsrt's default option. diff --git a/trunk/src/protocol/srs_protocol_st.hpp b/trunk/src/protocol/srs_protocol_st.hpp index c9367b32e..6888f84e1 100644 --- a/trunk/src/protocol/srs_protocol_st.hpp +++ b/trunk/src/protocol/srs_protocol_st.hpp @@ -14,6 +14,9 @@ #include #include +// Wrap for SRT. +typedef int srs_srt_t; + // Wrap for coroutine. typedef void *srs_netfd_t; typedef void *srs_thread_t; diff --git a/trunk/src/utest/srs_utest.cpp b/trunk/src/utest/srs_utest.cpp index f84ad16b1..56eafb569 100644 --- a/trunk/src/utest/srs_utest.cpp +++ b/trunk/src/utest/srs_utest.cpp @@ -36,7 +36,6 @@ ISrsLog *_srs_log = NULL; ISrsContext *_srs_context = NULL; // app module. SrsConfig *_srs_config = NULL; -SrsServer *_srs_server = NULL; bool _srs_in_docker = false; bool _srs_config_by_env = false; @@ -62,6 +61,8 @@ srs_error_t prepare_main() return srs_error_wrap(err, "init global"); } + _srs_server = new SrsServer(); + srs_freep(_srs_log); _srs_log = new MockEmptyLog(SrsLogLevelError);