diff --git a/trunk/auto/depends.sh b/trunk/auto/depends.sh index 87bf940fb5..f40bd64561 100755 --- a/trunk/auto/depends.sh +++ b/trunk/auto/depends.sh @@ -253,11 +253,9 @@ function OSX_prepare() echo "OSX detected, install tools if needed" - brew --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then - echo "install brew" - echo "ruby -e \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)\"" - ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi - echo "install brew success" + brew --version >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then + echo "Please install brew at https://brew.sh/" + exit $ret fi gcc --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then diff --git a/trunk/auto/options.sh b/trunk/auto/options.sh index 59bc9f7d55..7ab9e8feca 100755 --- a/trunk/auto/options.sh +++ b/trunk/auto/options.sh @@ -432,12 +432,6 @@ function apply_auto_options() { SRS_TOOL_LD=$SRS_TOOL_CC fi - # The SRT code in SRS requires c++11, although we build libsrt without c++11. - # TODO: FIXME: Remove c++11 code in SRT of SRS. - if [[ $SRS_SRT == YES ]]; then - SRS_CXX11=YES - fi - # Enable FFmpeg fit for RTC to transcode audio from AAC to OPUS, if user enabled it. if [[ $SRS_RTC == YES && $SRS_FFMPEG_FIT == RESERVED ]]; then SRS_FFMPEG_FIT=YES diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index 1e975ade8f..7d45cc7e58 100644 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -352,7 +352,7 @@ stream_caster { ############################################################################################# # SRT server section ############################################################################################# -# @doc https://github.com/ossrs/srs/issues/1147#issuecomment-577607026 +# @doc https://github.com/ossrs/srs/issues/1147#usage srt_server { # whether SRT server is enabled. # default: off @@ -360,15 +360,59 @@ srt_server { # The UDP listen port for SRT. listen 10080; # For detail parameters, please read wiki: - # https://github.com/ossrs/srs/wiki/v4_CN_SRTParams - # https://github.com/ossrs/srs/wiki/v4_EN_SRTParams + # https://github.com/ossrs/srs/wiki/v5_CN_SRTParams + # https://github.com/ossrs/srs/wiki/v5_EN_SRTParams + + # The maxbw is the max bandwidth of the sender side. + # -1: Means the biggest bandwidth is infinity. + # 0: Means the bandwidth is determined by SRTO_INPUTBW. + # >0: Means the bandwidth is the configuration value. + # default: -1 maxbw 1000000000; + # The timeout time of the SRT connection on the sender side in ms. When SRT connects to a peer costs time + # more than this config, it will be close. + # default: 3000 connect_timeout 4000; - peerlatency 300; - recvlatency 300; + # The timeout time of SRT connection on the receiver side in ms. When the SRT connection is idle + # more than this config, it will be close. + # default: 10000 + peer_idle_timeout 8000; # Default app for vmix, see https://github.com/ossrs/srs/pull/1615 # default: live default_app live; + # The peerlatency is set by the sender side and will notify the receiver side. + # default: 0 + peerlatency 0; + # The recvlatency means latency from sender to receiver. + # default: 120 + recvlatency 0; + # This latency configuration configures both recvlatency and peerlatency to the same value. + # default: 120 + latency 0; + # The tsbpd mode means timestamp based packet delivery. + # SRT sender side will pack timestamp in each packet. If this config is true, + # the receiver will read the packet according to the timestamp in the head of the packet. + # default: on + tsbpdmode off; + # The tlpkdrop means too-late Packet Drop + # SRT sender side will pack timestamp in each packet, When the network is congested, + # the packet will drop if latency is bigger than the configuration in both sender side and receiver side. + # And on the sender side, it also will be dropped because latency is bigger than configuration. + # default: on + tlpktdrop off; + # The send buffer size of SRT. + # default: 8192 * (1500-28) + sendbuf 2000000; + # The recv buffer size of SRT. + # default: 8192 * (1500-28) + recvbuf 2000000; +} + +vhost srt.vhost.srs.com { + srt { + enabled on; + srt_to_rtmp on; + } } ############################################################################################# diff --git a/trunk/conf/srt.conf b/trunk/conf/srt.conf index 396dccd3ae..582bc00563 100644 --- a/trunk/conf/srt.conf +++ b/trunk/conf/srt.conf @@ -21,12 +21,22 @@ srt_server { listen 10080; maxbw 1000000000; connect_timeout 4000; - peerlatency 300; - recvlatency 300; + peerlatency 0; + recvlatency 0; + latency 0; + tsbpdmode off; + tlpktdrop off; + sendbuf 2000000; + recvbuf 2000000; } # @doc https://github.com/ossrs/srs/issues/1147#issuecomment-577607026 vhost __defaultVhost__ { + srt { + enabled on; + srt_to_rtmp on; + } + http_remux { enabled on; mount [vhost]/[app]/[stream].flv; diff --git a/trunk/configure b/trunk/configure index dca9788677..5be1d5f3e4 100755 --- a/trunk/configure +++ b/trunk/configure @@ -164,7 +164,6 @@ fi # srt code path if [[ $SRS_SRT == YES ]]; then - SrsSRTRoot="${SRS_WORKDIR}/src/srt" LibSRTRoot="${SRS_OBJS_DIR}/srt/include"; LibSRTfile="${SRS_OBJS_DIR}/srt/lib/libsrt.a" if [[ $SRS_SHARED_SRT == YES ]]; then LibSRTfile="-L${SRS_OBJS_DIR}/srt/lib -lsrt"; fi fi @@ -200,7 +199,7 @@ MODULE_ID="CORE" MODULE_DEPENDS=() ModuleLibIncs=(${SRS_OBJS_DIR}) MODULE_FILES=("srs_core" "srs_core_version5" "srs_core_autofree" "srs_core_performance" - "srs_core_time" "srs_core_platform") + "srs_core_time" "srs_core_platform" "srs_core_lock") CORE_INCS="src/core"; MODULE_DIR=${CORE_INCS} . auto/modules.sh CORE_OBJS="${MODULE_OBJS[@]}" # @@ -228,6 +227,10 @@ MODULE_FILES=("srs_protocol_amf0" "srs_protocol_io" "srs_protocol_conn" "srs_pro "srs_protocol_raw_avc" "srs_protocol_rtsp_stack" "srs_protocol_http_stack" "srs_protocol_kbps" "srs_protocol_json" "srs_protocol_format" "srs_protocol_log" "srs_protocol_st" "srs_protocol_http_client" "srs_protocol_http_conn" "srs_protocol_rtmp_conn") +if [[ $SRS_SRT == YES ]]; then + MODULE_FILES+=("srs_protocol_srt") + ModuleLibIncs+=(${LibSRTRoot}) +fi if [[ $SRS_RTC == YES ]]; then MODULE_FILES+=("srs_protocol_rtc_stun") ModuleLibIncs+=(${LibSrtpRoot}) @@ -237,16 +240,6 @@ if [[ $SRS_FFMPEG_FIT == YES ]]; then fi PROTOCOL_INCS="src/protocol"; MODULE_DIR=${PROTOCOL_INCS} . auto/modules.sh PROTOCOL_OBJS="${MODULE_OBJS[@]}" -# -#srt protocol features. -if [[ $SRS_SRT == YES ]]; then - MODULE_ID="SRT" - MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "APP") - ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSSLRoot} ${LibSRTRoot}) - MODULE_FILES=("srt_server" "srt_handle" "srt_conn" "srt_to_rtmp" "ts_demux" "srt_data" "srt_log") - SRT_INCS=(${LibSRTRoot} ${SrsSRTRoot}); MODULE_DIR=${SrsSRTRoot} . auto/modules.sh - SRT_OBJS="${MODULE_OBJS[@]}" -fi # #App Module, for SRS server only. @@ -262,6 +255,9 @@ fi if [[ $SRS_FFMPEG_FIT == YES ]]; then ModuleLibIncs+=("${LibFfmpegRoot[*]}") fi +if [[ $SRS_SRT == YES ]]; then + ModuleLibIncs+=("${LibSRTRoot[*]}") +fi MODULE_FILES=("srs_app_server" "srs_app_conn" "srs_app_rtmp_conn" "srs_app_source" "srs_app_refer" "srs_app_hls" "srs_app_forward" "srs_app_encoder" "srs_app_http_stream" "srs_app_bandwidth" "srs_app_st" "srs_app_log" "srs_app_config" @@ -273,6 +269,9 @@ MODULE_FILES=("srs_app_server" "srs_app_conn" "srs_app_rtmp_conn" "srs_app_sourc "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_threads") +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") +fi if [[ $SRS_RTC == YES ]]; then MODULE_FILES+=("srs_app_rtc_conn" "srs_app_rtc_dtls" "srs_app_rtc_sdp" "srs_app_rtc_queue" "srs_app_rtc_server" "srs_app_rtc_source" "srs_app_rtc_api") @@ -294,9 +293,6 @@ APP_OBJS="${MODULE_OBJS[@]}" #Server Module, for SRS only. MODULE_ID="SERVER" MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "APP") -if [[ $SRS_SRT == YES ]]; then - MODULE_DEPENDS+=("SRT") -fi ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSTRoot} ${LibGperfRoot} ${LibSSLRoot}) if [[ $SRS_RTC == YES ]]; then ModuleLibIncs+=(${LibSrtpRoot}) @@ -306,7 +302,6 @@ if [[ $SRS_FFMPEG_FIT == YES ]]; then fi if [[ $SRS_SRT == YES ]]; then ModuleLibIncs+=(${LibSRTRoot}) - ModuleLibIncs+=("${SrsSRTRoot[*]}") fi MODULE_FILES=("srs_main_server") SERVER_INCS="src/main"; MODULE_DIR=${SERVER_INCS} . auto/modules.sh @@ -324,7 +319,6 @@ if [[ $SRS_FFMPEG_FIT == YES ]]; then fi if [[ $SRS_SRT == YES ]]; then ModuleLibIncs+=(${LibSRTRoot}) - ModuleLibIncs+=("${SrsSRTRoot[*]}") fi MODULE_FILES=() DEFINES="" @@ -370,7 +364,6 @@ if [[ $SRS_FFMPEG_FIT == YES ]]; then fi if [[ $SRS_SRT == YES ]]; then ModuleLibIncs+=(${LibSRTRoot}) - ModuleLibIncs+=("${SrsSRTRoot[*]}") MODULE_OBJS="${MODULE_OBJS} ${SRT_OBJS[@]}" fi LINK_OPTIONS="${SrsLinkOptions}${SrsGprofLink}${SrsGperfLink}" @@ -416,7 +409,7 @@ if [ $SRS_UTEST = YES ]; then ModuleLibIncs+=("${LibFfmpegRoot[*]}") fi if [[ $SRS_SRT == YES ]]; then - ModuleLibIncs+=("${SrsSRTRoot[*]}") + ModuleLibIncs+=("${LibSRTRoot[*]}") fi ModuleLibFiles=(${LibSTfile} ${LibSSLfile}) if [[ $SRS_RTC == YES ]]; then @@ -429,9 +422,6 @@ if [ $SRS_UTEST = YES ]; then ModuleLibFiles+=("${LibSRTfile[*]}") fi MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "APP") - if [[ $SRS_SRT == YES ]]; then - MODULE_DEPENDS+=("SRT") - fi MODULE_OBJS="${CORE_OBJS[@]} ${KERNEL_OBJS[@]} ${PROTOCOL_OBJS[@]} ${APP_OBJS[@]} ${SRT_OBJS[@]}" LINK_OPTIONS="-lpthread ${SrsLinkOptions}" MODULE_DIR="src/utest" APP_NAME="srs_utest" . auto/utest.sh fi diff --git a/trunk/ide/srs_clion/CMakeLists.txt b/trunk/ide/srs_clion/CMakeLists.txt index f1d7b41040..d5c4cd7507 100755 --- a/trunk/ide/srs_clion/CMakeLists.txt +++ b/trunk/ide/srs_clion/CMakeLists.txt @@ -26,10 +26,17 @@ ProcessorCount(JOBS) # We should always configure SRS for switching between branches. IF (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - EXEC_PROGRAM("cd ${SRS_DIR} && ./configure --osx --srt=on --utest=on --jobs=${JOBS}") + EXECUTE_PROCESS( + COMMAND ./configure --osx --srt=on --utest=on --jobs=${JOBS} + WORKING_DIRECTORY ${SRS_DIR} RESULT_VARIABLE ret) ELSE () - EXEC_PROGRAM("cd ${SRS_DIR} && ./configure --srt=on --utest=on --jobs=${JOBS}") + EXECUTE_PROCESS( + COMMAND ./configure --srt=on --utest=on --jobs=${JOBS} + WORKING_DIRECTORY ${SRS_DIR} RESULT_VARIABLE ret) ENDIF () +if(NOT ret EQUAL 0) + message(FATAL_ERROR "FAILED: ${ret}") +endif() set(DEPS_LIBS ${SRS_DIR}/objs/st/libst.a ${SRS_DIR}/objs/openssl/lib/libssl.a @@ -61,19 +68,13 @@ INCLUDE_DIRECTORIES(${SRS_DIR}/objs ${SRS_DIR}/src/kernel ${SRS_DIR}/src/protocol ${SRS_DIR}/src/app - ${SRS_DIR}/src/service - ${SRS_DIR}/src/srt) + ${SRS_DIR}/src/service) # Common used sources for SRS and utest. AUX_SOURCE_DIRECTORY(${SRS_DIR}/src/core SOURCE_FILES) AUX_SOURCE_DIRECTORY(${SRS_DIR}/src/kernel SOURCE_FILES) AUX_SOURCE_DIRECTORY(${SRS_DIR}/src/protocol SOURCE_FILES) AUX_SOURCE_DIRECTORY(${SRS_DIR}/src/app SOURCE_FILES) -AUX_SOURCE_DIRECTORY(${SRS_DIR}/src/srt SOURCE_FILES) - -# Remove the duplicated test main for srt. -# TODO: FIMXE: Remove the file directly, use utest or main or research. -list(REMOVE_ITEM SOURCE_FILES ${SRS_DIR}/src/srt/ts_demux_test.cpp) ADD_DEFINITIONS("-g -O0") diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index d69bdfb6df..1d59503b8a 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -2546,8 +2546,9 @@ srs_error_t SrsConfig::check_normal_config() && n != "mss" && n != "latency" && n != "recvlatency" && n != "peerlatency" && n != "tlpkdrop" && n != "connect_timeout" && n != "sendbuf" && n != "recvbuf" && n != "payloadsize" - && n != "default_app" && n != "mix_correct" && n != "sei_filter") { - return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal srt_stream.%s", n.c_str()); + && n != "default_app" && n != "sei_filter" && n != "mix_correct" + && n != "tlpktdrop" && n != "tsbpdmode") { + return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal srt_server.%s", n.c_str()); } } } @@ -2722,7 +2723,7 @@ srs_error_t SrsConfig::check_normal_config() && n != "play" && n != "publish" && n != "cluster" && n != "security" && n != "http_remux" && n != "dash" && n != "http_static" && n != "hds" && n != "exec" - && n != "in_ack_size" && n != "out_ack_size" && n != "rtc") { + && n != "in_ack_size" && n != "out_ack_size" && n != "rtc" && n != "srt") { return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal vhost.%s", n.c_str()); } // for each sub directives of vhost. @@ -2879,6 +2880,13 @@ srs_error_t SrsConfig::check_normal_config() return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal vhost.rtc.%s of %s", m.c_str(), vhost->arg0().c_str()); } } + } else if (n == "srt") { + for (int j = 0; j < (int)conf->directives.size(); j++) { + string m = conf->at(j)->name; + if (m != "enabled" && m != "srt_to_rtmp") { + return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal vhost.srt.%s of %s", m.c_str(), vhost->arg0().c_str()); + } + } } } } @@ -6756,49 +6764,53 @@ unsigned short SrsConfig::get_srt_listen_port() return (unsigned short)atoi(conf->arg0().c_str()); } -bool SrsConfig::get_srt_mix_correct() { - static bool DEFAULT = true; +int SrsConfig::get_srto_maxbw() +{ + static int64_t DEFAULT = -1; SrsConfDirective* conf = root->get("srt_server"); if (!conf) { return DEFAULT; } - conf = conf->get("mix_correct"); + conf = conf->get("maxbw"); if (!conf || conf->arg0().empty()) { return DEFAULT; } - return SRS_CONF_PERFER_TRUE(conf->arg0()); + return atoi(conf->arg0().c_str()); } -int SrsConfig::get_srto_maxbw() { - static int64_t DEFAULT = -1; +int SrsConfig::get_srto_mss() +{ + static int DEFAULT = 1500; SrsConfDirective* conf = root->get("srt_server"); if (!conf) { return DEFAULT; } - conf = conf->get("maxbw"); + conf = conf->get("mms"); if (!conf || conf->arg0().empty()) { return DEFAULT; } return atoi(conf->arg0().c_str()); } -int SrsConfig::get_srto_mss() { - static int DEFAULT = 1500; +bool SrsConfig::get_srto_tsbpdmode() +{ + static bool DEFAULT = true; SrsConfDirective* conf = root->get("srt_server"); if (!conf) { return DEFAULT; } - conf = conf->get("mms"); + conf = conf->get("tsbpdmode"); if (!conf || conf->arg0().empty()) { return DEFAULT; } - return atoi(conf->arg0().c_str()); + return SRS_CONF_PERFER_TRUE(conf->arg0()); } -int SrsConfig::get_srto_latency() { +int SrsConfig::get_srto_latency() +{ static int DEFAULT = 120; SrsConfDirective* conf = root->get("srt_server"); if (!conf) { @@ -6812,7 +6824,8 @@ int SrsConfig::get_srto_latency() { return atoi(conf->arg0().c_str()); } -int SrsConfig::get_srto_recv_latency() { +int SrsConfig::get_srto_recv_latency() +{ static int DEFAULT = 120; SrsConfDirective* conf = root->get("srt_server"); if (!conf) { @@ -6826,7 +6839,8 @@ int SrsConfig::get_srto_recv_latency() { return atoi(conf->arg0().c_str()); } -int SrsConfig::get_srto_peer_latency() { +int SrsConfig::get_srto_peer_latency() +{ static int DEFAULT = 0; SrsConfDirective* conf = root->get("srt_server"); if (!conf) { @@ -6840,7 +6854,8 @@ int SrsConfig::get_srto_peer_latency() { return atoi(conf->arg0().c_str()); } -bool SrsConfig::get_srt_sei_filter() { +bool SrsConfig::get_srt_sei_filter() +{ static bool DEFAULT = true; SrsConfDirective* conf = root->get("srt_server"); if (!conf) { @@ -6854,22 +6869,28 @@ bool SrsConfig::get_srt_sei_filter() { return SRS_CONF_PERFER_TRUE(conf->arg0()); } -bool SrsConfig::get_srto_tlpkdrop() { +bool SrsConfig::get_srto_tlpktdrop() +{ static bool DEFAULT = true; - SrsConfDirective* conf = root->get("srt_server"); - if (!conf) { + SrsConfDirective* srt_server_conf = root->get("srt_server"); + if (!srt_server_conf) { return DEFAULT; } - conf = conf->get("tlpkdrop"); + SrsConfDirective* conf = srt_server_conf->get("tlpkdrop"); + if (! conf) { + // make it compatible tlpkdrop and tlpktdrop opt. + conf = srt_server_conf->get("tlpktdrop"); + } if (!conf || conf->arg0().empty()) { return DEFAULT; } return SRS_CONF_PERFER_TRUE(conf->arg0()); } -int SrsConfig::get_srto_conntimeout() { - static int DEFAULT = 3000; +srs_utime_t SrsConfig::get_srto_conntimeout() +{ + static srs_utime_t DEFAULT = 3 * SRS_UTIME_SECONDS; SrsConfDirective* conf = root->get("srt_server"); if (!conf) { return DEFAULT; @@ -6879,11 +6900,27 @@ int SrsConfig::get_srto_conntimeout() { if (!conf || conf->arg0().empty()) { return DEFAULT; } - return atoi(conf->arg0().c_str()); + return (srs_utime_t)(::atoi(conf->arg0().c_str()) * SRS_UTIME_MILLISECONDS); +} + +srs_utime_t SrsConfig::get_srto_peeridletimeout() +{ + static srs_utime_t DEFAULT = 10 * SRS_UTIME_SECONDS; + SrsConfDirective* conf = root->get("srt_server"); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("peer_idle_timeout"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + return (srs_utime_t)(::atoi(conf->arg0().c_str()) * SRS_UTIME_MILLISECONDS); } -int SrsConfig::get_srto_sendbuf() { - static int64_t DEFAULT = 8192 * (1500-28); +int SrsConfig::get_srto_sendbuf() +{ + static int DEFAULT = 8192 * (1500-28); SrsConfDirective* conf = root->get("srt_server"); if (!conf) { return DEFAULT; @@ -6896,8 +6933,9 @@ int SrsConfig::get_srto_sendbuf() { return atoi(conf->arg0().c_str()); } -int SrsConfig::get_srto_recvbuf() { - static int64_t DEFAULT = 8192 * (1500-28); +int SrsConfig::get_srto_recvbuf() +{ + static int DEFAULT = 8192 * (1500-28); SrsConfDirective* conf = root->get("srt_server"); if (!conf) { return DEFAULT; @@ -6910,7 +6948,8 @@ int SrsConfig::get_srto_recvbuf() { return atoi(conf->arg0().c_str()); } -int SrsConfig::get_srto_payloadsize() { +int SrsConfig::get_srto_payloadsize() +{ static int DEFAULT = 1316; SrsConfDirective* conf = root->get("srt_server"); if (!conf) { @@ -6924,7 +6963,8 @@ int SrsConfig::get_srto_payloadsize() { return atoi(conf->arg0().c_str()); } -string SrsConfig::get_default_app_name() { +string SrsConfig::get_default_app_name() +{ static string DEFAULT = "live"; SrsConfDirective* conf = root->get("srt_server"); if (!conf) { @@ -6938,6 +6978,47 @@ string SrsConfig::get_default_app_name() { return conf->arg0(); } +SrsConfDirective* SrsConfig::get_srt(std::string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + return conf? conf->get("srt") : NULL; +} + +bool SrsConfig::get_srt_enabled(std::string vhost) +{ + static bool DEFAULT = false; + + SrsConfDirective* conf = get_srt(vhost); + + if (!conf) { + return DEFAULT; + } + + conf = conf->get("enabled"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + return SRS_CONF_PERFER_FALSE(conf->arg0()); +} + +bool SrsConfig::get_srt_to_rtmp(std::string vhost) +{ + static bool DEFAULT = true; + + SrsConfDirective* conf = get_srt(vhost); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("srt_to_rtmp"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + return SRS_CONF_PERFER_FALSE(conf->arg0()); +} + bool SrsConfig::get_http_stream_enabled() { SrsConfDirective* conf = root->get("http_server"); diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index 5d4fdddbab..1f59332c94 100644 --- a/trunk/src/app/srs_app_config.hpp +++ b/trunk/src/app/srs_app_config.hpp @@ -636,6 +636,8 @@ class SrsConfig virtual int get_srto_maxbw(); // Get the srt SRTO_MSS, Maximum Segment Size, default is 1500. virtual int get_srto_mss(); + // Get the srt SRTO_TSBPDMODE, timestamp base packet delivery mode, default is false. + virtual bool get_srto_tsbpdmode(); // Get the srt SRTO_LATENCY, latency, default is 0 which means peer/recv latency is 120ms. virtual int get_srto_latency(); // Get the srt SRTO_RCVLATENCY, recv latency, default is 120ms. @@ -644,10 +646,12 @@ class SrsConfig virtual int get_srto_peer_latency(); // Get the srt h264 sei filter, default is on, it will drop h264 sei packet. virtual bool get_srt_sei_filter(); - // Get the srt SRTO_TLPKDROP, Too-late Packet Drop, default is true. - virtual bool get_srto_tlpkdrop(); + // Get the srt SRTO_TLPKTDROP, Too-late Packet Drop, default is true. + virtual bool get_srto_tlpktdrop(); // Get the srt SRTO_CONNTIMEO, connection timeout, default is 3000ms. - virtual int get_srto_conntimeout(); + virtual srs_utime_t get_srto_conntimeout(); + // Get the srt SRTO_PEERIDLETIMEO, peer idle timeout, default is 10000ms. + virtual srs_utime_t get_srto_peeridletimeout(); // Get the srt SRTO_SNDBUF, send buffer, default is 8192 × (1500-28). virtual int get_srto_sendbuf(); // Get the srt SRTO_RCVBUF, recv buffer, default is 8192 × (1500-28). @@ -656,8 +660,11 @@ class SrsConfig virtual int get_srto_payloadsize(); // Get the default app. virtual std::string get_default_app_name(); - // Get the mix_correct - virtual bool get_srt_mix_correct(); +private: + SrsConfDirective* get_srt(std::string vhost); +public: + bool get_srt_enabled(std::string vhost); + bool get_srt_to_rtmp(std::string vhost); // http_hooks section private: diff --git a/trunk/src/app/srs_app_log.cpp b/trunk/src/app/srs_app_log.cpp index 89885cd6a4..38c10c4ff3 100644 --- a/trunk/src/app/srs_app_log.cpp +++ b/trunk/src/app/srs_app_log.cpp @@ -18,6 +18,7 @@ #include #include #include +#include // the max size of a line of log. #define LOG_MAX_SIZE 8192 @@ -35,6 +36,8 @@ SrsFileLog::SrsFileLog() fd = -1; log_to_file_tank = false; utc = false; + + pthread_mutex_init(&mutex_, NULL); } SrsFileLog::~SrsFileLog() @@ -49,6 +52,8 @@ SrsFileLog::~SrsFileLog() if (_srs_config) { _srs_config->unsubscribe(this); } + + pthread_mutex_destroy(&mutex_); } srs_error_t SrsFileLog::initialize() @@ -79,6 +84,8 @@ void SrsFileLog::reopen() void SrsFileLog::verbose(const char* tag, SrsContextId context_id, const char* fmt, ...) { + SrsScopeLock sl(&mutex_); + if (level > SrsLogLevelVerbose) { return; } @@ -99,6 +106,8 @@ void SrsFileLog::verbose(const char* tag, SrsContextId context_id, const char* f void SrsFileLog::info(const char* tag, SrsContextId context_id, const char* fmt, ...) { + SrsScopeLock sl(&mutex_); + if (level > SrsLogLevelInfo) { return; } @@ -119,6 +128,8 @@ void SrsFileLog::info(const char* tag, SrsContextId context_id, const char* fmt, void SrsFileLog::trace(const char* tag, SrsContextId context_id, const char* fmt, ...) { + SrsScopeLock sl(&mutex_); + if (level > SrsLogLevelTrace) { return; } @@ -139,6 +150,8 @@ void SrsFileLog::trace(const char* tag, SrsContextId context_id, const char* fmt void SrsFileLog::warn(const char* tag, SrsContextId context_id, const char* fmt, ...) { + SrsScopeLock sl(&mutex_); + if (level > SrsLogLevelWarn) { return; } @@ -159,6 +172,8 @@ void SrsFileLog::warn(const char* tag, SrsContextId context_id, const char* fmt, void SrsFileLog::error(const char* tag, SrsContextId context_id, const char* fmt, ...) { + SrsScopeLock sl(&mutex_); + if (level > SrsLogLevelError) { return; } diff --git a/trunk/src/app/srs_app_log.hpp b/trunk/src/app/srs_app_log.hpp index a186628342..fc0cbcccde 100644 --- a/trunk/src/app/srs_app_log.hpp +++ b/trunk/src/app/srs_app_log.hpp @@ -39,6 +39,9 @@ class SrsFileLog : public ISrsLog, public ISrsReloadHandler bool log_to_file_tank; // Whether use utc time. bool utc; + // TODO: FIXME: use macro define like SRS_MULTI_THREAD_LOG to switch enable log mutex or not. + // Mutex for multithread log. + pthread_mutex_t mutex_; public: SrsFileLog(); virtual ~SrsFileLog(); diff --git a/trunk/src/app/srs_app_pithy_print.cpp b/trunk/src/app/srs_app_pithy_print.cpp index ecbb3306ee..d4891f4025 100644 --- a/trunk/src/app/srs_app_pithy_print.cpp +++ b/trunk/src/app/srs_app_pithy_print.cpp @@ -219,6 +219,13 @@ SrsPithyPrint::SrsPithyPrint(int _stage_id) // for the rtc recv #define SRS_CONSTS_STAGE_RTC_RECV 14 +#ifdef SRS_SRT +// the pithy stage for srt play clients. +#define SRS_CONSTS_STAGE_SRT_PLAY 15 +// the pithy stage for srt publish clients. +#define SRS_CONSTS_STAGE_SRT_PUBLISH 16 +#endif + SrsPithyPrint* SrsPithyPrint::create_rtmp_play() { return new SrsPithyPrint(SRS_CONSTS_STAGE_PLAY_USER); @@ -289,6 +296,18 @@ SrsPithyPrint* SrsPithyPrint::create_rtc_recv(int fd) return new SrsPithyPrint(fd<<16 | SRS_CONSTS_STAGE_RTC_RECV); } +#ifdef SRS_SRT +SrsPithyPrint* SrsPithyPrint::create_srt_play() +{ + return new SrsPithyPrint(SRS_CONSTS_STAGE_SRT_PLAY); +} + +SrsPithyPrint* SrsPithyPrint::create_srt_publish() +{ + return new SrsPithyPrint(SRS_CONSTS_STAGE_SRT_PUBLISH); +} +#endif + SrsPithyPrint::~SrsPithyPrint() { leave_stage(); diff --git a/trunk/src/app/srs_app_pithy_print.hpp b/trunk/src/app/srs_app_pithy_print.hpp index b0173f630b..961d13ed8c 100644 --- a/trunk/src/app/srs_app_pithy_print.hpp +++ b/trunk/src/app/srs_app_pithy_print.hpp @@ -130,6 +130,10 @@ class SrsPithyPrint // For RTC sender and receiver, we create printer for each fd. static SrsPithyPrint* create_rtc_send(int fd); static SrsPithyPrint* create_rtc_recv(int fd); +#ifdef SRS_SRT + static SrsPithyPrint* create_srt_play(); + static SrsPithyPrint* create_srt_publish(); +#endif virtual ~SrsPithyPrint(); private: // Enter the specified stage, return the client id. diff --git a/trunk/src/app/srs_app_rtc_source.cpp b/trunk/src/app/srs_app_rtc_source.cpp index a5bc4e9a39..acf0d8875b 100644 --- a/trunk/src/app/srs_app_rtc_source.cpp +++ b/trunk/src/app/srs_app_rtc_source.cpp @@ -270,7 +270,7 @@ srs_error_t SrsRtcSourceManager::fetch_or_create(SrsRequest* r, SrsRtcSource** p // should always not exists for create a source. srs_assert (pool.find(stream_url) == pool.end()); - srs_trace("new source, stream_url=%s", stream_url.c_str()); + srs_trace("new rtc source, stream_url=%s", stream_url.c_str()); source = new SrsRtcSource(); if ((err = source->initialize(r)) != srs_success) { @@ -344,9 +344,10 @@ SrsRtcSource::~SrsRtcSource() // for all consumers are auto free. consumers.clear(); - srs_freep(req); srs_freep(bridge_); + srs_freep(stream_desc_); + srs_freep(req); } srs_error_t SrsRtcSource::initialize(SrsRequest* r) diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index 26ccb0d9da..e3d9901bfe 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -950,7 +950,7 @@ srs_error_t SrsRtmpConn::acquire_publish(SrsLiveSource* source) if (!source->can_publish(info->edge)) { return srs_error_new(ERROR_SYSTEM_STREAM_BUSY, "rtmp: stream %s is busy", req->get_stream_url().c_str()); } - + // Check whether RTC stream is busy. #ifdef SRS_RTC SrsRtcSource *rtc = NULL; diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index c440dc0c08..409eddffb8 100755 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -1802,7 +1802,7 @@ srs_error_t SrsLiveSourceManager::fetch_or_create(SrsRequest* r, ISrsLiveSourceH // should always not exists for create a source. srs_assert (pool.find(stream_url) == pool.end()); - srs_trace("new source, stream_url=%s", stream_url.c_str()); + srs_trace("new live source, stream_url=%s", stream_url.c_str()); source = new SrsLiveSource(); if ((err = source->initialize(r, h)) != srs_success) { @@ -1929,7 +1929,7 @@ SrsLiveSource::SrsLiveSource() handler = NULL; bridge_ = NULL; - + play_edge = new SrsPlayEdge(); publish_edge = new SrsPublishEdge(); gop_cache = new SrsGopCache(); @@ -2635,7 +2635,7 @@ void SrsLiveSource::on_unpublish() bridge_->on_unpublish(); srs_freep(bridge_); } - + // no consumer, stream is die. if (consumers.empty()) { die_at = srs_get_system_time(); diff --git a/trunk/src/app/srs_app_srt_conn.cpp b/trunk/src/app/srs_app_srt_conn.cpp new file mode 100644 index 0000000000..b49c03008e --- /dev/null +++ b/trunk/src/app/srs_app_srt_conn.cpp @@ -0,0 +1,697 @@ +// +// Copyright (c) 2013-2021 The SRS Authors +// +// SPDX-License-Identifier: MIT or MulanPSL-2.0 +// + +#include + +using namespace std; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +SrsSrtConnection::SrsSrtConnection(srs_srt_t srt_fd) +{ + srt_fd_ = srt_fd; + srt_skt_ = new SrsSrtSocket(_srt_eventloop->poller(), srt_fd_); +} + +SrsSrtConnection::~SrsSrtConnection() +{ + srs_freep(srt_skt_); +} + +srs_error_t SrsSrtConnection::initialize() +{ + srs_error_t err = srs_success; + return err; +} + +void SrsSrtConnection::set_recv_timeout(srs_utime_t tm) +{ + srt_skt_->set_recv_timeout(tm); +} + +srs_utime_t SrsSrtConnection::get_recv_timeout() +{ + return srt_skt_->get_recv_timeout(); +} + +srs_error_t SrsSrtConnection::read_fully(void* buf, size_t size, ssize_t* nread) +{ + return srs_error_new(ERROR_SRT_CONN, "unsupport method"); +} + +int64_t SrsSrtConnection::get_recv_bytes() +{ + return srt_skt_->get_recv_bytes(); +} + +int64_t SrsSrtConnection::get_send_bytes() +{ + return srt_skt_->get_send_bytes(); +} + +srs_error_t SrsSrtConnection::read(void* buf, size_t size, ssize_t* nread) +{ + return srt_skt_->recvmsg(buf, size, nread); +} + +void SrsSrtConnection::set_send_timeout(srs_utime_t tm) +{ + srt_skt_->set_send_timeout(tm); +} + +srs_utime_t SrsSrtConnection::get_send_timeout() +{ + return srt_skt_->get_send_timeout(); +} + +srs_error_t SrsSrtConnection::write(void* buf, size_t size, ssize_t* nwrite) +{ + return srt_skt_->sendmsg(buf, size, nwrite); +} + +srs_error_t SrsSrtConnection::writev(const iovec *iov, int iov_size, ssize_t* nwrite) +{ + return srs_error_new(ERROR_SRT_CONN, "unsupport method"); +} + +SrsSrtRecvThread::SrsSrtRecvThread(SrsSrtConnection* srt_conn) +{ + srt_conn_ = srt_conn; + trd_ = new SrsSTCoroutine("srt-recv", this, _srs_context->get_id()); + recv_err_ = srs_success; +} + +SrsSrtRecvThread::~SrsSrtRecvThread() +{ + srs_freep(trd_); + srs_error_reset(recv_err_); +} + +srs_error_t SrsSrtRecvThread::cycle() +{ + srs_error_t err = srs_success; + + if ((err = do_cycle()) != srs_success) { + recv_err_ = srs_error_copy(err); + } + + return err; +} + +srs_error_t SrsSrtRecvThread::do_cycle() +{ + srs_error_t err = srs_success; + + while (true) { + if ((err = trd_->pull()) != srs_success) { + return srs_error_wrap(err, "srt: thread quit"); + } + + char buf[1316]; + ssize_t nb = 0; + if ((err = srt_conn_->read(buf, sizeof(buf), &nb)) != srs_success) { + if (srs_error_code(err) != ERROR_SRT_TIMEOUT) { + return srs_error_wrap(err, "srt read"); + } + } + } + + return err; +} + +srs_error_t SrsSrtRecvThread::start() +{ + srs_error_t err = srs_success; + + if ((err = trd_->start()) != srs_success) { + return srs_error_wrap(err, "start srt recv thread"); + } + + return err; +} + +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) +{ + // Create a identify for this client. + _srs_context->set_id(_srs_context->generate_id()); + + srt_server_ = srt_server; + + srt_fd_ = srt_fd; + srt_conn_ = new SrsSrtConnection(srt_fd_); + clock_ = new SrsWallClock(); + kbps_ = new SrsKbps(clock_); + kbps_->set_io(srt_conn_, srt_conn_); + ip_ = ip; + port_ = port; + + trd_ = new SrsSTCoroutine("ts-srt", this, _srs_context->get_id()); + + srt_source_ = NULL; + req_ = new SrsRequest(); +} + +SrsMpegtsSrtConn::~SrsMpegtsSrtConn() +{ + srs_freep(trd_); + + srs_freep(kbps_); + srs_freep(clock_); + + srs_freep(srt_conn_); + + srs_freep(req_); +} + +std::string SrsMpegtsSrtConn::desc() +{ + return "srt-ts-conn"; +} + +void SrsMpegtsSrtConn::remark(int64_t* in, int64_t* out) +{ + // TODO: FIXME: no impl currently. + kbps_->remark(in, out); +} + +srs_error_t SrsMpegtsSrtConn::start() +{ + srs_error_t err = srs_success; + + if ((err = trd_->start()) != srs_success) { + return srs_error_wrap(err, "coroutine"); + } + + return err; +} + +std::string SrsMpegtsSrtConn::remote_ip() +{ + return ip_; +} + +const SrsContextId& SrsMpegtsSrtConn::get_id() +{ + return trd_->cid(); +} + +srs_error_t SrsMpegtsSrtConn::cycle() +{ + srs_error_t err = srs_success; + + err = do_cycle(); + + // Notify manager to remove it. + // Note that we create this object, so we use manager to remove it. + srt_server_->remove(this); + + // success. + if (err == srs_success) { + srs_trace("srt client finished."); + return err; + } + + srs_error("srt serve error %s", srs_error_desc(err).c_str()); + srs_freep(err); + return srs_success; +} + +srs_error_t SrsMpegtsSrtConn::do_cycle() +{ + srs_error_t err = srs_success; + + srs_trace("SRT client ip=%s:%d, fd=%d", ip_.c_str(), port_, srt_fd_); + + string streamid = ""; + if ((err = srs_srt_get_streamid(srt_fd_, streamid)) != srs_success) { + return srs_error_wrap(err, "get srt streamid"); + } + + // Must have streamid, because srt ts packet will convert to rtmp or rtc. + if (streamid.empty()) { + return srs_error_new(ERROR_SRT_CONN, "empty srt streamid"); + } + + // Detect streamid of srt to request. + SrtMode mode = SrtModePull; + if (! srs_srt_streamid_to_request(streamid, mode, req_)) { + return srs_error_new(ERROR_SRT_CONN, "invalid srt streamid=%s", streamid.c_str()); + } + + if (! _srs_config->get_srt_enabled(req_->vhost)) { + return srs_error_new(ERROR_SRT_CONN, "srt disabled, vhost=%s", req_->vhost.c_str()); + } + + srs_trace("@srt, streamid=%s, stream_url=%s, vhost=%s, app=%s, stream=%s, param=%s", + streamid.c_str(), req_->get_stream_url().c_str(), req_->vhost.c_str(), req_->app.c_str(), req_->stream.c_str(), req_->param.c_str()); + + if ((err = _srs_srt_sources->fetch_or_create(req_, &srt_source_)) != srs_success) { + return srs_error_wrap(err, "fetch srt source"); + } + + if ((err = http_hooks_on_connect()) != srs_success) { + return srs_error_wrap(err, "on connect"); + } + + if (mode == SrtModePush) { + err = publishing(); + } else if (mode == SrtModePull) { + err = playing(); + } + + http_hooks_on_close(); + + return err; +} + +srs_error_t SrsMpegtsSrtConn::publishing() +{ + srs_error_t err = srs_success; + + if ((err = http_hooks_on_publish()) != srs_success) { + return srs_error_wrap(err, "srt: callback on publish"); + } + + if ((err = acquire_publish()) == srs_success) { + err = do_publishing(); + release_publish(); + } + + http_hooks_on_unpublish(); + + return err; +} + +srs_error_t SrsMpegtsSrtConn::playing() +{ + srs_error_t err = srs_success; + + if ((err = http_hooks_on_play()) != srs_success) { + return srs_error_wrap(err, "rtmp: callback on play"); + } + + err = do_playing(); + http_hooks_on_stop(); + + return err; +} + +// TODO: FIXME: It's not atomic and has risk between multiple source checking. +srs_error_t SrsMpegtsSrtConn::acquire_publish() +{ + srs_error_t err = srs_success; + + // Check srt stream is busy. + if (! srt_source_->can_publish()) { + return srs_error_new(ERROR_SRT_SOURCE_BUSY, "srt stream %s busy", req_->get_stream_url().c_str()); + } + + if (_srs_config->get_srt_to_rtmp(req_->vhost)) { + // Check rtmp stream is busy. + SrsLiveSource *live_source = _srs_sources->fetch(req_); + if (live_source && !live_source->can_publish(false)) { + return srs_error_new(ERROR_SYSTEM_STREAM_BUSY, "live_source stream %s busy", req_->get_stream_url().c_str()); + } + + if ((err = _srs_sources->fetch_or_create(req_, _srs_hybrid->srs()->instance(), &live_source)) != srs_success) { + return srs_error_wrap(err, "create source"); + } + + SrsRtmpFromSrtBridge *bridge = new SrsRtmpFromSrtBridge(live_source); + if ((err = bridge->initialize(req_)) != srs_success) { + srs_freep(bridge); + return srs_error_wrap(err, "create bridge"); + } + + srt_source_->set_bridge(bridge); + } + + if ((err = srt_source_->on_publish()) != srs_success) { + return srs_error_wrap(err, "srt source publish"); + } + + return err; +} + +void SrsMpegtsSrtConn::release_publish() +{ + srt_source_->on_unpublish(); +} + +srs_error_t SrsMpegtsSrtConn::do_publishing() +{ + srs_error_t err = srs_success; + + SrsPithyPrint* pprint = SrsPithyPrint::create_srt_publish(); + SrsAutoFree(SrsPithyPrint, pprint); + + int nb_packets = 0; + + // Max udp packet size equal to 1500. + char buf[1500]; + while (true) { + if ((err = trd_->pull()) != srs_success) { + return srs_error_wrap(err, "srt: thread quit"); + } + + pprint->elapse(); + if (pprint->can_print()) { + SrsSrtStat s; + if ((err = s.fetch(srt_fd_, true)) != srs_success) { + srs_freep(err); + } else { + srs_trace("<- " SRS_CONSTS_LOG_SRT_PUBLISH " Transport Stats # pktRecv=%" PRId64 ", pktRcvLoss=%d, pktRcvRetrans=%d, pktRcvDrop=%d", + s.pktRecv(), s.pktRcvLoss(), s.pktRcvRetrans(), s.pktRcvDrop()); + } + + kbps_->sample(); + + srs_trace("<- " SRS_CONSTS_LOG_SRT_PUBLISH " time=%d, packets=%d, okbps=%d,%d,%d, ikbps=%d,%d,%d", + (int)pprint->age(), nb_packets, kbps_->get_send_kbps(), kbps_->get_send_kbps_30s(), kbps_->get_send_kbps_5m(), + kbps_->get_recv_kbps(), kbps_->get_recv_kbps_30s(), kbps_->get_recv_kbps_5m()); + nb_packets = 0; + } + + ssize_t nb = 0; + if ((err = srt_conn_->read(buf, sizeof(buf), &nb)) != srs_success) { + return srs_error_wrap(err, "srt: recvmsg"); + } + + ++nb_packets; + + if ((err = on_srt_packet(buf, nb)) != srs_success) { + return srs_error_wrap(err, "srt: process packet"); + } + } + + return err; +} + +srs_error_t SrsMpegtsSrtConn::do_playing() +{ + srs_error_t err = srs_success; + + SrsSrtConsumer* consumer = NULL; + SrsAutoFree(SrsSrtConsumer, consumer); + if ((err = srt_source_->create_consumer(consumer)) != srs_success) { + return srs_error_wrap(err, "create consumer, ts source=%s", req_->get_stream_url().c_str()); + } + srs_assert(consumer); + + // TODO: FIXME: Dumps the SPS/PPS from gop cache, without other frames. + if ((err = srt_source_->consumer_dumps(consumer)) != srs_success) { + return srs_error_wrap(err, "dumps consumer, url=%s", req_->get_stream_url().c_str()); + } + + SrsPithyPrint* pprint = SrsPithyPrint::create_srt_play(); + SrsAutoFree(SrsPithyPrint, pprint); + + SrsSrtRecvThread srt_recv_trd(srt_conn_); + if ((err = srt_recv_trd.start()) != srs_success) { + return srs_error_wrap(err, "start srt recv trd"); + } + + int nb_packets = 0; + + while (true) { + if ((err = trd_->pull()) != srs_success) { + return srs_error_wrap(err, "srt play thread"); + } + + if ((err = srt_recv_trd.get_recv_err()) != srs_success) { + return srs_error_wrap(err, "srt play recv thread"); + } + + // Wait for amount of packets. + SrsSrtPacket* pkt = NULL; + SrsAutoFree(SrsSrtPacket, pkt); + consumer->dump_packet(&pkt); + if (!pkt) { + // TODO: FIXME: We should check the quit event. + consumer->wait(1, 1000 * SRS_UTIME_MILLISECONDS); + continue; + } + + ++nb_packets; + + // reportable + pprint->elapse(); + if (pprint->can_print()) { + SrsSrtStat s; + if ((err = s.fetch(srt_fd_, true)) != srs_success) { + srs_freep(err); + } else { + srs_trace("-> " SRS_CONSTS_LOG_SRT_PLAY " Transport Stats # pktSent=%" PRId64 ", pktSndLoss=%d, pktRetrans=%d, pktSndDrop=%d", + s.pktSent(), s.pktSndLoss(), s.pktRetrans(), s.pktSndDrop()); + } + + kbps_->sample(); + + srs_trace("-> " SRS_CONSTS_LOG_SRT_PLAY " time=%d, packets=%d, okbps=%d,%d,%d, ikbps=%d,%d,%d", + (int)pprint->age(), nb_packets, kbps_->get_send_kbps(), kbps_->get_send_kbps_30s(), kbps_->get_send_kbps_5m(), + kbps_->get_recv_kbps(), kbps_->get_recv_kbps_30s(), kbps_->get_recv_kbps_5m()); + nb_packets = 0; + } + + ssize_t nb_write = 0; + if ((err = srt_conn_->write(pkt->data(), pkt->size(), &nb_write)) != srs_success) { + return srs_error_wrap(err, "srt send, size=%d", pkt->size()); + } + + // Yield to another coroutines. + // @see https://github.com/ossrs/srs/issues/2194#issuecomment-777542162 + // TODO: FIXME: Please check whether SRT sendmsg causing clock deviation, see srs_thread_yield of SrsUdpMuxSocket::sendto + } + + return err; +} + +srs_error_t SrsMpegtsSrtConn::on_srt_packet(char* buf, int nb_buf) +{ + srs_error_t err = srs_success; + + // Ignore if invalid length. + if (nb_buf <= 0) { + return err; + } + + // Check srt payload, mpegts must be N times of SRS_TS_PACKET_SIZE + if ((nb_buf % SRS_TS_PACKET_SIZE) != 0) { + return srs_error_new(ERROR_SRT_CONN, "invalid ts packet len=%d", nb_buf); + } + + // Check srt payload, the first byte must be 0x47 + if (buf[0] != 0x47) { + return srs_error_new(ERROR_SRT_CONN, "invalid ts packet first=%#x", (uint8_t)buf[0]); + } + + SrsSrtPacket* packet = new SrsSrtPacket(); + SrsAutoFree(SrsSrtPacket, packet); + packet->wrap(buf, nb_buf); + + if ((err = srt_source_->on_packet(packet)) != srs_success) { + return srs_error_wrap(err, "on srt packet"); + } + + return err; +} + +srs_error_t SrsMpegtsSrtConn::http_hooks_on_connect() +{ + srs_error_t err = srs_success; + + if (!_srs_config->get_vhost_http_hooks_enabled(req_->vhost)) { + return err; + } + + // the http hooks will cause context switch, + // so we must copy all hooks for the on_connect may freed. + // @see https://github.com/ossrs/srs/issues/475 + vector hooks; + + if (true) { + SrsConfDirective* conf = _srs_config->get_vhost_on_connect(req_->vhost); + + if (!conf) { + return err; + } + + hooks = conf->args; + } + + for (int i = 0; i < (int)hooks.size(); i++) { + std::string url = hooks.at(i); + if ((err = SrsHttpHooks::on_connect(url, req_)) != srs_success) { + return srs_error_wrap(err, "srt on_connect %s", url.c_str()); + } + } + + return err; +} + +void SrsMpegtsSrtConn::http_hooks_on_close() +{ + if (!_srs_config->get_vhost_http_hooks_enabled(req_->vhost)) { + return; + } + + // the http hooks will cause context switch, + // so we must copy all hooks for the on_connect may freed. + // @see https://github.com/ossrs/srs/issues/475 + vector hooks; + + if (true) { + SrsConfDirective* conf = _srs_config->get_vhost_on_close(req_->vhost); + + if (!conf) { + return; + } + + hooks = conf->args; + } + + for (int i = 0; i < (int)hooks.size(); i++) { + std::string url = hooks.at(i); + SrsHttpHooks::on_close(url, req_, kbps_->get_send_bytes(), kbps_->get_recv_bytes()); + } +} + +srs_error_t SrsMpegtsSrtConn::http_hooks_on_publish() +{ + srs_error_t err = srs_success; + + if (!_srs_config->get_vhost_http_hooks_enabled(req_->vhost)) { + return err; + } + + // the http hooks will cause context switch, + // so we must copy all hooks for the on_connect may freed. + // @see https://github.com/ossrs/srs/issues/475 + vector hooks; + + if (true) { + SrsConfDirective* conf = _srs_config->get_vhost_on_publish(req_->vhost); + + if (!conf) { + return err; + } + + hooks = conf->args; + } + + for (int i = 0; i < (int)hooks.size(); i++) { + std::string url = hooks.at(i); + if ((err = SrsHttpHooks::on_publish(url, req_)) != srs_success) { + return srs_error_wrap(err, "srt on_publish %s", url.c_str()); + } + } + + return err; +} + +void SrsMpegtsSrtConn::http_hooks_on_unpublish() +{ + if (!_srs_config->get_vhost_http_hooks_enabled(req_->vhost)) { + return; + } + + // the http hooks will cause context switch, + // so we must copy all hooks for the on_connect may freed. + // @see https://github.com/ossrs/srs/issues/475 + vector hooks; + + if (true) { + SrsConfDirective* conf = _srs_config->get_vhost_on_unpublish(req_->vhost); + + if (!conf) { + return; + } + + hooks = conf->args; + } + + for (int i = 0; i < (int)hooks.size(); i++) { + std::string url = hooks.at(i); + SrsHttpHooks::on_unpublish(url, req_); + } +} + +srs_error_t SrsMpegtsSrtConn::http_hooks_on_play() +{ + srs_error_t err = srs_success; + + if (!_srs_config->get_vhost_http_hooks_enabled(req_->vhost)) { + return err; + } + + // the http hooks will cause context switch, + // so we must copy all hooks for the on_connect may freed. + // @see https://github.com/ossrs/srs/issues/475 + vector hooks; + + if (true) { + SrsConfDirective* conf = _srs_config->get_vhost_on_play(req_->vhost); + + if (!conf) { + return err; + } + + hooks = conf->args; + } + + for (int i = 0; i < (int)hooks.size(); i++) { + std::string url = hooks.at(i); + if ((err = SrsHttpHooks::on_play(url, req_)) != srs_success) { + return srs_error_wrap(err, "srt on_play %s", url.c_str()); + } + } + + return err; +} + +void SrsMpegtsSrtConn::http_hooks_on_stop() +{ + if (!_srs_config->get_vhost_http_hooks_enabled(req_->vhost)) { + return; + } + + // the http hooks will cause context switch, + // so we must copy all hooks for the on_connect may freed. + // @see https://github.com/ossrs/srs/issues/475 + vector hooks; + + if (true) { + SrsConfDirective* conf = _srs_config->get_vhost_on_stop(req_->vhost); + + if (!conf) { + return; + } + + hooks = conf->args; + } + + for (int i = 0; i < (int)hooks.size(); i++) { + std::string url = hooks.at(i); + SrsHttpHooks::on_stop(url, req_); + } + + return; +} diff --git a/trunk/src/app/srs_app_srt_conn.hpp b/trunk/src/app/srs_app_srt_conn.hpp new file mode 100644 index 0000000000..76eaa53b08 --- /dev/null +++ b/trunk/src/app/srs_app_srt_conn.hpp @@ -0,0 +1,126 @@ +// +// Copyright (c) 2013-2021 The SRS Authors +// +// SPDX-License-Identifier: MIT or MulanPSL-2.0 +// + +#ifndef SRS_APP_SRT_CONN_HPP +#define SRS_APP_SRT_CONN_HPP + +#include + +#include +#include + +#include +#include +#include +#include + +class SrsBuffer; +class SrsLiveSource; +class SrsSrtSource; +class SrsSrtServer; + +// The basic connection of SRS, for SRT based protocols, +// all srt connections accept from srt listener must extends from this base class, +// srt server will add the connection to manager, and delete it when remove. +class SrsSrtConnection : public ISrsProtocolReadWriter +{ +public: + SrsSrtConnection(srs_srt_t srt_fd); + virtual ~SrsSrtConnection(); +public: + virtual srs_error_t initialize(); +// Interface ISrsProtocolReadWriter +public: + virtual void set_recv_timeout(srs_utime_t tm); + virtual srs_utime_t get_recv_timeout(); + virtual srs_error_t read_fully(void* buf, size_t size, ssize_t* nread); + virtual int64_t get_recv_bytes(); + virtual int64_t get_send_bytes(); + virtual srs_error_t read(void* buf, size_t size, ssize_t* nread); + virtual void set_send_timeout(srs_utime_t tm); + virtual srs_utime_t get_send_timeout(); + virtual srs_error_t write(void* buf, size_t size, ssize_t* nwrite); + virtual srs_error_t writev(const iovec *iov, int iov_size, ssize_t* nwrite); +private: + // The underlayer srt fd handler. + srs_srt_t srt_fd_; + // The underlayer srt socket. + SrsSrtSocket* srt_skt_; +}; + +class SrsSrtRecvThread : public ISrsCoroutineHandler +{ +public: + SrsSrtRecvThread(SrsSrtConnection* srt_conn); + ~SrsSrtRecvThread(); +// Interface ISrsCoroutineHandler +public: + virtual srs_error_t cycle(); +private: + srs_error_t do_cycle(); +public: + srs_error_t start(); + srs_error_t get_recv_err(); +private: + SrsSrtConnection* srt_conn_; + SrsCoroutine* trd_; + srs_error_t recv_err_; +}; + +class SrsMpegtsSrtConn : public ISrsStartableConneciton, public ISrsCoroutineHandler +{ +public: + SrsMpegtsSrtConn(SrsSrtServer* srt_server, srs_srt_t srt_fd, std::string ip, int port); + virtual ~SrsMpegtsSrtConn(); +// Interface ISrsResource. +public: + virtual std::string desc(); +// Interface ISrsKbpsDelta +public: + virtual void remark(int64_t* in, int64_t* out); +public: + virtual srs_error_t start(); +// Interface ISrsConnection. +public: + virtual std::string remote_ip(); + virtual const SrsContextId& get_id(); +// Interface ISrsCoroutineHandler +public: + virtual srs_error_t cycle(); +protected: + virtual srs_error_t do_cycle(); +private: + srs_error_t publishing(); + srs_error_t playing(); + srs_error_t acquire_publish(); + void release_publish(); + srs_error_t do_publishing(); + srs_error_t do_playing(); +private: + srs_error_t on_srt_packet(char* buf, int nb_buf); +private: + srs_error_t http_hooks_on_connect(); + void http_hooks_on_close(); + srs_error_t http_hooks_on_publish(); + void http_hooks_on_unpublish(); + srs_error_t http_hooks_on_play(); + void http_hooks_on_stop(); +private: + SrsSrtServer* srt_server_; + srs_srt_t srt_fd_; + SrsSrtConnection* srt_conn_; + SrsWallClock* clock_; + SrsKbps* kbps_; + std::string ip_; + int port_; + SrsCoroutine* trd_; + + SrsRequest* req_; + SrsSrtSource* srt_source_; +}; + +#endif + diff --git a/trunk/src/app/srs_app_srt_listener.cpp b/trunk/src/app/srs_app_srt_listener.cpp new file mode 100644 index 0000000000..4f223957c4 --- /dev/null +++ b/trunk/src/app/srs_app_srt_listener.cpp @@ -0,0 +1,101 @@ +// +// Copyright (c) 2013-2021 The SRS Authors +// +// SPDX-License-Identifier: MIT or MulanPSL-2.0 +// + +#include + +#include + +using namespace std; + +#include + +ISrsSrtHandler::ISrsSrtHandler() +{ +} + +ISrsSrtHandler::~ISrsSrtHandler() +{ +} + +SrsSrtListener::SrsSrtListener(ISrsSrtHandler* h, std::string i, int p) +{ + handler_ = h; + ip_ = i; + port_ = p; + + lfd_ = srs_srt_socket_invalid(); + srt_skt_ = NULL; + + trd_ = new SrsDummyCoroutine(); +} + +SrsSrtListener::~SrsSrtListener() +{ + srs_freep(trd_); + srs_freep(srt_skt_); + // TODO: FIXME: Handle error. + srs_srt_close(lfd_); +} + +int SrsSrtListener::fd() +{ + return lfd_; +} + +srs_error_t SrsSrtListener::create_socket() +{ + srs_error_t err = srs_success; + if ((err = srs_srt_socket(&lfd_)) != srs_success) { + return srs_error_wrap(err, "create_socket"); + } + return err; +} + +srs_error_t SrsSrtListener::listen() +{ + srs_error_t err = srs_success; + + if ((err = srs_srt_listen(lfd_, ip_, port_)) != srs_success) { + return srs_error_wrap(err, "srs_srt_listen"); + } + + srt_skt_ = new SrsSrtSocket(_srt_eventloop->poller(), lfd_); + // Accept never timeout. + srt_skt_->set_recv_timeout(ST_UTIME_NO_TIMEOUT); + srt_skt_->set_send_timeout(ST_UTIME_NO_TIMEOUT); + + srs_freep(trd_); + trd_ = new SrsSTCoroutine("srt_listener", this); + if ((err = trd_->start()) != srs_success) { + return srs_error_wrap(err, "start coroutine"); + } + + return err; +} + +srs_error_t SrsSrtListener::cycle() +{ + srs_error_t err = srs_success; + + while (true) { + if ((err = trd_->pull()) != srs_success) { + return srs_error_wrap(err, "srt listener"); + } + + srs_srt_t client_srt_fd = srs_srt_socket_invalid(); + if ((err = srt_skt_->accept(&client_srt_fd)) != srs_success) { + return srs_error_wrap(err, "srt accept"); + } + + // TODO: FIXME: print some log and client srt options. + + if ((err = handler_->on_srt_client(client_srt_fd)) != srs_success) { + return srs_error_wrap(err, "handle srt fd=%d", client_srt_fd); + } + } + + return err; +} diff --git a/trunk/src/app/srs_app_srt_listener.hpp b/trunk/src/app/srs_app_srt_listener.hpp new file mode 100644 index 0000000000..86a3a7ae05 --- /dev/null +++ b/trunk/src/app/srs_app_srt_listener.hpp @@ -0,0 +1,53 @@ +// +// Copyright (c) 2013-2021 The SRS Authors +// +// SPDX-License-Identifier: MIT or MulanPSL-2.0 +// + +#ifndef SRS_APP_SRT_LISTENER_HPP +#define SRS_APP_SRT_LISTENER_HPP + +#include +#include +#include + +#include + +// The srt connection handler. +class ISrsSrtHandler +{ +public: + ISrsSrtHandler(); + virtual ~ISrsSrtHandler(); +public: + // When got srt client. + virtual srs_error_t on_srt_client(srs_srt_t srt_fd) = 0; +}; + +// Bind and listen SRT(udp) port, use handler to process the client. +class SrsSrtListener : public ISrsCoroutineHandler +{ +private: + srs_srt_t lfd_; + SrsSrtSocket* srt_skt_; + SrsCoroutine* trd_; +private: + ISrsSrtHandler* handler_; + std::string ip_; + int port_; +public: + SrsSrtListener(ISrsSrtHandler* h, std::string i, int p); + virtual ~SrsSrtListener(); +public: + virtual srs_srt_t fd(); +public: + // Create srt socket, separate this step because of srt have some option must set before listen. + virtual srs_error_t create_socket(); + virtual srs_error_t listen(); +// Interface ISrsReusableThreadHandler. +public: + virtual srs_error_t cycle(); +}; + +#endif + diff --git a/trunk/src/app/srs_app_srt_server.cpp b/trunk/src/app/srs_app_srt_server.cpp new file mode 100644 index 0000000000..5952438326 --- /dev/null +++ b/trunk/src/app/srs_app_srt_server.cpp @@ -0,0 +1,375 @@ +// +// Copyright (c) 2013-2021 The SRS Authors +// +// SPDX-License-Identifier: MIT or MulanPSL-2.0 +// + +#include + +using namespace std; + +#include +#include +#include +#include +#include + +#ifdef SRS_SRT +SrsSrtEventLoop* _srt_eventloop = NULL; +#endif + +SrsSrtAcceptor::SrsSrtAcceptor(SrsSrtServer* srt_server) +{ + port_ = 0; + srt_server_ = srt_server; + listener_ = NULL; +} + +SrsSrtAcceptor::~SrsSrtAcceptor() +{ + srs_freep(listener_); +} + +srs_error_t SrsSrtAcceptor::listen(std::string ip, int port) +{ + srs_error_t err = srs_success; + + ip_ = ip; + port_ = port; + + srs_freep(listener_); + listener_ = new SrsSrtListener(this, ip_, port_); + + // Create srt socket. + if ((err = listener_->create_socket()) != srs_success) { + return srs_error_wrap(err, "message srt acceptor"); + } + + // Set all the srt option from config. + if ((err = set_srt_opt()) != srs_success) { + return srs_error_wrap(err, "set opt"); + } + + // Start listen srt socket, this function will set the socket in async mode. + if ((err = listener_->listen()) != srs_success) { + return srs_error_wrap(err, "message srt acceptor"); + } + + srs_trace("srt listen at udp://%s:%d, fd=%d", ip_.c_str(), port_, listener_->fd()); + + return err; +} + +srs_error_t SrsSrtAcceptor::set_srt_opt() +{ + srs_error_t err = srs_success; + + if ((err = srs_srt_set_maxbw(listener_->fd(), _srs_config->get_srto_maxbw())) != srs_success) { + return srs_error_wrap(err, "set opt"); + } + + if ((err = srs_srt_set_mss(listener_->fd(), _srs_config->get_srto_mss())) != srs_success) { + return srs_error_wrap(err, "set opt"); + } + + if ((err = srs_srt_set_tsbpdmode(listener_->fd(), _srs_config->get_srto_tsbpdmode())) != srs_success) { + return srs_error_wrap(err, "set opt"); + } + + if ((err = srs_srt_set_latency(listener_->fd(), _srs_config->get_srto_latency())) != srs_success) { + return srs_error_wrap(err, "set opt"); + } + + if ((err = srs_srt_set_rcv_latency(listener_->fd(), _srs_config->get_srto_recv_latency())) != srs_success) { + return srs_error_wrap(err, "set opt"); + } + + if ((err = srs_srt_set_peer_latency(listener_->fd(), _srs_config->get_srto_peer_latency())) != srs_success) { + return srs_error_wrap(err, "set opt"); + } + + if ((err = srs_srt_set_tlpktdrop(listener_->fd(), _srs_config->get_srto_tlpktdrop())) != srs_success) { + return srs_error_wrap(err, "set opt"); + } + + if ((err = srs_srt_set_connect_timeout(listener_->fd(), srsu2msi(_srs_config->get_srto_conntimeout()))) != srs_success) { + return srs_error_wrap(err, "set opt"); + } + + if ((err = srs_srt_set_peer_idle_timeout(listener_->fd(), srsu2msi(_srs_config->get_srto_peeridletimeout()))) != srs_success) { + return srs_error_wrap(err, "set opt"); + } + + if ((err = srs_srt_set_sndbuf(listener_->fd(), _srs_config->get_srto_sendbuf())) != srs_success) { + return srs_error_wrap(err, "set opt"); + } + + if ((err = srs_srt_set_rcvbuf(listener_->fd(), _srs_config->get_srto_recvbuf())) != srs_success) { + return srs_error_wrap(err, "set opt"); + } + + if ((err = srs_srt_set_payload_size(listener_->fd(), _srs_config->get_srto_payloadsize())) != srs_success) { + return srs_error_wrap(err, "set opt"); + } + + return err; +} + +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) { + srs_warn("accept srt client failed, err is %s", srs_error_desc(err).c_str()); + srs_freep(err); + } + + return err; +} + +SrsSrtServer::SrsSrtServer() +{ + conn_manager_ = new SrsResourceManager("SRT", true); +} + +SrsSrtServer::~SrsSrtServer() +{ + srs_freep(conn_manager_); +} + +srs_error_t SrsSrtServer::initialize() +{ + srs_error_t err = srs_success; + 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 a listener for SRT, we might need multiple listeners in the future. + SrsSrtAcceptor* acceptor = new SrsSrtAcceptor(this); + acceptors_.push_back(acceptor); + + int port; string ip; + srs_parse_endpoint(srs_int2str(_srs_config->get_srt_listen_port()), ip, port); + + if ((err = acceptor->listen(ip, port)) != srs_success) { + return srs_error_wrap(err, "srt listen %s:%d", ip.c_str(), port); + } + + 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; + + ISrsStartableConneciton* conn = NULL; + if ((err = fd_to_resource(srt_fd, &conn)) != 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(conn); + + // directly enqueue, the cycle thread will remove the client. + conn_manager_->add(conn); + + 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, ISrsStartableConneciton** 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) +{ + // TODO: FIXME: add some statistic of srt. + // ISrsStartableConneciton* conn = dynamic_cast(c); + + // SrsStatistic* stat = SrsStatistic::instance(); + // stat->kbps_add_delta(c->get_id().c_str(), conn); + // stat->on_disconnect(c->get_id().c_str()); + + // use manager to free it async. + conn_manager_->remove(c); +} + +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_initialie()) != 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; + trd_ = NULL; +} + +SrsSrtEventLoop::~SrsSrtEventLoop() +{ + srs_freep(trd_); + srs_freep(srt_poller_); +} + +srs_error_t SrsSrtEventLoop::initialize() +{ + srs_error_t err = srs_success; + + srt_poller_ = srs_srt_poller_new(); + + if ((err = srt_poller_->initialize()) != srs_success) { + return srs_error_wrap(err, "srt poller initialize"); + } + + return err; +} + +srs_error_t SrsSrtEventLoop::start() +{ + srs_error_t err = srs_success; + + trd_ = new SrsSTCoroutine("srt_listener", this); + if ((err = trd_->start()) != srs_success) { + return srs_error_wrap(err, "start coroutine"); + } + + return err; +} + +srs_error_t SrsSrtEventLoop::cycle() +{ + srs_error_t err = srs_success; + + while (true) { + if ((err = trd_->pull()) != srs_success) { + return srs_error_wrap(err, "srt listener"); + } + + // Check and notify fired SRT events by epoll. + // + // Note that the SRT poller use a dedicated and isolated epoll, which is not the same as the one of SRS, in + // short, the wait won't switch to other coroutines when no fd is active, so we must use timeout(0) to make sure + // to return directly, then use srs_usleep to do the coroutine switch. + int n_fds = 0; + if ((err = srt_poller_->wait(0, &n_fds)) != srs_success) { + srs_warn("srt poll wait failed, n_fds=%d, err=%s", n_fds, srs_error_desc(err).c_str()); + srs_error_reset(err); + } + + // We use sleep to switch to other coroutines, because the SRT poller is not possible to do this. + srs_usleep((n_fds ? 1 : 10) * SRS_UTIME_MILLISECONDS); + } + + return err; +} + diff --git a/trunk/src/app/srs_app_srt_server.hpp b/trunk/src/app/srs_app_srt_server.hpp new file mode 100644 index 0000000000..9c266c09f3 --- /dev/null +++ b/trunk/src/app/srs_app_srt_server.hpp @@ -0,0 +1,110 @@ +// +// Copyright (c) 2013-2021 The SRS Authors +// +// SPDX-License-Identifier: MIT or MulanPSL-2.0 +// + +#ifndef SRS_APP_SRT_SERVER_HPP +#define SRS_APP_SRT_SERVER_HPP + +#include + +#include +#include +#include + +class SrsSrtServer; + +// A common srt acceptor, for SRT server. +class SrsSrtAcceptor : public ISrsSrtHandler +{ +private: + std::string ip_; + int port_; + SrsSrtServer* srt_server_; +private: + SrsSrtListener* listener_; +public: + SrsSrtAcceptor(SrsSrtServer* srt_server); + virtual ~SrsSrtAcceptor(); +public: + virtual srs_error_t listen(std::string ip, int port); +private: + virtual srs_error_t set_srt_opt(); +// Interface ISrsSrtHandler +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 +{ +private: + SrsResourceManager* conn_manager_; +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, ISrsStartableConneciton** 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); +}; + +// 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 +{ +public: + SrsSrtEventLoop(); + virtual ~SrsSrtEventLoop(); +public: + ISrsSrtPoller* poller() { return srt_poller_; } +public: + srs_error_t initialize(); + srs_error_t start(); +// Interface ISrsCoroutineHandler. +public: + virtual srs_error_t cycle(); +private: + ISrsSrtPoller* srt_poller_; + SrsCoroutine* trd_; +}; + +// SrsSrtEventLoop is global singleton instance. +extern SrsSrtEventLoop* _srt_eventloop; + +#endif + diff --git a/trunk/src/app/srs_app_srt_source.cpp b/trunk/src/app/srs_app_srt_source.cpp new file mode 100644 index 0000000000..ca13fb9dfa --- /dev/null +++ b/trunk/src/app/srs_app_srt_source.cpp @@ -0,0 +1,796 @@ +// +// Copyright (c) 2013-2021 The SRS Authors +// +// SPDX-License-Identifier: MIT or MulanPSL-2.0 +// + +#include + +#include +using namespace std; + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +SrsSrtPacket::SrsSrtPacket() +{ + shared_buffer_ = NULL; + actual_buffer_size_ = 0; +} + +SrsSrtPacket::~SrsSrtPacket() +{ + srs_freep(shared_buffer_); +} + +char* SrsSrtPacket::wrap(int size) +{ + // The buffer size is larger or equals to the size of packet. + actual_buffer_size_ = size; + + // If the buffer is large enough, reuse it. + if (shared_buffer_ && shared_buffer_->size >= size) { + return shared_buffer_->payload; + } + + // Create a large enough message, with under-layer buffer. + srs_freep(shared_buffer_); + shared_buffer_ = new SrsSharedPtrMessage(); + + char* buf = new char[size]; + shared_buffer_->wrap(buf, size); + + return shared_buffer_->payload; +} + +char* SrsSrtPacket::wrap(char* data, int size) +{ + char* buf = wrap(size); + memcpy(buf, data, size); + return buf; +} + +char* SrsSrtPacket::wrap(SrsSharedPtrMessage* msg) +{ + // Generally, the wrap(msg) is used for RTMP to SRT, where the msg + // is not generated by SRT. + srs_freep(shared_buffer_); + + // Copy from the new message. + shared_buffer_ = msg->copy(); + // If we wrap a message, the size of packet equals to the message size. + actual_buffer_size_ = shared_buffer_->size; + + return msg->payload; +} + +SrsSrtPacket* SrsSrtPacket::copy() +{ + SrsSrtPacket* cp = new SrsSrtPacket(); + + cp->shared_buffer_ = shared_buffer_? shared_buffer_->copy2() : NULL; + cp->actual_buffer_size_ = actual_buffer_size_; + + return cp; +} + +char* SrsSrtPacket::data() +{ + return shared_buffer_->payload; +} + +int SrsSrtPacket::size() +{ + return shared_buffer_->size; +} + +SrsSrtSourceManager::SrsSrtSourceManager() +{ + lock = srs_mutex_new(); +} + +SrsSrtSourceManager::~SrsSrtSourceManager() +{ + srs_mutex_destroy(lock); +} + +srs_error_t SrsSrtSourceManager::fetch_or_create(SrsRequest* r, SrsSrtSource** pps) +{ + srs_error_t err = srs_success; + + // Use lock to protect coroutine switch. + // @bug https://github.com/ossrs/srs/issues/1230 + SrsLocker(lock); + + SrsSrtSource* source = NULL; + if ((source = fetch(r)) != NULL) { + // we always update the request of resource, + // for origin auth is on, the token in request maybe invalid, + // and we only need to update the token of request, it's simple. + source->update_auth(r); + *pps = source; + return err; + } + + string stream_url = r->get_stream_url(); + string vhost = r->vhost; + + // should always not exists for create a source. + srs_assert (pool.find(stream_url) == pool.end()); + + srs_trace("new srt source, stream_url=%s", stream_url.c_str()); + + source = new SrsSrtSource(); + if ((err = source->initialize(r)) != srs_success) { + return srs_error_wrap(err, "init source %s", r->get_stream_url().c_str()); + } + + pool[stream_url] = source; + + *pps = source; + + return err; +} + +SrsSrtSource* SrsSrtSourceManager::fetch(SrsRequest* r) +{ + SrsSrtSource* source = NULL; + + string stream_url = r->get_stream_url(); + if (pool.find(stream_url) == pool.end()) { + return NULL; + } + + source = pool[stream_url]; + + return source; +} + +SrsSrtSourceManager* _srs_srt_sources = NULL; + +SrsSrtConsumer::SrsSrtConsumer(SrsSrtSource* s) +{ + source = s; + should_update_source_id = false; + + mw_wait = srs_cond_new(); + mw_min_msgs = 0; + mw_waiting = false; +} + +SrsSrtConsumer::~SrsSrtConsumer() +{ + source->on_consumer_destroy(this); + + vector::iterator it; + for (it = queue.begin(); it != queue.end(); ++it) { + SrsSrtPacket* pkt = *it; + srs_freep(pkt); + } + + srs_cond_destroy(mw_wait); +} + +void SrsSrtConsumer::update_source_id() +{ + should_update_source_id = true; +} + +srs_error_t SrsSrtConsumer::enqueue(SrsSrtPacket* packet) +{ + srs_error_t err = srs_success; + + queue.push_back(packet); + + if (mw_waiting) { + if ((int)queue.size() > mw_min_msgs) { + srs_cond_signal(mw_wait); + mw_waiting = false; + return err; + } + } + + return err; +} + +srs_error_t SrsSrtConsumer::dump_packet(SrsSrtPacket** ppkt) +{ + srs_error_t err = srs_success; + + if (should_update_source_id) { + srs_trace("update source_id=%s/%s", source->source_id().c_str(), source->pre_source_id().c_str()); + should_update_source_id = false; + } + + // TODO: FIXME: Refine performance by ring buffer. + if (!queue.empty()) { + *ppkt = queue.front(); + queue.erase(queue.begin()); + } + + return err; +} + +void SrsSrtConsumer::wait(int nb_msgs, srs_utime_t timeout) +{ + mw_min_msgs = nb_msgs; + + // when duration ok, signal to flush. + if ((int)queue.size() > mw_min_msgs) { + return; + } + + // the enqueue will notify this cond. + mw_waiting = true; + + // use cond block wait for high performance mode. + srs_cond_timedwait(mw_wait, timeout); +} + +ISrsSrtSourceBridge::ISrsSrtSourceBridge() +{ +} + +ISrsSrtSourceBridge::~ISrsSrtSourceBridge() +{ +} + +SrsRtmpFromSrtBridge::SrsRtmpFromSrtBridge(SrsLiveSource* source) : ISrsSrtSourceBridge() +{ + ts_ctx_ = new SrsTsContext(); + + sps_pps_change_ = false; + sps_ = ""; + pps_ = ""; + + live_source_ = source; + req_ = NULL; +} + +SrsRtmpFromSrtBridge::~SrsRtmpFromSrtBridge() +{ + srs_freep(ts_ctx_); + srs_freep(req_); +} + +srs_error_t SrsRtmpFromSrtBridge::on_publish() +{ + srs_error_t err = srs_success; + + if ((err = live_source_->on_publish()) != srs_success) { + return srs_error_wrap(err, "on publish"); + } + + return err; +} + +srs_error_t SrsRtmpFromSrtBridge::on_packet(SrsSrtPacket *pkt) +{ + srs_error_t err = srs_success; + + char* buf = pkt->data(); + int nb_buf = pkt->size(); + + // use stream to parse ts packet. + int nb_packet = nb_buf / SRS_TS_PACKET_SIZE; + for (int i = 0; i < nb_packet; i++) { + char* p = buf + (i * SRS_TS_PACKET_SIZE); + + SrsBuffer* stream = new SrsBuffer(p, SRS_TS_PACKET_SIZE); + SrsAutoFree(SrsBuffer, stream); + + // Process each ts packet. Note that the jitter of UDP may cause video glitch when packet loss or wrong seq. We + // don't handle it because SRT will, see tlpkdrop at https://github.com/ossrs/srs/wiki/v5_EN_SRTParams + if ((err = ts_ctx_->decode(stream, this)) != srs_success) { + srs_warn("parse ts packet err=%s", srs_error_desc(err).c_str()); + srs_error_reset(err); + continue; + } + } + + return err; +} + +void SrsRtmpFromSrtBridge::on_unpublish() +{ + live_source_->on_unpublish(); +} + +srs_error_t SrsRtmpFromSrtBridge::initialize(SrsRequest* req) +{ + srs_error_t err = srs_success; + + // TODO: FIXME: check srt2rtmp enable in config. + req_ = req->copy(); + + return err; +} + +srs_error_t SrsRtmpFromSrtBridge::on_ts_message(SrsTsMessage* msg) +{ + srs_error_t err = srs_success; + + // 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); + } + + // parse the stream. + SrsBuffer avs(msg->payload->bytes(), msg->payload->length()); + + // publish audio or video. + if (msg->channel->stream == SrsTsStreamVideoH264) { + if ((err = on_ts_video(msg, &avs)) != srs_success) { + return srs_error_wrap(err, "ts: consume video"); + } + } + if (msg->channel->stream == SrsTsStreamAudioAAC) { + if ((err = on_ts_audio(msg, &avs)) != srs_success) { + return srs_error_wrap(err, "ts: consume audio"); + } + } + + // TODO: FIXME: implements other codec? + return err; +} + +srs_error_t SrsRtmpFromSrtBridge::on_ts_video(SrsTsMessage* msg, SrsBuffer* avs) +{ + srs_error_t err = srs_success; + + vector > ipb_frames; + + SrsRawH264Stream* avc = new SrsRawH264Stream(); + SrsAutoFree(SrsRawH264Stream, avc); + + // send each frame. + while (!avs->empty()) { + char* frame = NULL; + int frame_size = 0; + if ((err = avc->annexb_demux(avs, &frame, &frame_size)) != srs_success) { + return srs_error_wrap(err, "demux annexb"); + } + + // 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); + + // ignore the nalu type sps(7), pps(8), 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) { + return srs_error_wrap(err, "demux sps"); + } + + if (! sps.empty() && sps_ != sps) { + sps_pps_change_ = true; + } + + 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) { + return srs_error_wrap(err, "demux pps"); + } + + if (! pps.empty() && pps_ != pps) { + sps_pps_change_ = true; + } + + pps_ = pps; + continue; + } + + ipb_frames.push_back(make_pair(frame, frame_size)); + } + + if ((err = check_sps_pps_change(msg)) != srs_success) { + return srs_error_wrap(err, "check sps pps"); + } + + return on_h264_frame(msg, ipb_frames); +} + +srs_error_t SrsRtmpFromSrtBridge::check_sps_pps_change(SrsTsMessage* msg) +{ + srs_error_t err = srs_success; + + if (! sps_pps_change_) { + return err; + } + + // sps/pps changed, generate new video sh frame and dispatch it. + sps_pps_change_ = false; + + // ts tbn to flv tbn. + uint32_t dts = (uint32_t)(msg->dts / 90); + + //type_codec1 + avc_type + composition time + fix header + count of sps + len of sps + sps + count of pps + len of pps + pps + int nb_payload = 1 + 1 + 3 + 5 + 1 + 2 + sps_.size() + 1 + 2 + pps_.size(); + SrsCommonMessage rtmp; + rtmp.header.initialize_video(nb_payload, dts, 1); + rtmp.create_payload(nb_payload); + rtmp.size = nb_payload; + SrsBuffer payload(rtmp.payload, rtmp.size); + //TODO: call api + payload.write_1bytes(0x17);// type(4 bits): key frame; code(4bits): avc + payload.write_1bytes(0x0); // avc_type: sequence header + payload.write_1bytes(0x0); // composition time + payload.write_1bytes(0x0); + payload.write_1bytes(0x0); + payload.write_1bytes(0x01); // version + payload.write_1bytes(sps_[1]); + payload.write_1bytes(sps_[2]); + payload.write_1bytes(sps_[3]); + payload.write_1bytes(0xff); + payload.write_1bytes(0xe1); + payload.write_2bytes(sps_.size()); + payload.write_bytes((char*)sps_.data(), sps_.size()); + payload.write_1bytes(0x01); + payload.write_2bytes(pps_.size()); + payload.write_bytes((char*)pps_.data(), pps_.size()); + if ((err = live_source_->on_video(&rtmp)) != srs_success) { + return srs_error_wrap(err, "srt to rtmp sps/pps"); + } + + return err; +} + +srs_error_t SrsRtmpFromSrtBridge::on_h264_frame(SrsTsMessage* msg, vector >& ipb_frames) +{ + srs_error_t err = srs_success; + + if (ipb_frames.empty()) { + return srs_error_new(ERROR_SRT_CONN, "empty frame"); + } + + bool is_keyframe = false; + + // ts tbn to flv tbn. + uint32_t dts = (uint32_t)(msg->dts / 90); + uint32_t pts = (uint32_t)(msg->pts / 90); + int32_t cts = pts - dts; + + int frame_size = 5; // 5bytes video tag header + for (size_t i = 0; i != ipb_frames.size(); ++i) { + // 4 bytes for nalu length. + frame_size += 4 + ipb_frames[i].second; + if (((SrsAvcNaluType)(ipb_frames[i].first[0] & 0x1f)) == SrsAvcNaluTypeIDR) { + is_keyframe = true; + } + } + + SrsCommonMessage rtmp; + rtmp.header.initialize_video(frame_size, dts, 1/*streamid*/); + rtmp.create_payload(frame_size); + rtmp.size = frame_size; + SrsBuffer payload(rtmp.payload, rtmp.size); + // Write 5bytes video tag header. + if (is_keyframe) { + payload.write_1bytes(0x17); // type(4 bits): key frame; code(4bits): avc + } else { + payload.write_1bytes(0x27); // type(4 bits): inter frame; code(4bits): avc + } + payload.write_1bytes(0x01); // avc_type: nalu + payload.write_3bytes(cts); // composition time + + // Write video nalus. + for (size_t i = 0; i != ipb_frames.size(); ++i) { + char* nal = ipb_frames[i].first; + int nal_size = ipb_frames[i].second; + + // write 4 bytes of nalu length. + payload.write_4bytes(nal_size); + // write nalu + payload.write_bytes(nal, nal_size); + } + + if ((err = live_source_->on_video(&rtmp)) != srs_success) { + return srs_error_wrap(err ,"srt ts video to rtmp"); + } + + return err; +} + +srs_error_t SrsRtmpFromSrtBridge::on_ts_audio(SrsTsMessage* msg, SrsBuffer* avs) +{ + srs_error_t err = srs_success; + + SrsRawAacStream* aac = new SrsRawAacStream(); + SrsAutoFree(SrsRawAacStream, aac); + + // ts tbn to flv tbn. + uint32_t pts = (uint32_t)(msg->pts / 90); + + int frame_idx = 0; + + // 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) { + return srs_error_wrap(err, "demux adts"); + } + + // ignore invalid frame, + // * atleast 1bytes for aac to decode the data. + if (frame_size <= 0) { + continue; + } + + std::string sh; + if ((err = aac->mux_sequence_header(&codec, sh)) != srs_success) { + return srs_error_wrap(err, "mux sequence header"); + } + + if (! sh.empty() && sh != audio_sh_) { + audio_sh_ = sh; + audio_sh_change_ = true; + } + + // May have more than one aac frame in PES packet, and shared same timestamp, + // so we must calculate each aac frame's timestamp. + int sample_rate = 44100; + switch (codec.sound_rate) { + case SrsAudioSampleRate5512: sample_rate = 5512; break; + case SrsAudioSampleRate11025: sample_rate = 11025; break; + case SrsAudioSampleRate22050: sample_rate = 22050; break; + case SrsAudioSampleRate44100: + default: sample_rate = 44100; break; + } + uint32_t frame_pts = (double)pts + (frame_idx * (1024.0 * 1000.0 / sample_rate)); + ++frame_idx; + + if ((err = check_audio_sh_change(msg, frame_pts)) != srs_success) { + return srs_error_wrap(err, "audio sh"); + } + + if ((err = on_aac_frame(msg, frame_pts, frame, frame_size)) != srs_success) { + return srs_error_wrap(err, "audio frame"); + } + } + + return err; +} + +srs_error_t SrsRtmpFromSrtBridge::check_audio_sh_change(SrsTsMessage* msg, uint32_t pts) +{ + srs_error_t err = srs_success; + + if (! audio_sh_change_) { + return err; + } + + // audio specific config changed, generate new audio sh and dispatch it. + audio_sh_change_ = false; + + int rtmp_len = audio_sh_.size() + 2; + + SrsCommonMessage rtmp; + rtmp.header.initialize_audio(rtmp_len, pts, 1); + rtmp.create_payload(rtmp_len); + rtmp.size = rtmp_len; + + SrsBuffer stream(rtmp.payload, rtmp_len); + uint8_t aac_flag = (SrsAudioCodecIdAAC << 4) | (SrsAudioSampleRate44100 << 2) | (SrsAudioSampleBits16bit << 1) | SrsAudioChannelsStereo; + stream.write_1bytes(aac_flag); + stream.write_1bytes(0); + stream.write_bytes((char*)audio_sh_.data(), audio_sh_.size()); + + if ((err = live_source_->on_audio(&rtmp)) != srs_success) { + return srs_error_wrap(err, "srt to rtmp audio sh"); + } + + return err; +} + +srs_error_t SrsRtmpFromSrtBridge::on_aac_frame(SrsTsMessage* msg, uint32_t pts, char* frame, int frame_size) +{ + srs_error_t err = srs_success; + + int rtmp_len = frame_size + 2/* 2 bytes of flv audio tag header*/; + + SrsCommonMessage rtmp; + rtmp.header.initialize_audio(rtmp_len, pts, 2/*streamid*/); + rtmp.create_payload(rtmp_len); + rtmp.size = rtmp_len; + + SrsBuffer stream(rtmp.payload, rtmp_len); + uint8_t aac_flag = (SrsAudioCodecIdAAC << 4) | (SrsAudioSampleRate44100 << 2) | (SrsAudioSampleBits16bit << 1) | SrsAudioChannelsStereo; + // Write 2bytes audio tag header. + stream.write_1bytes(aac_flag); + stream.write_1bytes(1); + // Write audio frame. + stream.write_bytes(frame, frame_size); + + if ((err = live_source_->on_audio(&rtmp)) != srs_success) { + return srs_error_wrap(err, "srt to rtmp audio sh"); + } + + return err; +} + +SrsSrtSource::SrsSrtSource() +{ + req = NULL; + can_publish_ = true; + bridge_ = NULL; +} + +SrsSrtSource::~SrsSrtSource() +{ + // never free the consumers, + // for all consumers are auto free. + consumers.clear(); + + srs_freep(bridge_); +} + +srs_error_t SrsSrtSource::initialize(SrsRequest* r) +{ + srs_error_t err = srs_success; + + req = r->copy(); + + return err; +} + +srs_error_t SrsSrtSource::on_source_id_changed(SrsContextId id) +{ + srs_error_t err = srs_success; + + if (!_source_id.compare(id)) { + return err; + } + + if (_pre_source_id.empty()) { + _pre_source_id = id; + } + _source_id = id; + + // notice all consumer + std::vector::iterator it; + for (it = consumers.begin(); it != consumers.end(); ++it) { + SrsSrtConsumer* consumer = *it; + consumer->update_source_id(); + } + + return err; +} + +SrsContextId SrsSrtSource::source_id() +{ + return _source_id; +} + +SrsContextId SrsSrtSource::pre_source_id() +{ + return _pre_source_id; +} + +void SrsSrtSource::update_auth(SrsRequest* r) +{ + req->update_auth(r); +} + +void SrsSrtSource::set_bridge(ISrsSrtSourceBridge* bridge) +{ + srs_freep(bridge_); + bridge_ = bridge; +} + +srs_error_t SrsSrtSource::create_consumer(SrsSrtConsumer*& consumer) +{ + srs_error_t err = srs_success; + + consumer = new SrsSrtConsumer(this); + consumers.push_back(consumer); + + return err; +} + +srs_error_t SrsSrtSource::consumer_dumps(SrsSrtConsumer* consumer) +{ + srs_error_t err = srs_success; + + // print status. + srs_trace("create ts consumer, no gop cache"); + + return err; +} + +void SrsSrtSource::on_consumer_destroy(SrsSrtConsumer* consumer) +{ + std::vector::iterator it; + it = std::find(consumers.begin(), consumers.end(), consumer); + if (it != consumers.end()) { + consumers.erase(it); + } +} + +bool SrsSrtSource::can_publish() +{ + return can_publish_; +} + +srs_error_t SrsSrtSource::on_publish() +{ + srs_error_t err = srs_success; + + can_publish_ = false; + + if ((err = on_source_id_changed(_srs_context->get_id())) != srs_success) { + return srs_error_wrap(err, "source id change"); + } + + if ((err = bridge_->on_publish()) != srs_success) { + return srs_error_wrap(err, "bridge on publish"); + } + + SrsStatistic* stat = SrsStatistic::instance(); + stat->on_stream_publish(req, _source_id.c_str()); + + return err; +} + +void SrsSrtSource::on_unpublish() +{ + // ignore when already unpublished. + if (can_publish_) { + return; + } + + can_publish_ = true; + + bridge_->on_unpublish(); + srs_freep(bridge_); +} + +srs_error_t SrsSrtSource::on_packet(SrsSrtPacket* packet) +{ + srs_error_t err = srs_success; + + for (int i = 0; i < (int)consumers.size(); i++) { + SrsSrtConsumer* consumer = consumers.at(i); + if ((err = consumer->enqueue(packet->copy())) != srs_success) { + return srs_error_wrap(err, "consume ts packet"); + } + } + + if ((err = bridge_->on_packet(packet)) != srs_success) { + return srs_error_wrap(err, "bridge consume message"); + } + + return err; +} + diff --git a/trunk/src/app/srs_app_srt_source.hpp b/trunk/src/app/srs_app_srt_source.hpp new file mode 100644 index 0000000000..f3cdd6af3f --- /dev/null +++ b/trunk/src/app/srs_app_srt_source.hpp @@ -0,0 +1,186 @@ +// +// Copyright (c) 2013-2021 The SRS Authors +// +// SPDX-License-Identifier: MIT or MulanPSL-2.0 +// + +#ifndef SRS_APP_SRT_SOURCE_HPP +#define SRS_APP_SRT_SOURCE_HPP + +#include + +#include +#include + +#include +#include +#include + +class SrsSharedPtrMessage; +class SrsRequest; +class SrsLiveSource; +class SrsSrtSource; + +// The SRT packet with shared message. +class SrsSrtPacket +{ +public: + SrsSrtPacket(); + virtual ~SrsSrtPacket(); +public: + // Wrap buffer to shared_message, which is managed by us. + char* wrap(int size); + char* wrap(char* data, int size); + // Wrap the shared message, we copy it. + char* wrap(SrsSharedPtrMessage* msg); + // Copy the SRT packet. + virtual SrsSrtPacket* copy(); +public: + char* data(); + int size(); +private: + SrsSharedPtrMessage* shared_buffer_; + // The size of SRT packet or SRT payload. + int actual_buffer_size_; +}; + +class SrsSrtSourceManager +{ +private: + srs_mutex_t lock; + std::map pool; +public: + SrsSrtSourceManager(); + virtual ~SrsSrtSourceManager(); +public: + // create source when fetch from cache failed. + // @param r the client request. + // @param pps the matched source, if success never be NULL. + virtual srs_error_t fetch_or_create(SrsRequest* r, SrsSrtSource** pps); +public: + // Get the exists source, NULL when not exists. + virtual SrsSrtSource* fetch(SrsRequest* r); +}; + +// Global singleton instance. +extern SrsSrtSourceManager* _srs_srt_sources; + +class SrsSrtConsumer +{ +public: + SrsSrtConsumer(SrsSrtSource* source); + virtual ~SrsSrtConsumer(); +private: + SrsSrtSource* source; + std::vector queue; + // when source id changed, notice all consumers + bool should_update_source_id; + // The cond wait for mw. + srs_cond_t mw_wait; + bool mw_waiting; + int mw_min_msgs; +public: + // When source id changed, notice client to print. + void update_source_id(); + // Put SRT packet into queue. + srs_error_t enqueue(SrsSrtPacket* packet); + // For SRT, we only got one packet, because there is not many packets in queue. + virtual srs_error_t dump_packet(SrsSrtPacket** ppkt); + // Wait for at-least some messages incoming in queue. + virtual void wait(int nb_msgs, srs_utime_t timeout); +}; + +class ISrsSrtSourceBridge +{ +public: + ISrsSrtSourceBridge(); + virtual ~ISrsSrtSourceBridge(); +public: + virtual srs_error_t on_publish() = 0; + virtual srs_error_t on_packet(SrsSrtPacket *pkt) = 0; + virtual void on_unpublish() = 0; +}; + +class SrsRtmpFromSrtBridge : public ISrsSrtSourceBridge, public ISrsTsHandler +{ +public: + SrsRtmpFromSrtBridge(SrsLiveSource* source); + virtual ~SrsRtmpFromSrtBridge(); +public: + virtual srs_error_t on_publish(); + virtual srs_error_t on_packet(SrsSrtPacket *pkt); + virtual void on_unpublish(); +public: + srs_error_t initialize(SrsRequest* req); +// Interface ISrsTsHandler +public: + virtual srs_error_t on_ts_message(SrsTsMessage* msg); +private: + srs_error_t on_ts_video(SrsTsMessage* msg, SrsBuffer* avs); + srs_error_t on_ts_audio(SrsTsMessage* msg, SrsBuffer* avs); + srs_error_t check_sps_pps_change(SrsTsMessage* msg); + srs_error_t on_h264_frame(SrsTsMessage* msg, std::vector >& ipb_frames); + srs_error_t check_audio_sh_change(SrsTsMessage* msg, uint32_t pts); + srs_error_t on_aac_frame(SrsTsMessage* msg, uint32_t pts, char* frame, int frame_size); +private: + SrsTsContext* ts_ctx_; + + // Record sps/pps had changed, if change, need to generate new video sh frame. + bool sps_pps_change_; + std::string sps_; + std::string pps_; + + // Record audio sepcific config had changed, if change, need to generate new audio sh frame. + bool audio_sh_change_; + std::string audio_sh_; + + SrsRequest* req_; + SrsLiveSource* live_source_; +}; + +class SrsSrtSource +{ +public: + SrsSrtSource(); + virtual ~SrsSrtSource(); +public: + virtual srs_error_t initialize(SrsRequest* r); +public: + // The source id changed. + virtual srs_error_t on_source_id_changed(SrsContextId id); + // Get current source id. + virtual SrsContextId source_id(); + virtual SrsContextId pre_source_id(); + // Update the authentication information in request. + virtual void update_auth(SrsRequest* r); +public: + void set_bridge(ISrsSrtSourceBridge *bridge); +public: + // Create consumer + // @param consumer, output the create consumer. + virtual srs_error_t create_consumer(SrsSrtConsumer*& consumer); + // Dumps packets in cache to consumer. + virtual srs_error_t consumer_dumps(SrsSrtConsumer* consumer); + virtual void on_consumer_destroy(SrsSrtConsumer* consumer); + // Whether we can publish stream to the source, return false if it exists. + virtual bool can_publish(); + // When start publish stream. + virtual srs_error_t on_publish(); + // When stop publish stream. + virtual void on_unpublish(); +public: + srs_error_t on_packet(SrsSrtPacket* packet); +private: + // Source id. + SrsContextId _source_id; + // previous source id. + SrsContextId _pre_source_id; + SrsRequest* req; + // To delivery packets to clients. + std::vector consumers; + bool can_publish_; + ISrsSrtSourceBridge* bridge_; +}; + +#endif + diff --git a/trunk/src/srt/srt_conn.cpp b/trunk/src/app/srs_app_srt_utility.cpp similarity index 51% rename from trunk/src/srt/srt_conn.cpp rename to trunk/src/app/srs_app_srt_utility.cpp index 6626703245..c0e87cdda3 100644 --- a/trunk/src/srt/srt_conn.cpp +++ b/trunk/src/app/srs_app_srt_utility.cpp @@ -4,78 +4,23 @@ // SPDX-License-Identifier: MIT or MulanPSL-2.0 // -#include "srt_conn.hpp" -#include "time_help.hpp" -#include "stringex.hpp" -#include "srt_log.hpp" -#include +#include +using namespace std; + +#include +#include +#include +#include #include +#include #include -#include - -bool is_streamid_valid(const std::string& streamid) { - if (streamid.empty()) { - return false; - } - - size_t pos = streamid.find(" "); - if (pos != streamid.npos) { - return false; - } - - int mode; - std::string subpath; - std::string vhost; - - // Parse the stream info from streamid, see https://github.com/ossrs/srs/issues/2893 - bool ret = get_streamid_info(streamid, mode, vhost, subpath); - if (!ret) { - return false; - } - - std::vector info_vec; - string_split(subpath, "/", info_vec); - - // TODO: FIXME: Should fail at parsing the original SRT URL. - if (info_vec.size() != 2) { - srt_log_warn("path format must be appname/stream?key=value..."); - return false; - } - - for (auto item : info_vec) { - if (item.empty()) { - return false; - } - pos = item.find(" "); - if (pos != item.npos) { - return false; - } - } - return true; -} - -bool get_key_value(const std::string& info, std::string& key, std::string& value) { - size_t pos = info.find("="); - - if (pos == info.npos) { - return false; - } - - key = info.substr(0, pos); - value = info.substr(pos+1); - - if (key.empty() || value.empty()) { - return false; - } - return true; -} // See streamid of https://github.com/ossrs/srs/issues/2893 // TODO: FIMXE: We should parse SRT streamid to URL object, rather than a HTTP url subpath. -bool get_streamid_info(const std::string& streamid, int& mode, std::string& vhost, std::string& url_subpath) +bool srs_srt_streamid_info(const std::string& streamid, SrtMode& mode, std::string& vhost, std::string& url_subpath) { - mode = PULL_SRT_MODE; + mode = SrtModePull; size_t pos = streamid.find("#!::"); if (pos != 0) { @@ -140,11 +85,11 @@ bool get_streamid_info(const std::string& streamid, int& mode, std::string& vhos std::string mode_str = it->second; // support m=publish or m=request std::transform(it->second.begin(), it->second.end(), mode_str.begin(), ::tolower); if (mode_str == "publish") { - mode = PUSH_SRT_MODE; + mode = SrtModePush; } else if (mode_str == "request") { - mode = PULL_SRT_MODE; + mode = SrtModePull; } else { - srt_log_warn("unknown mode_str:%s", mode_str.c_str()); + srs_warn("unknown mode_str:%s", mode_str.c_str()); return false; } } else { @@ -168,96 +113,31 @@ bool get_streamid_info(const std::string& streamid, int& mode, std::string& vhos return true; } -srt_conn::srt_conn(SRTSOCKET conn_fd, const std::string& streamid):_conn_fd(conn_fd), - _streamid(streamid), - write_fail_cnt_(0) +bool srs_srt_streamid_to_request(const std::string& streamid, SrtMode& mode, SrsRequest* request) { - get_streamid_info(streamid, _mode, _vhost, _url_subpath); - - _update_timestamp = now_ms(); - - if (_vhost.empty()) { - _vhost = "__default_host__"; + string url_subpath = ""; + bool ret = srs_srt_streamid_info(streamid, mode, request->vhost, url_subpath); + if (! ret) { + return ret; } - srt_log_trace("srt connect construct streamid:%s, mode:%d, subpath:%s, vhost:%s", - streamid.c_str(), _mode, _url_subpath.c_str(), _vhost.c_str()); -} - -srt_conn::~srt_conn() { - close(); -} - -std::string srt_conn::get_vhost() { - return _vhost; -} - -void srt_conn::update_timestamp(long long now_ts) { - _update_timestamp = now_ts; -} - -long long srt_conn::get_last_ts() { - return _update_timestamp; -} - -void srt_conn::close() { - if (_conn_fd == SRT_INVALID_SOCK) { - return; + size_t pos = url_subpath.find("/"); + string stream_with_params = ""; + if (pos == string::npos) { + request->app = _srs_config->get_default_app_name(); + stream_with_params = url_subpath; + } else { + request->app = url_subpath.substr(0, pos); + stream_with_params = url_subpath.substr(pos + 1); } - srt_close(_conn_fd); - _conn_fd = SRT_INVALID_SOCK; -} -SRTSOCKET srt_conn::get_conn() { - return _conn_fd; -} -int srt_conn::get_mode() { - return _mode; -} - -std::string srt_conn::get_streamid() { - return _streamid; -} - -std::string srt_conn::get_path() { - if (!_url_path.empty()) { - return _url_path; - } - - size_t pos = _url_subpath.find("?"); - _url_path = (pos != std::string::npos) ? _url_subpath.substr(0, pos) : _url_subpath; - - return _url_path; -} - -std::string srt_conn::get_subpath() { - return _url_subpath; -} - -int srt_conn::read(unsigned char* data, int len) { - int ret = 0; - - ret = srt_recv(_conn_fd, (char*)data, len); - if (ret <= 0) { - srt_log_error("srt read error:%d, socket fd:%d", ret, _conn_fd); - return ret; + pos = stream_with_params.find("?"); + if (pos == string::npos) { + request->stream = stream_with_params; + } else { + request->stream = stream_with_params.substr(0, pos); + request->param = stream_with_params.substr(pos + 1); } - return ret; -} -int srt_conn::write(unsigned char* data, int len) { - int ret = 0; - - ret = srt_send(_conn_fd, (char*)data, len); - if (ret <= 0) { - srt_log_error("srt write error:%d, socket fd:%d", ret, _conn_fd); - write_fail_cnt_++; - return ret; - } - write_fail_cnt_ = 0; return ret; } - -int srt_conn::get_write_fail_count() { - return write_fail_cnt_; -} \ No newline at end of file diff --git a/trunk/src/app/srs_app_srt_utility.hpp b/trunk/src/app/srs_app_srt_utility.hpp new file mode 100644 index 0000000000..37b6c81713 --- /dev/null +++ b/trunk/src/app/srs_app_srt_utility.hpp @@ -0,0 +1,32 @@ +// +// Copyright (c) 2013-2021 The SRS Authors +// +// SPDX-License-Identifier: MIT or MulanPSL-2.0 +// + +#ifndef SRS_APP_SRT_UTILITY_HPP +#define SRS_APP_SRT_UTILITY_HPP + +#include + +#include + +#include +#include + +class SrsRequest; + +enum SrtMode +{ + SrtModePull = 1, + SrtModePush = 2, +}; + +// Get SRT streamid info. +extern bool srs_srt_streamid_info(const std::string& streamid, SrtMode& mode, std::string& vhost, std::string& url_subpath); + +// SRT streamid to request. +extern bool srs_srt_streamid_to_request(const std::string& streamid, SrtMode& mode, SrsRequest* request); + +#endif + diff --git a/trunk/src/app/srs_app_threads.cpp b/trunk/src/app/srs_app_threads.cpp index bcfd78c738..594da79184 100644 --- a/trunk/src/app/srs_app_threads.cpp +++ b/trunk/src/app/srs_app_threads.cpp @@ -22,6 +22,10 @@ #include #endif +#ifdef SRS_SRT +#include +#endif + #include using namespace std; @@ -298,6 +302,10 @@ srs_error_t srs_thread_initialize() _srs_stages = new SrsStageManager(); _srs_circuit_breaker = new SrsCircuitBreaker(); +#ifdef SRS_SRT + _srs_srt_sources = new SrsSrtSourceManager(); +#endif + #ifdef SRS_RTC _srs_rtc_sources = new SrsRtcSourceManager(); _srs_blackhole = new SrsRtcBlackhole(); diff --git a/trunk/src/core/srs_core_lock.cpp b/trunk/src/core/srs_core_lock.cpp new file mode 100644 index 0000000000..a2da00e24c --- /dev/null +++ b/trunk/src/core/srs_core_lock.cpp @@ -0,0 +1,8 @@ +// +// Copyright (c) 2013-2021 The SRS Authors +// +// SPDX-License-Identifier: MIT or MulanPSL-2.0 +// + +#include + diff --git a/trunk/src/core/srs_core_lock.hpp b/trunk/src/core/srs_core_lock.hpp new file mode 100644 index 0000000000..3eb1c1d3c1 --- /dev/null +++ b/trunk/src/core/srs_core_lock.hpp @@ -0,0 +1,57 @@ +// +// Copyright (c) 2013-2021 The SRS Authors +// +// SPDX-License-Identifier: MIT or MulanPSL-2.0 +// + +#ifndef SRS_CORE_LOCK_HPP +#define SRS_CORE_LOCK_HPP + +#include + +#include + +#include + +class SrsScopeLock +{ +private: + pthread_mutex_t* mutex_; + bool locked_; + +public: + explicit SrsScopeLock(pthread_mutex_t* mutex) + { + mutex_ = mutex; + locked_ = false; + + lock(); + } + + ~SrsScopeLock() + { + unlock(); + } + + void lock() + { + if (locked_) { + return; + } + + locked_ = true; + pthread_mutex_lock(mutex_); + } + + void unlock() + { + if (! locked_) { + return; + } + + locked_ = false; + pthread_mutex_unlock(mutex_); + } +}; + +#endif diff --git a/trunk/src/kernel/srs_kernel_consts.hpp b/trunk/src/kernel/srs_kernel_consts.hpp index ed3b2fba5d..48f590c734 100644 --- a/trunk/src/kernel/srs_kernel_consts.hpp +++ b/trunk/src/kernel/srs_kernel_consts.hpp @@ -170,6 +170,10 @@ #define SRS_CONSTS_LOG_EXEC "EXE" // The rtc. #define SRS_CONSTS_LOG_RTC "RTC" +// Srt client play +#define SRS_CONSTS_LOG_SRT_PLAY "SRT_PLA" +// Srt client publish +#define SRS_CONSTS_LOG_SRT_PUBLISH "SRT_CPB" /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index c07bb84c80..457afb346f 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -347,6 +347,20 @@ #define ERROR_RTC_NO_TRACK 5030 #define ERROR_RTC_RTCP_EMPTY_RR 5031 +/////////////////////////////////////////////////////// +// SRT protocol error. +/////////////////////////////////////////////////////// +#define ERROR_SRT_EPOLL 6000 +#define ERROR_SRT_IO 6001 +#define ERROR_SRT_TIMEOUT 6002 +#define ERROR_SRT_INTERRUPT 6003 +#define ERROR_SRT_LISTEN 6004 +#define ERROR_SRT_SOCKOPT 6005 +#define ERROR_SRT_CONN 6006 +#define ERROR_SRT_SOURCE_BUSY 6007 +#define ERROR_RTMP_TO_SRT 6008 +#define ERROR_SRT_STATS 6009 + /////////////////////////////////////////////////////// // HTTP API error. /////////////////////////////////////////////////////// diff --git a/trunk/src/main/srs_main_server.cpp b/trunk/src/main/srs_main_server.cpp index a9d51dfc5b..dd16864d89 100644 --- a/trunk/src/main/srs_main_server.cpp +++ b/trunk/src/main/srs_main_server.cpp @@ -44,7 +44,8 @@ using namespace std; #endif #ifdef SRS_SRT -#include +#include +#include #endif // pre-declare @@ -455,7 +456,7 @@ srs_error_t run_hybrid_server() _srs_hybrid->register_server(new SrsServerAdapter()); #ifdef SRS_SRT - _srs_hybrid->register_server(new SrtServerAdapter()); + _srs_hybrid->register_server(new SrsSrtServerAdapter()); #endif #ifdef SRS_RTC diff --git a/trunk/src/protocol/srs_protocol_srt.cpp b/trunk/src/protocol/srs_protocol_srt.cpp new file mode 100644 index 0000000000..d92adfb3af --- /dev/null +++ b/trunk/src/protocol/srs_protocol_srt.cpp @@ -0,0 +1,970 @@ +// +// Copyright (c) 2013-2021 The SRS Authors +// +// SPDX-License-Identifier: MIT or MulanPSL-2.0 +// + +#include + +#include + +using namespace std; + +#include +#include +#include + +#include + +// TODO: FIXME: protocol could no include app's header file, so define TAG_SRT in this file. +#define TAG_SRT "SRT" + +#define SET_SRT_OPT_STR(srtfd, optname, buf, size) \ + if (srt_setsockflag(srtfd, optname, buf, size) == SRT_ERROR) { \ + std::stringstream ss; \ + ss << "srtfd=" << srtfd << ",set " << #optname \ + << " failed,err=" << srt_getlasterror_str(); \ + return srs_error_new(ERROR_SRT_SOCKOPT, "%s", ss.str().c_str()); \ + } + +#define SET_SRT_OPT(srtfd, optname, val) \ + if (srt_setsockflag(srtfd, optname, &val, sizeof(val)) == SRT_ERROR) { \ + std::stringstream ss; \ + ss << "srtfd=" << srtfd << ",set " << #optname << "=" << val \ + << " failed,err=" << srt_getlasterror_str(); \ + return srs_error_new(ERROR_SRT_SOCKOPT, "%s", ss.str().c_str()); \ + } + +#define GET_SRT_OPT(srtfd, optname, val) \ + do { \ + int size = sizeof(val); \ + if (srt_getsockflag(srtfd, optname, &val, &size) == SRT_ERROR) { \ + std::stringstream ss; \ + ss << "srtfd=" << srtfd << ",get " << #optname \ + << " failed,err=" << srt_getlasterror_str(); \ + return srs_error_new(ERROR_SRT_SOCKOPT, "%s", ss.str().c_str()); \ + } \ + } while (0) + + +static srs_error_t do_srs_srt_listen(srs_srt_t srt_fd, addrinfo* r) +{ + srs_error_t err = srs_success; + + if ((err = srs_srt_nonblock(srt_fd)) != srs_success) { + return srs_error_wrap(err, "nonblock"); + } + + if (srt_bind(srt_fd, r->ai_addr, r->ai_addrlen) == -1) { + return srs_error_new(ERROR_SOCKET_BIND, "bind"); + } + + if (srt_listen(srt_fd, 100) == -1) { + return srs_error_new(ERROR_SOCKET_LISTEN, "listen"); + } + + return err; +} + +static srs_error_t do_srs_srt_get_streamid(srs_srt_t srt_fd, string& streamid) +{ + // SRT max streamid length is 512. + char sid[512]; + GET_SRT_OPT(srt_fd, SRTO_STREAMID, sid); + + streamid.assign(sid); + return srs_success; +} + +static void srs_srt_log_handler(void* opaque, int level, const char* file, int line, const char* area, const char* message) +{ + switch (level) { + case srt_logging::LogLevel::debug: + srs_info2(TAG_SRT, "%s:%d(%s) # %s", file, line, area, message); + break; + case srt_logging::LogLevel::note: + srs_trace2(TAG_SRT, "%s:%d(%s) # %s", file, line, area, message); + break; + case srt_logging::LogLevel::warning: + srs_warn2(TAG_SRT, "%s:%d(%s) # %s", file, line, area, message); + break; + case srt_logging::LogLevel::error: + case srt_logging::LogLevel::fatal: + srs_error2(TAG_SRT, "%s:%d(%s) # %s", file, line, area, message); + break; + default: + srs_trace2(TAG_SRT, "%s:%d(%s) # %s", file, line, area, message); + break; + } +} + +srs_error_t srs_srt_log_initialie() +{ + srs_error_t err = srs_success; + + srt_setlogflags(0 | SRT_LOGF_DISABLE_TIME | SRT_LOGF_DISABLE_SEVERITY | + SRT_LOGF_DISABLE_THREADNAME | SRT_LOGF_DISABLE_EOL); + srt_setloghandler(NULL, srs_srt_log_handler); + + return err; +} + +srs_srt_t srs_srt_socket_invalid() +{ + return SRT_INVALID_SOCK; +} + +srs_error_t srs_srt_socket(srs_srt_t* pfd) +{ + srs_error_t err = srs_success; + + srs_srt_t srt_fd = 0; + if ((srt_fd = srt_create_socket()) < 0) { + return srs_error_new(ERROR_SOCKET_CREATE, "create srt socket"); + } + + *pfd = srt_fd; + + return err; +} + +srs_error_t srs_srt_close(srs_srt_t fd) +{ + // TODO: FIXME: Handle error. + srt_close(fd); + return srs_success; +} + +srs_error_t srs_srt_socket_with_default_option(srs_srt_t* pfd) +{ + srs_error_t err = srs_success; + + srs_srt_t srt_fd = 0; + if ((srt_fd = srt_create_socket()) < 0) { + return srs_error_new(ERROR_SOCKET_CREATE, "create srt socket"); + } + + if ((err = srs_srt_nonblock(srt_fd)) != srs_success) { + return srs_error_wrap(err, "nonblock"); + } + + if ((err = srs_srt_set_tsbpdmode(srt_fd, false)) != srs_success) { + return srs_error_wrap(err, "set tsbpdmode"); + } + + if ((err = srs_srt_set_tlpktdrop(srt_fd, false)) != srs_success) { + return srs_error_wrap(err, "set tlpktdrop"); + } + + if ((err = srs_srt_set_latency(srt_fd, false)) != srs_success) { + return srs_error_wrap(err, "set latency"); + } + + *pfd = srt_fd; + + return err; +} + +srs_error_t srs_srt_listen(srs_srt_t srt_fd, std::string ip, int port) +{ + srs_error_t err = srs_success; + + char sport[8]; + snprintf(sport, sizeof(sport), "%d", port); + + addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICHOST; + + addrinfo* r = NULL; + SrsAutoFreeH(addrinfo, r, freeaddrinfo); + if(getaddrinfo(ip.c_str(), sport, (const addrinfo*)&hints, &r)) { + return srs_error_new(ERROR_SYSTEM_IP_INVALID, "getaddrinfo hints=(%d,%d,%d)", + hints.ai_family, hints.ai_socktype, hints.ai_flags); + } + + if ((err = do_srs_srt_listen(srt_fd, r)) != srs_success) { + srt_close(srt_fd); + return srs_error_wrap(err, "srt_fd=%d", srt_fd); + } + + return err; +} + +srs_error_t srs_srt_nonblock(srs_srt_t srt_fd) +{ + int sync = 0; + SET_SRT_OPT(srt_fd, SRTO_SNDSYN, sync); + SET_SRT_OPT(srt_fd, SRTO_RCVSYN, sync); + + return srs_success; +} + +srs_error_t srs_srt_set_maxbw(srs_srt_t srt_fd, int maxbw) +{ + SET_SRT_OPT(srt_fd, SRTO_MAXBW, maxbw); + return srs_success; +} + +srs_error_t srs_srt_set_mss(srs_srt_t srt_fd, int mss) +{ + SET_SRT_OPT(srt_fd, SRTO_MSS, mss); + return srs_success; +} + +srs_error_t srs_srt_set_payload_size(srs_srt_t srt_fd, int payload_size) +{ + SET_SRT_OPT(srt_fd, SRTO_PAYLOADSIZE, payload_size); + return srs_success; +} + +srs_error_t srs_srt_set_connect_timeout(srs_srt_t srt_fd, int timeout) +{ + SET_SRT_OPT(srt_fd, SRTO_CONNTIMEO, timeout); + return srs_success; +} + +srs_error_t srs_srt_set_peer_idle_timeout(srs_srt_t srt_fd, int timeout) +{ + SET_SRT_OPT(srt_fd, SRTO_PEERIDLETIMEO, timeout); + return srs_success; +} + +srs_error_t srs_srt_set_tsbpdmode(srs_srt_t srt_fd, bool tsbpdmode) +{ + SET_SRT_OPT(srt_fd, SRTO_TSBPDMODE, tsbpdmode); + return srs_success; +} + +srs_error_t srs_srt_set_sndbuf(srs_srt_t srt_fd, int sndbuf) +{ + SET_SRT_OPT(srt_fd, SRTO_SNDBUF, sndbuf); + return srs_success; +} + +srs_error_t srs_srt_set_rcvbuf(srs_srt_t srt_fd, int rcvbuf) +{ + SET_SRT_OPT(srt_fd, SRTO_RCVBUF, rcvbuf); + return srs_success; +} + +srs_error_t srs_srt_set_tlpktdrop(srs_srt_t srt_fd, bool tlpktdrop) +{ + SET_SRT_OPT(srt_fd, SRTO_TLPKTDROP, tlpktdrop); + return srs_success; +} + +srs_error_t srs_srt_set_latency(srs_srt_t srt_fd, int latency) +{ + SET_SRT_OPT(srt_fd, SRTO_LATENCY, latency); + return srs_success; +} + +srs_error_t srs_srt_set_rcv_latency(srs_srt_t srt_fd, int rcv_latency) +{ + SET_SRT_OPT(srt_fd, SRTO_RCVLATENCY, rcv_latency); + return srs_success; +} + +srs_error_t srs_srt_set_peer_latency(srs_srt_t srt_fd, int peer_latency) +{ + SET_SRT_OPT(srt_fd, SRTO_PEERLATENCY, peer_latency); + return srs_success; +} + +srs_error_t srs_srt_set_streamid(srs_srt_t srt_fd, const std::string& streamid) +{ + SET_SRT_OPT_STR(srt_fd, SRTO_STREAMID, streamid.data(), streamid.size()); + return srs_success; +} + +srs_error_t srs_srt_get_maxbw(srs_srt_t srt_fd, int& maxbw) +{ + GET_SRT_OPT(srt_fd, SRTO_MAXBW, maxbw); + return srs_success; +} + +srs_error_t srs_srt_get_mss(srs_srt_t srt_fd, int& mss) +{ + GET_SRT_OPT(srt_fd, SRTO_MSS, mss); + return srs_success; +} + +srs_error_t srs_srt_get_payload_size(srs_srt_t srt_fd, int& payload_size) +{ + GET_SRT_OPT(srt_fd, SRTO_PAYLOADSIZE, payload_size); + return srs_success; +} + +srs_error_t srs_srt_get_connect_timeout(srs_srt_t srt_fd, int& timeout) +{ + GET_SRT_OPT(srt_fd, SRTO_CONNTIMEO, timeout); + return srs_success; +} + +srs_error_t srs_srt_get_peer_idle_timeout(srs_srt_t srt_fd, int& timeout) +{ + GET_SRT_OPT(srt_fd, SRTO_PEERIDLETIMEO, timeout); + return srs_success; +} + +srs_error_t srs_srt_get_tsbpdmode(srs_srt_t srt_fd, bool& tsbpdmode) +{ + GET_SRT_OPT(srt_fd, SRTO_TSBPDMODE, tsbpdmode); + return srs_success; +} + +srs_error_t srs_srt_get_sndbuf(srs_srt_t srt_fd, int& sndbuf) +{ + GET_SRT_OPT(srt_fd, SRTO_SNDBUF, sndbuf); + return srs_success; +} + +srs_error_t srs_srt_get_rcvbuf(srs_srt_t srt_fd, int& rcvbuf) +{ + GET_SRT_OPT(srt_fd, SRTO_RCVBUF, rcvbuf); + return srs_success; +} + +srs_error_t srs_srt_get_tlpktdrop(srs_srt_t srt_fd, bool& tlpktdrop) +{ + GET_SRT_OPT(srt_fd, SRTO_TLPKTDROP, tlpktdrop); + return srs_success; +} + +srs_error_t srs_srt_get_latency(srs_srt_t srt_fd, int& latency) +{ + GET_SRT_OPT(srt_fd, SRTO_LATENCY, latency); + return srs_success; +} + +srs_error_t srs_srt_get_rcv_latency(srs_srt_t srt_fd, int& rcv_latency) +{ + GET_SRT_OPT(srt_fd, SRTO_RCVLATENCY, rcv_latency); + return srs_success; +} + +srs_error_t srs_srt_get_peer_latency(srs_srt_t srt_fd, int& peer_latency) +{ + GET_SRT_OPT(srt_fd, SRTO_PEERLATENCY, peer_latency); + return srs_success; +} + +srs_error_t srs_srt_get_streamid(srs_srt_t srt_fd, std::string& streamid) +{ + srs_error_t err = srs_success; + + if ((err = do_srs_srt_get_streamid(srt_fd, streamid)) != srs_success) { + return srs_error_wrap(err, "srt get streamid"); + } + + return err; +} + +srs_error_t srs_srt_get_local_ip_port(srs_srt_t srt_fd, std::string& ip, int& port) +{ + srs_error_t err = srs_success; + + // discovery client information + sockaddr_storage addr; + int addrlen = sizeof(addr); + if (srt_getsockname(srt_fd, (sockaddr*)&addr, &addrlen) == -1) { + return srs_error_new(ERROR_SRT_SOCKOPT, "srt_getsockname"); + } + + char saddr[64]; + char* h = (char*)saddr; + socklen_t nbh = (socklen_t)sizeof(saddr); + const int r0 = getnameinfo((const sockaddr*)&addr, addrlen, h, nbh,NULL, 0, NI_NUMERICHOST); + if (r0) { + return srs_error_new(ERROR_SRT_SOCKOPT, "getnameinfo"); + } + + switch(addr.ss_family) { + case AF_INET: + port = ntohs(((sockaddr_in*)&addr)->sin_port); + break; + case AF_INET6: + port = ntohs(((sockaddr_in6*)&addr)->sin6_port); + break; + } + + ip.assign(saddr); + return err; +} + +srs_error_t srs_srt_get_remote_ip_port(srs_srt_t srt_fd, std::string& ip, int& port) +{ + srs_error_t err = srs_success; + + // discovery client information + sockaddr_storage addr; + int addrlen = sizeof(addr); + if (srt_getpeername(srt_fd, (sockaddr*)&addr, &addrlen) == -1) { + return srs_error_new(ERROR_SRT_SOCKOPT, "srt_getpeername"); + } + + char saddr[64]; + char* h = (char*)saddr; + socklen_t nbh = (socklen_t)sizeof(saddr); + const int r0 = getnameinfo((const sockaddr*)&addr, addrlen, h, nbh,NULL, 0, NI_NUMERICHOST); + if (r0) { + return srs_error_new(ERROR_SRT_SOCKOPT, "getnameinfo"); + } + + switch(addr.ss_family) { + case AF_INET: + port = ntohs(((sockaddr_in*)&addr)->sin_port); + break; + case AF_INET6: + port = ntohs(((sockaddr_in6*)&addr)->sin6_port); + break; + } + + ip.assign(saddr); + return err; +} + +SrsSrtStat::SrsSrtStat() +{ + stat_ = new SRT_TRACEBSTATS(); +} + +SrsSrtStat::~SrsSrtStat() +{ + SRT_TRACEBSTATS* p = (SRT_TRACEBSTATS*)stat_; + srs_freep(p); +} + +int64_t SrsSrtStat::pktRecv() +{ + return ((SRT_TRACEBSTATS*)stat_)->pktRecv; +} + +int SrsSrtStat::pktRcvLoss() +{ + return ((SRT_TRACEBSTATS*)stat_)->pktRcvLoss; +} + +int SrsSrtStat::pktRcvRetrans() +{ + return ((SRT_TRACEBSTATS*)stat_)->pktRcvRetrans; +} + +int SrsSrtStat::pktRcvDrop() +{ + return ((SRT_TRACEBSTATS*)stat_)->pktRcvDrop; +} + +int64_t SrsSrtStat::pktSent() +{ + return ((SRT_TRACEBSTATS*)stat_)->pktSent; +} + +int SrsSrtStat::pktSndLoss() +{ + return ((SRT_TRACEBSTATS*)stat_)->pktSndLoss; +} + +int SrsSrtStat::pktRetrans() +{ + return ((SRT_TRACEBSTATS*)stat_)->pktRetrans; +} + +int SrsSrtStat::pktSndDrop() +{ + return ((SRT_TRACEBSTATS*)stat_)->pktSndDrop; +} + +srs_error_t SrsSrtStat::fetch(srs_srt_t srt_fd, bool clear) +{ + srs_error_t err = srs_success; + + int r0 = srt_bstats(srt_fd, (SRT_TRACEBSTATS*)stat_, clear); + if (r0) { + return srs_error_new(ERROR_SRT_STATS, "srt_bstats r0=%d", r0); + } + + return err; +} + +class SrsSrtPoller : public ISrsSrtPoller +{ +public: + SrsSrtPoller(); + virtual ~SrsSrtPoller(); +public: + srs_error_t initialize(); + srs_error_t add_socket(SrsSrtSocket* srt_skt); + srs_error_t mod_socket(SrsSrtSocket* srt_skt); + srs_error_t del_socket(SrsSrtSocket* srt_skt); + srs_error_t wait(int timeout_ms, int* pn_fds); +public: + virtual int size(); +private: + // Find SrsSrtSocket* context by srs_srt_t. + std::map fd_sockets_; + int srt_epoller_fd_; + std::vector events_; +}; + +SrsSrtPoller::SrsSrtPoller() +{ + srt_epoller_fd_ = -1; +} + +SrsSrtPoller::~SrsSrtPoller() +{ + if (srt_epoller_fd_ > 0) { + srt_epoll_release(srt_epoller_fd_); + } +} + +srs_error_t SrsSrtPoller::initialize() +{ + srs_error_t err = srs_success; + + srt_epoller_fd_ = srt_epoll_create(); + events_.resize(1024); + + // Enable srt empty poller, avoid warning. + srt_epoll_set(srt_epoller_fd_, SRT_EPOLL_ENABLE_EMPTY); + + return err; +} + +srs_error_t SrsSrtPoller::add_socket(SrsSrtSocket* srt_skt) +{ + srs_error_t err = srs_success; + + int events = srt_skt->events(); + srs_srt_t srtfd = srt_skt->fd(); + + int ret = srt_epoll_add_usock(srt_epoller_fd_, srtfd, &events); + + srs_info("srt poller %d add srt socket %d, events=%d", srt_epoller_fd_, srtfd, events); + if (ret == SRT_ERROR) { + return srs_error_new(ERROR_SRT_EPOLL, "srt epoll add socket=%lu failed, err=%s", srtfd, srt_getlasterror_str()); + } + + // record srtfd to SrsSrtSocket* + fd_sockets_[srtfd] = srt_skt; + + return err; +} + +srs_error_t SrsSrtPoller::del_socket(SrsSrtSocket* srt_skt) +{ + srs_error_t err = srs_success; + + srs_srt_t srtfd = srt_skt->fd(); + + int ret = srt_epoll_remove_usock(srt_epoller_fd_, srtfd); + srs_info("srt poller %d remove srt socket %d", srt_epoller_fd_, srtfd); + if (ret == SRT_ERROR) { + return srs_error_new(ERROR_SRT_EPOLL, "srt epoll remove socket=%lu failed, err=%s", srtfd, srt_getlasterror_str()); + } + + fd_sockets_.erase(srtfd); + + return err; +} + +srs_error_t SrsSrtPoller::wait(int timeout_ms, int* pn_fds) +{ + srs_error_t err = srs_success; + + // wait srt event fired, will timeout after `timeout_ms` milliseconds. + int ret = srt_epoll_uwait(srt_epoller_fd_, events_.data(), events_.size(), timeout_ms); + *pn_fds = ret; + + if (ret < 0) { + return srs_error_new(ERROR_SRT_EPOLL, "srt_epoll_uwait, ret=%d, err=%s", ret, srt_getlasterror_str()); + } + + for (int i = 0; i < ret; ++i) { + SRT_EPOLL_EVENT event = events_[i]; + map::iterator iter = fd_sockets_.find(event.fd); + if (iter == fd_sockets_.end()) { + srs_assert(false); + } + + SrsSrtSocket* srt_skt = iter->second; + srs_assert(srt_skt != NULL); + + // notify error, don't notify read/write event. + if (event.events & SRT_EPOLL_ERR) { + srt_skt->notify_error(); + } else { + if (event.events & SRT_EPOLL_IN) { + srt_skt->notify_readable(); + } + if (event.events & SRT_EPOLL_OUT) { + srt_skt->notify_writeable(); + } + } + } + + return err; +} + +int SrsSrtPoller::size() +{ + return (int)fd_sockets_.size(); +} + +srs_error_t SrsSrtPoller::mod_socket(SrsSrtSocket* srt_skt) +{ + srs_error_t err = srs_success; + + int events = srt_skt->events(); + srs_srt_t srtfd = srt_skt->fd(); + + int ret = srt_epoll_update_usock(srt_epoller_fd_, srtfd, &events); + srs_info("srt poller %d update srt socket %d, events=%d", srt_epoller_fd_, srtfd, events); + + if (ret == SRT_ERROR) { + return srs_error_new(ERROR_SRT_EPOLL, "srt epoll update socket=%lu failed, err=%s", srtfd, srt_getlasterror_str()); + } + + return err; +} + +ISrsSrtPoller::ISrsSrtPoller() +{ +} + +ISrsSrtPoller::~ISrsSrtPoller() +{ +} + +ISrsSrtPoller* srs_srt_poller_new() +{ + return new SrsSrtPoller(); +} + +SrsSrtSocket::SrsSrtSocket(ISrsSrtPoller* srt_poller, srs_srt_t srt_fd) +{ + srt_poller_ = srt_poller; + srt_fd_ = srt_fd; + has_error_ = 0; + read_cond_ = srs_cond_new(); + write_cond_ = srs_cond_new(); + + recv_timeout_ = 5 * SRS_UTIME_SECONDS; + send_timeout_ = 5 * SRS_UTIME_SECONDS; + + recv_bytes_ = 0; + send_bytes_ = 0; + + events_ = 0; +} + +SrsSrtSocket::~SrsSrtSocket() +{ + srs_error_t err = srt_poller_->del_socket(this); + if (err != srs_success) { + srs_error("srt poller remove socket failed, err=%s", srs_error_desc(err).c_str()); + srs_error_reset(err); + } + + srs_cond_destroy(read_cond_); + srs_cond_destroy(write_cond_); + + srs_trace("close srt_fd=%d", srt_fd_); + srt_close(srt_fd_); +} + +srs_error_t SrsSrtSocket::connect(const string& ip, int port) +{ + srs_error_t err = srs_success; + + sockaddr_in inaddr; + inaddr.sin_family = AF_INET; + inaddr.sin_port = htons(port); + // TODO: FIXME: inet_addr is deprecated + inaddr.sin_addr.s_addr = inet_addr(ip.c_str()); + + // @see https://github.com/Haivision/srt/blob/master/docs/API/API-functions.md#srt_connect + int ret = srt_connect(srt_fd_, (const sockaddr*)&inaddr, sizeof(inaddr)); + if (ret != 0) { + return srs_error_new(ERROR_SRT_IO, "srt_connect, err=%s", srt_getlasterror_str()); + } + + // Connect succeed, in async mode, means SRT API succeed and return directly, + // and the connection is in progress, like tcp socket API connect errno EINPROGRESS, + // and the SRT IO threads will do the real handshake step to finish srt connect. + SRT_SOCKSTATUS srt_status = srt_getsockstate(srt_fd_); + if (srt_status == SRTS_CONNECTED) { + return err; + } + + // Connect is in progress, wait until it finish or error. + if ((err = wait_writeable()) != srs_success) { + return srs_error_wrap(err, "wait writeable"); + } + + // Double check if connect is established. + srt_status = srt_getsockstate(srt_fd_); + if (srt_status != SRTS_CONNECTED) { + return srs_error_new(ERROR_SRT_IO, "srt_connect, err=%s", srt_getlasterror_str()); + } + + return err; +} + +srs_error_t SrsSrtSocket::accept(srs_srt_t* client_srt_fd) +{ + srs_error_t err = srs_success; + + while (true) { + sockaddr_in inaddr; + int addrlen = sizeof(inaddr); + // @see https://github.com/Haivision/srt/blob/master/docs/API/API-functions.md#srt_accept + srs_srt_t srt_fd = srt_accept(srt_fd_, (sockaddr*)&inaddr, &addrlen); + + // Accept ok, return with the SRT client fd. + if (srt_fd != srs_srt_socket_invalid()) { + *client_srt_fd = srt_fd; + return err; + } + + // Got something error, return immediately. + if (srt_getlasterror(NULL) != SRT_EASYNCRCV) { + return srs_error_new(ERROR_SRT_IO, "srt_accept, err=%s", srt_getlasterror_str()); + } + + // Accept would block, wait until new client connect or error. + if ((err = wait_readable()) != srs_success) { + return srs_error_wrap(err, "wait readable"); + } + } + + return err; +} + +srs_error_t SrsSrtSocket::recvmsg(void* buf, size_t size, ssize_t* nread) +{ + srs_error_t err = srs_success; + + while (true) { + // @see https://github.com/Haivision/srt/blob/master/docs/API/API-functions.md#srt_recvmsg + int ret = srt_recvmsg(srt_fd_, (char*)buf, size); + + // Receive message ok. + if (ret >= 0) { + recv_bytes_ += ret; + *nread = ret; + return err; + } + + // Got something error, return immediately. + if (srt_getlasterror(NULL) != SRT_EASYNCRCV) { + return srs_error_new(ERROR_SRT_IO, "srt_recvmsg, err=%s", srt_getlasterror_str()); + } + + // Wait for the fd ready or error, switch to other coroutines. + if ((err = wait_readable()) != srs_success) { + return srs_error_wrap(err, "wait readable"); + } + } + + return err; +} + +srs_error_t SrsSrtSocket::sendmsg(void* buf, size_t size, ssize_t* nwrite) +{ + srs_error_t err = srs_success; + + while (true) { + // @see https://github.com/Haivision/srt/blob/master/docs/API/API-functions.md#srt_sendmsg + int ret = srt_sendmsg(srt_fd_, (const char*)buf, size, -1, 1); + + // Send message ok. + if (ret >= 0) { + send_bytes_ += ret; + *nwrite = ret; + return err; + } + + // Got something error, return immediately. + if (srt_getlasterror(NULL) != SRT_EASYNCSND) { + return srs_error_new(ERROR_SRT_IO, "srt_sendmsg, err=%s", srt_getlasterror_str()); + } + + // Wait for the fd ready or error, switch to other coroutines. + if ((err = wait_writeable()) != srs_success) { + return srs_error_wrap(err, "wait writeable"); + } + } + + return err; +} + +srs_error_t SrsSrtSocket::wait_readable() +{ + srs_error_t err = srs_success; + + // Check if error occured. + if ((err = check_error()) != srs_success) { + return srs_error_wrap(err, "has error"); + } + + // Subscribe in and error event + if ((err = enable_read()) != srs_success) { + return srs_error_wrap(err, "enable read"); + } + + // Wait event fired or timeout. + int ret = srs_cond_timedwait(read_cond_, recv_timeout_); + // TODO: FIXME: need to disable it? + if ((err = disable_read()) != srs_success) { + srs_freep(err); + } + + if (ret != 0) { + // Timeout and events no fired. + if (errno == ETIME) { + return srs_error_new(ERROR_SRT_TIMEOUT, "srt socket %d timeout", srt_fd_); + } + // Interrupted, maybe coroutine terminated. + if (errno == EINTR) { + return srs_error_new(ERROR_SRT_INTERRUPT, "srt socket %d interrupted", srt_fd_); + } + return srs_error_new(ERROR_SRT_IO, "srt socket %d wait read", srt_fd_); + } + + // Check if we are notify with error event. + if ((err = check_error()) != srs_success) { + return srs_error_wrap(err, "has error"); + } + + return err; +} + +srs_error_t SrsSrtSocket::wait_writeable() +{ + srs_error_t err = srs_success; + + if ((err = check_error()) != srs_success) { + return srs_error_wrap(err, "has error"); + } + + if ((err = enable_write()) != srs_success) { + return srs_error_wrap(err, "enable write"); + } + + int ret = srs_cond_timedwait(write_cond_, send_timeout_); + if ((err = disable_write()) != srs_success) { + srs_freep(err); + } + + if (ret != 0) { + if (errno == ETIME) { + return srs_error_new(ERROR_SRT_TIMEOUT, "srt socket %d timeout", srt_fd_); + } + if (errno == EINTR) { + return srs_error_new(ERROR_SRT_INTERRUPT, "srt socket %d interrupted", srt_fd_); + } + return srs_error_new(ERROR_SRT_IO, "srt socket %d wait write", srt_fd_); + } + + if ((err = check_error()) != srs_success) { + return srs_error_wrap(err, "has error"); + } + + return err; +} + +void SrsSrtSocket::notify_readable() +{ + srs_cond_signal(read_cond_); +} + +void SrsSrtSocket::notify_writeable() +{ + srs_cond_signal(write_cond_); +} + +void SrsSrtSocket::notify_error() +{ + // mark error, and check when read/write + has_error_ = true; + srs_cond_signal(read_cond_); + srs_cond_signal(write_cond_); +} + +srs_error_t SrsSrtSocket::enable_read() +{ + return enable_event(SRT_EPOLL_IN | SRT_EPOLL_ERR); +} + +srs_error_t SrsSrtSocket::disable_read() +{ + return disable_event(SRT_EPOLL_IN); +} + +srs_error_t SrsSrtSocket::enable_write() +{ + return enable_event(SRT_EPOLL_OUT | SRT_EPOLL_ERR); +} + +srs_error_t SrsSrtSocket::disable_write() +{ + return disable_event(SRT_EPOLL_OUT); +} + +srs_error_t SrsSrtSocket::enable_event(int event) +{ + srs_error_t err = srs_success; + + // Event has been subscribed. + if ((events_ & event) == event) { + return err; + } + + int old_events = events_; + events_ |= event; + + if (old_events == 0) { + err = srt_poller_->add_socket(this); + } else { + err = srt_poller_->mod_socket(this); + } + + return err; +} + +srs_error_t SrsSrtSocket::disable_event(int event) +{ + srs_error_t err = srs_success; + + // Event has been unsubscribed. + if ((events_ & event) == 0) { + return err; + } + + events_ &= (~event); + + if (events_ == 0) { + err = srt_poller_->del_socket(this); + } else { + err = srt_poller_->mod_socket(this); + } + + return err; +} + +srs_error_t SrsSrtSocket::check_error() +{ + srs_error_t err = srs_success; + + if (has_error_) { + return srs_error_new(ERROR_SRT_IO, "has error"); + } + + return err; +} + diff --git a/trunk/src/protocol/srs_protocol_srt.hpp b/trunk/src/protocol/srs_protocol_srt.hpp new file mode 100644 index 0000000000..e1fd2e7680 --- /dev/null +++ b/trunk/src/protocol/srs_protocol_srt.hpp @@ -0,0 +1,180 @@ +// +// Copyright (c) 2013-2021 The SRS Authors +// +// SPDX-License-Identifier: MIT or MulanPSL-2.0 +// + +#ifndef SRS_PROTOCOL_SRT_HPP +#define SRS_PROTOCOL_SRT_HPP + +#include +#include + +#include +#include + +class SrsSrtSocket; + +extern srs_error_t srs_srt_log_initialie(); + +typedef int srs_srt_t; +extern srs_srt_t srs_srt_socket_invalid(); + +// Create srt socket only, with libsrt's default option. +extern srs_error_t srs_srt_socket(srs_srt_t* pfd); +extern srs_error_t srs_srt_close(srs_srt_t fd); + +// Create srt socket with srs recommend default option(tsbpdmode=false,tlpktdrop=false,latency=0,sndsyn=0,rcvsyn=0) +extern srs_error_t srs_srt_socket_with_default_option(srs_srt_t* pfd); + +// For server, listen at SRT endpoint. +extern srs_error_t srs_srt_listen(srs_srt_t srt_fd, std::string ip, int port); + +// Set read/write no block. +extern srs_error_t srs_srt_nonblock(srs_srt_t srt_fd); + +// Set SRT options. +extern srs_error_t srs_srt_set_maxbw(srs_srt_t srt_fd, int maxbw); +extern srs_error_t srs_srt_set_mss(srs_srt_t srt_fd, int mss); +extern srs_error_t srs_srt_set_payload_size(srs_srt_t srt_fd, int payload_size); +extern srs_error_t srs_srt_set_connect_timeout(srs_srt_t srt_fd, int timeout); +extern srs_error_t srs_srt_set_peer_idle_timeout(srs_srt_t srt_fd, int timeout); +extern srs_error_t srs_srt_set_tsbpdmode(srs_srt_t srt_fd, bool tsbpdmode); +extern srs_error_t srs_srt_set_sndbuf(srs_srt_t srt_fd, int sndbuf); +extern srs_error_t srs_srt_set_rcvbuf(srs_srt_t srt_fd, int rcvbuf); +extern srs_error_t srs_srt_set_tlpktdrop(srs_srt_t srt_fd, bool tlpktdrop); +extern srs_error_t srs_srt_set_latency(srs_srt_t srt_fd, int latency); +extern srs_error_t srs_srt_set_rcv_latency(srs_srt_t srt_fd, int rcv_latency); +extern srs_error_t srs_srt_set_peer_latency(srs_srt_t srt_fd, int peer_latency); +extern srs_error_t srs_srt_set_streamid(srs_srt_t srt_fd, const std::string& streamid); + +// Get SRT options. +extern srs_error_t srs_srt_get_maxbw(srs_srt_t srt_fd, int& maxbw); +extern srs_error_t srs_srt_get_mss(srs_srt_t srt_fd, int& mss); +extern srs_error_t srs_srt_get_payload_size(srs_srt_t srt_fd, int& payload_size); +extern srs_error_t srs_srt_get_connect_timeout(srs_srt_t srt_fd, int& timeout); +extern srs_error_t srs_srt_get_peer_idle_timeout(srs_srt_t srt_fd, int& timeout); +extern srs_error_t srs_srt_get_tsbpdmode(srs_srt_t srt_fd, bool& tsbpdmode); +extern srs_error_t srs_srt_get_sndbuf(srs_srt_t srt_fd, int& sndbuf); +extern srs_error_t srs_srt_get_rcvbuf(srs_srt_t srt_fd, int& rcvbuf); +extern srs_error_t srs_srt_get_tlpktdrop(srs_srt_t srt_fd, bool& tlpktdrop); +extern srs_error_t srs_srt_get_latency(srs_srt_t srt_fd, int& latency); +extern srs_error_t srs_srt_get_rcv_latency(srs_srt_t srt_fd, int& rcv_latency); +extern srs_error_t srs_srt_get_peer_latency(srs_srt_t srt_fd, int& peer_latency); +extern srs_error_t srs_srt_get_streamid(srs_srt_t srt_fd, std::string& streamid); + +// Get SRT socket info. +extern srs_error_t srs_srt_get_local_ip_port(srs_srt_t srt_fd, std::string& ip, int& port); +extern srs_error_t srs_srt_get_remote_ip_port(srs_srt_t srt_fd, std::string& ip, int& port); + +// Get SRT stats. +class SrsSrtStat +{ +private: + void* stat_; +public: + SrsSrtStat(); + virtual ~SrsSrtStat(); +public: + int64_t pktRecv(); + int pktRcvLoss(); + int pktRcvRetrans(); + int pktRcvDrop(); +public: + int64_t pktSent(); + int pktSndLoss(); + int pktRetrans(); + int pktSndDrop(); +public: + srs_error_t fetch(srs_srt_t srt_fd, bool clear); +}; + +// Srt poller, subscribe/unsubscribed events and wait them fired. +class ISrsSrtPoller +{ +public: + ISrsSrtPoller(); + virtual ~ISrsSrtPoller(); +public: + virtual srs_error_t initialize() = 0; + virtual srs_error_t add_socket(SrsSrtSocket* srt_skt) = 0; + virtual srs_error_t mod_socket(SrsSrtSocket* srt_skt) = 0; + virtual srs_error_t del_socket(SrsSrtSocket* srt_skt) = 0; + // Wait for the fds in its epoll to be fired in specified timeout_ms, where the pn_fds is the number of active fds. + // Note that for ST, please always use timeout_ms(0) and switch coroutine by yourself. + virtual srs_error_t wait(int timeout_ms, int* pn_fds) = 0; +public: + virtual int size() = 0; +}; +ISrsSrtPoller* srs_srt_poller_new(); + +// Srt ST socket, wrap SRT io and make it adapt to ST-thread. +class SrsSrtSocket +{ +public: + SrsSrtSocket(ISrsSrtPoller* srt_poller, srs_srt_t srt_fd); + virtual ~SrsSrtSocket(); +public: // IO API + srs_error_t connect(const std::string& ip, int port); + srs_error_t accept(srs_srt_t* client_srt_fd); + srs_error_t recvmsg(void* buf, size_t size, ssize_t* nread); + srs_error_t sendmsg(void* buf, size_t size, ssize_t* nwrite); +public: + srs_srt_t fd() const { return srt_fd_; } + int events() const { return events_; } +public: + void set_recv_timeout(srs_utime_t tm) { recv_timeout_ = tm; } + void set_send_timeout(srs_utime_t tm) { send_timeout_ = tm; } + srs_utime_t get_send_timeout() { return send_timeout_; } + srs_utime_t get_recv_timeout() { return recv_timeout_; } + int64_t get_send_bytes() { return send_bytes_; } + int64_t get_recv_bytes() { return recv_bytes_; } + // Yiled coroutine and wait this socket readable. + srs_error_t wait_readable(); + // Yiled coroutine and wait this socket writeable. + srs_error_t wait_writeable(); + // Notify this socket readable, and resume coroutine later. + void notify_readable(); + // Notify this socket writeable, and resume coroutine later. + void notify_writeable(); + // Notify this socket error, resume coroutine later and error will return in all the operator of this socket. + void notify_error(); +public: + // Subscribed IN/ERR event to srt poller. + srs_error_t enable_read(); + // Unsubscribed IN event to srt poller. + srs_error_t disable_read(); + // Subscribed OUT/ERR event to srt poller. + srs_error_t enable_write(); + // Unsubscribed OUT event to srt poller. + srs_error_t disable_write(); +private: + srs_error_t enable_event(int event); + srs_error_t disable_event(int event); + srs_error_t check_error(); + +private: + srs_srt_t srt_fd_; + // Mark if some error occured in srt socket. + bool has_error_; + // When read operator like recvmsg/accept would block, wait this condition timeout or notified, + // and the coroutine itself will be yiled and resume when condition be notified. + srs_cond_t read_cond_; + // When write operator like sendmsg/connectt would block, wait this condition timeout or notified, + // and the coroutine itself will be yiled and resume when condition be notified. + srs_cond_t write_cond_; + + srs_utime_t recv_timeout_; + srs_utime_t send_timeout_; + + int64_t recv_bytes_; + int64_t send_bytes_; + + // Event of this socket subscribed. + int events_; + // Srt poller which this socket attach to. + ISrsSrtPoller* srt_poller_; +}; + +#endif + diff --git a/trunk/src/srt/srt_conn.hpp b/trunk/src/srt/srt_conn.hpp deleted file mode 100644 index 0608e23059..0000000000 --- a/trunk/src/srt/srt_conn.hpp +++ /dev/null @@ -1,63 +0,0 @@ -// -// Copyright (c) 2013-2021 The SRS Authors -// -// SPDX-License-Identifier: MIT or MulanPSL-2.0 -// - -#ifndef SRT_CONN_H -#define SRT_CONN_H - -#include - -#include "stringex.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define ERR_SRT_MODE 0x00 -#define PULL_SRT_MODE 0x01 -#define PUSH_SRT_MODE 0x02 - -bool is_streamid_valid(const std::string& streamid); -bool get_key_value(const std::string& info, std::string& key, std::string& value); -bool get_streamid_info(const std::string& streamid, int& mode, std::string& vhost, std::string& url_subpash); - -class srt_conn { -public: - srt_conn(SRTSOCKET conn_fd, const std::string& streamid); - ~srt_conn(); - - void close(); - SRTSOCKET get_conn(); - int get_mode(); - std::string get_streamid(); - std::string get_path(); - std::string get_subpath(); - std::string get_vhost(); - int read(unsigned char* data, int len); - int write(unsigned char* data, int len); - - void update_timestamp(long long now_ts); - long long get_last_ts(); - int get_write_fail_count(); - -private: - SRTSOCKET _conn_fd; - std::string _streamid; - std::string _url_path; - std::string _url_subpath; - std::string _vhost; - int _mode; - long long _update_timestamp; - int write_fail_cnt_; -}; - -typedef std::shared_ptr SRT_CONN_PTR; - -#endif //SRT_CONN_H diff --git a/trunk/src/srt/srt_data.cpp b/trunk/src/srt/srt_data.cpp deleted file mode 100644 index 2087b70d57..0000000000 --- a/trunk/src/srt/srt_data.cpp +++ /dev/null @@ -1,71 +0,0 @@ -// -// Copyright (c) 2013-2021 The SRS Authors -// -// SPDX-License-Identifier: MIT or MulanPSL-2.0 -// - -#include "srt_data.hpp" -#include - -SRT_DATA_MSG::SRT_DATA_MSG(const std::string& path, unsigned int msg_type):_msg_type(msg_type) - ,_len(0) - ,_data_p(nullptr) - ,_key_path(path) { - -} - -SRT_DATA_MSG::SRT_DATA_MSG(unsigned int len, const std::string& path, unsigned int msg_type):_msg_type(msg_type) - ,_len(len) - ,_key_path(path) { - _data_p = new unsigned char[len]; - memset(_data_p, 0, len); -} - -SRT_DATA_MSG::SRT_DATA_MSG(unsigned char* data_p, unsigned int len, const std::string& path, unsigned int msg_type):_msg_type(msg_type) - ,_len(len) - ,_key_path(path) -{ - _data_p = new unsigned char[len]; - memcpy(_data_p, data_p, len); -} - -SRT_DATA_MSG::SRT_DATA_MSG(LOGGER_LEVEL log_level, const std::string& log_content): _msg_type(SRT_MSG_LOG_TYPE) - ,_log_content(log_content) - ,_log_level(log_level) -{ - -} - -SRT_DATA_MSG::~SRT_DATA_MSG() { - if (_data_p && (_len > 0)) { - delete[] _data_p; - } -} - -unsigned int SRT_DATA_MSG::msg_type() { - return _msg_type; -} - -void SRT_DATA_MSG::set_msg_type(unsigned int msg_type) { - _msg_type = msg_type; -} - -std::string SRT_DATA_MSG::get_path() { - return _key_path; -} - -unsigned int SRT_DATA_MSG::data_len() { - return _len; -} - -unsigned char* SRT_DATA_MSG::get_data() { - return _data_p; -} - -LOGGER_LEVEL SRT_DATA_MSG::get_log_level() { - return _log_level; -} - -const char* SRT_DATA_MSG::get_log_string() { - return _log_content.c_str(); -} diff --git a/trunk/src/srt/srt_data.hpp b/trunk/src/srt/srt_data.hpp deleted file mode 100644 index e7241fc90f..0000000000 --- a/trunk/src/srt/srt_data.hpp +++ /dev/null @@ -1,48 +0,0 @@ -// -// Copyright (c) 2013-2021 The SRS Authors -// -// SPDX-License-Identifier: MIT or MulanPSL-2.0 -// - -#ifndef SRT_DATA_H -#define SRT_DATA_H - -#include "srt_log.hpp" -#include - -#include -#include - -#define SRT_MSG_DATA_TYPE 0x01 -#define SRT_MSG_CLOSE_TYPE 0x02 -#define SRT_MSG_LOG_TYPE 0x03 - -class SRT_DATA_MSG { -public: - SRT_DATA_MSG(const std::string& path, unsigned int msg_type=SRT_MSG_DATA_TYPE); - SRT_DATA_MSG(unsigned int len, const std::string& path, unsigned int msg_type=SRT_MSG_DATA_TYPE); - SRT_DATA_MSG(unsigned char* data_p, unsigned int len, const std::string& path, unsigned int msg_type=SRT_MSG_DATA_TYPE); - SRT_DATA_MSG(LOGGER_LEVEL log_level, const std::string& log_content); - ~SRT_DATA_MSG(); - - unsigned int msg_type(); - unsigned int data_len(); - unsigned char* get_data(); - std::string get_path(); - LOGGER_LEVEL get_log_level(); - const char* get_log_string(); - - void set_msg_type(unsigned int msg_type); - -private: - unsigned int _msg_type; - unsigned int _len = 0; - unsigned char* _data_p = nullptr; - std::string _key_path; - std::string _log_content; - LOGGER_LEVEL _log_level = SRT_LOGGER_TRACE_LEVEL; -}; - -typedef std::shared_ptr SRT_DATA_MSG_PTR; - -#endif diff --git a/trunk/src/srt/srt_handle.cpp b/trunk/src/srt/srt_handle.cpp deleted file mode 100644 index fd1f951bb4..0000000000 --- a/trunk/src/srt/srt_handle.cpp +++ /dev/null @@ -1,406 +0,0 @@ -// -// Copyright (c) 2013-2021 The SRS Authors -// -// SPDX-License-Identifier: MIT or MulanPSL-2.0 -// - -#include "srt_handle.hpp" -#include "time_help.hpp" -#include "srt_log.hpp" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -static bool MONITOR_STATICS_ENABLE = false; -static long long MONITOR_TIMEOUT = 5000; -const unsigned int DEF_DATA_SIZE = 188*7; -const long long CHECK_ALIVE_INTERVAL = 5*1000; -const long long CHECK_ALIVE_TIMEOUT = 5*1000; -static const int SRT_WRTIE_FAIL_MAX = 10; - -long long srt_now_ms = 0; - -srt_handle::srt_handle(int pollid):_handle_pollid(pollid) - ,_last_timestamp(0) - ,_last_check_alive_ts(0) { -} - -srt_handle::~srt_handle() { - -} - -void srt_handle::debug_statics(SRTSOCKET srtsocket, const std::string& streamid) { - SRT_TRACEBSTATS mon; - srt_bstats(srtsocket, &mon, 1); - std::ostringstream output; - long long now_ul = now_ms(); - - if (!MONITOR_STATICS_ENABLE) { - return; - } - if (_last_timestamp == 0) { - _last_timestamp = now_ul; - return; - } - - if ((now_ul - _last_timestamp) < MONITOR_TIMEOUT) { - return; - } - _last_timestamp = now_ul; - output << "======= SRT STATS: sid=" << streamid << std::endl; - output << "PACKETS SENT: " << std::setw(11) << mon.pktSent << " RECEIVED: " << std::setw(11) << mon.pktRecv << std::endl; - output << "LOST PKT SENT: " << std::setw(11) << mon.pktSndLoss << " RECEIVED: " << std::setw(11) << mon.pktRcvLoss << std::endl; - output << "REXMIT SENT: " << std::setw(11) << mon.pktRetrans << " RECEIVED: " << std::setw(11) << mon.pktRcvRetrans << std::endl; - output << "DROP PKT SENT: " << std::setw(11) << mon.pktSndDrop << " RECEIVED: " << std::setw(11) << mon.pktRcvDrop << std::endl; - output << "RATE SENDING: " << std::setw(11) << mon.mbpsSendRate << " RECEIVING: " << std::setw(11) << mon.mbpsRecvRate << std::endl; - output << "BELATED RECEIVED: " << std::setw(11) << mon.pktRcvBelated << " AVG TIME: " << std::setw(11) << mon.pktRcvAvgBelatedTime << std::endl; - output << "REORDER DISTANCE: " << std::setw(11) << mon.pktReorderDistance << std::endl; - output << "WINDOW FLOW: " << std::setw(11) << mon.pktFlowWindow << " CONGESTION: " << std::setw(11) << mon.pktCongestionWindow << " FLIGHT: " << std::setw(11) << mon.pktFlightSize << std::endl; - output << "LINK RTT: " << std::setw(9) << mon.msRTT << "ms BANDWIDTH: " << std::setw(7) << mon.mbpsBandwidth << "Mb/s " << std::endl; - output << "BUFFERLEFT: SND: " << std::setw(11) << mon.byteAvailSndBuf << " RCV: " << std::setw(11) << mon.byteAvailRcvBuf << std::endl; - - srt_log_trace("\r\n%s", output.str().c_str()); - return; -} - -void srt_handle::add_new_puller(SRT_CONN_PTR conn_ptr, std::string stream_id) { - _conn_map.insert(std::make_pair(conn_ptr->get_conn(), conn_ptr)); - - auto iter = _streamid_map.find(stream_id); - if (iter == _streamid_map.end()) { - std::unordered_map srtsocket_map; - srtsocket_map.insert(std::make_pair(conn_ptr->get_conn(), conn_ptr)); - - _streamid_map.insert(std::make_pair(stream_id, srtsocket_map)); - srt_log_trace("add new puller fd:%d, streamid:%s", conn_ptr->get_conn(), stream_id.c_str()); - } else { - iter->second.insert(std::make_pair(conn_ptr->get_conn(), conn_ptr)); - srt_log_trace("add new puller fd:%d, streamid:%s, size:%d", - conn_ptr->get_conn(), stream_id.c_str(), iter->second.size()); - } - - return; -} - -void srt_handle::close_pull_conn(SRTSOCKET srtsocket, std::string stream_id) { - srt_log_trace("close_pull_conn read_fd=%d, streamid=%s", srtsocket, stream_id.c_str()); - srt_epoll_remove_usock(_handle_pollid, srtsocket); - - auto streamid_iter = _streamid_map.find(stream_id); - if (streamid_iter != _streamid_map.end()) { - if (streamid_iter->second.size() == 0) { - _streamid_map.erase(stream_id); - } else if (streamid_iter->second.size() == 1) { - streamid_iter->second.erase(srtsocket); - _streamid_map.erase(stream_id); - } else { - streamid_iter->second.erase(srtsocket); - } - } else { - assert(0); - } - - auto conn_iter = _conn_map.find(srtsocket); - if (conn_iter != _conn_map.end()) { - _conn_map.erase(conn_iter); - return; - } else { - assert(0); - } - - return; -} - -SRT_CONN_PTR srt_handle::get_srt_conn(SRTSOCKET conn_srt_socket) { - SRT_CONN_PTR ret_conn; - - auto iter = _conn_map.find(conn_srt_socket); - if (iter == _conn_map.end()) { - return ret_conn; - } - - ret_conn = iter->second; - - return ret_conn; -} - -void srt_handle::add_newconn(SRT_CONN_PTR conn_ptr, int events) { - int val_i; - int opt_len = sizeof(int); - - int64_t val_i64; - int opt64_len = sizeof(int64_t); - - srt_getsockopt(conn_ptr->get_conn(), 0, SRTO_LATENCY, &val_i, &opt_len); - srt_log_trace("srto SRTO_LATENCY=%d", val_i); - - srt_getsockopt(conn_ptr->get_conn(), 0, SRTO_PEERLATENCY, &val_i, &opt_len); - srt_log_trace("srto SRTO_PEERLATENCY=%d", val_i); - srt_getsockopt(conn_ptr->get_conn(), 0, SRTO_RCVLATENCY, &val_i, &opt_len); - srt_log_trace("srto SRTO_RCVLATENCY=%d", val_i); - - srt_getsockopt(conn_ptr->get_conn(), 0, SRTO_SNDBUF, &val_i, &opt_len); - srt_log_trace("srto SRTO_SNDBUF=%d", val_i); - srt_getsockopt(conn_ptr->get_conn(), 0, SRTO_RCVBUF, &val_i, &opt_len); - srt_log_trace("srto SRTO_RCVBUF=%d", val_i); - srt_getsockopt(conn_ptr->get_conn(), 0, SRTO_MAXBW, &val_i64, &opt64_len); - srt_log_trace("srto SRTO_MAXBW=%d", val_i64); - srt_log_trace("srt mix_correct is %s.", _srs_config->get_srt_mix_correct() ? "enable" : "disable"); - srt_log_trace("srt h264 sei filter is %s.", _srs_config->get_srt_sei_filter() ? "enable" : "disable"); - - if (conn_ptr->get_mode() == PULL_SRT_MODE) { - add_new_puller(conn_ptr, conn_ptr->get_path()); - } else { - if(add_new_pusher(conn_ptr) == false) { - srt_log_trace("push connection is repeated and rejected, fd:%d, streamid:%s", - conn_ptr->get_conn(), conn_ptr->get_streamid().c_str()); - conn_ptr->close(); - return; - } - } - srt_log_trace("new conn added fd:%d, event:0x%08x", conn_ptr->get_conn(), events); - int ret = srt_epoll_add_usock(_handle_pollid, conn_ptr->get_conn(), &events); - if (ret < 0) { - srt_log_error("srt handle run add epoll error:%d", ret); - return; - } - - return; -} - -void srt_handle::handle_push_data(SRT_SOCKSTATUS status, const std::string& path, const std::string& subpath, SRTSOCKET conn_fd) { - SRT_CONN_PTR srt_conn_ptr; - unsigned char data[DEF_DATA_SIZE]; - int ret; - srt_conn_ptr = get_srt_conn(conn_fd); - - if (!srt_conn_ptr) { - srt_log_error("handle_push_data fd:%d fail to find srt connection.", conn_fd); - return; - } - - if (status != SRTS_CONNECTED) { - srt_log_error("handle_push_data error status:%d fd:%d", status, conn_fd); - close_push_conn(conn_fd); - return; - } - - ret = srt_conn_ptr->read(data, DEF_DATA_SIZE); - if (ret <= 0) { - srt_log_error("handle_push_data srt connect read error:%d, fd:%d", ret, conn_fd); - close_push_conn(conn_fd); - return; - } - - srt_conn_ptr->update_timestamp(srt_now_ms); - - srt2rtmp::get_instance()->insert_data_message(data, ret, subpath); - { - std::unique_lock locker(srt2rtmp::_srt_error_mutex); - if (srt2rtmp::_srt_error_map.count(subpath) == 1) { - int err_code = srt2rtmp::_srt_error_map[subpath]; - if (err_code != ERROR_SUCCESS) { - close_push_conn(conn_fd); - srt_log_error("handle_push_data srt to rtmp error:%d, fd:%d", err_code,conn_fd); - //todo: reset to next use, maybe update by srt2rtmp::cycle again - srt2rtmp::_srt_error_map[subpath] = ERROR_SUCCESS; - return; - } - } - } - - //send data to subscriber(players) - //streamid, play map - auto streamid_iter = _streamid_map.find(path); - if (streamid_iter == _streamid_map.end()) {//no puler - srt_log_info("receive data size(%d) from pusher(%d) but no puller", ret, conn_fd); - return; - } - srt_log_info("receive data size(%d) from pusher(%d) to pullers, count:%d", - ret, conn_fd, streamid_iter->second.size()); - - std::vector remove_vec; - for (auto puller_iter = streamid_iter->second.begin(); - puller_iter != streamid_iter->second.end(); - puller_iter++) { - auto player_conn = puller_iter->second; - if (!player_conn) { - srt_log_error("handle_push_data get srt connect error from fd:%d", puller_iter->first); - continue; - } - int write_ret = player_conn->write(data, ret); - srt_log_info("send data size(%d) to puller fd:%d", write_ret, puller_iter->first); - if (write_ret > 0) { - puller_iter->second->update_timestamp(srt_now_ms); - } else { - if (player_conn->get_write_fail_count() > SRT_WRTIE_FAIL_MAX) { - remove_vec.push_back(puller_iter->first); - } - } - } - - for (auto item : remove_vec) { - streamid_iter->second.erase(item); - if (streamid_iter->second.empty()) { - _streamid_map.erase(streamid_iter); - } - } - - return; -} - -void srt_handle::check_alive() { - long long diff_t; - std::list conn_list; - - if (_last_check_alive_ts == 0) { - _last_check_alive_ts = srt_now_ms; - return; - } - diff_t = srt_now_ms - _last_check_alive_ts; - if (diff_t < CHECK_ALIVE_INTERVAL) { - return; - } - - for (auto conn_iter = _conn_map.begin(); - conn_iter != _conn_map.end(); - conn_iter++) - { - long long timeout = srt_now_ms - conn_iter->second->get_last_ts(); - if (timeout > CHECK_ALIVE_TIMEOUT) { - conn_list.push_back(conn_iter->second); - } - } - - for (auto del_iter = conn_list.begin(); - del_iter != conn_list.end(); - del_iter++) - { - SRT_CONN_PTR conn_ptr = *del_iter; - if (conn_ptr->get_mode() == PUSH_SRT_MODE) { - srt_log_warn("check alive close pull connection fd:%d, streamid:%s", - conn_ptr->get_conn(), conn_ptr->get_subpath().c_str()); - close_push_conn(conn_ptr->get_conn()); - } else if (conn_ptr->get_mode() == PULL_SRT_MODE) { - srt_log_warn("check alive close pull connection fd:%d, streamid:%s", - conn_ptr->get_conn(), conn_ptr->get_path().c_str()); - close_pull_conn(conn_ptr->get_conn(), conn_ptr->get_path()); - } else { - srt_log_error("check_alive get unkown srt mode:%d, fd:%d", - conn_ptr->get_mode(), conn_ptr->get_conn()); - assert(0); - } - } -} - -void srt_handle::close_push_conn(SRTSOCKET srtsocket) { - auto iter = _conn_map.find(srtsocket); - - if (iter != _conn_map.end()) { - SRT_CONN_PTR conn_ptr = iter->second; - auto push_iter = _push_conn_map.find(conn_ptr->get_path()); - if (push_iter != _push_conn_map.end()) { - _push_conn_map.erase(push_iter); - } - _conn_map.erase(iter); - srt2rtmp::get_instance()->insert_ctrl_message(SRT_MSG_CLOSE_TYPE, conn_ptr->get_subpath()); - conn_ptr->close(); - } - - srt_epoll_remove_usock(_handle_pollid, srtsocket); - - return; -} - -bool srt_handle::add_new_pusher(SRT_CONN_PTR conn_ptr) { - auto push_iter = _push_conn_map.find(conn_ptr->get_path()); - if (push_iter != _push_conn_map.end()) { - return false; - } - _push_conn_map.insert(std::make_pair(conn_ptr->get_path(), conn_ptr)); - _conn_map.insert(std::make_pair(conn_ptr->get_conn(), conn_ptr)); - srt_log_trace("srt_handle add new pusher streamid:%s, subpath:%s, sid:%s", - conn_ptr->get_streamid().c_str(), conn_ptr->get_subpath().c_str(), conn_ptr->get_path().c_str()); - return true; -} - -void srt_handle::handle_pull_data(SRT_SOCKSTATUS status, const std::string& subpath, SRTSOCKET conn_fd) { - srt_log_info("handle_pull_data status:%d, subpath:%s, fd:%d", - status, subpath.c_str(), conn_fd); - auto conn_ptr = get_srt_conn(conn_fd); - if (!conn_ptr) { - srt_log_error("handle_pull_data fail to find fd(%d)", conn_fd); - assert(0); - return; - } - conn_ptr->update_timestamp(srt_now_ms); -} - -void srt_handle::handle_srt_socket(SRT_SOCKSTATUS status, SRTSOCKET conn_fd) -{ - auto conn_ptr = get_srt_conn(conn_fd); - if (!conn_ptr) { - if (status != SRTS_CLOSED) { - srt_log_error("handle_srt_socket find srt connection error, fd:%d, status:%d", - conn_fd, status); - } - return; - } - - std::string path = conn_ptr->get_path(); - std::string subpath = conn_ptr->get_subpath(); - - int mode = conn_ptr->get_mode(); - if (mode == PUSH_SRT_MODE) { - switch (status) - { - case SRTS_CONNECTED: - { - handle_push_data(status, path, subpath, conn_fd); - break; - } - case SRTS_BROKEN: - { - srt_log_warn("srt push disconnected event fd:%d, streamid:%s", - conn_fd, conn_ptr->get_streamid().c_str()); - close_push_conn(conn_fd); - break; - } - default: - srt_log_error("push mode unkown status:%d, fd:%d", status, conn_fd); - break; - } - } else if (mode == PULL_SRT_MODE) { - switch (status) - { - case SRTS_CONNECTED: - { - handle_pull_data(status, subpath, conn_fd); - break; - } - case SRTS_BROKEN: - { - srt_log_warn("srt pull disconnected fd:%d, streamid:%s", - conn_fd, conn_ptr->get_streamid().c_str()); - close_pull_conn(conn_fd, path); - break; - } - default: - srt_log_error("pull mode unkown status:%d, fd:%d", status, conn_fd); - break; - } - } else { - assert(0); - } - return; -} diff --git a/trunk/src/srt/srt_handle.hpp b/trunk/src/srt/srt_handle.hpp deleted file mode 100644 index 052ac0f19a..0000000000 --- a/trunk/src/srt/srt_handle.hpp +++ /dev/null @@ -1,70 +0,0 @@ -// -// Copyright (c) 2013-2021 The SRS Authors -// -// SPDX-License-Identifier: MIT or MulanPSL-2.0 -// - -#ifndef SRT_HANDLE_H -#define SRT_HANDLE_H - -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include "srt_conn.hpp" -#include "srt_to_rtmp.hpp" - -class srt_handle { -public: - srt_handle(int pollid); - ~srt_handle(); - - //add new srt connection into epoll event - void add_newconn(SRT_CONN_PTR conn_ptr, int events); - //handle recv/send srt socket - void handle_srt_socket(SRT_SOCKSTATUS status, SRTSOCKET conn_fd); - //check srt connection whether it's still alive. - void check_alive(); - -private: - //get srt conn object by srt socket - SRT_CONN_PTR get_srt_conn(SRTSOCKET conn_srt_socket); - - void handle_push_data(SRT_SOCKSTATUS status, const std::string& path, const std::string& subpath, SRTSOCKET conn_fd); - void handle_pull_data(SRT_SOCKSTATUS status, const std::string& subpath, SRTSOCKET conn_fd); - - //add new puller into puller list and conn_map - void add_new_puller(SRT_CONN_PTR, std::string stream_id); - //remove pull srt from play list - void close_pull_conn(SRTSOCKET srtsocket, std::string stream_id); - - //add new pusher into pusher map: - bool add_new_pusher(SRT_CONN_PTR conn_ptr); - //remove push connection and remove epoll - void close_push_conn(SRTSOCKET srtsocket); - - //debug statics - void debug_statics(SRTSOCKET srtsocket, const std::string& streamid); - -private: - int _handle_pollid; - - std::unordered_map _conn_map;//save all srt connection: pull or push - - //save push srt connection for prevent from repeat push connection - std::unordered_map _push_conn_map;//key:streamid, value:SRT_CONN_PTR - //streamid, play map - std::unordered_map> _streamid_map; - - long long _last_timestamp; - long long _last_check_alive_ts; -}; - -#endif //SRT_HANDLE_H diff --git a/trunk/src/srt/srt_log.cpp b/trunk/src/srt/srt_log.cpp deleted file mode 100644 index f850b72030..0000000000 --- a/trunk/src/srt/srt_log.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "srt_log.hpp" -#include "srt_to_rtmp.hpp" -#include -#include -#include - -LOGGER_LEVEL s_log_level = SRT_LOGGER_TRACE_LEVEL; -static char* srt_log_buffer = new char[LOGGER_BUFFER_SIZE]; - -void snprintbuffer(char* buffer, size_t size, const char* fmt, ...) { - va_list ap; - - va_start(ap, fmt); - vsnprintf(buffer, size, fmt, ap); - va_end(ap); - - return; -} - -void set_srt_log_level(LOGGER_LEVEL level) { - s_log_level = level; -} - -LOGGER_LEVEL get_srt_log_level() { - return s_log_level; -} - -char* get_srt_log_buffer() { - return srt_log_buffer; -} - -void srt_log_output(LOGGER_LEVEL level, const char* buffer) { - std::string log_content(buffer); - srt2rtmp::get_instance()->insert_log_message(level, log_content); - return; -} diff --git a/trunk/src/srt/srt_log.hpp b/trunk/src/srt/srt_log.hpp deleted file mode 100644 index a7b2f5d348..0000000000 --- a/trunk/src/srt/srt_log.hpp +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef SRT_LOG_HPP -#define SRT_LOG_HPP -#include -#include - -#define LOGGER_BUFFER_SIZE (10*1024) - -typedef enum { - SRT_LOGGER_INFO_LEVEL, - SRT_LOGGER_TRACE_LEVEL, - SRT_LOGGER_WARN_LEVEL, - SRT_LOGGER_ERROR_LEVEL -} LOGGER_LEVEL; - -void set_srt_log_level(LOGGER_LEVEL level); -LOGGER_LEVEL get_srt_log_level(); -char* get_srt_log_buffer(); -void srt_log_output(LOGGER_LEVEL level, const char* buffer); -void snprintbuffer(char* buffer, size_t size, const char* fmt, ...); - -#define srt_log_error(...) \ - if (get_srt_log_level() <= SRT_LOGGER_ERROR_LEVEL) \ - { \ - char* buffer = get_srt_log_buffer(); \ - snprintbuffer(buffer, LOGGER_BUFFER_SIZE, __VA_ARGS__); \ - srt_log_output(SRT_LOGGER_ERROR_LEVEL, buffer); \ - } - -#define srt_log_warn(...) \ - if (get_srt_log_level() <= SRT_LOGGER_WARN_LEVEL) \ - { \ - char* buffer = get_srt_log_buffer(); \ - snprintbuffer(buffer, LOGGER_BUFFER_SIZE, __VA_ARGS__); \ - srt_log_output(SRT_LOGGER_WARN_LEVEL, buffer); \ - } - -#define srt_log_trace(...) \ - if (get_srt_log_level() <= SRT_LOGGER_TRACE_LEVEL) \ - { \ - char* buffer = get_srt_log_buffer(); \ - snprintbuffer(buffer, LOGGER_BUFFER_SIZE, __VA_ARGS__); \ - srt_log_output(SRT_LOGGER_TRACE_LEVEL, buffer); \ - } - -#define srt_log_info(...) \ - if (get_srt_log_level() <= SRT_LOGGER_INFO_LEVEL) \ - { \ - char* buffer = get_srt_log_buffer(); \ - snprintbuffer(buffer, LOGGER_BUFFER_SIZE, __VA_ARGS__); \ - srt_log_output(SRT_LOGGER_INFO_LEVEL, buffer); \ - } -#endif //SRT_LOG_HPP \ No newline at end of file diff --git a/trunk/src/srt/srt_server.cpp b/trunk/src/srt/srt_server.cpp deleted file mode 100644 index 10630efa3d..0000000000 --- a/trunk/src/srt/srt_server.cpp +++ /dev/null @@ -1,359 +0,0 @@ -// -// Copyright (c) 2013-2021 The SRS Authors -// -// SPDX-License-Identifier: MIT or MulanPSL-2.0 -// - -#include "srt_server.hpp" -#include "srt_handle.hpp" -#include "srt_log.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -srt_server::srt_server(unsigned short port):_listen_port(port) - ,_server_socket(-1) -{ -} - -srt_server::~srt_server() -{ - -} - -int srt_server::init_srt_parameter() { - const int DEF_RECV_LATENCY = 120; - const int DEF_PEER_LATENCY = 0; - - int opt_len = sizeof(int); - - if (_server_socket == -1) { - return -1; - } - int maxbw = _srs_config->get_srto_maxbw(); - srt_setsockopt(_server_socket, 0, SRTO_MAXBW, &maxbw, opt_len); - int mss = _srs_config->get_srto_mss(); - srt_setsockopt(_server_socket, 0, SRTO_MSS, &mss, opt_len); - - bool tlpkdrop = _srs_config->get_srto_tlpkdrop(); - int tlpkdrop_i = tlpkdrop ? 1 : 0; - srt_setsockopt(_server_socket, 0, SRTO_TLPKTDROP, &tlpkdrop_i, opt_len); - - int connection_timeout = _srs_config->get_srto_conntimeout(); - srt_setsockopt(_server_socket, 0, SRTO_CONNTIMEO, &connection_timeout, opt_len); - - int send_buff = _srs_config->get_srto_sendbuf(); - srt_setsockopt(_server_socket, 0, SRTO_SNDBUF, &send_buff, opt_len); - int recv_buff = _srs_config->get_srto_recvbuf(); - srt_setsockopt(_server_socket, 0, SRTO_RCVBUF, &recv_buff, opt_len); - int payload_size = _srs_config->get_srto_payloadsize(); - srt_setsockopt(_server_socket, 0, SRTO_PAYLOADSIZE, &payload_size, opt_len); - - int latency = _srs_config->get_srto_latency(); - if (DEF_RECV_LATENCY != latency) { - srt_setsockopt(_server_socket, 0, SRTO_LATENCY, &latency, opt_len); - } - - int recv_latency = _srs_config->get_srto_recv_latency(); - if (DEF_RECV_LATENCY != recv_latency) { - srt_setsockopt(_server_socket, 0, SRTO_RCVLATENCY, &recv_latency, opt_len); - } - - int peer_latency = _srs_config->get_srto_peer_latency(); - if (DEF_PEER_LATENCY != peer_latency) { - srt_setsockopt(_server_socket, 0, SRTO_PEERLATENCY, &recv_latency, opt_len); - } - - srt_log_trace("init srt parameter, maxbw:%d, mss:%d, tlpkdrop:%d, connect timeout:%d, \ -send buff:%d, recv buff:%d, payload size:%d, latency:%d, recv latency:%d, peer latency:%d", - maxbw, mss, tlpkdrop, connection_timeout, send_buff, recv_buff, payload_size, - latency, recv_latency, peer_latency); - return 0; -} - -void srt_server::init_srt_log() { - SrsLogLevel level = srs_get_log_level(_srs_config->get_log_level()); - switch (level) { - case SrsLogLevelInfo: - { - set_srt_log_level(SRT_LOGGER_INFO_LEVEL); - break; - } - case SrsLogLevelTrace: - { - set_srt_log_level(SRT_LOGGER_TRACE_LEVEL); - break; - } - case SrsLogLevelWarn: - { - set_srt_log_level(SRT_LOGGER_WARN_LEVEL); - break; - } - case SrsLogLevelError: - { - set_srt_log_level(SRT_LOGGER_ERROR_LEVEL); - break; - } - default: - { - set_srt_log_level(SRT_LOGGER_TRACE_LEVEL); - } - } - return; -} - -int srt_server::init_srt() { - if (_server_socket != -1) { - return -1; - } - - init_srt_log(); - - _server_socket = srt_create_socket(); - sockaddr_in sa; - memset(&sa, 0, sizeof sa); - sa.sin_family = AF_INET; - sa.sin_port = htons(_listen_port); - - sockaddr* psa = (sockaddr*)&sa; - - int ret = srt_bind(_server_socket, psa, sizeof(sa)); - if ( ret == SRT_ERROR ) - { - srt_close(_server_socket); - srt_log_error("srt bind error: %d", ret); - return -1; - } - - ret = srt_listen(_server_socket, 5); - if (ret == SRT_ERROR) - { - srt_close(_server_socket); - srt_log_error("srt listen error: %d", ret); - return -2; - } - - init_srt_parameter(); - - _pollid = srt_epoll_create(); - if (_pollid < -1) { - srt_log_error("srt server srt_epoll_create error, port=%d", _listen_port); - return -1; - } - _handle_ptr = std::make_shared(_pollid); - - int events = SRT_EPOLL_IN | SRT_EPOLL_ERR; - ret = srt_epoll_add_usock(_pollid, _server_socket, &events); - if (ret < 0) { - srt_log_error("srt server run add epoll error:%d", ret); - return ret; - } - - srt_log_trace("srt server listen port=%d, server_fd=%d", _listen_port, _server_socket); - - return 0; -} - -int srt_server::start() -{ - int ret; - - if ((ret = init_srt()) < 0) { - return ret; - } - - run_flag = true; - srt_log_trace("srt server is starting... port(%d)", _listen_port); - thread_run_ptr = std::make_shared(&srt_server::on_work, this); - return 0; -} - -void srt_server::stop() -{ - run_flag = false; - if (!thread_run_ptr) { - return; - } - thread_run_ptr->join(); - - return; -} - -void srt_server::srt_handle_connection(SRT_SOCKSTATUS status, SRTSOCKET input_fd, const std::string& dscr) { - SRTSOCKET conn_fd = -1; - sockaddr_in scl; - int sclen = sizeof(scl); - int conn_event;// = SRT_EPOLL_IN |SRT_EPOLL_OUT| SRT_EPOLL_ERR; - - switch(status) { - case SRTS_LISTENING: - { - conn_fd = srt_accept(input_fd, (sockaddr*)&scl, &sclen); - if (conn_fd == -1) { - return; - } - //add new srt connect into srt handle - std::string streamid = UDT::getstreamid(conn_fd); - if (!is_streamid_valid(streamid)) { - srt_log_trace("srt streamid(%s) error, fd:%d", streamid.c_str(), conn_fd); - srt_close(conn_fd); - return; - } - SRT_CONN_PTR srt_conn_ptr = std::make_shared(conn_fd, streamid); - - std::string vhost_str = srt_conn_ptr->get_vhost(); - srt_log_trace("new srt connection streamid:%s, fd:%d, vhost:%s", - streamid.c_str(), conn_fd, vhost_str.c_str()); - SrsConfDirective* vhost_p = _srs_config->get_vhost(vhost_str, true); - if (!vhost_p) { - srt_log_trace("srt streamid(%s): no vhost %s, fd:%d", - streamid.c_str(), vhost_str.c_str(), conn_fd); - srt_conn_ptr->close(); - return; - } - if (srt_conn_ptr->get_mode() == PULL_SRT_MODE) { - //add SRT_EPOLL_IN for information notify - conn_event = SRT_EPOLL_IN | SRT_EPOLL_ERR;//not inlucde SRT_EPOLL_OUT for save cpu - } else if (srt_conn_ptr->get_mode() == PUSH_SRT_MODE) { - conn_event = SRT_EPOLL_IN | SRT_EPOLL_ERR; - } else { - srt_log_trace("stream mode error, it should be m=push or m=pull, streamid:%s", - srt_conn_ptr->get_streamid().c_str()); - srt_conn_ptr->close(); - return; - } - - _handle_ptr->add_newconn(srt_conn_ptr, conn_event); - break; - } - case SRTS_CONNECTED: - { - srt_log_trace("srt connected: socket=%d, mode:%s", input_fd, dscr.c_str()); - break; - } - case SRTS_BROKEN: - { - srt_epoll_remove_usock(_pollid, input_fd); - srt_close(input_fd); - srt_log_warn("srt close: socket=%d", input_fd); - break; - } - default: - { - srt_log_error("srt server unkown status:%d", status); - } - } -} - -void srt_server::srt_handle_data(SRT_SOCKSTATUS status, SRTSOCKET input_fd, const std::string& dscr) { - _handle_ptr->handle_srt_socket(status, input_fd); - return; -} - -void srt_server::on_work() -{ - const unsigned int SRT_FD_MAX = 100; - srt_log_trace("srt server is working port(%d)", _listen_port); - while (run_flag) - { - SRTSOCKET read_fds[SRT_FD_MAX]; - SRTSOCKET write_fds[SRT_FD_MAX]; - int rfd_num = SRT_FD_MAX; - int wfd_num = SRT_FD_MAX; - - int ret = srt_epoll_wait(_pollid, read_fds, &rfd_num, write_fds, &wfd_num, -1, - nullptr, nullptr, nullptr, nullptr); - if (ret < 0) { - continue; - } - _handle_ptr->check_alive(); - - for (int index = 0; index < rfd_num; index++) { - SRT_SOCKSTATUS status = srt_getsockstate(read_fds[index]); - if (_server_socket == read_fds[index]) { - srt_handle_connection(status, read_fds[index], "read fd"); - } else { - srt_handle_data(status, read_fds[index], "read fd"); - } - } - - for (int index = 0; index < wfd_num; index++) { - SRT_SOCKSTATUS status = srt_getsockstate(write_fds[index]); - if (_server_socket == write_fds[index]) { - srt_handle_connection(status, write_fds[index], "write fd"); - } else { - srt_handle_data(status, write_fds[index], "write fd"); - } - } - } - - // New API at 2020-01-28, >1.4.1 - // @see https://github.com/Haivision/srt/commit/b8c70ec801a56bea151ecce9c09c4ebb720c2f68#diff-fb66028e8746fea578788532533a296bR786 -#if (SRT_VERSION_MAJOR<<24 | SRT_VERSION_MINOR<<16 | SRT_VERSION_PATCH<<8) > 0x01040100 - srt_epoll_clear_usocks(_pollid); -#endif -} - -SrtServerAdapter::SrtServerAdapter() -{ -} - -SrtServerAdapter::~SrtServerAdapter() -{ -} - -srs_error_t SrtServerAdapter::initialize() -{ - srs_error_t err = srs_success; - - // TODO: FIXME: We could fork processes here, because here only ST is initialized. - - return err; -} - -srs_error_t SrtServerAdapter::run(SrsWaitGroup* wg) -{ - srs_error_t err = srs_success; - - // TODO: FIXME: We could start a coroutine to dispatch SRT task to processes. - - if(_srs_config->get_srt_enabled()) { - srt_log_trace("srt server is enabled..."); - unsigned short srt_port = _srs_config->get_srt_listen_port(); - srt_log_trace("srt server listen port:%d", srt_port); - err = srt2rtmp::get_instance()->init(); - if (err != srs_success) { - return srs_error_wrap(err, "srt start srt2rtmp error"); - } - - srt_ptr = std::make_shared(srt_port); - if (!srt_ptr) { - return srs_error_wrap(err, "srt listen %d", srt_port); - } - } else { - srt_log_trace("srt server is disabled..."); - } - - if(_srs_config->get_srt_enabled()) { - srt_ptr->start(); - } - - return err; -} - -void SrtServerAdapter::stop() -{ - // TODO: FIXME: If forked processes, we should do cleanup. -} diff --git a/trunk/src/srt/srt_server.hpp b/trunk/src/srt/srt_server.hpp deleted file mode 100644 index 6b86ee4c7a..0000000000 --- a/trunk/src/srt/srt_server.hpp +++ /dev/null @@ -1,67 +0,0 @@ -// -// Copyright (c) 2013-2021 The SRS Authors -// -// SPDX-License-Identifier: MIT or MulanPSL-2.0 -// - -#ifndef SRT_SERVER_H -#define SRT_SERVER_H - -#include - -#include - -#include -#include - -#include - -class srt_handle; -class SrsWaitGroup; - -class srt_server { -public: - srt_server(unsigned short port); - ~srt_server(); - - int start();//init srt handl and create srt main thread loop - void stop();//stop srt main thread loop - -private: - //init srt socket and srt epoll - int init_srt(); - int init_srt_parameter(); - void init_srt_log(); - - //srt main epoll loop - void on_work(); - //accept new srt connection - void srt_handle_connection(SRT_SOCKSTATUS status, SRTSOCKET input_fd, const std::string& dscr); - //get srt data read/write - void srt_handle_data(SRT_SOCKSTATUS status, SRTSOCKET input_fd, const std::string& dscr); - -private: - unsigned short _listen_port; - SRTSOCKET _server_socket; - int _pollid; - bool run_flag; - std::shared_ptr thread_run_ptr; - std::shared_ptr _handle_ptr; -}; - -typedef std::shared_ptr SRT_SERVER_PTR; - -class SrtServerAdapter : public ISrsHybridServer -{ -private: - SRT_SERVER_PTR srt_ptr; -public: - SrtServerAdapter(); - virtual ~SrtServerAdapter(); -public: - virtual srs_error_t initialize(); - virtual srs_error_t run(SrsWaitGroup* wg); - virtual void stop(); -}; - -#endif//SRT_SERVER_H diff --git a/trunk/src/srt/srt_to_rtmp.cpp b/trunk/src/srt/srt_to_rtmp.cpp deleted file mode 100644 index f367201a46..0000000000 --- a/trunk/src/srt/srt_to_rtmp.cpp +++ /dev/null @@ -1,779 +0,0 @@ -// -// Copyright (c) 2013-2021 The SRS Authors -// -// SPDX-License-Identifier: MIT or MulanPSL-2.0 -// - -#include "srt_to_rtmp.hpp" -#include "stringex.hpp" -#include "time_help.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -std::shared_ptr srt2rtmp::s_srt2rtmp_ptr; -std::mutex srt2rtmp::_srt_error_mutex; -std::map srt2rtmp::_srt_error_map; - -std::shared_ptr srt2rtmp::get_instance() { - if (!s_srt2rtmp_ptr) { - s_srt2rtmp_ptr = std::make_shared(); - } - return s_srt2rtmp_ptr; -} - -srt2rtmp::srt2rtmp():_lastcheck_ts(0) { - -} - -srt2rtmp::~srt2rtmp() { - release(); -} - -srs_error_t srt2rtmp::init() { - srs_error_t err = srs_success; - - if (_trd_ptr.get() != nullptr) { - return srs_error_wrap(err, "don't start thread again"); - } - - _trd_ptr = std::make_shared("srt2rtmp", this); - - if ((err = _trd_ptr->start()) != srs_success) { - return srs_error_wrap(err, "start thread"); - } - - srs_trace("srt2rtmp start coroutine..."); - - return err; -} - -void srt2rtmp::release() { - if (!_trd_ptr) { - return; - } - _trd_ptr->stop(); - _trd_ptr = nullptr; -} - -void srt2rtmp::insert_data_message(unsigned char* data_p, unsigned int len, const std::string& key_path) { - std::unique_lock locker(_mutex); - - SRT_DATA_MSG_PTR msg_ptr = std::make_shared(data_p, len, key_path); - _msg_queue.push(msg_ptr); - //_notify_cond.notify_one(); - return; -} - -void srt2rtmp::insert_ctrl_message(unsigned int msg_type, const std::string& key_path) { - std::unique_lock locker(_mutex); - - SRT_DATA_MSG_PTR msg_ptr = std::make_shared(key_path, msg_type); - _msg_queue.push(msg_ptr); - //_notify_cond.notify_one(); - return; -} - -void srt2rtmp::insert_log_message(LOGGER_LEVEL level, const std::string& log_content) { - std::unique_lock locker(_mutex); - - SRT_DATA_MSG_PTR msg_ptr = std::make_shared(level, log_content); - msg_ptr->set_msg_type(SRT_MSG_LOG_TYPE); - _msg_queue.push(msg_ptr); - - return; -} - -SRT_DATA_MSG_PTR srt2rtmp::get_data_message() { - std::unique_lock locker(_mutex); - SRT_DATA_MSG_PTR msg_ptr; - - if (_msg_queue.empty()) - { - return msg_ptr; - } - //while (_msg_queue.empty()) { - // _notify_cond.wait(locker); - //} - - msg_ptr = _msg_queue.front(); - _msg_queue.pop(); - return msg_ptr; -} - -void srt2rtmp::check_rtmp_alive() { - const int64_t CHECK_INTERVAL = 5*1000; - const int64_t ALIVE_TIMEOUT_MAX = 5*1000; - - if (_lastcheck_ts == 0) { - _lastcheck_ts = now_ms(); - return; - } - int64_t timenow_ms = now_ms(); - - if ((timenow_ms - _lastcheck_ts) > CHECK_INTERVAL) { - _lastcheck_ts = timenow_ms; - - for (auto iter = _rtmp_client_map.begin(); - iter != _rtmp_client_map.end();) { - RTMP_CLIENT_PTR rtmp_ptr = iter->second; - - if ((timenow_ms - rtmp_ptr->get_last_live_ts()) >= ALIVE_TIMEOUT_MAX) { - srs_warn("srt2rtmp client is timeout, url:%s", - rtmp_ptr->get_url().c_str()); - _rtmp_client_map.erase(iter++); - rtmp_ptr->close(); - } else { - iter++; - } - } - } - return; -} - -void srt2rtmp::handle_close_rtmpsession(const std::string& key_path) { - RTMP_CLIENT_PTR rtmp_ptr; - auto iter = _rtmp_client_map.find(key_path); - if (iter == _rtmp_client_map.end()) { - srs_error("fail to close rtmp session fail, can't find session by key_path:%s", - key_path.c_str()); - return; - } - rtmp_ptr = iter->second; - _rtmp_client_map.erase(iter); - srs_trace("close rtmp session which key_path is %s", key_path.c_str()); - rtmp_ptr->close(); - - return; -} - -//the cycle is running in srs coroutine -srs_error_t srt2rtmp::cycle() { - srs_error_t err = srs_success; - _lastcheck_ts = 0; - int err_code = -1; - - while(true) { - SRT_DATA_MSG_PTR msg_ptr = get_data_message(); - - if (!msg_ptr) { - srs_usleep((30 * SRS_UTIME_MILLISECONDS)); - } else { - switch (msg_ptr->msg_type()) { - case SRT_MSG_DATA_TYPE: - { - err_code = handle_ts_data(msg_ptr); - if (err_code != ERROR_SUCCESS) { - std::unique_lock locker(_srt_error_mutex); - _srt_error_map[msg_ptr->get_path()] = err_code; - } - break; - } - case SRT_MSG_CLOSE_TYPE: - { - handle_close_rtmpsession(msg_ptr->get_path()); - break; - } - case SRT_MSG_LOG_TYPE: - { - handle_log_data(msg_ptr); - break; - } - default: - { - srs_error("srt to rtmp get wrong message type(%u), path:%s", - msg_ptr->msg_type(), msg_ptr->get_path().c_str()); - assert(0); - } - } - - } - check_rtmp_alive(); - if ((err = _trd_ptr->pull()) != srs_success) { - return srs_error_wrap(err, "forwarder"); - } - } -} - -int srt2rtmp::handle_ts_data(SRT_DATA_MSG_PTR data_ptr) { - RTMP_CLIENT_PTR rtmp_ptr; - auto iter = _rtmp_client_map.find(data_ptr->get_path()); - if (iter == _rtmp_client_map.end()) { - srs_trace("new rtmp client for srt upstream, key_path:%s", data_ptr->get_path().c_str()); - rtmp_ptr = std::make_shared(data_ptr->get_path()); - _rtmp_client_map.insert(std::make_pair(data_ptr->get_path(), rtmp_ptr)); - } else { - rtmp_ptr = iter->second; - } - - return rtmp_ptr->receive_ts_data(data_ptr); -} - -void srt2rtmp::handle_log_data(SRT_DATA_MSG_PTR data_ptr) { - switch (data_ptr->get_log_level()) { - case SRT_LOGGER_INFO_LEVEL: - { - srs_info(data_ptr->get_log_string()); - break; - } - case SRT_LOGGER_TRACE_LEVEL: - { - srs_trace(data_ptr->get_log_string()); - break; - } - case SRT_LOGGER_WARN_LEVEL: - { - srs_warn(data_ptr->get_log_string()); - break; - } - case SRT_LOGGER_ERROR_LEVEL: - { - srs_error(data_ptr->get_log_string()); - break; - } - default: - { - srs_trace(data_ptr->get_log_string()); - } - } - return; -} - -rtmp_client::rtmp_client(std::string key_path):_key_path(key_path) - , _connect_flag(false) { - const std::string DEF_VHOST = "DEFAULT_VHOST"; - _ts_demux_ptr = std::make_shared(); - _avc_ptr = std::make_shared(); - _aac_ptr = std::make_shared(); - std::vector ret_vec; - - string_split(key_path, "/", ret_vec); - - if (ret_vec.size() >= 3) { - _vhost = ret_vec[0]; - _appname = ret_vec[1]; - _streamname = ret_vec[2]; - } else { - _vhost = DEF_VHOST; - _appname = ret_vec[0]; - _streamname = ret_vec[1]; - } - std::stringstream url_ss; - - std::vector ip_ports = _srs_config->get_listens(); - int port = 0; - std::string ip; - - for (auto item : ip_ports) { - srs_parse_endpoint(item, ip, port); - if (port != 0) { - break; - } - } - - port = (port == 0) ? SRS_CONSTS_RTMP_DEFAULT_PORT : port; - - std::stringstream ss; - ss << "rtmp://" << SRS_CONSTS_LOCALHOST; - ss << ":" << port; - ss << "/" << _appname; - ss << "/" << _streamname; - ss << (_streamname.find("?") != std::string::npos ? "&" : "?") << "upstream=srt"; - if (_vhost != DEF_VHOST) { - ss << "&vhost=" << _vhost; - } - - _url = ss.str(); - - _h264_sps_changed = false; - _h264_pps_changed = false; - _h264_sps_pps_sent = false; - - _last_live_ts = now_ms(); - srs_trace("rtmp client construct url:%s", url_ss.str().c_str()); -} - -rtmp_client::~rtmp_client() { - -} - -void rtmp_client::close() { - _connect_flag = false; - if (!_rtmp_conn_ptr) { - return; - } - srs_trace("rtmp client close url:%s", _url.c_str()); - _rtmp_conn_ptr->close(); - _rtmp_conn_ptr = nullptr; - -} - -int64_t rtmp_client::get_last_live_ts() { - return _last_live_ts; -} - -std::string rtmp_client::get_url() { - return _url; -} - -srs_error_t rtmp_client::connect() { - srs_error_t err = srs_success; - srs_utime_t cto = SRS_CONSTS_RTMP_TIMEOUT; - srs_utime_t sto = SRS_CONSTS_RTMP_PULSE; - - _last_live_ts = now_ms(); - if (_connect_flag) { - return srs_success; - } - - if (_rtmp_conn_ptr.get() != nullptr) { - return srs_error_wrap(err, "repeated connect %s failed, cto=%dms, sto=%dms.", - _url.c_str(), srsu2msi(cto), srsu2msi(sto)); - } - - _rtmp_conn_ptr = std::make_shared(_url, cto, sto); - - if ((err = _rtmp_conn_ptr->connect()) != srs_success) { - close(); - return srs_error_wrap(err, "connect %s failed, cto=%dms, sto=%dms.", - _url.c_str(), srsu2msi(cto), srsu2msi(sto)); - } - - if ((err = _rtmp_conn_ptr->publish(SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE)) != srs_success) { - close(); - return srs_error_wrap(err, "rtmp client in srt2rtmp publish fail url:%s", _url.c_str()); - } - _connect_flag = true; - return err; -} - -int rtmp_client::receive_ts_data(SRT_DATA_MSG_PTR data_ptr) { - return _ts_demux_ptr->decode(data_ptr, shared_from_this());//on_data_callback is the decode callback -} - -srs_error_t rtmp_client::write_h264_sps_pps(uint32_t dts, uint32_t pts) { - srs_error_t err = srs_success; - - // TODO: FIMXE: there exists bug, see following comments. - // 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_changed || !_h264_pps_changed) { - return err; - } - - // h264 raw to h264 packet. - std::string sh; - if ((err = _avc_ptr->mux_sequence_header(_h264_sps, _h264_pps, dts, pts, sh)) != srs_success) { - return srs_error_wrap(err, "mux sequence header"); - } - - // 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_ptr->mux_avc2flv(sh, frame_type, avc_packet_type, dts, pts, &flv, &nb_flv)) != srs_success) { - return srs_error_wrap(err, "avc to flv"); - } - - if (_srs_config->get_srt_mix_correct()) { - _rtmp_queue.insert_rtmp_data((unsigned char*)flv, nb_flv, (int64_t)dts, SrsFrameTypeVideo); - err = rtmp_write_work(); - } else { - err = rtmp_write_packet(SrsFrameTypeVideo, dts, flv, nb_flv); - } - - // reset sps and pps. - _h264_sps_changed = false; - _h264_pps_changed = false; - _h264_sps_pps_sent = true; - - return err; -} - -srs_error_t rtmp_client::write_h264_ipb_frame(char* frame, int frame_size, uint32_t dts, uint32_t pts) { - srs_error_t err = srs_success; - - // when sps or pps not sent, ignore the packet. - // @see https://github.com/ossrs/srs/issues/203 - if (!_h264_sps_pps_sent) { - return srs_error_new(ERROR_H264_DROP_BEFORE_SPS_PPS, "drop sps/pps"); - } - - // 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. - SrsVideoAvcFrameType frame_type = SrsVideoAvcFrameTypeInterFrame; - if (nal_unit_type == SrsAvcNaluTypeIDR) { - frame_type = SrsVideoAvcFrameTypeKeyFrame; - } - - std::string ibp; - if ((err = _avc_ptr->mux_ipb_frame(frame, frame_size, ibp)) != srs_success) { - return srs_error_wrap(err, "mux frame"); - } - - int8_t avc_packet_type = SrsVideoAvcFrameTraitNALU; - char* flv = NULL; - int nb_flv = 0; - if ((err = _avc_ptr->mux_avc2flv(ibp, frame_type, avc_packet_type, dts, pts, &flv, &nb_flv)) != srs_success) { - return srs_error_wrap(err, "mux avc to flv"); - } - if (_srs_config->get_srt_mix_correct()) { - _rtmp_queue.insert_rtmp_data((unsigned char*)flv, nb_flv, (int64_t)dts, SrsFrameTypeVideo); - err = rtmp_write_work(); - } else { - err = rtmp_write_packet(SrsFrameTypeVideo, dts, flv, nb_flv); - } - - return err; -} - -srs_error_t rtmp_client::write_audio_raw_frame(char* frame, int frame_size, SrsRawAacStreamCodec* codec, uint32_t dts) { - srs_error_t err = srs_success; - - char* data = NULL; - int size = 0; - if ((err = _aac_ptr->mux_aac2flv(frame, frame_size, codec, dts, &data, &size)) != srs_success) { - return srs_error_wrap(err, "mux aac to flv"); - } - if (_srs_config->get_srt_mix_correct()) { - _rtmp_queue.insert_rtmp_data((unsigned char*)data, size, (int64_t)dts, SrsFrameTypeAudio); - err = rtmp_write_work(); - } else { - err = rtmp_write_packet(SrsFrameTypeAudio, dts, data, size); - } - - return err; -} - -srs_error_t rtmp_client::rtmp_write_packet(char type, uint32_t timestamp, char* data, int size) { - srs_error_t err = srs_success; - SrsSharedPtrMessage* msg = NULL; - - if (!_rtmp_conn_ptr) { - //when rtmp connection is closed, it's not error and just return; - srs_freepa(data); - return err; - } - - if ((err = srs_rtmp_create_msg(type, timestamp, data, size, _rtmp_conn_ptr->sid(), &msg)) != srs_success) { - return srs_error_wrap(err, "create message fail, url:%s", _url.c_str()); - } - srs_assert(msg); - - // send out encoded msg. - if ((err = _rtmp_conn_ptr->send_and_free_message(msg)) != srs_success) { - close(); - return srs_error_wrap(err, "rtmp client in srt2rtmp send message fail, url:%s", _url.c_str()); - } - - return err; -} - -srs_error_t rtmp_client::rtmp_write_work() { - srs_error_t err = srs_success; - rtmp_packet_info_s packet_info; - bool ret = false; - - do { - ret = _rtmp_queue.get_rtmp_data(packet_info); - if (ret) { - err = rtmp_write_packet(packet_info._type, packet_info._dts, (char*)packet_info._data, packet_info._len); - if (err != srs_success) { - break; - } - } - } while(ret); - - return err; -} - -srs_error_t rtmp_client::on_ts_video(std::shared_ptr avs_ptr, uint64_t dts, uint64_t pts) { - srs_error_t err = srs_success; - - // ensure rtmp connected. - if ((err = connect()) != srs_success) { - return err; - } - dts = dts / 90; - pts = pts / 90; - - if (dts == 0) { - dts = pts; - } - - // send each frame. - while (!avs_ptr->empty()) { - char* frame = NULL; - int frame_size = 0; - if ((err = _avc_ptr->annexb_demux(avs_ptr.get(), &frame, &frame_size)) != srs_success) { - return srs_error_wrap(err, "demux annexb"); - } - - // 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); - - // ignore the nalu type aud(9), pad(12) - if ((nal_unit_type == SrsAvcNaluTypeAccessUnitDelimiter) - || (nal_unit_type == SrsAvcNaluTypeFilterData)) { - continue; - } - - // TODO: FIXME: Should cache this config, it's better not to get it for each video frame. - if (_srs_config->get_srt_sei_filter()) { - if (nal_unit_type == SrsAvcNaluTypeSEI) { - continue; - } - } - - // for sps - if (_avc_ptr->is_sps(frame, frame_size)) { - std::string sps; - if ((err = _avc_ptr->sps_demux(frame, frame_size, sps)) != srs_success) { - return srs_error_wrap(err, "demux sps"); - } - - if (_h264_sps == sps) { - continue; - } - _h264_sps_changed = true; - _h264_sps = sps; - - if ((err = write_h264_sps_pps(dts, pts)) != srs_success) { - return srs_error_wrap(err, "write sps/pps"); - } - continue; - } - - // for pps - if (_avc_ptr->is_pps(frame, frame_size)) { - std::string pps; - if ((err = _avc_ptr->pps_demux(frame, frame_size, pps)) != srs_success) { - return srs_error_wrap(err, "demux pps"); - } - - if (_h264_pps == pps) { - continue; - } - _h264_pps_changed = true; - _h264_pps = pps; - - if ((err = write_h264_sps_pps(dts, pts)) != srs_success) { - return srs_error_wrap(err, "write sps/pps"); - } - continue; - } - - // ibp frame. - // for Issue: https://github.com/ossrs/srs/issues/2390 - // we only skip pps/sps frame and send left nalus. - srs_info("mpegts: demux avc ibp frame size=%d, dts=%d", avs_ptr->left() + frame_size, dts); - if ((err = write_h264_ipb_frame(avs_ptr->head() - frame_size, avs_ptr->left() + frame_size, dts, pts)) != srs_success) { - return srs_error_wrap(err, "write frame"); - } - _last_live_ts = now_ms(); - break; - } - - return err; -} - -int rtmp_client::get_sample_rate(char sample_index) { - int sample_rate = 44100; - - if ((sample_index >= 0) && (sample_index < SrsAAcSampleRateNumbers)) { - sample_rate = srs_aac_srates[(uint8_t)sample_index]; - } - - return sample_rate; -} - -srs_error_t rtmp_client::on_ts_audio(std::shared_ptr avs_ptr, uint64_t dts, uint64_t pts) { - srs_error_t err = srs_success; - uint64_t base_dts; - uint64_t real_dts; - uint64_t first_dts; - int index = 0; - int sample_size = 1024; - - // ensure rtmp connected. - if ((err = connect()) != srs_success) { - return srs_error_wrap(err, "connect"); - } - - base_dts = dts/90; - if (base_dts == 0) { - base_dts = pts/90; - } - - // send each frame. - while (!avs_ptr->empty()) { - char* frame = NULL; - int frame_size = 0; - SrsRawAacStreamCodec codec; - if ((err = _aac_ptr->adts_demux(avs_ptr.get(), &frame, &frame_size, codec)) != srs_success) { - return srs_error_wrap(err, "demux adts"); - } - - if (frame_size <= 0) { - continue; - } - int sample_rate = get_sample_rate(codec.sound_rate); - - if (codec.aac_packet_type > SrsAudioOpusFrameTraitRaw) { - sample_size = 2048; - } else { - sample_size = 1024; - } - - real_dts = base_dts + index * 1000.0 * sample_size / sample_rate; - if (index == 0) { - first_dts = real_dts; - } - index++; - - // generate sh. - if (_aac_specific_config.empty()) { - std::string sh; - if ((err = _aac_ptr->mux_sequence_header(&codec, sh)) != srs_success) { - return srs_error_wrap(err, "mux sequence header"); - } - _aac_specific_config = sh; - - codec.aac_packet_type = 0; - - if ((err = write_audio_raw_frame((char*)sh.data(), (int)sh.length(), &codec, real_dts)) != srs_success) { - return srs_error_wrap(err, "write raw audio frame"); - } - } - - // audio raw data. - codec.aac_packet_type = 1; - if ((err = write_audio_raw_frame(frame, frame_size, &codec, real_dts)) != srs_success) { - return srs_error_wrap(err, "write audio raw frame"); - } - _last_live_ts = now_ms(); - } - - uint64_t diff_t = real_dts - first_dts; - diff_t += 100; - if ((diff_t > 200) && (diff_t < 600)) { - srs_info("set_queue_timeout timeout:%lu", diff_t); - _rtmp_queue.set_queue_timeout(diff_t); - } - return err; -} - -int rtmp_client::on_data_callback(SRT_DATA_MSG_PTR data_ptr, unsigned int media_type, - uint64_t dts, uint64_t pts) -{ - srs_error_t err = srs_success; - if (!data_ptr || (data_ptr->get_data() == nullptr) || (data_ptr->data_len() == 0)) { - assert(0); - return 0; - } - - auto avs_ptr = std::make_shared((char*)data_ptr->get_data(), data_ptr->data_len()); - - if (media_type == STREAM_TYPE_VIDEO_H264) { - err = on_ts_video(avs_ptr, dts, pts); - } else if (media_type == STREAM_TYPE_AUDIO_AAC) { - err = on_ts_audio(avs_ptr, dts, pts); - } else { - srs_error("mpegts demux unkown stream type:0x%02x, only support h264+aac", media_type); - return 0; - } - - if (err != srs_success) { - srs_error("send media data error:%s", srs_error_desc(err).c_str()); - int err_code = srs_error_code(err); - srs_freep(err); - return err_code; - } - return 0; -} - -rtmp_packet_queue::rtmp_packet_queue():_queue_timeout(QUEUE_DEF_TIMEOUT) - ,_queue_maxlen(QUEUE_LEN_MAX) - ,_first_packet_t(-1) - ,_first_local_t(-1) { - -} - -rtmp_packet_queue::~rtmp_packet_queue() { - for (auto item : _send_map) { - rtmp_packet_info_s info = item.second; - if (info._data) { - delete info._data; - } - } - _send_map.clear(); -} - -void rtmp_packet_queue::set_queue_timeout(int64_t queue_timeout) { - _queue_timeout = queue_timeout; -} - -void rtmp_packet_queue::insert_rtmp_data(unsigned char* data, int len, int64_t dts, char media_type) { - rtmp_packet_info_s packet_info; - - packet_info._data = data; - packet_info._len = len; - packet_info._dts = dts; - packet_info._type = media_type; - - if (_first_packet_t == -1) { - _first_packet_t = dts; - _first_local_t = (int64_t)now_ms(); - } - - _send_map.insert(std::make_pair(dts, packet_info)); - return; -} - -bool rtmp_packet_queue::is_ready() { - if (!_srs_config->get_srt_mix_correct() && !_send_map.empty()) { - return true; - } - if (_send_map.size() < 2) { - return false; - } - - if (_send_map.size() >= (size_t)_queue_maxlen) { - return true; - } - - auto first_item = _send_map.begin(); - int64_t now_t = (int64_t)now_ms(); - - int64_t diff_t = (now_t - _first_local_t) - (first_item->first - _first_packet_t); - - if (diff_t >= _queue_timeout) { - return true; - } - return false; -} - -bool rtmp_packet_queue::get_rtmp_data(rtmp_packet_info_s& packet_info) { - if (!is_ready()) { - return false; - } - auto iter = _send_map.begin(); - packet_info = iter->second; - _send_map.erase(iter); - - return true; -} diff --git a/trunk/src/srt/srt_to_rtmp.hpp b/trunk/src/srt/srt_to_rtmp.hpp deleted file mode 100644 index cd54ff63c7..0000000000 --- a/trunk/src/srt/srt_to_rtmp.hpp +++ /dev/null @@ -1,162 +0,0 @@ -// -// Copyright (c) 2013-2021 The SRS Authors -// -// SPDX-License-Identifier: MIT or MulanPSL-2.0 -// - -#ifndef SRT_TO_RTMP_H -#define SRT_TO_RTMP_H - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "srt_data.hpp" -#include "ts_demux.hpp" -#include "srt_log.hpp" - -#define SRT_VIDEO_MSG_TYPE 0x01 -#define SRT_AUDIO_MSG_TYPE 0x02 - -typedef std::shared_ptr RTMP_CONN_PTR; -typedef std::shared_ptr AVC_PTR; -typedef std::shared_ptr AAC_PTR; - -#define DEFAULT_VHOST "__default_host__" - -#define QUEUE_DEF_TIMEOUT 500 -#define QUEUE_LEN_MAX 100 - -typedef struct { - unsigned char* _data; - int _len; - int64_t _dts; - char _type; - char reserve[3]; -} rtmp_packet_info_s; - -class rtmp_packet_queue { -public: - rtmp_packet_queue(); - ~rtmp_packet_queue(); - - void set_queue_timeout(int64_t queue_timeout); - void insert_rtmp_data(unsigned char* data, int len, int64_t dts, char media_type); - bool get_rtmp_data(rtmp_packet_info_s& packet_info); - -private: - bool is_ready(); - -private: - int64_t _queue_timeout; - int64_t _queue_maxlen; - int64_t _first_packet_t; - int64_t _first_local_t; - std::multimap _send_map;//key:dts, value:rtmp_packet_info -}; - -class rtmp_client : public ts_media_data_callback_I, public std::enable_shared_from_this { -public: - rtmp_client(std::string key_path); - virtual ~rtmp_client(); - - int receive_ts_data(SRT_DATA_MSG_PTR data_ptr); - int64_t get_last_live_ts(); - std::string get_url(); - - srs_error_t connect(); - void close(); - -private: - virtual int on_data_callback(SRT_DATA_MSG_PTR data_ptr, unsigned int media_type, uint64_t dts, uint64_t pts); - -private: - srs_error_t on_ts_video(std::shared_ptr avs_ptr, uint64_t dts, uint64_t pts); - srs_error_t on_ts_audio(std::shared_ptr avs_ptr, uint64_t dts, uint64_t pts); - virtual srs_error_t write_h264_sps_pps(uint32_t dts, uint32_t pts); - virtual srs_error_t write_h264_ipb_frame(char* frame, int frame_size, uint32_t dts, uint32_t pts); - virtual srs_error_t write_audio_raw_frame(char* frame, int frame_size, SrsRawAacStreamCodec* codec, uint32_t dts); - - int get_sample_rate(char sound_rate); - - srs_error_t rtmp_write_work(); - -private: - virtual srs_error_t rtmp_write_packet(char type, uint32_t timestamp, char* data, int size); - -private: - std::string _key_path; - std::string _url; - std::string _vhost; - std::string _appname; - std::string _streamname; - TS_DEMUX_PTR _ts_demux_ptr; - -private: - AVC_PTR _avc_ptr; - std::string _h264_sps; - bool _h264_sps_changed; - std::string _h264_pps; - bool _h264_pps_changed; - bool _h264_sps_pps_sent; -private: - std::string _aac_specific_config; - AAC_PTR _aac_ptr; -private: - RTMP_CONN_PTR _rtmp_conn_ptr; - bool _connect_flag; - int64_t _last_live_ts; - -private: - rtmp_packet_queue _rtmp_queue; -}; - -typedef std::shared_ptr RTMP_CLIENT_PTR; - -class srt2rtmp : public ISrsCoroutineHandler { -public: - static std::shared_ptr get_instance(); - srt2rtmp(); - virtual ~srt2rtmp(); - - srs_error_t init(); - void release(); - - void insert_data_message(unsigned char* data_p, unsigned int len, const std::string& key_path); - void insert_ctrl_message(unsigned int msg_type, const std::string& key_path); - void insert_log_message(LOGGER_LEVEL level, const std::string& log_content); - -private: - SRT_DATA_MSG_PTR get_data_message(); - virtual srs_error_t cycle(); - int handle_ts_data(SRT_DATA_MSG_PTR data_ptr); - void handle_close_rtmpsession(const std::string& key_path); - void handle_log_data(SRT_DATA_MSG_PTR data_ptr); - void check_rtmp_alive(); - -private: - static std::shared_ptr s_srt2rtmp_ptr; - std::shared_ptr _trd_ptr; - std::mutex _mutex; - //std::condition_variable_any _notify_cond; - std::queue _msg_queue; - - std::unordered_map _rtmp_client_map; - int64_t _lastcheck_ts; -public: - static std::mutex _srt_error_mutex; - static std::map _srt_error_map; -}; - -#endif diff --git a/trunk/src/srt/stringex.hpp b/trunk/src/srt/stringex.hpp deleted file mode 100644 index 633e58ecf2..0000000000 --- a/trunk/src/srt/stringex.hpp +++ /dev/null @@ -1,48 +0,0 @@ -// -// Copyright (c) 2013-2021 The SRS Authors -// -// SPDX-License-Identifier: MIT or MulanPSL-2.0 -// - -#ifndef STRING_EX_H -#define STRING_EX_H - -#include - -#include -#include -#include -#include -#include -#include - -inline int string_split(const std::string& input_str, const std::string& split_str, std::vector& output_vec) { - if (input_str.length() == 0) { - return 0; - } - - std::string tempString(input_str); - do { - - size_t pos = tempString.find(split_str); - if (pos == tempString.npos) { - output_vec.push_back(tempString); - break; - } - std::string seg_str = tempString.substr(0, pos); - tempString = tempString.substr(pos+split_str.size()); - output_vec.push_back(seg_str); - } while(tempString.size() > 0); - - return output_vec.size(); -} - -inline std::string string_lower(const std::string input_str) { - std::string output_str(input_str); - - std::transform(input_str.begin(), input_str.end(), output_str.begin(), ::tolower); - - return output_str; -} - -#endif//STRING_EX_H diff --git a/trunk/src/srt/time_help.hpp b/trunk/src/srt/time_help.hpp deleted file mode 100644 index e58e5c0fd5..0000000000 --- a/trunk/src/srt/time_help.hpp +++ /dev/null @@ -1,19 +0,0 @@ -// -// Copyright (c) 2013-2021 The SRS Authors -// -// SPDX-License-Identifier: MIT or MulanPSL-2.0 -// - -#ifndef TIME_HELP_H -#define TIME_HELP_H - -#include - -#include - -inline long long now_ms() { - return std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()).count(); -} - -#endif //TIME_HELP_H \ No newline at end of file diff --git a/trunk/src/srt/ts_demux.cpp b/trunk/src/srt/ts_demux.cpp deleted file mode 100644 index 6b919014e3..0000000000 --- a/trunk/src/srt/ts_demux.cpp +++ /dev/null @@ -1,603 +0,0 @@ -// -// Copyright (c) 2013-2021 The SRS Authors -// -// SPDX-License-Identifier: MIT or MulanPSL-2.0 -// - -#include "ts_demux.hpp" -#include "srt_log.hpp" -#include - -ts_demux::ts_demux():_data_total(0) - ,_last_pid(0) - ,_last_dts(0) - ,_last_pts(0) -{ - -} - -ts_demux::~ts_demux() { - -} - -int ts_demux::decode_unit(unsigned char* data_p, std::string key_path, TS_DATA_CALLBACK_PTR callback) -{ - int pos = 0; - int npos = 0; - ts_header ts_header_info; - - ts_header_info._sync_byte = data_p[pos]; - pos++; - - ts_header_info._transport_error_indicator = (data_p[pos]&0x80)>>7; - ts_header_info._payload_unit_start_indicator = (data_p[pos]&0x40)>>6; - ts_header_info._transport_priority = (data_p[pos]&0x20)>>5; - ts_header_info._PID = ((data_p[pos]<<8)|data_p[pos+1])&0x1FFF; - pos += 2; - - ts_header_info._transport_scrambling_control = (data_p[pos]&0xC0)>>6; - ts_header_info._adaptation_field_control = (data_p[pos]&0x30)>>4; - ts_header_info._continuity_counter = (data_p[pos]&0x0F); - pos++; - npos = pos; - - adaptation_field* field_p = &(ts_header_info._adaptation_field_info); - // adaptation field - // 0x01 No adaptation_field, payload only - // 0x02 Adaptation_field only, no payload - // 0x03 Adaptation_field followed by payload - if( ts_header_info._adaptation_field_control == 2 - || ts_header_info._adaptation_field_control == 3 ){ - // adaptation_field() - field_p->_adaptation_field_length = data_p[pos]; - pos++; - - if( field_p->_adaptation_field_length > 0 ){ - field_p->_discontinuity_indicator = (data_p[pos]&0x80)>>7; - field_p->_random_access_indicator = (data_p[pos]&0x40)>>6; - field_p->_elementary_stream_priority_indicator = (data_p[pos]&0x20)>>5; - field_p->_PCR_flag = (data_p[pos]&0x10)>>4; - field_p->_OPCR_flag = (data_p[pos]&0x08)>>3; - field_p->_splicing_point_flag = (data_p[pos]&0x04)>>2; - field_p->_transport_private_data_flag = (data_p[pos]&0x02)>>1; - field_p->_adaptation_field_extension_flag = (data_p[pos]&0x01); - pos++; - - if( field_p->_PCR_flag == 1 ) { // PCR info - //program_clock_reference_base 33 uimsbf - //reserved 6 bslbf - //program_clock_reference_extension 9 uimsbf - pos += 6; - } - if( field_p->_OPCR_flag == 1 ) { - //original_program_clock_reference_base 33 uimsbf - //reserved 6 bslbf - //original_program_clock_reference_extension 9 uimsbf - pos += 6; - } - if( field_p->_splicing_point_flag == 1 ) { - //splice_countdown 8 tcimsbf - pos++; - } - if( field_p->_transport_private_data_flag == 1 ) { - //transport_private_data_length 8 uimsbf - field_p->_transport_private_data_length = data_p[pos]; - pos++; - memcpy(field_p->_private_data_byte, data_p + pos, field_p->_transport_private_data_length); - pos += field_p->_transport_private_data_length; - } - if( field_p->_adaptation_field_extension_flag == 1 ) { - //adaptation_field_extension_length 8 uimsbf - field_p->_adaptation_field_extension_length = data_p[pos]; - pos++; - //ltw_flag 1 bslbf - field_p->_ltw_flag = (data_p[pos]&0x80)>>7; - //piecewise_rate_flag 1 bslbf - field_p->_piecewise_rate_flag = (data_p[pos]&0x40)>>6; - //seamless_splice_flag 1 bslbf - field_p->_seamless_splice_flag = (data_p[pos]&0x20)>>5; - //reserved 5 bslbf - pos++; - if (field_p->_ltw_flag == 1) { - //ltw_valid_flag 1 bslbf - //ltw_offset 15 uimsbf - pos += 2; - } - if (field_p->_piecewise_rate_flag == 1) { - //reserved 2 bslbf - //piecewise_rate 22 uimsbf - pos += 3; - } - if (field_p->_seamless_splice_flag == 1) { - //splice_type 4 bslbf - //DTS_next_AU[32..30] 3 bslbf - //marker_bit 1 bslbf - //DTS_next_AU[29..15] 15 bslbf - //marker_bit 1 bslbf - //DTS_next_AU[14..0] 15 bslbf - //marker_bit 1 bslbf - pos += 5; - } - } - } - npos += sizeof(field_p->_adaptation_field_length) + field_p->_adaptation_field_length; - pos = npos;//must consider the 'stuffing_byte' in adaptation field - } - - if(ts_header_info._adaptation_field_control == 1 - || ts_header_info._adaptation_field_control == 3 ) { - // data_byte with placeholder - // payload parser - if(ts_header_info._PID == 0x00){ - // PAT // program association table - if(ts_header_info._payload_unit_start_indicator) { - pos++; - } - _pat._table_id = data_p[pos]; - pos++; - _pat._section_syntax_indicator = (data_p[pos]>>7)&0x01; - // skip 3 bits of 1 zero and 2 reserved - _pat._section_length = ((data_p[pos]<<8)|data_p[pos+1])&0x0FFF; - pos += 2; - _pat._transport_stream_id = (data_p[pos]<<8)|data_p[pos+1]; - pos += 2; - // reserved 2 bits - _pat._version_number = (data_p[pos]&0x3E)>>1; - _pat._current_next_indicator = data_p[pos]&0x01; - pos++; - _pat._section_number = data_p[pos]; - pos++; - _pat._last_section_number = data_p[pos]; - - if (_pat._table_id != 0x00) { - srt_log_error("pat table id(0x%02x) error, it must be 0x00", _pat._table_id); - return -1; - } - // PAT = section_length + 3 - if((188 - npos) <= (_pat._section_length + 3)) { - srt_log_error("pat _section_length(%d) error, the left len:%d", _pat._section_length, (188 - npos)); - return -1; - } - pos++; - _pat._pid_vec.clear(); - for (;pos+4 <= _pat._section_length-5-4+9 + npos;) { // 4:CRC, 5:follow section_length item rpos + 4(following unit length) section_length + 9(above field and unit_start_first_byte ) - PID_INFO pid_info; - //program_number 16 uimsbf - pid_info._program_number = data_p[pos]<<8|data_p[pos+1]; - pos += 2; -// reserved 3 bslbf - - if (pid_info._program_number == 0) { -// // network_PID 13 uimsbf - pid_info._network_id = (data_p[pos]<<8|data_p[pos+1])&0x1FFF; - pos += 2; - } - else { -// // program_map_PID 13 uimsbf - pid_info._pid = (data_p[pos]<<8|data_p[pos+1])&0x1FFF; - pos += 2; - } - _pat._pid_vec.push_back(pid_info); - // network_PID and program_map_PID save to list - } -// CRC_32 use pat to calc crc32, eq - pos += 4; - }else if(ts_header_info._PID == 0x01){ - // CAT // conditional access table - }else if(ts_header_info._PID == 0x02){ - //TSDT // transport stream description table - }else if(ts_header_info._PID == 0x03){ - //IPMP // IPMP control information table - // 0x0004-0x000F Reserved - // 0x0010-0x1FFE May be assigned as network_PID, Program_map_PID, elementary_PID, or for other purposes - }else if(ts_header_info._PID == 0x11){ - // SDT // https://en.wikipedia.org/wiki/Service_Description_Table / https://en.wikipedia.org/wiki/MPEG_transport_stream - }else if(is_pmt(ts_header_info._PID)) { - if(ts_header_info._payload_unit_start_indicator) - pos++; - _pmt._table_id = data_p[pos]; - pos++; - _pmt._section_syntax_indicator = (data_p[pos]>>7)&0x01; - // skip 3 bits of 1 zero and 2 reserved - _pmt._section_length = ((data_p[pos]<<8)|data_p[pos+1])&0x0FFF; - pos += 2; - _pmt._program_number = (data_p[pos]<<8)|data_p[pos+1]; - pos += 2; - // reserved 2 bits - _pmt._version_number = (data_p[pos]&0x3E)>>1; - _pmt._current_next_indicator = data_p[pos]&0x01; - pos++; - _pmt._section_number = data_p[pos]; - pos++; - _pmt._last_section_number = data_p[pos]; - pos++; - // skip 3 bits for reserved 3 bslbf - _pmt._PCR_PID = ((data_p[pos]<<8)|data_p[pos+1])&0x1FFF; //PCR_PID 13 uimsbf - pos += 2; - - //reserved 4 bslbf - _pmt._program_info_length = ((data_p[pos]<<8)|data_p[pos+1])&0x0FFF;//program_info_length 12 uimsbf - pos += 2; - - //0x02, // TS_program_map_section - if (_pmt._table_id != 0x02) { - srt_log_error("pmt tableid(0x%02x) error, it must be 0x02", _pmt._table_id) - return -1; - } - memcpy(_pmt._dscr, data_p+pos, _pmt._program_info_length); -// for (i = 0; i < N; i++) { -// descriptor() -// } - pos += _pmt._program_info_length; - _pmt._stream_pid_vec.clear(); - _pmt._pid2steamtype.clear(); - - for (; pos + 5 <= _pmt._section_length + 4 - 4 + npos; ) { // pos(above field length) i+5(following unit length) section_length +3(PMT begin three bytes)+1(payload_unit_start_indicator) -4(crc32) - STREAM_PID_INFO pid_info; - pid_info._stream_type = data_p[pos];//stream_type 8 uimsbf 0x1B AVC video stream as defined in ITU-T Rec. H.264 | ISO/IEC 14496-10 Video - pos++; - //reserved 3 bslbf - pid_info._elementary_PID = ((data_p[pos]<<8)|data_p[pos+1])&0x1FFF; //elementary_PID 13 uimsbf - pos += 2; - //reserved 4 bslbf - pid_info._ES_info_length = ((data_p[pos]<<8)|data_p[pos+1])&0x0FFF; //ES_info_length 12 uimsbf - pos += 2; - if( pos + pid_info._ES_info_length > _pmt._section_length + 4 - 4 + npos ) - break; - int absES_info_length = pos + pid_info._ES_info_length; - for (; pos< absES_info_length; ) { - //descriptor() - int descriptor_tag = data_p[pos]; - (void)descriptor_tag; - pos++; - int descriptor_length = data_p[pos]; - pos++; - memcpy(pid_info._dscr, data_p + pos, descriptor_length); - pos += descriptor_length; - } - // save program_number(stream num) elementary_PID(PES PID) stream_type(stream codec) - _pmt._stream_pid_vec.push_back(pid_info); - _pmt._pid2steamtype.insert(std::make_pair((unsigned short)pid_info._elementary_PID, pid_info._stream_type)); - } - pos += 4;//CRC_32 - }else if(ts_header_info._PID == 0x0042){ - // USER - }else if(ts_header_info._PID == 0x1FFF){ - // Null packet - }else{//pes packet or pure data packet - //bool isFound = false; - for (size_t i = 0; i < _pmt._stream_pid_vec.size(); i++) { - if(ts_header_info._PID == _pmt._stream_pid_vec[i]._elementary_PID){ - //isFound = true; - if(ts_header_info._payload_unit_start_indicator){ - unsigned char* ret_data_p = nullptr; - size_t ret_size = 0; - uint64_t dts = 0; - uint64_t pts = 0; - - //callback last media data in data buffer - int err_code = on_callback(callback, _last_pid, key_path, _last_dts, _last_pts); - if (err_code != 0) - return err_code; - - int ret = pes_parse(data_p+npos, npos, &ret_data_p, ret_size, dts, pts); - if (ret > 188) { - srt_log_error("pes length(%d) error", ret); - return -1; - } - - _last_pts = pts; - _last_dts = (dts == 0) ? pts : dts; - - if ((ret_data_p != nullptr) && (ret_size > 0)) { - insert_into_databuf(ret_data_p, ret_size, key_path, ts_header_info._PID); - } - }else{ - //fwrite(p, 1, 188-(npos+pos), pes_info[i].fd); - insert_into_databuf(data_p + npos, 188-npos, key_path, ts_header_info._PID); - } - } - } - } - } - - return 0; -} -int ts_demux::decode(SRT_DATA_MSG_PTR data_ptr, TS_DATA_CALLBACK_PTR callback) -{ - int ret = -1; - std::string path; - - if (!data_ptr || (data_ptr->data_len() < 188) || (data_ptr->data_len()%188 != 0)) - { - return -1; - } - - unsigned int count = data_ptr->data_len()/188; - path = data_ptr->get_path(); - for (unsigned int index = 0; index < count; index++) - { - unsigned char* data = data_ptr->get_data() + 188*index; - if (data[0] != 0x47) { - continue; - } - ret = decode_unit(data, path, callback); - if (ret != 0) // srs_error_code is positive - { - break; - } - } - return ret; -} - -void ts_demux::insert_into_databuf(unsigned char* data_p, size_t data_size, std::string key_path, unsigned short pid) { - _last_pid = pid; - _data_total += data_size; - _data_buffer_vec.push_back(std::make_shared(data_p, data_size, key_path)); - return; -} - -int ts_demux::on_callback(TS_DATA_CALLBACK_PTR callback, unsigned short pid, std::string key_path, - uint64_t dts, uint64_t pts) { - if ((_data_total <=0 ) || (_data_buffer_vec.empty())) { - return 0; - } - - auto iter = _pmt._pid2steamtype.find(pid); - if (iter == _pmt._pid2steamtype.end()) { - return 0; - } - unsigned char stream_type = iter->second; - auto total_data_ptr = std::make_shared(_data_total, key_path); - size_t pos = 0; - - for (size_t index = 0; index < _data_buffer_vec.size(); index++) { - memcpy(total_data_ptr->get_data() + pos, - _data_buffer_vec[index]->get_data(), - _data_buffer_vec[index]->data_len()); - pos += _data_buffer_vec[index]->data_len(); - } - _data_buffer_vec.clear(); - _data_total = 0; - - return callback->on_data_callback(total_data_ptr, stream_type, dts, pts); -} - -bool ts_demux::is_pmt(unsigned short pid) { - for (size_t index = 0; index < _pat._pid_vec.size(); index++) { - if (_pat._pid_vec[index]._program_number != 0) { - if (_pat._pid_vec[index]._pid == pid) { - return true; - } - } - } - return false; -} - - -int ts_demux::pes_parse(unsigned char* p, size_t npos, - unsigned char** ret_pp, size_t& ret_size, - uint64_t& dts, uint64_t& pts) { - int pos = 0; - int packet_start_code_prefix = (p[pos]<<16)|(p[pos+1]<<8)|p[pos+2]; //packet_start_code_prefix 24 bslbf - pos += 3; - int stream_id = p[pos]; //stream_id 8 uimsbf - pos++; - - int PES_packet_length = ((unsigned int)p[pos]<<8)|p[pos+1]; //PES_packet_length 16 uimsbf - (void)PES_packet_length; - pos += 2; - - if (0x00000001 != packet_start_code_prefix) { - srt_log_error("pes packet start code prefix(%06x) error, it must be 0x00 00 01", packet_start_code_prefix); - return 255; - } - if (stream_id != 188//program_stream_map 1011 1100 - && stream_id != 190//padding_stream 1011 1110 - && stream_id != 191//private_stream_2 1011 1111 - && stream_id != 240//ECM 1111 0000 - && stream_id != 241//EMM 1111 0001 - && stream_id != 255//program_stream_directory 1111 1111 - && stream_id != 242//DSMCC_stream 1111 0010 - && stream_id != 248//ITU-T Rec. H.222.1 type E stream 1111 1000 - ) - { - if (0x80 != (p[pos] & 0xc0)) { - srt_log_error("the first 2 bits:0x%02x error, it must be 0x80.", (p[pos] & 0xc0)); - return 255; - } - //skip 2bits//'10' 2 bslbf - int PES_scrambling_control = (p[pos]&30)>>4; //PES_scrambling_control 2 bslbf - (void)PES_scrambling_control; - int PES_priority = (p[pos]&0x08)>>3; //PES_priority 1 bslbf - (void)PES_priority; - int data_alignment_indicator = (p[pos]&0x04)>>2;//data_alignment_indicator 1 bslbf - (void)data_alignment_indicator; - int copyright = (p[pos]&0x02)>>1; //copyright 1 bslbf - (void)copyright; - int original_or_copy = (p[pos]&0x01);//original_or_copy 1 bslbf - (void)original_or_copy; - pos++; - int PTS_DTS_flags = (p[pos]&0xC0)>>6; //PTS_DTS_flags 2 bslbf - int ESCR_flag = (p[pos]&0x20)>>5; // ESCR_flag 1 bslbf - int ES_rate_flag = (p[pos]&0x10)>>4;//ES_rate_flag 1 bslbf - int DSM_trick_mode_flag = (p[pos]&0x08)>>3;//DSM_trick_mode_flag 1 bslbf - int additional_copy_info_flag = (p[pos]&0x04)>>2; //additional_copy_info_flag 1 bslbf - int PES_CRC_flag = (p[pos]&0x02)>>1; //PES_CRC_flag 1 bslbf - int PES_extension_flag = (p[pos]&0x01);//PES_extension_flag 1 bslbf - pos++; - int PES_header_data_length = p[pos]; //PES_header_data_length 8 uimsbf - (void)PES_header_data_length; - pos++; - - if (PTS_DTS_flags == 2) { - // skip 4 bits '0010' 4 bslbf - // PTS [32..30] 3 bslbf - // marker_bit 1 bslbf - // PTS [29..15] 15 bslbf - // marker_bit 1 bslbf - // PTS [14..0] 15 bslbf - // marker_bit 1 bslbf - pts = (((p[pos]>>1)&0x07) << 30) | (p[pos+1]<<22) | (((p[pos+2]>>1)&0x7F)<<15) | (p[pos+3]<<7) | ((p[pos+4]>>1)&0x7F); - pos += 5; - } - if (PTS_DTS_flags == 3) { - // '0011' 4 bslbf - // PTS [32..30] 3 bslbf - // marker_bit 1 bslbf - //PTS [29..15] 15 bslbf - //marker_bit 1 bslbf - // PTS [14..0] 15 bslbf - // marker_bit 1 bslbf - pts = (((p[pos]>>1)&0x07) << 30) | (p[pos+1]<<22) | (((p[pos+2]>>1)&0x7F)<<15) | (p[pos+3]<<7) | ((p[pos+4]>>1)&0x7F); - pos += 5; - // '0001' 4 bslbf - // DTS [32..30] 3 bslbf - // marker_bit 1 bslbf - // DTS [29..15] 15 bslbf - // marker_bit 1 bslbf - // DTS [14..0] 15 bslbf - // marker_bit 1 bslbf - dts = (((p[pos]>>1)&0x07) << 30) | (p[pos+1]<<22) | (((p[pos+2]>>1)&0x7F)<<15) | (p[pos+3]<<7) | ((p[pos+4]>>1)&0x7F); - pos += 5; - } - if (ESCR_flag == 1) { - // reserved 2 bslbf - // ESCR_base[32..30] 3 bslbf - // marker_bit 1 bslbf - // ESCR_base[29..15] 15 bslbf - // marker_bit 1 bslbf - // ESCR_base[14..0] 15 bslbf - // marker_bit 1 bslbf - // ESCR_extension 9 uimsbf - // marker_bit 1 bslbf - uint64_t ESCR_base = ((((uint64_t)p[pos] >> 3) & 0x07) << 30) | (((uint64_t)p[pos] & 0x03) << 28) | ((uint64_t)p[pos + 1] << 20) | ((((uint64_t)p[pos + 2] >> 3) & 0x1F) << 15) | (((uint64_t)p[pos + 2] & 0x3) << 13) | ((uint64_t)p[pos + 3] << 5) | ((p[pos + 4] >> 3) & 0x1F); - int ESCR_extension = ((p[pos + 4] & 0x03) << 7) | ((p[pos + 5] >> 1) & 0x7F); - (void)ESCR_base; - (void)ESCR_extension; - pos += 6; - } - if (ES_rate_flag == 1) { - // marker_bit 1 bslbf - // ES_rate 22 uimsbf - // marker_bit 1 bslbf - int ES_rate = (p[pos]&0x7F)<<15 | (p[pos+1])<<7 | (p[pos+2]&0x7F)>>1; - (void)ES_rate; - pos += 3; - } - if (DSM_trick_mode_flag == 1) { // ignore - int trick_mode_control = (p[pos]&0xE0)>>5;//trick_mode_control 3 uimsbf - if ( trick_mode_control == 0/*fast_forward*/ ) { - // field_id 2 bslbf - // intra_slice_refresh 1 bslbf - // frequency_truncation 2 bslbf - } - else if ( trick_mode_control == 1/*slow_motion*/ ) { - //rep_cntrl 5 uimsbf - } - else if ( trick_mode_control == 2/*freeze_frame*/ ) { - // field_id 2 uimsbf - // reserved 3 bslbf - } - else if ( trick_mode_control == 3/*fast_reverse*/ ) { - // field_id 2 bslbf - // intra_slice_refresh 1 bslbf - // frequency_truncation 2 bslbf - }else if ( trick_mode_control == 4/*slow_reverse*/ ) { - // rep_cntrl 5 uimsbf - } - else{ - //reserved 5 bslbf - } - pos++; - } - if ( additional_copy_info_flag == 1) { // ignore - // marker_bit 1 bslbf - // additional_copy_info 7 bslbf - pos++; - } - if ( PES_CRC_flag == 1) { // ignore - // previous_PES_packet_CRC 16 bslbf - pos += 2; - } - if ( PES_extension_flag == 1) { // ignore - int PES_private_data_flag = (p[pos]&0x80)>>7;// PES_private_data_flag 1 bslbf - int pack_header_field_flag = (p[pos]&0x40)>>6;// pack_header_field_flag 1 bslbf - int program_packet_sequence_counter_flag = (p[pos]&0x20)>>5;// program_packet_sequence_counter_flag 1 bslbf - int P_STD_buffer_flag = (p[pos]&0x10)>>4; // P-STD_buffer_flag 1 bslbf - // reserved 3 bslbf - int PES_extension_flag_2 = (p[pos]&0x01);// PES_extension_flag_2 1 bslbf - pos++; - - if ( PES_private_data_flag == 1) { - // PES_private_data 128 bslbf - pos += 16; - } - if (pack_header_field_flag == 1) { - // pack_field_length 8 uimsbf - // pack_header() - } - if (program_packet_sequence_counter_flag == 1) { - // marker_bit 1 bslbf - // program_packet_sequence_counter 7 uimsbf - // marker_bit 1 bslbf - // MPEG1_MPEG2_identifier 1 bslbf - // original_stuff_length 6 uimsbf - pos += 2; - } - if ( P_STD_buffer_flag == 1) { - // '01' 2 bslbf - // P-STD_buffer_scale 1 bslbf - // P-STD_buffer_size 13 uimsbf - pos += 2; - } - if ( PES_extension_flag_2 == 1) { - // marker_bit 1 bslbf - int PES_extension_field_length = (p[pos]&0x7F);// PES_extension_field_length 7 uimsbf - pos++; - for (int i = 0; i < PES_extension_field_length; i++) { - // reserved 8 bslbf - pos++; - } - } - } - -// for (int i = 0; i < N1; i++) { - //stuffing_byte 8 bslbf -// rpos++; -// } -// for (int i = 0; i < N2; i++) { - //PES_packet_data_byte 8 bslbf -// rpos++; -// } - *ret_pp = p+pos; - ret_size = 188-(npos+pos); - } - else if ( stream_id == 188//program_stream_map 1011 1100 BC - || stream_id == 191//private_stream_2 1011 1111 BF - || stream_id == 240//ECM 1111 0000 F0 - || stream_id == 241//EMM 1111 0001 F1 - || stream_id == 255//program_stream_directory 1111 1111 FF - || stream_id == 242//DSMCC_stream 1111 0010 F2 - || stream_id == 248//ITU-T Rec. H.222.1 type E stream 1111 1000 F8 - ) { -// for (i = 0; i < PES_packet_length; i++) { - //PES_packet_data_byte 8 bslbf -// rpos++; -// } - *ret_pp = p+pos; - ret_size = 188-(npos+pos); - //fwrite(p, 1, 188-(npos+rpos), fd); - } - else if ( stream_id == 190//padding_stream 1011 1110 - ) { -// for (i = 0; i < PES_packet_length; i++) { - // padding_byte 8 bslbf -// rpos++; - *ret_pp = p+pos; - ret_size = 188-(npos+pos); -// } - } - - return pos; -} diff --git a/trunk/src/srt/ts_demux.hpp b/trunk/src/srt/ts_demux.hpp deleted file mode 100644 index 06a888c6c1..0000000000 --- a/trunk/src/srt/ts_demux.hpp +++ /dev/null @@ -1,247 +0,0 @@ -// -// Copyright (c) 2013-2021 The SRS Authors -// -// SPDX-License-Identifier: MIT or MulanPSL-2.0 -// - -#ifndef TS_DEMUX_H -#define TS_DEMUX_H - -#include - -#include "srt_data.hpp" -#include -#include -#include -#include - -/* mpegts stream type in ts pmt -Value Description -0x00 ITU-T | ISO/IEC Reserved -0x01 ISO/IEC 11172-2 Video (mpeg video v1) -0x02 ITU-T Rec. H.262 | ISO/IEC 13818-2 Video(mpeg video v2)or ISO/IEC 11172-2 constrained parameter video stream -0x03 ISO/IEC 11172-3 Audio (MPEG 1 Audio codec Layer I, Layer II and Layer III audio specifications) -0x04 ISO/IEC 13818-3 Audio (BC Audio Codec) -0x05 ITU-T Rec. H.222.0 | ISO/IEC 13818-1 private_sections -0x06 ITU-T Rec. H.222.0 | ISO/IEC 13818-1 PES packets containing private data -0x07 ISO/IEC 13522 MHEG -0x08 ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Annex A DSM-CC -0x09 ITU-T Rec. H.222.1 -0x0A ISO/IEC 13818-6 type A -0x0B ISO/IEC 13818-6 type B -0x0C ISO/IEC 13818-6 type C -0x0D ISO/IEC 13818-6 type D -0x0E ITU-T Rec. H.222.0 | ISO/IEC 13818-1 auxiliary -0x0F ISO/IEC 13818-7 Audio with ADTS transport syntax -0x10 ISO/IEC 14496-2 Visual -0x11 ISO/IEC 14496-3 Audio with the LATM transport syntax as defined in ISO/IEC 14496-3/Amd.1 -0x12 ISO/IEC 14496-1 SL-packetized stream or FlexMux stream carried in PES packets -0x13 ISO/IEC 14496-1 SL-packetized stream or FlexMux stream carried in ISO/IEC 14496_sections -0x14 ISO/IEC 13818-6 Synchronized Download Protocol -0x15 Metadata carried in PES packets -0x16 Metadata carried in metadata_sections -0x17 Metadata carried in ISO/IEC 13818-6 Data Carousel -0x18 Metadata carried in ISO/IEC 13818-6 Object Carousel -0x19 Metadata carried in ISO/IEC 13818-6 Synchronized Download Protocol -0x1A IPMP stream (defined in ISO/IEC 13818-11, MPEG-2 IPMP) -0x1B AVC video stream as defined in ITU-T Rec. H.264 | ISO/IEC 14496-10 Video (h.264) -0x1C ISO/IEC 14496-3 Audio, without using any additional transport syntax, such as DST, ALS and SLS -0x1D ISO/IEC 14496-17 Text -0x1E Auxiliary video stream as defined in ISO/IEC 23002-3 (AVS) -0x1F-0x7E ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Reserved -0x7F IPMP stream 0x80-0xFF User Private -*/ -#define STREAM_TYPE_VIDEO_MPEG1 0x01 -#define STREAM_TYPE_VIDEO_MPEG2 0x02 -#define STREAM_TYPE_AUDIO_MPEG1 0x03 -#define STREAM_TYPE_AUDIO_MPEG2 0x04 -#define STREAM_TYPE_PRIVATE_SECTION 0x05 -#define STREAM_TYPE_PRIVATE_DATA 0x06 -#define STREAM_TYPE_AUDIO_AAC 0x0f -#define STREAM_TYPE_AUDIO_AAC_LATM 0x11 -#define STREAM_TYPE_VIDEO_MPEG4 0x10 -#define STREAM_TYPE_METADATA 0x15 -#define STREAM_TYPE_VIDEO_H264 0x1b -#define STREAM_TYPE_VIDEO_HEVC 0x24 -#define STREAM_TYPE_VIDEO_CAVS 0x42 -#define STREAM_TYPE_VIDEO_VC1 0xea -#define STREAM_TYPE_VIDEO_DIRAC 0xd1 - -#define STREAM_TYPE_AUDIO_AC3 0x81 -#define STREAM_TYPE_AUDIO_DTS 0x82 -#define STREAM_TYPE_AUDIO_TRUEHD 0x83 -#define STREAM_TYPE_AUDIO_EAC3 0x87 - -class ts_media_data_callback_I { -public: - virtual int on_data_callback(SRT_DATA_MSG_PTR data_ptr, unsigned int media_type, uint64_t dts, uint64_t pts) = 0; -}; - -typedef std::shared_ptr TS_DATA_CALLBACK_PTR; - -class adaptation_field { -public: - adaptation_field(){}; - ~adaptation_field(){}; - -public: - unsigned char _adaptation_field_length; - - unsigned char _discontinuity_indicator:1; - unsigned char _random_access_indicator:1; - unsigned char _elementary_stream_priority_indicator:1; - unsigned char _PCR_flag:1; - unsigned char _OPCR_flag:1; - unsigned char _splicing_point_flag:1; - unsigned char _transport_private_data_flag:1; - unsigned char _adaptation_field_extension_flag:1; - - //if(PCR_flag == '1') - unsigned long _program_clock_reference_base;//33 bits - unsigned short _program_clock_reference_extension;//9bits - //if (OPCR_flag == '1') - unsigned long _original_program_clock_reference_base;//33 bits - unsigned short _original_program_clock_reference_extension;//9bits - //if (splicing_point_flag == '1') - unsigned char _splice_countdown; - //if (transport_private_data_flag == '1') - unsigned char _transport_private_data_length; - unsigned char _private_data_byte[256]; - //if (adaptation_field_extension_flag == '1') - unsigned char _adaptation_field_extension_length; - unsigned char _ltw_flag; - unsigned char _piecewise_rate_flag; - unsigned char _seamless_splice_flag; - unsigned char _reserved0; - //if (ltw_flag == '1') - unsigned short _ltw_valid_flag:1; - unsigned short _ltw_offset:15; - //if (piecewise_rate_flag == '1') - unsigned int _piecewise_rate;//22bits - //if (seamless_splice_flag == '1') - unsigned char _splice_type;//4bits - unsigned char _DTS_next_AU1;//3bits - unsigned char _marker_bit1;//1bit - unsigned short _DTS_next_AU2;//15bit - unsigned char _marker_bit2;//1bit - unsigned short _DTS_next_AU3;//15bit - unsigned char _marker_bit3;//1bit -}; - -class ts_header { -public: - ts_header(){} - ~ts_header(){} - -public: - unsigned char _sync_byte; - - unsigned short _transport_error_indicator:1; - unsigned short _payload_unit_start_indicator:1; - unsigned short _transport_priority:1; - unsigned short _PID:13; - - unsigned char _transport_scrambling_control:2; - unsigned char _adaptation_field_control:2; - unsigned char _continuity_counter:4; - - adaptation_field _adaptation_field_info; -}; - -typedef struct { - unsigned short _program_number; - unsigned short _pid; - unsigned short _network_id; -} PID_INFO; - -class pat_info { -public: - pat_info(){}; - ~pat_info(){}; - -public: - unsigned char _table_id; - - unsigned short _section_syntax_indicator:1; - unsigned short _reserved0:1; - unsigned short _reserved1:2; - unsigned short _section_length:12; - - unsigned short _transport_stream_id; - - unsigned char _reserved3:2; - unsigned char _version_number:5; - unsigned char _current_next_indicator:1; - - unsigned char _section_number; - unsigned char _last_section_number; - std::vector _pid_vec; -}; - -typedef struct { - unsigned char _stream_type; - unsigned short _reserved1:3; - unsigned short _elementary_PID:13; - unsigned short _reserved:4; - unsigned short _ES_info_length; - unsigned char _dscr[4096]; - unsigned int _crc_32; -} STREAM_PID_INFO; - -class pmt_info { -public: - pmt_info(){}; - ~pmt_info(){}; -public: - unsigned char _table_id; - unsigned short _section_syntax_indicator:1; - unsigned short _reserved1:1; - unsigned short _reserved2:2; - unsigned short _section_length:12; - unsigned short _program_number:16; - unsigned char _reserved:2; - unsigned char _version_number:5; - unsigned char _current_next_indicator:5; - unsigned char _section_number; - unsigned char _last_section_number; - unsigned short _reserved3:3; - unsigned short _PCR_PID:13; - unsigned short _reserved4:4; - unsigned short _program_info_length:12; - unsigned char _dscr[4096]; - - std::unordered_map _pid2steamtype; - std::vector _stream_pid_vec; -}; - -class ts_demux { -public: - ts_demux(); - ~ts_demux(); - - int decode(SRT_DATA_MSG_PTR data_ptr, TS_DATA_CALLBACK_PTR callback); - -private: - int decode_unit(unsigned char* data_p, std::string key_path, TS_DATA_CALLBACK_PTR callback); - bool is_pmt(unsigned short pmt_id); - int pes_parse(unsigned char* p, size_t npos, unsigned char** ret_pp, size_t& ret_size, - uint64_t& dts, uint64_t& pts); - void insert_into_databuf(unsigned char* data_p, size_t data_size, std::string key_path, unsigned short pid); - int on_callback(TS_DATA_CALLBACK_PTR callback, unsigned short pid, - std::string key_path, uint64_t dts, uint64_t pts); - -private: - std::string _key_path;//only for srt - - pat_info _pat; - pmt_info _pmt; - std::vector _data_buffer_vec; - size_t _data_total; - unsigned short _last_pid; - uint64_t _last_dts; - uint64_t _last_pts; -}; - -typedef std::shared_ptr TS_DEMUX_PTR; - -#endif diff --git a/trunk/src/srt/ts_demux_test.cpp b/trunk/src/srt/ts_demux_test.cpp deleted file mode 100644 index e386796409..0000000000 --- a/trunk/src/srt/ts_demux_test.cpp +++ /dev/null @@ -1,61 +0,0 @@ -// -// Copyright (c) 2013-2021 The SRS Authors -// -// SPDX-License-Identifier: MIT or MulanPSL-2.0 -// - -#include "ts_demux.hpp" -#include -#include - -#define TS_MAX 188 - -class media_data_get : public ts_media_data_callback_I { -public: - media_data_get() {}; - virtual ~media_data_get() {}; - -public: - virtual void on_data_callback(SRT_DATA_MSG_PTR data_ptr, unsigned int media_type - , uint64_t dts, uint64_t pts) { - printf("media type:%d, data len:%d, key_path:%s, dts:%lu(%lu), pts:%lu(%lu)\r\n", - media_type, data_ptr->data_len(), data_ptr->get_path().c_str(), dts, dts/90, pts, pts/90); - FILE* file_p; - char filename[80]; - - sprintf(filename, "%u.media", media_type); - file_p = fopen(filename, "ab+"); - if (file_p) { - fwrite(data_ptr->get_data(), data_ptr->data_len(), 1, file_p); - fclose(file_p); - } - return; - } -}; - -int main(int argn, char** argv) { - unsigned char data[TS_MAX]; - ts_demux demux_obj; - auto callback_ptr = std::make_shared(); - FILE* file_p; - if (argn < 2) { - printf("please input ts name.\r\n"); - return 0; - } - - const char* file_name = argv[1]; - printf("input ts name:%s.\r\n", file_name); - - file_p = fopen(file_name, "r"); - fseek(file_p, 0L, SEEK_END); /* 定位到文件末尾 */ - size_t flen = ftell(file_p); /* 得到文件大小 */ - fseek(file_p, 0L, SEEK_SET); /* 定位到文件开头 */ - - do { - fread(data, TS_MAX, 1, file_p); - auto input_ptr = std::make_shared((unsigned char*)data, (unsigned int)TS_MAX, std::string("live/shiwei")); - demux_obj.decode(input_ptr, callback_ptr); - flen -= TS_MAX; - } while(flen > 0); - return 1; -} diff --git a/trunk/src/utest/srs_utest.cpp b/trunk/src/utest/srs_utest.cpp index 2b07563f6b..52365b942a 100644 --- a/trunk/src/utest/srs_utest.cpp +++ b/trunk/src/utest/srs_utest.cpp @@ -17,6 +17,10 @@ #include using namespace std; +#ifdef SRS_SRT +#include +#endif + // Temporary disk config. std::string _srs_tmp_file_prefix = "/tmp/srs-utest-"; // Temporary network config. @@ -52,6 +56,16 @@ srs_error_t prepare_main() { srs_freep(_srs_context); _srs_context = new SrsThreadContext(); +#ifdef SRS_SRT + _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 + return err; } diff --git a/trunk/src/utest/srs_utest_config.cpp b/trunk/src/utest/srs_utest_config.cpp index 7a96748473..f779431480 100644 --- a/trunk/src/utest/srs_utest_config.cpp +++ b/trunk/src/utest/srs_utest_config.cpp @@ -2115,6 +2115,9 @@ VOID TEST(ConfigUnitTest, CheckDefaultValuesVhost) VOID TEST(ConfigUnitTest, CheckDefaultValuesGlobal) { if (true) { + // Schedule thread once, to update last_clock in state-thread. + srs_usleep(1); + srs_utime_t t0 = srs_update_system_time(); srs_usleep(10 * SRS_UTIME_MILLISECONDS); srs_utime_t t1 = srs_update_system_time(); diff --git a/trunk/src/utest/srs_utest_srt.cpp b/trunk/src/utest/srs_utest_srt.cpp index 7d420a22ca..be03e82c33 100644 --- a/trunk/src/utest/srs_utest_srt.cpp +++ b/trunk/src/utest/srs_utest_srt.cpp @@ -6,84 +6,463 @@ #include #include -#include +#include +#include +#include +#include +#include +#include #include using namespace std; -VOID TEST(ProtocolSrtTest, SrtGetStreamInfoNormal) { +#include + +extern SrsSrtEventLoop* _srt_eventloop; + +// Test srt st service +VOID TEST(ServiceSrtPoller, SrtPollOperateSocket) +{ + srs_error_t err = srs_success; + + ISrsSrtPoller* srt_poller = srs_srt_poller_new(); + HELPER_EXPECT_SUCCESS(srt_poller->initialize()); + + srs_srt_t srt_fd = srs_srt_socket_invalid(); + HELPER_EXPECT_SUCCESS(srs_srt_socket(&srt_fd)); + EXPECT_TRUE(srt_fd > 0); + + SrsSrtSocket* srt_socket = new SrsSrtSocket(srt_poller, srt_fd); + EXPECT_EQ(srt_socket->events(), 0); + + // Enable read, will subscribe SRT_EPOLL_IN and SRT_EPOLL_ERR event in srt poller. + HELPER_EXPECT_SUCCESS(srt_socket->enable_read()); + EXPECT_TRUE(srt_socket->events() & SRT_EPOLL_IN); + EXPECT_TRUE(srt_socket->events() & SRT_EPOLL_ERR); + + // Enable read, will subscribe SRT_EPOLL_OUT and SRT_EPOLL_ERR event in srt poller. + HELPER_EXPECT_SUCCESS(srt_socket->enable_write()); + EXPECT_TRUE(srt_socket->events() & SRT_EPOLL_OUT); + EXPECT_TRUE(srt_socket->events() & SRT_EPOLL_ERR); + + // Disable read, will unsubscribe SRT_EPOLL_IN event in srt poller. + HELPER_EXPECT_SUCCESS(srt_socket->disable_read()); + EXPECT_FALSE(srt_socket->events() & SRT_EPOLL_IN); + EXPECT_TRUE(srt_socket->events() & SRT_EPOLL_ERR); + + // Disable write, will unsubscribe SRT_EPOLL_OUT event in srt poller. + HELPER_EXPECT_SUCCESS(srt_socket->disable_write()); + EXPECT_FALSE(srt_socket->events() & SRT_EPOLL_OUT); + EXPECT_TRUE(srt_socket->events() & SRT_EPOLL_ERR); + + EXPECT_EQ(srt_poller->size(), 1); + // Delete socket, will remove in srt poller. + srs_freep(srt_socket); + EXPECT_EQ(srt_poller->size(), 0); + + srs_freep(srt_poller); +} + +VOID TEST(ServiceSrtPoller, SrtSetGetSocketOpt) +{ + srs_error_t err = srs_success; + + srs_srt_t srt_fd = srs_srt_socket_invalid(); + HELPER_EXPECT_SUCCESS(srs_srt_socket(&srt_fd)); + HELPER_EXPECT_SUCCESS(srs_srt_nonblock(srt_fd)); + + int maxbw = 20000; + int mss = 1400; + int payload_size = 1316; + int connect_timeout = 5000; + int peer_idle_timeout = 10000; + bool tsbpdmode = false; + int sndbuf = 2 * 1024 * 1024; + int rcvbuf = 10 * 1024 * 1024; + bool tlpktdrop = false; + int latency = 0; + int rcv_latency = 120; + int peer_latency = 120; + std::string streamid = "SRS_SRT"; + + HELPER_EXPECT_SUCCESS(srs_srt_set_maxbw(srt_fd, maxbw)); + HELPER_EXPECT_SUCCESS(srs_srt_set_mss(srt_fd, mss)); + HELPER_EXPECT_SUCCESS(srs_srt_set_payload_size(srt_fd, payload_size)); + HELPER_EXPECT_SUCCESS(srs_srt_set_connect_timeout(srt_fd, connect_timeout)); + HELPER_EXPECT_SUCCESS(srs_srt_set_peer_idle_timeout(srt_fd, peer_idle_timeout)); + HELPER_EXPECT_SUCCESS(srs_srt_set_tsbpdmode(srt_fd, tsbpdmode)); + HELPER_EXPECT_SUCCESS(srs_srt_set_sndbuf(srt_fd, sndbuf)); + HELPER_EXPECT_SUCCESS(srs_srt_set_rcvbuf(srt_fd, rcvbuf)); + HELPER_EXPECT_SUCCESS(srs_srt_set_tlpktdrop(srt_fd, tlpktdrop)); + HELPER_EXPECT_SUCCESS(srs_srt_set_latency(srt_fd, latency)); + HELPER_EXPECT_SUCCESS(srs_srt_set_rcv_latency(srt_fd, rcv_latency)); + HELPER_EXPECT_SUCCESS(srs_srt_set_peer_latency(srt_fd, peer_latency)); + HELPER_EXPECT_SUCCESS(srs_srt_set_streamid(srt_fd, streamid)); + + bool b; + int i = 0; + std::string s; + + HELPER_EXPECT_SUCCESS(srs_srt_get_maxbw(srt_fd, i)); + EXPECT_EQ(i, maxbw); + HELPER_EXPECT_SUCCESS(srs_srt_get_mss(srt_fd, i)); + EXPECT_EQ(i, mss); + HELPER_EXPECT_SUCCESS(srs_srt_get_payload_size(srt_fd, i)); + EXPECT_EQ(i, payload_size); + HELPER_EXPECT_SUCCESS(srs_srt_get_connect_timeout(srt_fd, i)); + EXPECT_EQ(i, connect_timeout); + HELPER_EXPECT_SUCCESS(srs_srt_get_peer_idle_timeout(srt_fd, i)); + EXPECT_EQ(i, peer_idle_timeout); + + // Don't check b equal to option blow, because some opt will deterimated after srt handshake done or change when set. + HELPER_EXPECT_SUCCESS(srs_srt_get_tsbpdmode(srt_fd, b)); + HELPER_EXPECT_SUCCESS(srs_srt_get_sndbuf(srt_fd, i)); + HELPER_EXPECT_SUCCESS(srs_srt_get_rcvbuf(srt_fd, i)); + HELPER_EXPECT_SUCCESS(srs_srt_get_tlpktdrop(srt_fd, b)); + HELPER_EXPECT_SUCCESS(srs_srt_get_latency(srt_fd, i)); + HELPER_EXPECT_SUCCESS(srs_srt_get_rcv_latency(srt_fd, i)); + HELPER_EXPECT_SUCCESS(srs_srt_get_peer_latency(srt_fd, i)); + + HELPER_EXPECT_SUCCESS(srs_srt_get_streamid(srt_fd, s)); + EXPECT_EQ(s, streamid); +} + +class MockSrtServer +{ +public: + SrsSrtSocket* srt_socket_; + srs_srt_t srt_server_fd_; + + MockSrtServer() { + srt_server_fd_ = srs_srt_socket_invalid(); + srt_socket_ = NULL; + } + + srs_error_t create_socket() { + srs_error_t err = srs_success; + if ((err = srs_srt_socket_with_default_option(&srt_server_fd_)) != srs_success) { + return srs_error_wrap(err, "create srt socket"); + } + return err; + } + + srs_error_t listen(std::string ip, int port) { + srs_error_t err = srs_success; + + if ((err = srs_srt_listen(srt_server_fd_, ip, port)) != srs_success) { + return srs_error_wrap(err, "srt listen"); + } + + srt_socket_ = new SrsSrtSocket(_srt_eventloop->poller(), srt_server_fd_); + + return err; + } + + virtual ~MockSrtServer() { + srs_freep(srt_socket_); + } + + virtual srs_error_t accept(srs_srt_t* client_fd) { + srs_error_t err = srs_success; + + if ((err = srt_socket_->accept(client_fd)) != srs_success) { + return srs_error_wrap(err, "srt accept"); + } + + return err; + } +}; + +VOID TEST(ServiceStSRTTest, ListenConnectAccept) +{ + srs_error_t err = srs_success; + + std::string server_ip = "127.0.0.1"; + int server_port = 9000; + + MockSrtServer srt_server; + HELPER_EXPECT_SUCCESS(srt_server.create_socket()); + HELPER_EXPECT_SUCCESS(srt_server.listen(server_ip, server_port)); + + srs_srt_t srt_client_fd = srs_srt_socket_invalid(); + HELPER_EXPECT_SUCCESS(srs_srt_socket(&srt_client_fd)); + + SrsSrtSocket* srt_client_socket = new SrsSrtSocket(_srt_eventloop->poller(), srt_client_fd); + + // No client connected, accept will timeout. + srs_srt_t srt_fd = srs_srt_socket_invalid(); + // Make utest fast timeout. + srt_server.srt_socket_->set_recv_timeout(50 * SRS_UTIME_MILLISECONDS); + err = srt_server.accept(&srt_fd); + EXPECT_EQ(srs_error_code(err), ERROR_SRT_TIMEOUT); + EXPECT_EQ(srt_fd, srs_srt_socket_invalid()); + + // Client connect to server + HELPER_EXPECT_SUCCESS(srt_client_socket->connect(server_ip, server_port)); + + // Server will accept one client. + HELPER_EXPECT_SUCCESS(srt_server.accept(&srt_fd)); + EXPECT_NE(srt_fd, srs_srt_socket_invalid()); +} + +VOID TEST(ServiceStSRTTest, ConnectTimeout) +{ + srs_error_t err = srs_success; + + srs_srt_t srt_client_fd = srs_srt_socket_invalid(); + HELPER_EXPECT_SUCCESS(srs_srt_socket_with_default_option(&srt_client_fd)); + SrsSrtSocket* srt_client_socket = new SrsSrtSocket(_srt_eventloop->poller(), srt_client_fd); + + srt_client_socket->set_send_timeout(50 * SRS_UTIME_MILLISECONDS); + // Client connect to server which is no listening. + HELPER_EXPECT_FAILED(srt_client_socket->connect("127.0.0.1", 9099)); +} + +VOID TEST(ServiceStSRTTest, ConnectWithStreamid) +{ + srs_error_t err = srs_success; + + std::string server_ip = "127.0.0.1"; + int server_port = 9000; + + MockSrtServer srt_server; + HELPER_EXPECT_SUCCESS(srt_server.create_socket()); + HELPER_EXPECT_SUCCESS(srt_server.listen(server_ip, server_port)); + + std::string streamid = "SRS_SRT_Streamid"; + srs_srt_t srt_client_fd = srs_srt_socket_invalid(); + HELPER_EXPECT_SUCCESS(srs_srt_socket_with_default_option(&srt_client_fd)); + HELPER_EXPECT_SUCCESS(srs_srt_set_streamid(srt_client_fd, streamid)); + SrsSrtSocket* srt_client_socket = new SrsSrtSocket(_srt_eventloop->poller(), srt_client_fd); + + HELPER_EXPECT_SUCCESS(srt_client_socket->connect("127.0.0.1", 9000)); + + srs_srt_t srt_server_accepted_fd = srs_srt_socket_invalid(); + HELPER_EXPECT_SUCCESS(srt_server.accept(&srt_server_accepted_fd)); + EXPECT_NE(srt_server_accepted_fd, srs_srt_socket_invalid()); + std::string s; + HELPER_EXPECT_SUCCESS(srs_srt_get_streamid(srt_server_accepted_fd, s)); + EXPECT_EQ(s, streamid); +} + +VOID TEST(ServiceStSRTTest, ReadWrite) +{ + srs_error_t err = srs_success; + + std::string server_ip = "127.0.0.1"; + int server_port = 9000; + + MockSrtServer srt_server; + HELPER_EXPECT_SUCCESS(srt_server.create_socket()); + HELPER_EXPECT_SUCCESS(srt_server.listen(server_ip, server_port)); + + srs_srt_t srt_client_fd = srs_srt_socket_invalid(); + HELPER_EXPECT_SUCCESS(srs_srt_socket_with_default_option(&srt_client_fd)); + SrsSrtSocket* srt_client_socket = new SrsSrtSocket(_srt_eventloop->poller(), srt_client_fd); + + // Client connect to server + HELPER_EXPECT_SUCCESS(srt_client_socket->connect(server_ip, server_port)); + + // Server will accept one client. + srs_srt_t srt_server_accepted_fd = srs_srt_socket_invalid(); + HELPER_EXPECT_SUCCESS(srt_server.accept(&srt_server_accepted_fd)); + EXPECT_NE(srt_server_accepted_fd, srs_srt_socket_invalid()); + SrsSrtSocket* srt_server_accepted_socket = new SrsSrtSocket(_srt_eventloop->poller(), srt_server_accepted_fd); + + if (true) { + std::string content = "Hello, SRS SRT!"; + + // Client send msg to server. + ssize_t nb_write = 0; + HELPER_EXPECT_SUCCESS(srt_client_socket->sendmsg((char*)content.data(), content.size(), &nb_write)); + EXPECT_EQ(nb_write, content.size()); + + // Server recv msg from client + char buf[1500]; + ssize_t nb_read = 0; + HELPER_EXPECT_SUCCESS(srt_server_accepted_socket->recvmsg(buf, sizeof(buf), &nb_read)); + EXPECT_EQ(nb_read, content.size()); + EXPECT_EQ(std::string(buf, nb_read), content); + + // Server echo msg back to client. + HELPER_EXPECT_SUCCESS(srt_server_accepted_socket->sendmsg(buf, nb_read, &nb_write)); + EXPECT_EQ(nb_write, content.size()); + + // Client recv echo msg from server. + HELPER_EXPECT_SUCCESS(srt_client_socket->recvmsg(buf, sizeof(buf), &nb_read)); + EXPECT_EQ(nb_read, content.size()); + EXPECT_EQ(std::string(buf, nb_read), content); + } + if (true) { - int mode; string vhost; string subpath; - EXPECT_TRUE(get_streamid_info("#!::r=live/livestream,key1=value1,key2=value2", mode, vhost, subpath)); - EXPECT_EQ(PULL_SRT_MODE, mode); + char buf[1500]; + ssize_t nb_read = 0; + // Make socket fast timeout in ustet. + srt_server_accepted_socket->set_recv_timeout(50 * SRS_UTIME_MILLISECONDS); + // Recv msg from client, but client no send any msg, so will be timeout. + err = srt_server_accepted_socket->recvmsg(buf, sizeof(buf), &nb_read); + EXPECT_EQ(srs_error_code(err), ERROR_SRT_TIMEOUT); + } +} + +// Test srt server +class MockSrtHandler : public ISrsSrtHandler +{ +private: + srs_srt_t srt_fd; +public: + MockSrtHandler() { + srt_fd = srs_srt_socket_invalid(); + } + virtual ~MockSrtHandler() { + } +public: + virtual srs_error_t on_srt_client(srs_srt_t fd) { + srt_fd = fd; + return srs_success; + } +}; + +VOID TEST(SrtServerTest, SrtListener) +{ + srs_error_t err = srs_success; + + if (true) { + MockSrtHandler h; + SrsSrtListener srt_listener(&h, "127.0.0.1", 9000); + HELPER_EXPECT_SUCCESS(srt_listener.create_socket()); + HELPER_EXPECT_SUCCESS(srt_listener.listen()); + EXPECT_TRUE(srt_listener.fd() > 0); + } +} + +// Test srt app +VOID TEST(ProtocolSrtTest, SrtGetStreamInfoNormal) +{ + if (true) { + SrtMode mode; string vhost; string subpath; + EXPECT_TRUE(srs_srt_streamid_info("#!::r=live/livestream,key1=value1,key2=value2", mode, vhost, subpath)); + EXPECT_EQ(SrtModePull, mode); EXPECT_STREQ("", vhost.c_str()); EXPECT_STREQ("live/livestream?key1=value1&key2=value2", subpath.c_str()); } if (true) { - int mode; string vhost; string subpath; - EXPECT_TRUE(get_streamid_info("#!::h=host.com,r=live/livestream,key1=value1,key2=value2", mode, vhost, subpath)); - EXPECT_EQ(PULL_SRT_MODE, mode); + SrtMode mode; string vhost; string subpath; + EXPECT_TRUE(srs_srt_streamid_info("#!::h=host.com,r=live/livestream,key1=value1,key2=value2", mode, vhost, subpath)); + EXPECT_EQ(SrtModePull, mode); EXPECT_STREQ("host.com", vhost.c_str()); EXPECT_STREQ("live/livestream?vhost=host.com&key1=value1&key2=value2", subpath.c_str()); } } -VOID TEST(ProtocolSrtTest, SrtGetStreamInfoMethod) { +VOID TEST(ProtocolSrtTest, SrtGetStreamInfoMethod) +{ if (true) { - int mode; string vhost; string subpath; - EXPECT_TRUE(get_streamid_info("#!::r=live/livestream,m=request", mode, vhost, subpath)); - EXPECT_EQ(PULL_SRT_MODE, mode); + SrtMode mode; string vhost; string subpath; + EXPECT_TRUE(srs_srt_streamid_info("#!::r=live/livestream,m=request", mode, vhost, subpath)); + EXPECT_EQ(SrtModePull, mode); EXPECT_STREQ("live/livestream", subpath.c_str()); } if (true) { - int mode; string vhost; string subpath; - EXPECT_TRUE(get_streamid_info("#!::r=live/livestream,m=publish", mode, vhost, subpath)); - EXPECT_EQ(PUSH_SRT_MODE, mode); + SrtMode mode; string vhost; string subpath; + EXPECT_TRUE(srs_srt_streamid_info("#!::r=live/livestream,m=publish", mode, vhost, subpath)); + EXPECT_EQ(SrtModePush, mode); EXPECT_STREQ("live/livestream", subpath.c_str()); } } -VOID TEST(ProtocolSrtTest, SrtGetStreamInfoCompatible) { +VOID TEST(ProtocolSrtTest, SrtGetStreamInfoCompatible) +{ if (true) { - int mode; string vhost; string subpath; - EXPECT_TRUE(get_streamid_info("#!::h=live/livestream,m=request", mode, vhost, subpath)); - EXPECT_EQ(PULL_SRT_MODE, mode); + SrtMode mode; string vhost; string subpath; + EXPECT_TRUE(srs_srt_streamid_info("#!::h=live/livestream,m=request", mode, vhost, subpath)); + EXPECT_EQ(SrtModePull, mode); EXPECT_STREQ("", vhost.c_str()); EXPECT_STREQ("live/livestream", subpath.c_str()); } if (true) { - int mode; string vhost; string subpath; - EXPECT_TRUE(get_streamid_info("#!::h=live/livestream,m=publish", mode, vhost, subpath)); - EXPECT_EQ(PUSH_SRT_MODE, mode); + SrtMode mode; string vhost; string subpath; + EXPECT_TRUE(srs_srt_streamid_info("#!::h=live/livestream,m=publish", mode, vhost, subpath)); + EXPECT_EQ(SrtModePush, mode); EXPECT_STREQ("", vhost.c_str()); EXPECT_STREQ("live/livestream", subpath.c_str()); } if (true) { - int mode; string vhost; string subpath; - EXPECT_TRUE(get_streamid_info("#!::h=srs.srt.com.cn/live/livestream,m=request", mode, vhost, subpath)); - EXPECT_EQ(PULL_SRT_MODE, mode); + SrtMode mode; string vhost; string subpath; + EXPECT_TRUE(srs_srt_streamid_info("#!::h=srs.srt.com.cn/live/livestream,m=request", mode, vhost, subpath)); + EXPECT_EQ(SrtModePull, mode); EXPECT_STREQ("srs.srt.com.cn", vhost.c_str()); EXPECT_STREQ("live/livestream?vhost=srs.srt.com.cn", subpath.c_str()); } if (true) { - int mode; string vhost; string subpath; - EXPECT_TRUE(get_streamid_info("#!::h=srs.srt.com.cn/live/livestream,m=publish", mode, vhost, subpath)); - EXPECT_EQ(PUSH_SRT_MODE, mode); + SrtMode mode; string vhost; string subpath; + EXPECT_TRUE(srs_srt_streamid_info("#!::h=srs.srt.com.cn/live/livestream,m=publish", mode, vhost, subpath)); + EXPECT_EQ(SrtModePush, mode); EXPECT_STREQ("srs.srt.com.cn", vhost.c_str()); EXPECT_STREQ("live/livestream?vhost=srs.srt.com.cn", subpath.c_str()); } if (true) { - int mode; string vhost; string subpath; - EXPECT_TRUE(get_streamid_info("#!::h=live/livestream?secret=d6d2be37,m=publish", mode, vhost, subpath)); - EXPECT_EQ(PUSH_SRT_MODE, mode); + SrtMode mode; string vhost; string subpath; + EXPECT_TRUE(srs_srt_streamid_info("#!::h=live/livestream?secret=d6d2be37,m=publish", mode, vhost, subpath)); + EXPECT_EQ(SrtModePush, mode); EXPECT_STREQ("", vhost.c_str()); EXPECT_STREQ("live/livestream?secret=d6d2be37", subpath.c_str()); } } +VOID TEST(ProtocolSrtTest, SrtStreamIdToRequest) +{ + if (true) { + SrtMode mode; + SrsRequest req; + EXPECT_TRUE(srs_srt_streamid_to_request("#!::r=live/livestream?key1=val1,key2=val2", mode, &req)); + EXPECT_EQ(mode, SrtModePull); + EXPECT_STREQ(req.vhost.c_str(), ""); + EXPECT_STREQ(req.app.c_str(), "live"); + EXPECT_STREQ(req.stream.c_str(), "livestream"); + EXPECT_STREQ(req.param.c_str(), "key1=val1&key2=val2"); + } + + if (true) { + SrtMode mode; + SrsRequest req; + EXPECT_TRUE(srs_srt_streamid_to_request("#!::h=srs.srt.com.cn,r=live/livestream?key1=val1,key2=val2", mode, &req)); + EXPECT_EQ(mode, SrtModePull); + EXPECT_STREQ(req.vhost.c_str(), "srs.srt.com.cn"); + EXPECT_STREQ(req.app.c_str(), "live"); + EXPECT_STREQ(req.stream.c_str(), "livestream"); + EXPECT_STREQ(req.param.c_str(), "vhost=srs.srt.com.cn&key1=val1&key2=val2"); + } + + if (true) { + SrtMode mode; + SrsRequest req; + EXPECT_TRUE(srs_srt_streamid_to_request("#!::h=live/livestream?key1=val1,key2=val2", mode, &req)); + EXPECT_EQ(mode, SrtModePull); + EXPECT_STREQ(req.vhost.c_str(), ""); + EXPECT_STREQ(req.app.c_str(), "live"); + EXPECT_STREQ(req.stream.c_str(), "livestream"); + EXPECT_STREQ(req.param.c_str(), "key1=val1&key2=val2"); + } + + if (true) { + SrtMode mode; + SrsRequest req; + EXPECT_TRUE(srs_srt_streamid_to_request("#!::h=srs.srt.com.cn/live/livestream?key1=val1,key2=val2", mode, &req)); + EXPECT_EQ(mode, SrtModePull); + EXPECT_STREQ(req.vhost.c_str(), "srs.srt.com.cn"); + EXPECT_STREQ(req.app.c_str(), "live"); + EXPECT_STREQ(req.stream.c_str(), "livestream"); + EXPECT_STREQ(req.param.c_str(), "vhost=srs.srt.com.cn&key1=val1&key2=val2"); + } +} + +// TODO: FIXME: add mpegts conn test +// set srt option, recv srt client, get srt client opt and check. +