From 32dfed43ef7a1139c7ccf8eee032500b88ba4179 Mon Sep 17 00:00:00 2001 From: Winlin Date: Sun, 31 Aug 2025 08:58:37 -0400 Subject: [PATCH] AI: Merge SRT and RTC servers into unified SrsServer. v7.0.68 (#4459) This PR consolidates the SRT and RTC server functionality into the main SrsServer class, eliminating the separate `SrsSrtServer` and `SrsRtcServer` classes and their corresponding adapter classes. This architectural change simplifies the codebase by removing the hybrid server pattern and integrating all protocol handling directly into `SrsServer`. As unified connection manager (`_srs_conn_manager`) for all protocol connections, all incoming connections are checked against the same connection limit in `on_before_connection()`. This enables consistent connection limits: `max_connections` now protects against resource exhaustion from any protocol, not just RTMP. Remove modules because it's not used now, so only keep the server application module and main entry point. Remove the wait group to run server, instead, directly run server and invoke the cycle method. After this PR, the startup workflow and servers architecture should be much easier to maintain. --------- Co-authored-by: OSSRS-AI --- trunk/configure | 82 +- trunk/doc/CHANGELOG.md | 1 + trunk/modules/hls-ingester/config | 6 - trunk/modules/mp4-parser/config | 6 - trunk/modules/readme.txt | 20 - trunk/src/app/srs_app_circuit_breaker.cpp | 6 +- trunk/src/app/srs_app_gb28181.cpp | 8 +- trunk/src/app/srs_app_gb28181.hpp | 4 +- trunk/src/app/srs_app_http_api.cpp | 2 +- trunk/src/app/srs_app_http_static.cpp | 5 +- trunk/src/app/srs_app_hybrid.cpp | 797 ---------- trunk/src/app/srs_app_hybrid.hpp | 86 -- trunk/src/app/srs_app_listener.cpp | 74 +- trunk/src/app/srs_app_listener.hpp | 6 + trunk/src/app/srs_app_rtc_api.cpp | 17 +- trunk/src/app/srs_app_rtc_api.hpp | 18 +- trunk/src/app/srs_app_rtc_conn.cpp | 36 +- trunk/src/app/srs_app_rtc_conn.hpp | 8 +- trunk/src/app/srs_app_rtc_network.cpp | 6 +- trunk/src/app/srs_app_rtc_server.cpp | 545 +------ trunk/src/app/srs_app_rtc_server.hpp | 60 +- trunk/src/app/srs_app_rtc_source.cpp | 6 +- trunk/src/app/srs_app_rtmp_conn.cpp | 2 +- trunk/src/app/srs_app_rtsp_source.cpp | 1 - trunk/src/app/srs_app_server.cpp | 1691 ++++++++++++++++++--- trunk/src/app/srs_app_server.hpp | 175 ++- trunk/src/app/srs_app_source.cpp | 5 +- trunk/src/app/srs_app_srt_conn.cpp | 6 +- trunk/src/app/srs_app_srt_conn.hpp | 4 +- trunk/src/app/srs_app_srt_server.cpp | 256 +--- trunk/src/app/srs_app_srt_server.hpp | 69 +- trunk/src/core/srs_core_version7.hpp | 2 +- trunk/src/main/srs_main_ingest_hls.cpp | 1405 ----------------- trunk/src/main/srs_main_mp4_parser.cpp | 132 -- trunk/src/main/srs_main_server.cpp | 33 +- trunk/src/protocol/srs_protocol_srt.hpp | 1 - trunk/src/protocol/srs_protocol_st.hpp | 3 + trunk/src/utest/srs_utest.cpp | 3 +- 38 files changed, 1743 insertions(+), 3844 deletions(-) delete mode 100644 trunk/modules/hls-ingester/config delete mode 100644 trunk/modules/mp4-parser/config delete mode 100644 trunk/modules/readme.txt delete mode 100644 trunk/src/app/srs_app_hybrid.cpp delete mode 100644 trunk/src/app/srs_app_hybrid.hpp delete mode 100644 trunk/src/main/srs_main_ingest_hls.cpp delete mode 100644 trunk/src/main/srs_main_mp4_parser.cpp 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);