mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
SRT: Support Coroutine Native SRT over ST. #3010
This commit is contained in:
commit
1af30dea32
52 changed files with 4457 additions and 3390 deletions
|
@ -63,10 +63,10 @@ RUN cd /srs/trunk && ./configure --jobs=2 --srt=on && make -j2
|
|||
########################################################
|
||||
FROM ossrs/srs:ubuntu16 AS ubuntu16-cross-armv7
|
||||
COPY . /srs
|
||||
RUN cd /srs/trunk && ./configure --jobs=2 --cross-build --cc=arm-linux-gnueabihf-gcc --cxx=arm-linux-gnueabihf-g++ \
|
||||
RUN cd /srs/trunk && ./configure --jobs=2 --srt=off --cross-build --cc=arm-linux-gnueabihf-gcc --cxx=arm-linux-gnueabihf-g++ \
|
||||
--ar=arm-linux-gnueabihf-ar --ld=arm-linux-gnueabihf-ld --randlib=arm-linux-gnueabihf-randlib && make -j2
|
||||
|
||||
FROM ossrs/srs:ubuntu16 AS ubuntu16-cross-aarch64
|
||||
COPY . /srs
|
||||
RUN cd /srs/trunk && ./configure --jobs=2 --cross-build --cc=aarch64-linux-gnu-gcc --cxx=aarch64-linux-gnu-g++ \
|
||||
RUN cd /srs/trunk && ./configure --jobs=2 --srt=off --cross-build --cc=aarch64-linux-gnu-gcc --cxx=aarch64-linux-gnu-g++ \
|
||||
--ar=aarch64-linux-gnu-ar --ld=aarch64-linux-gnu-ld --randlib=aarch64-linux-gnu-randlib && make -j2
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
help=no
|
||||
# feature options
|
||||
SRS_HDS=NO
|
||||
SRS_SRT=NO
|
||||
SRS_SRT=YES
|
||||
SRS_RTC=YES
|
||||
SRS_CXX11=YES
|
||||
SRS_CXX14=NO
|
||||
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
#############################################################################################
|
||||
|
|
|
@ -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;
|
||||
|
|
32
trunk/configure
vendored
32
trunk/configure
vendored
|
@ -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
|
||||
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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,21 +6764,8 @@ 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;
|
||||
SrsConfDirective* conf = root->get("srt_server");
|
||||
if (!conf) {
|
||||
return DEFAULT;
|
||||
}
|
||||
|
||||
conf = conf->get("mix_correct");
|
||||
if (!conf || conf->arg0().empty()) {
|
||||
return DEFAULT;
|
||||
}
|
||||
return SRS_CONF_PERFER_TRUE(conf->arg0());
|
||||
}
|
||||
|
||||
int SrsConfig::get_srto_maxbw() {
|
||||
int SrsConfig::get_srto_maxbw()
|
||||
{
|
||||
static int64_t DEFAULT = -1;
|
||||
SrsConfDirective* conf = root->get("srt_server");
|
||||
if (!conf) {
|
||||
|
@ -6784,7 +6779,8 @@ int SrsConfig::get_srto_maxbw() {
|
|||
return atoi(conf->arg0().c_str());
|
||||
}
|
||||
|
||||
int SrsConfig::get_srto_mss() {
|
||||
int SrsConfig::get_srto_mss()
|
||||
{
|
||||
static int DEFAULT = 1500;
|
||||
SrsConfDirective* conf = root->get("srt_server");
|
||||
if (!conf) {
|
||||
|
@ -6798,7 +6794,23 @@ int SrsConfig::get_srto_mss() {
|
|||
return atoi(conf->arg0().c_str());
|
||||
}
|
||||
|
||||
int SrsConfig::get_srto_latency() {
|
||||
bool SrsConfig::get_srto_tsbpdmode()
|
||||
{
|
||||
static bool DEFAULT = true;
|
||||
SrsConfDirective* conf = root->get("srt_server");
|
||||
if (!conf) {
|
||||
return DEFAULT;
|
||||
}
|
||||
|
||||
conf = conf->get("tsbpdmode");
|
||||
if (!conf || conf->arg0().empty()) {
|
||||
return DEFAULT;
|
||||
}
|
||||
return SRS_CONF_PERFER_TRUE(conf->arg0());
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
int SrsConfig::get_srto_sendbuf() {
|
||||
static int64_t DEFAULT = 8192 * (1500-28);
|
||||
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 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");
|
||||
|
|
|
@ -636,6 +636,8 @@ public:
|
|||
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 @@ public:
|
|||
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 @@ public:
|
|||
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:
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <srs_kernel_error.hpp>
|
||||
#include <srs_app_utility.hpp>
|
||||
#include <srs_kernel_utility.hpp>
|
||||
#include <srs_app_threads.hpp>
|
||||
|
||||
// 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;
|
||||
|
||||
mutex_ = new SrsMutex();
|
||||
}
|
||||
|
||||
SrsFileLog::~SrsFileLog()
|
||||
|
@ -49,6 +52,8 @@ SrsFileLog::~SrsFileLog()
|
|||
if (_srs_config) {
|
||||
_srs_config->unsubscribe(this);
|
||||
}
|
||||
|
||||
srs_freep(mutex_);
|
||||
}
|
||||
|
||||
srs_error_t SrsFileLog::initialize()
|
||||
|
@ -79,86 +84,96 @@ void SrsFileLog::reopen()
|
|||
|
||||
void SrsFileLog::verbose(const char* tag, SrsContextId context_id, const char* fmt, ...)
|
||||
{
|
||||
SrsAutoLock sl(mutex_);
|
||||
|
||||
if (level > SrsLogLevelVerbose) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
int size = 0;
|
||||
if (!srs_log_header(log_data, LOG_MAX_SIZE, utc, false, tag, context_id, "Verb", &size)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
// we reserved 1 bytes for the new line.
|
||||
size += vsnprintf(log_data + size, LOG_MAX_SIZE - size, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
|
||||
write_log(fd, log_data, size, SrsLogLevelVerbose);
|
||||
}
|
||||
|
||||
void SrsFileLog::info(const char* tag, SrsContextId context_id, const char* fmt, ...)
|
||||
{
|
||||
SrsAutoLock sl(mutex_);
|
||||
|
||||
if (level > SrsLogLevelInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
int size = 0;
|
||||
if (!srs_log_header(log_data, LOG_MAX_SIZE, utc, false, tag, context_id, "Debug", &size)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
// we reserved 1 bytes for the new line.
|
||||
size += vsnprintf(log_data + size, LOG_MAX_SIZE - size, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
|
||||
write_log(fd, log_data, size, SrsLogLevelInfo);
|
||||
}
|
||||
|
||||
void SrsFileLog::trace(const char* tag, SrsContextId context_id, const char* fmt, ...)
|
||||
{
|
||||
SrsAutoLock sl(mutex_);
|
||||
|
||||
if (level > SrsLogLevelTrace) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
int size = 0;
|
||||
if (!srs_log_header(log_data, LOG_MAX_SIZE, utc, false, tag, context_id, "Trace", &size)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
// we reserved 1 bytes for the new line.
|
||||
size += vsnprintf(log_data + size, LOG_MAX_SIZE - size, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
|
||||
write_log(fd, log_data, size, SrsLogLevelTrace);
|
||||
}
|
||||
|
||||
void SrsFileLog::warn(const char* tag, SrsContextId context_id, const char* fmt, ...)
|
||||
{
|
||||
SrsAutoLock sl(mutex_);
|
||||
|
||||
if (level > SrsLogLevelWarn) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
int size = 0;
|
||||
if (!srs_log_header(log_data, LOG_MAX_SIZE, utc, true, tag, context_id, "Warn", &size)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
// we reserved 1 bytes for the new line.
|
||||
size += vsnprintf(log_data + size, LOG_MAX_SIZE - size, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
|
||||
write_log(fd, log_data, size, SrsLogLevelWarn);
|
||||
}
|
||||
|
||||
void SrsFileLog::error(const char* tag, SrsContextId context_id, const char* fmt, ...)
|
||||
{
|
||||
SrsAutoLock sl(mutex_);
|
||||
|
||||
if (level > SrsLogLevelError) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include <srs_app_reload.hpp>
|
||||
#include <srs_protocol_log.hpp>
|
||||
|
||||
class SrsMutex;
|
||||
|
||||
// For log TAGs.
|
||||
#define TAG_MAIN "MAIN"
|
||||
#define TAG_MAYBE "MAYBE"
|
||||
|
@ -39,6 +41,9 @@ private:
|
|||
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.
|
||||
SrsMutex* mutex_;
|
||||
public:
|
||||
SrsFileLog();
|
||||
virtual ~SrsFileLog();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -130,6 +130,10 @@ public:
|
|||
// 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.
|
||||
|
|
|
@ -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,8 +344,8 @@ SrsRtcSource::~SrsRtcSource()
|
|||
// for all consumers are auto free.
|
||||
consumers.clear();
|
||||
|
||||
srs_freep(req);
|
||||
srs_freep(bridge_);
|
||||
srs_freep(req);
|
||||
srs_freep(stream_desc_);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
697
trunk/src/app/srs_app_srt_conn.cpp
Normal file
697
trunk/src/app/srs_app_srt_conn.cpp
Normal file
|
@ -0,0 +1,697 @@
|
|||
//
|
||||
// Copyright (c) 2013-2021 The SRS Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT or MulanPSL-2.0
|
||||
//
|
||||
|
||||
#include <srs_app_srt_conn.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#include <srs_kernel_buffer.hpp>
|
||||
#include <srs_kernel_flv.hpp>
|
||||
#include <srs_kernel_stream.hpp>
|
||||
#include <srs_core_autofree.hpp>
|
||||
#include <srs_protocol_rtmp_stack.hpp>
|
||||
#include <srs_protocol_srt.hpp>
|
||||
#include <srs_app_config.hpp>
|
||||
#include <srs_app_http_hooks.hpp>
|
||||
#include <srs_app_pithy_print.hpp>
|
||||
#include <srs_app_srt_server.hpp>
|
||||
#include <srs_app_srt_source.hpp>
|
||||
|
||||
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 *bridger = new SrsRtmpFromSrtBridge(live_source);
|
||||
if ((err = bridger->initialize(req_)) != srs_success) {
|
||||
srs_freep(bridger);
|
||||
return srs_error_wrap(err, "create bridger");
|
||||
}
|
||||
|
||||
srt_source_->set_bridge(bridger);
|
||||
}
|
||||
|
||||
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<string> 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<string> 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<string> 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<string> 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<string> 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<string> 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;
|
||||
}
|
126
trunk/src/app/srs_app_srt_conn.hpp
Normal file
126
trunk/src/app/srs_app_srt_conn.hpp
Normal file
|
@ -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 <srs_core.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <srs_protocol_srt.hpp>
|
||||
#include <srs_app_st.hpp>
|
||||
#include <srs_app_conn.hpp>
|
||||
#include <srs_app_srt_utility.hpp>
|
||||
|
||||
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
|
||||
|
101
trunk/src/app/srs_app_srt_listener.cpp
Normal file
101
trunk/src/app/srs_app_srt_listener.cpp
Normal file
|
@ -0,0 +1,101 @@
|
|||
//
|
||||
// Copyright (c) 2013-2021 The SRS Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT or MulanPSL-2.0
|
||||
//
|
||||
|
||||
#include <srs_app_srt_listener.hpp>
|
||||
|
||||
#include <st.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#include <srs_app_srt_server.hpp>
|
||||
|
||||
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;
|
||||
}
|
53
trunk/src/app/srs_app_srt_listener.hpp
Normal file
53
trunk/src/app/srs_app_srt_listener.hpp
Normal file
|
@ -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 <srs_core.hpp>
|
||||
#include <srs_app_st.hpp>
|
||||
#include <srs_protocol_srt.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
// 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
|
||||
|
375
trunk/src/app/srs_app_srt_server.cpp
Normal file
375
trunk/src/app/srs_app_srt_server.cpp
Normal file
|
@ -0,0 +1,375 @@
|
|||
//
|
||||
// Copyright (c) 2013-2021 The SRS Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT or MulanPSL-2.0
|
||||
//
|
||||
|
||||
#include <srs_app_srt_server.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#include <srs_kernel_log.hpp>
|
||||
#include <srs_kernel_utility.hpp>
|
||||
#include <srs_protocol_log.hpp>
|
||||
#include <srs_app_config.hpp>
|
||||
#include <srs_app_srt_conn.hpp>
|
||||
|
||||
#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<SrsSrtAcceptor*>::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<ISrsStartableConneciton*>(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;
|
||||
}
|
||||
|
110
trunk/src/app/srs_app_srt_server.hpp
Normal file
110
trunk/src/app/srs_app_srt_server.hpp
Normal file
|
@ -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 <srs_core.hpp>
|
||||
|
||||
#include <srs_protocol_srt.hpp>
|
||||
#include <srs_app_server.hpp>
|
||||
#include <srs_app_srt_listener.hpp>
|
||||
|
||||
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<SrsSrtAcceptor*> 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
|
||||
|
796
trunk/src/app/srs_app_srt_source.cpp
Normal file
796
trunk/src/app/srs_app_srt_source.cpp
Normal file
|
@ -0,0 +1,796 @@
|
|||
//
|
||||
// Copyright (c) 2013-2021 The SRS Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT or MulanPSL-2.0
|
||||
//
|
||||
|
||||
#include <srs_app_srt_source.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
using namespace std;
|
||||
|
||||
#include <srs_kernel_flv.hpp>
|
||||
#include <srs_kernel_utility.hpp>
|
||||
#include <srs_kernel_buffer.hpp>
|
||||
#include <srs_kernel_stream.hpp>
|
||||
#include <srs_core_autofree.hpp>
|
||||
#include <srs_protocol_raw_avc.hpp>
|
||||
#include <srs_protocol_rtmp_stack.hpp>
|
||||
#include <srs_app_source.hpp>
|
||||
#include <srs_app_statistic.hpp>
|
||||
|
||||
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<SrsSrtPacket*>::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<pair<char*, int> > 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<pair<char*, int> >& 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<SrsSrtConsumer*>::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<SrsSrtConsumer*>::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;
|
||||
}
|
||||
|
186
trunk/src/app/srs_app_srt_source.hpp
Normal file
186
trunk/src/app/srs_app_srt_source.hpp
Normal file
|
@ -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 <srs_core.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <srs_kernel_ts.hpp>
|
||||
#include <srs_protocol_st.hpp>
|
||||
#include <srs_app_source.hpp>
|
||||
|
||||
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<std::string, SrsSrtSource*> 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<SrsSrtPacket*> 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<std::pair<char*, int> >& 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 *bridger);
|
||||
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<SrsSrtConsumer*> consumers;
|
||||
bool can_publish_;
|
||||
ISrsSrtSourceBridge* bridge_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -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 <vector>
|
||||
#include <srs_app_srt_utility.hpp>
|
||||
|
||||
#include <srs_protocol_utility.hpp>
|
||||
#include <srs_kernel_utility.hpp>
|
||||
using namespace std;
|
||||
|
||||
#include <srs_kernel_log.hpp>
|
||||
#include <srs_app_config.hpp>
|
||||
|
||||
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<std::string> 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;
|
||||
}
|
||||
#include <srs_kernel_utility.hpp>
|
||||
#include <srs_kernel_error.hpp>
|
||||
#include <srs_protocol_utility.hpp>
|
||||
#include <srs_protocol_rtmp_stack.hpp>
|
||||
#include <srs_kernel_utility.hpp>
|
||||
|
||||
// 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 {
|
||||
|
@ -162,102 +107,37 @@ bool get_streamid_info(const std::string& streamid, int& mode, std::string& vhos
|
|||
if (!params.empty()) {
|
||||
url_subpath.append("?");
|
||||
url_subpath.append(params);
|
||||
url_subpath.pop_back(); // remove last '&'
|
||||
url_subpath = url_subpath.substr(0, url_subpath.length() - 1); // remove last '&'
|
||||
}
|
||||
|
||||
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__";
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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);
|
||||
string url_subpath = "";
|
||||
bool ret = srs_srt_streamid_info(streamid, mode, request->vhost, url_subpath);
|
||||
if (! ret) {
|
||||
return ret;
|
||||
}
|
||||
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;
|
||||
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);
|
||||
}
|
||||
write_fail_cnt_ = 0;
|
||||
|
||||
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::get_write_fail_count() {
|
||||
return write_fail_cnt_;
|
||||
}
|
32
trunk/src/app/srs_app_srt_utility.hpp
Normal file
32
trunk/src/app/srs_app_srt_utility.hpp
Normal file
|
@ -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 <srs_core.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <srs_kernel_log.hpp>
|
||||
#include <srs_protocol_utility.hpp>
|
||||
|
||||
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
|
||||
|
|
@ -22,6 +22,11 @@
|
|||
#include <srs_app_rtc_conn.hpp>
|
||||
#endif
|
||||
|
||||
#ifdef SRS_SRT
|
||||
#include <srs_app_srt_source.hpp>
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
using namespace std;
|
||||
|
||||
|
@ -298,6 +303,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();
|
||||
|
@ -419,3 +428,38 @@ srs_error_t srs_thread_initialize()
|
|||
return err;
|
||||
}
|
||||
|
||||
SrsMutex::SrsMutex()
|
||||
{
|
||||
int rc = pthread_mutex_init(&mutex_, NULL);
|
||||
srs_assert(!rc);
|
||||
}
|
||||
|
||||
SrsMutex::~SrsMutex()
|
||||
{
|
||||
int rc = pthread_mutex_destroy(&mutex_);
|
||||
srs_assert(!rc);
|
||||
}
|
||||
|
||||
void SrsMutex::lock()
|
||||
{
|
||||
int rc = pthread_mutex_lock(&mutex_);
|
||||
srs_assert(!rc);
|
||||
}
|
||||
|
||||
void SrsMutex::unlock()
|
||||
{
|
||||
int rc = pthread_mutex_unlock(&mutex_);
|
||||
srs_assert(!rc);
|
||||
}
|
||||
|
||||
SrsAutoLock::SrsAutoLock(SrsMutex* mutex)
|
||||
{
|
||||
mutex_ = mutex;
|
||||
mutex_->lock();
|
||||
}
|
||||
|
||||
SrsAutoLock::~SrsAutoLock()
|
||||
{
|
||||
mutex_->unlock();
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include <srs_app_hourglass.hpp>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
// Protect server in high load.
|
||||
class SrsCircuitBreaker : public ISrsFastTimer
|
||||
{
|
||||
|
@ -49,5 +51,28 @@ extern SrsCircuitBreaker* _srs_circuit_breaker;
|
|||
// Initialize global or thread-local variables.
|
||||
extern srs_error_t srs_thread_initialize();
|
||||
|
||||
// Wrapper for mutex.
|
||||
class SrsMutex
|
||||
{
|
||||
private:
|
||||
pthread_mutex_t mutex_;
|
||||
public:
|
||||
SrsMutex();
|
||||
~SrsMutex();
|
||||
public:
|
||||
void lock();
|
||||
void unlock();
|
||||
};
|
||||
|
||||
// Lock the mutex when enter current scope, and unlock it when out.
|
||||
class SrsAutoLock
|
||||
{
|
||||
private:
|
||||
SrsMutex* mutex_;
|
||||
public:
|
||||
SrsAutoLock(SrsMutex* mutex);
|
||||
~SrsAutoLock();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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.
|
||||
///////////////////////////////////////////////////////
|
||||
|
|
|
@ -44,7 +44,8 @@ using namespace std;
|
|||
#endif
|
||||
|
||||
#ifdef SRS_SRT
|
||||
#include <srt_server.hpp>
|
||||
#include <srs_protocol_srt.hpp>
|
||||
#include <srs_app_srt_server.hpp>
|
||||
#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
|
||||
|
|
970
trunk/src/protocol/srs_protocol_srt.cpp
Normal file
970
trunk/src/protocol/srs_protocol_srt.cpp
Normal file
|
@ -0,0 +1,970 @@
|
|||
//
|
||||
// Copyright (c) 2013-2021 The SRS Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT or MulanPSL-2.0
|
||||
//
|
||||
|
||||
#include <srs_protocol_srt.hpp>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#include <srs_kernel_error.hpp>
|
||||
#include <srs_kernel_log.hpp>
|
||||
#include <srs_core_autofree.hpp>
|
||||
|
||||
#include <srt/srt.h>
|
||||
|
||||
// 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<srs_srt_t, SrsSrtSocket*> fd_sockets_;
|
||||
int srt_epoller_fd_;
|
||||
std::vector<SRT_EPOLL_EVENT> 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<srs_srt_t, SrsSrtSocket*>::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;
|
||||
}
|
||||
|
180
trunk/src/protocol/srs_protocol_srt.hpp
Normal file
180
trunk/src/protocol/srs_protocol_srt.hpp
Normal file
|
@ -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 <srs_core.hpp>
|
||||
#include <srs_protocol_st.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
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
|
||||
|
|
@ -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 <srs_core.hpp>
|
||||
|
||||
#include "stringex.hpp"
|
||||
#include <srt/srt.h>
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <srs_kernel_log.hpp>
|
||||
#include <srs_kernel_error.hpp>
|
||||
#include <srs_app_rtmp_conn.hpp>
|
||||
#include <srs_app_config.hpp>
|
||||
|
||||
#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> SRT_CONN_PTR;
|
||||
|
||||
#endif //SRT_CONN_H
|
|
@ -1,71 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2013-2021 The SRS Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT or MulanPSL-2.0
|
||||
//
|
||||
|
||||
#include "srt_data.hpp"
|
||||
#include <string.h>
|
||||
|
||||
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();
|
||||
}
|
|
@ -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 <srs_core.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#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> SRT_DATA_MSG_PTR;
|
||||
|
||||
#endif
|
|
@ -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 <srs_kernel_log.hpp>
|
||||
#include <srs_kernel_error.hpp>
|
||||
#include <srs_app_rtmp_conn.hpp>
|
||||
#include <srs_app_config.hpp>
|
||||
|
||||
#include <srt/udt.h>
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <assert.h>
|
||||
#include <list>
|
||||
|
||||
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, SRT_CONN_PTR> 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<std::mutex> 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<SRTSOCKET, SRT_CONN_PTR>
|
||||
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<SRTSOCKET> 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<SRT_CONN_PTR> 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;
|
||||
}
|
|
@ -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 <srs_core.hpp>
|
||||
|
||||
#include <srt/srt.h>
|
||||
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <queue>
|
||||
#include <string.h>
|
||||
#include <mutex>
|
||||
|
||||
#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: <socket fd, pusher conn ptr>
|
||||
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<SRTSOCKET, SRT_CONN_PTR> _conn_map;//save all srt connection: pull or push
|
||||
|
||||
//save push srt connection for prevent from repeat push connection
|
||||
std::unordered_map<std::string, SRT_CONN_PTR> _push_conn_map;//key:streamid, value:SRT_CONN_PTR
|
||||
//streamid, play map<SRTSOCKET, SRT_CONN_PTR>
|
||||
std::unordered_map<std::string, std::unordered_map<SRTSOCKET, SRT_CONN_PTR>> _streamid_map;
|
||||
|
||||
long long _last_timestamp;
|
||||
long long _last_check_alive_ts;
|
||||
};
|
||||
|
||||
#endif //SRT_HANDLE_H
|
|
@ -1,36 +0,0 @@
|
|||
#include "srt_log.hpp"
|
||||
#include "srt_to_rtmp.hpp"
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
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;
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
#ifndef SRT_LOG_HPP
|
||||
#define SRT_LOG_HPP
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#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
|
|
@ -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 <srt/udt.h>
|
||||
#include <thread>
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <srs_kernel_log.hpp>
|
||||
#include <srs_kernel_error.hpp>
|
||||
#include <srs_app_rtmp_conn.hpp>
|
||||
#include <srs_app_config.hpp>
|
||||
#include <srs_app_utility.hpp>
|
||||
|
||||
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<srt_handle>(_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<std::thread>(&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<srt_conn>(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_server>(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.
|
||||
}
|
|
@ -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 <srs_core.hpp>
|
||||
|
||||
#include <srt/srt.h>
|
||||
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
|
||||
#include <srs_app_hybrid.hpp>
|
||||
|
||||
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<std::thread> thread_run_ptr;
|
||||
std::shared_ptr<srt_handle> _handle_ptr;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<srt_server> 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
|
|
@ -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 <srs_kernel_log.hpp>
|
||||
#include <srs_kernel_error.hpp>
|
||||
#include <srs_kernel_buffer.hpp>
|
||||
#include <srs_kernel_utility.hpp>
|
||||
#include <srs_app_rtmp_conn.hpp>
|
||||
#include <srs_app_config.hpp>
|
||||
#include <srs_kernel_stream.hpp>
|
||||
#include <list>
|
||||
#include <sstream>
|
||||
|
||||
std::shared_ptr<srt2rtmp> srt2rtmp::s_srt2rtmp_ptr;
|
||||
std::mutex srt2rtmp::_srt_error_mutex;
|
||||
std::map<std::string, int> srt2rtmp::_srt_error_map;
|
||||
|
||||
std::shared_ptr<srt2rtmp> srt2rtmp::get_instance() {
|
||||
if (!s_srt2rtmp_ptr) {
|
||||
s_srt2rtmp_ptr = std::make_shared<srt2rtmp>();
|
||||
}
|
||||
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<SrsSTCoroutine>("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<std::mutex> locker(_mutex);
|
||||
|
||||
SRT_DATA_MSG_PTR msg_ptr = std::make_shared<SRT_DATA_MSG>(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<std::mutex> locker(_mutex);
|
||||
|
||||
SRT_DATA_MSG_PTR msg_ptr = std::make_shared<SRT_DATA_MSG>(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<std::mutex> locker(_mutex);
|
||||
|
||||
SRT_DATA_MSG_PTR msg_ptr = std::make_shared<SRT_DATA_MSG>(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<std::mutex> 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<std::mutex> 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<rtmp_client>(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<ts_demux>();
|
||||
_avc_ptr = std::make_shared<SrsRawH264Stream>();
|
||||
_aac_ptr = std::make_shared<SrsRawAacStream>();
|
||||
std::vector<std::string> 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<std::string> 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<SrsSimpleRtmpClient>(_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<SrsBuffer> 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<SrsBuffer> 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<SrsBuffer>((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;
|
||||
}
|
|
@ -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 <srs_core.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <queue>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <srs_kernel_ts.hpp>
|
||||
#include <srs_app_rtmp_conn.hpp>
|
||||
#include <srs_protocol_raw_avc.hpp>
|
||||
#include <srs_protocol_utility.hpp>
|
||||
#include <unordered_map>
|
||||
|
||||
#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<SrsSimpleRtmpClient> RTMP_CONN_PTR;
|
||||
typedef std::shared_ptr<SrsRawH264Stream> AVC_PTR;
|
||||
typedef std::shared_ptr<SrsRawAacStream> 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<int64_t, rtmp_packet_info_s> _send_map;//key:dts, value:rtmp_packet_info
|
||||
};
|
||||
|
||||
class rtmp_client : public ts_media_data_callback_I, public std::enable_shared_from_this<rtmp_client> {
|
||||
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<SrsBuffer> avs_ptr, uint64_t dts, uint64_t pts);
|
||||
srs_error_t on_ts_audio(std::shared_ptr<SrsBuffer> 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> RTMP_CLIENT_PTR;
|
||||
|
||||
class srt2rtmp : public ISrsCoroutineHandler {
|
||||
public:
|
||||
static std::shared_ptr<srt2rtmp> 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<srt2rtmp> s_srt2rtmp_ptr;
|
||||
std::shared_ptr<SrsCoroutine> _trd_ptr;
|
||||
std::mutex _mutex;
|
||||
//std::condition_variable_any _notify_cond;
|
||||
std::queue<SRT_DATA_MSG_PTR> _msg_queue;
|
||||
|
||||
std::unordered_map<std::string, RTMP_CLIENT_PTR> _rtmp_client_map;
|
||||
int64_t _lastcheck_ts;
|
||||
public:
|
||||
static std::mutex _srt_error_mutex;
|
||||
static std::map<std::string, int> _srt_error_map;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -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 <srs_core.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <cctype>
|
||||
|
||||
inline int string_split(const std::string& input_str, const std::string& split_str, std::vector<std::string>& 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
|
|
@ -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 <srs_core.hpp>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
inline long long now_ms() {
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
}
|
||||
|
||||
#endif //TIME_HELP_H
|
|
@ -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 <string.h>
|
||||
|
||||
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<SRT_DATA_MSG>(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<SRT_DATA_MSG>(_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;
|
||||
}
|
|
@ -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 <srs_core.hpp>
|
||||
|
||||
#include "srt_data.hpp"
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
/* 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_media_data_callback_I> 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_INFO> _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<unsigned short, unsigned char> _pid2steamtype;
|
||||
std::vector<STREAM_PID_INFO> _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<SRT_DATA_MSG_PTR> _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> TS_DEMUX_PTR;
|
||||
|
||||
#endif
|
|
@ -1,61 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2013-2021 The SRS Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT or MulanPSL-2.0
|
||||
//
|
||||
|
||||
#include "ts_demux.hpp"
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#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<media_data_get>();
|
||||
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<SRT_DATA_MSG>((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;
|
||||
}
|
|
@ -17,6 +17,10 @@
|
|||
#include <string>
|
||||
using namespace std;
|
||||
|
||||
#ifdef SRS_SRT
|
||||
#include <srs_app_srt_server.hpp>
|
||||
#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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -6,84 +6,463 @@
|
|||
#include <srs_utest_srt.hpp>
|
||||
|
||||
#include <srs_kernel_error.hpp>
|
||||
#include <srt_conn.hpp>
|
||||
#include <srs_kernel_utility.hpp>
|
||||
#include <srs_protocol_srt.hpp>
|
||||
#include <srs_protocol_rtmp_stack.hpp>
|
||||
#include <srs_app_srt_utility.hpp>
|
||||
#include <srs_app_srt_server.hpp>
|
||||
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
|
||||
VOID TEST(ProtocolSrtTest, SrtGetStreamInfoNormal) {
|
||||
#include <srt/srt.h>
|
||||
|
||||
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) {
|
||||
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);
|
||||
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) {
|
||||
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.
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue