mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
Copy 4.0release
This commit is contained in:
parent
00395588bc
commit
5e3e013c60
183 changed files with 27373 additions and 13949 deletions
|
@ -11,11 +11,11 @@ echo "#ifndef SRS_AUTO_HEADER_HPP" >> $SRS_AUTO_HEADERS_H
|
||||||
echo "#define SRS_AUTO_HEADER_HPP" >> $SRS_AUTO_HEADERS_H
|
echo "#define SRS_AUTO_HEADER_HPP" >> $SRS_AUTO_HEADERS_H
|
||||||
echo "" >> $SRS_AUTO_HEADERS_H
|
echo "" >> $SRS_AUTO_HEADERS_H
|
||||||
|
|
||||||
echo "#define SRS_AUTO_BUILD_TS \"`date +%s`\"" >> $SRS_AUTO_HEADERS_H
|
echo "#define SRS_BUILD_TS \"`date +%s`\"" >> $SRS_AUTO_HEADERS_H
|
||||||
echo "#define SRS_AUTO_BUILD_DATE \"`date \"+%Y-%m-%d %H:%M:%S\"`\"" >> $SRS_AUTO_HEADERS_H
|
echo "#define SRS_BUILD_DATE \"`date \"+%Y-%m-%d %H:%M:%S\"`\"" >> $SRS_AUTO_HEADERS_H
|
||||||
echo "#define SRS_AUTO_UNAME \"`uname -a`\"" >> $SRS_AUTO_HEADERS_H
|
echo "#define SRS_UNAME \"`uname -a`\"" >> $SRS_AUTO_HEADERS_H
|
||||||
echo "#define SRS_AUTO_USER_CONFIGURE \"${SRS_AUTO_USER_CONFIGURE}\"" >> $SRS_AUTO_HEADERS_H
|
echo "#define SRS_USER_CONFIGURE \"${SRS_AUTO_USER_CONFIGURE}\"" >> $SRS_AUTO_HEADERS_H
|
||||||
echo "#define SRS_AUTO_CONFIGURE \"${SRS_AUTO_CONFIGURE}\"" >> $SRS_AUTO_HEADERS_H
|
echo "#define SRS_CONFIGURE \"${SRS_AUTO_CONFIGURE}\"" >> $SRS_AUTO_HEADERS_H
|
||||||
echo "" >> $SRS_AUTO_HEADERS_H
|
echo "" >> $SRS_AUTO_HEADERS_H
|
||||||
|
|
||||||
function srs_define_macro()
|
function srs_define_macro()
|
||||||
|
@ -49,11 +49,7 @@ fi
|
||||||
if [ $SRS_CUBIE = YES ]; then
|
if [ $SRS_CUBIE = YES ]; then
|
||||||
srs_define_macro "SRS_CUBIE" $SRS_AUTO_HEADERS_H
|
srs_define_macro "SRS_CUBIE" $SRS_AUTO_HEADERS_H
|
||||||
fi
|
fi
|
||||||
if [ $SRS_EXPORT_LIBRTMP_PROJECT != NO ]; then
|
echo "#undef SRS_EXPORT_LIBRTMP" >> $SRS_AUTO_HEADERS_H
|
||||||
echo "#define SRS_EXPORT_LIBRTMP" >> $SRS_AUTO_HEADERS_H
|
|
||||||
else
|
|
||||||
echo "#undef SRS_EXPORT_LIBRTMP" >> $SRS_AUTO_HEADERS_H
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "" >> $SRS_AUTO_HEADERS_H
|
echo "" >> $SRS_AUTO_HEADERS_H
|
||||||
|
|
||||||
|
@ -62,73 +58,103 @@ echo "" >> $SRS_AUTO_HEADERS_H
|
||||||
#####################################################################################
|
#####################################################################################
|
||||||
# auto headers in depends.
|
# auto headers in depends.
|
||||||
if [ $SRS_HDS = YES ]; then
|
if [ $SRS_HDS = YES ]; then
|
||||||
srs_define_macro "SRS_AUTO_HDS" $SRS_AUTO_HEADERS_H
|
srs_define_macro "SRS_HDS" $SRS_AUTO_HEADERS_H
|
||||||
else
|
else
|
||||||
srs_undefine_macro "SRS_AUTO_HDS" $SRS_AUTO_HEADERS_H
|
srs_undefine_macro "SRS_HDS" $SRS_AUTO_HEADERS_H
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $SRS_SRT = YES ]; then
|
if [ $SRS_SRT = YES ]; then
|
||||||
srs_define_macro "SRS_AUTO_SRT" $SRS_AUTO_HEADERS_H
|
srs_define_macro "SRS_SRT" $SRS_AUTO_HEADERS_H
|
||||||
else
|
else
|
||||||
srs_undefine_macro "SRS_AUTO_SRT" $SRS_AUTO_HEADERS_H
|
srs_undefine_macro "SRS_SRT" $SRS_AUTO_HEADERS_H
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $SRS_CXX11 = YES ]; then
|
||||||
|
srs_define_macro "SRS_CXX11" $SRS_AUTO_HEADERS_H
|
||||||
|
else
|
||||||
|
srs_undefine_macro "SRS_CXX11" $SRS_AUTO_HEADERS_H
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $SRS_CXX14 = YES ]; then
|
||||||
|
srs_define_macro "SRS_CXX14" $SRS_AUTO_HEADERS_H
|
||||||
|
else
|
||||||
|
srs_undefine_macro "SRS_CXX14" $SRS_AUTO_HEADERS_H
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $SRS_RTC = YES ]; then
|
if [ $SRS_RTC = YES ]; then
|
||||||
srs_define_macro "SRS_AUTO_RTC" $SRS_AUTO_HEADERS_H
|
srs_define_macro "SRS_RTC" $SRS_AUTO_HEADERS_H
|
||||||
else
|
else
|
||||||
srs_undefine_macro "SRS_AUTO_RTC" $SRS_AUTO_HEADERS_H
|
srs_undefine_macro "SRS_RTC" $SRS_AUTO_HEADERS_H
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $SRS_FFMPEG_FIT = YES ]; then
|
||||||
|
srs_define_macro "SRS_FFMPEG_FIT" $SRS_AUTO_HEADERS_H
|
||||||
|
else
|
||||||
|
srs_undefine_macro "SRS_FFMPEG_FIT" $SRS_AUTO_HEADERS_H
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $SRS_SIMULATOR = YES ]; then
|
||||||
|
srs_define_macro "SRS_SIMULATOR" $SRS_AUTO_HEADERS_H
|
||||||
|
else
|
||||||
|
srs_undefine_macro "SRS_SIMULATOR" $SRS_AUTO_HEADERS_H
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $SRS_GB28181 = YES ]; then
|
if [ $SRS_GB28181 = YES ]; then
|
||||||
srs_define_macro "SRS_AUTO_GB28181" $SRS_AUTO_HEADERS_H
|
srs_define_macro "SRS_GB28181" $SRS_AUTO_HEADERS_H
|
||||||
else
|
else
|
||||||
srs_undefine_macro "SRS_AUTO_GB28181" $SRS_AUTO_HEADERS_H
|
srs_undefine_macro "SRS_GB28181" $SRS_AUTO_HEADERS_H
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $SRS_HTTPS = YES ]; then
|
||||||
|
srs_define_macro "SRS_HTTPS" $SRS_AUTO_HEADERS_H
|
||||||
|
else
|
||||||
|
srs_undefine_macro "SRS_HTTPS" $SRS_AUTO_HEADERS_H
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $SRS_MEM_WATCH = YES ]; then
|
if [ $SRS_MEM_WATCH = YES ]; then
|
||||||
srs_define_macro "SRS_AUTO_MEM_WATCH" $SRS_AUTO_HEADERS_H
|
srs_define_macro "SRS_MEM_WATCH" $SRS_AUTO_HEADERS_H
|
||||||
else
|
else
|
||||||
srs_undefine_macro "SRS_AUTO_MEM_WATCH" $SRS_AUTO_HEADERS_H
|
srs_undefine_macro "SRS_MEM_WATCH" $SRS_AUTO_HEADERS_H
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $SRS_UTEST = YES ]; then
|
if [ $SRS_UTEST = YES ]; then
|
||||||
srs_define_macro "SRS_AUTO_UTEST" $SRS_AUTO_HEADERS_H
|
srs_define_macro "SRS_UTEST" $SRS_AUTO_HEADERS_H
|
||||||
else
|
else
|
||||||
srs_undefine_macro "SRS_AUTO_UTEST" $SRS_AUTO_HEADERS_H
|
srs_undefine_macro "SRS_UTEST" $SRS_AUTO_HEADERS_H
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# whatever the FFMPEG tools, if transcode and ingest specified,
|
# whatever the FFMPEG tools, if transcode and ingest specified,
|
||||||
# srs always compile the FFMPEG tool stub which used to start the FFMPEG process.
|
# srs always compile the FFMPEG tool stub which used to start the FFMPEG process.
|
||||||
if [ $SRS_FFMPEG_STUB = YES ]; then
|
if [ $SRS_FFMPEG_STUB = YES ]; then
|
||||||
srs_define_macro "SRS_AUTO_FFMPEG_STUB" $SRS_AUTO_HEADERS_H
|
srs_define_macro "SRS_FFMPEG_STUB" $SRS_AUTO_HEADERS_H
|
||||||
else
|
else
|
||||||
srs_undefine_macro "SRS_AUTO_FFMPEG_STUB" $SRS_AUTO_HEADERS_H
|
srs_undefine_macro "SRS_FFMPEG_STUB" $SRS_AUTO_HEADERS_H
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $SRS_GPERF = YES ]; then
|
if [ $SRS_GPERF = YES ]; then
|
||||||
srs_define_macro "SRS_AUTO_GPERF" $SRS_AUTO_HEADERS_H
|
srs_define_macro "SRS_GPERF" $SRS_AUTO_HEADERS_H
|
||||||
else
|
else
|
||||||
srs_undefine_macro "SRS_AUTO_GPERF" $SRS_AUTO_HEADERS_H
|
srs_undefine_macro "SRS_GPERF" $SRS_AUTO_HEADERS_H
|
||||||
fi
|
fi
|
||||||
if [ $SRS_GPERF_MC = YES ]; then
|
if [ $SRS_GPERF_MC = YES ]; then
|
||||||
srs_define_macro "SRS_AUTO_GPERF_MC" $SRS_AUTO_HEADERS_H
|
srs_define_macro "SRS_GPERF_MC" $SRS_AUTO_HEADERS_H
|
||||||
else
|
else
|
||||||
srs_undefine_macro "SRS_AUTO_GPERF_MC" $SRS_AUTO_HEADERS_H
|
srs_undefine_macro "SRS_GPERF_MC" $SRS_AUTO_HEADERS_H
|
||||||
fi
|
fi
|
||||||
if [ $SRS_GPERF_MD = YES ]; then
|
if [ $SRS_GPERF_MD = YES ]; then
|
||||||
srs_define_macro "SRS_AUTO_GPERF_MD" $SRS_AUTO_HEADERS_H
|
srs_define_macro "SRS_GPERF_MD" $SRS_AUTO_HEADERS_H
|
||||||
else
|
else
|
||||||
srs_undefine_macro "SRS_AUTO_GPERF_MD" $SRS_AUTO_HEADERS_H
|
srs_undefine_macro "SRS_GPERF_MD" $SRS_AUTO_HEADERS_H
|
||||||
fi
|
fi
|
||||||
if [ $SRS_GPERF_MP = YES ]; then
|
if [ $SRS_GPERF_MP = YES ]; then
|
||||||
srs_define_macro "SRS_AUTO_GPERF_MP" $SRS_AUTO_HEADERS_H
|
srs_define_macro "SRS_GPERF_MP" $SRS_AUTO_HEADERS_H
|
||||||
else
|
else
|
||||||
srs_undefine_macro "SRS_AUTO_GPERF_MP" $SRS_AUTO_HEADERS_H
|
srs_undefine_macro "SRS_GPERF_MP" $SRS_AUTO_HEADERS_H
|
||||||
fi
|
fi
|
||||||
if [ $SRS_GPERF_CP = YES ]; then
|
if [ $SRS_GPERF_CP = YES ]; then
|
||||||
srs_define_macro "SRS_AUTO_GPERF_CP" $SRS_AUTO_HEADERS_H
|
srs_define_macro "SRS_GPERF_CP" $SRS_AUTO_HEADERS_H
|
||||||
else
|
else
|
||||||
srs_undefine_macro "SRS_AUTO_GPERF_CP" $SRS_AUTO_HEADERS_H
|
srs_undefine_macro "SRS_GPERF_CP" $SRS_AUTO_HEADERS_H
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#####################################################################################
|
#####################################################################################
|
||||||
|
@ -136,50 +162,40 @@ fi
|
||||||
#####################################################################################
|
#####################################################################################
|
||||||
# for log level compile settings
|
# for log level compile settings
|
||||||
if [ $SRS_LOG_VERBOSE = YES ]; then
|
if [ $SRS_LOG_VERBOSE = YES ]; then
|
||||||
srs_define_macro "SRS_AUTO_VERBOSE" $SRS_AUTO_HEADERS_H
|
srs_define_macro "SRS_VERBOSE" $SRS_AUTO_HEADERS_H
|
||||||
else
|
else
|
||||||
srs_undefine_macro "SRS_AUTO_VERBOSE" $SRS_AUTO_HEADERS_H
|
srs_undefine_macro "SRS_VERBOSE" $SRS_AUTO_HEADERS_H
|
||||||
fi
|
fi
|
||||||
if [ $SRS_LOG_INFO = YES ]; then
|
if [ $SRS_LOG_INFO = YES ]; then
|
||||||
srs_define_macro "SRS_AUTO_INFO" $SRS_AUTO_HEADERS_H
|
srs_define_macro "SRS_INFO" $SRS_AUTO_HEADERS_H
|
||||||
else
|
else
|
||||||
srs_undefine_macro "SRS_AUTO_INFO" $SRS_AUTO_HEADERS_H
|
srs_undefine_macro "SRS_INFO" $SRS_AUTO_HEADERS_H
|
||||||
fi
|
fi
|
||||||
if [ $SRS_LOG_TRACE = YES ]; then
|
if [ $SRS_LOG_TRACE = YES ]; then
|
||||||
srs_define_macro "SRS_AUTO_TRACE" $SRS_AUTO_HEADERS_H
|
srs_define_macro "SRS_TRACE" $SRS_AUTO_HEADERS_H
|
||||||
else
|
else
|
||||||
srs_undefine_macro "SRS_AUTO_TRACE" $SRS_AUTO_HEADERS_H
|
srs_undefine_macro "SRS_TRACE" $SRS_AUTO_HEADERS_H
|
||||||
fi
|
fi
|
||||||
if [ $SRS_CROSS_BUILD = YES ]; then
|
if [ $SRS_CROSS_BUILD = YES ]; then
|
||||||
srs_define_macro "SRS_AUTO_CROSSBUILD" $SRS_AUTO_HEADERS_H
|
srs_define_macro "SRS_CROSSBUILD" $SRS_AUTO_HEADERS_H
|
||||||
else
|
else
|
||||||
srs_undefine_macro "SRS_AUTO_CROSSBUILD" $SRS_AUTO_HEADERS_H
|
srs_undefine_macro "SRS_CROSSBUILD" $SRS_AUTO_HEADERS_H
|
||||||
fi
|
fi
|
||||||
if [ $SRS_OSX = YES ]; then
|
if [ $SRS_OSX = YES ]; then
|
||||||
srs_define_macro "SRS_AUTO_OSX" $SRS_AUTO_HEADERS_H
|
srs_define_macro "SRS_OSX" $SRS_AUTO_HEADERS_H
|
||||||
else
|
else
|
||||||
srs_undefine_macro "SRS_AUTO_OSX" $SRS_AUTO_HEADERS_H
|
srs_undefine_macro "SRS_OSX" $SRS_AUTO_HEADERS_H
|
||||||
fi
|
|
||||||
if [ $SRS_SENDMMSG = YES ]; then
|
|
||||||
srs_define_macro "SRS_AUTO_SENDMMSG" $SRS_AUTO_HEADERS_H
|
|
||||||
else
|
|
||||||
srs_undefine_macro "SRS_AUTO_SENDMMSG" $SRS_AUTO_HEADERS_H
|
|
||||||
fi
|
|
||||||
if [ $SRS_HAS_SENDMMSG = YES ]; then
|
|
||||||
srs_define_macro "SRS_AUTO_HAS_SENDMMSG" $SRS_AUTO_HEADERS_H
|
|
||||||
else
|
|
||||||
srs_undefine_macro "SRS_AUTO_HAS_SENDMMSG" $SRS_AUTO_HEADERS_H
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $SRS_DEBUG = YES ]; then
|
if [ $SRS_DEBUG = YES ]; then
|
||||||
srs_define_macro "SRS_AUTO_DEBUG" $SRS_AUTO_HEADERS_H
|
srs_define_macro "SRS_DEBUG" $SRS_AUTO_HEADERS_H
|
||||||
else
|
else
|
||||||
srs_undefine_macro "SRS_AUTO_DEBUG" $SRS_AUTO_HEADERS_H
|
srs_undefine_macro "SRS_DEBUG" $SRS_AUTO_HEADERS_H
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# prefix
|
# prefix
|
||||||
echo "" >> $SRS_AUTO_HEADERS_H
|
echo "" >> $SRS_AUTO_HEADERS_H
|
||||||
echo "#define SRS_AUTO_PREFIX \"${SRS_PREFIX}\"" >> $SRS_AUTO_HEADERS_H
|
echo "#define SRS_PREFIX \"${SRS_PREFIX}\"" >> $SRS_AUTO_HEADERS_H
|
||||||
|
|
||||||
echo "" >> $SRS_AUTO_HEADERS_H
|
echo "" >> $SRS_AUTO_HEADERS_H
|
||||||
|
|
||||||
|
@ -188,13 +204,13 @@ echo "" >> $SRS_AUTO_HEADERS_H
|
||||||
#####################################################################################
|
#####################################################################################
|
||||||
if [[ -f ../AUTHORS.txt ]]; then
|
if [[ -f ../AUTHORS.txt ]]; then
|
||||||
SRS_CONSTRIBUTORS=`cat ../AUTHORS.txt|grep "*"|awk '{print $2}'`
|
SRS_CONSTRIBUTORS=`cat ../AUTHORS.txt|grep "*"|awk '{print $2}'`
|
||||||
echo "#define SRS_AUTO_CONSTRIBUTORS \"\\" >> $SRS_AUTO_HEADERS_H
|
echo "#define SRS_CONSTRIBUTORS \"\\" >> $SRS_AUTO_HEADERS_H
|
||||||
for CONTRIBUTOR in $SRS_CONSTRIBUTORS; do
|
for CONTRIBUTOR in $SRS_CONSTRIBUTORS; do
|
||||||
echo "${CONTRIBUTOR} \\" >> $SRS_AUTO_HEADERS_H
|
echo "${CONTRIBUTOR} \\" >> $SRS_AUTO_HEADERS_H
|
||||||
done
|
done
|
||||||
echo "\"" >> $SRS_AUTO_HEADERS_H
|
echo "\"" >> $SRS_AUTO_HEADERS_H
|
||||||
else
|
else
|
||||||
echo "#define SRS_AUTO_CONSTRIBUTORS \"ossrs\"" >> $SRS_AUTO_HEADERS_H
|
echo "#define SRS_CONSTRIBUTORS \"ossrs\"" >> $SRS_AUTO_HEADERS_H
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# new empty line to auto headers file.
|
# new empty line to auto headers file.
|
||||||
|
|
|
@ -19,8 +19,9 @@ SRS_HDS=NO
|
||||||
SRS_SRT=NO
|
SRS_SRT=NO
|
||||||
SRS_RTC=YES
|
SRS_RTC=YES
|
||||||
SRS_GB28181=NO
|
SRS_GB28181=NO
|
||||||
|
SRS_CXX11=NO
|
||||||
|
SRS_CXX14=NO
|
||||||
SRS_NGINX=NO
|
SRS_NGINX=NO
|
||||||
SRS_FFMPEG_TOOL=NO
|
|
||||||
SRS_LIBRTMP=NO
|
SRS_LIBRTMP=NO
|
||||||
SRS_RESEARCH=NO
|
SRS_RESEARCH=NO
|
||||||
SRS_UTEST=NO
|
SRS_UTEST=NO
|
||||||
|
@ -34,6 +35,8 @@ SRS_GPROF=NO # Performance test: gprof
|
||||||
SRS_STREAM_CASTER=YES
|
SRS_STREAM_CASTER=YES
|
||||||
SRS_INGEST=YES
|
SRS_INGEST=YES
|
||||||
SRS_SSL=YES
|
SRS_SSL=YES
|
||||||
|
SRS_SSL_1_0=NO
|
||||||
|
SRS_HTTPS=YES
|
||||||
SRS_STAT=YES
|
SRS_STAT=YES
|
||||||
SRS_TRANSCODE=YES
|
SRS_TRANSCODE=YES
|
||||||
SRS_HTTP_CALLBACK=YES
|
SRS_HTTP_CALLBACK=YES
|
||||||
|
@ -42,10 +45,15 @@ SRS_HTTP_API=YES
|
||||||
SRS_HTTP_CORE=YES
|
SRS_HTTP_CORE=YES
|
||||||
SRS_HLS=YES
|
SRS_HLS=YES
|
||||||
SRS_DVR=YES
|
SRS_DVR=YES
|
||||||
|
SRS_CHERRYPY=YES
|
||||||
#
|
#
|
||||||
################################################################
|
################################################################
|
||||||
# libraries
|
# FFmpeg stub is the stub code in SRS for ingester or encoder.
|
||||||
SRS_FFMPEG_STUB=NO
|
SRS_FFMPEG_STUB=NO
|
||||||
|
# FFmpeg tool is the binary for FFmpeg tool, to exec ingest or transcode.
|
||||||
|
SRS_FFMPEG_TOOL=NO
|
||||||
|
# FFmpeg fit is the source code for RTC, to transcode audio or video in SRS.
|
||||||
|
SRS_FFMPEG_FIT=RESERVED
|
||||||
# arguments
|
# arguments
|
||||||
SRS_PREFIX=/usr/local/srs
|
SRS_PREFIX=/usr/local/srs
|
||||||
SRS_JOBS=1
|
SRS_JOBS=1
|
||||||
|
@ -60,7 +68,7 @@ SRS_GCOV=NO
|
||||||
# always enable the warn/error level.
|
# always enable the warn/error level.
|
||||||
SRS_LOG_VERBOSE=NO
|
SRS_LOG_VERBOSE=NO
|
||||||
SRS_LOG_INFO=NO
|
SRS_LOG_INFO=NO
|
||||||
SRS_LOG_TRACE=NO
|
SRS_LOG_TRACE=YES
|
||||||
#
|
#
|
||||||
################################################################
|
################################################################
|
||||||
# experts
|
# experts
|
||||||
|
@ -80,6 +88,8 @@ SRS_VALGRIND=NO
|
||||||
SRS_BUILD_TAG=
|
SRS_BUILD_TAG=
|
||||||
# Whether do "make clean" when configure.
|
# Whether do "make clean" when configure.
|
||||||
SRS_CLEAN=YES
|
SRS_CLEAN=YES
|
||||||
|
# Whether enable RTC simulate API.
|
||||||
|
SRS_SIMULATOR=NO
|
||||||
#
|
#
|
||||||
################################################################
|
################################################################
|
||||||
# presets
|
# presets
|
||||||
|
@ -118,10 +128,9 @@ SRS_EXTRA_FLAGS=
|
||||||
#
|
#
|
||||||
#####################################################################################
|
#####################################################################################
|
||||||
# Performance optimize.
|
# Performance optimize.
|
||||||
SRS_NASM=YES
|
SRS_NASM=NO
|
||||||
SRS_SRTP_ASM=YES
|
SRS_SRTP_ASM=NO
|
||||||
SRS_SENDMMSG=YES
|
SRS_SENDMMSG=NO
|
||||||
SRS_HAS_SENDMMSG=YES
|
|
||||||
SRS_DEBUG=NO
|
SRS_DEBUG=NO
|
||||||
|
|
||||||
#####################################################################################
|
#####################################################################################
|
||||||
|
@ -133,37 +142,30 @@ function show_help() {
|
||||||
Presets:
|
Presets:
|
||||||
--x86-64, --x86-x64 [default] For x86/x64 cpu, common pc and servers.
|
--x86-64, --x86-x64 [default] For x86/x64 cpu, common pc and servers.
|
||||||
--arm Enable crossbuild for ARM, should also set bellow toolchain options.
|
--arm Enable crossbuild for ARM, should also set bellow toolchain options.
|
||||||
--mips Enable crossbuild for MIPS
|
--osx Enable build for OSX/Darwin AppleOS.
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
-h, --help Print this message and exit 0.
|
-h, --help Print this message and exit 0.
|
||||||
|
|
||||||
--with-ssl Enable rtmp complex handshake, requires openssl-devel installed.
|
--ssl=on|off Whether build the rtmp complex handshake, requires openssl-devel installed.
|
||||||
--with-hds Enable hds streaming, mux RTMP to F4M/F4V files.
|
--https=on|off Whether enable HTTPS client and server. Default: off
|
||||||
--with-stream-caster Enable stream caster to serve other stream over other protocol.
|
--hds=on|off Whether build the hds streaming, mux RTMP to F4M/F4V files.
|
||||||
--with-stat Enable the data statistic, for http api.
|
--stream-caster=on|off Whether build the stream caster to serve other stream over other protocol.
|
||||||
--with-librtmp Enable srs-librtmp, library for client.
|
--stat=on|off Whether build the the data statistic, for http api.
|
||||||
--with-research Build the research tools.
|
--librtmp=on|off Whether build the srs-librtmp, library for client.
|
||||||
--with-utest Build the utest for SRS.
|
--research=on|off Whether build the research tools.
|
||||||
--with-srt Build the SRT support for SRS.
|
--cherrypy=on|off Whether install CherryPy for demo api-server.
|
||||||
--with-rtc Build the WebRTC support for SRS.
|
--utest=on|off Whether build the utest for SRS.
|
||||||
--with-gb28181 Build the GB28181 support for SRS.
|
--srt=on|off Whether build the SRT support for SRS.
|
||||||
|
--rtc=on|off Whether build the WebRTC support for SRS.
|
||||||
--without-ssl Disable rtmp complex handshake.
|
--gb28181=on|off Whether build the GB28181 support for SRS.
|
||||||
--without-hds Disable hds, the adobe http dynamic streaming.
|
--cxx11=on|off Whether enable the C++11 support for SRS.
|
||||||
--without-stream-caster Disable stream caster, only listen and serve RTMP/HTTP.
|
--cxx14=on|off Whether enable the C++14 support for SRS.
|
||||||
--without-stat Disable the data statistic feature.
|
--ffmpeg-fit=on|off Whether enable the FFmpeg fit(source code) for SRS.
|
||||||
--without-librtmp Disable srs-librtmp, library for client.
|
|
||||||
--without-research Disable the research tools.
|
|
||||||
--without-utest Disable the utest for SRS.
|
|
||||||
--without-srt Disable the SRT support for SRS.
|
|
||||||
--without-rtc Disable the WebRTC support for SRS.
|
|
||||||
--without-gb28181 Disable the GB28181 support for SRS.
|
|
||||||
|
|
||||||
--prefix=<path> The absolute installation path for srs. Default: $SRS_PREFIX
|
--prefix=<path> The absolute installation path for srs. Default: $SRS_PREFIX
|
||||||
--static Whether add '-static' to link options.
|
--gcov=on|off Whether enable the GCOV compiler options.
|
||||||
--gcov Whether enable the GCOV compiler options.
|
--debug=on|off Whether enable the debug code, may hurt performance.
|
||||||
--debug Whether enable the debug code, may hurt performance.
|
|
||||||
--jobs[=N] Allow N jobs at once; infinite jobs with no arg.
|
--jobs[=N] Allow N jobs at once; infinite jobs with no arg.
|
||||||
Used for make in the configure, for example, to make ffmpeg.
|
Used for make in the configure, for example, to make ffmpeg.
|
||||||
--log-verbose Whether enable the log verbose level. default: no.
|
--log-verbose Whether enable the log verbose level. default: no.
|
||||||
|
@ -171,32 +173,21 @@ Features:
|
||||||
--log-trace Whether enable the log trace level. default: yes.
|
--log-trace Whether enable the log trace level. default: yes.
|
||||||
|
|
||||||
Performance: @see https://blog.csdn.net/win_lin/article/details/53503869
|
Performance: @see https://blog.csdn.net/win_lin/article/details/53503869
|
||||||
--with-valgrind Support valgrind for memory check.
|
--valgrind=on|off Whether build valgrind for memory check.
|
||||||
--with-gperf Build SRS with gperf tools(no gmd/gmc/gmp/gcp, with tcmalloc only).
|
--gperf=on|off Whether build SRS with gperf tools(no gmd/gmc/gmp/gcp, with tcmalloc only).
|
||||||
--with-gmc Build memory check for SRS with gperf tools.
|
--gmc=on|off Whether build memory check for SRS with gperf tools.
|
||||||
--with-gmd Build memory defense(corrupt memory) for SRS with gperf tools.
|
--gmd=on|off Whether build memory defense(corrupt memory) for SRS with gperf tools.
|
||||||
--with-gmp Build memory profile for SRS with gperf tools.
|
--gmp=on|off Whether build memory profile for SRS with gperf tools.
|
||||||
--with-gcp Build cpu profile for SRS with gperf tools.
|
--gcp=on|off Whether build cpu profile for SRS with gperf tools.
|
||||||
--with-gprof Build SRS with gprof(GNU profile tool).
|
--gprof=on|off Whether build SRS with gprof(GNU profile tool).
|
||||||
|
|
||||||
--without-valgrind Do not support valgrind for memory check.
|
--nasm=on|off Whether build FFMPEG for RTC with nasm support.
|
||||||
--without-gperf Do not build SRS with gperf tools(without tcmalloc and gmd/gmc/gmp/gcp).
|
--srtp-nasm=on|off Whether build SRTP with ASM(openssl-asm) support, requires RTC and openssl-1.0.*.
|
||||||
--without-gmc Do not build memory check for SRS with gperf tools.
|
--sendmmsg=on|off Whether enable UDP sendmmsg support. Default: off. @see http://man7.org/linux/man-pages/man2/sendmmsg.2.html
|
||||||
--without-gmd Do not build memory defense for SRS with gperf tools.
|
|
||||||
--without-gmp Do not build memory profile for SRS with gperf tools.
|
|
||||||
--without-gcp Do not build cpu profile for SRS with gperf tools.
|
|
||||||
--without-gprof Do not build srs with gprof(GNU profile tool).
|
|
||||||
|
|
||||||
--with-nasm Build FFMPEG for RTC with nasm support.
|
|
||||||
--without-nasm Build FFMPEG for RTC without nasm support, for CentOS6 nasm is too old.
|
|
||||||
--with-srtp-nasm Build SRTP with ASM(openssl-asm) support, requires RTC and openssl-1.0.*.
|
|
||||||
--without-srtp-nasm Disable SRTP ASM support.
|
|
||||||
--with-sendmmsg Enable UDP sendmmsg support. @see http://man7.org/linux/man-pages/man2/sendmmsg.2.html
|
|
||||||
--without-sendmmsg Disable UDP sendmmsg support.
|
|
||||||
|
|
||||||
Toolchain options: @see https://github.com/ossrs/srs/issues/1547#issuecomment-576078411
|
Toolchain options: @see https://github.com/ossrs/srs/issues/1547#issuecomment-576078411
|
||||||
|
--static Whether add '-static' to link options.
|
||||||
--arm Enable crossbuild for ARM.
|
--arm Enable crossbuild for ARM.
|
||||||
--mips Enable crossbuild for MIPS.
|
|
||||||
--cc=<CC> Use c compiler CC, default is gcc.
|
--cc=<CC> Use c compiler CC, default is gcc.
|
||||||
--cxx=<CXX> Use c++ compiler CXX, default is g++.
|
--cxx=<CXX> Use c++ compiler CXX, default is g++.
|
||||||
--ar=<AR> Use archive tool AR, default is ar.
|
--ar=<AR> Use archive tool AR, default is ar.
|
||||||
|
@ -213,14 +204,12 @@ Conflicts:
|
||||||
The complex tools not available for arm.
|
The complex tools not available for arm.
|
||||||
|
|
||||||
Experts:
|
Experts:
|
||||||
--use-sys-ssl Do not compile ssl, use system ssl(-lssl) if required.
|
--sys-ssl=on|off Do not compile ssl, use system ssl(-lssl) if required.
|
||||||
--use-shared-st Use link shared libraries for ST which uses MPL license.
|
--use-shared-st Use link shared libraries for ST which uses MPL license.
|
||||||
--use-shared-srt Use link shared libraries for SRT which uses MPL license.
|
--use-shared-srt Use link shared libraries for SRT which uses MPL license.
|
||||||
--export-librtmp-project=<path> Export srs-librtmp to specified project in path.
|
--build-tag=<TAG> Set the build object directory suffix.
|
||||||
--export-librtmp-single=<path> Export srs-librtmp to a single file(.h+.cpp) in path.
|
--clean=on|off Whether do 'make clean' when configure.
|
||||||
--build-tag=<TAG> Set the build object directory suffix.
|
--simulator=on|off Whether enable RTC network simulator. Default: off
|
||||||
--with-clean Configure SRS and do make clean if possible.
|
|
||||||
--without-clean Configure SRS and never make clean even possible..
|
|
||||||
|
|
||||||
Workflow:
|
Workflow:
|
||||||
1. Apply "Presets". if not specified, use default preset.
|
1. Apply "Presets". if not specified, use default preset.
|
||||||
|
@ -239,55 +228,6 @@ function parse_user_option() {
|
||||||
-h) help=yes ;;
|
-h) help=yes ;;
|
||||||
--help) help=yes ;;
|
--help) help=yes ;;
|
||||||
|
|
||||||
--with-ssl) SRS_SSL=YES ;;
|
|
||||||
--with-hds) SRS_HDS=YES ;;
|
|
||||||
--with-nginx) SRS_NGINX=YES ;;
|
|
||||||
--with-ffmpeg) SRS_FFMPEG_TOOL=YES ;;
|
|
||||||
--with-transcode) SRS_TRANSCODE=YES ;;
|
|
||||||
--with-ingest) SRS_INGEST=YES ;;
|
|
||||||
--with-stat) SRS_STAT=YES ;;
|
|
||||||
--with-stream-caster) SRS_STREAM_CASTER=YES ;;
|
|
||||||
--with-librtmp) SRS_LIBRTMP=YES ;;
|
|
||||||
--with-research) SRS_RESEARCH=YES ;;
|
|
||||||
--with-utest) SRS_UTEST=YES ;;
|
|
||||||
--with-srt) SRS_SRT=YES ;;
|
|
||||||
--with-rtc) SRS_RTC=YES ;;
|
|
||||||
--with-gb28181) SRS_GB28181=YES ;;
|
|
||||||
--with-nasm) SRS_NASM=YES ;;
|
|
||||||
--with-srtp-nasm) SRS_SRTP_ASM=YES ;;
|
|
||||||
--with-sendmmsg) SRS_SENDMMSG=YES ;;
|
|
||||||
--with-clean) SRS_CLEAN=YES ;;
|
|
||||||
--with-gperf) SRS_GPERF=YES ;;
|
|
||||||
--with-gmc) SRS_GPERF_MC=YES ;;
|
|
||||||
--with-gmd) SRS_GPERF_MD=YES ;;
|
|
||||||
--with-gmp) SRS_GPERF_MP=YES ;;
|
|
||||||
--with-gcp) SRS_GPERF_CP=YES ;;
|
|
||||||
--with-gprof) SRS_GPROF=YES ;;
|
|
||||||
--with-arm-ubuntu12) SRS_CROSS_BUILD=YES ;;
|
|
||||||
--with-mips-ubuntu12) SRS_CROSS_BUILD=YES ;;
|
|
||||||
|
|
||||||
--without-hds) SRS_HDS=NO ;;
|
|
||||||
--without-nginx) SRS_NGINX=NO ;;
|
|
||||||
--without-ffmpeg) SRS_FFMPEG_TOOL=NO ;;
|
|
||||||
--without-librtmp) SRS_LIBRTMP=NO ;;
|
|
||||||
--without-research) SRS_RESEARCH=NO ;;
|
|
||||||
--without-utest) SRS_UTEST=NO ;;
|
|
||||||
--without-srt) SRS_SRT=NO ;;
|
|
||||||
--without-rtc) SRS_RTC=NO ;;
|
|
||||||
--without-gb28181) SRS_GB28181=NO ;;
|
|
||||||
--without-nasm) SRS_NASM=NO ;;
|
|
||||||
--without-srtp-nasm) SRS_SRTP_ASM=NO ;;
|
|
||||||
--without-sendmmsg) SRS_SENDMMSG=NO ;;
|
|
||||||
--without-clean) SRS_CLEAN=NO ;;
|
|
||||||
--without-gperf) SRS_GPERF=NO ;;
|
|
||||||
--without-gmc) SRS_GPERF_MC=NO ;;
|
|
||||||
--without-gmd) SRS_GPERF_MD=NO ;;
|
|
||||||
--without-gmp) SRS_GPERF_MP=NO ;;
|
|
||||||
--without-gcp) SRS_GPERF_CP=NO ;;
|
|
||||||
--without-gprof) SRS_GPROF=NO ;;
|
|
||||||
--without-arm-ubuntu12) SRS_CROSS_BUILD=NO ;;
|
|
||||||
--without-mips-ubuntu12) SRS_CROSS_BUILD=NO ;;
|
|
||||||
|
|
||||||
--jobs) SRS_JOBS=${value} ;;
|
--jobs) SRS_JOBS=${value} ;;
|
||||||
--prefix) SRS_PREFIX=${value} ;;
|
--prefix) SRS_PREFIX=${value} ;;
|
||||||
--static) SRS_STATIC=YES ;;
|
--static) SRS_STATIC=YES ;;
|
||||||
|
@ -321,32 +261,152 @@ function parse_user_option() {
|
||||||
--pure-rtmp) SRS_PURE_RTMP=YES ;;
|
--pure-rtmp) SRS_PURE_RTMP=YES ;;
|
||||||
--full) SRS_ENABLE_ALL=YES ;;
|
--full) SRS_ENABLE_ALL=YES ;;
|
||||||
|
|
||||||
--use-sys-ssl) SRS_USE_SYS_SSL=YES ;;
|
|
||||||
--use-shared-st) SRS_SHARED_ST=YES ;;
|
|
||||||
--use-shared-srt) SRS_SHARED_SRT=YES ;;
|
|
||||||
|
|
||||||
--memory-watch) SRS_MEM_WATCH=YES ;;
|
--memory-watch) SRS_MEM_WATCH=YES ;;
|
||||||
--export-librtmp-project) SRS_EXPORT_LIBRTMP_PROJECT=${value} ;;
|
--export-librtmp-project) SRS_EXPORT_LIBRTMP_PROJECT=${value} ;;
|
||||||
--export-librtmp-single) SRS_EXPORT_LIBRTMP_SINGLE=${value} ;;
|
--export-librtmp-single) SRS_EXPORT_LIBRTMP_SINGLE=${value} ;;
|
||||||
|
|
||||||
|
--sendmmsg) if [[ $value == off ]]; then SRS_SENDMMSG=NO; else SRS_SENDMMSG=YES; fi ;;
|
||||||
|
|
||||||
|
--without-srtp-nasm) SRS_SRTP_ASM=NO ;;
|
||||||
|
--with-srtp-nasm) SRS_SRTP_ASM=YES ;;
|
||||||
|
--srtp-nasm) if [[ $value == off ]]; then SRS_SRTP_ASM=NO; else SRS_SRTP_ASM=YES; fi ;;
|
||||||
|
|
||||||
|
--without-nasm) SRS_NASM=NO ;;
|
||||||
|
--with-nasm) SRS_NASM=YES ;;
|
||||||
|
--nasm) if [[ $value == off ]]; then SRS_NASM=NO; else SRS_NASM=YES; fi ;;
|
||||||
|
|
||||||
|
--with-ssl) SRS_SSL=YES ;;
|
||||||
|
--ssl) if [[ $value == off ]]; then SRS_SSL=NO; else SRS_SSL=YES; fi ;;
|
||||||
|
--https) if [[ $value == off ]]; then SRS_HTTPS=NO; else SRS_HTTPS=YES; fi ;;
|
||||||
|
--ssl-1-0) if [[ $value == off ]]; then SRS_SSL_1_0=NO; else SRS_SSL_1_0=YES; fi ;;
|
||||||
|
|
||||||
|
--with-hds) SRS_HDS=YES ;;
|
||||||
|
--without-hds) SRS_HDS=NO ;;
|
||||||
|
--hds) if [[ $value == off ]]; then SRS_HDS=NO; else SRS_HDS=YES; fi ;;
|
||||||
|
|
||||||
|
--with-nginx) SRS_NGINX=YES ;;
|
||||||
|
--without-nginx) SRS_NGINX=NO ;;
|
||||||
|
--nginx) if [[ $value == off ]]; then SRS_NGINX=NO; else SRS_NGINX=YES; fi ;;
|
||||||
|
|
||||||
|
--with-ffmpeg) SRS_FFMPEG_TOOL=YES ;;
|
||||||
|
--without-ffmpeg) SRS_FFMPEG_TOOL=NO ;;
|
||||||
|
--ffmpeg-tool) if [[ $value == off ]]; then SRS_FFMPEG_TOOL=NO; else SRS_FFMPEG_TOOL=YES; fi ;;
|
||||||
|
|
||||||
|
--with-transcode) SRS_TRANSCODE=YES ;;
|
||||||
|
--without-transcode) echo "ignore option \"$option\"" ;;
|
||||||
|
--transcode) if [[ $value == off ]]; then SRS_TRANSCODE=NO; else SRS_TRANSCODE=YES; fi ;;
|
||||||
|
|
||||||
|
--with-ingest) SRS_INGEST=YES ;;
|
||||||
|
--without-ingest) echo "ignore option \"$option\"" ;;
|
||||||
|
--ingest) if [[ $value == off ]]; then SRS_INGEST=NO; else SRS_INGEST=YES; fi ;;
|
||||||
|
|
||||||
|
--with-stat) SRS_STAT=YES ;;
|
||||||
|
--without-stat) echo "ignore option \"$option\"" ;;
|
||||||
|
--stat) if [[ $value == off ]]; then SRS_STAT=NO; else SRS_STAT=YES; fi ;;
|
||||||
|
|
||||||
|
--with-stream-caster) SRS_STREAM_CASTER=YES ;;
|
||||||
|
--without-stream-caster) echo "ignore option \"$option\"" ;;
|
||||||
|
--stream-caster) if [[ $value == off ]]; then SRS_STREAM_CASTER=NO; else SRS_STREAM_CASTER=YES; fi ;;
|
||||||
|
|
||||||
|
--with-librtmp) SRS_LIBRTMP=YES ;;
|
||||||
|
--without-librtmp) SRS_LIBRTMP=NO ;;
|
||||||
|
--librtmp) if [[ $value == off ]]; then SRS_LIBRTMP=NO; else SRS_LIBRTMP=YES; fi ;;
|
||||||
|
|
||||||
|
--with-research) SRS_RESEARCH=YES ;;
|
||||||
|
--without-research) SRS_RESEARCH=NO ;;
|
||||||
|
--research) if [[ $value == off ]]; then SRS_RESEARCH=NO; else SRS_RESEARCH=YES; fi ;;
|
||||||
|
|
||||||
|
--with-utest) SRS_UTEST=YES ;;
|
||||||
|
--without-utest) SRS_UTEST=NO ;;
|
||||||
|
--utest) if [[ $value == off ]]; then SRS_UTEST=NO; else SRS_UTEST=YES; fi ;;
|
||||||
|
--cherrypy) if [[ $value == off ]]; then SRS_CHERRYPY=NO; else SRS_CHERRYPY=YES; fi ;;
|
||||||
|
|
||||||
|
--with-srt) SRS_SRT=YES ;;
|
||||||
|
--without-srt) SRS_SRT=NO ;;
|
||||||
|
--srt) if [[ $value == off ]]; then SRS_SRT=NO; else SRS_SRT=YES; fi ;;
|
||||||
|
|
||||||
|
--with-rtc) SRS_RTC=YES ;;
|
||||||
|
--without-rtc) SRS_RTC=NO ;;
|
||||||
|
--rtc) if [[ $value == off ]]; then SRS_RTC=NO; else SRS_RTC=YES; fi ;;
|
||||||
|
--simulator) if [[ $value == off ]]; then SRS_SIMULATOR=NO; else SRS_SIMULATOR=YES; fi ;;
|
||||||
|
|
||||||
|
--with-gb28181) SRS_GB28181=YES ;;
|
||||||
|
--without-gb28181) SRS_GB28181=NO ;;
|
||||||
|
--gb28181) if [[ $value == off ]]; then SRS_GB28181=NO; else SRS_GB28181=YES; fi ;;
|
||||||
|
|
||||||
|
--cxx11) if [[ $value == off ]]; then SRS_CXX11=NO; else SRS_CXX11=YES; fi ;;
|
||||||
|
--cxx14) if [[ $value == off ]]; then SRS_CXX14=NO; else SRS_CXX14=YES; fi ;;
|
||||||
|
--ffmpeg-fit) if [[ $value == off ]]; then SRS_FFMPEG_FIT=NO; else SRS_FFMPEG_FIT=YES; fi ;;
|
||||||
|
|
||||||
|
--with-clean) SRS_CLEAN=YES ;;
|
||||||
|
--without-clean) SRS_CLEAN=NO ;;
|
||||||
|
--clean) if [[ $value == off ]]; then SRS_CLEAN=NO; else SRS_CLEAN=YES; fi ;;
|
||||||
|
|
||||||
|
--with-gperf) SRS_GPERF=YES ;;
|
||||||
|
--without-gperf) SRS_GPERF=NO ;;
|
||||||
|
--gperf) if [[ $value == off ]]; then SRS_GPERF=NO; else SRS_GPERF=YES; fi ;;
|
||||||
|
|
||||||
|
--with-gmc) SRS_GPERF_MC=YES ;;
|
||||||
|
--without-gmc) SRS_GPERF_MC=NO ;;
|
||||||
|
--gmc) if [[ $value == off ]]; then SRS_GPERF_MC=NO; else SRS_GPERF_MC=YES; fi ;;
|
||||||
|
|
||||||
|
--with-gmd) SRS_GPERF_MD=YES ;;
|
||||||
|
--without-gmd) SRS_GPERF_MD=NO ;;
|
||||||
|
--gmd) if [[ $value == off ]]; then SRS_GPERF_MD=NO; else SRS_GPERF_MD=YES; fi ;;
|
||||||
|
|
||||||
|
--with-gmp) SRS_GPERF_MP=YES ;;
|
||||||
|
--without-gmp) SRS_GPERF_MP=NO ;;
|
||||||
|
--gmp) if [[ $value == off ]]; then SRS_GPERF_MP=NO; else SRS_GPERF_MP=YES; fi ;;
|
||||||
|
|
||||||
|
--with-gcp) SRS_GPERF_CP=YES ;;
|
||||||
|
--without-gcp) SRS_GPERF_CP=NO ;;
|
||||||
|
--gcp) if [[ $value == off ]]; then SRS_GPERF_CP=NO; else SRS_GPERF_CP=YES; fi ;;
|
||||||
|
|
||||||
|
--with-gprof) SRS_GPROF=YES ;;
|
||||||
|
--without-gprof) SRS_GPROF=NO ;;
|
||||||
|
--gprof) if [[ $value == off ]]; then SRS_GPROF=NO; else SRS_GPROF=YES; fi ;;
|
||||||
|
|
||||||
|
--with-arm-ubuntu12) SRS_CROSS_BUILD=YES ;;
|
||||||
|
--without-arm-ubuntu12) SRS_CROSS_BUILD=NO ;;
|
||||||
|
--arm-ubuntu12) if [[ $value == off ]]; then SRS_CROSS_BUILD=NO; else SRS_CROSS_BUILD=YES; fi ;;
|
||||||
|
|
||||||
|
--with-mips-ubuntu12) SRS_CROSS_BUILD=YES ;;
|
||||||
|
--without-mips-ubuntu12) SRS_CROSS_BUILD=NO ;;
|
||||||
|
--mips-ubuntu12) if [[ $value == off ]]; then SRS_CROSS_BUILD=NO; else SRS_CROSS_BUILD=YES; fi ;;
|
||||||
|
|
||||||
|
--use-sys-ssl) SRS_USE_SYS_SSL=YES ;;
|
||||||
|
--without-ssl) echo "ignore option \"$option\"" ;;
|
||||||
|
--sys-ssl) if [[ $value == off ]]; then SRS_USE_SYS_SSL=NO; else SRS_USE_SYS_SSL=YES; fi ;;
|
||||||
|
|
||||||
|
--use-shared-st) SRS_SHARED_ST=YES ;;
|
||||||
|
--shared-st) if [[ $value == off ]]; then SRS_SHARED_ST=NO; else SRS_SHARED_ST=YES; fi ;;
|
||||||
|
|
||||||
|
--use-shared-srt) SRS_SHARED_SRT=YES ;;
|
||||||
|
--shared-srt) if [[ $value == off ]]; then SRS_SHARED_SRT=NO; else SRS_SHARED_SRT=YES; fi ;;
|
||||||
|
|
||||||
--with-valgrind) SRS_VALGRIND=YES ;;
|
--with-valgrind) SRS_VALGRIND=YES ;;
|
||||||
--without-valgrind) SRS_VALGRIND=NO ;;
|
--without-valgrind) SRS_VALGRIND=NO ;;
|
||||||
|
--valgrind) if [[ $value == off ]]; then SRS_VALGRIND=NO; else SRS_VALGRIND=YES; fi ;;
|
||||||
|
|
||||||
--with-http-callback) SRS_HTTP_CALLBACK=YES ;;
|
--with-http-callback) SRS_HTTP_CALLBACK=YES ;;
|
||||||
--with-http-api) SRS_HTTP_API=YES ;;
|
|
||||||
--with-http-server) SRS_HTTP_SERVER=YES ;;
|
|
||||||
--with-hls) SRS_HLS=YES ;;
|
|
||||||
--with-dvr) SRS_DVR=YES ;;
|
|
||||||
|
|
||||||
--without-stream-caster) echo "ignore option \"$option\"" ;;
|
|
||||||
--without-ingest) echo "ignore option \"$option\"" ;;
|
|
||||||
--without-ssl) echo "ignore option \"$option\"" ;;
|
|
||||||
--without-stat) echo "ignore option \"$option\"" ;;
|
|
||||||
--without-transcode) echo "ignore option \"$option\"" ;;
|
|
||||||
--without-http-callback) echo "ignore option \"$option\"" ;;
|
--without-http-callback) echo "ignore option \"$option\"" ;;
|
||||||
--without-http-server) echo "ignore option \"$option\"" ;;
|
--http-callback) if [[ $value == off ]]; then SRS_HTTP_CALLBACK=NO; else SRS_HTTP_CALLBACK=YES; fi ;;
|
||||||
|
|
||||||
|
--with-http-api) SRS_HTTP_API=YES ;;
|
||||||
--without-http-api) echo "ignore option \"$option\"" ;;
|
--without-http-api) echo "ignore option \"$option\"" ;;
|
||||||
|
--http-api) if [[ $value == off ]]; then SRS_HTTP_API=NO; else SRS_HTTP_API=YES; fi ;;
|
||||||
|
|
||||||
|
--with-http-server) SRS_HTTP_SERVER=YES ;;
|
||||||
|
--without-http-server) echo "ignore option \"$option\"" ;;
|
||||||
|
--http-server) if [[ $value == off ]]; then SRS_HTTP_SERVER=NO; else SRS_HTTP_SERVER=YES; fi ;;
|
||||||
|
|
||||||
|
--with-hls) SRS_HLS=YES ;;
|
||||||
--without-hls) echo "ignore option \"$option\"" ;;
|
--without-hls) echo "ignore option \"$option\"" ;;
|
||||||
|
--hls) if [[ $value == off ]]; then SRS_HLS=NO; else SRS_HLS=YES; fi ;;
|
||||||
|
|
||||||
|
--with-dvr) SRS_DVR=YES ;;
|
||||||
--without-dvr) echo "ignore option \"$option\"" ;;
|
--without-dvr) echo "ignore option \"$option\"" ;;
|
||||||
|
--dvr) if [[ $value == off ]]; then SRS_DVR=NO; else SRS_DVR=YES; fi ;;
|
||||||
|
|
||||||
*)
|
*)
|
||||||
echo "$0: error: invalid option \"$option\""
|
echo "$0: error: invalid option \"$option\""
|
||||||
|
@ -382,12 +442,7 @@ if [ $help = yes ]; then
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
function apply_user_presets() {
|
function apply_detail_options() {
|
||||||
# always set the log level for all presets.
|
|
||||||
SRS_LOG_VERBOSE=NO
|
|
||||||
SRS_LOG_INFO=NO
|
|
||||||
SRS_LOG_TRACE=YES
|
|
||||||
|
|
||||||
# set default preset if not specifies
|
# set default preset if not specifies
|
||||||
if [[ $SRS_PURE_RTMP == NO && $SRS_FAST == NO && $SRS_DISABLE_ALL == NO && $SRS_ENABLE_ALL == NO && \
|
if [[ $SRS_PURE_RTMP == NO && $SRS_FAST == NO && $SRS_DISABLE_ALL == NO && $SRS_ENABLE_ALL == NO && \
|
||||||
$SRS_DEV == NO && $SRS_FAST_DEV == NO && $SRS_DEMO == NO && $SRS_PI == NO && $SRS_CUBIE == NO && \
|
$SRS_DEV == NO && $SRS_FAST_DEV == NO && $SRS_DEMO == NO && $SRS_PI == NO && $SRS_CUBIE == NO && \
|
||||||
|
@ -396,116 +451,16 @@ function apply_user_presets() {
|
||||||
SRS_X86_X64=YES; opt="--x86-x64 $opt";
|
SRS_X86_X64=YES; opt="--x86-x64 $opt";
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# all disabled.
|
# Enable c++11 for SRT.
|
||||||
if [ $SRS_DISABLE_ALL = YES ]; then
|
if [[ $SRS_SRT == YES ]]; then
|
||||||
SRS_HDS=NO
|
SRS_CXX11=YES
|
||||||
SRS_LIBRTMP=NO
|
|
||||||
SRS_RESEARCH=NO
|
|
||||||
SRS_UTEST=NO
|
|
||||||
SRS_STATIC=NO
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# all enabled.
|
# Enable FFmpeg fit for RTC to trancode audio from AAC to OPUS, if user has't disabled it.
|
||||||
if [ $SRS_ENABLE_ALL = YES ]; then
|
if [[ $SRS_RTC == YES && $SRS_FFMPEG_FIT == RESERVED ]]; then
|
||||||
SRS_HDS=YES
|
SRS_FFMPEG_FIT=YES
|
||||||
SRS_LIBRTMP=YES
|
|
||||||
SRS_RESEARCH=YES
|
|
||||||
SRS_UTEST=YES
|
|
||||||
SRS_STATIC=NO
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# only rtmp vp6
|
|
||||||
if [ $SRS_FAST = YES ]; then
|
|
||||||
SRS_HDS=NO
|
|
||||||
SRS_LIBRTMP=NO
|
|
||||||
SRS_RESEARCH=NO
|
|
||||||
SRS_UTEST=NO
|
|
||||||
SRS_STATIC=NO
|
|
||||||
fi
|
|
||||||
|
|
||||||
# only ssl for RTMP with complex handshake.
|
|
||||||
if [ $SRS_PURE_RTMP = YES ]; then
|
|
||||||
SRS_HDS=NO
|
|
||||||
SRS_LIBRTMP=NO
|
|
||||||
SRS_RESEARCH=NO
|
|
||||||
SRS_UTEST=NO
|
|
||||||
SRS_STATIC=NO
|
|
||||||
fi
|
|
||||||
|
|
||||||
# defaults for x86/x64
|
|
||||||
if [ $SRS_X86_X64 = YES ]; then
|
|
||||||
SRS_HDS=YES
|
|
||||||
SRS_LIBRTMP=YES
|
|
||||||
SRS_RESEARCH=NO
|
|
||||||
SRS_UTEST=NO
|
|
||||||
SRS_STATIC=NO
|
|
||||||
fi
|
|
||||||
|
|
||||||
# if dev specified, open features if possible.
|
|
||||||
if [ $SRS_DEV = YES ]; then
|
|
||||||
SRS_HDS=YES
|
|
||||||
SRS_LIBRTMP=YES
|
|
||||||
SRS_RESEARCH=YES
|
|
||||||
SRS_UTEST=YES
|
|
||||||
SRS_STATIC=NO
|
|
||||||
fi
|
|
||||||
|
|
||||||
# if fast dev specified, open main server features.
|
|
||||||
if [ $SRS_FAST_DEV = YES ]; then
|
|
||||||
SRS_HDS=YES
|
|
||||||
SRS_LIBRTMP=NO
|
|
||||||
SRS_RESEARCH=NO
|
|
||||||
SRS_UTEST=NO
|
|
||||||
SRS_STATIC=NO
|
|
||||||
fi
|
|
||||||
|
|
||||||
# for srs demo
|
|
||||||
if [ $SRS_DEMO = YES ]; then
|
|
||||||
SRS_HDS=YES
|
|
||||||
SRS_LIBRTMP=YES
|
|
||||||
SRS_RESEARCH=NO
|
|
||||||
SRS_UTEST=NO
|
|
||||||
SRS_STATIC=NO
|
|
||||||
fi
|
|
||||||
|
|
||||||
# if raspberry-pi specified, open ssl/hls/static features
|
|
||||||
if [ $SRS_PI = YES ]; then
|
|
||||||
SRS_HDS=YES
|
|
||||||
SRS_LIBRTMP=YES
|
|
||||||
SRS_RESEARCH=NO
|
|
||||||
SRS_UTEST=NO
|
|
||||||
SRS_STATIC=NO
|
|
||||||
fi
|
|
||||||
|
|
||||||
# if cubieboard specified, open features except ffmpeg/nginx.
|
|
||||||
if [ $SRS_CUBIE = YES ]; then
|
|
||||||
SRS_HDS=YES
|
|
||||||
SRS_LIBRTMP=YES
|
|
||||||
SRS_RESEARCH=NO
|
|
||||||
SRS_UTEST=NO
|
|
||||||
SRS_STATIC=NO
|
|
||||||
fi
|
|
||||||
|
|
||||||
# if crossbuild, disable research and librtmp.
|
|
||||||
if [[ $SRS_CROSS_BUILD == YES ]]; then
|
|
||||||
SRS_LIBRTMP=NO
|
|
||||||
SRS_RESEARCH=NO
|
|
||||||
SRS_UTEST=NO
|
|
||||||
SRS_STATIC=NO
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
apply_user_presets
|
|
||||||
|
|
||||||
#####################################################################################
|
|
||||||
# parse detail feature options
|
|
||||||
#####################################################################################
|
|
||||||
for option
|
|
||||||
do
|
|
||||||
parse_user_option_to_value_and_option
|
|
||||||
parse_user_option
|
|
||||||
done
|
|
||||||
|
|
||||||
function apply_user_detail_options() {
|
|
||||||
# if transcode/ingest specified, requires the ffmpeg stub classes.
|
# if transcode/ingest specified, requires the ffmpeg stub classes.
|
||||||
SRS_FFMPEG_STUB=NO
|
SRS_FFMPEG_STUB=NO
|
||||||
if [ $SRS_TRANSCODE = YES ]; then SRS_FFMPEG_STUB=YES; fi
|
if [ $SRS_TRANSCODE = YES ]; then SRS_FFMPEG_STUB=YES; fi
|
||||||
|
@ -533,28 +488,24 @@ function apply_user_detail_options() {
|
||||||
|
|
||||||
# if specified export single file, export project first.
|
# if specified export single file, export project first.
|
||||||
if [ $SRS_EXPORT_LIBRTMP_SINGLE != NO ]; then
|
if [ $SRS_EXPORT_LIBRTMP_SINGLE != NO ]; then
|
||||||
SRS_EXPORT_LIBRTMP_PROJECT=$SRS_EXPORT_LIBRTMP_SINGLE
|
echo "Warning: Ingore --export-librtmp-single"
|
||||||
|
SRS_EXPORT_LIBRTMP_SINGLE=NO
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# disable almost all features for export srs-librtmp.
|
# disable almost all features for export srs-librtmp.
|
||||||
if [ $SRS_EXPORT_LIBRTMP_PROJECT != NO ]; then
|
if [ $SRS_EXPORT_LIBRTMP_PROJECT != NO ]; then
|
||||||
SRS_HDS=NO
|
echo "Warning: Ingore --export-librtmp-project"
|
||||||
SRS_SSL=NO
|
SRS_EXPORT_LIBRTMP_PROJECT=NO
|
||||||
SRS_TRANSCODE=NO
|
fi
|
||||||
SRS_HTTP_CALLBACK=NO
|
|
||||||
SRS_INGEST=NO
|
if [[ $SRS_LIBRTMP != NO ]]; then
|
||||||
SRS_STAT=NO
|
echo "Warning: Ingore --librtmp"
|
||||||
SRS_STREAM_CASTER=NO
|
SRS_LIBRTMP=NO
|
||||||
SRS_LIBRTMP=YES
|
fi
|
||||||
SRS_RESEARCH=YES
|
|
||||||
SRS_UTEST=NO
|
if [[ $SRS_RESEARCH != NO ]]; then
|
||||||
SRS_GPERF=NO
|
echo "Warning: Ingore --research"
|
||||||
SRS_GPERF_MC=NO
|
SRS_RESEARCH=NO
|
||||||
SRS_GPERF_MD=NO
|
|
||||||
SRS_GPERF_MP=NO
|
|
||||||
SRS_GPERF_CP=NO
|
|
||||||
SRS_GPROF=NO
|
|
||||||
SRS_STATIC=NO
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $SRS_SRTP_ASM == YES && $SRS_RTC == NO ]]; then
|
if [[ $SRS_SRTP_ASM == YES && $SRS_RTC == NO ]]; then
|
||||||
|
@ -567,74 +518,72 @@ function apply_user_detail_options() {
|
||||||
SRS_SRTP_ASM=NO
|
SRS_SRTP_ASM=NO
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Detect whether has sendmmsg.
|
# Which openssl we choose, openssl-1.0.* for SRTP with ASM, others we use openssl-1.1.*
|
||||||
# @see http://man7.org/linux/man-pages/man2/sendmmsg.2.html
|
if [[ $SRS_SRTP_ASM == YES && $SRS_SSL_1_0 == NO ]]; then
|
||||||
mkdir -p ${SRS_OBJS} &&
|
echo "Use openssl-1.0 for SRTP ASM."
|
||||||
echo " #include <sys/socket.h> " > ${SRS_OBJS}/_tmp_sendmmsg_detect.c
|
SRS_SSL_1_0=YES
|
||||||
echo " int main(int argc, char** argv) { " >> ${SRS_OBJS}/_tmp_sendmmsg_detect.c
|
fi
|
||||||
echo " struct mmsghdr hdr; " >> ${SRS_OBJS}/_tmp_sendmmsg_detect.c
|
|
||||||
echo " hdr.msg_len = 0; " >> ${SRS_OBJS}/_tmp_sendmmsg_detect.c
|
if [[ $SRS_OSX == YES && $SRS_SENDMMSG == YES ]]; then
|
||||||
echo " return 0; " >> ${SRS_OBJS}/_tmp_sendmmsg_detect.c
|
echo "Disable sendmmsg for OSX"
|
||||||
echo " } " >> ${SRS_OBJS}/_tmp_sendmmsg_detect.c
|
SRS_SENDMMSG=NO
|
||||||
${SRS_TOOL_CC} -c ${SRS_OBJS}/_tmp_sendmmsg_detect.c -D_GNU_SOURCE -o /dev/null >/dev/null 2>&1
|
|
||||||
ret=$?; rm -f ${SRS_OBJS}/_tmp_sendmmsg_detect.c;
|
|
||||||
if [[ $ret -ne 0 ]]; then
|
|
||||||
SRS_HAS_SENDMMSG=NO
|
|
||||||
if [[ $SRS_SENDMMSG == YES ]]; then
|
|
||||||
echo "Disable UDP sendmmsg automatically"
|
|
||||||
SRS_SENDMMSG=NO
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
apply_user_detail_options
|
apply_detail_options
|
||||||
|
|
||||||
function regenerate_options() {
|
function regenerate_options() {
|
||||||
# save all config options to macro to write to auto headers file
|
# save all config options to macro to write to auto headers file
|
||||||
SRS_AUTO_USER_CONFIGURE=`echo $opt`
|
SRS_AUTO_USER_CONFIGURE=`echo $opt`
|
||||||
# regenerate the options for default values.
|
# regenerate the options for default values.
|
||||||
SRS_AUTO_CONFIGURE="--prefix=${SRS_PREFIX}"
|
SRS_AUTO_CONFIGURE="--prefix=${SRS_PREFIX}"
|
||||||
if [ $SRS_HLS = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-hls"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-hls"; fi
|
if [ $SRS_HLS = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --hls=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --hls=off"; fi
|
||||||
if [ $SRS_HDS = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-hds"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-hds"; fi
|
if [ $SRS_HDS = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --hds=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --hds=off"; fi
|
||||||
if [ $SRS_DVR = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-dvr"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-dvr"; fi
|
if [ $SRS_DVR = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --dvr=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --dvr=off"; fi
|
||||||
if [ $SRS_SSL = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-ssl"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-ssl"; fi
|
if [ $SRS_SSL = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --ssl=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --ssl=off"; fi
|
||||||
if [ $SRS_TRANSCODE = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-transcode"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-transcode"; fi
|
if [ $SRS_HTTPS = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --https=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --https=off"; fi
|
||||||
if [ $SRS_INGEST = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-ingest"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-ingest"; fi
|
if [ $SRS_SSL_1_0 = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --ssl-1-0=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --ssl-1-0=off"; fi
|
||||||
if [ $SRS_STAT = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-stat"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-stat"; fi
|
if [ $SRS_USE_SYS_SSL = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --sys-ssl=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --sys-ssl=off"; fi
|
||||||
if [ $SRS_HTTP_CALLBACK = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-http-callback"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-http-callback"; fi
|
if [ $SRS_TRANSCODE = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --transcode=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --transcode=off"; fi
|
||||||
if [ $SRS_HTTP_SERVER = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-http-server"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-http-server"; fi
|
if [ $SRS_INGEST = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --ingest=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --ingest=off"; fi
|
||||||
if [ $SRS_STREAM_CASTER = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-stream-caster"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-stream-caster"; fi
|
if [ $SRS_STAT = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --stat=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --stat=off"; fi
|
||||||
if [ $SRS_HTTP_API = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-http-api"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-http-api"; fi
|
if [ $SRS_HTTP_CALLBACK = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --http-callback=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --http-callback=off"; fi
|
||||||
if [ $SRS_LIBRTMP = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-librtmp"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-librtmp"; fi
|
if [ $SRS_HTTP_SERVER = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --http-server=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --http-server=off"; fi
|
||||||
if [ $SRS_RESEARCH = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-research"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-research"; fi
|
if [ $SRS_STREAM_CASTER = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --stream-caster=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --stream-caster=off"; fi
|
||||||
if [ $SRS_UTEST = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-utest"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-utest"; fi
|
if [ $SRS_HTTP_API = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --http-api=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --http-api=off"; fi
|
||||||
if [ $SRS_SRT = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-srt"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-srt"; fi
|
if [ $SRS_UTEST = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --utest=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --utest=off"; fi
|
||||||
if [ $SRS_RTC = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-rtc"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-rtc"; fi
|
if [ $SRS_CHERRYPY = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --cherrypy=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --cherrypy=off"; fi
|
||||||
if [ $SRS_GB28181 = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-gb28181"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-gb28181"; fi
|
if [ $SRS_SRT = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --srt=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --srt=off"; fi
|
||||||
if [ $SRS_NASM = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-nasm"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-nasm"; fi
|
if [ $SRS_RTC = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --rtc=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --rtc=off"; fi
|
||||||
if [ $SRS_SRTP_ASM = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-srtp-nasm"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-srtp-nasm"; fi
|
if [ $SRS_SIMULATOR = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --simulator=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --simulator=off"; fi
|
||||||
if [ $SRS_SENDMMSG = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-sendmmsg"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-sendmmsg"; fi
|
if [ $SRS_GB28181 = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --gb28181=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --gb28181=off"; fi
|
||||||
if [ $SRS_CLEAN = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-clean"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-clean"; fi
|
if [ $SRS_CXX11 = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --cxx11=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --cxx11=off"; fi
|
||||||
if [ $SRS_GPERF = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-gperf"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-gperf"; fi
|
if [ $SRS_CXX14 = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --cxx14=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --cxx14=off"; fi
|
||||||
if [ $SRS_GPERF_MC = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-gmc"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-gmc"; fi
|
if [ $SRS_FFMPEG_FIT = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --ffmpeg-fit=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --ffmpeg-fit=off"; fi
|
||||||
if [ $SRS_GPERF_MD = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-gmd"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-gmd"; fi
|
if [ $SRS_NASM = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --nasm=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --nasm=off"; fi
|
||||||
if [ $SRS_GPERF_MP = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-gmp"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-gmp"; fi
|
if [ $SRS_SRTP_ASM = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --srtp-nasm=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --srtp-nasm=off"; fi
|
||||||
if [ $SRS_GPERF_CP = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-gcp"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-gcp"; fi
|
if [ $SRS_SENDMMSG = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --sendmmsg=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --sendmmsg=off"; fi
|
||||||
if [ $SRS_GPROF = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-gprof"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-gprof"; fi
|
if [ $SRS_CLEAN = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --clean=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --clean=off"; fi
|
||||||
if [ $SRS_STATIC = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --static"; fi
|
if [ $SRS_GPERF = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --gperf=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --gperf=off"; fi
|
||||||
if [ $SRS_SHARED_ST = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --use-shared-st"; fi
|
if [ $SRS_GPERF_MC = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --gmc=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --gmc=off"; fi
|
||||||
if [ $SRS_SHARED_SRT = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --use-shared-srt"; fi
|
if [ $SRS_GPERF_MD = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --gmd=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --gmd=off"; fi
|
||||||
if [ $SRS_LOG_VERBOSE = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --log-verbose"; fi
|
if [ $SRS_GPERF_MP = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --gmp=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --gmp=off"; fi
|
||||||
if [ $SRS_LOG_INFO = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --log-info"; fi
|
if [ $SRS_GPERF_CP = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --gcp=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --gcp=off"; fi
|
||||||
if [ $SRS_LOG_TRACE = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --log-trace"; fi
|
if [ $SRS_GPROF = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --gprof=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --gprof=off"; fi
|
||||||
if [ $SRS_GCOV = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --gcov"; fi
|
if [ $SRS_STATIC = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --static"; fi
|
||||||
if [ $SRS_DEBUG = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --debug"; fi
|
if [ $SRS_SHARED_ST = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --use-shared-st"; fi
|
||||||
if [[ $SRS_EXTRA_FLAGS != '' ]]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --extra-flags=\\\"$SRS_EXTRA_FLAGS\\\""; fi
|
if [ $SRS_SHARED_SRT = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --use-shared-srt"; fi
|
||||||
if [[ $SRS_BUILD_TAG != '' ]]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --build-tag=\\\"$SRS_BUILD_TAG\\\""; fi
|
if [ $SRS_LOG_VERBOSE = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --log-verbose"; fi
|
||||||
if [[ $SRS_TOOL_CC != '' ]]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --cc=$SRS_TOOL_CC"; fi
|
if [ $SRS_LOG_INFO = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --log-info"; fi
|
||||||
if [[ $SRS_TOOL_CXX != '' ]]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --cxx=$SRS_TOOL_CXX"; fi
|
if [ $SRS_LOG_TRACE = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --log-trace"; fi
|
||||||
if [[ $SRS_TOOL_AR != '' ]]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --ar=$SRS_TOOL_AR"; fi
|
if [ $SRS_GCOV = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --gcov"; fi
|
||||||
if [[ $SRS_TOOL_LD != '' ]]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --ld=$SRS_TOOL_LD"; fi
|
if [ $SRS_DEBUG = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --debug"; fi
|
||||||
if [[ $SRS_TOOL_RANDLIB != '' ]]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --randlib=$SRS_TOOL_RANDLIB"; fi
|
if [[ $SRS_EXTRA_FLAGS != '' ]]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --extra-flags=\\\"$SRS_EXTRA_FLAGS\\\""; fi
|
||||||
|
if [[ $SRS_BUILD_TAG != '' ]]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --build-tag=\\\"$SRS_BUILD_TAG\\\""; fi
|
||||||
|
if [[ $SRS_TOOL_CC != '' ]]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --cc=$SRS_TOOL_CC"; fi
|
||||||
|
if [[ $SRS_TOOL_CXX != '' ]]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --cxx=$SRS_TOOL_CXX"; fi
|
||||||
|
if [[ $SRS_TOOL_AR != '' ]]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --ar=$SRS_TOOL_AR"; fi
|
||||||
|
if [[ $SRS_TOOL_LD != '' ]]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --ld=$SRS_TOOL_LD"; fi
|
||||||
|
if [[ $SRS_TOOL_RANDLIB != '' ]]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --randlib=$SRS_TOOL_RANDLIB"; fi
|
||||||
echo "User config: $SRS_AUTO_USER_CONFIGURE"
|
echo "User config: $SRS_AUTO_USER_CONFIGURE"
|
||||||
echo "Detail config: ${SRS_AUTO_CONFIGURE}"
|
echo "Detail config: ${SRS_AUTO_CONFIGURE}"
|
||||||
}
|
}
|
||||||
|
@ -649,17 +598,13 @@ function check_option_conflicts() {
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $SRS_CROSS_BUILD == YES && ($SRS_TOOL_CC == 'gcc' || $SRS_TOOL_CXX == 'g++' || $SRS_TOOL_AR == 'ar') ]]; then
|
if [[ $SRS_CROSS_BUILD == YES && ($SRS_TOOL_CC == 'gcc' || $SRS_TOOL_CXX == 'g++' || $SRS_TOOL_AR == 'ar') ]]; then
|
||||||
echo "For crossbuild, must not use default toolchain, cc: $SRS_TOOL_CC, cxx: $SRS_TOOL_CXX, ar: $SRS_TOOL_AR"; exit -1
|
echo "Warning: For crossbuild, must not use default toolchain, cc: $SRS_TOOL_CC, cxx: $SRS_TOOL_CXX, ar: $SRS_TOOL_AR"
|
||||||
|
SRS_CROSS_BUILD=NO
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $SRS_NGINX == YES ]]; then
|
if [[ $SRS_NGINX == YES ]]; then
|
||||||
echo "Don't support building NGINX, please use docker https://github.com/ossrs/srs-docker"; exit -1;
|
echo "Warning: Don't support building NGINX, please use docker https://github.com/ossrs/srs-docker"
|
||||||
fi
|
SRS_NGINX=NO
|
||||||
|
|
||||||
# For OSX, recommend to use DTrace, https://blog.csdn.net/win_lin/article/details/53503869
|
|
||||||
if [[ $SRS_OSX == YES && $SRS_GPROF == YES ]]; then
|
|
||||||
echo "Tool gprof for OSX is unavailable, please use dtrace, read https://blog.csdn.net/win_lin/article/details/53503869"
|
|
||||||
exit -1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# TODO: FIXME: check more os.
|
# TODO: FIXME: check more os.
|
||||||
|
@ -692,8 +637,6 @@ function check_option_conflicts() {
|
||||||
if [ $SRS_HDS = RESERVED ]; then echo "you must specifies the hds, see: ./configure --help"; __check_ok=NO; fi
|
if [ $SRS_HDS = RESERVED ]; then echo "you must specifies the hds, see: ./configure --help"; __check_ok=NO; fi
|
||||||
if [ $SRS_SSL = RESERVED ]; then echo "you must specifies the ssl, see: ./configure --help"; __check_ok=NO; fi
|
if [ $SRS_SSL = RESERVED ]; then echo "you must specifies the ssl, see: ./configure --help"; __check_ok=NO; fi
|
||||||
if [ $SRS_STREAM_CASTER = RESERVED ]; then echo "you must specifies the stream-caster, see: ./configure --help"; __check_ok=NO; fi
|
if [ $SRS_STREAM_CASTER = RESERVED ]; then echo "you must specifies the stream-caster, see: ./configure --help"; __check_ok=NO; fi
|
||||||
if [ $SRS_LIBRTMP = RESERVED ]; then echo "you must specifies the librtmp, see: ./configure --help"; __check_ok=NO; fi
|
|
||||||
if [ $SRS_RESEARCH = RESERVED ]; then echo "you must specifies the research, see: ./configure --help"; __check_ok=NO; fi
|
|
||||||
if [ $SRS_UTEST = RESERVED ]; then echo "you must specifies the utest, see: ./configure --help"; __check_ok=NO; fi
|
if [ $SRS_UTEST = RESERVED ]; then echo "you must specifies the utest, see: ./configure --help"; __check_ok=NO; fi
|
||||||
if [ $SRS_GPERF = RESERVED ]; then echo "you must specifies the gperf, see: ./configure --help"; __check_ok=NO; fi
|
if [ $SRS_GPERF = RESERVED ]; then echo "you must specifies the gperf, see: ./configure --help"; __check_ok=NO; fi
|
||||||
if [ $SRS_GPERF_MC = RESERVED ]; then echo "you must specifies the gperf-mc, see: ./configure --help"; __check_ok=NO; fi
|
if [ $SRS_GPERF_MC = RESERVED ]; then echo "you must specifies the gperf-mc, see: ./configure --help"; __check_ok=NO; fi
|
||||||
|
|
691
trunk/configure
vendored
691
trunk/configure
vendored
|
@ -32,9 +32,6 @@ rm -f ${SRS_WORKDIR}/${SRS_MAKEFILE}
|
||||||
# create objs
|
# create objs
|
||||||
mkdir -p ${SRS_OBJS}/${SRS_PLATFORM}
|
mkdir -p ${SRS_OBJS}/${SRS_PLATFORM}
|
||||||
|
|
||||||
# for export srs-librtmp, change target to it.
|
|
||||||
. auto/generate-srs-librtmp-project.sh
|
|
||||||
|
|
||||||
# apply user options.
|
# apply user options.
|
||||||
. auto/depends.sh
|
. auto/depends.sh
|
||||||
|
|
||||||
|
@ -47,9 +44,6 @@ mkdir -p ${SRS_OBJS}/${SRS_PLATFORM}
|
||||||
# ubuntu echo in Makefile cannot display color, use bash instead
|
# ubuntu echo in Makefile cannot display color, use bash instead
|
||||||
SRS_BUILD_SUMMARY="_srs_build_summary.sh"
|
SRS_BUILD_SUMMARY="_srs_build_summary.sh"
|
||||||
|
|
||||||
# srs-librtmp sample entry
|
|
||||||
SrsLibrtmpSampleEntry="nossl"
|
|
||||||
if [ $SRS_SSL = YES ]; then SrsLibrtmpSampleEntry="ssl";fi
|
|
||||||
# utest make entry, (cd utest; make)
|
# utest make entry, (cd utest; make)
|
||||||
SrsUtestMakeEntry="@echo -e \"ignore utest for it's disabled\""
|
SrsUtestMakeEntry="@echo -e \"ignore utest for it's disabled\""
|
||||||
if [ $SRS_UTEST = YES ]; then SrsUtestMakeEntry="(cd ${SRS_OBJS_DIR}/${SRS_PLATFORM}/utest && \$(MAKE))"; fi
|
if [ $SRS_UTEST = YES ]; then SrsUtestMakeEntry="(cd ${SRS_OBJS_DIR}/${SRS_PLATFORM}/utest && \$(MAKE))"; fi
|
||||||
|
@ -90,15 +84,14 @@ END
|
||||||
# enable gdb debug
|
# enable gdb debug
|
||||||
GDBDebug=" -g -O0"
|
GDBDebug=" -g -O0"
|
||||||
# the warning level.
|
# the warning level.
|
||||||
WarnLevel=" -Wall"
|
WarnLevel=" -Wall -Wno-deprecated-declarations"
|
||||||
# the compile standard.
|
# the compile standard.
|
||||||
CppStd="-ansi"
|
CppStd="-ansi"
|
||||||
if [[ $SRS_SRT == YES ]]; then
|
if [[ $SRS_CXX11 == YES ]]; then
|
||||||
CppStd="-std=c++11"
|
CppStd="-std=c++11"
|
||||||
fi
|
fi
|
||||||
# for library compile
|
if [[ $SRS_CXX14 == YES ]]; then
|
||||||
if [[ $SRS_EXPORT_LIBRTMP_PROJECT == YES ]]; then
|
CppStd="-std=c++14"
|
||||||
LibraryCompile=" -fPIC"
|
|
||||||
fi
|
fi
|
||||||
# performance of gprof
|
# performance of gprof
|
||||||
SrsGprof=""; SrsGprofLink=""; if [ $SRS_GPROF = YES ]; then SrsGprof=" -pg -lc_p"; SrsGprofLink=" -pg"; fi
|
SrsGprof=""; SrsGprofLink=""; if [ $SRS_GPROF = YES ]; then SrsGprof=" -pg -lc_p"; SrsGprofLink=" -pg"; fi
|
||||||
|
@ -129,7 +122,7 @@ ARFLAGS = -rs
|
||||||
LINK = ${SRS_TOOL_CXX}
|
LINK = ${SRS_TOOL_CXX}
|
||||||
CXXFLAGS = ${CXXFLAGS}
|
CXXFLAGS = ${CXXFLAGS}
|
||||||
|
|
||||||
.PHONY: default srs srs_ingest_hls librtmp
|
.PHONY: default srs srs_ingest_hls
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
||||||
|
@ -143,20 +136,24 @@ END
|
||||||
# st(state-threads) the basic network library for SRS.
|
# st(state-threads) the basic network library for SRS.
|
||||||
LibSTRoot="${SRS_OBJS_DIR}/st"; LibSTfile="${LibSTRoot}/libst.a"
|
LibSTRoot="${SRS_OBJS_DIR}/st"; LibSTfile="${LibSTRoot}/libst.a"
|
||||||
if [[ $SRS_SHARED_ST == YES ]]; then LibSTfile="-lst"; fi
|
if [[ $SRS_SHARED_ST == YES ]]; then LibSTfile="-lst"; fi
|
||||||
|
|
||||||
# srtp
|
# srtp
|
||||||
if [[ $SRS_RTC == YES ]]; then
|
if [[ $SRS_RTC == YES ]]; then
|
||||||
LibSrtpRoot="${SRS_OBJS_DIR}/srtp2/include"; LibSrtpFile="${SRS_OBJS_DIR}/srtp2/lib/libsrtp2.a"
|
LibSrtpRoot="${SRS_OBJS_DIR}/srtp2/include"; LibSrtpFile="${SRS_OBJS_DIR}/srtp2/lib/libsrtp2.a"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# FFMPEG for WebRTC transcoding, such as aac to opus.
|
# FFMPEG for WebRTC transcoding, such as aac to opus.
|
||||||
if [[ $SRS_RTC == YES ]]; then
|
if [[ $SRS_FFMPEG_FIT == YES ]]; then
|
||||||
LibFfmpegRoot="${SRS_OBJS_DIR}/ffmpeg/include"; LibFfmpegFile="${SRS_OBJS_DIR}/ffmpeg/lib/libavcodec.a ${SRS_OBJS_DIR}/ffmpeg/lib/libswresample.a ${SRS_OBJS_DIR}/ffmpeg/lib/libavutil.a"
|
LibFfmpegRoot="${SRS_OBJS_DIR}/ffmpeg/include"; LibFfmpegFile="${SRS_OBJS_DIR}/ffmpeg/lib/libavcodec.a ${SRS_OBJS_DIR}/ffmpeg/lib/libswresample.a ${SRS_OBJS_DIR}/ffmpeg/lib/libavutil.a"
|
||||||
LibFfmpegRoot="${LibFfmpegRoot} ${SRS_OBJS_DIR}/opus/include"; LibFfmpegFile="${LibFfmpegFile} ${SRS_OBJS_DIR}/opus/lib/libopus.a"
|
LibFfmpegRoot="${LibFfmpegRoot} ${SRS_OBJS_DIR}/opus/include"; LibFfmpegFile="${LibFfmpegFile} ${SRS_OBJS_DIR}/opus/lib/libopus.a"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# openssl-1.1.0e, for the RTMP complex handshake.
|
# openssl-1.1.0e, for the RTMP complex handshake.
|
||||||
LibSSLRoot="";LibSSLfile=""
|
LibSSLRoot="";LibSSLfile=""
|
||||||
if [[ $SRS_SSL == YES && $SRS_USE_SYS_SSL == NO ]]; then
|
if [[ $SRS_SSL == YES && $SRS_USE_SYS_SSL == NO ]]; then
|
||||||
LibSSLRoot="${SRS_OBJS_DIR}/openssl/include"; LibSSLfile="${SRS_OBJS_DIR}/openssl/lib/libssl.a ${SRS_OBJS_DIR}/openssl/lib/libcrypto.a";
|
LibSSLRoot="${SRS_OBJS_DIR}/openssl/include"; LibSSLfile="${SRS_OBJS_DIR}/openssl/lib/libssl.a ${SRS_OBJS_DIR}/openssl/lib/libcrypto.a";
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# gperftools-2.1, for mem check and mem/cpu profile
|
# gperftools-2.1, for mem check and mem/cpu profile
|
||||||
LibGperfRoot=""; LibGperfFile=""
|
LibGperfRoot=""; LibGperfFile=""
|
||||||
if [ $SRS_GPERF = YES ]; then
|
if [ $SRS_GPERF = YES ]; then
|
||||||
|
@ -165,30 +162,37 @@ fi
|
||||||
if [ $SRS_GPERF_MD = YES ]; then
|
if [ $SRS_GPERF_MD = YES ]; then
|
||||||
LibGperfFile="${SRS_OBJS_DIR}/gperf/lib/libtcmalloc_debug.a";
|
LibGperfFile="${SRS_OBJS_DIR}/gperf/lib/libtcmalloc_debug.a";
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# srt code path
|
# srt code path
|
||||||
if [[ $SRS_SRT == YES ]]; then
|
if [[ $SRS_SRT == YES ]]; then
|
||||||
LibSRTRoot="${SRS_WORKDIR}/src/srt"; LibSRTfile="${SRS_OBJS_DIR}/srt/lib/libsrt.a"
|
LibSRTRoot="${SRS_WORKDIR}/src/srt"; LibSRTfile="${SRS_OBJS_DIR}/srt/lib/libsrt.a"
|
||||||
if [[ $SRS_SHARED_SRT == YES ]]; then LibSRTfile="-lsrt"; fi
|
if [[ $SRS_SHARED_SRT == YES ]]; then LibSRTfile="-lsrt"; fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# the link options, always use static link
|
# the link options, always use static link
|
||||||
SrsLinkOptions="-ldl";
|
SrsLinkOptions="-ldl";
|
||||||
if [[ $SRS_SRT == YES || $SRS_RTC == YES ]]; then
|
if [[ $SRS_SRT == YES || $SRS_RTC == YES ]]; then
|
||||||
SrsLinkOptions="${SrsLinkOptions} -lpthread";
|
SrsLinkOptions="${SrsLinkOptions} -lpthread";
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $SRS_SSL == YES && $SRS_USE_SYS_SSL == YES ]]; then
|
if [[ $SRS_SSL == YES && $SRS_USE_SYS_SSL == YES ]]; then
|
||||||
SrsLinkOptions="${SrsLinkOptions} -lssl -lcrypto";
|
SrsLinkOptions="${SrsLinkOptions} -lssl -lcrypto";
|
||||||
fi
|
fi
|
||||||
# if static specified, add static
|
|
||||||
# TODO: FIXME: remove static.
|
# Static link the c++ libraries, for user who build SRS by a new version of gcc,
|
||||||
|
# so we need to link the c++ libraries staticly but not all.
|
||||||
|
# @see https://stackoverflow.com/a/26107550
|
||||||
if [ $SRS_STATIC = YES ]; then
|
if [ $SRS_STATIC = YES ]; then
|
||||||
SrsLinkOptions="${SrsLinkOptions} -static";
|
SrsLinkOptions="${SrsLinkOptions} -static-libstdc++";
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# For coverage.
|
# For coverage.
|
||||||
if [[ $SRS_GCOV == YES ]]; then
|
if [[ $SRS_GCOV == YES ]]; then
|
||||||
SrsLinkOptions="${SrsLinkOptions} ${SrsGcov}";
|
SrsLinkOptions="${SrsLinkOptions} ${SrsGcov}";
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# For FFMPEG/RTC.
|
# For FFMPEG/RTC.
|
||||||
if [[ $SRS_RTC == YES && $SRS_NASM == NO ]]; then
|
if [[ $SRS_RTC == YES && $SRS_NASM == NO && $SRS_OSX == NO ]]; then
|
||||||
SrsLinkOptions="${SrsLinkOptions} -lrt";
|
SrsLinkOptions="${SrsLinkOptions} -lrt";
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -213,7 +217,7 @@ MODULE_FILES=("srs_kernel_error" "srs_kernel_log" "srs_kernel_buffer"
|
||||||
"srs_kernel_consts" "srs_kernel_aac" "srs_kernel_mp3" "srs_kernel_ts"
|
"srs_kernel_consts" "srs_kernel_aac" "srs_kernel_mp3" "srs_kernel_ts"
|
||||||
"srs_kernel_stream" "srs_kernel_balance" "srs_kernel_mp4" "srs_kernel_file")
|
"srs_kernel_stream" "srs_kernel_balance" "srs_kernel_mp4" "srs_kernel_file")
|
||||||
if [[ $SRS_RTC == YES ]]; then
|
if [[ $SRS_RTC == YES ]]; then
|
||||||
MODULE_FILES+=("srs_kernel_rtp")
|
MODULE_FILES+=("srs_kernel_rtc_rtp" "srs_kernel_rtc_rtcp")
|
||||||
fi
|
fi
|
||||||
KERNEL_INCS="src/kernel"; MODULE_DIR=${KERNEL_INCS} . auto/modules.sh
|
KERNEL_INCS="src/kernel"; MODULE_DIR=${KERNEL_INCS} . auto/modules.sh
|
||||||
KERNEL_OBJS="${MODULE_OBJS[@]}"
|
KERNEL_OBJS="${MODULE_OBJS[@]}"
|
||||||
|
@ -221,13 +225,21 @@ KERNEL_OBJS="${MODULE_OBJS[@]}"
|
||||||
#RTMP/HTTP/Raw Protocol, depends on core/kernel, provides rtmp/htttp protocol features.
|
#RTMP/HTTP/Raw Protocol, depends on core/kernel, provides rtmp/htttp protocol features.
|
||||||
MODULE_ID="PROTOCOL"
|
MODULE_ID="PROTOCOL"
|
||||||
MODULE_DEPENDS=("CORE" "KERNEL")
|
MODULE_DEPENDS=("CORE" "KERNEL")
|
||||||
ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSSLRoot})
|
ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSTRoot} ${LibSSLRoot})
|
||||||
MODULE_FILES=("srs_protocol_amf0" "srs_protocol_io" "srs_rtmp_stack"
|
MODULE_FILES=("srs_protocol_amf0" "srs_protocol_io" "srs_rtmp_stack"
|
||||||
"srs_rtmp_handshake" "srs_protocol_utility" "srs_rtmp_msg_array" "srs_protocol_stream"
|
"srs_rtmp_handshake" "srs_protocol_utility" "srs_rtmp_msg_array" "srs_protocol_stream"
|
||||||
"srs_raw_avc" "srs_rtsp_stack" "srs_sip_stack" "srs_http_stack" "srs_protocol_kbps" "srs_protocol_json"
|
"srs_raw_avc" "srs_rtsp_stack" "srs_http_stack" "srs_protocol_kbps" "srs_protocol_json"
|
||||||
"srs_protocol_format")
|
"srs_protocol_format" "srs_service_log" "srs_service_st" "srs_service_http_client" "srs_service_http_conn"
|
||||||
|
"srs_service_rtmp_conn" "srs_service_utility" "srs_service_conn")
|
||||||
if [[ $SRS_RTC == YES ]]; then
|
if [[ $SRS_RTC == YES ]]; then
|
||||||
MODULE_FILES+=("srs_stun_stack")
|
MODULE_FILES+=("srs_rtc_stun_stack")
|
||||||
|
ModuleLibIncs+=(${LibSrtpRoot})
|
||||||
|
fi
|
||||||
|
if [[ $SRS_GB28181 == YES ]]; then
|
||||||
|
MODULE_FILES+=("srs_sip_stack")
|
||||||
|
fi
|
||||||
|
if [[ $SRS_FFMPEG_FIT == YES ]]; then
|
||||||
|
ModuleLibIncs+=("${LibFfmpegRoot[*]}")
|
||||||
fi
|
fi
|
||||||
PROTOCOL_INCS="src/protocol"; MODULE_DIR=${PROTOCOL_INCS} . auto/modules.sh
|
PROTOCOL_INCS="src/protocol"; MODULE_DIR=${PROTOCOL_INCS} . auto/modules.sh
|
||||||
PROTOCOL_OBJS="${MODULE_OBJS[@]}"
|
PROTOCOL_OBJS="${MODULE_OBJS[@]}"
|
||||||
|
@ -235,167 +247,154 @@ PROTOCOL_OBJS="${MODULE_OBJS[@]}"
|
||||||
#srt protocol features.
|
#srt protocol features.
|
||||||
if [ $SRS_SRT = YES ]; then
|
if [ $SRS_SRT = YES ]; then
|
||||||
MODULE_ID="SRT"
|
MODULE_ID="SRT"
|
||||||
MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "SERVICE" "APP")
|
MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "APP")
|
||||||
ModuleLibIncs=(${SRS_OBJS_DIR})
|
ModuleLibIncs=(${SRS_OBJS_DIR})
|
||||||
MODULE_FILES=("srt_server" "srt_handle" "srt_conn" "srt_to_rtmp" "ts_demux" "srt_data")
|
MODULE_FILES=("srt_server" "srt_handle" "srt_conn" "srt_to_rtmp" "ts_demux" "srt_data")
|
||||||
SRT_INCS=${LibSRTRoot}; MODULE_DIR=${LibSRTRoot} . auto/modules.sh
|
SRT_INCS=${LibSRTRoot}; MODULE_DIR=${LibSRTRoot} . auto/modules.sh
|
||||||
SRT_OBJS="${MODULE_OBJS[@]}"
|
SRT_OBJS="${MODULE_OBJS[@]}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#
|
|
||||||
#Service Module, for both Server and Client Modules.
|
|
||||||
if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
|
|
||||||
MODULE_ID="SERVICE"
|
|
||||||
MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL")
|
|
||||||
ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibSSLRoot})
|
|
||||||
if [[ $SRS_RTC == YES ]]; then
|
|
||||||
ModuleLibIncs+=("${LibFfmpegRoot[*]}" ${LibSrtpRoot})
|
|
||||||
fi
|
|
||||||
MODULE_FILES=("srs_service_log" "srs_service_st" "srs_service_http_client"
|
|
||||||
"srs_service_http_conn" "srs_service_rtmp_conn" "srs_service_utility"
|
|
||||||
"srs_service_conn")
|
|
||||||
DEFINES=""
|
|
||||||
SERVICE_INCS="src/service"; MODULE_DIR=${SERVICE_INCS} . auto/modules.sh
|
|
||||||
SERVICE_OBJS="${MODULE_OBJS[@]}"
|
|
||||||
fi
|
|
||||||
#
|
#
|
||||||
#App Module, for SRS server only.
|
#App Module, for SRS server only.
|
||||||
if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
|
MODULE_ID="APP"
|
||||||
MODULE_ID="APP"
|
|
||||||
MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "SERVICE")
|
|
||||||
ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibSSLRoot} ${LibGperfRoot})
|
|
||||||
if [[ $SRS_RTC == YES ]]; then
|
|
||||||
ModuleLibIncs+=("${LibFfmpegRoot[*]}" ${LibSrtpRoot})
|
|
||||||
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_thread" "srs_app_bandwidth" "srs_app_st" "srs_app_log" "srs_app_config"
|
|
||||||
"srs_app_pithy_print" "srs_app_reload" "srs_app_http_api" "srs_app_http_conn" "srs_app_http_hooks"
|
|
||||||
"srs_app_ingest" "srs_app_ffmpeg" "srs_app_utility" "srs_app_edge"
|
|
||||||
"srs_app_heartbeat" "srs_app_empty" "srs_app_http_client" "srs_app_http_static"
|
|
||||||
"srs_app_recv_thread" "srs_app_security" "srs_app_statistic" "srs_app_hds"
|
|
||||||
"srs_app_mpegts_udp" "srs_app_rtsp" "srs_app_listener" "srs_app_async_call"
|
|
||||||
"srs_app_caster_flv" "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")
|
|
||||||
if [[ $SRS_RTC == YES ]]; then
|
|
||||||
MODULE_FILES+=("srs_app_rtc" "srs_app_rtc_conn" "srs_app_dtls" "srs_app_audio_recode" "srs_app_sdp")
|
|
||||||
fi
|
|
||||||
if [[ $SRS_GB28181 == YES ]]; then
|
|
||||||
MODULE_FILES+=("srs_app_gb28181" "srs_app_gb28181_sip")
|
|
||||||
fi
|
|
||||||
DEFINES=""
|
|
||||||
# add each modules for app
|
|
||||||
for SRS_MODULE in ${SRS_MODULES[*]}; do
|
|
||||||
. $SRS_MODULE/config
|
|
||||||
MODULE_FILES+=("${SRS_MODULE_APP[*]}")
|
|
||||||
DEFINES="${DEFINES} ${SRS_MODULE_DEFINES}"
|
|
||||||
done
|
|
||||||
APP_INCS="src/app"; MODULE_DIR=${APP_INCS} . auto/modules.sh
|
|
||||||
APP_OBJS="${MODULE_OBJS[@]}"
|
|
||||||
fi
|
|
||||||
#
|
|
||||||
#LIBS Module, build libsrs.a for static link.
|
|
||||||
MODULE_ID="LIBS"
|
|
||||||
MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL")
|
MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL")
|
||||||
ModuleLibIncs=(${SRS_OBJS_DIR})
|
ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSTRoot} ${LibSSLRoot})
|
||||||
MODULE_FILES=("srs_librtmp" "srs_lib_simple_socket" "srs_lib_bandwidth")
|
if [ $SRS_GPERF = YES ]; then
|
||||||
LIBS_INCS="src/libs"; MODULE_DIR=${LIBS_INCS} . auto/modules.sh
|
ModuleLibIncs+=(${LibGperfRoot})
|
||||||
LIBS_OBJS="${MODULE_OBJS[@]}"
|
fi
|
||||||
|
if [[ $SRS_RTC == YES ]]; then
|
||||||
|
ModuleLibIncs+=(${LibSrtpRoot})
|
||||||
|
fi
|
||||||
|
if [[ $SRS_FFMPEG_FIT == YES ]]; then
|
||||||
|
ModuleLibIncs+=("${LibFfmpegRoot[*]}")
|
||||||
|
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"
|
||||||
|
"srs_app_pithy_print" "srs_app_reload" "srs_app_http_api" "srs_app_http_conn" "srs_app_http_hooks"
|
||||||
|
"srs_app_ingest" "srs_app_ffmpeg" "srs_app_utility" "srs_app_edge"
|
||||||
|
"srs_app_heartbeat" "srs_app_empty" "srs_app_http_client" "srs_app_http_static"
|
||||||
|
"srs_app_recv_thread" "srs_app_security" "srs_app_statistic" "srs_app_hds"
|
||||||
|
"srs_app_mpegts_udp" "srs_app_rtsp" "srs_app_listener" "srs_app_async_call"
|
||||||
|
"srs_app_caster_flv" "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")
|
||||||
|
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")
|
||||||
|
fi
|
||||||
|
if [[ $SRS_FFMPEG_FIT == YES ]]; then
|
||||||
|
MODULE_FILES+=("srs_app_rtc_codec")
|
||||||
|
fi
|
||||||
|
if [[ $SRS_GB28181 == YES ]]; then
|
||||||
|
MODULE_FILES+=("srs_app_gb28181" "srs_app_gb28181_sip" "srs_app_gb28181_jitbuffer")
|
||||||
|
fi
|
||||||
|
DEFINES=""
|
||||||
|
# add each modules for app
|
||||||
|
for SRS_MODULE in ${SRS_MODULES[*]}; do
|
||||||
|
. $SRS_MODULE/config
|
||||||
|
MODULE_FILES+=("${SRS_MODULE_APP[*]}")
|
||||||
|
DEFINES="${DEFINES} ${SRS_MODULE_DEFINES}"
|
||||||
|
done
|
||||||
|
APP_INCS="src/app"; MODULE_DIR=${APP_INCS} . auto/modules.sh
|
||||||
|
APP_OBJS="${MODULE_OBJS[@]}"
|
||||||
#
|
#
|
||||||
#Server Module, for SRS only.
|
#Server Module, for SRS only.
|
||||||
if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
|
MODULE_ID="SERVER"
|
||||||
MODULE_ID="SERVER"
|
MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "APP")
|
||||||
MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "SERVICE" "APP")
|
if [[ $SRS_SRT == YES ]]; then
|
||||||
if [[ $SRS_SRT == YES ]]; then
|
MODULE_DEPENDS+=("SRT")
|
||||||
MODULE_DEPENDS+=("SRT")
|
|
||||||
fi
|
|
||||||
ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot})
|
|
||||||
if [[ $SRS_RTC == YES ]]; then
|
|
||||||
ModuleLibIncs+=("${LibFfmpegRoot[*]}" ${LibSrtpRoot})
|
|
||||||
fi
|
|
||||||
if [[ $SRS_SRT == YES ]]; then
|
|
||||||
ModuleLibIncs+=("${LibSRTRoot[*]}")
|
|
||||||
fi
|
|
||||||
MODULE_FILES=("srs_main_server")
|
|
||||||
SERVER_INCS="src/main"; MODULE_DIR=${SERVER_INCS} . auto/modules.sh
|
|
||||||
SERVER_OBJS="${MODULE_OBJS[@]}"
|
|
||||||
fi
|
fi
|
||||||
|
ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSTRoot} ${LibGperfRoot} ${LibSSLRoot})
|
||||||
|
if [[ $SRS_RTC == YES ]]; then
|
||||||
|
ModuleLibIncs+=(${LibSrtpRoot})
|
||||||
|
fi
|
||||||
|
if [[ $SRS_FFMPEG_FIT == YES ]]; then
|
||||||
|
ModuleLibIncs+=("${LibFfmpegRoot[*]}")
|
||||||
|
fi
|
||||||
|
if [[ $SRS_SRT == YES ]]; then
|
||||||
|
ModuleLibIncs+=("${LibSRTRoot[*]}")
|
||||||
|
fi
|
||||||
|
MODULE_FILES=("srs_main_server")
|
||||||
|
SERVER_INCS="src/main"; MODULE_DIR=${SERVER_INCS} . auto/modules.sh
|
||||||
|
SERVER_OBJS="${MODULE_OBJS[@]}"
|
||||||
#
|
#
|
||||||
#Main Module, for app from modules.
|
#Main Module, for app from modules.
|
||||||
if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
|
MODULE_ID="MAIN"
|
||||||
MODULE_ID="MAIN"
|
MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL")
|
||||||
MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "SERVICE")
|
ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSTRoot} ${LibGperfRoot} ${LibSSLRoot})
|
||||||
ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot})
|
if [[ $SRS_RTC == YES ]]; then
|
||||||
if [[ $SRS_RTC == YES ]]; then
|
ModuleLibIncs+=(${LibSrtpRoot})
|
||||||
ModuleLibIncs+=("${LibFfmpegRoot[*]}" ${LibSrtpRoot})
|
|
||||||
fi
|
|
||||||
MODULE_FILES=()
|
|
||||||
DEFINES=""
|
|
||||||
# add each modules for main
|
|
||||||
for SRS_MODULE in ${SRS_MODULES[*]}; do
|
|
||||||
. $SRS_MODULE/config
|
|
||||||
MODULE_FILES+=("${SRS_MODULE_MAIN[*]}")
|
|
||||||
DEFINES="${DEFINES} ${SRS_MODULE_DEFINES}"
|
|
||||||
done
|
|
||||||
MAIN_INCS="src/main"; MODULE_DIR=${MAIN_INCS} . auto/modules.sh
|
|
||||||
MAIN_OBJS="${MODULE_OBJS[@]}"
|
|
||||||
fi
|
fi
|
||||||
|
if [[ $SRS_FFMPEG_FIT == YES ]]; then
|
||||||
|
ModuleLibIncs+=("${LibFfmpegRoot[*]}")
|
||||||
|
fi
|
||||||
|
MODULE_FILES=()
|
||||||
|
DEFINES=""
|
||||||
|
# add each modules for main
|
||||||
|
for SRS_MODULE in ${SRS_MODULES[*]}; do
|
||||||
|
. $SRS_MODULE/config
|
||||||
|
MODULE_FILES+=("${SRS_MODULE_MAIN[*]}")
|
||||||
|
DEFINES="${DEFINES} ${SRS_MODULE_DEFINES}"
|
||||||
|
done
|
||||||
|
MAIN_INCS="src/main"; MODULE_DIR=${MAIN_INCS} . auto/modules.sh
|
||||||
|
MAIN_OBJS="${MODULE_OBJS[@]}"
|
||||||
|
|
||||||
#####################################################################################
|
#####################################################################################
|
||||||
# Binaries, main entrances, link the module and its depends modules,
|
# Binaries, main entrances, link the module and its depends modules,
|
||||||
# then link to a binary, for example, objs/srs
|
# then link to a binary, for example, objs/srs
|
||||||
#
|
#
|
||||||
# Disable SRS application for exporting librtmp.
|
# all main entrances
|
||||||
if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
|
MAIN_ENTRANCES=("srs_main_server")
|
||||||
# all main entrances
|
for SRS_MODULE in ${SRS_MODULES[*]}; do
|
||||||
MAIN_ENTRANCES=("srs_main_server")
|
. $SRS_MODULE/config
|
||||||
for SRS_MODULE in ${SRS_MODULES[*]}; do
|
MAIN_ENTRANCES+=("${SRS_MODULE_MAIN[*]}")
|
||||||
. $SRS_MODULE/config
|
done
|
||||||
MAIN_ENTRANCES+=("${SRS_MODULE_MAIN[*]}")
|
#
|
||||||
done
|
# all depends libraries
|
||||||
#
|
ModuleLibFiles=(${LibSTfile} ${LibSSLfile} ${LibGperfFile})
|
||||||
# all depends libraries
|
if [[ $SRS_RTC == YES ]]; then
|
||||||
ModuleLibFiles=(${LibSTfile} ${LibSSLfile} ${LibGperfFile})
|
ModuleLibFiles+=(${LibSrtpFile})
|
||||||
if [[ $SRS_RTC == YES ]]; then
|
|
||||||
ModuleLibFiles+=("${LibFfmpegFile[*]}" ${LibSrtpFile})
|
|
||||||
fi
|
|
||||||
if [[ $SRS_SRT == YES ]]; then
|
|
||||||
ModuleLibFiles+=("${LibSRTfile[*]}")
|
|
||||||
fi
|
|
||||||
# all depends objects
|
|
||||||
MODULE_OBJS="${CORE_OBJS[@]} ${KERNEL_OBJS[@]} ${PROTOCOL_OBJS[@]} ${SERVICE_OBJS[@]} ${APP_OBJS[@]} ${SERVER_OBJS[@]}"
|
|
||||||
ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot})
|
|
||||||
if [[ $SRS_RTC == YES ]]; then
|
|
||||||
ModuleLibIncs+=("${LibFfmpegRoot[*]}" ${LibSrtpRoot})
|
|
||||||
fi
|
|
||||||
if [[ $SRS_SRT == YES ]]; then
|
|
||||||
MODULE_OBJS="${MODULE_OBJS} ${SRT_OBJS[@]}"
|
|
||||||
fi
|
|
||||||
LINK_OPTIONS="${SrsLinkOptions}${SrsGprofLink}${SrsGperfLink}"
|
|
||||||
#
|
|
||||||
# srs: srs(simple rtmp server) over st(state-threads)
|
|
||||||
BUILD_KEY="srs" APP_MAIN="srs_main_server" APP_NAME="srs" . auto/apps.sh
|
|
||||||
#
|
|
||||||
# For modules, without the app module.
|
|
||||||
MODULE_OBJS="${CORE_OBJS[@]} ${KERNEL_OBJS[@]} ${PROTOCOL_OBJS[@]} ${SERVICE_OBJS[@]} ${MAIN_OBJS[@]}"
|
|
||||||
ModuleLibFiles=(${LibSTfile} ${LibSSLfile} ${LibGperfFile})
|
|
||||||
if [[ $SRS_RTC == YES ]]; then
|
|
||||||
ModuleLibFiles+=("${LibFfmpegFile[*]}" ${LibSrtpFile})
|
|
||||||
fi
|
|
||||||
#
|
|
||||||
for SRS_MODULE in ${SRS_MODULES[*]}; do
|
|
||||||
. $SRS_MODULE/config
|
|
||||||
# no SRS_MODULE_MAIN
|
|
||||||
if [[ 0 -eq ${#SRS_MODULE_MAIN[@]} ]]; then continue; fi
|
|
||||||
BUILD_KEY="$SRS_MODULE_NAME" APP_MAIN="${SRS_MODULE_MAIN[0]}" APP_NAME="$SRS_MODULE_NAME" . auto/apps.sh
|
|
||||||
done
|
|
||||||
fi
|
fi
|
||||||
# srs librtmp
|
if [[ $SRS_FFMPEG_FIT == YES ]]; then
|
||||||
if [ $SRS_LIBRTMP = YES ]; then
|
ModuleLibFiles+=("${LibFfmpegFile[*]}")
|
||||||
MODULE_OBJS="${CORE_OBJS[@]} ${KERNEL_OBJS[@]} ${PROTOCOL_OBJS[@]} ${LIBS_OBJS[@]}"
|
|
||||||
BUILD_KEY="librtmp" LIB_NAME="lib/srs_librtmp" . auto/libs.sh
|
|
||||||
fi
|
fi
|
||||||
|
if [[ $SRS_SRT == YES ]]; then
|
||||||
|
ModuleLibFiles+=("${LibSRTfile[*]}")
|
||||||
|
fi
|
||||||
|
# all depends objects
|
||||||
|
MODULE_OBJS="${CORE_OBJS[@]} ${KERNEL_OBJS[@]} ${PROTOCOL_OBJS[@]} ${APP_OBJS[@]} ${SERVER_OBJS[@]}"
|
||||||
|
ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSTRoot} ${LibGperfRoot} ${LibSSLRoot})
|
||||||
|
if [[ $SRS_RTC == YES ]]; then
|
||||||
|
ModuleLibIncs+=(${LibSrtpRoot})
|
||||||
|
fi
|
||||||
|
if [[ $SRS_FFMPEG_FIT == YES ]]; then
|
||||||
|
ModuleLibIncs+=("${LibFfmpegRoot[*]}")
|
||||||
|
fi
|
||||||
|
if [[ $SRS_SRT == YES ]]; then
|
||||||
|
MODULE_OBJS="${MODULE_OBJS} ${SRT_OBJS[@]}"
|
||||||
|
fi
|
||||||
|
LINK_OPTIONS="${SrsLinkOptions}${SrsGprofLink}${SrsGperfLink}"
|
||||||
|
#
|
||||||
|
# srs: srs(simple rtmp server) over st(state-threads)
|
||||||
|
BUILD_KEY="srs" APP_MAIN="srs_main_server" APP_NAME="srs" . auto/apps.sh
|
||||||
|
#
|
||||||
|
# For modules, without the app module.
|
||||||
|
MODULE_OBJS="${CORE_OBJS[@]} ${KERNEL_OBJS[@]} ${PROTOCOL_OBJS[@]} ${MAIN_OBJS[@]}"
|
||||||
|
ModuleLibFiles=(${LibSTfile} ${LibSSLfile} ${LibGperfFile})
|
||||||
|
if [[ $SRS_RTC == YES ]]; then
|
||||||
|
ModuleLibFiles+=(${LibSrtpFile})
|
||||||
|
fi
|
||||||
|
if [[ $SRS_FFMPEG_FIT == YES ]]; then
|
||||||
|
ModuleLibFiles+=("${LibFfmpegFile[*]}")
|
||||||
|
fi
|
||||||
|
#
|
||||||
|
for SRS_MODULE in ${SRS_MODULES[*]}; do
|
||||||
|
. $SRS_MODULE/config
|
||||||
|
# no SRS_MODULE_MAIN
|
||||||
|
if [[ 0 -eq ${#SRS_MODULE_MAIN[@]} ]]; then continue; fi
|
||||||
|
BUILD_KEY="$SRS_MODULE_NAME" APP_MAIN="${SRS_MODULE_MAIN[0]}" APP_NAME="$SRS_MODULE_NAME" . auto/apps.sh
|
||||||
|
done
|
||||||
# For utest on mac.
|
# For utest on mac.
|
||||||
# @see https://github.com/protocolbuffers/protobuf/issues/51#issuecomment-111044468
|
# @see https://github.com/protocolbuffers/protobuf/issues/51#issuecomment-111044468
|
||||||
if [[ $SRS_OSX == YES ]]; then
|
if [[ $SRS_OSX == YES ]]; then
|
||||||
|
@ -406,26 +405,32 @@ fi
|
||||||
if [ $SRS_UTEST = YES ]; then
|
if [ $SRS_UTEST = YES ]; then
|
||||||
MODULE_FILES=("srs_utest" "srs_utest_amf0" "srs_utest_protocol" "srs_utest_kernel" "srs_utest_core"
|
MODULE_FILES=("srs_utest" "srs_utest_amf0" "srs_utest_protocol" "srs_utest_kernel" "srs_utest_core"
|
||||||
"srs_utest_config" "srs_utest_rtmp" "srs_utest_http" "srs_utest_avc" "srs_utest_reload"
|
"srs_utest_config" "srs_utest_rtmp" "srs_utest_http" "srs_utest_avc" "srs_utest_reload"
|
||||||
"srs_utest_mp4" "srs_utest_service" "srs_utest_app")
|
"srs_utest_mp4" "srs_utest_service" "srs_utest_app" "srs_utest_rtc")
|
||||||
ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSTRoot} ${LibSSLRoot})
|
ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSTRoot} ${LibSSLRoot})
|
||||||
if [[ $SRS_RTC == YES ]]; then
|
if [[ $SRS_RTC == YES ]]; then
|
||||||
ModuleLibIncs+=("${LibFfmpegRoot[*]}" ${LibSrtpRoot})
|
ModuleLibIncs+=(${LibSrtpRoot})
|
||||||
|
fi
|
||||||
|
if [[ $SRS_FFMPEG_FIT == YES ]]; then
|
||||||
|
ModuleLibIncs+=("${LibFfmpegRoot[*]}")
|
||||||
fi
|
fi
|
||||||
if [[ $SRS_SRT == YES ]]; then
|
if [[ $SRS_SRT == YES ]]; then
|
||||||
ModuleLibIncs+=("${LibSRTRoot[*]}")
|
ModuleLibIncs+=("${LibSRTRoot[*]}")
|
||||||
fi
|
fi
|
||||||
ModuleLibFiles=(${LibSTfile} ${LibSSLfile})
|
ModuleLibFiles=(${LibSTfile} ${LibSSLfile})
|
||||||
if [[ $SRS_RTC == YES ]]; then
|
if [[ $SRS_RTC == YES ]]; then
|
||||||
ModuleLibFiles+=("${LibFfmpegFile[*]}" ${LibSrtpFile})
|
ModuleLibFiles+=(${LibSrtpFile})
|
||||||
|
fi
|
||||||
|
if [[ $SRS_FFMPEG_FIT == YES ]]; then
|
||||||
|
ModuleLibFiles+=("${LibFfmpegFile[*]}")
|
||||||
fi
|
fi
|
||||||
if [[ $SRS_SRT == YES ]]; then
|
if [[ $SRS_SRT == YES ]]; then
|
||||||
ModuleLibFiles+=("${LibSRTfile[*]}")
|
ModuleLibFiles+=("${LibSRTfile[*]}")
|
||||||
fi
|
fi
|
||||||
MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "SERVICE" "APP")
|
MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "APP")
|
||||||
if [[ $SRS_SRT == YES ]]; then
|
if [[ $SRS_SRT == YES ]]; then
|
||||||
MODULE_DEPENDS+=("SRT")
|
MODULE_DEPENDS+=("SRT")
|
||||||
fi
|
fi
|
||||||
MODULE_OBJS="${CORE_OBJS[@]} ${KERNEL_OBJS[@]} ${PROTOCOL_OBJS[@]} ${SERVICE_OBJS[@]} ${APP_OBJS[@]} ${SRT_OBJS[@]}"
|
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
|
LINK_OPTIONS="-lpthread ${SrsLinkOptions}" MODULE_DIR="src/utest" APP_NAME="srs_utest" . auto/utest.sh
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -443,7 +448,7 @@ mv ${SRS_WORKDIR}/${SRS_MAKEFILE} ${SRS_WORKDIR}/${SRS_MAKEFILE}.bk
|
||||||
|
|
||||||
# generate phony header
|
# generate phony header
|
||||||
cat << END > ${SRS_WORKDIR}/${SRS_MAKEFILE}
|
cat << END > ${SRS_WORKDIR}/${SRS_MAKEFILE}
|
||||||
.PHONY: default _default install install-api help clean destroy server srs_ingest_hls librtmp utest _prepare_dir $__mphonys
|
.PHONY: default _default install install-api help clean destroy server srs_ingest_hls utest _prepare_dir $__mphonys
|
||||||
.PHONY: clean_srs clean_modules clean_openssl clean_nginx clean_cherrypy clean_srtp2 clean_opus clean_ffmpeg clean_st
|
.PHONY: clean_srs clean_modules clean_openssl clean_nginx clean_cherrypy clean_srtp2 clean_opus clean_ffmpeg clean_st
|
||||||
.PHONY: st ffmpeg
|
.PHONY: st ffmpeg
|
||||||
|
|
||||||
|
@ -457,19 +462,16 @@ default:
|
||||||
END
|
END
|
||||||
|
|
||||||
# the real entry for all platform:
|
# the real entry for all platform:
|
||||||
# the server, librtmp and utest
|
|
||||||
# where the bellow will check and disable some entry by only echo.
|
|
||||||
cat << END >> ${SRS_WORKDIR}/${SRS_MAKEFILE}
|
cat << END >> ${SRS_WORKDIR}/${SRS_MAKEFILE}
|
||||||
_default: server srs_ingest_hls librtmp utest __modules $__mdefaults
|
_default: server srs_ingest_hls utest __modules $__mdefaults
|
||||||
@bash objs/_srs_build_summary.sh
|
@bash objs/_srs_build_summary.sh
|
||||||
|
|
||||||
help:
|
help:
|
||||||
@echo "Usage: make <help>|<clean>|<destroy>|<server>|<librtmp>|<utest>|<install>|<install-api>|<uninstall>"
|
@echo "Usage: make <help>|<clean>|<destroy>|<server>|<utest>|<install>|<install-api>|<uninstall>"
|
||||||
@echo " help Display this help menu"
|
@echo " help Display this help menu"
|
||||||
@echo " clean Cleanup project and all depends"
|
@echo " clean Cleanup project and all depends"
|
||||||
@echo " destroy Cleanup all files for this platform in ${SRS_OBJS_DIR}/${SRS_PLATFORM}"
|
@echo " destroy Cleanup all files for this platform in ${SRS_OBJS_DIR}/${SRS_PLATFORM}"
|
||||||
@echo " server Build the srs and other modules in main"
|
@echo " server Build the srs and other modules in main"
|
||||||
@echo " librtmp Build the client publish/play library, and samples"
|
|
||||||
@echo " utest Build the utest for srs"
|
@echo " utest Build the utest for srs"
|
||||||
@echo " install Install srs to the prefix path"
|
@echo " install Install srs to the prefix path"
|
||||||
@echo " install-api Install srs and api-server to the prefix path"
|
@echo " install-api Install srs and api-server to the prefix path"
|
||||||
|
@ -491,7 +493,6 @@ doclean:
|
||||||
(cd ${SRS_OBJS_DIR} && rm -rf srs srs_utest $__mcleanups)
|
(cd ${SRS_OBJS_DIR} && rm -rf srs srs_utest $__mcleanups)
|
||||||
(cd ${SRS_OBJS_DIR} && rm -rf src/* include lib)
|
(cd ${SRS_OBJS_DIR} && rm -rf src/* include lib)
|
||||||
(mkdir -p ${SRS_OBJS_DIR}/utest && cd ${SRS_OBJS_DIR}/utest && rm -rf *.o *.a)
|
(mkdir -p ${SRS_OBJS_DIR}/utest && cd ${SRS_OBJS_DIR}/utest && rm -rf *.o *.a)
|
||||||
(cd research/librtmp && make clean)
|
|
||||||
(cd research/api-server/static-dir && rm -rf crossdomain.xml forward live players)
|
(cd research/api-server/static-dir && rm -rf crossdomain.xml forward live players)
|
||||||
|
|
||||||
clean: clean_srs clean_modules
|
clean: clean_srs clean_modules
|
||||||
|
@ -501,13 +502,13 @@ destroy:
|
||||||
(cd ${SRS_OBJS_DIR} && rm -rf ${SRS_PLATFORM})
|
(cd ${SRS_OBJS_DIR} && rm -rf ${SRS_PLATFORM})
|
||||||
|
|
||||||
clean_srs:
|
clean_srs:
|
||||||
(cd ${SRS_OBJS_DIR} && rm -rf srs srs_utest)
|
@(cd ${SRS_OBJS_DIR} && rm -rf srs srs_utest)
|
||||||
(cd ${SRS_OBJS_DIR}/${SRS_PLATFORM} && rm -rf include/* lib/*)
|
@(cd ${SRS_OBJS_DIR}/${SRS_PLATFORM} && rm -rf include/* lib/*)
|
||||||
(cd ${SRS_OBJS_DIR}/${SRS_PLATFORM} && find src -name "*.o" -delete)
|
@(cd ${SRS_OBJS_DIR}/${SRS_PLATFORM} && find src -name "*.o" -delete)
|
||||||
(cd ${SRS_OBJS_DIR}/${SRS_PLATFORM} && find utest -name "*.o" -delete)
|
@(cd ${SRS_OBJS_DIR}/${SRS_PLATFORM} && find utest -name "*.o" -delete)
|
||||||
|
|
||||||
clean_modules:
|
clean_modules:
|
||||||
(cd ${SRS_OBJS_DIR} && rm -rf $__mdefaults)
|
@(cd ${SRS_OBJS_DIR} && rm -rf $__mdefaults)
|
||||||
|
|
||||||
clean_openssl:
|
clean_openssl:
|
||||||
(cd ${SRS_OBJS_DIR}/${SRS_PLATFORM} && rm -rf openssl*)
|
(cd ${SRS_OBJS_DIR}/${SRS_PLATFORM} && rm -rf openssl*)
|
||||||
|
@ -533,7 +534,6 @@ clean_nginx:
|
||||||
(cd ${SRS_OBJS_DIR} && rm -rf nginx)
|
(cd ${SRS_OBJS_DIR} && rm -rf nginx)
|
||||||
|
|
||||||
clean_cherrypy:
|
clean_cherrypy:
|
||||||
(cd research/librtmp && make clean)
|
|
||||||
(cd research/api-server/static-dir && rm -rf crossdomain.xml forward live players)
|
(cd research/api-server/static-dir && rm -rf crossdomain.xml forward live players)
|
||||||
|
|
||||||
st:
|
st:
|
||||||
|
@ -556,56 +556,25 @@ for MMF in ${__makefiles[*]}; do
|
||||||
done
|
done
|
||||||
echo "" >> ${SRS_WORKDIR}/${SRS_MAKEFILE}
|
echo "" >> ${SRS_WORKDIR}/${SRS_MAKEFILE}
|
||||||
|
|
||||||
# if export librtmp, donot build the srs server.
|
cat << END >> ${SRS_WORKDIR}/${SRS_MAKEFILE}
|
||||||
if [ $SRS_EXPORT_LIBRTMP_PROJECT != NO ]; then
|
|
||||||
cat << END >> ${SRS_WORKDIR}/${SRS_MAKEFILE}
|
|
||||||
server: _prepare_dir
|
|
||||||
@echo "Ingore srs(simple rtmp server) for srs-librtmp"
|
|
||||||
|
|
||||||
END
|
|
||||||
else
|
|
||||||
cat << END >> ${SRS_WORKDIR}/${SRS_MAKEFILE}
|
|
||||||
server: _prepare_dir
|
server: _prepare_dir
|
||||||
@echo "Build the srs(simple rtmp server) over ST(state-threads)"
|
@echo "Build the srs(simple rtmp server) over ST(state-threads)"
|
||||||
\$(MAKE) -f ${SRS_OBJS_DIR}/${SRS_MAKEFILE} srs
|
\$(MAKE) -f ${SRS_OBJS_DIR}/${SRS_MAKEFILE} srs
|
||||||
|
|
||||||
END
|
END
|
||||||
fi
|
|
||||||
# generate all modules entry
|
# generate all modules entry
|
||||||
for SRS_MODULE in ${SRS_MODULES[*]}; do
|
for SRS_MODULE in ${SRS_MODULES[*]}; do
|
||||||
. $SRS_MODULE/config
|
. $SRS_MODULE/config
|
||||||
# if export librtmp, donot build the bravo-ingest.
|
cat << END >> ${SRS_WORKDIR}/${SRS_MAKEFILE}
|
||||||
if [ $SRS_EXPORT_LIBRTMP_PROJECT != NO ]; then
|
|
||||||
cat << END >> ${SRS_WORKDIR}/${SRS_MAKEFILE}
|
|
||||||
$SRS_MODULE_NAME: _prepare_dir server
|
|
||||||
@echo "Ingore the $SRS_MODULE_NAME for srs-librtmp"
|
|
||||||
|
|
||||||
END
|
|
||||||
else
|
|
||||||
cat << END >> ${SRS_WORKDIR}/${SRS_MAKEFILE}
|
|
||||||
$SRS_MODULE_NAME: _prepare_dir server
|
$SRS_MODULE_NAME: _prepare_dir server
|
||||||
@echo "Build the $SRS_MODULE_NAME over SRS"
|
@echo "Build the $SRS_MODULE_NAME over SRS"
|
||||||
\$(MAKE) -f ${SRS_OBJS_DIR}/${SRS_MAKEFILE} $SRS_MODULE_NAME
|
\$(MAKE) -f ${SRS_OBJS_DIR}/${SRS_MAKEFILE} $SRS_MODULE_NAME
|
||||||
|
|
||||||
END
|
END
|
||||||
fi
|
|
||||||
done
|
done
|
||||||
|
|
||||||
# disable install entry for srs-librtmp
|
# install entry
|
||||||
if [ $SRS_EXPORT_LIBRTMP_PROJECT != NO ]; then
|
cat << END >> ${SRS_WORKDIR}/${SRS_MAKEFILE}
|
||||||
cat << END >> ${SRS_WORKDIR}/${SRS_MAKEFILE}
|
|
||||||
uninstall:
|
|
||||||
@echo "Disable uninstall for srs-librtmp"
|
|
||||||
|
|
||||||
install-api:
|
|
||||||
@echo "Disable install-api for srs-librtmp"
|
|
||||||
|
|
||||||
install:
|
|
||||||
@echo "Disable install for srs-librtmp"
|
|
||||||
|
|
||||||
END
|
|
||||||
else
|
|
||||||
cat << END >> ${SRS_WORKDIR}/${SRS_MAKEFILE}
|
|
||||||
uninstall:
|
uninstall:
|
||||||
@echo "rmdir \$(SRS_PREFIX)"
|
@echo "rmdir \$(SRS_PREFIX)"
|
||||||
@rm -rf \$(SRS_PREFIX)
|
@rm -rf \$(SRS_PREFIX)
|
||||||
|
@ -665,25 +634,6 @@ install:
|
||||||
@echo "@see: https://github.com/ossrs/srs/wiki/v3_CN_LinuxService"
|
@echo "@see: https://github.com/ossrs/srs/wiki/v3_CN_LinuxService"
|
||||||
|
|
||||||
END
|
END
|
||||||
fi
|
|
||||||
|
|
||||||
# generate srs-librtmp entry
|
|
||||||
if [ $SRS_LIBRTMP = YES ]; then
|
|
||||||
cat << END >> ${SRS_WORKDIR}/${SRS_MAKEFILE}
|
|
||||||
librtmp: server
|
|
||||||
@echo "Building the client publish/play library."
|
|
||||||
\$(MAKE) -f ${SRS_OBJS_DIR}/${SRS_MAKEFILE} librtmp
|
|
||||||
@echo "Building the srs-librtmp example."
|
|
||||||
(cd research/librtmp && \$(MAKE) EXTRA_CXXFLAGS="${SrsGcov}" ${SrsLibrtmpSampleEntry})
|
|
||||||
|
|
||||||
END
|
|
||||||
else
|
|
||||||
cat << END >> ${SRS_WORKDIR}/${SRS_MAKEFILE}
|
|
||||||
librtmp: server
|
|
||||||
@echo "Ignore srs-librtmp for it's disabled."
|
|
||||||
|
|
||||||
END
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ $SRS_UTEST = YES ]; then
|
if [ $SRS_UTEST = YES ]; then
|
||||||
cat << END >> ${SRS_WORKDIR}/${SRS_MAKEFILE}
|
cat << END >> ${SRS_WORKDIR}/${SRS_MAKEFILE}
|
||||||
|
@ -717,173 +667,144 @@ echo 'Configure ok! '
|
||||||
# when configure success, prepare build
|
# when configure success, prepare build
|
||||||
#####################################################################################
|
#####################################################################################
|
||||||
# create objs/logs for ffmpeg to write log.
|
# create objs/logs for ffmpeg to write log.
|
||||||
if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
|
mkdir -p ${SRS_OBJS}/logs
|
||||||
mkdir -p ${SRS_OBJS}/logs
|
|
||||||
fi
|
|
||||||
|
|
||||||
#####################################################################################
|
#####################################################################################
|
||||||
# configure summary
|
# configure summary
|
||||||
#####################################################################################
|
#####################################################################################
|
||||||
# summary
|
# summary
|
||||||
if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
|
echo ""
|
||||||
echo ""
|
echo "Configure summary:"
|
||||||
echo "Configure summary:"
|
echo " ${SRS_AUTO_USER_CONFIGURE}"
|
||||||
echo " ${SRS_AUTO_USER_CONFIGURE}"
|
echo " ${SRS_AUTO_CONFIGURE}"
|
||||||
echo " ${SRS_AUTO_CONFIGURE}"
|
if [ $SRS_HLS = YES ]; then
|
||||||
if [ $SRS_HLS = YES ]; then
|
echo -e "${GREEN}HLS is enabled.${BLACK}"
|
||||||
echo -e "${GREEN}HLS is enabled.${BLACK}"
|
else
|
||||||
else
|
echo -e "${YELLOW}Warning: HLS is disabled.${BLACK}"
|
||||||
echo -e "${YELLOW}Warning: HLS is disabled.${BLACK}"
|
|
||||||
fi
|
|
||||||
if [ $SRS_STREAM_CASTER = YES ]; then
|
|
||||||
echo -e "${YELLOW}Experiment: StreamCaster is enabled.${BLACK}"
|
|
||||||
else
|
|
||||||
echo -e "${GREEN}Note: StreamCaster is disabled.${BLACK}"
|
|
||||||
fi
|
|
||||||
if [ $SRS_HDS = YES ]; then
|
|
||||||
echo -e "${YELLOW}Experiment: HDS is enabled.${BLACK}"
|
|
||||||
else
|
|
||||||
echo -e "${GREEN}Warning: HDS is disabled.${BLACK}"
|
|
||||||
fi
|
|
||||||
if [ $SRS_SRT = YES ]; then
|
|
||||||
echo -e "${YELLOW}Experiment: SRT is enabled. https://github.com/ossrs/srs/issues/1147${BLACK}"
|
|
||||||
else
|
|
||||||
echo -e "${GREEN}Warning: SRT is disabled.${BLACK}"
|
|
||||||
fi
|
|
||||||
if [ $SRS_RTC = YES ]; then
|
|
||||||
echo -e "${YELLOW}Experiment: RTC is enabled. https://github.com/ossrs/srs/issues/307${BLACK}"
|
|
||||||
else
|
|
||||||
echo -e "${GREEN}Warning: RTC is disabled.${BLACK}"
|
|
||||||
fi
|
|
||||||
if [ $SRS_DVR = YES ]; then
|
|
||||||
echo -e "${GREEN}DVR is enabled.${BLACK}"
|
|
||||||
else
|
|
||||||
echo -e "${YELLOW}Warning: DVR is disabled.${BLACK}"
|
|
||||||
fi
|
|
||||||
if [ $SRS_SSL = YES ]; then
|
|
||||||
echo -e "${GREEN}RTMP complex handshake is enabled${BLACK}"
|
|
||||||
else
|
|
||||||
echo -e "${YELLOW}Warning: RTMP complex handshake is disabled, flash cann't play h264/aac.${BLACK}"
|
|
||||||
fi
|
|
||||||
if [ $SRS_TRANSCODE = YES ]; then
|
|
||||||
echo -e "${GREEN}The transcoding is enabled${BLACK}"
|
|
||||||
else
|
|
||||||
echo -e "${YELLOW}Warning: The transcoding is disabled.${BLACK}"
|
|
||||||
fi
|
|
||||||
if [ $SRS_INGEST = YES ]; then
|
|
||||||
echo -e "${GREEN}The ingesting is enabled.${BLACK}"
|
|
||||||
else
|
|
||||||
echo -e "${YELLOW}Warning: The ingesting is disabled.${BLACK}"
|
|
||||||
fi
|
|
||||||
if [ $SRS_HTTP_CALLBACK = YES ]; then
|
|
||||||
echo -e "${GREEN}The http-callback is enabled${BLACK}"
|
|
||||||
else
|
|
||||||
echo -e "${YELLOW}Warning: The http-callback is disabled.${BLACK}"
|
|
||||||
fi
|
|
||||||
if [ $SRS_HTTP_SERVER = YES ]; then
|
|
||||||
echo -e "${GREEN}Embeded HTTP server for HTTP-FLV/HLS is enabled.${BLACK}"
|
|
||||||
else
|
|
||||||
echo -e "${YELLOW}Warning: Embeded HTTP server is disabled, HTTP-FLV is disabled, please use nginx to delivery HLS.${BLACK}"
|
|
||||||
fi
|
|
||||||
if [ $SRS_HTTP_API = YES ]; then
|
|
||||||
echo -e "${GREEN}The HTTP API is enabled${BLACK}"
|
|
||||||
else
|
|
||||||
echo -e "${YELLOW}Warning: The HTTP API is disabled.${BLACK}"
|
|
||||||
fi
|
|
||||||
if [ $SRS_LIBRTMP = YES ]; then
|
|
||||||
echo -e "${GREEN}The client-side srs-librtmp is enabled.${BLACK}"
|
|
||||||
else
|
|
||||||
echo -e "${YELLOW}Note: The client-side srs-librtmp is disabled.${BLACK}"
|
|
||||||
fi
|
|
||||||
if [ $SRS_RESEARCH = YES ]; then
|
|
||||||
echo -e "${GREEN}The research tools are enabled.${BLACK}"
|
|
||||||
else
|
|
||||||
echo -e "${GREEN}Note: The research tools are disabled.${BLACK}"
|
|
||||||
fi
|
|
||||||
if [ $SRS_UTEST = YES ]; then
|
|
||||||
echo -e "${GREEN}The utests are enabled.${BLACK}"
|
|
||||||
else
|
|
||||||
echo -e "${YELLOW}Note: The utests are disabled.${BLACK}"
|
|
||||||
fi
|
|
||||||
if [ $SRS_GPERF = YES ]; then
|
|
||||||
echo -e "${GREEN}The gperf(tcmalloc) is enabled.${BLACK}"
|
|
||||||
else
|
|
||||||
echo -e "${GREEN}Note: The gperf(tcmalloc) is disabled.${BLACK}"
|
|
||||||
fi
|
|
||||||
if [ $SRS_GPERF_MC = YES ]; then
|
|
||||||
echo -e "${YELLOW}The gmc(gperf memory check) is enabled, performance may suffer.${BLACK}"
|
|
||||||
else
|
|
||||||
echo -e "${GREEN}Note: The gmc(gperf memory check) is disabled.${BLACK}"
|
|
||||||
fi
|
|
||||||
if [ $SRS_GPERF_MD = YES ]; then
|
|
||||||
echo -e "${YELLOW}The gmd(gperf memory defense) is enabled, performance may suffer.${BLACK}"
|
|
||||||
else
|
|
||||||
echo -e "${GREEN}Note: The gmd(gperf memory defense) is disabled.${BLACK}"
|
|
||||||
fi
|
|
||||||
if [ $SRS_GPERF_MP = YES ]; then
|
|
||||||
echo -e "${YELLOW}The gmp(gperf memory profile) is enabled, performance may suffer.${BLACK}"
|
|
||||||
else
|
|
||||||
echo -e "${GREEN}Note: The gmp(gperf memory profile) is disabled.${BLACK}"
|
|
||||||
fi
|
|
||||||
if [ $SRS_GPERF_CP = YES ]; then
|
|
||||||
echo -e "${YELLOW}The gcp(gperf cpu profile) is enabled, performance may suffer.${BLACK}"
|
|
||||||
else
|
|
||||||
echo -e "${GREEN}Note: The gcp(gperf cpu profile) is disabled.${BLACK}"
|
|
||||||
fi
|
|
||||||
if [ $SRS_GPROF = YES ]; then
|
|
||||||
echo -e "${YELLOW}The gprof(GNU profile tool) is enabled, performance may suffer.${BLACK}"
|
|
||||||
else
|
|
||||||
echo -e "${GREEN}Note: The gprof(GNU profile tool) is disabled.${BLACK}"
|
|
||||||
fi
|
|
||||||
if [ $SRS_VALGRIND = YES ]; then
|
|
||||||
echo -e "${GREEN}The valgrind is enabled.${BLACK}"
|
|
||||||
else
|
|
||||||
echo -e "${GREEN}Note: The valgrind is disabled.${BLACK}"
|
|
||||||
fi
|
|
||||||
# add each modules for application
|
|
||||||
for SRS_MODULE in ${SRS_MODULES[*]}; do
|
|
||||||
echo -e "${GREEN}Enable module: $SRS_MODULE${BLACK}"
|
|
||||||
done
|
|
||||||
fi
|
fi
|
||||||
|
if [ $SRS_STREAM_CASTER = YES ]; then
|
||||||
|
echo -e "${YELLOW}Experiment: StreamCaster is enabled.${BLACK}"
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}Note: StreamCaster is disabled.${BLACK}"
|
||||||
|
fi
|
||||||
|
if [ $SRS_HDS = YES ]; then
|
||||||
|
echo -e "${YELLOW}Experiment: HDS is enabled.${BLACK}"
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}Warning: HDS is disabled.${BLACK}"
|
||||||
|
fi
|
||||||
|
if [ $SRS_SRT = YES ]; then
|
||||||
|
echo -e "${YELLOW}Experiment: SRT is enabled. https://github.com/ossrs/srs/issues/1147${BLACK}"
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}Warning: SRT is disabled.${BLACK}"
|
||||||
|
fi
|
||||||
|
if [ $SRS_RTC = YES ]; then
|
||||||
|
echo -e "${YELLOW}Experiment: RTC is enabled. https://github.com/ossrs/srs/issues/307${BLACK}"
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}Warning: RTC is disabled.${BLACK}"
|
||||||
|
fi
|
||||||
|
if [ $SRS_HTTPS = YES ]; then
|
||||||
|
echo -e "${YELLOW}Experiment: HTTPS is enabled. https://github.com/ossrs/srs/issues/1657${BLACK}"
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}Warning: HTTPS is disabled.${BLACK}"
|
||||||
|
fi
|
||||||
|
if [ $SRS_DVR = YES ]; then
|
||||||
|
echo -e "${GREEN}DVR is enabled.${BLACK}"
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}Warning: DVR is disabled.${BLACK}"
|
||||||
|
fi
|
||||||
|
if [ $SRS_SSL = YES ]; then
|
||||||
|
echo -e "${GREEN}RTMP complex handshake is enabled${BLACK}"
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}Warning: RTMP complex handshake is disabled, flash cann't play h264/aac.${BLACK}"
|
||||||
|
fi
|
||||||
|
if [ $SRS_TRANSCODE = YES ]; then
|
||||||
|
echo -e "${GREEN}The transcoding is enabled${BLACK}"
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}Warning: The transcoding is disabled.${BLACK}"
|
||||||
|
fi
|
||||||
|
if [ $SRS_INGEST = YES ]; then
|
||||||
|
echo -e "${GREEN}The ingesting is enabled.${BLACK}"
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}Warning: The ingesting is disabled.${BLACK}"
|
||||||
|
fi
|
||||||
|
if [ $SRS_HTTP_CALLBACK = YES ]; then
|
||||||
|
echo -e "${GREEN}The http-callback is enabled${BLACK}"
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}Warning: The http-callback is disabled.${BLACK}"
|
||||||
|
fi
|
||||||
|
if [ $SRS_HTTP_SERVER = YES ]; then
|
||||||
|
echo -e "${GREEN}Embeded HTTP server for HTTP-FLV/HLS is enabled.${BLACK}"
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}Warning: Embeded HTTP server is disabled, HTTP-FLV is disabled, please use nginx to delivery HLS.${BLACK}"
|
||||||
|
fi
|
||||||
|
if [ $SRS_HTTP_API = YES ]; then
|
||||||
|
echo -e "${GREEN}The HTTP API is enabled${BLACK}"
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}Warning: The HTTP API is disabled.${BLACK}"
|
||||||
|
fi
|
||||||
|
if [ $SRS_UTEST = YES ]; then
|
||||||
|
echo -e "${GREEN}The utests are enabled.${BLACK}"
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}Note: The utests are disabled.${BLACK}"
|
||||||
|
fi
|
||||||
|
if [ $SRS_GPERF = YES ]; then
|
||||||
|
echo -e "${GREEN}The gperf(tcmalloc) is enabled.${BLACK}"
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}Note: The gperf(tcmalloc) is disabled.${BLACK}"
|
||||||
|
fi
|
||||||
|
if [ $SRS_GPERF_MC = YES ]; then
|
||||||
|
echo -e "${YELLOW}The gmc(gperf memory check) is enabled, performance may suffer.${BLACK}"
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}Note: The gmc(gperf memory check) is disabled.${BLACK}"
|
||||||
|
fi
|
||||||
|
if [ $SRS_GPERF_MD = YES ]; then
|
||||||
|
echo -e "${YELLOW}The gmd(gperf memory defense) is enabled, performance may suffer.${BLACK}"
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}Note: The gmd(gperf memory defense) is disabled.${BLACK}"
|
||||||
|
fi
|
||||||
|
if [ $SRS_GPERF_MP = YES ]; then
|
||||||
|
echo -e "${YELLOW}The gmp(gperf memory profile) is enabled, performance may suffer.${BLACK}"
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}Note: The gmp(gperf memory profile) is disabled.${BLACK}"
|
||||||
|
fi
|
||||||
|
if [ $SRS_GPERF_CP = YES ]; then
|
||||||
|
echo -e "${YELLOW}The gcp(gperf cpu profile) is enabled, performance may suffer.${BLACK}"
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}Note: The gcp(gperf cpu profile) is disabled.${BLACK}"
|
||||||
|
fi
|
||||||
|
if [ $SRS_GPROF = YES ]; then
|
||||||
|
echo -e "${YELLOW}The gprof(GNU profile tool) is enabled, performance may suffer.${BLACK}"
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}Note: The gprof(GNU profile tool) is disabled.${BLACK}"
|
||||||
|
fi
|
||||||
|
if [ $SRS_VALGRIND = YES ]; then
|
||||||
|
echo -e "${GREEN}The valgrind is enabled.${BLACK}"
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}Note: The valgrind is disabled.${BLACK}"
|
||||||
|
fi
|
||||||
|
# add each modules for application
|
||||||
|
for SRS_MODULE in ${SRS_MODULES[*]}; do
|
||||||
|
echo -e "${GREEN}Enable module: $SRS_MODULE${BLACK}"
|
||||||
|
done
|
||||||
|
|
||||||
#####################################################################################
|
#####################################################################################
|
||||||
# Do cleanup when configure done.
|
# Do cleanup when configure done.
|
||||||
#####################################################################################
|
#####################################################################################
|
||||||
if [[ $SRS_CLEAN == YES && -f Makefile ]]; then
|
if [[ $SRS_CLEAN == YES && -f Makefile ]]; then
|
||||||
echo "Do full cleanup, you can disable it by: --without-clean"
|
echo "Do full cleanup, you can disable it by: --clean=off"
|
||||||
make clean
|
make clean
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#####################################################################################
|
#####################################################################################
|
||||||
# next step
|
# next step
|
||||||
#####################################################################################
|
#####################################################################################
|
||||||
if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
|
echo ""
|
||||||
echo ""
|
echo "You can run 3rdparty applications:"
|
||||||
echo "You can run 3rdparty applications:"
|
if [ $SRS_HTTP_CALLBACK = YES ]; then
|
||||||
if [ $SRS_HTTP_CALLBACK = YES ]; then
|
echo -e "\" python ./research/api-server/server.py 8085 \" to start the api-server"
|
||||||
echo -e "\" python ./research/api-server/server.py 8085 \" to start the api-server"
|
|
||||||
fi
|
|
||||||
echo ""
|
|
||||||
echo "You can build SRS:"
|
|
||||||
echo "\" make \" to build the srs(simple rtmp server)."
|
|
||||||
echo "\" make help \" to get the usage of make"
|
|
||||||
else
|
|
||||||
# for srs-librtmp single file,
|
|
||||||
# package the whole project to srs_librtmp.h and srs_librtmp.cpp
|
|
||||||
if [ $SRS_EXPORT_LIBRTMP_SINGLE != NO ]; then
|
|
||||||
echo "Packaging the whole project to srs_librtmp.h and srs_librtmp.cpp"
|
|
||||||
. $SRS_EXPORT_LIBRTMP_SINGLE/auto/generate-srs-librtmp-single.sh
|
|
||||||
echo -e "${GREEN}Please use the srs-librtmp files: ${BLACK}"
|
|
||||||
echo -e "${GREEN} $SRS_EXPORT_LIBRTMP_PROJECT/srs_librtmp.h ${BLACK}"
|
|
||||||
echo -e "${GREEN} $SRS_EXPORT_LIBRTMP_PROJECT/srs_librtmp.cpp ${BLACK}"
|
|
||||||
echo -e "${GREEN} $SRS_EXPORT_LIBRTMP_PROJECT/example.c ${BLACK}"
|
|
||||||
echo -e "${GREEN}To compile the example: ${BLACK}"
|
|
||||||
echo -e "${GREEN} cd $SRS_EXPORT_LIBRTMP_PROJECT && $SRS_SINGLE_LIBRTMP_COMPILE ${BLACK}"
|
|
||||||
# for srs-librtmp project.
|
|
||||||
else
|
|
||||||
echo -e "${GREEN}Please use the srs-librtmp project: ${BLACK}"
|
|
||||||
echo -e "${GREEN} cd $SRS_EXPORT_LIBRTMP_PROJECT && make ${BLACK}"
|
|
||||||
fi
|
|
||||||
# Change to experiment.
|
|
||||||
echo -e "${YELLOW}Warning: Notice srs-librtmp is deprecated and maybe removed in future.${BLACK}"
|
|
||||||
fi
|
fi
|
||||||
|
echo ""
|
||||||
|
echo "You can build SRS:"
|
||||||
|
echo "\" make \" to build the srs(simple rtmp server)."
|
||||||
|
echo "\" make help \" to get the usage of make"
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <srs_app_thread.hpp>
|
#include <srs_app_st.hpp>
|
||||||
|
|
||||||
// The async call for http hooks, for the http hooks will switch st-thread,
|
// The async call for http hooks, for the http hooks will switch st-thread,
|
||||||
// so we must use isolate thread to avoid the thread corrupt,
|
// so we must use isolate thread to avoid the thread corrupt,
|
||||||
|
|
|
@ -91,7 +91,7 @@ bool _bandwidth_is_stopped_publish(SrsBandwidthPacket* pkt)
|
||||||
{
|
{
|
||||||
return pkt->is_stopped_publish();
|
return pkt->is_stopped_publish();
|
||||||
}
|
}
|
||||||
srs_error_t _srs_expect_bandwidth_packet(SrsRtmpServer* rtmp, _CheckPacketType pfn)
|
srs_error_t srs_expect_bandwidth_packet(SrsRtmpServer* rtmp, _CheckPacketType pfn)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
@ -151,7 +151,7 @@ srs_error_t SrsBandwidth::bandwidth_check(SrsRtmpServer* rtmp, ISrsProtocolStati
|
||||||
// reject the connection in the interval window.
|
// reject the connection in the interval window.
|
||||||
if (last_check_time > 0 && time_now - last_check_time < interval) {
|
if (last_check_time > 0 && time_now - last_check_time < interval) {
|
||||||
_rtmp->response_connect_reject(_req, "bandcheck rejected");
|
_rtmp->response_connect_reject(_req, "bandcheck rejected");
|
||||||
return srs_error_new(ERROR_SYSTEM_BANDWIDTH_DENIED, "reject, last_check=%" PRId64 ", now=%" PRId64 ", interval=%d", last_check_time, time_now, interval);
|
return srs_error_new(ERROR_SYSTEM_BANDWIDTH_DENIED, "reject, last_check=%" PRId64 ", now=%" PRId64 ", interval=%" PRId64 "", last_check_time, time_now, interval);
|
||||||
}
|
}
|
||||||
|
|
||||||
// accept and do bandwidth check.
|
// accept and do bandwidth check.
|
||||||
|
@ -241,7 +241,7 @@ srs_error_t SrsBandwidth::play_start(SrsBandwidthSample* sample, SrsKbpsLimit* l
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = _srs_expect_bandwidth_packet(_rtmp, _bandwidth_is_starting_play)) != srs_success) {
|
if ((err = srs_expect_bandwidth_packet(_rtmp, _bandwidth_is_starting_play)) != srs_success) {
|
||||||
return srs_error_wrap(err, "expect bandwidth");
|
return srs_error_wrap(err, "expect bandwidth");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,7 +304,7 @@ srs_error_t SrsBandwidth::play_stop(SrsBandwidthSample* sample, SrsKbpsLimit* /*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = _srs_expect_bandwidth_packet(_rtmp, _bandwidth_is_stopped_play)) != srs_success) {
|
if ((err = srs_expect_bandwidth_packet(_rtmp, _bandwidth_is_stopped_play)) != srs_success) {
|
||||||
return srs_error_wrap(err, "expect bandwidth");
|
return srs_error_wrap(err, "expect bandwidth");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,7 +328,7 @@ srs_error_t SrsBandwidth::publish_start(SrsBandwidthSample* sample, SrsKbpsLimit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = _srs_expect_bandwidth_packet(_rtmp, _bandwidth_is_starting_publish)) != srs_success) {
|
if ((err = srs_expect_bandwidth_packet(_rtmp, _bandwidth_is_starting_publish)) != srs_success) {
|
||||||
return srs_error_wrap(err, "expect packet");
|
return srs_error_wrap(err, "expect packet");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,7 +388,7 @@ srs_error_t SrsBandwidth::publish_stop(SrsBandwidthSample* sample, SrsKbpsLimit*
|
||||||
// we just ignore the packet and send the bandwidth test data.
|
// we just ignore the packet and send the bandwidth test data.
|
||||||
bool is_flash = (_req->swfUrl != "");
|
bool is_flash = (_req->swfUrl != "");
|
||||||
if (!is_flash) {
|
if (!is_flash) {
|
||||||
if ((err = _srs_expect_bandwidth_packet(_rtmp, _bandwidth_is_stopped_publish)) != srs_success) {
|
if ((err = srs_expect_bandwidth_packet(_rtmp, _bandwidth_is_stopped_publish)) != srs_success) {
|
||||||
return srs_error_wrap(err, "expect bandwidth");
|
return srs_error_wrap(err, "expect bandwidth");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -422,7 +422,7 @@ srs_error_t SrsBandwidth::do_final(SrsBandwidthSample& play_sample, SrsBandwidth
|
||||||
bool is_flash = (_req->swfUrl != "");
|
bool is_flash = (_req->swfUrl != "");
|
||||||
if (!is_flash) {
|
if (!is_flash) {
|
||||||
// ignore any error.
|
// ignore any error.
|
||||||
err = _srs_expect_bandwidth_packet(_rtmp, _bandwidth_is_final);
|
err = srs_expect_bandwidth_packet(_rtmp, _bandwidth_is_final);
|
||||||
srs_error_reset(err);
|
srs_error_reset(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ SrsAppCasterFlv::SrsAppCasterFlv(SrsConfDirective* c)
|
||||||
{
|
{
|
||||||
http_mux = new SrsHttpServeMux();
|
http_mux = new SrsHttpServeMux();
|
||||||
output = _srs_config->get_stream_caster_output(c);
|
output = _srs_config->get_stream_caster_output(c);
|
||||||
manager = new SrsCoroutineManager();
|
manager = new SrsResourceManager("CFLV");
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsAppCasterFlv::~SrsAppCasterFlv()
|
SrsAppCasterFlv::~SrsAppCasterFlv()
|
||||||
|
@ -77,12 +77,15 @@ srs_error_t SrsAppCasterFlv::on_tcp_client(srs_netfd_t stfd)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
string ip = srs_get_peer_ip(srs_netfd_fileno(stfd));
|
int fd = srs_netfd_fileno(stfd);
|
||||||
|
string ip = srs_get_peer_ip(fd);
|
||||||
|
int port = srs_get_peer_port(fd);
|
||||||
|
|
||||||
if (ip.empty() && !_srs_config->empty_ip_ok()) {
|
if (ip.empty() && !_srs_config->empty_ip_ok()) {
|
||||||
srs_warn("empty ip for fd=%d", srs_netfd_fileno(stfd));
|
srs_warn("empty ip for fd=%d", srs_netfd_fileno(stfd));
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsHttpConn* conn = new SrsDynamicHttpConn(this, stfd, http_mux, ip);
|
ISrsStartableConneciton* conn = new SrsDynamicHttpConn(this, stfd, http_mux, ip, port);
|
||||||
conns.push_back(conn);
|
conns.push_back(conn);
|
||||||
|
|
||||||
if ((err = conn->start()) != srs_success) {
|
if ((err = conn->start()) != srs_success) {
|
||||||
|
@ -92,16 +95,16 @@ srs_error_t SrsAppCasterFlv::on_tcp_client(srs_netfd_t stfd)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SrsAppCasterFlv::remove(ISrsConnection* c)
|
void SrsAppCasterFlv::remove(ISrsResource* c)
|
||||||
{
|
{
|
||||||
SrsConnection* conn = dynamic_cast<SrsConnection*>(c);
|
ISrsStartableConneciton* conn = dynamic_cast<ISrsStartableConneciton*>(c);
|
||||||
|
|
||||||
std::vector<SrsHttpConn*>::iterator it;
|
std::vector<ISrsStartableConneciton*>::iterator it;
|
||||||
if ((it = std::find(conns.begin(), conns.end(), conn)) != conns.end()) {
|
if ((it = std::find(conns.begin(), conns.end(), conn)) != conns.end()) {
|
||||||
conns.erase(it);
|
conns.erase(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
// fixbug: SrsHttpConn for CasterFlv is not freed, which could cause memory leak
|
// fixbug: ISrsStartableConneciton for CasterFlv is not freed, which could cause memory leak
|
||||||
// so, free conn which is not managed by SrsServer->conns;
|
// so, free conn which is not managed by SrsServer->conns;
|
||||||
// @see: https://github.com/ossrs/srs/issues/826
|
// @see: https://github.com/ossrs/srs/issues/826
|
||||||
manager->remove(c);
|
manager->remove(c);
|
||||||
|
@ -138,30 +141,38 @@ srs_error_t SrsAppCasterFlv::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessa
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsDynamicHttpConn::SrsDynamicHttpConn(IConnectionManager* cm, srs_netfd_t fd, SrsHttpServeMux* m, string cip)
|
SrsDynamicHttpConn::SrsDynamicHttpConn(ISrsResourceManager* cm, srs_netfd_t fd, SrsHttpServeMux* m, string cip, int cport)
|
||||||
: SrsHttpConn(cm, fd, m, cip)
|
|
||||||
{
|
{
|
||||||
|
// Create a identify for this client.
|
||||||
|
_srs_context->set_id(_srs_context->generate_id());
|
||||||
|
|
||||||
|
manager = cm;
|
||||||
sdk = NULL;
|
sdk = NULL;
|
||||||
pprint = SrsPithyPrint::create_caster();
|
pprint = SrsPithyPrint::create_caster();
|
||||||
|
skt = new SrsTcpConnection(fd);
|
||||||
|
conn = new SrsHttpConn(this, skt, m, cip, cport);
|
||||||
|
ip = cip;
|
||||||
|
port = cport;
|
||||||
|
|
||||||
|
_srs_config->subscribe(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsDynamicHttpConn::~SrsDynamicHttpConn()
|
SrsDynamicHttpConn::~SrsDynamicHttpConn()
|
||||||
{
|
{
|
||||||
|
_srs_config->unsubscribe(this);
|
||||||
|
|
||||||
|
srs_freep(conn);
|
||||||
|
srs_freep(skt);
|
||||||
srs_freep(sdk);
|
srs_freep(sdk);
|
||||||
srs_freep(pprint);
|
srs_freep(pprint);
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsDynamicHttpConn::on_got_http_message(ISrsHttpMessage* msg)
|
|
||||||
{
|
|
||||||
return srs_success;
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_error_t SrsDynamicHttpConn::proxy(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string o)
|
srs_error_t SrsDynamicHttpConn::proxy(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string o)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
output = o;
|
output = o;
|
||||||
srs_trace("flv: proxy %s to %s", r->uri().c_str(), output.c_str());
|
srs_trace("flv: proxy %s:%d %s to %s", ip.c_str(), port, r->uri().c_str(), output.c_str());
|
||||||
|
|
||||||
char* buffer = new char[SRS_HTTP_FLV_STREAM_BUFFER];
|
char* buffer = new char[SRS_HTTP_FLV_STREAM_BUFFER];
|
||||||
SrsAutoFreeA(char, buffer);
|
SrsAutoFreeA(char, buffer);
|
||||||
|
@ -247,6 +258,72 @@ srs_error_t SrsDynamicHttpConn::do_proxy(ISrsHttpResponseReader* rr, SrsFlvDecod
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsDynamicHttpConn::on_reload_http_stream_crossdomain()
|
||||||
|
{
|
||||||
|
bool v = _srs_config->get_http_stream_crossdomain();
|
||||||
|
return conn->set_crossdomain_enabled(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsDynamicHttpConn::on_start()
|
||||||
|
{
|
||||||
|
return srs_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsDynamicHttpConn::on_http_message(ISrsHttpMessage* r, SrsHttpResponseWriter* w)
|
||||||
|
{
|
||||||
|
return srs_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsDynamicHttpConn::on_message_done(ISrsHttpMessage* r, SrsHttpResponseWriter* w)
|
||||||
|
{
|
||||||
|
return srs_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsDynamicHttpConn::on_conn_done(srs_error_t r0)
|
||||||
|
{
|
||||||
|
// Because we use manager to manage this object,
|
||||||
|
// not the http connection object, so we must remove it here.
|
||||||
|
manager->remove(this);
|
||||||
|
|
||||||
|
return r0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SrsDynamicHttpConn::desc()
|
||||||
|
{
|
||||||
|
return "DHttpConn";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SrsDynamicHttpConn::remote_ip()
|
||||||
|
{
|
||||||
|
return conn->remote_ip();
|
||||||
|
}
|
||||||
|
|
||||||
|
const SrsContextId& SrsDynamicHttpConn::get_id()
|
||||||
|
{
|
||||||
|
return conn->get_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsDynamicHttpConn::start()
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
bool v = _srs_config->get_http_stream_crossdomain();
|
||||||
|
if ((err = conn->set_crossdomain_enabled(v)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "set cors=%d", v);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = skt->initialize()) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "init socket");
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsDynamicHttpConn::remark(int64_t* in, int64_t* out)
|
||||||
|
{
|
||||||
|
conn->remark(in, out);
|
||||||
|
}
|
||||||
|
|
||||||
SrsHttpFileReader::SrsHttpFileReader(ISrsHttpResponseReader* h)
|
SrsHttpFileReader::SrsHttpFileReader(ISrsHttpResponseReader* h)
|
||||||
{
|
{
|
||||||
http = h;
|
http = h;
|
||||||
|
|
|
@ -40,7 +40,7 @@ class SrsFlvDecoder;
|
||||||
class SrsTcpClient;
|
class SrsTcpClient;
|
||||||
class SrsSimpleRtmpClient;
|
class SrsSimpleRtmpClient;
|
||||||
|
|
||||||
#include <srs_app_thread.hpp>
|
#include <srs_app_st.hpp>
|
||||||
#include <srs_app_listener.hpp>
|
#include <srs_app_listener.hpp>
|
||||||
#include <srs_app_conn.hpp>
|
#include <srs_app_conn.hpp>
|
||||||
#include <srs_app_http_conn.hpp>
|
#include <srs_app_http_conn.hpp>
|
||||||
|
@ -48,13 +48,13 @@ class SrsSimpleRtmpClient;
|
||||||
|
|
||||||
// The stream caster for flv stream over HTTP POST.
|
// The stream caster for flv stream over HTTP POST.
|
||||||
class SrsAppCasterFlv : virtual public ISrsTcpHandler
|
class SrsAppCasterFlv : virtual public ISrsTcpHandler
|
||||||
, virtual public IConnectionManager, virtual public ISrsHttpHandler
|
, virtual public ISrsResourceManager, virtual public ISrsHttpHandler
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
std::string output;
|
std::string output;
|
||||||
SrsHttpServeMux* http_mux;
|
SrsHttpServeMux* http_mux;
|
||||||
std::vector<SrsHttpConn*> conns;
|
std::vector<ISrsStartableConneciton*> conns;
|
||||||
SrsCoroutineManager* manager;
|
SrsResourceManager* manager;
|
||||||
public:
|
public:
|
||||||
SrsAppCasterFlv(SrsConfDirective* c);
|
SrsAppCasterFlv(SrsConfDirective* c);
|
||||||
virtual ~SrsAppCasterFlv();
|
virtual ~SrsAppCasterFlv();
|
||||||
|
@ -63,30 +63,60 @@ public:
|
||||||
// Interface ISrsTcpHandler
|
// Interface ISrsTcpHandler
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t on_tcp_client(srs_netfd_t stfd);
|
virtual srs_error_t on_tcp_client(srs_netfd_t stfd);
|
||||||
// Interface IConnectionManager
|
// Interface ISrsResourceManager
|
||||||
public:
|
public:
|
||||||
virtual void remove(ISrsConnection* c);
|
virtual void remove(ISrsResource* c);
|
||||||
// Interface ISrsHttpHandler
|
// Interface ISrsHttpHandler
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
|
virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
|
||||||
};
|
};
|
||||||
|
|
||||||
// The dynamic http connection, never drop the body.
|
// The dynamic http connection, never drop the body.
|
||||||
class SrsDynamicHttpConn : public SrsHttpConn
|
class SrsDynamicHttpConn : virtual public ISrsStartableConneciton, virtual public ISrsHttpConnOwner
|
||||||
|
, virtual public ISrsReloadHandler
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
// The manager object to manage the connection.
|
||||||
|
ISrsResourceManager* manager;
|
||||||
std::string output;
|
std::string output;
|
||||||
SrsPithyPrint* pprint;
|
SrsPithyPrint* pprint;
|
||||||
SrsSimpleRtmpClient* sdk;
|
SrsSimpleRtmpClient* sdk;
|
||||||
|
SrsTcpConnection* skt;
|
||||||
|
SrsHttpConn* conn;
|
||||||
|
private:
|
||||||
|
// The ip and port of client.
|
||||||
|
std::string ip;
|
||||||
|
int port;
|
||||||
public:
|
public:
|
||||||
SrsDynamicHttpConn(IConnectionManager* cm, srs_netfd_t fd, SrsHttpServeMux* m, std::string cip);
|
SrsDynamicHttpConn(ISrsResourceManager* cm, srs_netfd_t fd, SrsHttpServeMux* m, std::string cip, int port);
|
||||||
virtual ~SrsDynamicHttpConn();
|
virtual ~SrsDynamicHttpConn();
|
||||||
public:
|
|
||||||
virtual srs_error_t on_got_http_message(ISrsHttpMessage* msg);
|
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t proxy(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string o);
|
virtual srs_error_t proxy(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string o);
|
||||||
private:
|
private:
|
||||||
virtual srs_error_t do_proxy(ISrsHttpResponseReader* rr, SrsFlvDecoder* dec);
|
virtual srs_error_t do_proxy(ISrsHttpResponseReader* rr, SrsFlvDecoder* dec);
|
||||||
|
// Extract APIs from SrsTcpConnection.
|
||||||
|
// Interface ISrsReloadHandler
|
||||||
|
public:
|
||||||
|
virtual srs_error_t on_reload_http_stream_crossdomain();
|
||||||
|
// Interface ISrsHttpConnOwner.
|
||||||
|
public:
|
||||||
|
virtual srs_error_t on_start();
|
||||||
|
virtual srs_error_t on_http_message(ISrsHttpMessage* r, SrsHttpResponseWriter* w);
|
||||||
|
virtual srs_error_t on_message_done(ISrsHttpMessage* r, SrsHttpResponseWriter* w);
|
||||||
|
virtual srs_error_t on_conn_done(srs_error_t r0);
|
||||||
|
// Interface ISrsResource.
|
||||||
|
public:
|
||||||
|
virtual std::string desc();
|
||||||
|
// Interface ISrsConnection.
|
||||||
|
public:
|
||||||
|
virtual std::string remote_ip();
|
||||||
|
virtual const SrsContextId& get_id();
|
||||||
|
// Interface ISrsStartable
|
||||||
|
public:
|
||||||
|
virtual srs_error_t start();
|
||||||
|
// Interface ISrsKbpsDelta
|
||||||
|
public:
|
||||||
|
virtual void remark(int64_t* in, int64_t* out);
|
||||||
};
|
};
|
||||||
|
|
||||||
// The http wrapper for file reader, to read http post stream like a file.
|
// The http wrapper for file reader, to read http post stream like a file.
|
||||||
|
|
|
@ -57,7 +57,7 @@ using namespace std;
|
||||||
#include <srs_kernel_utility.hpp>
|
#include <srs_kernel_utility.hpp>
|
||||||
#include <srs_rtmp_stack.hpp>
|
#include <srs_rtmp_stack.hpp>
|
||||||
|
|
||||||
using namespace _srs_internal;
|
using namespace srs_internal;
|
||||||
|
|
||||||
// @global the version to identify the core.
|
// @global the version to identify the core.
|
||||||
const char* _srs_version = "XCORE-" RTMP_SIG_SRS_SERVER;
|
const char* _srs_version = "XCORE-" RTMP_SIG_SRS_SERVER;
|
||||||
|
@ -92,7 +92,7 @@ bool is_common_space(char ch)
|
||||||
return (ch == ' ' || ch == '\t' || ch == SRS_CR || ch == SRS_LF);
|
return (ch == ' ' || ch == '\t' || ch == SRS_CR || ch == SRS_LF);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace _srs_internal
|
namespace srs_internal
|
||||||
{
|
{
|
||||||
SrsConfigBuffer::SrsConfigBuffer()
|
SrsConfigBuffer::SrsConfigBuffer()
|
||||||
{
|
{
|
||||||
|
@ -130,7 +130,7 @@ namespace _srs_internal
|
||||||
// read total content from file.
|
// read total content from file.
|
||||||
ssize_t nread = 0;
|
ssize_t nread = 0;
|
||||||
if ((err = reader.read(start, filesize, &nread)) != srs_success) {
|
if ((err = reader.read(start, filesize, &nread)) != srs_success) {
|
||||||
return srs_error_wrap(err, "read %d only %d bytes", filesize, nread);
|
return srs_error_wrap(err, "read %d only %d bytes", filesize, (int)nread);
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
@ -298,7 +298,7 @@ bool srs_config_apply_filter(SrsConfDirective* dvr_apply, SrsRequest* req)
|
||||||
}
|
}
|
||||||
|
|
||||||
string id = req->app + "/" + req->stream;
|
string id = req->app + "/" + req->stream;
|
||||||
if (::find(args.begin(), args.end(), id) != args.end()) {
|
if (std::find(args.begin(), args.end(), id) != args.end()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -620,6 +620,7 @@ srs_error_t srs_config_dumps_engine(SrsConfDirective* dir, SrsJsonObject* engine
|
||||||
|
|
||||||
SrsConfDirective::SrsConfDirective()
|
SrsConfDirective::SrsConfDirective()
|
||||||
{
|
{
|
||||||
|
conf_line = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsConfDirective::~SrsConfDirective()
|
SrsConfDirective::~SrsConfDirective()
|
||||||
|
@ -785,7 +786,7 @@ SrsConfDirective* SrsConfDirective::set_arg0(string a0)
|
||||||
void SrsConfDirective::remove(SrsConfDirective* v)
|
void SrsConfDirective::remove(SrsConfDirective* v)
|
||||||
{
|
{
|
||||||
std::vector<SrsConfDirective*>::iterator it;
|
std::vector<SrsConfDirective*>::iterator it;
|
||||||
if ((it = ::find(directives.begin(), directives.end(), v)) != directives.end()) {
|
if ((it = std::find(directives.begin(), directives.end(), v)) != directives.end()) {
|
||||||
directives.erase(it);
|
directives.erase(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1140,6 +1141,7 @@ SrsConfig::SrsConfig()
|
||||||
show_help = false;
|
show_help = false;
|
||||||
show_version = false;
|
show_version = false;
|
||||||
test_conf = false;
|
test_conf = false;
|
||||||
|
show_signature = false;
|
||||||
|
|
||||||
root = new SrsConfDirective();
|
root = new SrsConfDirective();
|
||||||
root->conf_line = 0;
|
root->conf_line = 0;
|
||||||
|
@ -1468,9 +1470,6 @@ srs_error_t SrsConfig::reload_conf(SrsConfig* conf)
|
||||||
root = conf->root;
|
root = conf->root;
|
||||||
conf->root = NULL;
|
conf->root = NULL;
|
||||||
|
|
||||||
// merge config.
|
|
||||||
std::vector<ISrsReloadHandler*>::iterator it;
|
|
||||||
|
|
||||||
// never support reload:
|
// never support reload:
|
||||||
// daemon
|
// daemon
|
||||||
//
|
//
|
||||||
|
@ -1988,9 +1987,8 @@ srs_error_t SrsConfig::parse_options(int argc, char** argv)
|
||||||
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "no log file");
|
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "no log file");
|
||||||
}
|
}
|
||||||
if (get_log_tank_file()) {
|
if (get_log_tank_file()) {
|
||||||
srs_trace("write log to file %s", log_filename.c_str());
|
srs_trace("you can check log by: tail -f %s (@see %s)", log_filename.c_str(), SRS_WIKI_URL_LOG);
|
||||||
srs_trace("you can: tailf %s", log_filename.c_str());
|
srs_trace("please check SRS by: ./etc/init.d/srs status");
|
||||||
srs_trace("@see: %s", SRS_WIKI_URL_LOG);
|
|
||||||
} else {
|
} else {
|
||||||
srs_trace("write log to console");
|
srs_trace("write log to console");
|
||||||
}
|
}
|
||||||
|
@ -2189,6 +2187,8 @@ srs_error_t SrsConfig::global_to_json(SrsJsonObject* obj)
|
||||||
sobj->set(sdir->name, sdir->dumps_arg0_to_integer());
|
sobj->set(sdir->name, sdir->dumps_arg0_to_integer());
|
||||||
} else if (sdir->name == "audio_enable") {
|
} else if (sdir->name == "audio_enable") {
|
||||||
sobj->set(sdir->name, sdir->dumps_arg0_to_boolean());
|
sobj->set(sdir->name, sdir->dumps_arg0_to_boolean());
|
||||||
|
} else if (sdir->name == "jitterbuffer_enable") {
|
||||||
|
sobj->set(sdir->name, sdir->dumps_arg0_to_boolean());
|
||||||
} else if (sdir->name == "host") {
|
} else if (sdir->name == "host") {
|
||||||
sobj->set(sdir->name, sdir->dumps_arg0_to_str());
|
sobj->set(sdir->name, sdir->dumps_arg0_to_str());
|
||||||
} else if (sdir->name == "wait_keyframe") {
|
} else if (sdir->name == "wait_keyframe") {
|
||||||
|
@ -2243,8 +2243,11 @@ srs_error_t SrsConfig::global_to_json(SrsJsonObject* obj)
|
||||||
SrsJsonObject* sobj = SrsJsonAny::object();
|
SrsJsonObject* sobj = SrsJsonAny::object();
|
||||||
sobjs->set(dir->arg0(), sobj);
|
sobjs->set(dir->arg0(), sobj);
|
||||||
|
|
||||||
SrsStatisticVhost* svhost = stat->find_vhost(dir->arg0());
|
SrsStatisticVhost* svhost = stat->find_vhost_by_name(dir->arg0());
|
||||||
sobj->set("id", SrsJsonAny::integer(svhost? (double)svhost->id : 0));
|
if (!svhost) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
sobj->set("id", SrsJsonAny::str(svhost->id.c_str()));
|
||||||
sobj->set("name", dir->dumps_arg0_to_str());
|
sobj->set("name", dir->dumps_arg0_to_str());
|
||||||
sobj->set("enabled", SrsJsonAny::boolean(get_vhost_enabled(dir->arg0())));
|
sobj->set("enabled", SrsJsonAny::boolean(get_vhost_enabled(dir->arg0())));
|
||||||
|
|
||||||
|
@ -2368,8 +2371,11 @@ srs_error_t SrsConfig::vhost_to_json(SrsConfDirective* vhost, SrsJsonObject* obj
|
||||||
// always present in vhost.
|
// always present in vhost.
|
||||||
SrsStatistic* stat = SrsStatistic::instance();
|
SrsStatistic* stat = SrsStatistic::instance();
|
||||||
|
|
||||||
SrsStatisticVhost* svhost = stat->find_vhost(vhost->arg0());
|
SrsStatisticVhost* svhost = stat->find_vhost_by_name(vhost->arg0());
|
||||||
obj->set("id", SrsJsonAny::integer(svhost? (double)svhost->id : 0));
|
if (!svhost) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
obj->set("id", SrsJsonAny::str(svhost->id.c_str()));
|
||||||
|
|
||||||
obj->set("name", vhost->dumps_arg0_to_str());
|
obj->set("name", vhost->dumps_arg0_to_str());
|
||||||
obj->set("enabled", SrsJsonAny::boolean(get_vhost_enabled(vhost)));
|
obj->set("enabled", SrsJsonAny::boolean(get_vhost_enabled(vhost)));
|
||||||
|
@ -3185,7 +3191,7 @@ srs_error_t SrsConfig::raw_enable_dvr(string vhost, string stream, bool& applied
|
||||||
conf->args.clear();
|
conf->args.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (::find(conf->args.begin(), conf->args.end(), stream) == conf->args.end()) {
|
if (std::find(conf->args.begin(), conf->args.end(), stream) == conf->args.end()) {
|
||||||
conf->args.push_back(stream);
|
conf->args.push_back(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3209,7 +3215,7 @@ srs_error_t SrsConfig::raw_disable_dvr(string vhost, string stream, bool& applie
|
||||||
|
|
||||||
std::vector<string>::iterator it;
|
std::vector<string>::iterator it;
|
||||||
|
|
||||||
if ((it = ::find(conf->args.begin(), conf->args.end(), stream)) != conf->args.end()) {
|
if ((it = std::find(conf->args.begin(), conf->args.end(), stream)) != conf->args.end()) {
|
||||||
conf->args.erase(it);
|
conf->args.erase(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3584,7 +3590,7 @@ srs_error_t SrsConfig::check_normal_config()
|
||||||
for (int i = 0; conf && i < (int)conf->directives.size(); i++) {
|
for (int i = 0; conf && i < (int)conf->directives.size(); i++) {
|
||||||
SrsConfDirective* obj = conf->at(i);
|
SrsConfDirective* obj = conf->at(i);
|
||||||
string n = obj->name;
|
string n = obj->name;
|
||||||
if (n != "enabled" && n != "listen" && n != "crossdomain" && n != "raw_api") {
|
if (n != "enabled" && n != "listen" && n != "crossdomain" && n != "raw_api" && n != "https") {
|
||||||
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal http_api.%s", n.c_str());
|
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal http_api.%s", n.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3602,7 +3608,7 @@ srs_error_t SrsConfig::check_normal_config()
|
||||||
SrsConfDirective* conf = root->get("http_server");
|
SrsConfDirective* conf = root->get("http_server");
|
||||||
for (int i = 0; conf && i < (int)conf->directives.size(); i++) {
|
for (int i = 0; conf && i < (int)conf->directives.size(); i++) {
|
||||||
string n = conf->at(i)->name;
|
string n = conf->at(i)->name;
|
||||||
if (n != "enabled" && n != "listen" && n != "dir" && n != "crossdomain") {
|
if (n != "enabled" && n != "listen" && n != "dir" && n != "crossdomain" && n != "https") {
|
||||||
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal http_stream.%s", n.c_str());
|
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal http_stream.%s", n.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3644,8 +3650,8 @@ srs_error_t SrsConfig::check_normal_config()
|
||||||
for (int i = 0; conf && i < (int)conf->directives.size(); i++) {
|
for (int i = 0; conf && i < (int)conf->directives.size(); i++) {
|
||||||
string n = conf->at(i)->name;
|
string n = conf->at(i)->name;
|
||||||
if (n != "enabled" && n != "listen" && n != "dir" && n != "candidate" && n != "ecdsa"
|
if (n != "enabled" && n != "listen" && n != "dir" && n != "candidate" && n != "ecdsa"
|
||||||
&& n != "sendmmsg" && n != "encrypt" && n != "reuseport" && n != "gso" && n != "merge_nalus"
|
&& n != "encrypt" && n != "reuseport" && n != "merge_nalus" && n != "perf_stat" && n != "black_hole"
|
||||||
&& n != "padding" && n != "perf_stat" && n != "queue_length") {
|
&& n != "ip_family") {
|
||||||
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal rtc_server.%s", n.c_str());
|
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal rtc_server.%s", n.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3662,7 +3668,7 @@ srs_error_t SrsConfig::check_normal_config()
|
||||||
for (int i = 0; i < (int)listens.size(); i++) {
|
for (int i = 0; i < (int)listens.size(); i++) {
|
||||||
string port = listens[i];
|
string port = listens[i];
|
||||||
if (port.empty() || ::atoi(port.c_str()) <= 0) {
|
if (port.empty() || ::atoi(port.c_str()) <= 0) {
|
||||||
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "listen.port=%d is invalid", port.c_str());
|
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "listen.port=%s is invalid", port.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3682,13 +3688,15 @@ srs_error_t SrsConfig::check_normal_config()
|
||||||
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "invalid stats.network=%d", get_stats_network());
|
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "invalid stats.network=%d", get_stats_network());
|
||||||
}
|
}
|
||||||
if (true) {
|
if (true) {
|
||||||
vector<std::string> ips = srs_get_local_ips();
|
vector<SrsIPAddress*> ips = srs_get_local_ips();
|
||||||
int index = get_stats_network();
|
int index = get_stats_network();
|
||||||
if (index >= (int)ips.size()) {
|
if (index >= (int)ips.size()) {
|
||||||
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "invalid stats.network=%d of %d",
|
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "invalid stats.network=%d of %d",
|
||||||
index, (int)ips.size());
|
index, (int)ips.size());
|
||||||
}
|
}
|
||||||
srs_warn("stats network use index=%d, ip=%s", index, ips.at(index).c_str());
|
|
||||||
|
SrsIPAddress* addr = ips.at(index);
|
||||||
|
srs_warn("stats network use index=%d, ip=%s, ifname=%s", index, addr->ip.c_str(), addr->ifname.c_str());
|
||||||
}
|
}
|
||||||
if (true) {
|
if (true) {
|
||||||
SrsConfDirective* conf = get_stats_disk_device();
|
SrsConfDirective* conf = get_stats_disk_device();
|
||||||
|
@ -3727,9 +3735,8 @@ srs_error_t SrsConfig::check_normal_config()
|
||||||
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "log file is empty");
|
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "log file is empty");
|
||||||
}
|
}
|
||||||
if (get_log_tank_file()) {
|
if (get_log_tank_file()) {
|
||||||
srs_trace("write log to file %s", log_filename.c_str());
|
srs_trace("you can check log by: tail -f %s (@see %s)", log_filename.c_str(), SRS_WIKI_URL_LOG);
|
||||||
srs_trace("you can: tailf %s", log_filename.c_str());
|
srs_trace("please check SRS by: ./etc/init.d/srs status");
|
||||||
srs_trace("@see: %s", SRS_WIKI_URL_LOG);
|
|
||||||
} else {
|
} else {
|
||||||
srs_trace("write log to console");
|
srs_trace("write log to console");
|
||||||
}
|
}
|
||||||
|
@ -3745,9 +3752,9 @@ srs_error_t SrsConfig::check_normal_config()
|
||||||
SrsConfDirective* conf = stream_caster->at(i);
|
SrsConfDirective* conf = stream_caster->at(i);
|
||||||
string n = conf->name;
|
string n = conf->name;
|
||||||
if (n != "enabled" && n != "caster" && n != "output"
|
if (n != "enabled" && n != "caster" && n != "output"
|
||||||
&& n != "listen" && n != "rtp_port_min" && n != "rtp_port_max"
|
&& n != "listen" && n != "tcp_enable" && n != "rtp_port_min" && n != "rtp_port_max"
|
||||||
&& n != "rtp_idle_timeout" && n != "sip"
|
&& n != "rtp_idle_timeout" && n != "sip"
|
||||||
&& n != "audio_enable" && n != "wait_keyframe"
|
&& n != "audio_enable" && n != "wait_keyframe" && n != "jitterbuffer_enable"
|
||||||
&& n != "host" && n != "auto_create_channel") {
|
&& n != "host" && n != "auto_create_channel") {
|
||||||
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal stream_caster.%s", n.c_str());
|
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal stream_caster.%s", n.c_str());
|
||||||
}
|
}
|
||||||
|
@ -3782,7 +3789,8 @@ srs_error_t SrsConfig::check_normal_config()
|
||||||
&& n != "play" && n != "publish" && n != "cluster"
|
&& n != "play" && n != "publish" && n != "cluster"
|
||||||
&& n != "security" && n != "http_remux" && n != "dash"
|
&& n != "security" && n != "http_remux" && n != "dash"
|
||||||
&& n != "http_static" && n != "hds" && n != "exec"
|
&& 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 != "nack"
|
||||||
|
&& n != "twcc") {
|
||||||
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal vhost.%s", n.c_str());
|
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal vhost.%s", n.c_str());
|
||||||
}
|
}
|
||||||
// for each sub directives of vhost.
|
// for each sub directives of vhost.
|
||||||
|
@ -3932,7 +3940,8 @@ srs_error_t SrsConfig::check_normal_config()
|
||||||
} else if (n == "rtc") {
|
} else if (n == "rtc") {
|
||||||
for (int j = 0; j < (int)conf->directives.size(); j++) {
|
for (int j = 0; j < (int)conf->directives.size(); j++) {
|
||||||
string m = conf->at(j)->name;
|
string m = conf->at(j)->name;
|
||||||
if (m != "enabled" && m != "bframe" && m != "aac" && m != "stun_timeout" && m != "stun_strict_check") {
|
if (m != "enabled" && m != "bframe" && m != "aac" && m != "stun_timeout" && m != "stun_strict_check"
|
||||||
|
&& m != "dtls_role" && m != "dtls_version" && m != "drop_for_pt") {
|
||||||
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal vhost.rtc.%s of %s", m.c_str(), vhost->arg0().c_str());
|
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal vhost.rtc.%s of %s", m.c_str(), vhost->arg0().c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4367,6 +4376,22 @@ int SrsConfig::get_stream_caster_listen(SrsConfDirective* conf)
|
||||||
return ::atoi(conf->arg0().c_str());
|
return ::atoi(conf->arg0().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SrsConfig::get_stream_caster_tcp_enable(SrsConfDirective* conf)
|
||||||
|
{
|
||||||
|
static bool DEFAULT = false;
|
||||||
|
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf = conf->get("tcp_enable");
|
||||||
|
if (!conf || conf->arg0().empty()) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SRS_CONF_PERFER_FALSE(conf->arg0());
|
||||||
|
}
|
||||||
|
|
||||||
int SrsConfig::get_stream_caster_rtp_port_min(SrsConfDirective* conf)
|
int SrsConfig::get_stream_caster_rtp_port_min(SrsConfDirective* conf)
|
||||||
{
|
{
|
||||||
static int DEFAULT = 0;
|
static int DEFAULT = 0;
|
||||||
|
@ -4538,9 +4563,25 @@ bool SrsConfig::get_stream_caster_gb28181_audio_enable(SrsConfDirective* conf)
|
||||||
return SRS_CONF_PERFER_FALSE(conf->arg0());
|
return SRS_CONF_PERFER_FALSE(conf->arg0());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SrsConfig::get_stream_caster_gb28181_jitterbuffer_enable(SrsConfDirective* conf)
|
||||||
|
{
|
||||||
|
static bool DEFAULT = true;
|
||||||
|
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf = conf->get("jitterbuffer_enable");
|
||||||
|
if (!conf || conf->arg0().empty()) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SRS_CONF_PERFER_FALSE(conf->arg0());
|
||||||
|
}
|
||||||
|
|
||||||
bool SrsConfig::get_stream_caster_gb28181_wait_keyframe(SrsConfDirective* conf)
|
bool SrsConfig::get_stream_caster_gb28181_wait_keyframe(SrsConfDirective* conf)
|
||||||
{
|
{
|
||||||
static bool DEFAULT = false;
|
static bool DEFAULT = true;
|
||||||
|
|
||||||
if (!conf) {
|
if (!conf) {
|
||||||
return DEFAULT;
|
return DEFAULT;
|
||||||
|
@ -4679,7 +4720,7 @@ srs_utime_t SrsConfig::get_stream_caster_gb28181_sip_query_catalog_interval(SrsC
|
||||||
return (srs_utime_t)(::atoi(conf->arg0().c_str()) * SRS_UTIME_SECONDS);
|
return (srs_utime_t)(::atoi(conf->arg0().c_str()) * SRS_UTIME_SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
int SrsConfig::get_rtc_server_enabled()
|
bool SrsConfig::get_rtc_server_enabled()
|
||||||
{
|
{
|
||||||
SrsConfDirective* conf = root->get("rtc_server");
|
SrsConfDirective* conf = root->get("rtc_server");
|
||||||
return get_rtc_server_enabled(conf);
|
return get_rtc_server_enabled(conf);
|
||||||
|
@ -4742,7 +4783,24 @@ std::string SrsConfig::get_rtc_server_candidates()
|
||||||
return DEFAULT;
|
return DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (conf->arg0().c_str());
|
return conf->arg0();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SrsConfig::get_rtc_server_ip_family()
|
||||||
|
{
|
||||||
|
static string DEFAULT = "ipv4";
|
||||||
|
|
||||||
|
SrsConfDirective* conf = root->get("rtc_server");
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf = conf->get("ip_family");
|
||||||
|
if (!conf || conf->arg0().empty()) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return conf->arg0();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SrsConfig::get_rtc_server_ecdsa()
|
bool SrsConfig::get_rtc_server_ecdsa()
|
||||||
|
@ -4779,35 +4837,15 @@ bool SrsConfig::get_rtc_server_encrypt()
|
||||||
return SRS_CONF_PERFER_TRUE(conf->arg0());
|
return SRS_CONF_PERFER_TRUE(conf->arg0());
|
||||||
}
|
}
|
||||||
|
|
||||||
int SrsConfig::get_rtc_server_sendmmsg()
|
|
||||||
{
|
|
||||||
#if !defined(SRS_AUTO_HAS_SENDMMSG) || !defined(SRS_AUTO_SENDMMSG)
|
|
||||||
return 1;
|
|
||||||
#else
|
|
||||||
static int DEFAULT = 256;
|
|
||||||
|
|
||||||
SrsConfDirective* conf = root->get("rtc_server");
|
|
||||||
if (!conf) {
|
|
||||||
return DEFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
conf = conf->get("sendmmsg");
|
|
||||||
if (!conf || conf->arg0().empty()) {
|
|
||||||
return DEFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
int v = ::atoi(conf->arg0().c_str());
|
|
||||||
return srs_max(1, v);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
int SrsConfig::get_rtc_server_reuseport()
|
int SrsConfig::get_rtc_server_reuseport()
|
||||||
{
|
{
|
||||||
int v = get_rtc_server_reuseport2();
|
int v = get_rtc_server_reuseport2();
|
||||||
|
|
||||||
#if !defined(SO_REUSEPORT)
|
#if !defined(SO_REUSEPORT)
|
||||||
srs_warn("REUSEPORT not supported, reset %d to %d", reuseport, DEFAULT);
|
if (v > 1) {
|
||||||
v = 1
|
srs_warn("REUSEPORT not supported, reset %d to %d", reuseport, DEFAULT);
|
||||||
|
v = 1
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
|
@ -4815,7 +4853,7 @@ int SrsConfig::get_rtc_server_reuseport()
|
||||||
|
|
||||||
int SrsConfig::get_rtc_server_reuseport2()
|
int SrsConfig::get_rtc_server_reuseport2()
|
||||||
{
|
{
|
||||||
static int DEFAULT = 4;
|
static int DEFAULT = 1;
|
||||||
|
|
||||||
SrsConfDirective* conf = root->get("rtc_server");
|
SrsConfDirective* conf = root->get("rtc_server");
|
||||||
if (!conf) {
|
if (!conf) {
|
||||||
|
@ -4832,7 +4870,7 @@ int SrsConfig::get_rtc_server_reuseport2()
|
||||||
|
|
||||||
bool SrsConfig::get_rtc_server_merge_nalus()
|
bool SrsConfig::get_rtc_server_merge_nalus()
|
||||||
{
|
{
|
||||||
static int DEFAULT = true;
|
static int DEFAULT = false;
|
||||||
|
|
||||||
SrsConfDirective* conf = root->get("rtc_server");
|
SrsConfDirective* conf = root->get("rtc_server");
|
||||||
if (!conf) {
|
if (!conf) {
|
||||||
|
@ -4847,68 +4885,6 @@ bool SrsConfig::get_rtc_server_merge_nalus()
|
||||||
return SRS_CONF_PERFER_TRUE(conf->arg0());
|
return SRS_CONF_PERFER_TRUE(conf->arg0());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SrsConfig::get_rtc_server_gso()
|
|
||||||
{
|
|
||||||
bool v = get_rtc_server_gso2();
|
|
||||||
|
|
||||||
bool gso_disabled = false;
|
|
||||||
#if !defined(__linux__)
|
|
||||||
gso_disabled = true;
|
|
||||||
if (v) {
|
|
||||||
srs_warn("GSO is disabled, for Linux 4.18+ only");
|
|
||||||
}
|
|
||||||
#elif LINUX_VERSION_CODE < KERNEL_VERSION(4,18,0)
|
|
||||||
if (v) {
|
|
||||||
utsname un = {0};
|
|
||||||
int r0 = uname(&un);
|
|
||||||
if (r0 || strcmp(un.release, "4.18.0") < 0) {
|
|
||||||
gso_disabled = true;
|
|
||||||
srs_warn("GSO is disabled, for Linux 4.18+ only, r0=%d, kernel=%s", r0, un.release);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (v && gso_disabled) {
|
|
||||||
v = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SrsConfig::get_rtc_server_gso2()
|
|
||||||
{
|
|
||||||
static int DEFAULT = true;
|
|
||||||
|
|
||||||
SrsConfDirective* conf = root->get("rtc_server");
|
|
||||||
if (!conf) {
|
|
||||||
return DEFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
conf = conf->get("gso");
|
|
||||||
if (!conf || conf->arg0().empty()) {
|
|
||||||
return DEFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SRS_CONF_PERFER_TRUE(conf->arg0());
|
|
||||||
}
|
|
||||||
|
|
||||||
int SrsConfig::get_rtc_server_padding()
|
|
||||||
{
|
|
||||||
static int DEFAULT = 127;
|
|
||||||
|
|
||||||
SrsConfDirective* conf = root->get("rtc_server");
|
|
||||||
if (!conf) {
|
|
||||||
return DEFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
conf = conf->get("padding");
|
|
||||||
if (!conf || conf->arg0().empty()) {
|
|
||||||
return DEFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
return srs_min(127, ::atoi(conf->arg0().c_str()));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SrsConfig::get_rtc_server_perf_stat()
|
bool SrsConfig::get_rtc_server_perf_stat()
|
||||||
{
|
{
|
||||||
static bool DEFAULT = true;
|
static bool DEFAULT = true;
|
||||||
|
@ -4926,21 +4902,48 @@ bool SrsConfig::get_rtc_server_perf_stat()
|
||||||
return SRS_CONF_PERFER_TRUE(conf->arg0());
|
return SRS_CONF_PERFER_TRUE(conf->arg0());
|
||||||
}
|
}
|
||||||
|
|
||||||
int SrsConfig::get_rtc_server_queue_length()
|
bool SrsConfig::get_rtc_server_black_hole()
|
||||||
{
|
{
|
||||||
static int DEFAULT = 2000;
|
static bool DEFAULT = false;
|
||||||
|
|
||||||
SrsConfDirective* conf = root->get("rtc_server");
|
SrsConfDirective* conf = root->get("rtc_server");
|
||||||
if (!conf) {
|
if (!conf) {
|
||||||
return DEFAULT;
|
return DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
conf = conf->get("queue_length");
|
conf = conf->get("black_hole");
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf = conf->get("enabled");
|
||||||
if (!conf || conf->arg0().empty()) {
|
if (!conf || conf->arg0().empty()) {
|
||||||
return DEFAULT;
|
return DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ::atoi(conf->arg0().c_str());
|
return SRS_CONF_PERFER_FALSE(conf->arg0());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SrsConfig::get_rtc_server_black_hole_addr()
|
||||||
|
{
|
||||||
|
static string DEFAULT = "";
|
||||||
|
|
||||||
|
SrsConfDirective* conf = root->get("rtc_server");
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf = conf->get("black_hole");
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf = conf->get("addr");
|
||||||
|
if (!conf || conf->arg0().empty()) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return conf->arg0();
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsConfDirective* SrsConfig::get_rtc(string vhost)
|
SrsConfDirective* SrsConfig::get_rtc(string vhost)
|
||||||
|
@ -5039,6 +5042,103 @@ bool SrsConfig::get_rtc_stun_strict_check(string vhost)
|
||||||
return SRS_CONF_PERFER_FALSE(conf->arg0());
|
return SRS_CONF_PERFER_FALSE(conf->arg0());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string SrsConfig::get_rtc_dtls_role(string vhost)
|
||||||
|
{
|
||||||
|
static std::string DEFAULT = "passive";
|
||||||
|
|
||||||
|
SrsConfDirective* conf = get_rtc(vhost);
|
||||||
|
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf = conf->get("dtls_role");
|
||||||
|
if (!conf || conf->arg0().empty()) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return conf->arg0();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SrsConfig::get_rtc_dtls_version(string vhost)
|
||||||
|
{
|
||||||
|
static std::string DEFAULT = "auto";
|
||||||
|
|
||||||
|
SrsConfDirective* conf = get_rtc(vhost);
|
||||||
|
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf = conf->get("dtls_version");
|
||||||
|
if (!conf || conf->arg0().empty()) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return conf->arg0();
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsConfig::get_rtc_drop_for_pt(string vhost)
|
||||||
|
{
|
||||||
|
static int DEFAULT = 0;
|
||||||
|
|
||||||
|
SrsConfDirective* conf = get_vhost(vhost);
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf = conf->get("drop_for_pt");
|
||||||
|
if (!conf || conf->arg0().empty()) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ::atoi(conf->arg0().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SrsConfig::get_rtc_nack_enabled(string vhost)
|
||||||
|
{
|
||||||
|
static bool DEFAULT = true;
|
||||||
|
|
||||||
|
SrsConfDirective* conf = get_vhost(vhost);
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf = conf->get("nack");
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf = conf->get("enabled");
|
||||||
|
if (!conf || conf->arg0().empty()) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SRS_CONF_PERFER_TRUE(conf->arg0());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SrsConfig::get_rtc_twcc_enabled(string vhost)
|
||||||
|
{
|
||||||
|
static bool DEFAULT = true;
|
||||||
|
|
||||||
|
SrsConfDirective* conf = get_vhost(vhost);
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf = conf->get("twcc");
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf = conf->get("enabled");
|
||||||
|
if (!conf || conf->arg0().empty()) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SRS_CONF_PERFER_TRUE(conf->arg0());
|
||||||
|
}
|
||||||
|
|
||||||
SrsConfDirective* SrsConfig::get_vhost(string vhost, bool try_default_vhost)
|
SrsConfDirective* SrsConfig::get_vhost(string vhost, bool try_default_vhost)
|
||||||
{
|
{
|
||||||
srs_assert(root);
|
srs_assert(root);
|
||||||
|
@ -5194,20 +5294,20 @@ int SrsConfig::get_time_jitter(string vhost)
|
||||||
|
|
||||||
SrsConfDirective* conf = get_vhost(vhost);
|
SrsConfDirective* conf = get_vhost(vhost);
|
||||||
if (!conf) {
|
if (!conf) {
|
||||||
return _srs_time_jitter_string2int(DEFAULT);
|
return srs_time_jitter_string2int(DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
conf = conf->get("play");
|
conf = conf->get("play");
|
||||||
if (!conf) {
|
if (!conf) {
|
||||||
return _srs_time_jitter_string2int(DEFAULT);
|
return srs_time_jitter_string2int(DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
conf = conf->get("time_jitter");
|
conf = conf->get("time_jitter");
|
||||||
if (!conf || conf->arg0().empty()) {
|
if (!conf || conf->arg0().empty()) {
|
||||||
return _srs_time_jitter_string2int(DEFAULT);
|
return srs_time_jitter_string2int(DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _srs_time_jitter_string2int(conf->arg0());
|
return srs_time_jitter_string2int(conf->arg0());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SrsConfig::get_mix_correct(string vhost)
|
bool SrsConfig::get_mix_correct(string vhost)
|
||||||
|
@ -7427,15 +7527,15 @@ int SrsConfig::get_dvr_time_jitter(string vhost)
|
||||||
SrsConfDirective* conf = get_dvr(vhost);
|
SrsConfDirective* conf = get_dvr(vhost);
|
||||||
|
|
||||||
if (!conf) {
|
if (!conf) {
|
||||||
return _srs_time_jitter_string2int(DEFAULT);
|
return srs_time_jitter_string2int(DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
conf = conf->get("time_jitter");
|
conf = conf->get("time_jitter");
|
||||||
if (!conf || conf->arg0().empty()) {
|
if (!conf || conf->arg0().empty()) {
|
||||||
return _srs_time_jitter_string2int(DEFAULT);
|
return srs_time_jitter_string2int(DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _srs_time_jitter_string2int(conf->arg0());
|
return srs_time_jitter_string2int(conf->arg0());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SrsConfig::get_http_api_enabled()
|
bool SrsConfig::get_http_api_enabled()
|
||||||
|
@ -7583,6 +7683,83 @@ bool SrsConfig::get_raw_api_allow_update()
|
||||||
return SRS_CONF_PERFER_FALSE(conf->arg0());
|
return SRS_CONF_PERFER_FALSE(conf->arg0());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SrsConfDirective* SrsConfig::get_https_api()
|
||||||
|
{
|
||||||
|
SrsConfDirective* conf = root->get("http_api");
|
||||||
|
if (!conf) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return conf->get("https");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SrsConfig::get_https_api_enabled()
|
||||||
|
{
|
||||||
|
static bool DEFAULT = false;
|
||||||
|
|
||||||
|
SrsConfDirective* conf = get_https_api();
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf = conf->get("enabled");
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SRS_CONF_PERFER_FALSE(conf->arg0());
|
||||||
|
}
|
||||||
|
|
||||||
|
string SrsConfig::get_https_api_listen()
|
||||||
|
{
|
||||||
|
static string DEFAULT = "1990";
|
||||||
|
|
||||||
|
SrsConfDirective* conf = get_https_api();
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf = conf->get("listen");
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return conf->arg0();
|
||||||
|
}
|
||||||
|
|
||||||
|
string SrsConfig::get_https_api_ssl_key()
|
||||||
|
{
|
||||||
|
static string DEFAULT = "./conf/server.key";
|
||||||
|
|
||||||
|
SrsConfDirective* conf = get_https_api();
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf = conf->get("key");
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return conf->arg0();
|
||||||
|
}
|
||||||
|
|
||||||
|
string SrsConfig::get_https_api_ssl_cert()
|
||||||
|
{
|
||||||
|
static string DEFAULT = "./conf/server.crt";
|
||||||
|
|
||||||
|
SrsConfDirective* conf = get_https_api();
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf = conf->get("cert");
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return conf->arg0();
|
||||||
|
}
|
||||||
|
|
||||||
bool SrsConfig::get_srt_enabled()
|
bool SrsConfig::get_srt_enabled()
|
||||||
{
|
{
|
||||||
|
@ -7871,6 +8048,84 @@ bool SrsConfig::get_http_stream_crossdomain()
|
||||||
return SRS_CONF_PERFER_TRUE(conf->arg0());
|
return SRS_CONF_PERFER_TRUE(conf->arg0());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SrsConfDirective* SrsConfig::get_https_stream()
|
||||||
|
{
|
||||||
|
SrsConfDirective* conf = root->get("http_server");
|
||||||
|
if (!conf) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return conf->get("https");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SrsConfig::get_https_stream_enabled()
|
||||||
|
{
|
||||||
|
static bool DEFAULT = false;
|
||||||
|
|
||||||
|
SrsConfDirective* conf = get_https_stream();
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf = conf->get("enabled");
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SRS_CONF_PERFER_FALSE(conf->arg0());
|
||||||
|
}
|
||||||
|
|
||||||
|
string SrsConfig::get_https_stream_listen()
|
||||||
|
{
|
||||||
|
static string DEFAULT = "8088";
|
||||||
|
|
||||||
|
SrsConfDirective* conf = get_https_stream();
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf = conf->get("listen");
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return conf->arg0();
|
||||||
|
}
|
||||||
|
|
||||||
|
string SrsConfig::get_https_stream_ssl_key()
|
||||||
|
{
|
||||||
|
static string DEFAULT = "./conf/server.key";
|
||||||
|
|
||||||
|
SrsConfDirective* conf = get_https_stream();
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf = conf->get("key");
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return conf->arg0();
|
||||||
|
}
|
||||||
|
|
||||||
|
string SrsConfig::get_https_stream_ssl_cert()
|
||||||
|
{
|
||||||
|
static string DEFAULT = "./conf/server.crt";
|
||||||
|
|
||||||
|
SrsConfDirective* conf = get_https_stream();
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf = conf->get("cert");
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return conf->arg0();
|
||||||
|
}
|
||||||
|
|
||||||
bool SrsConfig::get_vhost_http_enabled(string vhost)
|
bool SrsConfig::get_vhost_http_enabled(string vhost)
|
||||||
{
|
{
|
||||||
static bool DEFAULT = false;
|
static bool DEFAULT = false;
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
|
|
||||||
#include <srs_app_reload.hpp>
|
#include <srs_app_reload.hpp>
|
||||||
#include <srs_app_async_call.hpp>
|
#include <srs_app_async_call.hpp>
|
||||||
#include <srs_app_thread.hpp>
|
#include <srs_app_st.hpp>
|
||||||
|
|
||||||
class SrsRequest;
|
class SrsRequest;
|
||||||
class SrsFileWriter;
|
class SrsFileWriter;
|
||||||
|
@ -77,7 +77,7 @@ bool srs_vector_actual_equals(const std::vector<T>& a, const std::vector<T>& b)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace _srs_internal
|
namespace srs_internal
|
||||||
{
|
{
|
||||||
// The buffer of config content.
|
// The buffer of config content.
|
||||||
class SrsConfigBuffer
|
class SrsConfigBuffer
|
||||||
|
@ -229,7 +229,7 @@ public:
|
||||||
// Parse utilities
|
// Parse utilities
|
||||||
public:
|
public:
|
||||||
// Parse config directive from file buffer.
|
// Parse config directive from file buffer.
|
||||||
virtual srs_error_t parse(_srs_internal::SrsConfigBuffer* buffer);
|
virtual srs_error_t parse(srs_internal::SrsConfigBuffer* buffer);
|
||||||
// Marshal the directive to writer.
|
// Marshal the directive to writer.
|
||||||
// @param level, the root is level0, all its directives are level1, and so on.
|
// @param level, the root is level0, all its directives are level1, and so on.
|
||||||
virtual srs_error_t persistence(SrsFileWriter* writer, int level);
|
virtual srs_error_t persistence(SrsFileWriter* writer, int level);
|
||||||
|
@ -253,13 +253,13 @@ private:
|
||||||
// 1. read a token(directive args and a ret flag),
|
// 1. read a token(directive args and a ret flag),
|
||||||
// 2. initialize the directive by args, args[0] is name, args[1-N] is args of directive,
|
// 2. initialize the directive by args, args[0] is name, args[1-N] is args of directive,
|
||||||
// 3. if ret flag indicates there are child-directives, read_conf(directive, block) recursively.
|
// 3. if ret flag indicates there are child-directives, read_conf(directive, block) recursively.
|
||||||
virtual srs_error_t parse_conf(_srs_internal::SrsConfigBuffer* buffer, SrsDirectiveType type);
|
virtual srs_error_t parse_conf(srs_internal::SrsConfigBuffer* buffer, SrsDirectiveType type);
|
||||||
// Read a token from buffer.
|
// Read a token from buffer.
|
||||||
// A token, is the directive args and a flag indicates whether has child-directives.
|
// A token, is the directive args and a flag indicates whether has child-directives.
|
||||||
// @param args, the output directive args, the first is the directive name, left is the args.
|
// @param args, the output directive args, the first is the directive name, left is the args.
|
||||||
// @param line_start, the actual start line of directive.
|
// @param line_start, the actual start line of directive.
|
||||||
// @return, an error code indicates error or has child-directives.
|
// @return, an error code indicates error or has child-directives.
|
||||||
virtual srs_error_t read_token(_srs_internal::SrsConfigBuffer* buffer, std::vector<std::string>& args, int& line_start);
|
virtual srs_error_t read_token(srs_internal::SrsConfigBuffer* buffer, std::vector<std::string>& args, int& line_start);
|
||||||
};
|
};
|
||||||
|
|
||||||
// The config service provider.
|
// The config service provider.
|
||||||
|
@ -425,7 +425,7 @@ protected:
|
||||||
// Parse config from the buffer.
|
// Parse config from the buffer.
|
||||||
// @param buffer, the config buffer, user must delete it.
|
// @param buffer, the config buffer, user must delete it.
|
||||||
// @remark, use protected for the utest to override with mock.
|
// @remark, use protected for the utest to override with mock.
|
||||||
virtual srs_error_t parse_buffer(_srs_internal::SrsConfigBuffer* buffer);
|
virtual srs_error_t parse_buffer(srs_internal::SrsConfigBuffer* buffer);
|
||||||
// global env
|
// global env
|
||||||
public:
|
public:
|
||||||
// Get the current work directory.
|
// Get the current work directory.
|
||||||
|
@ -499,6 +499,8 @@ public:
|
||||||
virtual std::string get_stream_caster_output(SrsConfDirective* conf);
|
virtual std::string get_stream_caster_output(SrsConfDirective* conf);
|
||||||
// Get the listen port of stream caster.
|
// Get the listen port of stream caster.
|
||||||
virtual int get_stream_caster_listen(SrsConfDirective* conf);
|
virtual int get_stream_caster_listen(SrsConfDirective* conf);
|
||||||
|
// Get the listen port type of stream caster.
|
||||||
|
virtual bool get_stream_caster_tcp_enable(SrsConfDirective* conf);
|
||||||
// Get the min udp port for rtp of stream caster rtsp.
|
// Get the min udp port for rtp of stream caster rtsp.
|
||||||
virtual int get_stream_caster_rtp_port_min(SrsConfDirective* conf);
|
virtual int get_stream_caster_rtp_port_min(SrsConfDirective* conf);
|
||||||
// Get the max udp port for rtp of stream caster rtsp.
|
// Get the max udp port for rtp of stream caster rtsp.
|
||||||
|
@ -508,6 +510,7 @@ public:
|
||||||
virtual srs_utime_t get_stream_caster_gb28181_ack_timeout(SrsConfDirective* conf);
|
virtual srs_utime_t get_stream_caster_gb28181_ack_timeout(SrsConfDirective* conf);
|
||||||
virtual srs_utime_t get_stream_caster_gb28181_keepalive_timeout(SrsConfDirective* conf);
|
virtual srs_utime_t get_stream_caster_gb28181_keepalive_timeout(SrsConfDirective* conf);
|
||||||
virtual bool get_stream_caster_gb28181_audio_enable(SrsConfDirective* conf);
|
virtual bool get_stream_caster_gb28181_audio_enable(SrsConfDirective* conf);
|
||||||
|
virtual bool get_stream_caster_gb28181_jitterbuffer_enable(SrsConfDirective* conf);
|
||||||
virtual std::string get_stream_caster_gb28181_host(SrsConfDirective* conf);
|
virtual std::string get_stream_caster_gb28181_host(SrsConfDirective* conf);
|
||||||
virtual std::string get_stream_caster_gb28181_serial(SrsConfDirective* conf);
|
virtual std::string get_stream_caster_gb28181_serial(SrsConfDirective* conf);
|
||||||
virtual std::string get_stream_caster_gb28181_realm(SrsConfDirective* conf);
|
virtual std::string get_stream_caster_gb28181_realm(SrsConfDirective* conf);
|
||||||
|
@ -521,22 +524,20 @@ public:
|
||||||
|
|
||||||
// rtc section
|
// rtc section
|
||||||
public:
|
public:
|
||||||
virtual int get_rtc_server_enabled();
|
virtual bool get_rtc_server_enabled();
|
||||||
virtual bool get_rtc_server_enabled(SrsConfDirective* conf);
|
virtual bool get_rtc_server_enabled(SrsConfDirective* conf);
|
||||||
virtual int get_rtc_server_listen();
|
virtual int get_rtc_server_listen();
|
||||||
virtual std::string get_rtc_server_candidates();
|
virtual std::string get_rtc_server_candidates();
|
||||||
|
virtual std::string get_rtc_server_ip_family();
|
||||||
virtual bool get_rtc_server_ecdsa();
|
virtual bool get_rtc_server_ecdsa();
|
||||||
virtual int get_rtc_server_sendmmsg();
|
|
||||||
virtual bool get_rtc_server_encrypt();
|
virtual bool get_rtc_server_encrypt();
|
||||||
virtual int get_rtc_server_reuseport();
|
virtual int get_rtc_server_reuseport();
|
||||||
virtual bool get_rtc_server_merge_nalus();
|
virtual bool get_rtc_server_merge_nalus();
|
||||||
virtual bool get_rtc_server_gso();
|
|
||||||
virtual int get_rtc_server_padding();
|
|
||||||
virtual bool get_rtc_server_perf_stat();
|
virtual bool get_rtc_server_perf_stat();
|
||||||
virtual int get_rtc_server_queue_length();
|
virtual bool get_rtc_server_black_hole();
|
||||||
|
virtual std::string get_rtc_server_black_hole_addr();
|
||||||
private:
|
private:
|
||||||
virtual int get_rtc_server_reuseport2();
|
virtual int get_rtc_server_reuseport2();
|
||||||
virtual bool get_rtc_server_gso2();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SrsConfDirective* get_rtc(std::string vhost);
|
SrsConfDirective* get_rtc(std::string vhost);
|
||||||
|
@ -545,6 +546,11 @@ public:
|
||||||
bool get_rtc_aac_discard(std::string vhost);
|
bool get_rtc_aac_discard(std::string vhost);
|
||||||
srs_utime_t get_rtc_stun_timeout(std::string vhost);
|
srs_utime_t get_rtc_stun_timeout(std::string vhost);
|
||||||
bool get_rtc_stun_strict_check(std::string vhost);
|
bool get_rtc_stun_strict_check(std::string vhost);
|
||||||
|
std::string get_rtc_dtls_role(std::string vhost);
|
||||||
|
std::string get_rtc_dtls_version(std::string vhost);
|
||||||
|
int get_rtc_drop_for_pt(std::string vhost);
|
||||||
|
bool get_rtc_nack_enabled(std::string vhost);
|
||||||
|
bool get_rtc_twcc_enabled(std::string vhost);
|
||||||
|
|
||||||
// vhost specified section
|
// vhost specified section
|
||||||
public:
|
public:
|
||||||
|
@ -1010,6 +1016,14 @@ public:
|
||||||
virtual bool get_raw_api_allow_query();
|
virtual bool get_raw_api_allow_query();
|
||||||
// Whether allow rpc update.
|
// Whether allow rpc update.
|
||||||
virtual bool get_raw_api_allow_update();
|
virtual bool get_raw_api_allow_update();
|
||||||
|
// https api section
|
||||||
|
private:
|
||||||
|
SrsConfDirective* get_https_api();
|
||||||
|
public:
|
||||||
|
virtual bool get_https_api_enabled();
|
||||||
|
virtual std::string get_https_api_listen();
|
||||||
|
virtual std::string get_https_api_ssl_key();
|
||||||
|
virtual std::string get_https_api_ssl_cert();
|
||||||
// http stream section
|
// http stream section
|
||||||
private:
|
private:
|
||||||
// Whether http stream enabled.
|
// Whether http stream enabled.
|
||||||
|
@ -1024,6 +1038,14 @@ public:
|
||||||
virtual std::string get_http_stream_dir();
|
virtual std::string get_http_stream_dir();
|
||||||
// Whether enable crossdomain for http static and stream server.
|
// Whether enable crossdomain for http static and stream server.
|
||||||
virtual bool get_http_stream_crossdomain();
|
virtual bool get_http_stream_crossdomain();
|
||||||
|
// https api section
|
||||||
|
private:
|
||||||
|
SrsConfDirective* get_https_stream();
|
||||||
|
public:
|
||||||
|
virtual bool get_https_stream_enabled();
|
||||||
|
virtual std::string get_https_stream_listen();
|
||||||
|
virtual std::string get_https_stream_ssl_key();
|
||||||
|
virtual std::string get_https_stream_ssl_cert();
|
||||||
public:
|
public:
|
||||||
// Get whether vhost enabled http stream
|
// Get whether vhost enabled http stream
|
||||||
virtual bool get_vhost_http_enabled(std::string vhost);
|
virtual bool get_vhost_http_enabled(std::string vhost);
|
||||||
|
|
|
@ -24,51 +24,350 @@
|
||||||
#include <srs_app_conn.hpp>
|
#include <srs_app_conn.hpp>
|
||||||
|
|
||||||
#include <netinet/tcp.h>
|
#include <netinet/tcp.h>
|
||||||
|
#include <algorithm>
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
#include <srs_kernel_log.hpp>
|
#include <srs_kernel_log.hpp>
|
||||||
#include <srs_kernel_error.hpp>
|
#include <srs_kernel_error.hpp>
|
||||||
#include <srs_app_utility.hpp>
|
#include <srs_app_utility.hpp>
|
||||||
#include <srs_kernel_utility.hpp>
|
#include <srs_kernel_utility.hpp>
|
||||||
|
#include <srs_service_log.hpp>
|
||||||
|
#include <srs_app_log.hpp>
|
||||||
|
#include <srs_app_config.hpp>
|
||||||
|
#include <srs_core_autofree.hpp>
|
||||||
|
|
||||||
SrsConnection::SrsConnection(IConnectionManager* cm, srs_netfd_t c, string cip)
|
ISrsDisposingHandler::ISrsDisposingHandler()
|
||||||
{
|
{
|
||||||
manager = cm;
|
|
||||||
stfd = c;
|
|
||||||
ip = cip;
|
|
||||||
create_time = srsu2ms(srs_get_system_time());
|
|
||||||
|
|
||||||
skt = new SrsStSocket();
|
|
||||||
clk = new SrsWallClock();
|
|
||||||
kbps = new SrsKbps(clk);
|
|
||||||
kbps->set_io(skt, skt);
|
|
||||||
|
|
||||||
trd = new SrsSTCoroutine("conn", this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsConnection::~SrsConnection()
|
ISrsDisposingHandler::~ISrsDisposingHandler()
|
||||||
{
|
{
|
||||||
dispose();
|
}
|
||||||
|
|
||||||
srs_freep(kbps);
|
SrsResourceManager::SrsResourceManager(const std::string& label, bool verbose)
|
||||||
srs_freep(clk);
|
{
|
||||||
|
verbose_ = verbose;
|
||||||
|
label_ = label;
|
||||||
|
cond = srs_cond_new();
|
||||||
|
trd = NULL;
|
||||||
|
p_disposing_ = NULL;
|
||||||
|
removing_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsResourceManager::~SrsResourceManager()
|
||||||
|
{
|
||||||
|
if (trd) {
|
||||||
|
srs_cond_signal(cond);
|
||||||
|
trd->stop();
|
||||||
|
|
||||||
|
srs_freep(trd);
|
||||||
|
srs_cond_destroy(cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsResourceManager::start()
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
cid_ = _srs_context->generate_id();
|
||||||
|
trd = new SrsSTCoroutine("manager", this, cid_);
|
||||||
|
|
||||||
|
if ((err = trd->start()) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "conn manager");
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SrsResourceManager::empty()
|
||||||
|
{
|
||||||
|
return conns_.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t SrsResourceManager::size()
|
||||||
|
{
|
||||||
|
return conns_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsResourceManager::cycle()
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
srs_trace("%s: connection manager run, conns=%d", label_.c_str(), (int)conns_.size());
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if ((err = trd->pull()) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "conn manager");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear all zombies, because we may switch context and lost signal
|
||||||
|
// when we clear zombie connection.
|
||||||
|
while (!zombies_.empty()) {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_cond_wait(cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsResourceManager::add(ISrsResource* conn)
|
||||||
|
{
|
||||||
|
if (std::find(conns_.begin(), conns_.end(), conn) == conns_.end()) {
|
||||||
|
conns_.push_back(conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsResourceManager::add_with_id(const std::string& id, ISrsResource* conn)
|
||||||
|
{
|
||||||
|
add(conn);
|
||||||
|
conns_id_[id] = conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsResourceManager::add_with_name(const std::string& name, ISrsResource* conn)
|
||||||
|
{
|
||||||
|
add(conn);
|
||||||
|
conns_name_[name] = conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
ISrsResource* SrsResourceManager::at(int index)
|
||||||
|
{
|
||||||
|
return (index < (int)conns_.size())? conns_.at(index) : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ISrsResource* SrsResourceManager::find_by_id(std::string id)
|
||||||
|
{
|
||||||
|
map<string, ISrsResource*>::iterator it = conns_id_.find(id);
|
||||||
|
return (it != conns_id_.end())? it->second : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ISrsResource* SrsResourceManager::find_by_name(std::string name)
|
||||||
|
{
|
||||||
|
map<string, ISrsResource*>::iterator it = conns_name_.find(name);
|
||||||
|
return (it != conns_name_.end())? it->second : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsResourceManager::subscribe(ISrsDisposingHandler* h)
|
||||||
|
{
|
||||||
|
if (std::find(handlers_.begin(), handlers_.end(), h) == handlers_.end()) {
|
||||||
|
handlers_.push_back(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore the handler from unsubscribing handlers.
|
||||||
|
vector<ISrsDisposingHandler*>::iterator it;
|
||||||
|
if ((it = std::find(unsubs_.begin(), unsubs_.end(), h)) != unsubs_.end()) {
|
||||||
|
unsubs_.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsResourceManager::unsubscribe(ISrsDisposingHandler* h)
|
||||||
|
{
|
||||||
|
vector<ISrsDisposingHandler*>::iterator it = find(handlers_.begin(), handlers_.end(), h);
|
||||||
|
if (it != handlers_.end()) {
|
||||||
|
handlers_.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put it to the unsubscribing handlers.
|
||||||
|
if (std::find(unsubs_.begin(), unsubs_.end(), h) == unsubs_.end()) {
|
||||||
|
unsubs_.push_back(h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsResourceManager::remove(ISrsResource* c)
|
||||||
|
{
|
||||||
|
SrsContextRestore(_srs_context->get_id());
|
||||||
|
|
||||||
|
removing_ = true;
|
||||||
|
do_remove(c);
|
||||||
|
removing_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsResourceManager::do_remove(ISrsResource* c)
|
||||||
|
{
|
||||||
|
bool in_zombie = false;
|
||||||
|
bool in_disposing = false;
|
||||||
|
check_remove(c, in_zombie, in_disposing);
|
||||||
|
bool ignored = in_zombie || in_disposing;
|
||||||
|
|
||||||
|
if (verbose_) {
|
||||||
|
_srs_context->set_id(c->get_id());
|
||||||
|
srs_trace("%s: before dispose resource(%s)(%p), conns=%d, zombies=%d, ign=%d, inz=%d, ind=%d",
|
||||||
|
label_.c_str(), c->desc().c_str(), c, (int)conns_.size(), (int)zombies_.size(), ignored,
|
||||||
|
in_zombie, in_disposing);
|
||||||
|
}
|
||||||
|
if (ignored) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push to zombies, we will free it in another coroutine.
|
||||||
|
zombies_.push_back(c);
|
||||||
|
|
||||||
|
// We should copy all handlers, because it may change during callback.
|
||||||
|
vector<ISrsDisposingHandler*> handlers = handlers_;
|
||||||
|
|
||||||
|
// Notify other handlers to handle the before-dispose event.
|
||||||
|
for (int i = 0; i < (int)handlers.size(); i++) {
|
||||||
|
ISrsDisposingHandler* h = handlers.at(i);
|
||||||
|
|
||||||
|
// Ignore if handler is unsubscribing.
|
||||||
|
if (!unsubs_.empty() && std::find(unsubs_.begin(), unsubs_.end(), h) != unsubs_.end()) {
|
||||||
|
srs_warn2(TAG_RESOURCE_UNSUB, "%s: ignore before-dispose resource(%s)(%p) for %p, conns=%d",
|
||||||
|
label_.c_str(), c->desc().c_str(), c, h, (int)conns_.size());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
h->on_before_dispose(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify the coroutine to free it.
|
||||||
|
srs_cond_signal(cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsResourceManager::check_remove(ISrsResource* c, bool& in_zombie, bool& in_disposing)
|
||||||
|
{
|
||||||
|
// Only notify when not removed(in zombies_).
|
||||||
|
vector<ISrsResource*>::iterator it = std::find(zombies_.begin(), zombies_.end(), c);
|
||||||
|
if (it != zombies_.end()) {
|
||||||
|
in_zombie = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also ignore when we are disposing it.
|
||||||
|
if (p_disposing_) {
|
||||||
|
it = std::find(p_disposing_->begin(), p_disposing_->end(), c);
|
||||||
|
if (it != p_disposing_->end()) {
|
||||||
|
in_disposing = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsResourceManager::clear()
|
||||||
|
{
|
||||||
|
if (zombies_.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsContextRestore(cid_);
|
||||||
|
if (verbose_) {
|
||||||
|
srs_trace("%s: clear zombies=%d resources, conns=%d, removing=%d, unsubs=%d",
|
||||||
|
label_.c_str(), (int)zombies_.size(), (int)conns_.size(), removing_, (int)unsubs_.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear all unsubscribing handlers, if not removing any resource.
|
||||||
|
if (!removing_ && !unsubs_.empty()) {
|
||||||
|
vector<ISrsDisposingHandler*>().swap(unsubs_);
|
||||||
|
}
|
||||||
|
|
||||||
|
do_clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsResourceManager::do_clear()
|
||||||
|
{
|
||||||
|
// To prevent thread switch when delete connection,
|
||||||
|
// we copy all connections then free one by one.
|
||||||
|
vector<ISrsResource*> copy;
|
||||||
|
copy.swap(zombies_);
|
||||||
|
p_disposing_ = ©
|
||||||
|
|
||||||
|
for (int i = 0; i < (int)copy.size(); i++) {
|
||||||
|
ISrsResource* conn = copy.at(i);
|
||||||
|
|
||||||
|
if (verbose_) {
|
||||||
|
_srs_context->set_id(conn->get_id());
|
||||||
|
srs_trace("%s: disposing #%d resource(%s)(%p), conns=%d, disposing=%d, zombies=%d", label_.c_str(),
|
||||||
|
i, conn->desc().c_str(), conn, (int)conns_.size(), (int)copy.size(), (int)zombies_.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset it for it points to a local object.
|
||||||
|
// @remark We must set the disposing to NULL to avoid reusing address,
|
||||||
|
// because the context might switch.
|
||||||
|
p_disposing_ = NULL;
|
||||||
|
|
||||||
|
// We should free the resources when finished all disposing callbacks,
|
||||||
|
// which might cause context switch and reuse the freed addresses.
|
||||||
|
for (int i = 0; i < (int)copy.size(); i++) {
|
||||||
|
ISrsResource* conn = copy.at(i);
|
||||||
|
srs_freep(conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsResourceManager::dispose(ISrsResource* c)
|
||||||
|
{
|
||||||
|
for (map<string, ISrsResource*>::iterator it = conns_name_.begin(); it != conns_name_.end();) {
|
||||||
|
if (c != it->second) {
|
||||||
|
++it;
|
||||||
|
} else {
|
||||||
|
// Use C++98 style: https://stackoverflow.com/a/4636230
|
||||||
|
conns_name_.erase(it++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (map<string, ISrsResource*>::iterator it = conns_id_.begin(); it != conns_id_.end();) {
|
||||||
|
if (c != it->second) {
|
||||||
|
++it;
|
||||||
|
} else {
|
||||||
|
// Use C++98 style: https://stackoverflow.com/a/4636230
|
||||||
|
conns_id_.erase(it++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<ISrsResource*>::iterator it = std::find(conns_.begin(), conns_.end(), c);
|
||||||
|
if (it != conns_.end()) {
|
||||||
|
conns_.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We should copy all handlers, because it may change during callback.
|
||||||
|
vector<ISrsDisposingHandler*> handlers = handlers_;
|
||||||
|
|
||||||
|
// Notify other handlers to handle the disposing event.
|
||||||
|
for (int i = 0; i < (int)handlers.size(); i++) {
|
||||||
|
ISrsDisposingHandler* h = handlers.at(i);
|
||||||
|
|
||||||
|
// Ignore if handler is unsubscribing.
|
||||||
|
if (!unsubs_.empty() && std::find(unsubs_.begin(), unsubs_.end(), h) != unsubs_.end()) {
|
||||||
|
srs_warn2(TAG_RESOURCE_UNSUB, "%s: ignore disposing resource(%s)(%p) for %p, conns=%d",
|
||||||
|
label_.c_str(), c->desc().c_str(), c, h, (int)conns_.size());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
h->on_disposing(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ISrsExpire::ISrsExpire()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ISrsExpire::~ISrsExpire()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ISrsStartableConneciton::ISrsStartableConneciton()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ISrsStartableConneciton::~ISrsStartableConneciton()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsTcpConnection::SrsTcpConnection(srs_netfd_t c)
|
||||||
|
{
|
||||||
|
stfd = c;
|
||||||
|
skt = new SrsStSocket();
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsTcpConnection::~SrsTcpConnection()
|
||||||
|
{
|
||||||
srs_freep(skt);
|
srs_freep(skt);
|
||||||
srs_freep(trd);
|
|
||||||
|
|
||||||
srs_close_stfd(stfd);
|
srs_close_stfd(stfd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SrsConnection::remark(int64_t* in, int64_t* out)
|
srs_error_t SrsTcpConnection::initialize()
|
||||||
{
|
|
||||||
kbps->remark(in, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SrsConnection::dispose()
|
|
||||||
{
|
|
||||||
trd->interrupt();
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_error_t SrsConnection::start()
|
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
@ -76,14 +375,10 @@ srs_error_t SrsConnection::start()
|
||||||
return srs_error_wrap(err, "init socket");
|
return srs_error_wrap(err, "init socket");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = trd->start()) != srs_success) {
|
|
||||||
return srs_error_wrap(err, "coroutine");
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsConnection::set_tcp_nodelay(bool v)
|
srs_error_t SrsTcpConnection::set_tcp_nodelay(bool v)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
@ -114,7 +409,7 @@ srs_error_t SrsConnection::set_tcp_nodelay(bool v)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsConnection::set_socket_buffer(srs_utime_t buffer_v)
|
srs_error_t SrsTcpConnection::set_socket_buffer(srs_utime_t buffer_v)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
@ -166,52 +461,342 @@ srs_error_t SrsConnection::set_socket_buffer(srs_utime_t buffer_v)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsConnection::cycle()
|
void SrsTcpConnection::set_recv_timeout(srs_utime_t tm)
|
||||||
{
|
{
|
||||||
srs_error_t err = do_cycle();
|
skt->set_recv_timeout(tm);
|
||||||
|
}
|
||||||
|
|
||||||
// Notify manager to remove it.
|
srs_utime_t SrsTcpConnection::get_recv_timeout()
|
||||||
manager->remove(this);
|
{
|
||||||
|
return skt->get_recv_timeout();
|
||||||
|
}
|
||||||
|
|
||||||
// success.
|
srs_error_t SrsTcpConnection::read_fully(void* buf, size_t size, ssize_t* nread)
|
||||||
if (err == srs_success) {
|
{
|
||||||
srs_trace("client finished.");
|
return skt->read_fully(buf, size, nread);
|
||||||
return err;
|
}
|
||||||
|
|
||||||
|
int64_t SrsTcpConnection::get_recv_bytes()
|
||||||
|
{
|
||||||
|
return skt->get_recv_bytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t SrsTcpConnection::get_send_bytes()
|
||||||
|
{
|
||||||
|
return skt->get_send_bytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsTcpConnection::read(void* buf, size_t size, ssize_t* nread)
|
||||||
|
{
|
||||||
|
return skt->read(buf, size, nread);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsTcpConnection::set_send_timeout(srs_utime_t tm)
|
||||||
|
{
|
||||||
|
skt->set_send_timeout(tm);
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_utime_t SrsTcpConnection::get_send_timeout()
|
||||||
|
{
|
||||||
|
return skt->get_send_timeout();
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsTcpConnection::write(void* buf, size_t size, ssize_t* nwrite)
|
||||||
|
{
|
||||||
|
return skt->write(buf, size, nwrite);
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsTcpConnection::writev(const iovec *iov, int iov_size, ssize_t* nwrite)
|
||||||
|
{
|
||||||
|
return skt->writev(iov, iov_size, nwrite);
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsSslConnection::SrsSslConnection(ISrsProtocolReadWriter* c)
|
||||||
|
{
|
||||||
|
transport = c;
|
||||||
|
ssl_ctx = NULL;
|
||||||
|
ssl = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsSslConnection::~SrsSslConnection()
|
||||||
|
{
|
||||||
|
if (ssl) {
|
||||||
|
// this function will free bio_in and bio_out
|
||||||
|
SSL_free(ssl);
|
||||||
|
ssl = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// It maybe success with message.
|
if (ssl_ctx) {
|
||||||
if (srs_error_code(err) == ERROR_SUCCESS) {
|
SSL_CTX_free(ssl_ctx);
|
||||||
srs_trace("client finished%s.", srs_error_summary(err).c_str());
|
ssl_ctx = NULL;
|
||||||
srs_freep(err);
|
}
|
||||||
return err;
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsSslConnection::handshake(string key_file, string crt_file)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
// For HTTPS, try to connect over security transport.
|
||||||
|
#if (OPENSSL_VERSION_NUMBER < 0x10002000L) // v1.0.2
|
||||||
|
ssl_ctx = SSL_CTX_new(TLS_method());
|
||||||
|
#else
|
||||||
|
ssl_ctx = SSL_CTX_new(TLSv1_2_method());
|
||||||
|
#endif
|
||||||
|
SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
|
||||||
|
srs_assert(SSL_CTX_set_cipher_list(ssl_ctx, "ALL") == 1);
|
||||||
|
|
||||||
|
// TODO: Setup callback, see SSL_set_ex_data and SSL_set_info_callback
|
||||||
|
if ((ssl = SSL_new(ssl_ctx)) == NULL) {
|
||||||
|
return srs_error_new(ERROR_HTTPS_HANDSHAKE, "SSL_new ssl");
|
||||||
}
|
}
|
||||||
|
|
||||||
// client close peer.
|
if ((bio_in = BIO_new(BIO_s_mem())) == NULL) {
|
||||||
// TODO: FIXME: Only reset the error when client closed it.
|
return srs_error_new(ERROR_HTTPS_HANDSHAKE, "BIO_new in");
|
||||||
if (srs_is_client_gracefully_close(err)) {
|
|
||||||
srs_warn("client disconnect peer. ret=%d", srs_error_code(err));
|
|
||||||
} else if (srs_is_server_gracefully_close(err)) {
|
|
||||||
srs_warn("server disconnect. ret=%d", srs_error_code(err));
|
|
||||||
} else {
|
|
||||||
srs_error("serve error %s", srs_error_desc(err).c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_freep(err);
|
if ((bio_out = BIO_new(BIO_s_mem())) == NULL) {
|
||||||
return srs_success;
|
BIO_free(bio_in);
|
||||||
|
return srs_error_new(ERROR_HTTPS_HANDSHAKE, "BIO_new out");
|
||||||
|
}
|
||||||
|
|
||||||
|
SSL_set_bio(ssl, bio_in, bio_out);
|
||||||
|
|
||||||
|
// SSL setup active, as server role.
|
||||||
|
SSL_set_accept_state(ssl);
|
||||||
|
SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);
|
||||||
|
|
||||||
|
uint8_t* data = NULL;
|
||||||
|
int r0, r1, size;
|
||||||
|
|
||||||
|
// Setup the key and cert file for server.
|
||||||
|
if ((r0 = SSL_use_certificate_file(ssl, crt_file.c_str(), SSL_FILETYPE_PEM)) != 1) {
|
||||||
|
return srs_error_new(ERROR_HTTPS_KEY_CRT, "use cert %s", crt_file.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((r0 = SSL_use_RSAPrivateKey_file(ssl, key_file.c_str(), SSL_FILETYPE_PEM)) != 1) {
|
||||||
|
return srs_error_new(ERROR_HTTPS_KEY_CRT, "use key %s", key_file.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((r0 = SSL_check_private_key(ssl)) != 1) {
|
||||||
|
return srs_error_new(ERROR_HTTPS_KEY_CRT, "check key %s with cert %s",
|
||||||
|
key_file.c_str(), crt_file.c_str());
|
||||||
|
}
|
||||||
|
srs_info("ssl: use key %s and cert %s", key_file.c_str(), crt_file.c_str());
|
||||||
|
|
||||||
|
// Receive ClientHello
|
||||||
|
while (true) {
|
||||||
|
char buf[1024]; ssize_t nn = 0;
|
||||||
|
if ((err = transport->read(buf, sizeof(buf), &nn)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "handshake: read");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((r0 = BIO_write(bio_in, buf, nn)) <= 0) {
|
||||||
|
// TODO: 0 or -1 maybe block, use BIO_should_retry to check.
|
||||||
|
return srs_error_new(ERROR_HTTPS_HANDSHAKE, "BIO_write r0=%d, data=%p, size=%d", r0, buf, nn);
|
||||||
|
}
|
||||||
|
|
||||||
|
r0 = SSL_do_handshake(ssl); r1 = SSL_get_error(ssl, r0);
|
||||||
|
if (r0 != -1 || r1 != SSL_ERROR_WANT_READ) {
|
||||||
|
return srs_error_new(ERROR_HTTPS_HANDSHAKE, "handshake r0=%d, r1=%d", r0, r1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((size = BIO_get_mem_data(bio_out, &data)) > 0) {
|
||||||
|
// OK, reset it for the next write.
|
||||||
|
if ((r0 = BIO_reset(bio_in)) != 1) {
|
||||||
|
return srs_error_new(ERROR_HTTPS_HANDSHAKE, "BIO_reset r0=%d", r0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_info("https: ClientHello done");
|
||||||
|
|
||||||
|
// Send ServerHello, Certificate, Server Key Exchange, Server Hello Done
|
||||||
|
size = BIO_get_mem_data(bio_out, &data);
|
||||||
|
if (!data || size <= 0) {
|
||||||
|
return srs_error_new(ERROR_HTTPS_HANDSHAKE, "handshake data=%p, size=%d", data, size);
|
||||||
|
}
|
||||||
|
if ((err = transport->write(data, size, NULL)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "handshake: write data=%p, size=%d", data, size);
|
||||||
|
}
|
||||||
|
if ((r0 = BIO_reset(bio_out)) != 1) {
|
||||||
|
return srs_error_new(ERROR_HTTPS_HANDSHAKE, "BIO_reset r0=%d", r0);
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_info("https: ServerHello done");
|
||||||
|
|
||||||
|
// Receive Client Key Exchange, Change Cipher Spec, Encrypted Handshake Message
|
||||||
|
while (true) {
|
||||||
|
char buf[1024]; ssize_t nn = 0;
|
||||||
|
if ((err = transport->read(buf, sizeof(buf), &nn)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "handshake: read");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((r0 = BIO_write(bio_in, buf, nn)) <= 0) {
|
||||||
|
// TODO: 0 or -1 maybe block, use BIO_should_retry to check.
|
||||||
|
return srs_error_new(ERROR_HTTPS_HANDSHAKE, "BIO_write r0=%d, data=%p, size=%d", r0, buf, nn);
|
||||||
|
}
|
||||||
|
|
||||||
|
r0 = SSL_do_handshake(ssl); r1 = SSL_get_error(ssl, r0);
|
||||||
|
if (r0 == 1 && r1 == SSL_ERROR_NONE) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r0 != -1 || r1 != SSL_ERROR_WANT_READ) {
|
||||||
|
return srs_error_new(ERROR_HTTPS_HANDSHAKE, "handshake r0=%d, r1=%d", r0, r1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((size = BIO_get_mem_data(bio_out, &data)) > 0) {
|
||||||
|
// OK, reset it for the next write.
|
||||||
|
if ((r0 = BIO_reset(bio_in)) != 1) {
|
||||||
|
return srs_error_new(ERROR_HTTPS_HANDSHAKE, "BIO_reset r0=%d", r0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_info("https: Client done");
|
||||||
|
|
||||||
|
// Send New Session Ticket, Change Cipher Spec, Encrypted Handshake Message
|
||||||
|
size = BIO_get_mem_data(bio_out, &data);
|
||||||
|
if (!data || size <= 0) {
|
||||||
|
return srs_error_new(ERROR_HTTPS_HANDSHAKE, "handshake data=%p, size=%d", data, size);
|
||||||
|
}
|
||||||
|
if ((err = transport->write(data, size, NULL)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "handshake: write data=%p, size=%d", data, size);
|
||||||
|
}
|
||||||
|
if ((r0 = BIO_reset(bio_out)) != 1) {
|
||||||
|
return srs_error_new(ERROR_HTTPS_HANDSHAKE, "BIO_reset r0=%d", r0);
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_info("https: Server done");
|
||||||
|
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SrsConnection::srs_id()
|
void SrsSslConnection::set_recv_timeout(srs_utime_t tm)
|
||||||
{
|
{
|
||||||
return trd->cid();
|
transport->set_recv_timeout(tm);
|
||||||
}
|
}
|
||||||
|
|
||||||
string SrsConnection::remote_ip() {
|
srs_utime_t SrsSslConnection::get_recv_timeout()
|
||||||
return ip;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SrsConnection::expire()
|
|
||||||
{
|
{
|
||||||
trd->interrupt();
|
return transport->get_recv_timeout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsSslConnection::read_fully(void* buf, size_t size, ssize_t* nread)
|
||||||
|
{
|
||||||
|
return transport->read_fully(buf, size, nread);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t SrsSslConnection::get_recv_bytes()
|
||||||
|
{
|
||||||
|
return transport->get_recv_bytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t SrsSslConnection::get_send_bytes()
|
||||||
|
{
|
||||||
|
return transport->get_send_bytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsSslConnection::read(void* plaintext, size_t nn_plaintext, ssize_t* nread)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
int r0 = SSL_read(ssl, plaintext, nn_plaintext); int r1 = SSL_get_error(ssl, r0);
|
||||||
|
int r2 = BIO_ctrl_pending(bio_in); int r3 = SSL_is_init_finished(ssl);
|
||||||
|
|
||||||
|
// OK, got data.
|
||||||
|
if (r0 > 0) {
|
||||||
|
srs_assert(r0 <= (int)nn_plaintext);
|
||||||
|
if (nread) {
|
||||||
|
*nread = r0;
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need to read more data to feed SSL.
|
||||||
|
if (r0 == -1 && r1 == SSL_ERROR_WANT_READ) {
|
||||||
|
// TODO: Can we avoid copy?
|
||||||
|
int nn_cipher = nn_plaintext;
|
||||||
|
char* cipher = new char[nn_cipher];
|
||||||
|
SrsAutoFreeA(char, cipher);
|
||||||
|
|
||||||
|
// Read the cipher from SSL.
|
||||||
|
ssize_t nn = 0;
|
||||||
|
if ((err = transport->read(cipher, nn_cipher, &nn)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "https: read");
|
||||||
|
}
|
||||||
|
|
||||||
|
int r0 = BIO_write(bio_in, cipher, nn);
|
||||||
|
if (r0 <= 0) {
|
||||||
|
// TODO: 0 or -1 maybe block, use BIO_should_retry to check.
|
||||||
|
return srs_error_new(ERROR_HTTPS_READ, "BIO_write r0=%d, cipher=%p, size=%d", r0, cipher, nn);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fail for error.
|
||||||
|
if (r0 <= 0) {
|
||||||
|
return srs_error_new(ERROR_HTTPS_READ, "SSL_read r0=%d, r1=%d, r2=%d, r3=%d",
|
||||||
|
r0, r1, r2, r3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsSslConnection::set_send_timeout(srs_utime_t tm)
|
||||||
|
{
|
||||||
|
transport->set_send_timeout(tm);
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_utime_t SrsSslConnection::get_send_timeout()
|
||||||
|
{
|
||||||
|
return transport->get_send_timeout();
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsSslConnection::write(void* plaintext, size_t nn_plaintext, ssize_t* nwrite)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
for (char* p = (char*)plaintext; p < (char*)plaintext + nn_plaintext;) {
|
||||||
|
int left = (int)nn_plaintext - (p - (char*)plaintext);
|
||||||
|
int r0 = SSL_write(ssl, (const void*)p, left);
|
||||||
|
int r1 = SSL_get_error(ssl, r0);
|
||||||
|
if (r0 <= 0) {
|
||||||
|
return srs_error_new(ERROR_HTTPS_WRITE, "https: write data=%p, size=%d, r0=%d, r1=%d", p, left, r0, r1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move p to the next writing position.
|
||||||
|
p += r0;
|
||||||
|
if (nwrite) {
|
||||||
|
*nwrite += (ssize_t)r0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t* data = NULL;
|
||||||
|
int size = BIO_get_mem_data(bio_out, &data);
|
||||||
|
if ((err = transport->write(data, size, NULL)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "https: write data=%p, size=%d", data, size);
|
||||||
|
}
|
||||||
|
if ((r0 = BIO_reset(bio_out)) != 1) {
|
||||||
|
return srs_error_new(ERROR_HTTPS_WRITE, "BIO_reset r0=%d", r0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsSslConnection::writev(const iovec *iov, int iov_size, ssize_t* nwrite)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
for (int i = 0; i < iov_size; i++) {
|
||||||
|
const iovec* p = iov + i;
|
||||||
|
if ((err = write((void*)p->iov_base, (size_t)p->iov_len, nwrite)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "write iov #%d base=%p, size=%d", i, p->iov_base, p->iov_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,79 +27,171 @@
|
||||||
#include <srs_core.hpp>
|
#include <srs_core.hpp>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include <openssl/ssl.h>
|
||||||
|
|
||||||
#include <srs_app_st.hpp>
|
#include <srs_app_st.hpp>
|
||||||
#include <srs_app_thread.hpp>
|
|
||||||
#include <srs_protocol_kbps.hpp>
|
#include <srs_protocol_kbps.hpp>
|
||||||
#include <srs_app_reload.hpp>
|
#include <srs_app_reload.hpp>
|
||||||
#include <srs_service_conn.hpp>
|
#include <srs_service_conn.hpp>
|
||||||
|
|
||||||
class SrsWallClock;
|
class SrsWallClock;
|
||||||
|
|
||||||
// The basic connection of SRS,
|
// Hooks for connection manager, to handle the event when disposing connections.
|
||||||
|
class ISrsDisposingHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ISrsDisposingHandler();
|
||||||
|
virtual ~ISrsDisposingHandler();
|
||||||
|
public:
|
||||||
|
// When before disposing resource, trigger when manager.remove(c), sync API.
|
||||||
|
virtual void on_before_dispose(ISrsResource* c) = 0;
|
||||||
|
// When disposing resource, async API, c is freed after it.
|
||||||
|
virtual void on_disposing(ISrsResource* c) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The resource manager remove resource and delete it asynchronously.
|
||||||
|
class SrsResourceManager : virtual public ISrsCoroutineHandler, virtual public ISrsResourceManager
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::string label_;
|
||||||
|
SrsContextId cid_;
|
||||||
|
bool verbose_;
|
||||||
|
private:
|
||||||
|
SrsCoroutine* trd;
|
||||||
|
srs_cond_t cond;
|
||||||
|
// Callback handlers.
|
||||||
|
std::vector<ISrsDisposingHandler*> handlers_;
|
||||||
|
// Unsubscribing handlers, skip it for notifying.
|
||||||
|
std::vector<ISrsDisposingHandler*> unsubs_;
|
||||||
|
// Whether we are removing resources.
|
||||||
|
bool removing_;
|
||||||
|
// The zombie connections, we will delete it asynchronously.
|
||||||
|
std::vector<ISrsResource*> zombies_;
|
||||||
|
std::vector<ISrsResource*>* p_disposing_;
|
||||||
|
private:
|
||||||
|
// The connections without any id.
|
||||||
|
std::vector<ISrsResource*> conns_;
|
||||||
|
// The connections with resource id.
|
||||||
|
std::map<std::string, ISrsResource*> conns_id_;
|
||||||
|
// The connections with resource name.
|
||||||
|
std::map<std::string, ISrsResource*> conns_name_;
|
||||||
|
public:
|
||||||
|
SrsResourceManager(const std::string& label, bool verbose = false);
|
||||||
|
virtual ~SrsResourceManager();
|
||||||
|
public:
|
||||||
|
srs_error_t start();
|
||||||
|
bool empty();
|
||||||
|
size_t size();
|
||||||
|
// Interface ISrsCoroutineHandler
|
||||||
|
public:
|
||||||
|
virtual srs_error_t cycle();
|
||||||
|
public:
|
||||||
|
void add(ISrsResource* conn);
|
||||||
|
void add_with_id(const std::string& id, ISrsResource* conn);
|
||||||
|
void add_with_name(const std::string& name, ISrsResource* conn);
|
||||||
|
ISrsResource* at(int index);
|
||||||
|
ISrsResource* find_by_id(std::string id);
|
||||||
|
ISrsResource* find_by_name(std::string name);
|
||||||
|
public:
|
||||||
|
void subscribe(ISrsDisposingHandler* h);
|
||||||
|
void unsubscribe(ISrsDisposingHandler* h);
|
||||||
|
// Interface ISrsResourceManager
|
||||||
|
public:
|
||||||
|
virtual void remove(ISrsResource* c);
|
||||||
|
private:
|
||||||
|
void do_remove(ISrsResource* c);
|
||||||
|
void check_remove(ISrsResource* c, bool& in_zombie, bool& in_disposing);
|
||||||
|
void clear();
|
||||||
|
void do_clear();
|
||||||
|
void dispose(ISrsResource* c);
|
||||||
|
};
|
||||||
|
|
||||||
|
// If a connection is able to be expired,
|
||||||
|
// user can use HTTP-API to kick-off it.
|
||||||
|
class ISrsExpire
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ISrsExpire();
|
||||||
|
virtual ~ISrsExpire();
|
||||||
|
public:
|
||||||
|
// Set connection to expired to kick-off it.
|
||||||
|
virtual void expire() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Interface for connection that is startable.
|
||||||
|
class ISrsStartableConneciton : virtual public ISrsConnection
|
||||||
|
, virtual public ISrsStartable, virtual public ISrsKbpsDelta
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ISrsStartableConneciton();
|
||||||
|
virtual ~ISrsStartableConneciton();
|
||||||
|
};
|
||||||
|
|
||||||
|
// The basic connection of SRS, for TCP based protocols,
|
||||||
// all connections accept from listener must extends from this base class,
|
// all connections accept from listener must extends from this base class,
|
||||||
// server will add the connection to manager, and delete it when remove.
|
// server will add the connection to manager, and delete it when remove.
|
||||||
class SrsConnection : virtual public ISrsConnection, virtual public ISrsCoroutineHandler
|
class SrsTcpConnection : virtual public ISrsProtocolReadWriter
|
||||||
, virtual public ISrsKbpsDelta, virtual public ISrsReloadHandler
|
|
||||||
{
|
{
|
||||||
protected:
|
private:
|
||||||
// Each connection start a green thread,
|
|
||||||
// when thread stop, the connection will be delete by server.
|
|
||||||
SrsCoroutine* trd;
|
|
||||||
// The manager object to manage the connection.
|
|
||||||
IConnectionManager* manager;
|
|
||||||
// The underlayer st fd handler.
|
// The underlayer st fd handler.
|
||||||
srs_netfd_t stfd;
|
srs_netfd_t stfd;
|
||||||
// The ip of client.
|
|
||||||
std::string ip;
|
|
||||||
// The underlayer socket.
|
// The underlayer socket.
|
||||||
SrsStSocket* skt;
|
SrsStSocket* skt;
|
||||||
// The connection total kbps.
|
|
||||||
// not only the rtmp or http connection, all type of connection are
|
|
||||||
// need to statistic the kbps of io.
|
|
||||||
// The SrsStatistic will use it indirectly to statistic the bytes delta of current connection.
|
|
||||||
SrsKbps* kbps;
|
|
||||||
SrsWallClock* clk;
|
|
||||||
// The create time in milliseconds.
|
|
||||||
// for current connection to log self create time and calculate the living time.
|
|
||||||
int64_t create_time;
|
|
||||||
public:
|
public:
|
||||||
SrsConnection(IConnectionManager* cm, srs_netfd_t c, std::string cip);
|
SrsTcpConnection(srs_netfd_t c);
|
||||||
virtual ~SrsConnection();
|
virtual ~SrsTcpConnection();
|
||||||
// Interface ISrsKbpsDelta
|
|
||||||
public:
|
public:
|
||||||
virtual void remark(int64_t* in, int64_t* out);
|
virtual srs_error_t initialize();
|
||||||
public:
|
public:
|
||||||
// To dipose the connection.
|
|
||||||
virtual void dispose();
|
|
||||||
// Start the client green thread.
|
|
||||||
// when server get a client from listener,
|
|
||||||
// 1. server will create an concrete connection(for instance, RTMP connection),
|
|
||||||
// 2. then add connection to its connection manager,
|
|
||||||
// 3. start the client thread by invoke this start()
|
|
||||||
// when client cycle thread stop, invoke the on_thread_stop(), which will use server
|
|
||||||
// To remove the client by server->remove(this).
|
|
||||||
virtual srs_error_t start();
|
|
||||||
// Set socket option TCP_NODELAY.
|
// Set socket option TCP_NODELAY.
|
||||||
virtual srs_error_t set_tcp_nodelay(bool v);
|
virtual srs_error_t set_tcp_nodelay(bool v);
|
||||||
// Set socket option SO_SNDBUF in srs_utime_t.
|
// Set socket option SO_SNDBUF in srs_utime_t.
|
||||||
virtual srs_error_t set_socket_buffer(srs_utime_t buffer_v);
|
virtual srs_error_t set_socket_buffer(srs_utime_t buffer_v);
|
||||||
// Interface ISrsOneCycleThreadHandler
|
// Interface ISrsProtocolReadWriter
|
||||||
public:
|
public:
|
||||||
// The thread cycle function,
|
virtual void set_recv_timeout(srs_utime_t tm);
|
||||||
// when serve connection completed, terminate the loop which will terminate the thread,
|
virtual srs_utime_t get_recv_timeout();
|
||||||
// thread will invoke the on_thread_stop() when it terminated.
|
virtual srs_error_t read_fully(void* buf, size_t size, ssize_t* nread);
|
||||||
virtual srs_error_t cycle();
|
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);
|
||||||
|
};
|
||||||
|
|
||||||
|
// The SSL connection over TCP transport, in server mode.
|
||||||
|
class SrsSslConnection : virtual public ISrsProtocolReadWriter
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// The under-layer plaintext transport.
|
||||||
|
ISrsProtocolReadWriter* transport;
|
||||||
|
private:
|
||||||
|
SSL_CTX* ssl_ctx;
|
||||||
|
SSL* ssl;
|
||||||
|
BIO* bio_in;
|
||||||
|
BIO* bio_out;
|
||||||
public:
|
public:
|
||||||
// Get the srs id which identify the client.
|
SrsSslConnection(ISrsProtocolReadWriter* c);
|
||||||
virtual int srs_id();
|
virtual ~SrsSslConnection();
|
||||||
// Get the remote ip of peer.
|
public:
|
||||||
virtual std::string remote_ip();
|
virtual srs_error_t handshake(std::string key_file, std::string crt_file);
|
||||||
// Set connection to expired.
|
// Interface ISrsProtocolReadWriter
|
||||||
virtual void expire();
|
public:
|
||||||
protected:
|
virtual void set_recv_timeout(srs_utime_t tm);
|
||||||
// For concrete connection to do the cycle.
|
virtual srs_utime_t get_recv_timeout();
|
||||||
virtual srs_error_t do_cycle() = 0;
|
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);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -307,6 +307,7 @@ SrsDashController::SrsDashController()
|
||||||
vfragments = new SrsFragmentWindow();
|
vfragments = new SrsFragmentWindow();
|
||||||
afragments = new SrsFragmentWindow();
|
afragments = new SrsFragmentWindow();
|
||||||
audio_dts = video_dts = 0;
|
audio_dts = video_dts = 0;
|
||||||
|
fragment = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsDashController::~SrsDashController()
|
SrsDashController::~SrsDashController()
|
||||||
|
|
|
@ -1,258 +0,0 @@
|
||||||
/**
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2013-2020 Winlin
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
* this software and associated documentation files (the "Software"), to deal in
|
|
||||||
* the Software without restriction, including without limitation the rights to
|
|
||||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
||||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
* subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <srs_app_dtls.hpp>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <srs_kernel_log.hpp>
|
|
||||||
#include <srs_kernel_error.hpp>
|
|
||||||
#include <srs_app_config.hpp>
|
|
||||||
|
|
||||||
#include <srtp2/srtp.h>
|
|
||||||
#include <openssl/ssl.h>
|
|
||||||
|
|
||||||
SrsDtls* SrsDtls::_instance = NULL;
|
|
||||||
|
|
||||||
SrsDtls::SrsDtls()
|
|
||||||
{
|
|
||||||
dtls_ctx = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
SrsDtls::~SrsDtls()
|
|
||||||
{
|
|
||||||
SSL_CTX_free(dtls_ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
SrsDtls* SrsDtls::instance()
|
|
||||||
{
|
|
||||||
if (!_instance) {
|
|
||||||
_instance = new SrsDtls();
|
|
||||||
}
|
|
||||||
return _instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The return value of verify_callback controls the strategy of the further verification process. If verify_callback
|
|
||||||
// returns 0, the verification process is immediately stopped with "verification failed" state. If SSL_VERIFY_PEER is
|
|
||||||
// set, a verification failure alert is sent to the peer and the TLS/SSL handshake is terminated. If verify_callback
|
|
||||||
// returns 1, the verification process is continued. If verify_callback always returns 1, the TLS/SSL handshake will
|
|
||||||
// not be terminated with respect to verification failures and the connection will be established. The calling process
|
|
||||||
// can however retrieve the error code of the last verification error using SSL_get_verify_result(3) or by maintaining
|
|
||||||
// its own error storage managed by verify_callback.
|
|
||||||
// @see https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_verify.html
|
|
||||||
static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
|
|
||||||
{
|
|
||||||
// Always OK, we don't check the certificate of client,
|
|
||||||
// because we allow client self-sign certificate.
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_error_t SrsDtls::init(const SrsRequest& req)
|
|
||||||
{
|
|
||||||
srs_error_t err = srs_success;
|
|
||||||
|
|
||||||
// Initialize once.
|
|
||||||
if (dtls_ctx) {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L // v1.1.x
|
|
||||||
// Initialize SSL library by registering algorithms
|
|
||||||
// The SSL_library_init() and OpenSSL_add_ssl_algorithms() functions were deprecated in OpenSSL 1.1.0 by OPENSSL_init_ssl().
|
|
||||||
// @see https://www.openssl.org/docs/man1.1.0/man3/OpenSSL_add_ssl_algorithms.html
|
|
||||||
// @see https://web.archive.org/web/20150806185102/http://sctp.fh-muenster.de:80/dtls/dtls_udp_echo.c
|
|
||||||
OpenSSL_add_ssl_algorithms();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if OPENSSL_VERSION_NUMBER < 0x10002000L // v1.0.2
|
|
||||||
dtls_ctx = SSL_CTX_new(DTLSv1_method());
|
|
||||||
#else
|
|
||||||
//dtls_ctx = SSL_CTX_new(DTLS_method());
|
|
||||||
dtls_ctx = SSL_CTX_new(DTLSv1_method());
|
|
||||||
//dtls_ctx = SSL_CTX_new(DTLSv1_2_method());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Initialize SRTP first.
|
|
||||||
srs_assert(srtp_init() == 0);
|
|
||||||
|
|
||||||
// Whether use ECDSA certificate.
|
|
||||||
bool is_ecdsa = _srs_config->get_rtc_server_ecdsa();
|
|
||||||
|
|
||||||
// Create keys by RSA or ECDSA.
|
|
||||||
EVP_PKEY* dtls_pkey = EVP_PKEY_new();
|
|
||||||
srs_assert(dtls_pkey);
|
|
||||||
if (!is_ecdsa) { // By RSA
|
|
||||||
RSA* rsa = RSA_new();
|
|
||||||
srs_assert(rsa);
|
|
||||||
|
|
||||||
// Initialize the big-number for private key.
|
|
||||||
BIGNUM* exponent = BN_new();
|
|
||||||
srs_assert(exponent);
|
|
||||||
BN_set_word(exponent, RSA_F4);
|
|
||||||
|
|
||||||
// Generates a key pair and stores it in the RSA structure provided in rsa.
|
|
||||||
// @see https://www.openssl.org/docs/man1.0.2/man3/RSA_generate_key_ex.html
|
|
||||||
int key_bits = 1024;
|
|
||||||
RSA_generate_key_ex(rsa, key_bits, exponent, NULL);
|
|
||||||
|
|
||||||
// @see https://www.openssl.org/docs/man1.1.0/man3/EVP_PKEY_type.html
|
|
||||||
srs_assert(EVP_PKEY_set1_RSA(dtls_pkey, rsa) == 1);
|
|
||||||
|
|
||||||
RSA_free(rsa);
|
|
||||||
BN_free(exponent);
|
|
||||||
}
|
|
||||||
if (is_ecdsa) { // By ECDSA, https://stackoverflow.com/a/6006898
|
|
||||||
EC_KEY* eckey = EC_KEY_new();
|
|
||||||
srs_assert(eckey);
|
|
||||||
|
|
||||||
#if OPENSSL_VERSION_NUMBER >= 0x10002000L // v1.0.2
|
|
||||||
// For ECDSA, we could set the curves list.
|
|
||||||
// @see https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set1_curves_list.html
|
|
||||||
SSL_CTX_set1_curves_list(dtls_ctx, "P-521:P-384:P-256");
|
|
||||||
#endif
|
|
||||||
// Should use the curves in ClientHello.supported_groups
|
|
||||||
// For example:
|
|
||||||
// Supported Group: x25519 (0x001d)
|
|
||||||
// Supported Group: secp256r1 (0x0017)
|
|
||||||
// Supported Group: secp384r1 (0x0018)
|
|
||||||
// @remark The curve NID_secp256k1 is not secp256r1, k1 != r1.
|
|
||||||
// TODO: FIXME: Parse ClientHello and choose the curve.
|
|
||||||
// Note that secp256r1 in openssl is called NID_X9_62_prime256v1, not NID_secp256k1
|
|
||||||
// @see https://stackoverflow.com/questions/41950056/openssl1-1-0-b-is-not-support-secp256r1openssl-ecparam-list-curves
|
|
||||||
EC_GROUP* ecgroup = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
|
|
||||||
//EC_GROUP* ecgroup = EC_GROUP_new_by_curve_name(NID_secp384r1);
|
|
||||||
srs_assert(ecgroup);
|
|
||||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L // v1.1.x
|
|
||||||
// For openssl 1.0, we must set the group parameters, so that cert is ok.
|
|
||||||
// @see https://github.com/monero-project/monero/blob/master/contrib/epee/src/net_ssl.cpp#L225
|
|
||||||
EC_GROUP_set_asn1_flag(ecgroup, OPENSSL_EC_NAMED_CURVE);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
srs_assert(EC_KEY_set_group(eckey, ecgroup) == 1);
|
|
||||||
srs_assert(EC_KEY_generate_key(eckey) == 1);
|
|
||||||
|
|
||||||
// For openssl <1.1, we must set the ECDH manually.
|
|
||||||
// @see https://stackoverrun.com/cn/q/10791887
|
|
||||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L // v1.1.x
|
|
||||||
#if OPENSSL_VERSION_NUMBER < 0x10002000L // v1.0.2
|
|
||||||
SSL_CTX_set_tmp_ecdh(dtls_ctx, eckey);
|
|
||||||
#else
|
|
||||||
SSL_CTX_set_ecdh_auto(dtls_ctx, 1);
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
// @see https://www.openssl.org/docs/man1.1.0/man3/EVP_PKEY_type.html
|
|
||||||
srs_assert(EVP_PKEY_set1_EC_KEY(dtls_pkey, eckey) == 1);
|
|
||||||
|
|
||||||
EC_GROUP_free(ecgroup);
|
|
||||||
EC_KEY_free(eckey);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create certificate, from previous generated pkey.
|
|
||||||
// TODO: Support ECDSA certificate.
|
|
||||||
X509* dtls_cert = X509_new();
|
|
||||||
srs_assert(dtls_cert);
|
|
||||||
if (true) {
|
|
||||||
X509_NAME* subject = X509_NAME_new();
|
|
||||||
srs_assert(subject);
|
|
||||||
|
|
||||||
int serial = rand();
|
|
||||||
ASN1_INTEGER_set(X509_get_serialNumber(dtls_cert), serial);
|
|
||||||
|
|
||||||
const std::string& aor = "ossrs.net";
|
|
||||||
X509_NAME_add_entry_by_txt(subject, "CN", MBSTRING_ASC, (unsigned char *) aor.data(), aor.size(), -1, 0);
|
|
||||||
|
|
||||||
X509_set_issuer_name(dtls_cert, subject);
|
|
||||||
X509_set_subject_name(dtls_cert, subject);
|
|
||||||
|
|
||||||
int expire_day = 365;
|
|
||||||
const long cert_duration = 60*60*24*expire_day;
|
|
||||||
|
|
||||||
X509_gmtime_adj(X509_get_notBefore(dtls_cert), 0);
|
|
||||||
X509_gmtime_adj(X509_get_notAfter(dtls_cert), cert_duration);
|
|
||||||
|
|
||||||
X509_set_version(dtls_cert, 2);
|
|
||||||
srs_assert(X509_set_pubkey(dtls_cert, dtls_pkey) == 1);
|
|
||||||
srs_assert(X509_sign(dtls_cert, dtls_pkey, EVP_sha1()) != 0);
|
|
||||||
|
|
||||||
X509_NAME_free(subject);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup DTLS context.
|
|
||||||
if (true) {
|
|
||||||
// We use "ALL", while you can use "DEFAULT" means "ALL:!EXPORT:!LOW:!aNULL:!eNULL:!SSLv2"
|
|
||||||
// @see https://www.openssl.org/docs/man1.0.2/man1/ciphers.html
|
|
||||||
srs_assert(SSL_CTX_set_cipher_list(dtls_ctx, "ALL") == 1);
|
|
||||||
|
|
||||||
// Setup the certificate.
|
|
||||||
srs_assert(SSL_CTX_use_certificate(dtls_ctx, dtls_cert) == 1);
|
|
||||||
srs_assert(SSL_CTX_use_PrivateKey(dtls_ctx, dtls_pkey) == 1);
|
|
||||||
|
|
||||||
// Server will send Certificate Request.
|
|
||||||
// @see https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_verify.html
|
|
||||||
// TODO: FIXME: Config it, default to off to make the packet smaller.
|
|
||||||
SSL_CTX_set_verify(dtls_ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, verify_callback);
|
|
||||||
// The depth count is "level 0:peer certificate", "level 1: CA certificate",
|
|
||||||
// "level 2: higher level CA certificate", and so on.
|
|
||||||
// @see https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_verify.html
|
|
||||||
SSL_CTX_set_verify_depth(dtls_ctx, 4);
|
|
||||||
|
|
||||||
// Whether we should read as many input bytes as possible (for non-blocking reads) or not.
|
|
||||||
// @see https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_read_ahead.html
|
|
||||||
SSL_CTX_set_read_ahead(dtls_ctx, 1);
|
|
||||||
|
|
||||||
// TODO: Maybe we can use SRTP-GCM in future.
|
|
||||||
// @see https://bugs.chromium.org/p/chromium/issues/detail?id=713701
|
|
||||||
// @see https://groups.google.com/forum/#!topic/discuss-webrtc/PvCbWSetVAQ
|
|
||||||
// @remark Only support SRTP_AES128_CM_SHA1_80, please read ssl/d1_srtp.c
|
|
||||||
srs_assert(SSL_CTX_set_tlsext_use_srtp(dtls_ctx, "SRTP_AES128_CM_SHA1_80") == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show DTLS fingerprint
|
|
||||||
if (true) {
|
|
||||||
char fp[100] = {0};
|
|
||||||
char *p = fp;
|
|
||||||
unsigned char md[EVP_MAX_MD_SIZE];
|
|
||||||
unsigned int n = 0;
|
|
||||||
|
|
||||||
// TODO: FIXME: Unused variable.
|
|
||||||
/*int r = */X509_digest(dtls_cert, EVP_sha256(), md, &n);
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < n; i++, ++p) {
|
|
||||||
sprintf(p, "%02X", md[i]);
|
|
||||||
p += 2;
|
|
||||||
|
|
||||||
if(i < (n-1)) {
|
|
||||||
*p = ':';
|
|
||||||
} else {
|
|
||||||
*p = '\0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fingerprint.assign(fp, strlen(fp));
|
|
||||||
srs_trace("fingerprint=%s", fingerprint.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
/**
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2013-2020 Winlin
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
* this software and associated documentation files (the "Software"), to deal in
|
|
||||||
* the Software without restriction, including without limitation the rights to
|
|
||||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
||||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
* subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SRS_APP_DTLS_HPP
|
|
||||||
#define SRS_APP_DTLS_HPP
|
|
||||||
|
|
||||||
#include <srs_core.hpp>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
class SrsRequest;
|
|
||||||
|
|
||||||
#include <openssl/ssl.h>
|
|
||||||
|
|
||||||
class SrsDtls
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
static SrsDtls* _instance;
|
|
||||||
private:
|
|
||||||
std::string fingerprint;
|
|
||||||
SSL_CTX* dtls_ctx;
|
|
||||||
private:
|
|
||||||
SrsDtls();
|
|
||||||
virtual ~SrsDtls();
|
|
||||||
public:
|
|
||||||
srs_error_t init(const SrsRequest& req);
|
|
||||||
public:
|
|
||||||
static SrsDtls* instance();
|
|
||||||
SSL_CTX* get_dtls_ctx() { return dtls_ctx; }
|
|
||||||
public:
|
|
||||||
std::string get_fingerprint() const { return fingerprint; }
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -48,6 +48,7 @@ SrsDvrSegmenter::SrsDvrSegmenter()
|
||||||
req = NULL;
|
req = NULL;
|
||||||
jitter = NULL;
|
jitter = NULL;
|
||||||
plan = NULL;
|
plan = NULL;
|
||||||
|
wait_keyframe = true;
|
||||||
|
|
||||||
fragment = new SrsFragment();
|
fragment = new SrsFragment();
|
||||||
fs = new SrsFileWriter();
|
fs = new SrsFileWriter();
|
||||||
|
@ -533,7 +534,7 @@ srs_error_t SrsDvrMp4Segmenter::close_encoder()
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsDvrAsyncCallOnDvr::SrsDvrAsyncCallOnDvr(int c, SrsRequest* r, string p)
|
SrsDvrAsyncCallOnDvr::SrsDvrAsyncCallOnDvr(SrsContextId c, SrsRequest* r, string p)
|
||||||
{
|
{
|
||||||
cid = c;
|
cid = c;
|
||||||
req = r->copy();
|
req = r->copy();
|
||||||
|
@ -585,6 +586,7 @@ string SrsDvrAsyncCallOnDvr::to_string()
|
||||||
SrsDvrPlan::SrsDvrPlan()
|
SrsDvrPlan::SrsDvrPlan()
|
||||||
{
|
{
|
||||||
req = NULL;
|
req = NULL;
|
||||||
|
hub = NULL;
|
||||||
|
|
||||||
dvr_enabled = false;
|
dvr_enabled = false;
|
||||||
segment = NULL;
|
segment = NULL;
|
||||||
|
@ -673,7 +675,7 @@ srs_error_t SrsDvrPlan::on_reap_segment()
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
int cid = _srs_context->get_id();
|
SrsContextId cid = _srs_context->get_id();
|
||||||
|
|
||||||
SrsFragment* fragment = segment->current();
|
SrsFragment* fragment = segment->current();
|
||||||
string fullpath = fragment->fullpath();
|
string fullpath = fragment->fullpath();
|
||||||
|
|
|
@ -159,11 +159,11 @@ protected:
|
||||||
class SrsDvrAsyncCallOnDvr : public ISrsAsyncCallTask
|
class SrsDvrAsyncCallOnDvr : public ISrsAsyncCallTask
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
int cid;
|
SrsContextId cid;
|
||||||
std::string path;
|
std::string path;
|
||||||
SrsRequest* req;
|
SrsRequest* req;
|
||||||
public:
|
public:
|
||||||
SrsDvrAsyncCallOnDvr(int c, SrsRequest* r, std::string p);
|
SrsDvrAsyncCallOnDvr(SrsContextId c, SrsRequest* r, std::string p);
|
||||||
virtual ~SrsDvrAsyncCallOnDvr();
|
virtual ~SrsDvrAsyncCallOnDvr();
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t call();
|
virtual srs_error_t call();
|
||||||
|
|
|
@ -65,6 +65,7 @@ SrsEdgeRtmpUpstream::SrsEdgeRtmpUpstream(string r)
|
||||||
{
|
{
|
||||||
redirect = r;
|
redirect = r;
|
||||||
sdk = NULL;
|
sdk = NULL;
|
||||||
|
selected_port = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsEdgeRtmpUpstream::~SrsEdgeRtmpUpstream()
|
SrsEdgeRtmpUpstream::~SrsEdgeRtmpUpstream()
|
||||||
|
@ -125,10 +126,15 @@ srs_error_t SrsEdgeRtmpUpstream::connect(SrsRequest* r, SrsLbRoundRobin* lb)
|
||||||
return srs_error_wrap(err, "edge pull %s failed, cto=%dms, sto=%dms.", url.c_str(), srsu2msi(cto), srsu2msi(sto));
|
return srs_error_wrap(err, "edge pull %s failed, cto=%dms, sto=%dms.", url.c_str(), srsu2msi(cto), srsu2msi(sto));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = sdk->play(_srs_config->get_chunk_size(req->vhost))) != srs_success) {
|
// For RTMP client, we pass the vhost in tcUrl when connecting,
|
||||||
|
// so we publish without vhost in stream.
|
||||||
|
string stream;
|
||||||
|
if ((err = sdk->play(_srs_config->get_chunk_size(req->vhost), false, &stream)) != srs_success) {
|
||||||
return srs_error_wrap(err, "edge pull %s stream failed", url.c_str());
|
return srs_error_wrap(err, "edge pull %s stream failed", url.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
srs_trace("edge-pull publish url %s, stream=%s%s as %s", url.c_str(), req->stream.c_str(), req->param.c_str(), stream.c_str());
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -440,6 +446,7 @@ SrsEdgeForwarder::SrsEdgeForwarder()
|
||||||
edge = NULL;
|
edge = NULL;
|
||||||
req = NULL;
|
req = NULL;
|
||||||
send_error_code = ERROR_SUCCESS;
|
send_error_code = ERROR_SUCCESS;
|
||||||
|
source = NULL;
|
||||||
|
|
||||||
sdk = NULL;
|
sdk = NULL;
|
||||||
lb = new SrsLbRoundRobin();
|
lb = new SrsLbRoundRobin();
|
||||||
|
@ -505,7 +512,10 @@ srs_error_t SrsEdgeForwarder::start()
|
||||||
return srs_error_wrap(err, "sdk connect %s failed, cto=%dms, sto=%dms.", url.c_str(), srsu2msi(cto), srsu2msi(sto));
|
return srs_error_wrap(err, "sdk connect %s failed, cto=%dms, sto=%dms.", url.c_str(), srsu2msi(cto), srsu2msi(sto));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = sdk->publish(_srs_config->get_chunk_size(req->vhost))) != srs_success) {
|
// For RTMP client, we pass the vhost in tcUrl when connecting,
|
||||||
|
// so we publish without vhost in stream.
|
||||||
|
string stream;
|
||||||
|
if ((err = sdk->publish(_srs_config->get_chunk_size(req->vhost), false, &stream)) != srs_success) {
|
||||||
return srs_error_wrap(err, "sdk publish");
|
return srs_error_wrap(err, "sdk publish");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -515,7 +525,8 @@ srs_error_t SrsEdgeForwarder::start()
|
||||||
if ((err = trd->start()) != srs_success) {
|
if ((err = trd->start()) != srs_success) {
|
||||||
return srs_error_wrap(err, "coroutine");
|
return srs_error_wrap(err, "coroutine");
|
||||||
}
|
}
|
||||||
srs_trace("edge-fwr publish url %s", url.c_str());
|
|
||||||
|
srs_trace("edge-fwr publish url %s, stream=%s%s as %s", url.c_str(), req->stream.c_str(), req->param.c_str(), stream.c_str());
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -689,11 +700,13 @@ void SrsPlayEdge::on_all_client_stop()
|
||||||
// when all client disconnected,
|
// when all client disconnected,
|
||||||
// and edge is ingesting origin stream, abort it.
|
// and edge is ingesting origin stream, abort it.
|
||||||
if (state == SrsEdgeStatePlay || state == SrsEdgeStateIngestConnected) {
|
if (state == SrsEdgeStatePlay || state == SrsEdgeStateIngestConnected) {
|
||||||
|
SrsEdgeState pstate = state;
|
||||||
|
state = SrsEdgeStateIngestStopping;
|
||||||
|
|
||||||
ingester->stop();
|
ingester->stop();
|
||||||
|
|
||||||
SrsEdgeState pstate = state;
|
|
||||||
state = SrsEdgeStateInit;
|
state = SrsEdgeStateInit;
|
||||||
srs_trace("edge change from %d to state %d (init).", pstate, state);
|
srs_trace("edge change from %d to %d then %d (init).", pstate, SrsEdgeStateIngestStopping, state);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,6 @@
|
||||||
#include <srs_core.hpp>
|
#include <srs_core.hpp>
|
||||||
|
|
||||||
#include <srs_app_st.hpp>
|
#include <srs_app_st.hpp>
|
||||||
#include <srs_app_thread.hpp>
|
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
@ -59,6 +58,9 @@ enum SrsEdgeState
|
||||||
|
|
||||||
// For publish edge
|
// For publish edge
|
||||||
SrsEdgeStatePublish = 200,
|
SrsEdgeStatePublish = 200,
|
||||||
|
|
||||||
|
// We are stopping edge ingesting.
|
||||||
|
SrsEdgeStateIngestStopping = 300,
|
||||||
};
|
};
|
||||||
|
|
||||||
// The state of edge from user, manual machine
|
// The state of edge from user, manual machine
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <srs_app_thread.hpp>
|
#include <srs_app_st.hpp>
|
||||||
|
|
||||||
class SrsConfDirective;
|
class SrsConfDirective;
|
||||||
class SrsRequest;
|
class SrsRequest;
|
||||||
|
|
|
@ -45,7 +45,7 @@ using namespace std;
|
||||||
#include <srs_core_autofree.hpp>
|
#include <srs_core_autofree.hpp>
|
||||||
#include <srs_kernel_utility.hpp>
|
#include <srs_kernel_utility.hpp>
|
||||||
|
|
||||||
#ifdef SRS_AUTO_FFMPEG_STUB
|
#ifdef SRS_FFMPEG_STUB
|
||||||
|
|
||||||
#define SRS_RTMP_ENCODER_COPY "copy"
|
#define SRS_RTMP_ENCODER_COPY "copy"
|
||||||
#define SRS_RTMP_ENCODER_NO_VIDEO "vn"
|
#define SRS_RTMP_ENCODER_NO_VIDEO "vn"
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
|
|
||||||
#include <srs_core.hpp>
|
#include <srs_core.hpp>
|
||||||
|
|
||||||
#ifdef SRS_AUTO_FFMPEG_STUB
|
#ifdef SRS_FFMPEG_STUB
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
|
@ -223,7 +223,10 @@ srs_error_t SrsForwarder::do_cycle()
|
||||||
return srs_error_wrap(err, "sdk connect url=%s, cto=%dms, sto=%dms.", url.c_str(), srsu2msi(cto), srsu2msi(sto));
|
return srs_error_wrap(err, "sdk connect url=%s, cto=%dms, sto=%dms.", url.c_str(), srsu2msi(cto), srsu2msi(sto));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = sdk->publish(_srs_config->get_chunk_size(req->vhost))) != srs_success) {
|
// For RTMP client, we pass the vhost in tcUrl when connecting,
|
||||||
|
// so we publish without vhost in stream.
|
||||||
|
string stream;
|
||||||
|
if ((err = sdk->publish(_srs_config->get_chunk_size(req->vhost), false, &stream)) != srs_success) {
|
||||||
return srs_error_wrap(err, "sdk publish");
|
return srs_error_wrap(err, "sdk publish");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,6 +238,8 @@ srs_error_t SrsForwarder::do_cycle()
|
||||||
return srs_error_wrap(err, "forward");
|
return srs_error_wrap(err, "forward");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
srs_trace("forward publish url %s, stream=%s%s as %s", url.c_str(), req->stream.c_str(), req->param.c_str(), stream.c_str());
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,6 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <srs_app_st.hpp>
|
#include <srs_app_st.hpp>
|
||||||
#include <srs_app_thread.hpp>
|
|
||||||
|
|
||||||
class ISrsProtocolReadWriter;
|
class ISrsProtocolReadWriter;
|
||||||
class SrsSharedPtrMessage;
|
class SrsSharedPtrMessage;
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -33,7 +33,6 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include <srs_app_st.hpp>
|
#include <srs_app_st.hpp>
|
||||||
#include <srs_app_thread.hpp>
|
|
||||||
#include <srs_app_listener.hpp>
|
#include <srs_app_listener.hpp>
|
||||||
#include <srs_rtsp_stack.hpp>
|
#include <srs_rtsp_stack.hpp>
|
||||||
#include <srs_kernel_stream.hpp>
|
#include <srs_kernel_stream.hpp>
|
||||||
|
@ -41,10 +40,43 @@
|
||||||
#include <srs_kernel_file.hpp>
|
#include <srs_kernel_file.hpp>
|
||||||
#include <srs_protocol_json.hpp>
|
#include <srs_protocol_json.hpp>
|
||||||
#include <srs_app_gb28181_sip.hpp>
|
#include <srs_app_gb28181_sip.hpp>
|
||||||
|
#include <srs_app_gb28181_jitbuffer.hpp>
|
||||||
|
#include <srs_rtmp_stack.hpp>
|
||||||
|
#include <srs_app_source.hpp>
|
||||||
|
#include <srs_service_conn.hpp>
|
||||||
|
|
||||||
#define RTP_PORT_MODE_FIXED "fixed"
|
#define RTP_PORT_MODE_FIXED "fixed"
|
||||||
#define RTP_PORT_MODE_RANDOM "random"
|
#define RTP_PORT_MODE_RANDOM "random"
|
||||||
|
|
||||||
|
#define PS_AUDIO_ID 0xc0
|
||||||
|
#define PS_AUDIO_ID_END 0xdf
|
||||||
|
#define PS_VIDEO_ID 0xe0
|
||||||
|
#define PS_VIDEO_ID_END 0xef
|
||||||
|
|
||||||
|
#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_VIDEO_MPEG4 0x10
|
||||||
|
#define STREAM_TYPE_VIDEO_H264 0x1b
|
||||||
|
#define STREAM_TYPE_VIDEO_HEVC 0x24
|
||||||
|
#define STREAM_TYPE_VIDEO_CAVS 0x42
|
||||||
|
#define STREAM_TYPE_VIDEO_SAVC 0x80
|
||||||
|
|
||||||
|
#define STREAM_TYPE_AUDIO_AC3 0x81
|
||||||
|
|
||||||
|
#define STREAM_TYPE_AUDIO_G711 0x90
|
||||||
|
#define STREAM_TYPE_AUDIO_G711ULAW 0x91
|
||||||
|
#define STREAM_TYPE_AUDIO_G722_1 0x92
|
||||||
|
#define STREAM_TYPE_AUDIO_G723_1 0x93
|
||||||
|
#define STREAM_TYPE_AUDIO_G726 0x96
|
||||||
|
#define STREAM_TYPE_AUDIO_G729_1 0x99
|
||||||
|
#define STREAM_TYPE_AUDIO_SVAC 0x9b
|
||||||
|
#define STREAM_TYPE_AUDIO_PCM 0x9c
|
||||||
|
|
||||||
class SrsConfDirective;
|
class SrsConfDirective;
|
||||||
class SrsRtpPacket;
|
class SrsRtpPacket;
|
||||||
class SrsRtmpClient;
|
class SrsRtmpClient;
|
||||||
|
@ -63,16 +95,26 @@ class SrsSipRequest;
|
||||||
class SrsGb28181RtmpMuxer;
|
class SrsGb28181RtmpMuxer;
|
||||||
class SrsGb28181Config;
|
class SrsGb28181Config;
|
||||||
class SrsGb28181PsRtpProcessor;
|
class SrsGb28181PsRtpProcessor;
|
||||||
|
class SrsGb28181TcpPsRtpProcessor;
|
||||||
class SrsGb28181SipService;
|
class SrsGb28181SipService;
|
||||||
class SrsGb28181StreamChannel;
|
class SrsGb28181StreamChannel;
|
||||||
class SrsGb28181SipSession;
|
class SrsGb28181SipSession;
|
||||||
|
class SrsPsJitterBuffer;
|
||||||
|
class SrsServer;
|
||||||
|
class SrsSource;
|
||||||
|
class SrsRequest;
|
||||||
|
class SrsResourceManager;
|
||||||
|
class SrsGb28181Conn;
|
||||||
|
class SrsGb28181Caster;
|
||||||
|
|
||||||
//ps rtp header packet parse
|
//ps rtp header packet parse
|
||||||
|
|
||||||
class SrsPsRtpPacket: public SrsRtpPacket
|
class SrsPsRtpPacket: public SrsRtpPacket
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SrsPsRtpPacket();
|
SrsPsRtpPacket();
|
||||||
virtual ~SrsPsRtpPacket();
|
virtual ~SrsPsRtpPacket();
|
||||||
|
bool isFirstPacket;
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t decode(SrsBuffer* stream);
|
virtual srs_error_t decode(SrsBuffer* stream);
|
||||||
};
|
};
|
||||||
|
@ -119,7 +161,7 @@ private:
|
||||||
SrsPithyPrint* pprint;
|
SrsPithyPrint* pprint;
|
||||||
SrsGb28181Config* config;
|
SrsGb28181Config* config;
|
||||||
std::map<std::string, SrsPsRtpPacket*> cache_ps_rtp_packet;
|
std::map<std::string, SrsPsRtpPacket*> cache_ps_rtp_packet;
|
||||||
std::map<std::string, SrsPsRtpPacket*> pre_packet;
|
std::map<std::string, SrsPsRtpPacket*> pre_packet;
|
||||||
std::string channel_id;
|
std::string channel_id;
|
||||||
bool auto_create_channel;
|
bool auto_create_channel;
|
||||||
public:
|
public:
|
||||||
|
@ -129,9 +171,42 @@ private:
|
||||||
bool can_send_ps_av_packet();
|
bool can_send_ps_av_packet();
|
||||||
void dispose();
|
void dispose();
|
||||||
void clear_pre_packet();
|
void clear_pre_packet();
|
||||||
|
SrsGb28181RtmpMuxer* fetch_rtmpmuxer(std::string channel_id, uint32_t ssrc);
|
||||||
|
srs_error_t rtmpmuxer_enqueue_data(SrsGb28181RtmpMuxer *muxer, uint32_t ssrc,
|
||||||
|
int peer_port, std::string address_string, SrsPsRtpPacket *pkt);
|
||||||
// Interface ISrsUdpHandler
|
// Interface ISrsUdpHandler
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t on_udp_packet(const sockaddr* from, const int fromlen, char* buf, int nb_buf);
|
virtual srs_error_t on_udp_packet(const sockaddr* from, const int fromlen, char* buf, int nb_buf);
|
||||||
|
public:
|
||||||
|
virtual srs_error_t on_rtp_packet_jitter(const sockaddr* from, const int fromlen, char* buf, int nb_buf);
|
||||||
|
virtual srs_error_t on_rtp_packet(const sockaddr* from, const int fromlen, char* buf, int nb_buf);
|
||||||
|
};
|
||||||
|
|
||||||
|
class SrsGb28181TcpPsRtpProcessor
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
SrsPithyPrint* pprint;
|
||||||
|
SrsGb28181Config* config;
|
||||||
|
std::map<std::string, SrsPsRtpPacket*> cache_ps_rtp_packet;
|
||||||
|
std::map<std::string, SrsPsRtpPacket*> pre_packet;
|
||||||
|
std::string channel_id;
|
||||||
|
bool auto_create_channel;
|
||||||
|
public:
|
||||||
|
SrsGb28181TcpPsRtpProcessor(SrsGb28181Config* c, std::string sid);
|
||||||
|
virtual ~SrsGb28181TcpPsRtpProcessor();
|
||||||
|
private:
|
||||||
|
bool can_send_ps_av_packet();
|
||||||
|
void dispose();
|
||||||
|
void clear_pre_packet();
|
||||||
|
SrsGb28181RtmpMuxer* create_rtmpmuxer(std::string channel_id, uint32_t ssrc);
|
||||||
|
srs_error_t rtmpmuxer_enqueue_data(SrsGb28181RtmpMuxer *muxer, uint32_t ssrc,
|
||||||
|
int peer_port, std::string address_string, SrsPsRtpPacket *pkt);
|
||||||
|
// Interface ISrsTcpHandler
|
||||||
|
public:
|
||||||
|
virtual srs_error_t on_rtp(char* buf, int nb_buf, std::string ip, int port);
|
||||||
|
public:
|
||||||
|
virtual srs_error_t on_rtp_packet_jitter(char* buf, int nb_buf, std::string ip, int port);
|
||||||
|
virtual srs_error_t on_rtp_packet(char* buf, int nb_buf, std::string ip, int port);
|
||||||
};
|
};
|
||||||
|
|
||||||
//ps stream processing parsing interface
|
//ps stream processing parsing interface
|
||||||
|
@ -142,7 +217,7 @@ public:
|
||||||
virtual ~ISrsPsStreamHander();
|
virtual ~ISrsPsStreamHander();
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t on_rtp_video(SrsSimpleStream* stream, int64_t dts)=0;
|
virtual srs_error_t on_rtp_video(SrsSimpleStream* stream, int64_t dts)=0;
|
||||||
virtual srs_error_t on_rtp_audio(SrsSimpleStream* stream, int64_t dts)=0;
|
virtual srs_error_t on_rtp_audio(SrsSimpleStream* stream, int64_t dts, int type)=0;
|
||||||
};
|
};
|
||||||
|
|
||||||
//analysis of PS stream and
|
//analysis of PS stream and
|
||||||
|
@ -197,6 +272,14 @@ private:
|
||||||
bool audio_enable;
|
bool audio_enable;
|
||||||
std::string channel_id;
|
std::string channel_id;
|
||||||
|
|
||||||
|
uint8_t video_es_id;
|
||||||
|
uint8_t video_es_type;
|
||||||
|
uint8_t audio_es_id;
|
||||||
|
uint8_t audio_es_type;
|
||||||
|
int audio_check_aac_try_count;
|
||||||
|
|
||||||
|
SrsRawAacStream *aac;
|
||||||
|
|
||||||
ISrsPsStreamHander *hander;
|
ISrsPsStreamHander *hander;
|
||||||
public:
|
public:
|
||||||
SrsPsStreamDemixer(ISrsPsStreamHander *h, std::string sid, bool a, bool k);
|
SrsPsStreamDemixer(ISrsPsStreamHander *h, std::string sid, bool a, bool k);
|
||||||
|
@ -205,6 +288,7 @@ private:
|
||||||
bool can_send_ps_av_packet();
|
bool can_send_ps_av_packet();
|
||||||
public:
|
public:
|
||||||
int64_t parse_ps_timestamp(const uint8_t* p);
|
int64_t parse_ps_timestamp(const uint8_t* p);
|
||||||
|
std::string get_ps_map_type_str(uint8_t);
|
||||||
virtual srs_error_t on_ps_stream(char* ps_data, int ps_size, uint32_t timestamp, uint32_t ssrc);
|
virtual srs_error_t on_ps_stream(char* ps_data, int ps_size, uint32_t timestamp, uint32_t ssrc);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -242,6 +326,24 @@ private:
|
||||||
SrsRawAacStream* aac;
|
SrsRawAacStream* aac;
|
||||||
std::string aac_specific_config;
|
std::string aac_specific_config;
|
||||||
|
|
||||||
|
SrsRequest* req;
|
||||||
|
SrsSource* source;
|
||||||
|
SrsServer* server;
|
||||||
|
|
||||||
|
SrsPsJitterBuffer *jitter_buffer;
|
||||||
|
SrsPsJitterBuffer *jitter_buffer_audio;
|
||||||
|
|
||||||
|
char *ps_buffer;
|
||||||
|
char *ps_buffer_audio;
|
||||||
|
|
||||||
|
int ps_buflen;
|
||||||
|
int ps_buflen_auido;
|
||||||
|
|
||||||
|
uint32_t ps_rtp_video_ts;
|
||||||
|
uint32_t ps_rtp_audio_ts;
|
||||||
|
|
||||||
|
bool source_publish;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::queue<SrsPsRtpPacket*> ps_queue;
|
std::queue<SrsPsRtpPacket*> ps_queue;
|
||||||
|
|
||||||
|
@ -252,6 +354,7 @@ public:
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t serve();
|
virtual srs_error_t serve();
|
||||||
virtual void stop();
|
virtual void stop();
|
||||||
|
srs_error_t initialize(SrsServer* s, SrsRequest* r);
|
||||||
|
|
||||||
virtual std::string get_channel_id();
|
virtual std::string get_channel_id();
|
||||||
virtual void ps_packet_enqueue(SrsPsRtpPacket *pkt);
|
virtual void ps_packet_enqueue(SrsPsRtpPacket *pkt);
|
||||||
|
@ -265,6 +368,8 @@ public:
|
||||||
virtual SrsGb28181StreamChannel get_channel();
|
virtual SrsGb28181StreamChannel get_channel();
|
||||||
srs_utime_t get_recv_stream_time();
|
srs_utime_t get_recv_stream_time();
|
||||||
|
|
||||||
|
void insert_jitterbuffer(SrsPsRtpPacket *pkt);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual srs_error_t do_cycle();
|
virtual srs_error_t do_cycle();
|
||||||
virtual void destroy();
|
virtual void destroy();
|
||||||
|
@ -272,15 +377,23 @@ private:
|
||||||
// Interface ISrsOneCycleThreadHandler
|
// Interface ISrsOneCycleThreadHandler
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t cycle();
|
virtual srs_error_t cycle();
|
||||||
|
// Interface ISrsConnection.
|
||||||
|
public:
|
||||||
virtual std::string remote_ip();
|
virtual std::string remote_ip();
|
||||||
|
virtual const SrsContextId& get_id();
|
||||||
|
virtual std::string desc();
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t on_rtp_video(SrsSimpleStream* stream, int64_t dts);
|
virtual srs_error_t on_rtp_video(SrsSimpleStream* stream, int64_t dts);
|
||||||
virtual srs_error_t on_rtp_audio(SrsSimpleStream* stream, int64_t dts);
|
virtual srs_error_t on_rtp_audio(SrsSimpleStream* stream, int64_t dts, int type);
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
srs_error_t replace_startcode_with_nalulen(char *video_data, int &size, uint32_t pts, uint32_t dts);
|
||||||
|
srs_error_t write_h264_ipb_frame2(char *frame, int frame_size, uint32_t pts, uint32_t dts);
|
||||||
virtual srs_error_t write_h264_sps_pps(uint32_t dts, uint32_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_h264_ipb_frame(char* frame, int frame_size, uint32_t dts, uint32_t pts, bool b = true);
|
||||||
virtual srs_error_t write_audio_raw_frame(char* frame, int frame_size, SrsRawAacStreamCodec* codec, uint32_t dts);
|
virtual srs_error_t write_audio_raw_frame(char* frame, int frame_size, SrsRawAacStreamCodec* codec, uint32_t dts);
|
||||||
virtual srs_error_t rtmp_write_packet(char type, uint32_t timestamp, char* data, int size);
|
virtual srs_error_t rtmp_write_packet(char type, uint32_t timestamp, char* data, int size);
|
||||||
|
virtual srs_error_t rtmp_write_packet_by_source(char type, uint32_t timestamp, char* data, int size);
|
||||||
private:
|
private:
|
||||||
// Connect to RTMP server.
|
// Connect to RTMP server.
|
||||||
virtual srs_error_t connect();
|
virtual srs_error_t connect();
|
||||||
|
@ -303,7 +416,9 @@ public:
|
||||||
int rtp_port_min;
|
int rtp_port_min;
|
||||||
int rtp_port_max;
|
int rtp_port_max;
|
||||||
int rtp_mux_port;
|
int rtp_mux_port;
|
||||||
|
bool rtp_mux_tcp_enable;
|
||||||
bool auto_create_channel;
|
bool auto_create_channel;
|
||||||
|
bool jitterbuffer_enable;
|
||||||
|
|
||||||
//sip config
|
//sip config
|
||||||
int sip_port;
|
int sip_port;
|
||||||
|
@ -393,16 +508,18 @@ private:
|
||||||
std::map<uint32_t, SrsPsRtpListener*> rtp_pool;
|
std::map<uint32_t, SrsPsRtpListener*> rtp_pool;
|
||||||
std::map<uint32_t, SrsGb28181RtmpMuxer*> rtmpmuxers_ssrc;
|
std::map<uint32_t, SrsGb28181RtmpMuxer*> rtmpmuxers_ssrc;
|
||||||
std::map<std::string, SrsGb28181RtmpMuxer*> rtmpmuxers;
|
std::map<std::string, SrsGb28181RtmpMuxer*> rtmpmuxers;
|
||||||
SrsCoroutineManager* manager;
|
SrsResourceManager* manager;
|
||||||
SrsGb28181SipService* sip_service;
|
SrsGb28181SipService* sip_service;
|
||||||
|
SrsServer* server;
|
||||||
public:
|
public:
|
||||||
SrsGb28181Manger(SrsConfDirective* c);
|
SrsGb28181Manger(SrsServer* s, SrsConfDirective* c);
|
||||||
virtual ~SrsGb28181Manger();
|
virtual ~SrsGb28181Manger();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
srs_error_t fetch_or_create_rtmpmuxer(std::string id, SrsGb28181RtmpMuxer** gb28181);
|
srs_error_t fetch_or_create_rtmpmuxer(std::string id, SrsRequest *req, SrsGb28181RtmpMuxer** gb28181);
|
||||||
SrsGb28181RtmpMuxer* fetch_rtmpmuxer(std::string id);
|
SrsGb28181RtmpMuxer* fetch_rtmpmuxer(std::string id);
|
||||||
SrsGb28181RtmpMuxer* fetch_rtmpmuxer_by_ssrc(uint32_t ssrc);
|
SrsGb28181RtmpMuxer* fetch_rtmpmuxer_by_ssrc(uint32_t ssrc);
|
||||||
|
void update_rtmpmuxer_to_newssrc_by_id(std::string id, uint32_t ssrc);
|
||||||
void rtmpmuxer_map_by_ssrc(SrsGb28181RtmpMuxer*muxer, uint32_t ssrc);
|
void rtmpmuxer_map_by_ssrc(SrsGb28181RtmpMuxer*muxer, uint32_t ssrc);
|
||||||
void rtmpmuxer_unmap_by_ssrc(uint32_t ssrc);
|
void rtmpmuxer_unmap_by_ssrc(uint32_t ssrc);
|
||||||
uint32_t generate_ssrc(std::string id);
|
uint32_t generate_ssrc(std::string id);
|
||||||
|
@ -410,11 +527,12 @@ public:
|
||||||
|
|
||||||
void set_sip_service(SrsGb28181SipService *s) { sip_service = s; }
|
void set_sip_service(SrsGb28181SipService *s) { sip_service = s; }
|
||||||
SrsGb28181SipService* get_sip_service() { return sip_service; }
|
SrsGb28181SipService* get_sip_service() { return sip_service; }
|
||||||
|
SrsGb28181Config* get_gb28181_config_ptr() { return config;}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
//stream channel api
|
//stream channel api
|
||||||
srs_error_t create_stream_channel(SrsGb28181StreamChannel *channel);
|
srs_error_t create_stream_channel(SrsGb28181StreamChannel *channel);
|
||||||
srs_error_t delete_stream_channel(std::string id);
|
srs_error_t delete_stream_channel(std::string id, std::string chid);
|
||||||
srs_error_t query_stream_channel(std::string id, SrsJsonArray* arr);
|
srs_error_t query_stream_channel(std::string id, SrsJsonArray* arr);
|
||||||
//sip api
|
//sip api
|
||||||
srs_error_t notify_sip_invite(std::string id, std::string ip, int port, uint32_t ssrc, std::string chid);
|
srs_error_t notify_sip_invite(std::string id, std::string ip, int port, uint32_t ssrc, std::string chid);
|
||||||
|
@ -424,6 +542,7 @@ public:
|
||||||
srs_error_t notify_sip_query_catalog(std::string id);
|
srs_error_t notify_sip_query_catalog(std::string id);
|
||||||
srs_error_t notify_sip_ptz(std::string id, std::string chid, std::string cmd, uint8_t speed, int priority);
|
srs_error_t notify_sip_ptz(std::string id, std::string chid, std::string cmd, uint8_t speed, int priority);
|
||||||
srs_error_t query_sip_session(std::string id, SrsJsonArray* arr);
|
srs_error_t query_sip_session(std::string id, SrsJsonArray* arr);
|
||||||
|
srs_error_t query_device_list(std::string id, SrsJsonArray* arr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void destroy();
|
void destroy();
|
||||||
|
@ -445,5 +564,54 @@ public:
|
||||||
void remove_sip_session(SrsGb28181SipSession* sess);
|
void remove_sip_session(SrsGb28181SipSession* sess);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The gb28181 tcp connection serve the fd.
|
||||||
|
class SrsGb28181Conn : public ISrsCoroutineHandler, public ISrsConnection
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
char* mbuffer;
|
||||||
|
srs_netfd_t stfd;
|
||||||
|
SrsStSocket* skt;
|
||||||
|
SrsRtspStack* rtsp;
|
||||||
|
SrsGb28181Caster* caster;
|
||||||
|
SrsCoroutine* trd;
|
||||||
|
SrsGb28181TcpPsRtpProcessor *processor;
|
||||||
|
public:
|
||||||
|
SrsGb28181Conn(SrsGb28181Caster* c, srs_netfd_t fd, SrsGb28181TcpPsRtpProcessor *rtp_processor);
|
||||||
|
virtual ~SrsGb28181Conn();
|
||||||
|
public:
|
||||||
|
virtual srs_error_t serve();
|
||||||
|
virtual std::string remote_ip();
|
||||||
|
private:
|
||||||
|
virtual srs_error_t do_cycle();
|
||||||
|
// Interface ISrsOneCycleThreadHandler
|
||||||
|
public:
|
||||||
|
virtual srs_error_t cycle();
|
||||||
|
virtual std::string desc();
|
||||||
|
virtual const SrsContextId& get_id();
|
||||||
|
};
|
||||||
|
|
||||||
|
// The caster for gb28181.
|
||||||
|
class SrsGb28181Caster : public ISrsTcpHandler
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::string output;
|
||||||
|
SrsGb28181Config *config;
|
||||||
|
SrsGb28181TcpPsRtpProcessor *rtp_processor;
|
||||||
|
private:
|
||||||
|
std::vector<SrsGb28181Conn*> clients;
|
||||||
|
SrsResourceManager* manager;
|
||||||
|
public:
|
||||||
|
SrsGb28181Caster(SrsConfDirective* c);
|
||||||
|
virtual ~SrsGb28181Caster();
|
||||||
|
public:
|
||||||
|
virtual srs_error_t initialize();
|
||||||
|
// Interface ISrsTcpHandler
|
||||||
|
public:
|
||||||
|
virtual srs_error_t on_tcp_client(srs_netfd_t stfd);
|
||||||
|
// internal methods.
|
||||||
|
public:
|
||||||
|
virtual void remove(SrsGb28181Conn* conn);
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
1715
trunk/src/app/srs_app_gb28181_jitbuffer.cpp
Normal file
1715
trunk/src/app/srs_app_gb28181_jitbuffer.cpp
Normal file
File diff suppressed because it is too large
Load diff
461
trunk/src/app/srs_app_gb28181_jitbuffer.hpp
Normal file
461
trunk/src/app/srs_app_gb28181_jitbuffer.hpp
Normal file
|
@ -0,0 +1,461 @@
|
||||||
|
/**
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2020 Lixin
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SRS_APP_GB28181_JITBUFFER_HPP
|
||||||
|
#define SRS_APP_GB28181_JITBUFFER_HPP
|
||||||
|
|
||||||
|
#include <srs_core.hpp>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <queue>
|
||||||
|
#include <map>
|
||||||
|
#include <list>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
#include <srs_app_log.hpp>
|
||||||
|
#include <srs_kernel_utility.hpp>
|
||||||
|
#include <srs_app_gb28181.hpp>
|
||||||
|
|
||||||
|
class SrsPsRtpPacket;
|
||||||
|
class SrsPsFrameBuffer;
|
||||||
|
class PsDecodingState;
|
||||||
|
class SrsGb28181RtmpMuxer;
|
||||||
|
class VCMPacket;
|
||||||
|
|
||||||
|
///jittbuffer
|
||||||
|
|
||||||
|
enum FrameType {
|
||||||
|
kEmptyFrame = 0,
|
||||||
|
kAudioFrameSpeech = 1,
|
||||||
|
kAudioFrameCN = 2,
|
||||||
|
kVideoFrameKey = 3, // independent frame
|
||||||
|
kVideoFrameDelta = 4, // depends on the previus frame
|
||||||
|
kVideoFrameGolden = 5, // depends on a old known previus frame
|
||||||
|
kVideoFrameAltRef = 6
|
||||||
|
};
|
||||||
|
|
||||||
|
// Used to indicate which decode with errors mode should be used.
|
||||||
|
enum PsDecodeErrorMode {
|
||||||
|
kNoErrors, // Never decode with errors. Video will freeze
|
||||||
|
// if nack is disabled.
|
||||||
|
kSelectiveErrors, // Frames that are determined decodable in
|
||||||
|
// VCMSessionInfo may be decoded with missing
|
||||||
|
// packets. As not all incomplete frames will be
|
||||||
|
// decodable, video will freeze if nack is disabled.
|
||||||
|
kWithErrors // Release frames as needed. Errors may be
|
||||||
|
// introduced as some encoded frames may not be
|
||||||
|
// complete.
|
||||||
|
};
|
||||||
|
|
||||||
|
// Used to estimate rolling average of packets per frame.
|
||||||
|
static const float kFastConvergeMultiplier = 0.4f;
|
||||||
|
static const float kNormalConvergeMultiplier = 0.2f;
|
||||||
|
|
||||||
|
enum { kMaxNumberOfFrames = 300 };
|
||||||
|
enum { kStartNumberOfFrames = 6 };
|
||||||
|
enum { kMaxVideoDelayMs = 10000 };
|
||||||
|
enum { kPacketsPerFrameMultiplier = 5 };
|
||||||
|
enum { kFastConvergeThreshold = 5};
|
||||||
|
|
||||||
|
enum PsJitterBufferEnum {
|
||||||
|
kMaxConsecutiveOldFrames = 60,
|
||||||
|
kMaxConsecutiveOldPackets = 300,
|
||||||
|
kMaxPacketsInSession = 800,
|
||||||
|
kBufferIncStepSizeBytes = 30000, // >20 packets.
|
||||||
|
kMaxJBFrameSizeBytes = 4000000 // sanity don't go above 4Mbyte.
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PsFrameBufferEnum {
|
||||||
|
kOutOfBoundsPacket = -7,
|
||||||
|
kNotInitialized = -6,
|
||||||
|
kOldPacket = -5,
|
||||||
|
kGeneralError = -4,
|
||||||
|
kFlushIndicator = -3, // Indicator that a flush has occurred.
|
||||||
|
kTimeStampError = -2,
|
||||||
|
kSizeError = -1,
|
||||||
|
kNoError = 0,
|
||||||
|
kIncomplete = 1, // Frame incomplete.
|
||||||
|
kCompleteSession = 3, // at least one layer in the frame complete.
|
||||||
|
kDecodableSession = 4, // Frame incomplete, but ready to be decoded
|
||||||
|
kDuplicatePacket = 5 // We're receiving a duplicate packet.
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PsFrameBufferStateEnum {
|
||||||
|
kStateEmpty, // frame popped by the RTP receiver
|
||||||
|
kStateIncomplete, // frame that have one or more packet(s) stored
|
||||||
|
kStateComplete, // frame that have all packets
|
||||||
|
kStateDecodable // Hybrid mode - frame can be decoded
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PsNackMode {
|
||||||
|
kNack,
|
||||||
|
kNoNack
|
||||||
|
};
|
||||||
|
|
||||||
|
// Used to pass data from jitter buffer to session info.
|
||||||
|
// This data is then used in determining whether a frame is decodable.
|
||||||
|
struct FrameData {
|
||||||
|
int64_t rtt_ms;
|
||||||
|
float rolling_average_packets_per_frame;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline bool IsNewerSequenceNumber(uint16_t sequence_number,
|
||||||
|
uint16_t prev_sequence_number)
|
||||||
|
{
|
||||||
|
return sequence_number != prev_sequence_number &&
|
||||||
|
static_cast<uint16_t>(sequence_number - prev_sequence_number) < 0x8000;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool IsNewerTimestamp(uint32_t timestamp, uint32_t prev_timestamp)
|
||||||
|
{
|
||||||
|
return timestamp != prev_timestamp &&
|
||||||
|
static_cast<uint32_t>(timestamp - prev_timestamp) < 0x80000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint16_t LatestSequenceNumber(uint16_t sequence_number1,
|
||||||
|
uint16_t sequence_number2)
|
||||||
|
{
|
||||||
|
return IsNewerSequenceNumber(sequence_number1, sequence_number2)
|
||||||
|
? sequence_number1
|
||||||
|
: sequence_number2;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint32_t LatestTimestamp(uint32_t timestamp1, uint32_t timestamp2)
|
||||||
|
{
|
||||||
|
return IsNewerTimestamp(timestamp1, timestamp2) ? timestamp1 : timestamp2;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef std::list<SrsPsFrameBuffer*> UnorderedFrameList;
|
||||||
|
|
||||||
|
class TimestampLessThan {
|
||||||
|
public:
|
||||||
|
bool operator() (const uint32_t& timestamp1,
|
||||||
|
const uint32_t& timestamp2) const
|
||||||
|
{
|
||||||
|
return IsNewerTimestamp(timestamp2, timestamp1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class FrameList
|
||||||
|
: public std::map<uint32_t, SrsPsFrameBuffer*, TimestampLessThan> {
|
||||||
|
public:
|
||||||
|
void InsertFrame(SrsPsFrameBuffer* frame);
|
||||||
|
SrsPsFrameBuffer* PopFrame(uint32_t timestamp);
|
||||||
|
SrsPsFrameBuffer* Front() const;
|
||||||
|
SrsPsFrameBuffer* FrontNext() const;
|
||||||
|
SrsPsFrameBuffer* Back() const;
|
||||||
|
int RecycleFramesUntilKeyFrame(FrameList::iterator* key_frame_it,
|
||||||
|
UnorderedFrameList* free_frames);
|
||||||
|
void CleanUpOldOrEmptyFrames(PsDecodingState* decoding_state, UnorderedFrameList* free_frames);
|
||||||
|
void Reset(UnorderedFrameList* free_frames);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class VCMPacket {
|
||||||
|
public:
|
||||||
|
VCMPacket();
|
||||||
|
VCMPacket(const uint8_t* ptr,
|
||||||
|
size_t size,
|
||||||
|
uint16_t seqNum,
|
||||||
|
uint32_t timestamp,
|
||||||
|
bool markerBit);
|
||||||
|
|
||||||
|
void Reset();
|
||||||
|
|
||||||
|
uint8_t payloadType;
|
||||||
|
uint32_t timestamp;
|
||||||
|
// NTP time of the capture time in local timebase in milliseconds.
|
||||||
|
int64_t ntp_time_ms_;
|
||||||
|
uint16_t seqNum;
|
||||||
|
const uint8_t* dataPtr;
|
||||||
|
size_t sizeBytes;
|
||||||
|
bool markerBit;
|
||||||
|
|
||||||
|
FrameType frameType;
|
||||||
|
//cloopenwebrtc::VideoCodecType codec;
|
||||||
|
|
||||||
|
bool isFirstPacket; // Is this first packet in a frame.
|
||||||
|
//VCMNaluCompleteness completeNALU; // Default is kNaluIncomplete.
|
||||||
|
bool insertStartCode; // True if a start code should be inserted before this
|
||||||
|
// packet.
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
//RTPVideoHeader codecSpecificHeader;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SrsPsFrameBuffer {
|
||||||
|
public:
|
||||||
|
SrsPsFrameBuffer();
|
||||||
|
virtual ~SrsPsFrameBuffer();
|
||||||
|
|
||||||
|
public:
|
||||||
|
PsFrameBufferEnum InsertPacket(const VCMPacket& packet, const FrameData& frame_data);
|
||||||
|
void UpdateCompleteSession();
|
||||||
|
void UpdateDecodableSession(const FrameData& frame_data);
|
||||||
|
bool HaveFirstPacket() const;
|
||||||
|
bool HaveLastPacket() const;
|
||||||
|
void Reset();
|
||||||
|
|
||||||
|
uint32_t GetTimeStamp() const;
|
||||||
|
FrameType GetFrameType() const;
|
||||||
|
PsFrameBufferStateEnum GetState() const;
|
||||||
|
|
||||||
|
int32_t GetHighSeqNum() const;
|
||||||
|
int32_t GetLowSeqNum() const;
|
||||||
|
size_t Length() const;
|
||||||
|
const uint8_t* Buffer() const;
|
||||||
|
|
||||||
|
int NumPackets() const;
|
||||||
|
void InformOfEmptyPacket(uint16_t seq_num);
|
||||||
|
|
||||||
|
bool complete() const;
|
||||||
|
bool decodable() const;
|
||||||
|
|
||||||
|
bool GetPsPlayload(SrsSimpleStream **ps_data, int &count);
|
||||||
|
bool DeletePacket(int &count);
|
||||||
|
void PrepareForDecode(bool continuous);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
typedef std::list<VCMPacket> PacketList;
|
||||||
|
typedef PacketList::iterator PacketIterator;
|
||||||
|
typedef PacketList::const_iterator PacketIteratorConst;
|
||||||
|
typedef PacketList::reverse_iterator ReversePacketIterator;
|
||||||
|
|
||||||
|
bool InSequence(const PacketIterator& packet_it,
|
||||||
|
const PacketIterator& prev_packet_it);
|
||||||
|
|
||||||
|
size_t InsertBuffer(uint8_t* frame_buffer, PacketIterator packet_it);
|
||||||
|
size_t Insert(const uint8_t* buffer, size_t length, uint8_t* frame_buffer);
|
||||||
|
void ShiftSubsequentPackets(PacketIterator it, int steps_to_shift);
|
||||||
|
void VerifyAndAllocate(const uint32_t minimumSize);
|
||||||
|
void UpdateDataPointers(const uint8_t* old_base_ptr, const uint8_t* new_base_ptr);
|
||||||
|
size_t DeletePacketData(PacketIterator start, PacketIterator end);
|
||||||
|
size_t MakeDecodable();
|
||||||
|
|
||||||
|
|
||||||
|
PacketList packets_;
|
||||||
|
int empty_seq_num_low_;
|
||||||
|
int empty_seq_num_high_;
|
||||||
|
|
||||||
|
int first_packet_seq_num_;
|
||||||
|
int last_packet_seq_num_;
|
||||||
|
|
||||||
|
bool complete_;
|
||||||
|
bool decodable_;
|
||||||
|
|
||||||
|
uint32_t timeStamp_;
|
||||||
|
FrameType frame_type_;
|
||||||
|
|
||||||
|
PsDecodeErrorMode decode_error_mode_;
|
||||||
|
PsFrameBufferStateEnum state_;
|
||||||
|
|
||||||
|
uint16_t nackCount_;
|
||||||
|
int64_t latestPacketTimeMs_;
|
||||||
|
|
||||||
|
// The payload.
|
||||||
|
uint8_t* _buffer;
|
||||||
|
size_t _size;
|
||||||
|
size_t _length;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PsDecodingState {
|
||||||
|
public:
|
||||||
|
PsDecodingState();
|
||||||
|
~PsDecodingState();
|
||||||
|
// Check for old frame
|
||||||
|
bool IsOldFrame(const SrsPsFrameBuffer* frame) const;
|
||||||
|
// Check for old packet
|
||||||
|
bool IsOldPacket(const VCMPacket* packet);
|
||||||
|
// Check for frame continuity based on current decoded state. Use best method
|
||||||
|
// possible, i.e. temporal info, picture ID or sequence number.
|
||||||
|
bool ContinuousFrame(const SrsPsFrameBuffer* frame) const;
|
||||||
|
void SetState(const SrsPsFrameBuffer* frame);
|
||||||
|
void CopyFrom(const PsDecodingState& state);
|
||||||
|
bool UpdateEmptyFrame(const SrsPsFrameBuffer* frame);
|
||||||
|
// Update the sequence number if the timestamp matches current state and the
|
||||||
|
// sequence number is higher than the current one. This accounts for packets
|
||||||
|
// arriving late.
|
||||||
|
void UpdateOldPacket(const VCMPacket* packet);
|
||||||
|
void SetSeqNum(uint16_t new_seq_num);
|
||||||
|
void Reset();
|
||||||
|
uint32_t time_stamp() const;
|
||||||
|
uint16_t sequence_num() const;
|
||||||
|
// Return true if at initial state.
|
||||||
|
bool in_initial_state() const;
|
||||||
|
// Return true when sync is on - decode all layers.
|
||||||
|
bool full_sync() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void UpdateSyncState(const SrsPsFrameBuffer* frame);
|
||||||
|
// Designated continuity functions
|
||||||
|
//bool ContinuousPictureId(int picture_id) const;
|
||||||
|
bool ContinuousSeqNum(uint16_t seq_num) const;
|
||||||
|
//bool ContinuousLayer(int temporal_id, int tl0_pic_id) const;
|
||||||
|
//bool UsingPictureId(const SrsPsFrameBuffer* frame) const;
|
||||||
|
|
||||||
|
// Keep state of last decoded frame.
|
||||||
|
// TODO(mikhal/stefan): create designated classes to handle these types.
|
||||||
|
uint16_t sequence_num_;
|
||||||
|
uint32_t time_stamp_;
|
||||||
|
int picture_id_;
|
||||||
|
int temporal_id_;
|
||||||
|
int tl0_pic_id_;
|
||||||
|
bool full_sync_; // Sync flag when temporal layers are used.
|
||||||
|
bool in_initial_state_;
|
||||||
|
|
||||||
|
bool m_firstPacket;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SrsPsJitterBuffer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SrsPsJitterBuffer(std::string key);
|
||||||
|
virtual ~SrsPsJitterBuffer();
|
||||||
|
|
||||||
|
public:
|
||||||
|
srs_error_t start();
|
||||||
|
void Reset();
|
||||||
|
PsFrameBufferEnum InsertPacket(const SrsPsRtpPacket &packet, char *buf, int size, bool* retransmitted);
|
||||||
|
void ReleaseFrame(SrsPsFrameBuffer* frame);
|
||||||
|
bool FoundFrame(uint32_t& time_stamp);
|
||||||
|
bool GetPsFrame(char **buffer, int &buf_len, int &size, const uint32_t time_stamp);
|
||||||
|
void SetDecodeErrorMode(PsDecodeErrorMode error_mode);
|
||||||
|
void SetNackMode(PsNackMode mode,int64_t low_rtt_nack_threshold_ms,
|
||||||
|
int64_t high_rtt_nack_threshold_ms);
|
||||||
|
void SetNackSettings(size_t max_nack_list_size,int max_packet_age_to_nack,
|
||||||
|
int max_incomplete_time_ms);
|
||||||
|
uint16_t* GetNackList(uint16_t* nack_list_size, bool* request_key_frame);
|
||||||
|
void Flush();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
PsFrameBufferEnum GetFrame(const VCMPacket& packet, SrsPsFrameBuffer** frame,
|
||||||
|
FrameList** frame_list);
|
||||||
|
SrsPsFrameBuffer* GetEmptyFrame();
|
||||||
|
bool NextCompleteTimestamp(uint32_t max_wait_time_ms, uint32_t* timestamp);
|
||||||
|
bool NextMaybeIncompleteTimestamp(uint32_t* timestamp);
|
||||||
|
SrsPsFrameBuffer* ExtractAndSetDecode(uint32_t timestamp);
|
||||||
|
SrsPsFrameBuffer* NextFrame() const;
|
||||||
|
|
||||||
|
|
||||||
|
bool TryToIncreaseJitterBufferSize();
|
||||||
|
bool RecycleFramesUntilKeyFrame();
|
||||||
|
bool IsContinuous(const SrsPsFrameBuffer& frame) const;
|
||||||
|
bool IsContinuousInState(const SrsPsFrameBuffer& frame,
|
||||||
|
const PsDecodingState& decoding_state) const;
|
||||||
|
void FindAndInsertContinuousFrames(const SrsPsFrameBuffer& new_frame);
|
||||||
|
void CleanUpOldOrEmptyFrames();
|
||||||
|
|
||||||
|
//nack
|
||||||
|
bool UpdateNackList(uint16_t sequence_number);
|
||||||
|
bool TooLargeNackList() const;
|
||||||
|
bool HandleTooLargeNackList();
|
||||||
|
bool MissingTooOldPacket(uint16_t latest_sequence_number) const;
|
||||||
|
bool HandleTooOldPackets(uint16_t latest_sequence_number);
|
||||||
|
void DropPacketsFromNackList(uint16_t last_decoded_sequence_number);
|
||||||
|
PsNackMode nack_mode() const;
|
||||||
|
int NonContinuousOrIncompleteDuration();
|
||||||
|
uint16_t EstimatedLowSequenceNumber(const SrsPsFrameBuffer& frame) const;
|
||||||
|
bool WaitForRetransmissions();
|
||||||
|
|
||||||
|
private:
|
||||||
|
class SequenceNumberLessThan {
|
||||||
|
public:
|
||||||
|
bool operator() (const uint16_t& sequence_number1,
|
||||||
|
const uint16_t& sequence_number2) const
|
||||||
|
{
|
||||||
|
return IsNewerSequenceNumber(sequence_number2, sequence_number1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
typedef std::set<uint16_t, SequenceNumberLessThan> SequenceNumberSet;
|
||||||
|
|
||||||
|
std::string key_;
|
||||||
|
|
||||||
|
srs_cond_t wait_cond_t;
|
||||||
|
// If we are running (have started) or not.
|
||||||
|
bool running_;
|
||||||
|
// Number of allocated frames.
|
||||||
|
int max_number_of_frames_;
|
||||||
|
UnorderedFrameList free_frames_;
|
||||||
|
FrameList decodable_frames_;
|
||||||
|
FrameList incomplete_frames_;
|
||||||
|
PsDecodingState last_decoded_state_;
|
||||||
|
bool first_packet_since_reset_;
|
||||||
|
|
||||||
|
// Statistics.
|
||||||
|
//VCMReceiveStatisticsCallback* stats_callback_ GUARDED_BY(crit_sect_);
|
||||||
|
// Frame counts for each type (key, delta, ...)
|
||||||
|
//FrameCounts receive_statistics_;
|
||||||
|
// Latest calculated frame rates of incoming stream.
|
||||||
|
unsigned int incoming_frame_rate_;
|
||||||
|
unsigned int incoming_frame_count_;
|
||||||
|
int64_t time_last_incoming_frame_count_;
|
||||||
|
unsigned int incoming_bit_count_;
|
||||||
|
unsigned int incoming_bit_rate_;
|
||||||
|
// Number of frames in a row that have been too old.
|
||||||
|
int num_consecutive_old_frames_;
|
||||||
|
// Number of packets in a row that have been too old.
|
||||||
|
int num_consecutive_old_packets_;
|
||||||
|
// Number of packets received.
|
||||||
|
int num_packets_;
|
||||||
|
int num_packets_free_;
|
||||||
|
// Number of duplicated packets received.
|
||||||
|
int num_duplicated_packets_;
|
||||||
|
// Number of packets discarded by the jitter buffer.
|
||||||
|
int num_discarded_packets_;
|
||||||
|
// Time when first packet is received.
|
||||||
|
int64_t time_first_packet_ms_;
|
||||||
|
|
||||||
|
// Jitter estimation.
|
||||||
|
// Filter for estimating jitter.
|
||||||
|
//VCMJitterEstimator jitter_estimate_;
|
||||||
|
// Calculates network delays used for jitter calculations.
|
||||||
|
//VCMInterFrameDelay inter_frame_delay_;
|
||||||
|
//VCMJitterSample waiting_for_completion_;
|
||||||
|
int64_t rtt_ms_;
|
||||||
|
|
||||||
|
// NACK and retransmissions.
|
||||||
|
PsNackMode nack_mode_;
|
||||||
|
int64_t low_rtt_nack_threshold_ms_;
|
||||||
|
int64_t high_rtt_nack_threshold_ms_;
|
||||||
|
// Holds the internal NACK list (the missing sequence numbers).
|
||||||
|
SequenceNumberSet missing_sequence_numbers_;
|
||||||
|
uint16_t latest_received_sequence_number_;
|
||||||
|
std::vector<uint16_t> nack_seq_nums_;
|
||||||
|
size_t max_nack_list_size_;
|
||||||
|
int max_packet_age_to_nack_; // Measured in sequence numbers.
|
||||||
|
int max_incomplete_time_ms_;
|
||||||
|
|
||||||
|
PsDecodeErrorMode decode_error_mode_;
|
||||||
|
// Estimated rolling average of packets per frame
|
||||||
|
float average_packets_per_frame_;
|
||||||
|
// average_packets_per_frame converges fast if we have fewer than this many
|
||||||
|
// frames.
|
||||||
|
int frame_counter_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -68,6 +68,7 @@ std::string srs_get_sip_session_status_str(SrsGb28181SipSessionStatusType status
|
||||||
SrsGb28181Device::SrsGb28181Device()
|
SrsGb28181Device::SrsGb28181Device()
|
||||||
{
|
{
|
||||||
device_id = "";
|
device_id = "";
|
||||||
|
device_name = "";
|
||||||
invite_status = SrsGb28181SipSessionUnkonw;
|
invite_status = SrsGb28181SipSessionUnkonw;
|
||||||
invite_time = 0;
|
invite_time = 0;
|
||||||
device_status = "";
|
device_status = "";
|
||||||
|
@ -101,6 +102,9 @@ SrsGb28181SipSession::SrsGb28181SipSession(SrsGb28181SipService *c, SrsSipReques
|
||||||
|
|
||||||
_fromlen = 0;
|
_fromlen = 0;
|
||||||
_sip_cseq = 100;
|
_sip_cseq = 100;
|
||||||
|
|
||||||
|
item_list_sumnum = 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsGb28181SipSession::~SrsGb28181SipSession()
|
SrsGb28181SipSession::~SrsGb28181SipSession()
|
||||||
|
@ -162,6 +166,8 @@ srs_error_t SrsGb28181SipSession::do_cycle()
|
||||||
if (_register_status == SrsGb28181SipSessionRegisterOk &&
|
if (_register_status == SrsGb28181SipSessionRegisterOk &&
|
||||||
_alive_status == SrsGb28181SipSessionAliveOk)
|
_alive_status == SrsGb28181SipSessionAliveOk)
|
||||||
{
|
{
|
||||||
|
std::list<std::string> auto_play_list;
|
||||||
|
|
||||||
std::map<std::string, SrsGb28181Device*>::iterator it;
|
std::map<std::string, SrsGb28181Device*>::iterator it;
|
||||||
for (it = _device_list.begin(); it != _device_list.end(); it++) {
|
for (it = _device_list.begin(); it != _device_list.end(); it++) {
|
||||||
SrsGb28181Device *device = it->second;
|
SrsGb28181Device *device = it->second;
|
||||||
|
@ -190,6 +196,14 @@ srs_error_t SrsGb28181SipSession::do_cycle()
|
||||||
if (device->device_status != "ON" ||
|
if (device->device_status != "ON" ||
|
||||||
device->invite_status != SrsGb28181SipSessionUnkonw) continue;
|
device->invite_status != SrsGb28181SipSessionUnkonw) continue;
|
||||||
|
|
||||||
|
auto_play_list.push_back(chid);
|
||||||
|
}//end for (it)
|
||||||
|
|
||||||
|
//auto send sip invite and create stream chennal
|
||||||
|
while(auto_play_list.size() > 0){
|
||||||
|
std::string chid = auto_play_list.front();
|
||||||
|
auto_play_list.pop_front();
|
||||||
|
|
||||||
SrsGb28181StreamChannel ch;
|
SrsGb28181StreamChannel ch;
|
||||||
|
|
||||||
ch.set_channel_id(_session_id + "@" + chid);
|
ch.set_channel_id(_session_id + "@" + chid);
|
||||||
|
@ -223,7 +237,7 @@ srs_error_t SrsGb28181SipSession::do_cycle()
|
||||||
|
|
||||||
srs_trace("gb28181: %s clients device=%s send invite code=%d",
|
srs_trace("gb28181: %s clients device=%s send invite code=%d",
|
||||||
_session_id.c_str(), chid.c_str(), code);
|
_session_id.c_str(), chid.c_str(), code);
|
||||||
}//end for (it)
|
}//end while (auto_play_list.size())
|
||||||
}//end if (config)
|
}//end if (config)
|
||||||
|
|
||||||
if (_register_status == SrsGb28181SipSessionRegisterOk &&
|
if (_register_status == SrsGb28181SipSessionRegisterOk &&
|
||||||
|
@ -272,7 +286,7 @@ srs_error_t SrsGb28181SipSession::do_cycle()
|
||||||
invite_duration = 0;
|
invite_duration = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_trace("gb28181: sip session=%s device=%s status(%s, %s), duration(%u)",
|
srs_info("gb28181: sip session=%s device=%s status(%s, %s), duration(%u)",
|
||||||
_session_id.c_str(), chid.c_str(), device->device_status.c_str(),
|
_session_id.c_str(), chid.c_str(), device->device_status.c_str(),
|
||||||
srs_get_sip_session_status_str(device->invite_status).c_str(),
|
srs_get_sip_session_status_str(device->invite_status).c_str(),
|
||||||
(invite_duration / SRS_UTIME_SECONDS));
|
(invite_duration / SRS_UTIME_SECONDS));
|
||||||
|
@ -290,6 +304,16 @@ std::string SrsGb28181SipSession::remote_ip()
|
||||||
return _peer_ip;
|
return _peer_ip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SrsContextId& SrsGb28181SipSession::get_id()
|
||||||
|
{
|
||||||
|
return _srs_context->get_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SrsGb28181SipSession::desc()
|
||||||
|
{
|
||||||
|
return "SipConn";
|
||||||
|
}
|
||||||
|
|
||||||
srs_error_t SrsGb28181SipSession::cycle()
|
srs_error_t SrsGb28181SipSession::cycle()
|
||||||
{
|
{
|
||||||
srs_error_t err = do_cycle();
|
srs_error_t err = do_cycle();
|
||||||
|
@ -317,22 +341,42 @@ void SrsGb28181SipSession::update_device_list(std::map<std::string, std::string>
|
||||||
if (_device_list.find(id) == _device_list.end()){
|
if (_device_list.find(id) == _device_list.end()){
|
||||||
SrsGb28181Device *device = new SrsGb28181Device();
|
SrsGb28181Device *device = new SrsGb28181Device();
|
||||||
device->device_id = id;
|
device->device_id = id;
|
||||||
device->device_status = status;
|
if (status.find(",") != std::string::npos) {
|
||||||
|
device->device_status = status.substr(0,status.find(","));
|
||||||
|
device->device_name = status.substr(status.find(",")+1);
|
||||||
|
} else {
|
||||||
|
device->device_status = status;
|
||||||
|
device->device_name = "NONAME";
|
||||||
|
}
|
||||||
device->invite_status = SrsGb28181SipSessionUnkonw;
|
device->invite_status = SrsGb28181SipSessionUnkonw;
|
||||||
device->invite_time = 0;
|
device->invite_time = 0;
|
||||||
_device_list[id] = device;
|
_device_list[id] = device;
|
||||||
|
|
||||||
}else {
|
}else {
|
||||||
SrsGb28181Device *device = _device_list[id];
|
SrsGb28181Device *device = _device_list[id];
|
||||||
device->device_status = status;
|
if (status.find(",") != std::string::npos) {
|
||||||
|
device->device_status = status.substr(0,status.find(","));
|
||||||
|
device->device_name = status.substr(status.find(",")+1);
|
||||||
|
} else {
|
||||||
|
device->device_status = status;
|
||||||
|
device->device_name = "NONAME";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// srs_trace("gb28181: sip session %s, deviceid=%s status=(%s,%s)",
|
|
||||||
// _session_id.c_str(), id.c_str(), status.c_str(),
|
|
||||||
// srs_get_sip_session_status_str(device.invite_status).c_str());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SrsGb28181SipSession::clear_device_list()
|
||||||
|
{
|
||||||
|
//destory all device
|
||||||
|
std::map<std::string, SrsGb28181Device*>::iterator it;
|
||||||
|
for (it = _device_list.begin(); it != _device_list.end(); ++it) {
|
||||||
|
srs_freep(it->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
_device_list.clear();
|
||||||
|
}
|
||||||
|
|
||||||
SrsGb28181Device* SrsGb28181SipSession::get_device_info(std::string chid)
|
SrsGb28181Device* SrsGb28181SipSession::get_device_info(std::string chid)
|
||||||
{
|
{
|
||||||
if (_device_list.find(chid) != _device_list.end()){
|
if (_device_list.find(chid) != _device_list.end()){
|
||||||
|
@ -354,23 +398,31 @@ void SrsGb28181SipSession::dumps(SrsJsonObject* obj)
|
||||||
SrsJsonObject* obj = SrsJsonAny::object();
|
SrsJsonObject* obj = SrsJsonAny::object();
|
||||||
arr->append(obj);
|
arr->append(obj);
|
||||||
obj->set("device_id", SrsJsonAny::str(device->device_id.c_str()));
|
obj->set("device_id", SrsJsonAny::str(device->device_id.c_str()));
|
||||||
|
obj->set("device_name", SrsJsonAny::str(device->device_name.c_str()));
|
||||||
obj->set("device_status", SrsJsonAny::str(device->device_status.c_str()));
|
obj->set("device_status", SrsJsonAny::str(device->device_status.c_str()));
|
||||||
obj->set("invite_status", SrsJsonAny::str(srs_get_sip_session_status_str(device->invite_status).c_str()));
|
obj->set("invite_status", SrsJsonAny::str(srs_get_sip_session_status_str(device->invite_status).c_str()));
|
||||||
obj->set("invite_time", SrsJsonAny::integer(device->invite_time/SRS_UTIME_SECONDS));
|
obj->set("invite_time", SrsJsonAny::integer(device->invite_time/SRS_UTIME_SECONDS));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//obj->set("rtmp_port", SrsJsonAny::integer(rtmp_port));
|
void SrsGb28181SipSession::dumpItemList(SrsJsonObject* obj)
|
||||||
// obj->set("app", SrsJsonAny::str(app.c_str()));
|
{
|
||||||
// obj->set("stream", SrsJsonAny::str(stream.c_str()));
|
obj->set("TopDeviceID", SrsJsonAny::str(_session_id.c_str()));
|
||||||
// obj->set("rtmp_url", SrsJsonAny::str(rtmp_url.c_str()));
|
obj->set("SumNum", SrsJsonAny::integer(item_list_sumnum));
|
||||||
|
obj->set("RealSumNum", SrsJsonAny::integer(item_list.size()));
|
||||||
|
|
||||||
// obj->set("ssrc", SrsJsonAny::integer(ssrc));
|
SrsJsonArray* arr = SrsJsonAny::array();
|
||||||
// obj->set("rtp_port", SrsJsonAny::integer(rtp_port));
|
obj->set("ItemList", arr);
|
||||||
// obj->set("port_mode", SrsJsonAny::str(port_mode.c_str()));
|
std::map<std::string, std::map<std::string, std::string> >::iterator it;
|
||||||
// obj->set("rtp_peer_port", SrsJsonAny::integer(rtp_peer_port));
|
for (it = item_list.begin(); it != item_list.end(); ++it) {
|
||||||
// obj->set("rtp_peer_ip", SrsJsonAny::str(rtp_peer_ip.c_str()));
|
std::map<std::string, std::string> device = it->second;
|
||||||
// obj->set("recv_time", SrsJsonAny::integer(recv_time/SRS_UTIME_SECONDS));
|
SrsJsonObject* obj2 = SrsJsonAny::object();
|
||||||
// obj->set("recv_time_str", SrsJsonAny::str(recv_time_str.c_str()));
|
arr->append(obj2);
|
||||||
|
std::map<std::string, std::string>::iterator it2;
|
||||||
|
for (it2 = device.begin(); it2 != device.end(); ++it2) {
|
||||||
|
obj2->set(it2->first, SrsJsonAny::str(it2->second.c_str()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//gb28181 sip Service
|
//gb28181 sip Service
|
||||||
|
@ -380,6 +432,8 @@ SrsGb28181SipService::SrsGb28181SipService(SrsConfDirective* c)
|
||||||
config = new SrsGb28181Config(c);
|
config = new SrsGb28181Config(c);
|
||||||
sip = new SrsSipStack();
|
sip = new SrsSipStack();
|
||||||
|
|
||||||
|
lock_session = srs_mutex_new();
|
||||||
|
|
||||||
if (_srs_gb28181){
|
if (_srs_gb28181){
|
||||||
_srs_gb28181->set_sip_service(this);
|
_srs_gb28181->set_sip_service(this);
|
||||||
}
|
}
|
||||||
|
@ -388,6 +442,8 @@ SrsGb28181SipService::SrsGb28181SipService(SrsConfDirective* c)
|
||||||
SrsGb28181SipService::~SrsGb28181SipService()
|
SrsGb28181SipService::~SrsGb28181SipService()
|
||||||
{
|
{
|
||||||
destroy();
|
destroy();
|
||||||
|
srs_mutex_destroy(lock_session);
|
||||||
|
|
||||||
srs_freep(sip);
|
srs_freep(sip);
|
||||||
srs_freep(config);
|
srs_freep(config);
|
||||||
}
|
}
|
||||||
|
@ -410,7 +466,9 @@ srs_error_t SrsGb28181SipService::on_udp_packet(const sockaddr* from, const int
|
||||||
(char*)&address_string, sizeof(address_string),
|
(char*)&address_string, sizeof(address_string),
|
||||||
(char*)&port_string, sizeof(port_string),
|
(char*)&port_string, sizeof(port_string),
|
||||||
NI_NUMERICHOST|NI_NUMERICSERV)) {
|
NI_NUMERICHOST|NI_NUMERICSERV)) {
|
||||||
return srs_error_new(ERROR_SYSTEM_IP_INVALID, "bad address");
|
// return srs_error_new(ERROR_SYSTEM_IP_INVALID, "bad address");
|
||||||
|
srs_warn("gb28181: bad address");
|
||||||
|
return srs_success;
|
||||||
}
|
}
|
||||||
std::string peer_ip = std::string(address_string);
|
std::string peer_ip = std::string(address_string);
|
||||||
int peer_port = atoi(port_string);
|
int peer_port = atoi(port_string);
|
||||||
|
@ -418,7 +476,10 @@ srs_error_t SrsGb28181SipService::on_udp_packet(const sockaddr* from, const int
|
||||||
std::string recv_msg(buf, nb_buf);
|
std::string recv_msg(buf, nb_buf);
|
||||||
srs_error_t err = on_udp_sip(peer_ip, peer_port, recv_msg, (sockaddr*)from, fromlen);
|
srs_error_t err = on_udp_sip(peer_ip, peer_port, recv_msg, (sockaddr*)from, fromlen);
|
||||||
if (err != srs_success) {
|
if (err != srs_success) {
|
||||||
return srs_error_wrap(err, "process udp");
|
// return srs_error_wrap(err, "process udp");
|
||||||
|
srs_warn("gb28181: process udp");
|
||||||
|
srs_freep(err);
|
||||||
|
return srs_success;
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -456,11 +517,11 @@ srs_error_t SrsGb28181SipService::on_udp_sip(string peer_ip, int peer_port,
|
||||||
return srs_error_new(ERROR_GB28181_SIP_PRASE_FAILED, "register string split");
|
return srs_error_new(ERROR_GB28181_SIP_PRASE_FAILED, "register string split");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (serial.at(0) != config->sip_serial){
|
//if (serial.at(0) != config->sip_serial){
|
||||||
srs_warn("gb28181: client:%s request serial and server serial inconformity(%s:%s)",
|
// srs_warn("gb28181: client:%s request serial and server serial inconformity(%s:%s)",
|
||||||
req->sip_auth_id.c_str(), serial.at(0).c_str(), config->sip_serial.c_str());
|
// req->sip_auth_id.c_str(), serial.at(0).c_str(), config->sip_serial.c_str());
|
||||||
return err;
|
// return err;
|
||||||
}
|
//}
|
||||||
|
|
||||||
srs_trace("gb28181: request client id=%s peer(%s, %d)", req->sip_auth_id.c_str(), peer_ip.c_str(), peer_port);
|
srs_trace("gb28181: request client id=%s peer(%s, %d)", req->sip_auth_id.c_str(), peer_ip.c_str(), peer_port);
|
||||||
srs_trace("gb28181: %s method=%s, uri=%s, version=%s expires=%d",
|
srs_trace("gb28181: %s method=%s, uri=%s, version=%s expires=%d",
|
||||||
|
@ -510,6 +571,24 @@ srs_error_t SrsGb28181SipService::on_udp_sip(string peer_ip, int peer_port,
|
||||||
if (req->device_list_map.size() > 0){
|
if (req->device_list_map.size() > 0){
|
||||||
sip_session->update_device_list(req->device_list_map);
|
sip_session->update_device_list(req->device_list_map);
|
||||||
}
|
}
|
||||||
|
if (!strcasecmp(req->content_type.c_str(),"application/manscdp+xml")
|
||||||
|
&& req->xml_body_map.find("Response@CmdType") != req->xml_body_map.end()
|
||||||
|
&& req->xml_body_map["Response@CmdType"] == "Catalog") {
|
||||||
|
if (req->xml_body_map.find("Response@SumNum") != req->xml_body_map.end()) {
|
||||||
|
sip_session->item_list_sumnum = atoi(req->xml_body_map["Response@SumNum"].c_str());
|
||||||
|
}
|
||||||
|
std::vector<std::map<std::string, std::string> >::iterator it;
|
||||||
|
for (it = req->item_list.begin(); it != req->item_list.end(); ++it) {
|
||||||
|
std::map<std::string, std::string> device = *it;
|
||||||
|
std::map<std::string, std::map<std::string, std::string> >::iterator it2 = sip_session->item_list.find(device["DeviceID"]);
|
||||||
|
if (it2 != sip_session->item_list.end()) {
|
||||||
|
sip_session->item_list.erase(it2);
|
||||||
|
sip_session->item_list[device["DeviceID"]] = device;
|
||||||
|
} else {
|
||||||
|
sip_session->item_list[device["DeviceID"]] = device;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}else if (req->is_invite()) {
|
}else if (req->is_invite()) {
|
||||||
|
@ -538,6 +617,14 @@ srs_error_t SrsGb28181SipService::on_udp_sip(string peer_ip, int peer_port,
|
||||||
srs_trace("gb28181: INVITE response %s client status=%s", req->sip_auth_id.c_str(), req->status.c_str());
|
srs_trace("gb28181: INVITE response %s client status=%s", req->sip_auth_id.c_str(), req->status.c_str());
|
||||||
|
|
||||||
if (req->status == "200") {
|
if (req->status == "200") {
|
||||||
|
srs_trace("gb28181: device unique id is %s@%s", sip_session->session_id().c_str(), req->sip_auth_id.c_str());
|
||||||
|
// if srs is external realm, ssrc is generated by source realm rather than srs
|
||||||
|
// so update ssrc to the y line in source realm '200 OK' response
|
||||||
|
// actually, we should do this all the time
|
||||||
|
if (req->y_ssrc != 0) {
|
||||||
|
_srs_gb28181->update_rtmpmuxer_to_newssrc_by_id(sip_session->session_id()+"@"+req->sip_auth_id, req->y_ssrc);
|
||||||
|
req->y_ssrc = 0;
|
||||||
|
}
|
||||||
send_ack(req, from, fromlen);
|
send_ack(req, from, fromlen);
|
||||||
SrsGb28181Device *device = sip_session->get_device_info(req->sip_auth_id);
|
SrsGb28181Device *device = sip_session->get_device_info(req->sip_auth_id);
|
||||||
if (device){
|
if (device){
|
||||||
|
@ -690,7 +777,7 @@ srs_error_t SrsGb28181SipService::send_invite(SrsSipRequest *req, string ip, i
|
||||||
req->from_realm = config->sip_realm;
|
req->from_realm = config->sip_realm;
|
||||||
|
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
sip->req_invite(ss, req, ip, port, ssrc);
|
sip->req_invite(ss, req, ip, port, ssrc, config->rtp_mux_tcp_enable);
|
||||||
|
|
||||||
sockaddr addr = sip_session->sockaddr_from();
|
sockaddr addr = sip_session->sockaddr_from();
|
||||||
|
|
||||||
|
@ -784,8 +871,6 @@ srs_error_t SrsGb28181SipService::send_sip_raw_data(SrsSipRequest *req, std::st
|
||||||
|
|
||||||
srs_error_t SrsGb28181SipService::send_query_catalog(SrsSipRequest *req)
|
srs_error_t SrsGb28181SipService::send_query_catalog(SrsSipRequest *req)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
|
||||||
|
|
||||||
srs_assert(req);
|
srs_assert(req);
|
||||||
|
|
||||||
SrsGb28181SipSession *sip_session = fetch(req->sip_auth_id);
|
SrsGb28181SipSession *sip_session = fetch(req->sip_auth_id);
|
||||||
|
@ -912,6 +997,31 @@ srs_error_t SrsGb28181SipService::query_sip_session(std::string sid, SrsJsonArr
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsGb28181SipService::query_device_list(std::string sid, SrsJsonArray* arr)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
if (!sid.empty()){
|
||||||
|
SrsGb28181SipSession* sess = fetch(sid);
|
||||||
|
if (!sess){
|
||||||
|
return srs_error_new(ERROR_GB28181_SESSION_IS_NOTEXIST, "sip session not exist");
|
||||||
|
}
|
||||||
|
SrsJsonObject* obj = SrsJsonAny::object();
|
||||||
|
arr->append(obj);
|
||||||
|
sess->dumpItemList(obj);
|
||||||
|
}else {
|
||||||
|
std::map<std::string, SrsGb28181SipSession*>::iterator it;
|
||||||
|
for (it = sessions.begin(); it != sessions.end(); ++it) {
|
||||||
|
SrsGb28181SipSession* sess = it->second;
|
||||||
|
SrsJsonObject* obj = SrsJsonAny::object();
|
||||||
|
arr->append(obj);
|
||||||
|
sess->dumpItemList(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
srs_error_t SrsGb28181SipService::fetch_or_create_sip_session(SrsSipRequest *req, SrsGb28181SipSession** sip_session)
|
srs_error_t SrsGb28181SipService::fetch_or_create_sip_session(SrsSipRequest *req, SrsGb28181SipSession** sip_session)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
@ -935,6 +1045,8 @@ srs_error_t SrsGb28181SipService::fetch_or_create_sip_session(SrsSipRequest *req
|
||||||
|
|
||||||
SrsGb28181SipSession* SrsGb28181SipService::fetch(std::string sid)
|
SrsGb28181SipSession* SrsGb28181SipService::fetch(std::string sid)
|
||||||
{
|
{
|
||||||
|
SrsLocker(lock_session);
|
||||||
|
|
||||||
std::map<std::string, SrsGb28181SipSession*>::iterator it = sessions.find(sid);
|
std::map<std::string, SrsGb28181SipSession*>::iterator it = sessions.find(sid);
|
||||||
if (it == sessions.end()){
|
if (it == sessions.end()){
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -945,6 +1057,8 @@ SrsGb28181SipSession* SrsGb28181SipService::fetch(std::string sid)
|
||||||
|
|
||||||
void SrsGb28181SipService::remove_session(std::string sid)
|
void SrsGb28181SipService::remove_session(std::string sid)
|
||||||
{
|
{
|
||||||
|
SrsLocker(lock_session);
|
||||||
|
|
||||||
std::map<std::string, SrsGb28181SipSession*>::iterator it = sessions.find(sid);
|
std::map<std::string, SrsGb28181SipSession*>::iterator it = sessions.find(sid);
|
||||||
if (it != sessions.end()){
|
if (it != sessions.end()){
|
||||||
//srs_freep(it->second);
|
//srs_freep(it->second);
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
#include <srs_sip_stack.hpp>
|
#include <srs_sip_stack.hpp>
|
||||||
#include <srs_app_gb28181.hpp>
|
#include <srs_app_gb28181.hpp>
|
||||||
#include <srs_app_pithy_print.hpp>
|
#include <srs_app_pithy_print.hpp>
|
||||||
|
#include <srs_service_conn.hpp>
|
||||||
|
|
||||||
class SrsConfDirective;
|
class SrsConfDirective;
|
||||||
class SrsSipRequest;
|
class SrsSipRequest;
|
||||||
|
@ -59,6 +59,7 @@ public:
|
||||||
virtual ~SrsGb28181Device();
|
virtual ~SrsGb28181Device();
|
||||||
public:
|
public:
|
||||||
std::string device_id;
|
std::string device_id;
|
||||||
|
std::string device_name;
|
||||||
std::string device_status;
|
std::string device_status;
|
||||||
SrsGb28181SipSessionStatusType invite_status;
|
SrsGb28181SipSessionStatusType invite_status;
|
||||||
srs_utime_t invite_time;
|
srs_utime_t invite_time;
|
||||||
|
@ -132,10 +133,14 @@ public:
|
||||||
int sip_cseq(){ return _sip_cseq++;}
|
int sip_cseq(){ return _sip_cseq++;}
|
||||||
|
|
||||||
std::string session_id() { return _session_id;}
|
std::string session_id() { return _session_id;}
|
||||||
|
std::map<std::string, std::map<std::string, std::string> > item_list;
|
||||||
|
int item_list_sumnum;
|
||||||
public:
|
public:
|
||||||
void update_device_list(std::map<std::string, std::string> devlist);
|
void update_device_list(std::map<std::string, std::string> devlist);
|
||||||
|
void clear_device_list();
|
||||||
SrsGb28181Device *get_device_info(std::string chid);
|
SrsGb28181Device *get_device_info(std::string chid);
|
||||||
void dumps(SrsJsonObject* obj);
|
void dumps(SrsJsonObject* obj);
|
||||||
|
void dumpItemList(SrsJsonObject* obj);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t serve();
|
virtual srs_error_t serve();
|
||||||
|
@ -143,7 +148,11 @@ public:
|
||||||
// Interface ISrsOneCycleThreadHandler
|
// Interface ISrsOneCycleThreadHandler
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t cycle();
|
virtual srs_error_t cycle();
|
||||||
|
// Interface ISrsConnection.
|
||||||
|
public:
|
||||||
virtual std::string remote_ip();
|
virtual std::string remote_ip();
|
||||||
|
virtual const SrsContextId& get_id();
|
||||||
|
virtual std::string desc();
|
||||||
private:
|
private:
|
||||||
virtual srs_error_t do_cycle();
|
virtual srs_error_t do_cycle();
|
||||||
};
|
};
|
||||||
|
@ -157,6 +166,8 @@ private:
|
||||||
|
|
||||||
std::map<std::string, SrsGb28181SipSession*> sessions;
|
std::map<std::string, SrsGb28181SipSession*> sessions;
|
||||||
std::map<std::string, SrsGb28181SipSession*> sessions_by_callid;
|
std::map<std::string, SrsGb28181SipSession*> sessions_by_callid;
|
||||||
|
|
||||||
|
srs_mutex_t lock_session;
|
||||||
public:
|
public:
|
||||||
SrsGb28181SipService(SrsConfDirective* c);
|
SrsGb28181SipService(SrsConfDirective* c);
|
||||||
virtual ~SrsGb28181SipService();
|
virtual ~SrsGb28181SipService();
|
||||||
|
@ -195,6 +206,7 @@ public:
|
||||||
//
|
//
|
||||||
srs_error_t send_sip_raw_data(SrsSipRequest *req, std::string data);
|
srs_error_t send_sip_raw_data(SrsSipRequest *req, std::string data);
|
||||||
srs_error_t query_sip_session(std::string sid, SrsJsonArray* arr);
|
srs_error_t query_sip_session(std::string sid, SrsJsonArray* arr);
|
||||||
|
srs_error_t query_device_list(std::string sid, SrsJsonArray* arr);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
srs_error_t fetch_or_create_sip_session(SrsSipRequest *req, SrsGb28181SipSession** sess);
|
srs_error_t fetch_or_create_sip_session(SrsSipRequest *req, SrsGb28181SipSession** sess);
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
|
|
||||||
#include <srs_app_hds.hpp>
|
#include <srs_app_hds.hpp>
|
||||||
|
|
||||||
#ifdef SRS_AUTO_HDS
|
#ifdef SRS_HDS
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
|
|
||||||
#include <srs_core.hpp>
|
#include <srs_core.hpp>
|
||||||
|
|
||||||
#ifdef SRS_AUTO_HDS
|
#ifdef SRS_HDS
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
|
|
|
@ -65,10 +65,10 @@ srs_error_t SrsHttpHeartbeat::do_heartbeat()
|
||||||
return srs_error_wrap(err, "http uri parse hartbeart url failed. url=%s", url.c_str());
|
return srs_error_wrap(err, "http uri parse hartbeart url failed. url=%s", url.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ip = "";
|
SrsIPAddress* ip = NULL;
|
||||||
std::string device_id = _srs_config->get_heartbeat_device_id();
|
std::string device_id = _srs_config->get_heartbeat_device_id();
|
||||||
|
|
||||||
vector<string>& ips = srs_get_local_ips();
|
vector<SrsIPAddress*>& ips = srs_get_local_ips();
|
||||||
if (!ips.empty()) {
|
if (!ips.empty()) {
|
||||||
ip = ips[_srs_config->get_stats_network() % (int)ips.size()];
|
ip = ips[_srs_config->get_stats_network() % (int)ips.size()];
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ srs_error_t SrsHttpHeartbeat::do_heartbeat()
|
||||||
SrsAutoFree(SrsJsonObject, obj);
|
SrsAutoFree(SrsJsonObject, obj);
|
||||||
|
|
||||||
obj->set("device_id", SrsJsonAny::str(device_id.c_str()));
|
obj->set("device_id", SrsJsonAny::str(device_id.c_str()));
|
||||||
obj->set("ip", SrsJsonAny::str(ip.c_str()));
|
obj->set("ip", SrsJsonAny::str(ip->ip.c_str()));
|
||||||
|
|
||||||
if (_srs_config->get_heartbeat_summaries()) {
|
if (_srs_config->get_heartbeat_summaries()) {
|
||||||
SrsJsonObject* summaries = SrsJsonAny::object();
|
SrsJsonObject* summaries = SrsJsonAny::object();
|
||||||
|
@ -87,7 +87,7 @@ srs_error_t SrsHttpHeartbeat::do_heartbeat()
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsHttpClient http;
|
SrsHttpClient http;
|
||||||
if ((err = http.initialize(uri.get_host(), uri.get_port())) != srs_success) {
|
if ((err = http.initialize(uri.get_schema(), uri.get_host(), uri.get_port())) != srs_success) {
|
||||||
return srs_error_wrap(err, "init uri=%s", uri.get_url().c_str());
|
return srs_error_wrap(err, "init uri=%s", uri.get_url().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ using namespace std;
|
||||||
|
|
||||||
// drop the segment when duration of ts too small.
|
// drop the segment when duration of ts too small.
|
||||||
// TODO: FIXME: Refine to time unit.
|
// TODO: FIXME: Refine to time unit.
|
||||||
#define SRS_AUTO_HLS_SEGMENT_MIN_DURATION (100 * SRS_UTIME_MILLISECONDS)
|
#define SRS_HLS_SEGMENT_MIN_DURATION (100 * SRS_UTIME_MILLISECONDS)
|
||||||
|
|
||||||
// fragment plus the deviation percent.
|
// fragment plus the deviation percent.
|
||||||
#define SRS_HLS_FLOOR_REAP_PERCENT 0.3
|
#define SRS_HLS_FLOOR_REAP_PERCENT 0.3
|
||||||
|
@ -82,7 +82,7 @@ void SrsHlsSegment::config_cipher(unsigned char* key,unsigned char* iv)
|
||||||
fw->config_cipher(key, iv);
|
fw->config_cipher(key, iv);
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsDvrAsyncCallOnHls::SrsDvrAsyncCallOnHls(int c, SrsRequest* r, string p, string t, string m, string mu, int s, srs_utime_t d)
|
SrsDvrAsyncCallOnHls::SrsDvrAsyncCallOnHls(SrsContextId c, SrsRequest* r, string p, string t, string m, string mu, int s, srs_utime_t d)
|
||||||
{
|
{
|
||||||
req = r->copy();
|
req = r->copy();
|
||||||
cid = c;
|
cid = c;
|
||||||
|
@ -137,7 +137,7 @@ string SrsDvrAsyncCallOnHls::to_string()
|
||||||
return "on_hls: " + path;
|
return "on_hls: " + path;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsDvrAsyncCallOnHlsNotify::SrsDvrAsyncCallOnHlsNotify(int c, SrsRequest* r, string u)
|
SrsDvrAsyncCallOnHlsNotify::SrsDvrAsyncCallOnHlsNotify(SrsContextId c, SrsRequest* r, string u)
|
||||||
{
|
{
|
||||||
cid = c;
|
cid = c;
|
||||||
req = r->copy();
|
req = r->copy();
|
||||||
|
@ -200,6 +200,7 @@ SrsHlsMuxer::SrsHlsMuxer()
|
||||||
accept_floor_ts = 0;
|
accept_floor_ts = 0;
|
||||||
hls_ts_floor = false;
|
hls_ts_floor = false;
|
||||||
max_td = 0;
|
max_td = 0;
|
||||||
|
writer = NULL;
|
||||||
_sequence_no = 0;
|
_sequence_no = 0;
|
||||||
current = NULL;
|
current = NULL;
|
||||||
hls_keys = false;
|
hls_keys = false;
|
||||||
|
@ -214,6 +215,7 @@ SrsHlsMuxer::SrsHlsMuxer()
|
||||||
|
|
||||||
SrsHlsMuxer::~SrsHlsMuxer()
|
SrsHlsMuxer::~SrsHlsMuxer()
|
||||||
{
|
{
|
||||||
|
srs_freep(segments);
|
||||||
srs_freep(current);
|
srs_freep(current);
|
||||||
srs_freep(req);
|
srs_freep(req);
|
||||||
srs_freep(async);
|
srs_freep(async);
|
||||||
|
@ -498,7 +500,7 @@ bool SrsHlsMuxer::is_segment_overflow()
|
||||||
srs_assert(current);
|
srs_assert(current);
|
||||||
|
|
||||||
// to prevent very small segment.
|
// to prevent very small segment.
|
||||||
if (current->duration() < 2 * SRS_AUTO_HLS_SEGMENT_MIN_DURATION) {
|
if (current->duration() < 2 * SRS_HLS_SEGMENT_MIN_DURATION) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -518,7 +520,7 @@ bool SrsHlsMuxer::is_segment_absolutely_overflow()
|
||||||
srs_assert(current);
|
srs_assert(current);
|
||||||
|
|
||||||
// to prevent very small segment.
|
// to prevent very small segment.
|
||||||
if (current->duration() < 2 * SRS_AUTO_HLS_SEGMENT_MIN_DURATION) {
|
if (current->duration() < 2 * SRS_HLS_SEGMENT_MIN_DURATION) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -619,7 +621,7 @@ srs_error_t SrsHlsMuxer::do_segment_close()
|
||||||
// when too small, it maybe not enough data to play.
|
// when too small, it maybe not enough data to play.
|
||||||
// when too large, it maybe timestamp corrupt.
|
// when too large, it maybe timestamp corrupt.
|
||||||
// make the segment more acceptable, when in [min, max_td * 2], it's ok.
|
// make the segment more acceptable, when in [min, max_td * 2], it's ok.
|
||||||
bool matchMinDuration = current->duration() >= SRS_AUTO_HLS_SEGMENT_MIN_DURATION;
|
bool matchMinDuration = current->duration() >= SRS_HLS_SEGMENT_MIN_DURATION;
|
||||||
bool matchMaxDuration = current->duration() <= max_td * 2 * 1000;
|
bool matchMaxDuration = current->duration() <= max_td * 2 * 1000;
|
||||||
if (matchMinDuration && matchMaxDuration) {
|
if (matchMinDuration && matchMaxDuration) {
|
||||||
// use async to call the http hooks, for it will cause thread switch.
|
// use async to call the http hooks, for it will cause thread switch.
|
||||||
|
@ -757,10 +759,11 @@ srs_error_t SrsHlsMuxer::_refresh_m3u8(string m3u8_file)
|
||||||
|
|
||||||
// #EXT-X-MEDIA-SEQUENCE:4294967295\n
|
// #EXT-X-MEDIA-SEQUENCE:4294967295\n
|
||||||
SrsHlsSegment* first = dynamic_cast<SrsHlsSegment*>(segments->first());
|
SrsHlsSegment* first = dynamic_cast<SrsHlsSegment*>(segments->first());
|
||||||
ss << "#EXT-X-MEDIA-SEQUENCE:" << first->sequence_no << SRS_CONSTS_LF;
|
if (first == NULL) {
|
||||||
|
return srs_error_new(ERROR_HLS_WRITE_FAILED, "segments cast");
|
||||||
|
}
|
||||||
|
|
||||||
// iterator shared for td generation and segemnts wrote.
|
ss << "#EXT-X-MEDIA-SEQUENCE:" << first->sequence_no << SRS_CONSTS_LF;
|
||||||
std::vector<SrsHlsSegment*>::iterator it;
|
|
||||||
|
|
||||||
// #EXT-X-TARGETDURATION:4294967295\n
|
// #EXT-X-TARGETDURATION:4294967295\n
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -80,7 +80,7 @@ public:
|
||||||
class SrsDvrAsyncCallOnHls : public ISrsAsyncCallTask
|
class SrsDvrAsyncCallOnHls : public ISrsAsyncCallTask
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
int cid;
|
SrsContextId cid;
|
||||||
std::string path;
|
std::string path;
|
||||||
std::string ts_url;
|
std::string ts_url;
|
||||||
std::string m3u8;
|
std::string m3u8;
|
||||||
|
@ -90,7 +90,7 @@ private:
|
||||||
srs_utime_t duration;
|
srs_utime_t duration;
|
||||||
public:
|
public:
|
||||||
// TODO: FIXME: Use TBN 1000.
|
// TODO: FIXME: Use TBN 1000.
|
||||||
SrsDvrAsyncCallOnHls(int c, SrsRequest* r, std::string p, std::string t, std::string m, std::string mu, int s, srs_utime_t d);
|
SrsDvrAsyncCallOnHls(SrsContextId c, SrsRequest* r, std::string p, std::string t, std::string m, std::string mu, int s, srs_utime_t d);
|
||||||
virtual ~SrsDvrAsyncCallOnHls();
|
virtual ~SrsDvrAsyncCallOnHls();
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t call();
|
virtual srs_error_t call();
|
||||||
|
@ -101,11 +101,11 @@ public:
|
||||||
class SrsDvrAsyncCallOnHlsNotify : public ISrsAsyncCallTask
|
class SrsDvrAsyncCallOnHlsNotify : public ISrsAsyncCallTask
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
int cid;
|
SrsContextId cid;
|
||||||
std::string ts_url;
|
std::string ts_url;
|
||||||
SrsRequest* req;
|
SrsRequest* req;
|
||||||
public:
|
public:
|
||||||
SrsDvrAsyncCallOnHlsNotify(int c, SrsRequest* r, std::string u);
|
SrsDvrAsyncCallOnHlsNotify(SrsContextId c, SrsRequest* r, std::string u);
|
||||||
virtual ~SrsDvrAsyncCallOnHlsNotify();
|
virtual ~SrsDvrAsyncCallOnHlsNotify();
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t call();
|
virtual srs_error_t call();
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <unistd.h>
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
#include <srs_kernel_log.hpp>
|
#include <srs_kernel_log.hpp>
|
||||||
|
@ -46,9 +47,6 @@ using namespace std;
|
||||||
#include <srs_protocol_amf0.hpp>
|
#include <srs_protocol_amf0.hpp>
|
||||||
#include <srs_protocol_utility.hpp>
|
#include <srs_protocol_utility.hpp>
|
||||||
#include <srs_app_coworkers.hpp>
|
#include <srs_app_coworkers.hpp>
|
||||||
#ifdef SRS_AUTO_RTC
|
|
||||||
#include <srs_app_rtc_conn.hpp>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
srs_error_t srs_api_response_jsonp(ISrsHttpResponseWriter* w, string callback, string data)
|
srs_error_t srs_api_response_jsonp(ISrsHttpResponseWriter* w, string callback, string data)
|
||||||
{
|
{
|
||||||
|
@ -196,13 +194,25 @@ srs_error_t SrsGoApiRoot::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage*
|
||||||
SrsAutoFree(SrsJsonObject, obj);
|
SrsAutoFree(SrsJsonObject, obj);
|
||||||
|
|
||||||
obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
||||||
obj->set("server", SrsJsonAny::integer(stat->server_id()));
|
obj->set("server", SrsJsonAny::str(stat->server_id().c_str()));
|
||||||
|
|
||||||
SrsJsonObject* urls = SrsJsonAny::object();
|
SrsJsonObject* urls = SrsJsonAny::object();
|
||||||
obj->set("urls", urls);
|
obj->set("urls", urls);
|
||||||
|
|
||||||
urls->set("api", SrsJsonAny::str("the api root"));
|
urls->set("api", SrsJsonAny::str("the api root"));
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
SrsJsonObject* rtc = SrsJsonAny::object();
|
||||||
|
urls->set("rtc", rtc);
|
||||||
|
|
||||||
|
SrsJsonObject* v1 = SrsJsonAny::object();
|
||||||
|
rtc->set("v1", v1);
|
||||||
|
|
||||||
|
v1->set("play", SrsJsonAny::str("Play stream"));
|
||||||
|
v1->set("publish", SrsJsonAny::str("Publish stream"));
|
||||||
|
v1->set("nack", SrsJsonAny::str("Simulate the NACK"));
|
||||||
|
}
|
||||||
|
|
||||||
return srs_api_response(w, r, obj->dumps());
|
return srs_api_response(w, r, obj->dumps());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,7 +232,7 @@ srs_error_t SrsGoApiApi::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage*
|
||||||
SrsAutoFree(SrsJsonObject, obj);
|
SrsAutoFree(SrsJsonObject, obj);
|
||||||
|
|
||||||
obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
||||||
obj->set("server", SrsJsonAny::integer(stat->server_id()));
|
obj->set("server", SrsJsonAny::str(stat->server_id().c_str()));
|
||||||
|
|
||||||
SrsJsonObject* urls = SrsJsonAny::object();
|
SrsJsonObject* urls = SrsJsonAny::object();
|
||||||
obj->set("urls", urls);
|
obj->set("urls", urls);
|
||||||
|
@ -248,7 +258,7 @@ srs_error_t SrsGoApiV1::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r
|
||||||
SrsAutoFree(SrsJsonObject, obj);
|
SrsAutoFree(SrsJsonObject, obj);
|
||||||
|
|
||||||
obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
||||||
obj->set("server", SrsJsonAny::integer(stat->server_id()));
|
obj->set("server", SrsJsonAny::str(stat->server_id().c_str()));
|
||||||
|
|
||||||
SrsJsonObject* urls = SrsJsonAny::object();
|
SrsJsonObject* urls = SrsJsonAny::object();
|
||||||
obj->set("urls", urls);
|
obj->set("urls", urls);
|
||||||
|
@ -297,7 +307,7 @@ srs_error_t SrsGoApiVersion::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessa
|
||||||
SrsAutoFree(SrsJsonObject, obj);
|
SrsAutoFree(SrsJsonObject, obj);
|
||||||
|
|
||||||
obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
||||||
obj->set("server", SrsJsonAny::integer(stat->server_id()));
|
obj->set("server", SrsJsonAny::str(stat->server_id().c_str()));
|
||||||
|
|
||||||
SrsJsonObject* data = SrsJsonAny::object();
|
SrsJsonObject* data = SrsJsonAny::object();
|
||||||
obj->set("data", data);
|
obj->set("data", data);
|
||||||
|
@ -326,7 +336,7 @@ srs_error_t SrsGoApiSummaries::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMes
|
||||||
SrsAutoFree(SrsJsonObject, obj);
|
SrsAutoFree(SrsJsonObject, obj);
|
||||||
|
|
||||||
obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
||||||
obj->set("server", SrsJsonAny::integer(stat->server_id()));
|
obj->set("server", SrsJsonAny::str(stat->server_id().c_str()));
|
||||||
|
|
||||||
srs_api_dump_summaries(obj);
|
srs_api_dump_summaries(obj);
|
||||||
|
|
||||||
|
@ -349,7 +359,7 @@ srs_error_t SrsGoApiRusages::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessa
|
||||||
SrsAutoFree(SrsJsonObject, obj);
|
SrsAutoFree(SrsJsonObject, obj);
|
||||||
|
|
||||||
obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
||||||
obj->set("server", SrsJsonAny::integer(stat->server_id()));
|
obj->set("server", SrsJsonAny::str(stat->server_id().c_str()));
|
||||||
|
|
||||||
SrsJsonObject* data = SrsJsonAny::object();
|
SrsJsonObject* data = SrsJsonAny::object();
|
||||||
obj->set("data", data);
|
obj->set("data", data);
|
||||||
|
@ -394,7 +404,7 @@ srs_error_t SrsGoApiSelfProcStats::serve_http(ISrsHttpResponseWriter* w, ISrsHtt
|
||||||
SrsAutoFree(SrsJsonObject, obj);
|
SrsAutoFree(SrsJsonObject, obj);
|
||||||
|
|
||||||
obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
||||||
obj->set("server", SrsJsonAny::integer(stat->server_id()));
|
obj->set("server", SrsJsonAny::str(stat->server_id().c_str()));
|
||||||
|
|
||||||
SrsJsonObject* data = SrsJsonAny::object();
|
SrsJsonObject* data = SrsJsonAny::object();
|
||||||
obj->set("data", data);
|
obj->set("data", data);
|
||||||
|
@ -471,7 +481,7 @@ srs_error_t SrsGoApiSystemProcStats::serve_http(ISrsHttpResponseWriter* w, ISrsH
|
||||||
SrsAutoFree(SrsJsonObject, obj);
|
SrsAutoFree(SrsJsonObject, obj);
|
||||||
|
|
||||||
obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
||||||
obj->set("server", SrsJsonAny::integer(stat->server_id()));
|
obj->set("server", SrsJsonAny::str(stat->server_id().c_str()));
|
||||||
|
|
||||||
SrsJsonObject* data = SrsJsonAny::object();
|
SrsJsonObject* data = SrsJsonAny::object();
|
||||||
obj->set("data", data);
|
obj->set("data", data);
|
||||||
|
@ -510,7 +520,7 @@ srs_error_t SrsGoApiMemInfos::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMess
|
||||||
SrsAutoFree(SrsJsonObject, obj);
|
SrsAutoFree(SrsJsonObject, obj);
|
||||||
|
|
||||||
obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
||||||
obj->set("server", SrsJsonAny::integer(stat->server_id()));
|
obj->set("server", SrsJsonAny::str(stat->server_id().c_str()));
|
||||||
|
|
||||||
SrsJsonObject* data = SrsJsonAny::object();
|
SrsJsonObject* data = SrsJsonAny::object();
|
||||||
obj->set("data", data);
|
obj->set("data", data);
|
||||||
|
@ -550,13 +560,13 @@ srs_error_t SrsGoApiAuthors::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessa
|
||||||
SrsAutoFree(SrsJsonObject, obj);
|
SrsAutoFree(SrsJsonObject, obj);
|
||||||
|
|
||||||
obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
||||||
obj->set("server", SrsJsonAny::integer(stat->server_id()));
|
obj->set("server", SrsJsonAny::str(stat->server_id().c_str()));
|
||||||
|
|
||||||
SrsJsonObject* data = SrsJsonAny::object();
|
SrsJsonObject* data = SrsJsonAny::object();
|
||||||
obj->set("data", data);
|
obj->set("data", data);
|
||||||
|
|
||||||
data->set("license", SrsJsonAny::str(RTMP_SIG_SRS_LICENSE));
|
data->set("license", SrsJsonAny::str(RTMP_SIG_SRS_LICENSE));
|
||||||
data->set("contributors", SrsJsonAny::str(SRS_AUTO_CONSTRIBUTORS));
|
data->set("contributors", SrsJsonAny::str(SRS_CONSTRIBUTORS));
|
||||||
|
|
||||||
return srs_api_response(w, r, obj->dumps());
|
return srs_api_response(w, r, obj->dumps());
|
||||||
}
|
}
|
||||||
|
@ -577,22 +587,22 @@ srs_error_t SrsGoApiFeatures::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMess
|
||||||
SrsAutoFree(SrsJsonObject, obj);
|
SrsAutoFree(SrsJsonObject, obj);
|
||||||
|
|
||||||
obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
||||||
obj->set("server", SrsJsonAny::integer(stat->server_id()));
|
obj->set("server", SrsJsonAny::str(stat->server_id().c_str()));
|
||||||
|
|
||||||
SrsJsonObject* data = SrsJsonAny::object();
|
SrsJsonObject* data = SrsJsonAny::object();
|
||||||
obj->set("data", data);
|
obj->set("data", data);
|
||||||
|
|
||||||
data->set("options", SrsJsonAny::str(SRS_AUTO_USER_CONFIGURE));
|
data->set("options", SrsJsonAny::str(SRS_USER_CONFIGURE));
|
||||||
data->set("options2", SrsJsonAny::str(SRS_AUTO_CONFIGURE));
|
data->set("options2", SrsJsonAny::str(SRS_CONFIGURE));
|
||||||
data->set("build", SrsJsonAny::str(SRS_AUTO_BUILD_DATE));
|
data->set("build", SrsJsonAny::str(SRS_BUILD_DATE));
|
||||||
data->set("build2", SrsJsonAny::str(SRS_AUTO_BUILD_TS));
|
data->set("build2", SrsJsonAny::str(SRS_BUILD_TS));
|
||||||
|
|
||||||
SrsJsonObject* features = SrsJsonAny::object();
|
SrsJsonObject* features = SrsJsonAny::object();
|
||||||
data->set("features", features);
|
data->set("features", features);
|
||||||
|
|
||||||
features->set("ssl", SrsJsonAny::boolean(true));
|
features->set("ssl", SrsJsonAny::boolean(true));
|
||||||
features->set("hls", SrsJsonAny::boolean(true));
|
features->set("hls", SrsJsonAny::boolean(true));
|
||||||
#ifdef SRS_AUTO_HDS
|
#ifdef SRS_HDS
|
||||||
features->set("hds", SrsJsonAny::boolean(true));
|
features->set("hds", SrsJsonAny::boolean(true));
|
||||||
#else
|
#else
|
||||||
features->set("hds", SrsJsonAny::boolean(false));
|
features->set("hds", SrsJsonAny::boolean(false));
|
||||||
|
@ -645,7 +655,7 @@ srs_error_t SrsGoApiRequests::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMess
|
||||||
SrsAutoFree(SrsJsonObject, obj);
|
SrsAutoFree(SrsJsonObject, obj);
|
||||||
|
|
||||||
obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
||||||
obj->set("server", SrsJsonAny::integer(stat->server_id()));
|
obj->set("server", SrsJsonAny::str(stat->server_id().c_str()));
|
||||||
|
|
||||||
SrsJsonObject* data = SrsJsonAny::object();
|
SrsJsonObject* data = SrsJsonAny::object();
|
||||||
obj->set("data", data);
|
obj->set("data", data);
|
||||||
|
@ -689,10 +699,10 @@ srs_error_t SrsGoApiVhosts::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessag
|
||||||
|
|
||||||
// path: {pattern}{vhost_id}
|
// path: {pattern}{vhost_id}
|
||||||
// e.g. /api/v1/vhosts/100 pattern= /api/v1/vhosts/, vhost_id=100
|
// e.g. /api/v1/vhosts/100 pattern= /api/v1/vhosts/, vhost_id=100
|
||||||
int vid = r->parse_rest_id(entry->pattern);
|
string vid = r->parse_rest_id(entry->pattern);
|
||||||
SrsStatisticVhost* vhost = NULL;
|
SrsStatisticVhost* vhost = NULL;
|
||||||
|
|
||||||
if (vid > 0 && (vhost = stat->find_vhost(vid)) == NULL) {
|
if (!vid.empty() && (vhost = stat->find_vhost_by_id(vid)) == NULL) {
|
||||||
return srs_api_response_code(w, r, ERROR_RTMP_VHOST_NOT_FOUND);
|
return srs_api_response_code(w, r, ERROR_RTMP_VHOST_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -700,7 +710,7 @@ srs_error_t SrsGoApiVhosts::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessag
|
||||||
SrsAutoFree(SrsJsonObject, obj);
|
SrsAutoFree(SrsJsonObject, obj);
|
||||||
|
|
||||||
obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
||||||
obj->set("server", SrsJsonAny::integer(stat->server_id()));
|
obj->set("server", SrsJsonAny::str(stat->server_id().c_str()));
|
||||||
|
|
||||||
if (r->is_http_get()) {
|
if (r->is_http_get()) {
|
||||||
if (!vhost) {
|
if (!vhost) {
|
||||||
|
@ -745,10 +755,10 @@ srs_error_t SrsGoApiStreams::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessa
|
||||||
|
|
||||||
// path: {pattern}{stream_id}
|
// path: {pattern}{stream_id}
|
||||||
// e.g. /api/v1/streams/100 pattern= /api/v1/streams/, stream_id=100
|
// e.g. /api/v1/streams/100 pattern= /api/v1/streams/, stream_id=100
|
||||||
int sid = r->parse_rest_id(entry->pattern);
|
string sid = r->parse_rest_id(entry->pattern);
|
||||||
|
|
||||||
SrsStatisticStream* stream = NULL;
|
SrsStatisticStream* stream = NULL;
|
||||||
if (sid >= 0 && (stream = stat->find_stream(sid)) == NULL) {
|
if (!sid.empty() && (stream = stat->find_stream(sid)) == NULL) {
|
||||||
return srs_api_response_code(w, r, ERROR_RTMP_STREAM_NOT_FOUND);
|
return srs_api_response_code(w, r, ERROR_RTMP_STREAM_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -756,7 +766,7 @@ srs_error_t SrsGoApiStreams::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessa
|
||||||
SrsAutoFree(SrsJsonObject, obj);
|
SrsAutoFree(SrsJsonObject, obj);
|
||||||
|
|
||||||
obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
||||||
obj->set("server", SrsJsonAny::integer(stat->server_id()));
|
obj->set("server", SrsJsonAny::str(stat->server_id().c_str()));
|
||||||
|
|
||||||
if (r->is_http_get()) {
|
if (r->is_http_get()) {
|
||||||
if (!stream) {
|
if (!stream) {
|
||||||
|
@ -785,314 +795,6 @@ srs_error_t SrsGoApiStreams::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessa
|
||||||
return srs_api_response(w, r, obj->dumps());
|
return srs_api_response(w, r, obj->dumps());
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SRS_AUTO_RTC
|
|
||||||
uint32_t SrsGoApiRtcPlay::ssrc_num = 0;
|
|
||||||
|
|
||||||
SrsGoApiRtcPlay::SrsGoApiRtcPlay(SrsRtcServer* rtc_svr)
|
|
||||||
{
|
|
||||||
rtc_server = rtc_svr;
|
|
||||||
}
|
|
||||||
|
|
||||||
SrsGoApiRtcPlay::~SrsGoApiRtcPlay()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Request:
|
|
||||||
// POST /rtc/v1/play/
|
|
||||||
// {
|
|
||||||
// "sdp":"offer...", "streamurl":"webrtc://r.ossrs.net/live/livestream",
|
|
||||||
// "api":'http...", "clientip":"..."
|
|
||||||
// }
|
|
||||||
// Response:
|
|
||||||
// {"sdp":"answer...", "sid":"..."}
|
|
||||||
// @see https://github.com/rtcdn/rtcdn-draft
|
|
||||||
srs_error_t SrsGoApiRtcPlay::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
|
|
||||||
{
|
|
||||||
srs_error_t err = srs_success;
|
|
||||||
|
|
||||||
SrsJsonObject* res = SrsJsonAny::object();
|
|
||||||
SrsAutoFree(SrsJsonObject, res);
|
|
||||||
|
|
||||||
if ((err = do_serve_http(w, r, res)) != srs_success) {
|
|
||||||
srs_warn("RTC error %s", srs_error_desc(err).c_str()); srs_freep(err);
|
|
||||||
return srs_api_response_code(w, r, SRS_CONSTS_HTTP_BadRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
return srs_api_response(w, r, res->dumps());
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_error_t SrsGoApiRtcPlay::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, SrsJsonObject* res)
|
|
||||||
{
|
|
||||||
srs_error_t err = srs_success;
|
|
||||||
|
|
||||||
// For each RTC session, we use short-term HTTP connection.
|
|
||||||
SrsHttpHeader* hdr = w->header();
|
|
||||||
hdr->set("Connection", "Close");
|
|
||||||
|
|
||||||
// Parse req, the request json object, from body.
|
|
||||||
SrsJsonObject* req = NULL;
|
|
||||||
SrsAutoFree(SrsJsonObject, req);
|
|
||||||
if (true) {
|
|
||||||
string req_json;
|
|
||||||
if ((err = r->body_read_all(req_json)) != srs_success) {
|
|
||||||
return srs_error_wrap(err, "read body");
|
|
||||||
}
|
|
||||||
|
|
||||||
SrsJsonAny* json = SrsJsonAny::loads(req_json);
|
|
||||||
if (!json || !json->is_object()) {
|
|
||||||
return srs_error_wrap(err, "not json");
|
|
||||||
}
|
|
||||||
|
|
||||||
req = json->to_object();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch params from req object.
|
|
||||||
SrsJsonAny* prop = NULL;
|
|
||||||
if ((prop = req->ensure_property_string("sdp")) == NULL) {
|
|
||||||
return srs_error_wrap(err, "not sdp");
|
|
||||||
}
|
|
||||||
string remote_sdp_str = prop->to_str();
|
|
||||||
|
|
||||||
if ((prop = req->ensure_property_string("streamurl")) == NULL) {
|
|
||||||
return srs_error_wrap(err, "not streamurl");
|
|
||||||
}
|
|
||||||
string streamurl = prop->to_str();
|
|
||||||
|
|
||||||
string clientip;
|
|
||||||
if ((prop = req->ensure_property_string("clientip")) != NULL) {
|
|
||||||
clientip = prop->to_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
string api;
|
|
||||||
if ((prop = req->ensure_property_string("api")) != NULL) {
|
|
||||||
api = prop->to_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: FIXME: Parse vhost.
|
|
||||||
// Parse app and stream from streamurl.
|
|
||||||
string app;
|
|
||||||
string stream_name;
|
|
||||||
if (true) {
|
|
||||||
string tcUrl;
|
|
||||||
srs_parse_rtmp_url(streamurl, tcUrl, stream_name);
|
|
||||||
|
|
||||||
int port;
|
|
||||||
string schema, host, vhost, param;
|
|
||||||
srs_discovery_tc_url(tcUrl, schema, host, vhost, app, stream_name, port, param);
|
|
||||||
}
|
|
||||||
|
|
||||||
// For client to specifies the EIP of server.
|
|
||||||
string eip = r->query_get("eip");
|
|
||||||
// For client to specifies whether encrypt by SRTP.
|
|
||||||
string encrypt = r->query_get("encrypt");
|
|
||||||
|
|
||||||
srs_trace("RTC play %s, api=%s, clientip=%s, app=%s, stream=%s, offer=%dB, eip=%s, encrypt=%s",
|
|
||||||
streamurl.c_str(), api.c_str(), clientip.c_str(), app.c_str(), stream_name.c_str(), remote_sdp_str.length(),
|
|
||||||
eip.c_str(), encrypt.c_str());
|
|
||||||
|
|
||||||
// TODO: FIXME: It seems remote_sdp doesn't represents the full SDP information.
|
|
||||||
SrsSdp remote_sdp;
|
|
||||||
if ((err = remote_sdp.parse(remote_sdp_str)) != srs_success) {
|
|
||||||
return srs_error_wrap(err, "parse sdp failed: %s", remote_sdp_str.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((err = check_remote_sdp(remote_sdp)) != srs_success) {
|
|
||||||
return srs_error_wrap(err, "remote sdp check failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
SrsSdp local_sdp;
|
|
||||||
if ((err = exchange_sdp(app, stream_name, remote_sdp, local_sdp)) != srs_success) {
|
|
||||||
return srs_error_wrap(err, "remote sdp have error or unsupport attributes");
|
|
||||||
}
|
|
||||||
|
|
||||||
SrsRequest request;
|
|
||||||
request.app = app;
|
|
||||||
request.stream = stream_name;
|
|
||||||
|
|
||||||
// TODO: FIXME: Parse vhost.
|
|
||||||
// discovery vhost, resolve the vhost from config
|
|
||||||
SrsConfDirective* parsed_vhost = _srs_config->get_vhost("");
|
|
||||||
if (parsed_vhost) {
|
|
||||||
request.vhost = parsed_vhost->arg0();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: FIXME: Maybe need a better name?
|
|
||||||
// TODO: FIXME: When server enabled, but vhost disabled, should report error.
|
|
||||||
SrsRtcSession* rtc_session = rtc_server->create_rtc_session(request, remote_sdp, local_sdp, eip);
|
|
||||||
if (encrypt.empty()) {
|
|
||||||
rtc_session->set_encrypt(_srs_config->get_rtc_server_encrypt());
|
|
||||||
} else {
|
|
||||||
rtc_session->set_encrypt(encrypt != "false");
|
|
||||||
}
|
|
||||||
|
|
||||||
ostringstream os;
|
|
||||||
if ((err = local_sdp.encode(os)) != srs_success) {
|
|
||||||
return srs_error_wrap(err, "encode sdp");
|
|
||||||
}
|
|
||||||
|
|
||||||
string local_sdp_str = os.str();
|
|
||||||
|
|
||||||
srs_verbose("local_sdp=%s", local_sdp_str.c_str());
|
|
||||||
|
|
||||||
res->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
|
||||||
res->set("server", SrsJsonAny::integer(SrsStatistic::instance()->server_id()));
|
|
||||||
|
|
||||||
// TODO: add candidates in response json?
|
|
||||||
|
|
||||||
res->set("sdp", SrsJsonAny::str(local_sdp_str.c_str()));
|
|
||||||
res->set("sessionid", SrsJsonAny::str(rtc_session->id().c_str()));
|
|
||||||
|
|
||||||
srs_trace("RTC sid=%s, offer=%dB, answer=%dB", rtc_session->id().c_str(), remote_sdp_str.length(), local_sdp_str.length());
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_error_t SrsGoApiRtcPlay::check_remote_sdp(const SrsSdp& remote_sdp)
|
|
||||||
{
|
|
||||||
srs_error_t err = srs_success;
|
|
||||||
|
|
||||||
if (remote_sdp.group_policy_ != "BUNDLE") {
|
|
||||||
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "now only support BUNDLE, group policy=%s", remote_sdp.group_policy_.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remote_sdp.media_descs_.empty()) {
|
|
||||||
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no media descriptions");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (std::vector<SrsMediaDesc>::const_iterator iter = remote_sdp.media_descs_.begin(); iter != remote_sdp.media_descs_.end(); ++iter) {
|
|
||||||
if (iter->type_ != "audio" && iter->type_ != "video") {
|
|
||||||
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "unsupport media type=%s", iter->type_.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! iter->rtcp_mux_) {
|
|
||||||
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "now only suppor rtcp-mux");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (std::vector<SrsMediaPayloadType>::const_iterator iter_media = iter->payload_types_.begin(); iter_media != iter->payload_types_.end(); ++iter_media) {
|
|
||||||
if (iter->sendonly_) {
|
|
||||||
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "play API only support sendrecv/recvonly");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_error_t SrsGoApiRtcPlay::exchange_sdp(const std::string& app, const std::string& stream, const SrsSdp& remote_sdp, SrsSdp& local_sdp)
|
|
||||||
{
|
|
||||||
srs_error_t err = srs_success;
|
|
||||||
local_sdp.version_ = "0";
|
|
||||||
|
|
||||||
local_sdp.username_ = RTMP_SIG_SRS_SERVER;
|
|
||||||
local_sdp.session_id_ = srs_int2str((int64_t)this);
|
|
||||||
local_sdp.session_version_ = "2";
|
|
||||||
local_sdp.nettype_ = "IN";
|
|
||||||
local_sdp.addrtype_ = "IP4";
|
|
||||||
local_sdp.unicast_address_ = "0.0.0.0";
|
|
||||||
|
|
||||||
local_sdp.session_name_ = "live_play_session";
|
|
||||||
|
|
||||||
local_sdp.msid_semantic_ = "WMS";
|
|
||||||
local_sdp.msids_.push_back(app + "/" + stream);
|
|
||||||
|
|
||||||
local_sdp.group_policy_ = "BUNDLE";
|
|
||||||
|
|
||||||
for (size_t i = 0; i < remote_sdp.media_descs_.size(); ++i) {
|
|
||||||
const SrsMediaDesc& remote_media_desc = remote_sdp.media_descs_[i];
|
|
||||||
|
|
||||||
if (remote_media_desc.is_audio()) {
|
|
||||||
local_sdp.media_descs_.push_back(SrsMediaDesc("audio"));
|
|
||||||
} else if (remote_media_desc.is_video()) {
|
|
||||||
local_sdp.media_descs_.push_back(SrsMediaDesc("video"));
|
|
||||||
}
|
|
||||||
|
|
||||||
SrsMediaDesc& local_media_desc = local_sdp.media_descs_.back();
|
|
||||||
|
|
||||||
if (remote_media_desc.is_audio()) {
|
|
||||||
// TODO: check opus format specific param
|
|
||||||
std::vector<SrsMediaPayloadType> payloads = remote_media_desc.find_media_with_encoding_name("opus");
|
|
||||||
for (std::vector<SrsMediaPayloadType>::iterator iter = payloads.begin(); iter != payloads.end(); ++iter) {
|
|
||||||
// Only choose one match opus codec.
|
|
||||||
local_media_desc.payload_types_.push_back(*iter);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (local_media_desc.payload_types_.empty()) {
|
|
||||||
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no found valid opus payload type");
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (remote_media_desc.is_video()) {
|
|
||||||
std::deque<SrsMediaPayloadType> backup_payloads;
|
|
||||||
std::vector<SrsMediaPayloadType> payloads = remote_media_desc.find_media_with_encoding_name("H264");
|
|
||||||
for (std::vector<SrsMediaPayloadType>::iterator iter = payloads.begin(); iter != payloads.end(); ++iter) {
|
|
||||||
if (iter->format_specific_param_.empty()) {
|
|
||||||
backup_payloads.push_front(*iter);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
H264SpecificParam h264_param;
|
|
||||||
if ((err = parse_h264_fmtp(iter->format_specific_param_, h264_param)) != srs_success) {
|
|
||||||
srs_error_reset(err); continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to pick the "best match" H.264 payload type.
|
|
||||||
if (h264_param.packetization_mode == "1" && h264_param.level_asymmerty_allow == "1") {
|
|
||||||
// Only choose first match H.264 payload type.
|
|
||||||
local_media_desc.payload_types_.push_back(*iter);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
backup_payloads.push_back(*iter);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try my best to pick at least one media payload type.
|
|
||||||
if (local_media_desc.payload_types_.empty() && ! backup_payloads.empty()) {
|
|
||||||
srs_warn("choose backup H.264 payload type=%d", backup_payloads.front().payload_type_);
|
|
||||||
local_media_desc.payload_types_.push_back(backup_payloads.front());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (local_media_desc.payload_types_.empty()) {
|
|
||||||
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no found valid H.264 payload type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
local_media_desc.mid_ = remote_media_desc.mid_;
|
|
||||||
local_sdp.groups_.push_back(local_media_desc.mid_);
|
|
||||||
|
|
||||||
local_media_desc.port_ = 9;
|
|
||||||
local_media_desc.protos_ = "UDP/TLS/RTP/SAVPF";
|
|
||||||
|
|
||||||
if (remote_media_desc.session_info_.setup_ == "active") {
|
|
||||||
local_media_desc.session_info_.setup_ = "passive";
|
|
||||||
} else if (remote_media_desc.session_info_.setup_ == "passive") {
|
|
||||||
local_media_desc.session_info_.setup_ = "active";
|
|
||||||
} else if (remote_media_desc.session_info_.setup_ == "actpass") {
|
|
||||||
local_media_desc.session_info_.setup_ = "passive";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remote_media_desc.sendonly_) {
|
|
||||||
local_media_desc.recvonly_ = true;
|
|
||||||
} else if (remote_media_desc.recvonly_) {
|
|
||||||
local_media_desc.sendonly_ = true;
|
|
||||||
} else if (remote_media_desc.sendrecv_) {
|
|
||||||
local_media_desc.sendrecv_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
local_media_desc.rtcp_mux_ = true;
|
|
||||||
local_media_desc.rtcp_rsize_ = true;
|
|
||||||
|
|
||||||
SrsSSRCInfo ssrc_info;
|
|
||||||
ssrc_info.ssrc_ = ++ssrc_num;
|
|
||||||
// TODO:use formated cname
|
|
||||||
ssrc_info.cname_ = "test_sdp_cname";
|
|
||||||
local_media_desc.ssrc_infos_.push_back(ssrc_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
SrsGoApiClients::SrsGoApiClients()
|
SrsGoApiClients::SrsGoApiClients()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -1109,10 +811,10 @@ srs_error_t SrsGoApiClients::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessa
|
||||||
|
|
||||||
// path: {pattern}{client_id}
|
// path: {pattern}{client_id}
|
||||||
// e.g. /api/v1/clients/100 pattern= /api/v1/clients/, client_id=100
|
// e.g. /api/v1/clients/100 pattern= /api/v1/clients/, client_id=100
|
||||||
int cid = r->parse_rest_id(entry->pattern);
|
string client_id = r->parse_rest_id(entry->pattern);
|
||||||
|
|
||||||
SrsStatisticClient* client = NULL;
|
SrsStatisticClient* client = NULL;
|
||||||
if (cid >= 0 && (client = stat->find_client(cid)) == NULL) {
|
if (!client_id.empty() && (client = stat->find_client(client_id)) == NULL) {
|
||||||
return srs_api_response_code(w, r, ERROR_RTMP_CLIENT_NOT_FOUND);
|
return srs_api_response_code(w, r, ERROR_RTMP_CLIENT_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1120,7 +822,7 @@ srs_error_t SrsGoApiClients::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessa
|
||||||
SrsAutoFree(SrsJsonObject, obj);
|
SrsAutoFree(SrsJsonObject, obj);
|
||||||
|
|
||||||
obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
||||||
obj->set("server", SrsJsonAny::integer(stat->server_id()));
|
obj->set("server", SrsJsonAny::str(stat->server_id().c_str()));
|
||||||
|
|
||||||
if (r->is_http_get()) {
|
if (r->is_http_get()) {
|
||||||
if (!client) {
|
if (!client) {
|
||||||
|
@ -1152,7 +854,7 @@ srs_error_t SrsGoApiClients::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessa
|
||||||
}
|
}
|
||||||
|
|
||||||
client->conn->expire();
|
client->conn->expire();
|
||||||
srs_warn("kickoff client id=%d ok", cid);
|
srs_warn("kickoff client id=%s ok", client_id.c_str());
|
||||||
} else {
|
} else {
|
||||||
return srs_go_http_error(w, SRS_CONSTS_HTTP_MethodNotAllowed);
|
return srs_go_http_error(w, SRS_CONSTS_HTTP_MethodNotAllowed);
|
||||||
}
|
}
|
||||||
|
@ -1633,7 +1335,7 @@ srs_error_t SrsGoApiPerf::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage*
|
||||||
|
|
||||||
p->set("target", SrsJsonAny::str(target.c_str()));
|
p->set("target", SrsJsonAny::str(target.c_str()));
|
||||||
p->set("reset", SrsJsonAny::str(reset.c_str()));
|
p->set("reset", SrsJsonAny::str(reset.c_str()));
|
||||||
p->set("help", SrsJsonAny::str("?target=avframes|rtc|rtp|gso|writev_iovs|sendmmsg|bytes|dropped"));
|
p->set("help", SrsJsonAny::str("?target=avframes|rtc|rtp|writev_iovs|bytes"));
|
||||||
p->set("help2", SrsJsonAny::str("?reset=all"));
|
p->set("help2", SrsJsonAny::str("?reset=all"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1669,24 +1371,6 @@ srs_error_t SrsGoApiPerf::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target.empty() || target == "gso") {
|
|
||||||
SrsJsonObject* p = SrsJsonAny::object();
|
|
||||||
data->set("gso", p);
|
|
||||||
if ((err = stat->dumps_perf_gso(p)) != srs_success) {
|
|
||||||
int code = srs_error_code(err); srs_error_reset(err);
|
|
||||||
return srs_api_response_code(w, r, code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (target.empty() || target == "sendmmsg") {
|
|
||||||
SrsJsonObject* p = SrsJsonAny::object();
|
|
||||||
data->set("sendmmsg", p);
|
|
||||||
if ((err = stat->dumps_perf_sendmmsg(p)) != srs_success) {
|
|
||||||
int code = srs_error_code(err); srs_error_reset(err);
|
|
||||||
return srs_api_response_code(w, r, code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (target.empty() || target == "writev_iovs") {
|
if (target.empty() || target == "writev_iovs") {
|
||||||
SrsJsonObject* p = SrsJsonAny::object();
|
SrsJsonObject* p = SrsJsonAny::object();
|
||||||
data->set("writev_iovs", p);
|
data->set("writev_iovs", p);
|
||||||
|
@ -1705,15 +1389,6 @@ srs_error_t SrsGoApiPerf::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target.empty() || target == "dropped") {
|
|
||||||
SrsJsonObject* p = SrsJsonAny::object();
|
|
||||||
data->set("dropped", p);
|
|
||||||
if ((err = stat->dumps_perf_dropped(p)) != srs_success) {
|
|
||||||
int code = srs_error_code(err); srs_error_reset(err);
|
|
||||||
return srs_api_response_code(w, r, code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return srs_api_response(w, r, obj->dumps());
|
return srs_api_response(w, r, obj->dumps());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1730,7 +1405,7 @@ srs_error_t SrsGoApiError::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage
|
||||||
return srs_api_response_code(w, r, 100);
|
return srs_api_response_code(w, r, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SRS_AUTO_GB28181
|
#ifdef SRS_GB28181
|
||||||
SrsGoApiGb28181::SrsGoApiGb28181()
|
SrsGoApiGb28181::SrsGoApiGb28181()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -1801,11 +1476,12 @@ srs_error_t SrsGoApiGb28181::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMe
|
||||||
return srs_api_response(w, r, obj->dumps());
|
return srs_api_response(w, r, obj->dumps());
|
||||||
|
|
||||||
} else if(action == "delete_channel"){
|
} else if(action == "delete_channel"){
|
||||||
if (id.empty()){
|
string chid = r->query_get("chid");
|
||||||
return srs_error_new(ERROR_GB28181_VALUE_EMPTY, "no id");
|
if (id.empty() || chid.empty()){
|
||||||
|
return srs_error_new(ERROR_GB28181_VALUE_EMPTY, "no id or chid");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = _srs_gb28181->delete_stream_channel(id)) != srs_success) {
|
if ((err = _srs_gb28181->delete_stream_channel(id, chid)) != srs_success) {
|
||||||
return srs_error_wrap(err, "delete stream channel");
|
return srs_error_wrap(err, "delete stream channel");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1898,6 +1574,16 @@ srs_error_t SrsGoApiGb28181::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMe
|
||||||
}
|
}
|
||||||
|
|
||||||
return srs_api_response_code(w, r, 0);
|
return srs_api_response_code(w, r, 0);
|
||||||
|
} else if(action == "sip_query_devicelist"){
|
||||||
|
SrsJsonArray* arr = SrsJsonAny::array();
|
||||||
|
data->set("PlatformID", SrsJsonAny::str(_srs_gb28181->get_gb28181_config_ptr()->sip_serial.c_str()));
|
||||||
|
data->set("DeviceList", arr);
|
||||||
|
|
||||||
|
if ((err = _srs_gb28181->query_device_list("", arr)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "query device list");
|
||||||
|
}
|
||||||
|
|
||||||
|
return srs_api_response(w, r, obj->dumps());
|
||||||
} else if(action == "sip_query_session"){
|
} else if(action == "sip_query_session"){
|
||||||
SrsJsonArray* arr = SrsJsonAny::array();
|
SrsJsonArray* arr = SrsJsonAny::array();
|
||||||
data->set("sessions", arr);
|
data->set("sessions", arr);
|
||||||
|
@ -1913,7 +1599,7 @@ srs_error_t SrsGoApiGb28181::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMe
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRS_AUTO_GPERF
|
#ifdef SRS_GPERF
|
||||||
#include <gperftools/malloc_extension.h>
|
#include <gperftools/malloc_extension.h>
|
||||||
|
|
||||||
SrsGoApiTcmalloc::SrsGoApiTcmalloc()
|
SrsGoApiTcmalloc::SrsGoApiTcmalloc()
|
||||||
|
@ -1999,126 +1685,149 @@ srs_error_t SrsGoApiTcmalloc::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMess
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SrsHttpApi::SrsHttpApi(IConnectionManager* cm, srs_netfd_t fd, SrsHttpServeMux* m, string cip)
|
SrsHttpApi::SrsHttpApi(bool https, ISrsResourceManager* cm, srs_netfd_t fd, SrsHttpServeMux* m, string cip, int port)
|
||||||
: SrsConnection(cm, fd, cip)
|
|
||||||
{
|
{
|
||||||
mux = m;
|
// Create a identify for this client.
|
||||||
cors = new SrsHttpCorsMux();
|
_srs_context->set_id(_srs_context->generate_id());
|
||||||
parser = new SrsHttpParser();
|
|
||||||
|
manager = cm;
|
||||||
|
skt = new SrsTcpConnection(fd);
|
||||||
|
|
||||||
|
if (https) {
|
||||||
|
ssl = new SrsSslConnection(skt);
|
||||||
|
conn = new SrsHttpConn(this, ssl, m, cip, port);
|
||||||
|
} else {
|
||||||
|
ssl = NULL;
|
||||||
|
conn = new SrsHttpConn(this, skt, m, cip, port);
|
||||||
|
}
|
||||||
|
|
||||||
_srs_config->subscribe(this);
|
_srs_config->subscribe(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsHttpApi::~SrsHttpApi()
|
SrsHttpApi::~SrsHttpApi()
|
||||||
{
|
{
|
||||||
srs_freep(parser);
|
|
||||||
srs_freep(cors);
|
|
||||||
|
|
||||||
_srs_config->unsubscribe(this);
|
_srs_config->unsubscribe(this);
|
||||||
|
|
||||||
|
srs_freep(conn);
|
||||||
|
srs_freep(ssl);
|
||||||
|
srs_freep(skt);
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsHttpApi::on_start()
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
if ((err = conn->set_jsonp(true)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "set jsonp");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ssl) {
|
||||||
|
srs_utime_t starttime = srs_update_system_time();
|
||||||
|
string crt_file = _srs_config->get_https_api_ssl_cert();
|
||||||
|
string key_file = _srs_config->get_https_api_ssl_key();
|
||||||
|
if ((err = ssl->handshake(key_file, crt_file)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "handshake");
|
||||||
|
}
|
||||||
|
|
||||||
|
int cost = srsu2msi(srs_update_system_time() - starttime);
|
||||||
|
srs_trace("https: api server done, use key %s and cert %s, cost=%dms",
|
||||||
|
key_file.c_str(), crt_file.c_str(), cost);
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsHttpApi::on_http_message(ISrsHttpMessage* r, SrsHttpResponseWriter* w)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
// After parsed the message, set the schema to https.
|
||||||
|
if (ssl) {
|
||||||
|
SrsHttpMessage* hm = dynamic_cast<SrsHttpMessage*>(r);
|
||||||
|
hm->set_https(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: For each API session, we use short-term HTTP connection.
|
||||||
|
//SrsHttpHeader* hdr = w->header();
|
||||||
|
//hdr->set("Connection", "Close");
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsHttpApi::on_message_done(ISrsHttpMessage* r, SrsHttpResponseWriter* w)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
// read all rest bytes in request body.
|
||||||
|
char buf[SRS_HTTP_READ_CACHE_BYTES];
|
||||||
|
ISrsHttpResponseReader* br = r->body_reader();
|
||||||
|
while (!br->eof()) {
|
||||||
|
if ((err = br->read(buf, SRS_HTTP_READ_CACHE_BYTES, NULL)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "read response");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsHttpApi::on_conn_done(srs_error_t r0)
|
||||||
|
{
|
||||||
|
// Because we use manager to manage this object,
|
||||||
|
// not the http connection object, so we must remove it here.
|
||||||
|
manager->remove(this);
|
||||||
|
|
||||||
|
// For HTTP-API timeout, we think it's done successfully,
|
||||||
|
// because there may be no request or response for HTTP-API.
|
||||||
|
if (srs_error_code(r0) == ERROR_SOCKET_TIMEOUT) {
|
||||||
|
srs_freep(r0);
|
||||||
|
return srs_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SrsHttpApi::desc()
|
||||||
|
{
|
||||||
|
if (ssl) {
|
||||||
|
return "HttpsConn";
|
||||||
|
}
|
||||||
|
return "HttpConn";
|
||||||
}
|
}
|
||||||
|
|
||||||
void SrsHttpApi::remark(int64_t* in, int64_t* out)
|
void SrsHttpApi::remark(int64_t* in, int64_t* out)
|
||||||
{
|
{
|
||||||
// TODO: FIXME: implements it
|
conn->remark(in, out);
|
||||||
}
|
|
||||||
|
|
||||||
srs_error_t SrsHttpApi::do_cycle()
|
|
||||||
{
|
|
||||||
srs_error_t err = srs_success;
|
|
||||||
|
|
||||||
srs_trace("API server client, ip=%s", ip.c_str());
|
|
||||||
|
|
||||||
// initialize parser
|
|
||||||
if ((err = parser->initialize(HTTP_REQUEST, true)) != srs_success) {
|
|
||||||
return srs_error_wrap(err, "init parser");
|
|
||||||
}
|
|
||||||
|
|
||||||
// set the recv timeout, for some clients never disconnect the connection.
|
|
||||||
// @see https://github.com/ossrs/srs/issues/398
|
|
||||||
skt->set_recv_timeout(SRS_HTTP_RECV_TIMEOUT);
|
|
||||||
|
|
||||||
// initialize the cors, which will proxy to mux.
|
|
||||||
bool crossdomain_enabled = _srs_config->get_http_api_crossdomain();
|
|
||||||
if ((err = cors->initialize(mux, crossdomain_enabled)) != srs_success) {
|
|
||||||
return srs_error_wrap(err, "init cors");
|
|
||||||
}
|
|
||||||
|
|
||||||
// process http messages.
|
|
||||||
while ((err = trd->pull()) == srs_success) {
|
|
||||||
ISrsHttpMessage* req = NULL;
|
|
||||||
|
|
||||||
// get a http message
|
|
||||||
if ((err = parser->parse_message(skt, &req)) != srs_success) {
|
|
||||||
// For HTTP timeout, we think it's ok.
|
|
||||||
if (srs_error_code(err) == ERROR_SOCKET_TIMEOUT) {
|
|
||||||
srs_freep(err);
|
|
||||||
return srs_error_wrap(srs_success, "http api timeout");
|
|
||||||
}
|
|
||||||
return srs_error_wrap(err, "parse message");
|
|
||||||
}
|
|
||||||
|
|
||||||
// if SUCCESS, always NOT-NULL.
|
|
||||||
// always free it in this scope.
|
|
||||||
srs_assert(req);
|
|
||||||
SrsAutoFree(ISrsHttpMessage, req);
|
|
||||||
|
|
||||||
// Attach owner connection to message.
|
|
||||||
SrsHttpMessage* hreq = (SrsHttpMessage*)req;
|
|
||||||
hreq->set_connection(this);
|
|
||||||
|
|
||||||
// ok, handle http request.
|
|
||||||
SrsHttpResponseWriter writer(skt);
|
|
||||||
if ((err = process_request(&writer, req)) != srs_success) {
|
|
||||||
return srs_error_wrap(err, "process request");
|
|
||||||
}
|
|
||||||
|
|
||||||
// read all rest bytes in request body.
|
|
||||||
char buf[SRS_HTTP_READ_CACHE_BYTES];
|
|
||||||
ISrsHttpResponseReader* br = req->body_reader();
|
|
||||||
while (!br->eof()) {
|
|
||||||
if ((err = br->read(buf, SRS_HTTP_READ_CACHE_BYTES, NULL)) != srs_success) {
|
|
||||||
return srs_error_wrap(err, "read response");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// donot keep alive, disconnect it.
|
|
||||||
// @see https://github.com/ossrs/srs/issues/399
|
|
||||||
if (!req->is_keep_alive()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_error_t SrsHttpApi::process_request(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
|
|
||||||
{
|
|
||||||
srs_error_t err = srs_success;
|
|
||||||
|
|
||||||
SrsHttpMessage* hm = dynamic_cast<SrsHttpMessage*>(r);
|
|
||||||
srs_assert(hm);
|
|
||||||
|
|
||||||
srs_trace("HTTP API %s %s, content-length=%" PRId64 ", chunked=%d/%d",
|
|
||||||
r->method_str().c_str(), r->url().c_str(), r->content_length(),
|
|
||||||
hm->is_chunked(), hm->is_infinite_chunked());
|
|
||||||
|
|
||||||
// use cors server mux to serve http request, which will proxy to mux.
|
|
||||||
if ((err = cors->serve_http(w, r)) != srs_success) {
|
|
||||||
return srs_error_wrap(err, "mux serve");
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsHttpApi::on_reload_http_api_crossdomain()
|
srs_error_t SrsHttpApi::on_reload_http_api_crossdomain()
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
bool v = _srs_config->get_http_api_crossdomain();
|
||||||
|
return conn->set_crossdomain_enabled(v);
|
||||||
bool crossdomain_enabled = _srs_config->get_http_api_crossdomain();
|
}
|
||||||
if ((err = cors->initialize(mux, crossdomain_enabled)) != srs_success) {
|
|
||||||
return srs_error_wrap(err, "reload");
|
srs_error_t SrsHttpApi::start()
|
||||||
}
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
return err;
|
|
||||||
|
bool v = _srs_config->get_http_api_crossdomain();
|
||||||
|
if ((err = conn->set_crossdomain_enabled(v)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "set cors=%d", v);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = skt->initialize()) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "init socket");
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
string SrsHttpApi::remote_ip()
|
||||||
|
{
|
||||||
|
return conn->remote_ip();
|
||||||
|
}
|
||||||
|
|
||||||
|
const SrsContextId& SrsHttpApi::get_id()
|
||||||
|
{
|
||||||
|
return conn->get_id();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,6 @@
|
||||||
|
|
||||||
#include <srs_core.hpp>
|
#include <srs_core.hpp>
|
||||||
|
|
||||||
class SrsStSocket;
|
|
||||||
class ISrsHttpMessage;
|
class ISrsHttpMessage;
|
||||||
class SrsHttpParser;
|
class SrsHttpParser;
|
||||||
class SrsHttpHandler;
|
class SrsHttpHandler;
|
||||||
|
@ -34,11 +33,21 @@ class SrsServer;
|
||||||
class SrsRtcServer;
|
class SrsRtcServer;
|
||||||
class SrsJsonObject;
|
class SrsJsonObject;
|
||||||
class SrsSdp;
|
class SrsSdp;
|
||||||
|
class SrsRequest;
|
||||||
|
class ISrsHttpResponseWriter;
|
||||||
|
class SrsHttpConn;
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include <srs_app_st.hpp>
|
#include <srs_app_st.hpp>
|
||||||
#include <srs_app_conn.hpp>
|
#include <srs_app_conn.hpp>
|
||||||
#include <srs_http_stack.hpp>
|
#include <srs_http_stack.hpp>
|
||||||
#include <srs_app_reload.hpp>
|
#include <srs_app_reload.hpp>
|
||||||
|
#include <srs_app_http_conn.hpp>
|
||||||
|
|
||||||
|
extern srs_error_t srs_api_response(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string json);
|
||||||
|
extern srs_error_t srs_api_response_code(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, int code);
|
||||||
|
extern srs_error_t srs_api_response_code(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, srs_error_t code);
|
||||||
|
|
||||||
// For http root.
|
// For http root.
|
||||||
class SrsGoApiRoot : public ISrsHttpHandler
|
class SrsGoApiRoot : public ISrsHttpHandler
|
||||||
|
@ -167,25 +176,6 @@ public:
|
||||||
virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
|
virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef SRS_AUTO_RTC
|
|
||||||
class SrsGoApiRtcPlay : public ISrsHttpHandler
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static uint32_t ssrc_num;
|
|
||||||
private:
|
|
||||||
SrsRtcServer* rtc_server;
|
|
||||||
public:
|
|
||||||
SrsGoApiRtcPlay(SrsRtcServer* rtc_svr);
|
|
||||||
virtual ~SrsGoApiRtcPlay();
|
|
||||||
public:
|
|
||||||
virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
|
|
||||||
private:
|
|
||||||
virtual srs_error_t do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, SrsJsonObject* res);
|
|
||||||
srs_error_t exchange_sdp(const std::string& app, const std::string& stream, const SrsSdp& remote_sdp, SrsSdp& local_sdp);
|
|
||||||
srs_error_t check_remote_sdp(const SrsSdp& remote_sdp);
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class SrsGoApiClients : public ISrsHttpHandler
|
class SrsGoApiClients : public ISrsHttpHandler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -241,7 +231,7 @@ public:
|
||||||
virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
|
virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef SRS_AUTO_GB28181
|
#ifdef SRS_GB28181
|
||||||
class SrsGoApiGb28181 : public ISrsHttpHandler
|
class SrsGoApiGb28181 : public ISrsHttpHandler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -254,7 +244,7 @@ private:
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRS_AUTO_GPERF
|
#ifdef SRS_GPERF
|
||||||
class SrsGoApiTcmalloc : public ISrsHttpHandler
|
class SrsGoApiTcmalloc : public ISrsHttpHandler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -265,25 +255,49 @@ public:
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class SrsHttpApi : virtual public SrsConnection, virtual public ISrsReloadHandler
|
// Handle the HTTP API request.
|
||||||
|
class SrsHttpApi : virtual public ISrsStartableConneciton, virtual public ISrsHttpConnOwner
|
||||||
|
, virtual public ISrsReloadHandler
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
SrsHttpParser* parser;
|
// The manager object to manage the connection.
|
||||||
SrsHttpCorsMux* cors;
|
ISrsResourceManager* manager;
|
||||||
SrsHttpServeMux* mux;
|
SrsTcpConnection* skt;
|
||||||
|
SrsSslConnection* ssl;
|
||||||
|
SrsHttpConn* conn;
|
||||||
public:
|
public:
|
||||||
SrsHttpApi(IConnectionManager* cm, srs_netfd_t fd, SrsHttpServeMux* m, std::string cip);
|
SrsHttpApi(bool https, ISrsResourceManager* cm, srs_netfd_t fd, SrsHttpServeMux* m, std::string cip, int port);
|
||||||
virtual ~SrsHttpApi();
|
virtual ~SrsHttpApi();
|
||||||
|
// Interface ISrsHttpConnOwner.
|
||||||
|
public:
|
||||||
|
virtual srs_error_t on_start();
|
||||||
|
virtual srs_error_t on_http_message(ISrsHttpMessage* r, SrsHttpResponseWriter* w);
|
||||||
|
virtual srs_error_t on_message_done(ISrsHttpMessage* r, SrsHttpResponseWriter* w);
|
||||||
|
virtual srs_error_t on_conn_done(srs_error_t r0);
|
||||||
|
// Interface ISrsResource.
|
||||||
|
public:
|
||||||
|
virtual std::string desc();
|
||||||
// Interface ISrsKbpsDelta
|
// Interface ISrsKbpsDelta
|
||||||
public:
|
public:
|
||||||
virtual void remark(int64_t* in, int64_t* out);
|
virtual void remark(int64_t* in, int64_t* out);
|
||||||
protected:
|
|
||||||
virtual srs_error_t do_cycle();
|
|
||||||
private:
|
|
||||||
virtual srs_error_t process_request(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
|
|
||||||
// Interface ISrsReloadHandler
|
// Interface ISrsReloadHandler
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t on_reload_http_api_crossdomain();
|
virtual srs_error_t on_reload_http_api_crossdomain();
|
||||||
|
// Extract APIs from SrsTcpConnection.
|
||||||
|
// Interface ISrsStartable
|
||||||
|
public:
|
||||||
|
// Start the client green thread.
|
||||||
|
// when server get a client from listener,
|
||||||
|
// 1. server will create an concrete connection(for instance, RTMP connection),
|
||||||
|
// 2. then add connection to its connection manager,
|
||||||
|
// 3. start the client thread by invoke this start()
|
||||||
|
// when client cycle thread stop, invoke the on_thread_stop(), which will use server
|
||||||
|
// To remove the client by server->remove(this).
|
||||||
|
virtual srs_error_t start();
|
||||||
|
// Interface ISrsConnection.
|
||||||
|
public:
|
||||||
|
virtual std::string remote_ip();
|
||||||
|
virtual const SrsContextId& get_id();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -59,55 +59,145 @@ using namespace std;
|
||||||
#include <srs_app_utility.hpp>
|
#include <srs_app_utility.hpp>
|
||||||
#include <srs_app_st.hpp>
|
#include <srs_app_st.hpp>
|
||||||
|
|
||||||
SrsHttpConn::SrsHttpConn(IConnectionManager* cm, srs_netfd_t fd, ISrsHttpServeMux* m, string cip) : SrsConnection(cm, fd, cip)
|
ISrsHttpConnOwner::ISrsHttpConnOwner()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ISrsHttpConnOwner::~ISrsHttpConnOwner()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsHttpConn::SrsHttpConn(ISrsHttpConnOwner* handler, ISrsProtocolReadWriter* fd, ISrsHttpServeMux* m, string cip, int cport)
|
||||||
{
|
{
|
||||||
parser = new SrsHttpParser();
|
parser = new SrsHttpParser();
|
||||||
cors = new SrsHttpCorsMux();
|
cors = new SrsHttpCorsMux();
|
||||||
http_mux = m;
|
http_mux = m;
|
||||||
|
handler_ = handler;
|
||||||
|
|
||||||
|
skt = fd;
|
||||||
|
ip = cip;
|
||||||
|
port = cport;
|
||||||
|
create_time = srsu2ms(srs_get_system_time());
|
||||||
|
clk = new SrsWallClock();
|
||||||
|
kbps = new SrsKbps(clk);
|
||||||
|
kbps->set_io(skt, skt);
|
||||||
|
trd = new SrsSTCoroutine("http", this, _srs_context->get_id());
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsHttpConn::~SrsHttpConn()
|
SrsHttpConn::~SrsHttpConn()
|
||||||
{
|
{
|
||||||
|
trd->interrupt();
|
||||||
|
srs_freep(trd);
|
||||||
|
|
||||||
srs_freep(parser);
|
srs_freep(parser);
|
||||||
srs_freep(cors);
|
srs_freep(cors);
|
||||||
|
|
||||||
|
srs_freep(kbps);
|
||||||
|
srs_freep(clk);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SrsHttpConn::desc()
|
||||||
|
{
|
||||||
|
return "HttpConn";
|
||||||
}
|
}
|
||||||
|
|
||||||
void SrsHttpConn::remark(int64_t* in, int64_t* out)
|
void SrsHttpConn::remark(int64_t* in, int64_t* out)
|
||||||
{
|
{
|
||||||
// TODO: FIXME: implements it
|
kbps->remark(in, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsHttpConn::start()
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
if ((err = trd->start()) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "coroutine");
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsHttpConn::cycle()
|
||||||
|
{
|
||||||
|
srs_error_t err = do_cycle();
|
||||||
|
|
||||||
|
// Notify handler to handle it.
|
||||||
|
// @remark The error may be transformed by handler.
|
||||||
|
err = handler_->on_conn_done(err);
|
||||||
|
|
||||||
|
// success.
|
||||||
|
if (err == srs_success) {
|
||||||
|
srs_trace("client finished.");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// It maybe success with message.
|
||||||
|
if (srs_error_code(err) == ERROR_SUCCESS) {
|
||||||
|
srs_trace("client finished%s.", srs_error_summary(err).c_str());
|
||||||
|
srs_freep(err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// client close peer.
|
||||||
|
// TODO: FIXME: Only reset the error when client closed it.
|
||||||
|
if (srs_is_client_gracefully_close(err)) {
|
||||||
|
srs_warn("client disconnect peer. ret=%d", srs_error_code(err));
|
||||||
|
} else if (srs_is_server_gracefully_close(err)) {
|
||||||
|
srs_warn("server disconnect. ret=%d", srs_error_code(err));
|
||||||
|
} else {
|
||||||
|
srs_error("serve error %s", srs_error_desc(err).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_freep(err);
|
||||||
|
return srs_success;
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsHttpConn::do_cycle()
|
srs_error_t SrsHttpConn::do_cycle()
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
// initialize parser
|
|
||||||
if ((err = parser->initialize(HTTP_REQUEST, false)) != srs_success) {
|
|
||||||
return srs_error_wrap(err, "init parser for %s", ip.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// set the recv timeout, for some clients never disconnect the connection.
|
// set the recv timeout, for some clients never disconnect the connection.
|
||||||
// @see https://github.com/ossrs/srs/issues/398
|
// @see https://github.com/ossrs/srs/issues/398
|
||||||
skt->set_recv_timeout(SRS_HTTP_RECV_TIMEOUT);
|
skt->set_recv_timeout(SRS_HTTP_RECV_TIMEOUT);
|
||||||
|
|
||||||
|
// initialize parser
|
||||||
|
if ((err = parser->initialize(HTTP_REQUEST)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "init parser for %s", ip.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify the handler that we are starting to process the connection.
|
||||||
|
if ((err = handler_->on_start()) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "start");
|
||||||
|
}
|
||||||
|
|
||||||
SrsRequest* last_req = NULL;
|
SrsRequest* last_req = NULL;
|
||||||
SrsAutoFree(SrsRequest, last_req);
|
SrsAutoFree(SrsRequest, last_req);
|
||||||
|
|
||||||
// initialize the cors, which will proxy to mux.
|
// process all http messages.
|
||||||
bool crossdomain_enabled = _srs_config->get_http_stream_crossdomain();
|
err = process_requests(&last_req);
|
||||||
if ((err = cors->initialize(http_mux, crossdomain_enabled)) != srs_success) {
|
|
||||||
return srs_error_wrap(err, "init cors");
|
srs_error_t r0 = srs_success;
|
||||||
|
if ((r0 = on_disconnect(last_req)) != srs_success) {
|
||||||
|
err = srs_error_wrap(err, "on disconnect %s", srs_error_desc(r0).c_str());
|
||||||
|
srs_freep(r0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// process http messages.
|
return err;
|
||||||
for (int req_id = 0; (err = trd->pull()) == srs_success; req_id++) {
|
}
|
||||||
// Try to receive a message from http.
|
|
||||||
srs_trace("HTTP client ip=%s, request=%d, to=%dms", ip.c_str(), req_id, srsu2ms(SRS_HTTP_RECV_TIMEOUT));
|
srs_error_t SrsHttpConn::process_requests(SrsRequest** preq)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
for (int req_id = 0; ; req_id++) {
|
||||||
|
if ((err = trd->pull()) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "pull");
|
||||||
|
}
|
||||||
|
|
||||||
// get a http message
|
// get a http message
|
||||||
ISrsHttpMessage* req = NULL;
|
ISrsHttpMessage* req = NULL;
|
||||||
if ((err = parser->parse_message(skt, &req)) != srs_success) {
|
if ((err = parser->parse_message(skt, &req)) != srs_success) {
|
||||||
break;
|
return srs_error_wrap(err, "parse message");
|
||||||
}
|
}
|
||||||
|
|
||||||
// if SUCCESS, always NOT-NULL.
|
// if SUCCESS, always NOT-NULL.
|
||||||
|
@ -120,18 +210,23 @@ srs_error_t SrsHttpConn::do_cycle()
|
||||||
hreq->set_connection(this);
|
hreq->set_connection(this);
|
||||||
|
|
||||||
// copy request to last request object.
|
// copy request to last request object.
|
||||||
srs_freep(last_req);
|
srs_freep(*preq);
|
||||||
last_req = hreq->to_request(hreq->host());
|
*preq = hreq->to_request(hreq->host());
|
||||||
|
|
||||||
// may should discard the body.
|
// may should discard the body.
|
||||||
if ((err = on_got_http_message(req)) != srs_success) {
|
SrsHttpResponseWriter writer(skt);
|
||||||
break;
|
if ((err = handler_->on_http_message(req, &writer)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "on http message");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ok, handle http request.
|
// ok, handle http request.
|
||||||
SrsHttpResponseWriter writer(skt);
|
if ((err = process_request(&writer, req, req_id)) != srs_success) {
|
||||||
if ((err = process_request(&writer, req)) != srs_success) {
|
return srs_error_wrap(err, "process request=%d", req_id);
|
||||||
break;
|
}
|
||||||
|
|
||||||
|
// After the request is processed.
|
||||||
|
if ((err = handler_->on_message_done(req, &writer)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "on message done");
|
||||||
}
|
}
|
||||||
|
|
||||||
// donot keep alive, disconnect it.
|
// donot keep alive, disconnect it.
|
||||||
|
@ -141,20 +236,14 @@ srs_error_t SrsHttpConn::do_cycle()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t r0 = srs_success;
|
|
||||||
if ((r0 = on_disconnect(last_req)) != srs_success) {
|
|
||||||
err = srs_error_wrap(err, "on disconnect %s", srs_error_desc(r0).c_str());
|
|
||||||
srs_freep(r0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsHttpConn::process_request(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
|
srs_error_t SrsHttpConn::process_request(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, int rid)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
srs_trace("HTTP %s %s, content-length=%" PRId64 "",
|
srs_trace("HTTP #%d %s:%d %s %s, content-length=%" PRId64 "", rid, ip.c_str(), port,
|
||||||
r->method_str().c_str(), r->url().c_str(), r->content_length());
|
r->method_str().c_str(), r->url().c_str(), r->content_length());
|
||||||
|
|
||||||
// use cors server mux to serve http request, which will proxy to http_remux.
|
// use cors server mux to serve http request, which will proxy to http_remux.
|
||||||
|
@ -171,49 +260,99 @@ srs_error_t SrsHttpConn::on_disconnect(SrsRequest* req)
|
||||||
return srs_success;
|
return srs_success;
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsHttpConn::on_reload_http_stream_crossdomain()
|
ISrsHttpConnOwner* SrsHttpConn::handler()
|
||||||
|
{
|
||||||
|
return handler_;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsHttpConn::pull()
|
||||||
|
{
|
||||||
|
return trd->pull();
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsHttpConn::set_crossdomain_enabled(bool v)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
// initialize the cors, which will proxy to mux.
|
// initialize the cors, which will proxy to mux.
|
||||||
bool crossdomain_enabled = _srs_config->get_http_stream_crossdomain();
|
if ((err = cors->initialize(http_mux, v)) != srs_success) {
|
||||||
if ((err = cors->initialize(http_mux, crossdomain_enabled)) != srs_success) {
|
return srs_error_wrap(err, "init cors");
|
||||||
return srs_error_wrap(err, "init mux");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsResponseOnlyHttpConn::SrsResponseOnlyHttpConn(IConnectionManager* cm, srs_netfd_t fd, ISrsHttpServeMux* m, string cip)
|
srs_error_t SrsHttpConn::set_jsonp(bool v)
|
||||||
: SrsHttpConn(cm, fd, m, cip)
|
|
||||||
{
|
{
|
||||||
|
parser->set_jsonp(v);
|
||||||
|
return srs_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
string SrsHttpConn::remote_ip()
|
||||||
|
{
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SrsContextId& SrsHttpConn::get_id()
|
||||||
|
{
|
||||||
|
return trd->cid();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsHttpConn::expire()
|
||||||
|
{
|
||||||
|
trd->interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsResponseOnlyHttpConn::SrsResponseOnlyHttpConn(bool https, ISrsResourceManager* cm, srs_netfd_t fd, ISrsHttpServeMux* m, string cip, int port)
|
||||||
|
{
|
||||||
|
// Create a identify for this client.
|
||||||
|
_srs_context->set_id(_srs_context->generate_id());
|
||||||
|
|
||||||
|
manager = cm;
|
||||||
|
skt = new SrsTcpConnection(fd);
|
||||||
|
|
||||||
|
if (https) {
|
||||||
|
ssl = new SrsSslConnection(skt);
|
||||||
|
conn = new SrsHttpConn(this, ssl, m, cip, port);
|
||||||
|
} else {
|
||||||
|
ssl = NULL;
|
||||||
|
conn = new SrsHttpConn(this, skt, m, cip, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
_srs_config->subscribe(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsResponseOnlyHttpConn::~SrsResponseOnlyHttpConn()
|
SrsResponseOnlyHttpConn::~SrsResponseOnlyHttpConn()
|
||||||
{
|
{
|
||||||
|
_srs_config->unsubscribe(this);
|
||||||
|
|
||||||
|
srs_freep(conn);
|
||||||
|
srs_freep(ssl);
|
||||||
|
srs_freep(skt);
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsResponseOnlyHttpConn::pop_message(ISrsHttpMessage** preq)
|
srs_error_t SrsResponseOnlyHttpConn::pop_message(ISrsHttpMessage** preq)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
SrsStSocket skt;
|
ISrsProtocolReadWriter* io = skt;
|
||||||
|
if (ssl) {
|
||||||
if ((err = skt.initialize(stfd)) != srs_success) {
|
io = ssl;
|
||||||
return srs_error_wrap(err, "init socket");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check user interrupt by interval.
|
// Check user interrupt by interval.
|
||||||
skt.set_recv_timeout(3 * SRS_UTIME_SECONDS);
|
io->set_recv_timeout(3 * SRS_UTIME_SECONDS);
|
||||||
|
|
||||||
|
// We start a socket to read the stfd, which is writing by conn.
|
||||||
|
// It's ok, because conn never read it after processing the HTTP request.
|
||||||
// drop all request body.
|
// drop all request body.
|
||||||
char body[4096];
|
char body[4096];
|
||||||
while (true) {
|
while (true) {
|
||||||
if ((err = trd->pull()) != srs_success) {
|
if ((err = conn->pull()) != srs_success) {
|
||||||
return srs_error_wrap(err, "timeout");
|
return srs_error_wrap(err, "timeout");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = skt.read(body, 4096, NULL)) != srs_success) {
|
if ((err = io->read(body, 4096, NULL)) != srs_success) {
|
||||||
// Because we use timeout to check trd state, so we should ignore any timeout.
|
// Because we use timeout to check trd state, so we should ignore any timeout.
|
||||||
if (srs_error_code(err) == ERROR_SOCKET_TIMEOUT) {
|
if (srs_error_code(err) == ERROR_SOCKET_TIMEOUT) {
|
||||||
srs_freep(err);
|
srs_freep(err);
|
||||||
|
@ -227,18 +366,51 @@ srs_error_t SrsResponseOnlyHttpConn::pop_message(ISrsHttpMessage** preq)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsResponseOnlyHttpConn::on_got_http_message(ISrsHttpMessage* msg)
|
srs_error_t SrsResponseOnlyHttpConn::on_reload_http_stream_crossdomain()
|
||||||
|
{
|
||||||
|
bool v = _srs_config->get_http_stream_crossdomain();
|
||||||
|
return conn->set_crossdomain_enabled(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsResponseOnlyHttpConn::on_start()
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
ISrsHttpResponseReader* br = msg->body_reader();
|
if (ssl) {
|
||||||
|
srs_utime_t starttime = srs_update_system_time();
|
||||||
|
string crt_file = _srs_config->get_https_stream_ssl_cert();
|
||||||
|
string key_file = _srs_config->get_https_stream_ssl_key();
|
||||||
|
if ((err = ssl->handshake(key_file, crt_file)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "handshake");
|
||||||
|
}
|
||||||
|
|
||||||
|
int cost = srsu2msi(srs_update_system_time() - starttime);
|
||||||
|
srs_trace("https: stream server done, use key %s and cert %s, cost=%dms",
|
||||||
|
key_file.c_str(), crt_file.c_str(), cost);
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsResponseOnlyHttpConn::on_http_message(ISrsHttpMessage* r, SrsHttpResponseWriter* w)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
// After parsed the message, set the schema to https.
|
||||||
|
if (ssl) {
|
||||||
|
SrsHttpMessage* hm = dynamic_cast<SrsHttpMessage*>(r);
|
||||||
|
hm->set_https(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
ISrsHttpResponseReader* br = r->body_reader();
|
||||||
|
|
||||||
// when not specified the content length, ignore.
|
// when not specified the content length, ignore.
|
||||||
if (msg->content_length() == -1) {
|
if (r->content_length() == -1) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
// drop all request body.
|
// Drop all request body.
|
||||||
|
// TODO: Should we set timeout for max reading?
|
||||||
char body[4096];
|
char body[4096];
|
||||||
while (!br->eof()) {
|
while (!br->eof()) {
|
||||||
if ((err = br->read(body, 4096, NULL)) != srs_success) {
|
if ((err = br->read(body, 4096, NULL)) != srs_success) {
|
||||||
|
@ -249,9 +421,67 @@ srs_error_t SrsResponseOnlyHttpConn::on_got_http_message(ISrsHttpMessage* msg)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SrsResponseOnlyHttpConn::expire()
|
srs_error_t SrsResponseOnlyHttpConn::on_message_done(ISrsHttpMessage* r, SrsHttpResponseWriter* w)
|
||||||
{
|
{
|
||||||
SrsHttpConn::expire();
|
return srs_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsResponseOnlyHttpConn::on_conn_done(srs_error_t r0)
|
||||||
|
{
|
||||||
|
// Because we use manager to manage this object,
|
||||||
|
// not the http connection object, so we must remove it here.
|
||||||
|
manager->remove(this);
|
||||||
|
|
||||||
|
return r0;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsResponseOnlyHttpConn::set_tcp_nodelay(bool v)
|
||||||
|
{
|
||||||
|
return skt->set_tcp_nodelay(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsResponseOnlyHttpConn::set_socket_buffer(srs_utime_t buffer_v)
|
||||||
|
{
|
||||||
|
return skt->set_socket_buffer(buffer_v);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SrsResponseOnlyHttpConn::desc()
|
||||||
|
{
|
||||||
|
if (ssl) {
|
||||||
|
return "HttpsStream";
|
||||||
|
}
|
||||||
|
return "HttpStream";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SrsResponseOnlyHttpConn::remote_ip()
|
||||||
|
{
|
||||||
|
return conn->remote_ip();
|
||||||
|
}
|
||||||
|
|
||||||
|
const SrsContextId& SrsResponseOnlyHttpConn::get_id()
|
||||||
|
{
|
||||||
|
return conn->get_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsResponseOnlyHttpConn::start()
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
bool v = _srs_config->get_http_stream_crossdomain();
|
||||||
|
if ((err = conn->set_crossdomain_enabled(v)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "set cors=%d", v);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = skt->initialize()) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "init socket");
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsResponseOnlyHttpConn::remark(int64_t* in, int64_t* out)
|
||||||
|
{
|
||||||
|
conn->remark(in, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsHttpServer::SrsHttpServer(SrsServer* svr)
|
SrsHttpServer::SrsHttpServer(SrsServer* svr)
|
||||||
|
@ -273,7 +503,7 @@ srs_error_t SrsHttpServer::initialize()
|
||||||
|
|
||||||
// for SRS go-sharp to detect the status of HTTP server of SRS HTTP FLV Cluster.
|
// for SRS go-sharp to detect the status of HTTP server of SRS HTTP FLV Cluster.
|
||||||
if ((err = http_static->mux.handle("/api/v1/versions", new SrsGoApiVersion())) != srs_success) {
|
if ((err = http_static->mux.handle("/api/v1/versions", new SrsGoApiVersion())) != srs_success) {
|
||||||
return srs_error_wrap(err, "handle versin");
|
return srs_error_wrap(err, "handle versions");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = http_stream->initialize()) != srs_success) {
|
if ((err = http_stream->initialize()) != srs_success) {
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
#include <srs_service_http_conn.hpp>
|
#include <srs_service_http_conn.hpp>
|
||||||
#include <srs_app_reload.hpp>
|
#include <srs_app_reload.hpp>
|
||||||
#include <srs_kernel_file.hpp>
|
#include <srs_kernel_file.hpp>
|
||||||
#include <srs_app_thread.hpp>
|
#include <srs_app_st.hpp>
|
||||||
#include <srs_app_conn.hpp>
|
#include <srs_app_conn.hpp>
|
||||||
#include <srs_app_source.hpp>
|
#include <srs_app_source.hpp>
|
||||||
|
|
||||||
|
@ -50,47 +50,110 @@ class SrsSharedPtrMessage;
|
||||||
class SrsRequest;
|
class SrsRequest;
|
||||||
class SrsFastStream;
|
class SrsFastStream;
|
||||||
class SrsHttpUri;
|
class SrsHttpUri;
|
||||||
class SrsConnection;
|
|
||||||
class SrsHttpMessage;
|
class SrsHttpMessage;
|
||||||
class SrsHttpStreamServer;
|
class SrsHttpStreamServer;
|
||||||
class SrsHttpStaticServer;
|
class SrsHttpStaticServer;
|
||||||
|
|
||||||
|
// The owner of HTTP connection.
|
||||||
|
class ISrsHttpConnOwner
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ISrsHttpConnOwner();
|
||||||
|
virtual ~ISrsHttpConnOwner();
|
||||||
|
public:
|
||||||
|
// When start the coroutine to process connection.
|
||||||
|
virtual srs_error_t on_start() = 0;
|
||||||
|
// Handle the HTTP message r, which may be parsed partially.
|
||||||
|
// For the static service or api, discard any body.
|
||||||
|
// For the stream caster, for instance, http flv streaming, may discard the flv header or not.
|
||||||
|
virtual srs_error_t on_http_message(ISrsHttpMessage* r, SrsHttpResponseWriter* w) = 0;
|
||||||
|
// When message is processed, we may need to do more things.
|
||||||
|
virtual srs_error_t on_message_done(ISrsHttpMessage* r, SrsHttpResponseWriter* w) = 0;
|
||||||
|
// When connection is destroy, should use manager to dispose it.
|
||||||
|
// The r0 is the original error, we will use the returned new error.
|
||||||
|
virtual srs_error_t on_conn_done(srs_error_t r0) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
// The http connection which request the static or stream content.
|
// The http connection which request the static or stream content.
|
||||||
class SrsHttpConn : public SrsConnection
|
class SrsHttpConn : virtual public ISrsStartableConneciton, virtual public ISrsCoroutineHandler
|
||||||
|
, virtual public ISrsExpire
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
SrsHttpParser* parser;
|
SrsHttpParser* parser;
|
||||||
ISrsHttpServeMux* http_mux;
|
ISrsHttpServeMux* http_mux;
|
||||||
SrsHttpCorsMux* cors;
|
SrsHttpCorsMux* cors;
|
||||||
|
ISrsHttpConnOwner* handler_;
|
||||||
|
protected:
|
||||||
|
ISrsProtocolReadWriter* skt;
|
||||||
|
// Each connection start a green thread,
|
||||||
|
// when thread stop, the connection will be delete by server.
|
||||||
|
SrsCoroutine* trd;
|
||||||
|
// The ip and port of client.
|
||||||
|
std::string ip;
|
||||||
|
int port;
|
||||||
|
private:
|
||||||
|
// The connection total kbps.
|
||||||
|
// not only the rtmp or http connection, all type of connection are
|
||||||
|
// need to statistic the kbps of io.
|
||||||
|
// The SrsStatistic will use it indirectly to statistic the bytes delta of current connection.
|
||||||
|
SrsKbps* kbps;
|
||||||
|
SrsWallClock* clk;
|
||||||
|
// The create time in milliseconds.
|
||||||
|
// for current connection to log self create time and calculate the living time.
|
||||||
|
int64_t create_time;
|
||||||
public:
|
public:
|
||||||
SrsHttpConn(IConnectionManager* cm, srs_netfd_t fd, ISrsHttpServeMux* m, std::string cip);
|
SrsHttpConn(ISrsHttpConnOwner* handler, ISrsProtocolReadWriter* fd, ISrsHttpServeMux* m, std::string cip, int port);
|
||||||
virtual ~SrsHttpConn();
|
virtual ~SrsHttpConn();
|
||||||
|
// Interface ISrsResource.
|
||||||
|
public:
|
||||||
|
virtual std::string desc();
|
||||||
// Interface ISrsKbpsDelta
|
// Interface ISrsKbpsDelta
|
||||||
public:
|
public:
|
||||||
virtual void remark(int64_t* in, int64_t* out);
|
virtual void remark(int64_t* in, int64_t* out);
|
||||||
protected:
|
// Interface ISrsStartable
|
||||||
virtual srs_error_t do_cycle();
|
public:
|
||||||
protected:
|
virtual srs_error_t start();
|
||||||
// When got http message,
|
// Interface ISrsOneCycleThreadHandler
|
||||||
// for the static service or api, discard any body.
|
public:
|
||||||
// for the stream caster, for instance, http flv streaming, may discard the flv header or not.
|
virtual srs_error_t cycle();
|
||||||
virtual srs_error_t on_got_http_message(ISrsHttpMessage* msg) = 0;
|
|
||||||
private:
|
private:
|
||||||
virtual srs_error_t process_request(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
|
virtual srs_error_t do_cycle();
|
||||||
|
virtual srs_error_t process_requests(SrsRequest** preq);
|
||||||
|
virtual srs_error_t process_request(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, int rid);
|
||||||
// When the connection disconnect, call this method.
|
// When the connection disconnect, call this method.
|
||||||
// e.g. log msg of connection and report to other system.
|
// e.g. log msg of connection and report to other system.
|
||||||
// @param request: request which is converted by the last http message.
|
// @param request: request which is converted by the last http message.
|
||||||
virtual srs_error_t on_disconnect(SrsRequest* req);
|
virtual srs_error_t on_disconnect(SrsRequest* req);
|
||||||
// Interface ISrsReloadHandler
|
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t on_reload_http_stream_crossdomain();
|
// Get the HTTP message handler.
|
||||||
|
virtual ISrsHttpConnOwner* handler();
|
||||||
|
// Whether the connection coroutine is error or terminated.
|
||||||
|
virtual srs_error_t pull();
|
||||||
|
// Whether enable the CORS(cross-domain).
|
||||||
|
virtual srs_error_t set_crossdomain_enabled(bool v);
|
||||||
|
// Whether enable the JSONP.
|
||||||
|
virtual srs_error_t set_jsonp(bool v);
|
||||||
|
// Interface ISrsConnection.
|
||||||
|
public:
|
||||||
|
virtual std::string remote_ip();
|
||||||
|
virtual const SrsContextId& get_id();
|
||||||
|
// Interface ISrsExpire.
|
||||||
|
public:
|
||||||
|
virtual void expire();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Drop body of request, only process the response.
|
// Drop body of request, only process the response.
|
||||||
class SrsResponseOnlyHttpConn : public SrsHttpConn
|
class SrsResponseOnlyHttpConn : virtual public ISrsStartableConneciton, virtual public ISrsHttpConnOwner
|
||||||
|
, virtual public ISrsReloadHandler
|
||||||
{
|
{
|
||||||
|
private:
|
||||||
|
// The manager object to manage the connection.
|
||||||
|
ISrsResourceManager* manager;
|
||||||
|
SrsTcpConnection* skt;
|
||||||
|
SrsSslConnection* ssl;
|
||||||
|
SrsHttpConn* conn;
|
||||||
public:
|
public:
|
||||||
SrsResponseOnlyHttpConn(IConnectionManager* cm, srs_netfd_t fd, ISrsHttpServeMux* m, std::string cip);
|
SrsResponseOnlyHttpConn(bool https, ISrsResourceManager* cm, srs_netfd_t fd, ISrsHttpServeMux* m, std::string cip, int port);
|
||||||
virtual ~SrsResponseOnlyHttpConn();
|
virtual ~SrsResponseOnlyHttpConn();
|
||||||
public:
|
public:
|
||||||
// Directly read a HTTP request message.
|
// Directly read a HTTP request message.
|
||||||
|
@ -99,11 +162,34 @@ public:
|
||||||
// @see https://github.com/ossrs/srs/issues/636#issuecomment-298208427
|
// @see https://github.com/ossrs/srs/issues/636#issuecomment-298208427
|
||||||
// @remark Should only used in HTTP-FLV streaming connection.
|
// @remark Should only used in HTTP-FLV streaming connection.
|
||||||
virtual srs_error_t pop_message(ISrsHttpMessage** preq);
|
virtual srs_error_t pop_message(ISrsHttpMessage** preq);
|
||||||
|
// Interface ISrsReloadHandler
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t on_got_http_message(ISrsHttpMessage* msg);
|
virtual srs_error_t on_reload_http_stream_crossdomain();
|
||||||
|
// Interface ISrsHttpConnOwner.
|
||||||
public:
|
public:
|
||||||
// Set connection to expired.
|
virtual srs_error_t on_start();
|
||||||
virtual void expire();
|
virtual srs_error_t on_http_message(ISrsHttpMessage* r, SrsHttpResponseWriter* w);
|
||||||
|
virtual srs_error_t on_message_done(ISrsHttpMessage* r, SrsHttpResponseWriter* w);
|
||||||
|
virtual srs_error_t on_conn_done(srs_error_t r0);
|
||||||
|
// Extract APIs from SrsTcpConnection.
|
||||||
|
public:
|
||||||
|
// Set socket option TCP_NODELAY.
|
||||||
|
virtual srs_error_t set_tcp_nodelay(bool v);
|
||||||
|
// Set socket option SO_SNDBUF in srs_utime_t.
|
||||||
|
virtual srs_error_t set_socket_buffer(srs_utime_t buffer_v);
|
||||||
|
// Interface ISrsResource.
|
||||||
|
public:
|
||||||
|
virtual std::string desc();
|
||||||
|
// Interface ISrsConnection.
|
||||||
|
public:
|
||||||
|
virtual std::string remote_ip();
|
||||||
|
virtual const SrsContextId& get_id();
|
||||||
|
// Interface ISrsStartable
|
||||||
|
public:
|
||||||
|
virtual srs_error_t start();
|
||||||
|
// Interface ISrsKbpsDelta
|
||||||
|
public:
|
||||||
|
virtual void remark(int64_t* in, int64_t* out);
|
||||||
};
|
};
|
||||||
|
|
||||||
// The http server, use http stream or static server to serve requests.
|
// The http server, use http stream or static server to serve requests.
|
||||||
|
|
|
@ -60,13 +60,13 @@ srs_error_t SrsHttpHooks::on_connect(string url, SrsRequest* req)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
int client_id = _srs_context->get_id();
|
SrsContextId cid = _srs_context->get_id();
|
||||||
|
|
||||||
SrsJsonObject* obj = SrsJsonAny::object();
|
SrsJsonObject* obj = SrsJsonAny::object();
|
||||||
SrsAutoFree(SrsJsonObject, obj);
|
SrsAutoFree(SrsJsonObject, obj);
|
||||||
|
|
||||||
obj->set("action", SrsJsonAny::str("on_connect"));
|
obj->set("action", SrsJsonAny::str("on_connect"));
|
||||||
obj->set("client_id", SrsJsonAny::integer(client_id));
|
obj->set("client_id", SrsJsonAny::str(cid.c_str()));
|
||||||
obj->set("ip", SrsJsonAny::str(req->ip.c_str()));
|
obj->set("ip", SrsJsonAny::str(req->ip.c_str()));
|
||||||
obj->set("vhost", SrsJsonAny::str(req->vhost.c_str()));
|
obj->set("vhost", SrsJsonAny::str(req->vhost.c_str()));
|
||||||
obj->set("app", SrsJsonAny::str(req->app.c_str()));
|
obj->set("app", SrsJsonAny::str(req->app.c_str()));
|
||||||
|
@ -79,12 +79,12 @@ srs_error_t SrsHttpHooks::on_connect(string url, SrsRequest* req)
|
||||||
|
|
||||||
SrsHttpClient http;
|
SrsHttpClient http;
|
||||||
if ((err = do_post(&http, url, data, status_code, res)) != srs_success) {
|
if ((err = do_post(&http, url, data, status_code, res)) != srs_success) {
|
||||||
return srs_error_wrap(err, "http: on_connect failed, client_id=%d, url=%s, request=%s, response=%s, code=%d",
|
return srs_error_wrap(err, "http: on_connect failed, client_id=%s, url=%s, request=%s, response=%s, code=%d",
|
||||||
client_id, url.c_str(), data.c_str(), res.c_str(), status_code);
|
cid.c_str(), url.c_str(), data.c_str(), res.c_str(), status_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_trace("http: on_connect ok, client_id=%d, url=%s, request=%s, response=%s",
|
srs_trace("http: on_connect ok, client_id=%s, url=%s, request=%s, response=%s",
|
||||||
client_id, url.c_str(), data.c_str(), res.c_str());
|
cid.c_str(), url.c_str(), data.c_str(), res.c_str());
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -93,13 +93,13 @@ void SrsHttpHooks::on_close(string url, SrsRequest* req, int64_t send_bytes, int
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
int client_id = _srs_context->get_id();
|
SrsContextId cid = _srs_context->get_id();
|
||||||
|
|
||||||
SrsJsonObject* obj = SrsJsonAny::object();
|
SrsJsonObject* obj = SrsJsonAny::object();
|
||||||
SrsAutoFree(SrsJsonObject, obj);
|
SrsAutoFree(SrsJsonObject, obj);
|
||||||
|
|
||||||
obj->set("action", SrsJsonAny::str("on_close"));
|
obj->set("action", SrsJsonAny::str("on_close"));
|
||||||
obj->set("client_id", SrsJsonAny::integer(client_id));
|
obj->set("client_id", SrsJsonAny::str(cid.c_str()));
|
||||||
obj->set("ip", SrsJsonAny::str(req->ip.c_str()));
|
obj->set("ip", SrsJsonAny::str(req->ip.c_str()));
|
||||||
obj->set("vhost", SrsJsonAny::str(req->vhost.c_str()));
|
obj->set("vhost", SrsJsonAny::str(req->vhost.c_str()));
|
||||||
obj->set("app", SrsJsonAny::str(req->app.c_str()));
|
obj->set("app", SrsJsonAny::str(req->app.c_str()));
|
||||||
|
@ -114,13 +114,13 @@ void SrsHttpHooks::on_close(string url, SrsRequest* req, int64_t send_bytes, int
|
||||||
if ((err = do_post(&http, url, data, status_code, res)) != srs_success) {
|
if ((err = do_post(&http, url, data, status_code, res)) != srs_success) {
|
||||||
int ret = srs_error_code(err);
|
int ret = srs_error_code(err);
|
||||||
srs_freep(err);
|
srs_freep(err);
|
||||||
srs_warn("http: ignore on_close failed, client_id=%d, url=%s, request=%s, response=%s, code=%d, ret=%d",
|
srs_warn("http: ignore on_close failed, client_id=%s, url=%s, request=%s, response=%s, code=%d, ret=%d",
|
||||||
client_id, url.c_str(), data.c_str(), res.c_str(), status_code, ret);
|
cid.c_str(), url.c_str(), data.c_str(), res.c_str(), status_code, ret);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_trace("http: on_close ok, client_id=%d, url=%s, request=%s, response=%s",
|
srs_trace("http: on_close ok, client_id=%s, url=%s, request=%s, response=%s",
|
||||||
client_id, url.c_str(), data.c_str(), res.c_str());
|
cid.c_str(), url.c_str(), data.c_str(), res.c_str());
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -129,13 +129,13 @@ srs_error_t SrsHttpHooks::on_publish(string url, SrsRequest* req)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
int client_id = _srs_context->get_id();
|
SrsContextId cid = _srs_context->get_id();
|
||||||
|
|
||||||
SrsJsonObject* obj = SrsJsonAny::object();
|
SrsJsonObject* obj = SrsJsonAny::object();
|
||||||
SrsAutoFree(SrsJsonObject, obj);
|
SrsAutoFree(SrsJsonObject, obj);
|
||||||
|
|
||||||
obj->set("action", SrsJsonAny::str("on_publish"));
|
obj->set("action", SrsJsonAny::str("on_publish"));
|
||||||
obj->set("client_id", SrsJsonAny::integer(client_id));
|
obj->set("client_id", SrsJsonAny::str(cid.c_str()));
|
||||||
obj->set("ip", SrsJsonAny::str(req->ip.c_str()));
|
obj->set("ip", SrsJsonAny::str(req->ip.c_str()));
|
||||||
obj->set("vhost", SrsJsonAny::str(req->vhost.c_str()));
|
obj->set("vhost", SrsJsonAny::str(req->vhost.c_str()));
|
||||||
obj->set("app", SrsJsonAny::str(req->app.c_str()));
|
obj->set("app", SrsJsonAny::str(req->app.c_str()));
|
||||||
|
@ -149,12 +149,12 @@ srs_error_t SrsHttpHooks::on_publish(string url, SrsRequest* req)
|
||||||
|
|
||||||
SrsHttpClient http;
|
SrsHttpClient http;
|
||||||
if ((err = do_post(&http, url, data, status_code, res)) != srs_success) {
|
if ((err = do_post(&http, url, data, status_code, res)) != srs_success) {
|
||||||
return srs_error_wrap(err, "http: on_publish failed, client_id=%d, url=%s, request=%s, response=%s, code=%d",
|
return srs_error_wrap(err, "http: on_publish failed, client_id=%s, url=%s, request=%s, response=%s, code=%d",
|
||||||
client_id, url.c_str(), data.c_str(), res.c_str(), status_code);
|
cid.c_str(), url.c_str(), data.c_str(), res.c_str(), status_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_trace("http: on_publish ok, client_id=%d, url=%s, request=%s, response=%s",
|
srs_trace("http: on_publish ok, client_id=%s, url=%s, request=%s, response=%s",
|
||||||
client_id, url.c_str(), data.c_str(), res.c_str());
|
cid.c_str(), url.c_str(), data.c_str(), res.c_str());
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -163,13 +163,13 @@ void SrsHttpHooks::on_unpublish(string url, SrsRequest* req)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
int client_id = _srs_context->get_id();
|
SrsContextId cid = _srs_context->get_id();
|
||||||
|
|
||||||
SrsJsonObject* obj = SrsJsonAny::object();
|
SrsJsonObject* obj = SrsJsonAny::object();
|
||||||
SrsAutoFree(SrsJsonObject, obj);
|
SrsAutoFree(SrsJsonObject, obj);
|
||||||
|
|
||||||
obj->set("action", SrsJsonAny::str("on_unpublish"));
|
obj->set("action", SrsJsonAny::str("on_unpublish"));
|
||||||
obj->set("client_id", SrsJsonAny::integer(client_id));
|
obj->set("client_id", SrsJsonAny::str(cid.c_str()));
|
||||||
obj->set("ip", SrsJsonAny::str(req->ip.c_str()));
|
obj->set("ip", SrsJsonAny::str(req->ip.c_str()));
|
||||||
obj->set("vhost", SrsJsonAny::str(req->vhost.c_str()));
|
obj->set("vhost", SrsJsonAny::str(req->vhost.c_str()));
|
||||||
obj->set("app", SrsJsonAny::str(req->app.c_str()));
|
obj->set("app", SrsJsonAny::str(req->app.c_str()));
|
||||||
|
@ -184,13 +184,13 @@ void SrsHttpHooks::on_unpublish(string url, SrsRequest* req)
|
||||||
if ((err = do_post(&http, url, data, status_code, res)) != srs_success) {
|
if ((err = do_post(&http, url, data, status_code, res)) != srs_success) {
|
||||||
int ret = srs_error_code(err);
|
int ret = srs_error_code(err);
|
||||||
srs_freep(err);
|
srs_freep(err);
|
||||||
srs_warn("http: ignore on_unpublish failed, client_id=%d, url=%s, request=%s, response=%s, status=%d, ret=%d",
|
srs_warn("http: ignore on_unpublish failed, client_id=%s, url=%s, request=%s, response=%s, status=%d, ret=%d",
|
||||||
client_id, url.c_str(), data.c_str(), res.c_str(), status_code, ret);
|
cid.c_str(), url.c_str(), data.c_str(), res.c_str(), status_code, ret);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_trace("http: on_unpublish ok, client_id=%d, url=%s, request=%s, response=%s",
|
srs_trace("http: on_unpublish ok, client_id=%s, url=%s, request=%s, response=%s",
|
||||||
client_id, url.c_str(), data.c_str(), res.c_str());
|
cid.c_str(), url.c_str(), data.c_str(), res.c_str());
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -199,13 +199,13 @@ srs_error_t SrsHttpHooks::on_play(string url, SrsRequest* req)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
int client_id = _srs_context->get_id();
|
SrsContextId cid = _srs_context->get_id();
|
||||||
|
|
||||||
SrsJsonObject* obj = SrsJsonAny::object();
|
SrsJsonObject* obj = SrsJsonAny::object();
|
||||||
SrsAutoFree(SrsJsonObject, obj);
|
SrsAutoFree(SrsJsonObject, obj);
|
||||||
|
|
||||||
obj->set("action", SrsJsonAny::str("on_play"));
|
obj->set("action", SrsJsonAny::str("on_play"));
|
||||||
obj->set("client_id", SrsJsonAny::integer(client_id));
|
obj->set("client_id", SrsJsonAny::str(cid.c_str()));
|
||||||
obj->set("ip", SrsJsonAny::str(req->ip.c_str()));
|
obj->set("ip", SrsJsonAny::str(req->ip.c_str()));
|
||||||
obj->set("vhost", SrsJsonAny::str(req->vhost.c_str()));
|
obj->set("vhost", SrsJsonAny::str(req->vhost.c_str()));
|
||||||
obj->set("app", SrsJsonAny::str(req->app.c_str()));
|
obj->set("app", SrsJsonAny::str(req->app.c_str()));
|
||||||
|
@ -219,12 +219,12 @@ srs_error_t SrsHttpHooks::on_play(string url, SrsRequest* req)
|
||||||
|
|
||||||
SrsHttpClient http;
|
SrsHttpClient http;
|
||||||
if ((err = do_post(&http, url, data, status_code, res)) != srs_success) {
|
if ((err = do_post(&http, url, data, status_code, res)) != srs_success) {
|
||||||
return srs_error_wrap(err, "http: on_play failed, client_id=%d, url=%s, request=%s, response=%s, status=%d",
|
return srs_error_wrap(err, "http: on_play failed, client_id=%s, url=%s, request=%s, response=%s, status=%d",
|
||||||
client_id, url.c_str(), data.c_str(), res.c_str(), status_code);
|
cid.c_str(), url.c_str(), data.c_str(), res.c_str(), status_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_trace("http: on_play ok, client_id=%d, url=%s, request=%s, response=%s",
|
srs_trace("http: on_play ok, client_id=%s, url=%s, request=%s, response=%s",
|
||||||
client_id, url.c_str(), data.c_str(), res.c_str());
|
cid.c_str(), url.c_str(), data.c_str(), res.c_str());
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -233,13 +233,13 @@ void SrsHttpHooks::on_stop(string url, SrsRequest* req)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
int client_id = _srs_context->get_id();
|
SrsContextId cid = _srs_context->get_id();
|
||||||
|
|
||||||
SrsJsonObject* obj = SrsJsonAny::object();
|
SrsJsonObject* obj = SrsJsonAny::object();
|
||||||
SrsAutoFree(SrsJsonObject, obj);
|
SrsAutoFree(SrsJsonObject, obj);
|
||||||
|
|
||||||
obj->set("action", SrsJsonAny::str("on_stop"));
|
obj->set("action", SrsJsonAny::str("on_stop"));
|
||||||
obj->set("client_id", SrsJsonAny::integer(client_id));
|
obj->set("client_id", SrsJsonAny::str(cid.c_str()));
|
||||||
obj->set("ip", SrsJsonAny::str(req->ip.c_str()));
|
obj->set("ip", SrsJsonAny::str(req->ip.c_str()));
|
||||||
obj->set("vhost", SrsJsonAny::str(req->vhost.c_str()));
|
obj->set("vhost", SrsJsonAny::str(req->vhost.c_str()));
|
||||||
obj->set("app", SrsJsonAny::str(req->app.c_str()));
|
obj->set("app", SrsJsonAny::str(req->app.c_str()));
|
||||||
|
@ -254,29 +254,29 @@ void SrsHttpHooks::on_stop(string url, SrsRequest* req)
|
||||||
if ((err = do_post(&http, url, data, status_code, res)) != srs_success) {
|
if ((err = do_post(&http, url, data, status_code, res)) != srs_success) {
|
||||||
int ret = srs_error_code(err);
|
int ret = srs_error_code(err);
|
||||||
srs_freep(err);
|
srs_freep(err);
|
||||||
srs_warn("http: ignore on_stop failed, client_id=%d, url=%s, request=%s, response=%s, code=%d, ret=%d",
|
srs_warn("http: ignore on_stop failed, client_id=%s, url=%s, request=%s, response=%s, code=%d, ret=%d",
|
||||||
client_id, url.c_str(), data.c_str(), res.c_str(), status_code, ret);
|
cid.c_str(), url.c_str(), data.c_str(), res.c_str(), status_code, ret);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_trace("http: on_stop ok, client_id=%d, url=%s, request=%s, response=%s",
|
srs_trace("http: on_stop ok, client_id=%s, url=%s, request=%s, response=%s",
|
||||||
client_id, url.c_str(), data.c_str(), res.c_str());
|
cid.c_str(), url.c_str(), data.c_str(), res.c_str());
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsHttpHooks::on_dvr(int cid, string url, SrsRequest* req, string file)
|
srs_error_t SrsHttpHooks::on_dvr(SrsContextId c, string url, SrsRequest* req, string file)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
int client_id = cid;
|
SrsContextId cid = c;
|
||||||
std::string cwd = _srs_config->cwd();
|
std::string cwd = _srs_config->cwd();
|
||||||
|
|
||||||
SrsJsonObject* obj = SrsJsonAny::object();
|
SrsJsonObject* obj = SrsJsonAny::object();
|
||||||
SrsAutoFree(SrsJsonObject, obj);
|
SrsAutoFree(SrsJsonObject, obj);
|
||||||
|
|
||||||
obj->set("action", SrsJsonAny::str("on_dvr"));
|
obj->set("action", SrsJsonAny::str("on_dvr"));
|
||||||
obj->set("client_id", SrsJsonAny::integer(client_id));
|
obj->set("client_id", SrsJsonAny::str(cid.c_str()));
|
||||||
obj->set("ip", SrsJsonAny::str(req->ip.c_str()));
|
obj->set("ip", SrsJsonAny::str(req->ip.c_str()));
|
||||||
obj->set("vhost", SrsJsonAny::str(req->vhost.c_str()));
|
obj->set("vhost", SrsJsonAny::str(req->vhost.c_str()));
|
||||||
obj->set("app", SrsJsonAny::str(req->app.c_str()));
|
obj->set("app", SrsJsonAny::str(req->app.c_str()));
|
||||||
|
@ -291,21 +291,21 @@ srs_error_t SrsHttpHooks::on_dvr(int cid, string url, SrsRequest* req, string fi
|
||||||
|
|
||||||
SrsHttpClient http;
|
SrsHttpClient http;
|
||||||
if ((err = do_post(&http, url, data, status_code, res)) != srs_success) {
|
if ((err = do_post(&http, url, data, status_code, res)) != srs_success) {
|
||||||
return srs_error_wrap(err, "http post on_dvr uri failed, client_id=%d, url=%s, request=%s, response=%s, code=%d",
|
return srs_error_wrap(err, "http post on_dvr uri failed, client_id=%s, url=%s, request=%s, response=%s, code=%d",
|
||||||
client_id, url.c_str(), data.c_str(), res.c_str(), status_code);
|
cid.c_str(), url.c_str(), data.c_str(), res.c_str(), status_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_trace("http hook on_dvr success. client_id=%d, url=%s, request=%s, response=%s",
|
srs_trace("http hook on_dvr success. client_id=%s, url=%s, request=%s, response=%s",
|
||||||
client_id, url.c_str(), data.c_str(), res.c_str());
|
cid.c_str(), url.c_str(), data.c_str(), res.c_str());
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsHttpHooks::on_hls(int cid, string url, SrsRequest* req, string file, string ts_url, string m3u8, string m3u8_url, int sn, srs_utime_t duration)
|
srs_error_t SrsHttpHooks::on_hls(SrsContextId c, string url, SrsRequest* req, string file, string ts_url, string m3u8, string m3u8_url, int sn, srs_utime_t duration)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
int client_id = cid;
|
SrsContextId cid = c;
|
||||||
std::string cwd = _srs_config->cwd();
|
std::string cwd = _srs_config->cwd();
|
||||||
|
|
||||||
// the ts_url is under the same dir of m3u8_url.
|
// the ts_url is under the same dir of m3u8_url.
|
||||||
|
@ -318,7 +318,7 @@ srs_error_t SrsHttpHooks::on_hls(int cid, string url, SrsRequest* req, string fi
|
||||||
SrsAutoFree(SrsJsonObject, obj);
|
SrsAutoFree(SrsJsonObject, obj);
|
||||||
|
|
||||||
obj->set("action", SrsJsonAny::str("on_hls"));
|
obj->set("action", SrsJsonAny::str("on_hls"));
|
||||||
obj->set("client_id", SrsJsonAny::integer(client_id));
|
obj->set("client_id", SrsJsonAny::str(cid.c_str()));
|
||||||
obj->set("ip", SrsJsonAny::str(req->ip.c_str()));
|
obj->set("ip", SrsJsonAny::str(req->ip.c_str()));
|
||||||
obj->set("vhost", SrsJsonAny::str(req->vhost.c_str()));
|
obj->set("vhost", SrsJsonAny::str(req->vhost.c_str()));
|
||||||
obj->set("app", SrsJsonAny::str(req->app.c_str()));
|
obj->set("app", SrsJsonAny::str(req->app.c_str()));
|
||||||
|
@ -341,17 +341,17 @@ srs_error_t SrsHttpHooks::on_hls(int cid, string url, SrsRequest* req, string fi
|
||||||
return srs_error_wrap(err, "http: post %s with %s, status=%d, res=%s", url.c_str(), data.c_str(), status_code, res.c_str());
|
return srs_error_wrap(err, "http: post %s with %s, status=%d, res=%s", url.c_str(), data.c_str(), status_code, res.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_trace("http: on_hls ok, client_id=%d, url=%s, request=%s, response=%s",
|
srs_trace("http: on_hls ok, client_id=%s, url=%s, request=%s, response=%s",
|
||||||
client_id, url.c_str(), data.c_str(), res.c_str());
|
cid.c_str(), url.c_str(), data.c_str(), res.c_str());
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsHttpHooks::on_hls_notify(int cid, std::string url, SrsRequest* req, std::string ts_url, int nb_notify)
|
srs_error_t SrsHttpHooks::on_hls_notify(SrsContextId c, std::string url, SrsRequest* req, std::string ts_url, int nb_notify)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
int client_id = cid;
|
SrsContextId cid = c;
|
||||||
std::string cwd = _srs_config->cwd();
|
std::string cwd = _srs_config->cwd();
|
||||||
|
|
||||||
if (srs_string_is_http(ts_url)) {
|
if (srs_string_is_http(ts_url)) {
|
||||||
|
@ -371,7 +371,7 @@ srs_error_t SrsHttpHooks::on_hls_notify(int cid, std::string url, SrsRequest* re
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsHttpClient http;
|
SrsHttpClient http;
|
||||||
if ((err = http.initialize(uri.get_host(), uri.get_port(), SRS_HLS_NOTIFY_TIMEOUT)) != srs_success) {
|
if ((err = http.initialize(uri.get_schema(), uri.get_host(), uri.get_port(), SRS_HLS_NOTIFY_TIMEOUT)) != srs_success) {
|
||||||
return srs_error_wrap(err, "http: init client for %s", url.c_str());
|
return srs_error_wrap(err, "http: init client for %s", url.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,8 +406,8 @@ srs_error_t SrsHttpHooks::on_hls_notify(int cid, std::string url, SrsRequest* re
|
||||||
}
|
}
|
||||||
|
|
||||||
int spenttime = (int)(srsu2ms(srs_update_system_time()) - starttime);
|
int spenttime = (int)(srsu2ms(srs_update_system_time()) - starttime);
|
||||||
srs_trace("http hook on_hls_notify success. client_id=%d, url=%s, code=%d, spent=%dms, read=%dB, err=%s",
|
srs_trace("http hook on_hls_notify success. client_id=%s, url=%s, code=%d, spent=%dms, read=%dB, err=%s",
|
||||||
client_id, url.c_str(), msg->status_code(), spenttime, nb_read, srs_error_desc(err).c_str());
|
cid.c_str(), url.c_str(), msg->status_code(), spenttime, nb_read, srs_error_desc(err).c_str());
|
||||||
|
|
||||||
// ignore any error for on_hls_notify.
|
// ignore any error for on_hls_notify.
|
||||||
srs_error_reset(err);
|
srs_error_reset(err);
|
||||||
|
@ -478,7 +478,7 @@ srs_error_t SrsHttpHooks::do_post(SrsHttpClient* hc, std::string url, std::strin
|
||||||
return srs_error_wrap(err, "http: post failed. url=%s", url.c_str());
|
return srs_error_wrap(err, "http: post failed. url=%s", url.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = hc->initialize(uri.get_host(), uri.get_port())) != srs_success) {
|
if ((err = hc->initialize(uri.get_schema(), uri.get_host(), uri.get_port())) != srs_success) {
|
||||||
return srs_error_wrap(err, "http: init client");
|
return srs_error_wrap(err, "http: init client");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -532,7 +532,7 @@ srs_error_t SrsHttpHooks::do_post(SrsHttpClient* hc, std::string url, std::strin
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((res_code->to_integer()) != ERROR_SUCCESS) {
|
if ((res_code->to_integer()) != ERROR_SUCCESS) {
|
||||||
return srs_error_new(ERROR_RESPONSE_CODE, "http: response object code %d %s", res_code->to_integer(), res.c_str());
|
return srs_error_new(ERROR_RESPONSE_CODE, "http: response object code %" PRId64 " %s", res_code->to_integer(), res.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
|
|
@ -74,7 +74,7 @@ public:
|
||||||
// ignore if empty.
|
// ignore if empty.
|
||||||
// @param file the file path, can be relative or absolute path.
|
// @param file the file path, can be relative or absolute path.
|
||||||
// @param cid the source connection cid, for the on_dvr is async call.
|
// @param cid the source connection cid, for the on_dvr is async call.
|
||||||
static srs_error_t on_dvr(int cid, std::string url, SrsRequest* req, std::string file);
|
static srs_error_t on_dvr(SrsContextId cid, std::string url, SrsRequest* req, std::string file);
|
||||||
// When hls reap segment, callback.
|
// When hls reap segment, callback.
|
||||||
// @param url the api server url, to process the event.
|
// @param url the api server url, to process the event.
|
||||||
// ignore if empty.
|
// ignore if empty.
|
||||||
|
@ -85,7 +85,7 @@ public:
|
||||||
// @param sn the seq_no, the sequence number of ts in hls/m3u8.
|
// @param sn the seq_no, the sequence number of ts in hls/m3u8.
|
||||||
// @param duration the segment duration in srs_utime_t.
|
// @param duration the segment duration in srs_utime_t.
|
||||||
// @param cid the source connection cid, for the on_dvr is async call.
|
// @param cid the source connection cid, for the on_dvr is async call.
|
||||||
static srs_error_t on_hls(int cid, std::string url, SrsRequest* req, std::string file, std::string ts_url,
|
static srs_error_t on_hls(SrsContextId cid, std::string url, SrsRequest* req, std::string file, std::string ts_url,
|
||||||
std::string m3u8, std::string m3u8_url, int sn, srs_utime_t duration);
|
std::string m3u8, std::string m3u8_url, int sn, srs_utime_t duration);
|
||||||
// When hls reap segment, callback.
|
// When hls reap segment, callback.
|
||||||
// @param url the api server url, to process the event.
|
// @param url the api server url, to process the event.
|
||||||
|
@ -93,7 +93,7 @@ public:
|
||||||
// @param ts_url the ts uri, used to replace the variable [ts_url] in url.
|
// @param ts_url the ts uri, used to replace the variable [ts_url] in url.
|
||||||
// @param nb_notify the max bytes to read from notify server.
|
// @param nb_notify the max bytes to read from notify server.
|
||||||
// @param cid the source connection cid, for the on_dvr is async call.
|
// @param cid the source connection cid, for the on_dvr is async call.
|
||||||
static srs_error_t on_hls_notify(int cid, std::string url, SrsRequest* req, std::string ts_url, int nb_notify);
|
static srs_error_t on_hls_notify(SrsContextId cid, std::string url, SrsRequest* req, std::string ts_url, int nb_notify);
|
||||||
// Discover co-workers for origin cluster.
|
// Discover co-workers for origin cluster.
|
||||||
static srs_error_t discover_co_workers(std::string url, std::string& host, int& port);
|
static srs_error_t discover_co_workers(std::string url, std::string& host, int& port);
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -134,7 +134,7 @@ srs_error_t SrsVodStream::serve_flv_stream(ISrsHttpResponseWriter* w, ISrsHttpMe
|
||||||
|
|
||||||
// send data
|
// send data
|
||||||
if ((err = copy(w, fs, r, (int)left)) != srs_success) {
|
if ((err = copy(w, fs, r, (int)left)) != srs_success) {
|
||||||
return srs_error_wrap(err, "read flv=%s size=%d", fullpath.c_str(), left);
|
return srs_error_wrap(err, "read flv=%s size=%d", fullpath.c_str(), (int)left);
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
@ -184,7 +184,7 @@ srs_error_t SrsVodStream::serve_mp4_stream(ISrsHttpResponseWriter* w, ISrsHttpMe
|
||||||
|
|
||||||
// send data
|
// send data
|
||||||
if ((err = copy(w, fs, r, (int)left)) != srs_success) {
|
if ((err = copy(w, fs, r, (int)left)) != srs_success) {
|
||||||
return srs_error_wrap(err, "read mp4=%s size=%d", fullpath.c_str(), left);
|
return srs_error_wrap(err, "read mp4=%s size=%d", fullpath.c_str(), (int)left);
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
|
|
@ -128,10 +128,13 @@ srs_error_t SrsBufferCache::cycle()
|
||||||
// the stream cache will create consumer to cache stream,
|
// the stream cache will create consumer to cache stream,
|
||||||
// which will trigger to fetch stream from origin for edge.
|
// which will trigger to fetch stream from origin for edge.
|
||||||
SrsConsumer* consumer = NULL;
|
SrsConsumer* consumer = NULL;
|
||||||
if ((err = source->create_consumer(NULL, consumer, false, false, true)) != srs_success) {
|
SrsAutoFree(SrsConsumer, consumer);
|
||||||
|
if ((err = source->create_consumer(consumer)) != srs_success) {
|
||||||
return srs_error_wrap(err, "create consumer");
|
return srs_error_wrap(err, "create consumer");
|
||||||
}
|
}
|
||||||
SrsAutoFree(SrsConsumer, consumer);
|
if ((err = source->consumer_dumps(consumer, false, false, true)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "dumps consumer");
|
||||||
|
}
|
||||||
|
|
||||||
SrsPithyPrint* pprint = SrsPithyPrint::create_http_stream_cache();
|
SrsPithyPrint* pprint = SrsPithyPrint::create_http_stream_cache();
|
||||||
SrsAutoFree(SrsPithyPrint, pprint);
|
SrsAutoFree(SrsPithyPrint, pprint);
|
||||||
|
@ -583,11 +586,13 @@ srs_error_t SrsLiveStream::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMess
|
||||||
|
|
||||||
// create consumer of souce, ignore gop cache, use the audio gop cache.
|
// create consumer of souce, ignore gop cache, use the audio gop cache.
|
||||||
SrsConsumer* consumer = NULL;
|
SrsConsumer* consumer = NULL;
|
||||||
if ((err = source->create_consumer(NULL, consumer, true, true, !enc->has_cache())) != srs_success) {
|
SrsAutoFree(SrsConsumer, consumer);
|
||||||
|
if ((err = source->create_consumer(consumer)) != srs_success) {
|
||||||
return srs_error_wrap(err, "create consumer");
|
return srs_error_wrap(err, "create consumer");
|
||||||
}
|
}
|
||||||
SrsAutoFree(SrsConsumer, consumer);
|
if ((err = source->consumer_dumps(consumer, true, true, !enc->has_cache())) != srs_success) {
|
||||||
srs_verbose("http: consumer created success.");
|
return srs_error_wrap(err, "dumps consumer");
|
||||||
|
}
|
||||||
|
|
||||||
SrsPithyPrint* pprint = SrsPithyPrint::create_http_stream();
|
SrsPithyPrint* pprint = SrsPithyPrint::create_http_stream();
|
||||||
SrsAutoFree(SrsPithyPrint, pprint);
|
SrsAutoFree(SrsPithyPrint, pprint);
|
||||||
|
@ -597,7 +602,7 @@ srs_error_t SrsLiveStream::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMess
|
||||||
// Use receive thread to accept the close event to avoid FD leak.
|
// Use receive thread to accept the close event to avoid FD leak.
|
||||||
// @see https://github.com/ossrs/srs/issues/636#issuecomment-298208427
|
// @see https://github.com/ossrs/srs/issues/636#issuecomment-298208427
|
||||||
SrsHttpMessage* hr = dynamic_cast<SrsHttpMessage*>(r);
|
SrsHttpMessage* hr = dynamic_cast<SrsHttpMessage*>(r);
|
||||||
SrsResponseOnlyHttpConn* hc = dynamic_cast<SrsResponseOnlyHttpConn*>(hr->connection());
|
SrsHttpConn* hc = dynamic_cast<SrsHttpConn*>(hr->connection());
|
||||||
|
|
||||||
// update the statistic when source disconveried.
|
// update the statistic when source disconveried.
|
||||||
SrsStatistic* stat = SrsStatistic::instance();
|
SrsStatistic* stat = SrsStatistic::instance();
|
||||||
|
@ -618,22 +623,28 @@ srs_error_t SrsLiveStream::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMess
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try to use fast flv encoder, remember that it maybe NULL.
|
||||||
SrsFlvStreamEncoder* ffe = dynamic_cast<SrsFlvStreamEncoder*>(enc);
|
SrsFlvStreamEncoder* ffe = dynamic_cast<SrsFlvStreamEncoder*>(enc);
|
||||||
|
|
||||||
|
// Note that the handler of hc now is rohc.
|
||||||
|
SrsResponseOnlyHttpConn* rohc = dynamic_cast<SrsResponseOnlyHttpConn*>(hc->handler());
|
||||||
|
srs_assert(rohc);
|
||||||
|
|
||||||
// Set the socket options for transport.
|
// Set the socket options for transport.
|
||||||
bool tcp_nodelay = _srs_config->get_tcp_nodelay(req->vhost);
|
bool tcp_nodelay = _srs_config->get_tcp_nodelay(req->vhost);
|
||||||
if (tcp_nodelay) {
|
if (tcp_nodelay) {
|
||||||
if ((err = hc->set_tcp_nodelay(tcp_nodelay)) != srs_success) {
|
if ((err = rohc->set_tcp_nodelay(tcp_nodelay)) != srs_success) {
|
||||||
return srs_error_wrap(err, "set tcp nodelay");
|
return srs_error_wrap(err, "set tcp nodelay");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_utime_t mw_sleep = _srs_config->get_mw_sleep(req->vhost);
|
srs_utime_t mw_sleep = _srs_config->get_mw_sleep(req->vhost);
|
||||||
if ((err = hc->set_socket_buffer(mw_sleep)) != srs_success) {
|
if ((err = rohc->set_socket_buffer(mw_sleep)) != srs_success) {
|
||||||
return srs_error_wrap(err, "set mw_sleep %" PRId64, mw_sleep);
|
return srs_error_wrap(err, "set mw_sleep %" PRId64, mw_sleep);
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsHttpRecvThread* trd = new SrsHttpRecvThread(hc);
|
// Start a thread to receive all messages from client, then drop them.
|
||||||
|
SrsHttpRecvThread* trd = new SrsHttpRecvThread(rohc);
|
||||||
SrsAutoFree(SrsHttpRecvThread, trd);
|
SrsAutoFree(SrsHttpRecvThread, trd);
|
||||||
|
|
||||||
if ((err = trd->start()) != srs_success) {
|
if ((err = trd->start()) != srs_success) {
|
||||||
|
@ -1136,8 +1147,8 @@ srs_error_t SrsHttpStreamServer::hijack(ISrsHttpMessage* request, ISrsHttpHandle
|
||||||
|
|
||||||
// trigger edge to fetch from origin.
|
// trigger edge to fetch from origin.
|
||||||
bool vhost_is_edge = _srs_config->get_vhost_is_edge(r->vhost);
|
bool vhost_is_edge = _srs_config->get_vhost_is_edge(r->vhost);
|
||||||
srs_trace("flv: source url=%s, is_edge=%d, source_id=[%d][%d]",
|
srs_trace("flv: source url=%s, is_edge=%d, source_id=%s/%s",
|
||||||
r->get_stream_url().c_str(), vhost_is_edge, ::getpid(), s->source_id());
|
r->get_stream_url().c_str(), vhost_is_edge, s->source_id().c_str(), s->pre_source_id().c_str());
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,6 +151,7 @@ srs_error_t SrsHybridServer::run()
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
// TODO: FIXME: Identify master server directly.
|
||||||
// Run master server in this main thread.
|
// Run master server in this main thread.
|
||||||
SrsServerAdapter* master_server = NULL;
|
SrsServerAdapter* master_server = NULL;
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ using namespace std;
|
||||||
SrsIngesterFFMPEG::SrsIngesterFFMPEG()
|
SrsIngesterFFMPEG::SrsIngesterFFMPEG()
|
||||||
{
|
{
|
||||||
ffmpeg = NULL;
|
ffmpeg = NULL;
|
||||||
|
starttime = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsIngesterFFMPEG::~SrsIngesterFFMPEG()
|
SrsIngesterFFMPEG::~SrsIngesterFFMPEG()
|
||||||
|
@ -194,7 +195,7 @@ void SrsIngester::fast_kill()
|
||||||
|
|
||||||
// when error, ingester sleep for a while and retry.
|
// when error, ingester sleep for a while and retry.
|
||||||
// ingest never sleep a long time, for we must start the stream ASAP.
|
// ingest never sleep a long time, for we must start the stream ASAP.
|
||||||
#define SRS_AUTO_INGESTER_CIMS (3 * SRS_UTIME_SECONDS)
|
#define SRS_INGESTER_CIMS (3 * SRS_UTIME_SECONDS)
|
||||||
|
|
||||||
srs_error_t SrsIngester::cycle()
|
srs_error_t SrsIngester::cycle()
|
||||||
{
|
{
|
||||||
|
@ -212,7 +213,7 @@ srs_error_t SrsIngester::cycle()
|
||||||
srs_freep(err);
|
srs_freep(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_usleep(SRS_AUTO_INGESTER_CIMS);
|
srs_usleep(SRS_INGESTER_CIMS);
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <srs_app_thread.hpp>
|
#include <srs_app_st.hpp>
|
||||||
#include <srs_app_reload.hpp>
|
#include <srs_app_reload.hpp>
|
||||||
|
|
||||||
class SrsFFMPEG;
|
class SrsFFMPEG;
|
||||||
|
|
|
@ -116,6 +116,51 @@ srs_netfd_t SrsUdpListener::stfd()
|
||||||
return lfd;
|
return lfd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SrsUdpListener::set_socket_buffer()
|
||||||
|
{
|
||||||
|
int default_sndbuf = 0;
|
||||||
|
// TODO: FIXME: Config it.
|
||||||
|
int expect_sndbuf = 1024*1024*10; // 10M
|
||||||
|
int actual_sndbuf = expect_sndbuf;
|
||||||
|
int r0_sndbuf = 0;
|
||||||
|
if (true) {
|
||||||
|
socklen_t opt_len = sizeof(default_sndbuf);
|
||||||
|
// TODO: FIXME: check err
|
||||||
|
getsockopt(fd(), SOL_SOCKET, SO_SNDBUF, (void*)&default_sndbuf, &opt_len);
|
||||||
|
|
||||||
|
if ((r0_sndbuf = setsockopt(fd(), SOL_SOCKET, SO_SNDBUF, (void*)&actual_sndbuf, sizeof(actual_sndbuf))) < 0) {
|
||||||
|
srs_warn("set SO_SNDBUF failed, expect=%d, r0=%d", expect_sndbuf, r0_sndbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
opt_len = sizeof(actual_sndbuf);
|
||||||
|
// TODO: FIXME: check err
|
||||||
|
getsockopt(fd(), SOL_SOCKET, SO_SNDBUF, (void*)&actual_sndbuf, &opt_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int default_rcvbuf = 0;
|
||||||
|
// TODO: FIXME: Config it.
|
||||||
|
int expect_rcvbuf = 1024*1024*10; // 10M
|
||||||
|
int actual_rcvbuf = expect_rcvbuf;
|
||||||
|
int r0_rcvbuf = 0;
|
||||||
|
if (true) {
|
||||||
|
socklen_t opt_len = sizeof(default_rcvbuf);
|
||||||
|
// TODO: FIXME: check err
|
||||||
|
getsockopt(fd(), SOL_SOCKET, SO_RCVBUF, (void*)&default_rcvbuf, &opt_len);
|
||||||
|
|
||||||
|
if ((r0_rcvbuf = setsockopt(fd(), SOL_SOCKET, SO_RCVBUF, (void*)&actual_rcvbuf, sizeof(actual_rcvbuf))) < 0) {
|
||||||
|
srs_warn("set SO_RCVBUF failed, expect=%d, r0=%d", expect_rcvbuf, r0_rcvbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
opt_len = sizeof(actual_rcvbuf);
|
||||||
|
// TODO: FIXME: check err
|
||||||
|
getsockopt(fd(), SOL_SOCKET, SO_RCVBUF, (void*)&actual_rcvbuf, &opt_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_trace("UDP #%d LISTEN at %s:%d, SO_SNDBUF(default=%d, expect=%d, actual=%d, r0=%d), SO_RCVBUF(default=%d, expect=%d, actual=%d, r0=%d)",
|
||||||
|
srs_netfd_fileno(lfd), ip.c_str(), port, default_sndbuf, expect_sndbuf, actual_sndbuf, r0_sndbuf, default_rcvbuf, expect_rcvbuf, actual_rcvbuf, r0_rcvbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
srs_error_t SrsUdpListener::listen()
|
srs_error_t SrsUdpListener::listen()
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
@ -124,10 +169,12 @@ srs_error_t SrsUdpListener::listen()
|
||||||
return srs_error_wrap(err, "listen %s:%d", ip.c_str(), port);
|
return srs_error_wrap(err, "listen %s:%d", ip.c_str(), port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set_socket_buffer();
|
||||||
|
|
||||||
handler->set_stfd(lfd);
|
handler->set_stfd(lfd);
|
||||||
|
|
||||||
srs_freep(trd);
|
srs_freep(trd);
|
||||||
trd = new SrsSTCoroutine("udp", this);
|
trd = new SrsSTCoroutine("udp", this, _srs_context->get_id());
|
||||||
if ((err = trd->start()) != srs_success) {
|
if ((err = trd->start()) != srs_success) {
|
||||||
return srs_error_wrap(err, "start thread");
|
return srs_error_wrap(err, "start thread");
|
||||||
}
|
}
|
||||||
|
@ -236,24 +283,16 @@ srs_error_t SrsTcpListener::cycle()
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
ISrsUdpSender::ISrsUdpSender()
|
SrsUdpMuxSocket::SrsUdpMuxSocket(srs_netfd_t fd)
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ISrsUdpSender::~ISrsUdpSender()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
SrsUdpMuxSocket::SrsUdpMuxSocket(ISrsUdpSender* h, srs_netfd_t fd)
|
|
||||||
{
|
{
|
||||||
nb_buf = SRS_UDP_MAX_PACKET_SIZE;
|
nb_buf = SRS_UDP_MAX_PACKET_SIZE;
|
||||||
buf = new char[nb_buf];
|
buf = new char[nb_buf];
|
||||||
nread = 0;
|
nread = 0;
|
||||||
|
|
||||||
handler = h;
|
|
||||||
lfd = fd;
|
lfd = fd;
|
||||||
|
|
||||||
fromlen = 0;
|
fromlen = 0;
|
||||||
|
peer_port = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsUdpMuxSocket::~SrsUdpMuxSocket()
|
SrsUdpMuxSocket::~SrsUdpMuxSocket()
|
||||||
|
@ -261,23 +300,6 @@ SrsUdpMuxSocket::~SrsUdpMuxSocket()
|
||||||
srs_freepa(buf);
|
srs_freepa(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsUdpMuxSocket* SrsUdpMuxSocket::copy_sendonly()
|
|
||||||
{
|
|
||||||
SrsUdpMuxSocket* sendonly = new SrsUdpMuxSocket(handler, lfd);
|
|
||||||
|
|
||||||
// Don't copy buffer
|
|
||||||
srs_freepa(sendonly->buf);
|
|
||||||
sendonly->nb_buf = 0;
|
|
||||||
sendonly->nread = 0;
|
|
||||||
sendonly->lfd = lfd;
|
|
||||||
sendonly->from = from;
|
|
||||||
sendonly->fromlen = fromlen;
|
|
||||||
sendonly->peer_ip = peer_ip;
|
|
||||||
sendonly->peer_port = peer_port;
|
|
||||||
|
|
||||||
return sendonly;
|
|
||||||
}
|
|
||||||
|
|
||||||
int SrsUdpMuxSocket::recvfrom(srs_utime_t timeout)
|
int SrsUdpMuxSocket::recvfrom(srs_utime_t timeout)
|
||||||
{
|
{
|
||||||
fromlen = sizeof(from);
|
fromlen = sizeof(from);
|
||||||
|
@ -341,7 +363,27 @@ socklen_t SrsUdpMuxSocket::peer_addrlen()
|
||||||
return (socklen_t)fromlen;
|
return (socklen_t)fromlen;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string SrsUdpMuxSocket::get_peer_id()
|
char* SrsUdpMuxSocket::data()
|
||||||
|
{
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsUdpMuxSocket::size()
|
||||||
|
{
|
||||||
|
return nread;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SrsUdpMuxSocket::get_peer_ip() const
|
||||||
|
{
|
||||||
|
return peer_ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsUdpMuxSocket::get_peer_port() const
|
||||||
|
{
|
||||||
|
return peer_port;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SrsUdpMuxSocket::peer_id()
|
||||||
{
|
{
|
||||||
char id_buf[1024];
|
char id_buf[1024];
|
||||||
int len = snprintf(id_buf, sizeof(id_buf), "%s:%d", peer_ip.c_str(), peer_port);
|
int len = snprintf(id_buf, sizeof(id_buf), "%s:%d", peer_ip.c_str(), peer_port);
|
||||||
|
@ -349,10 +391,26 @@ std::string SrsUdpMuxSocket::get_peer_id()
|
||||||
return string(id_buf, len);
|
return string(id_buf, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsUdpMuxListener::SrsUdpMuxListener(ISrsUdpMuxHandler* h, ISrsUdpSender* s, std::string i, int p)
|
SrsUdpMuxSocket* SrsUdpMuxSocket::copy_sendonly()
|
||||||
|
{
|
||||||
|
SrsUdpMuxSocket* sendonly = new SrsUdpMuxSocket(lfd);
|
||||||
|
|
||||||
|
// Don't copy buffer
|
||||||
|
srs_freepa(sendonly->buf);
|
||||||
|
sendonly->nb_buf = 0;
|
||||||
|
sendonly->nread = 0;
|
||||||
|
sendonly->lfd = lfd;
|
||||||
|
sendonly->from = from;
|
||||||
|
sendonly->fromlen = fromlen;
|
||||||
|
sendonly->peer_ip = peer_ip;
|
||||||
|
sendonly->peer_port = peer_port;
|
||||||
|
|
||||||
|
return sendonly;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsUdpMuxListener::SrsUdpMuxListener(ISrsUdpMuxHandler* h, std::string i, int p)
|
||||||
{
|
{
|
||||||
handler = h;
|
handler = h;
|
||||||
sender = s;
|
|
||||||
|
|
||||||
ip = i;
|
ip = i;
|
||||||
port = p;
|
port = p;
|
||||||
|
@ -362,6 +420,7 @@ SrsUdpMuxListener::SrsUdpMuxListener(ISrsUdpMuxHandler* h, ISrsUdpSender* s, std
|
||||||
buf = new char[nb_buf];
|
buf = new char[nb_buf];
|
||||||
|
|
||||||
trd = new SrsDummyCoroutine();
|
trd = new SrsDummyCoroutine();
|
||||||
|
cid = _srs_context->generate_id();
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsUdpMuxListener::~SrsUdpMuxListener()
|
SrsUdpMuxListener::~SrsUdpMuxListener()
|
||||||
|
@ -389,10 +448,8 @@ srs_error_t SrsUdpMuxListener::listen()
|
||||||
return srs_error_wrap(err, "listen %s:%d", ip.c_str(), port);
|
return srs_error_wrap(err, "listen %s:%d", ip.c_str(), port);
|
||||||
}
|
}
|
||||||
|
|
||||||
set_socket_buffer();
|
|
||||||
|
|
||||||
srs_freep(trd);
|
srs_freep(trd);
|
||||||
trd = new SrsSTCoroutine("udp", this);
|
trd = new SrsSTCoroutine("udp", this, cid);
|
||||||
if ((err = trd->start()) != srs_success) {
|
if ((err = trd->start()) != srs_success) {
|
||||||
return srs_error_wrap(err, "start thread");
|
return srs_error_wrap(err, "start thread");
|
||||||
}
|
}
|
||||||
|
@ -453,6 +510,11 @@ srs_error_t SrsUdpMuxListener::cycle()
|
||||||
uint64_t nn_loop = 0;
|
uint64_t nn_loop = 0;
|
||||||
srs_utime_t time_last = srs_get_system_time();
|
srs_utime_t time_last = srs_get_system_time();
|
||||||
|
|
||||||
|
SrsErrorPithyPrint* pp_pkt_handler_err = new SrsErrorPithyPrint();
|
||||||
|
SrsAutoFree(SrsErrorPithyPrint, pp_pkt_handler_err);
|
||||||
|
|
||||||
|
set_socket_buffer();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if ((err = trd->pull()) != srs_success) {
|
if ((err = trd->pull()) != srs_success) {
|
||||||
return srs_error_wrap(err, "udp listener");
|
return srs_error_wrap(err, "udp listener");
|
||||||
|
@ -460,12 +522,16 @@ srs_error_t SrsUdpMuxListener::cycle()
|
||||||
|
|
||||||
nn_loop++;
|
nn_loop++;
|
||||||
|
|
||||||
SrsUdpMuxSocket skt(sender, lfd);
|
// TODO: FIXME: Refactor the memory cache for receiver.
|
||||||
|
// Because we have to decrypt the cipher of received packet payload,
|
||||||
|
// and the size is not determined, so we think there is at least one copy,
|
||||||
|
// and we can reuse the plaintext h264/opus with players when got plaintext.
|
||||||
|
SrsUdpMuxSocket skt(lfd);
|
||||||
|
|
||||||
int nread = skt.recvfrom(SRS_UTIME_NO_TIMEOUT);
|
int nread = skt.recvfrom(SRS_UTIME_NO_TIMEOUT);
|
||||||
if (nread <= 0) {
|
if (nread <= 0) {
|
||||||
if (nread < 0) {
|
if (nread < 0) {
|
||||||
srs_warn("udp recv error");
|
srs_warn("udp recv error nn=%d", nread);
|
||||||
}
|
}
|
||||||
// remux udp never return
|
// remux udp never return
|
||||||
continue;
|
continue;
|
||||||
|
@ -474,10 +540,20 @@ srs_error_t SrsUdpMuxListener::cycle()
|
||||||
nn_msgs++;
|
nn_msgs++;
|
||||||
nn_msgs_stage++;
|
nn_msgs_stage++;
|
||||||
|
|
||||||
if ((err = handler->on_udp_packet(&skt)) != srs_success) {
|
// Restore context when packets processed.
|
||||||
// remux udp never return
|
if (true) {
|
||||||
srs_warn("udp packet handler error:%s", srs_error_desc(err).c_str());
|
SrsContextRestore(cid);
|
||||||
srs_error_reset(err);
|
err = handler->on_udp_packet(&skt);
|
||||||
|
}
|
||||||
|
// Use pithy print to show more smart information.
|
||||||
|
if (err != srs_success) {
|
||||||
|
uint32_t nn = 0;
|
||||||
|
if (pp_pkt_handler_err->can_print(err, &nn)) {
|
||||||
|
// Append more information.
|
||||||
|
err = srs_error_wrap(err, "size=%u, data=[%s]", skt.size(), srs_string_dumps_hex(skt.data(), skt.size(), 8).c_str());
|
||||||
|
srs_warn("handle udp pkt, count=%u/%u, err: %s", pp_pkt_handler_err->nn_count, nn, srs_error_desc(err).c_str());
|
||||||
|
}
|
||||||
|
srs_freep(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
pprint->elapse();
|
pprint->elapse();
|
||||||
|
|
|
@ -32,7 +32,6 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <srs_app_st.hpp>
|
#include <srs_app_st.hpp>
|
||||||
#include <srs_app_thread.hpp>
|
|
||||||
|
|
||||||
struct sockaddr;
|
struct sockaddr;
|
||||||
|
|
||||||
|
@ -102,6 +101,8 @@ public:
|
||||||
public:
|
public:
|
||||||
virtual int fd();
|
virtual int fd();
|
||||||
virtual srs_netfd_t stfd();
|
virtual srs_netfd_t stfd();
|
||||||
|
private:
|
||||||
|
void set_socket_buffer();
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t listen();
|
virtual srs_error_t listen();
|
||||||
// Interface ISrsReusableThreadHandler.
|
// Interface ISrsReusableThreadHandler.
|
||||||
|
@ -131,27 +132,10 @@ public:
|
||||||
virtual srs_error_t cycle();
|
virtual srs_error_t cycle();
|
||||||
};
|
};
|
||||||
|
|
||||||
class ISrsUdpSender
|
// TODO: FIXME: Rename it. Refine it for performance issue.
|
||||||
{
|
|
||||||
public:
|
|
||||||
ISrsUdpSender();
|
|
||||||
virtual ~ISrsUdpSender();
|
|
||||||
public:
|
|
||||||
// Fetch a mmsghdr from sender's cache.
|
|
||||||
virtual srs_error_t fetch(mmsghdr** pphdr) = 0;
|
|
||||||
// Notify the sender to send out the msg.
|
|
||||||
virtual srs_error_t sendmmsg(mmsghdr* hdr) = 0;
|
|
||||||
// Whether sender exceed the max queue, that is, overflow.
|
|
||||||
virtual bool overflow() = 0;
|
|
||||||
// Set the queue extra ratio, for example, when mw_msgs > 0, we need larger queue.
|
|
||||||
// For r, 100 means x1, 200 means x2.
|
|
||||||
virtual void set_extra_ratio(int r) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class SrsUdpMuxSocket
|
class SrsUdpMuxSocket
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
ISrsUdpSender* handler;
|
|
||||||
char* buf;
|
char* buf;
|
||||||
int nb_buf;
|
int nb_buf;
|
||||||
int nread;
|
int nread;
|
||||||
|
@ -161,45 +145,37 @@ private:
|
||||||
std::string peer_ip;
|
std::string peer_ip;
|
||||||
int peer_port;
|
int peer_port;
|
||||||
public:
|
public:
|
||||||
SrsUdpMuxSocket(ISrsUdpSender* h, srs_netfd_t fd);
|
SrsUdpMuxSocket(srs_netfd_t fd);
|
||||||
virtual ~SrsUdpMuxSocket();
|
virtual ~SrsUdpMuxSocket();
|
||||||
|
public:
|
||||||
int recvfrom(srs_utime_t timeout);
|
int recvfrom(srs_utime_t timeout);
|
||||||
srs_error_t sendto(void* data, int size, srs_utime_t timeout);
|
srs_error_t sendto(void* data, int size, srs_utime_t timeout);
|
||||||
|
|
||||||
srs_netfd_t stfd();
|
srs_netfd_t stfd();
|
||||||
sockaddr_in* peer_addr();
|
sockaddr_in* peer_addr();
|
||||||
socklen_t peer_addrlen();
|
socklen_t peer_addrlen();
|
||||||
|
char* data();
|
||||||
char* data() { return buf; }
|
int size();
|
||||||
int size() { return nread; }
|
std::string get_peer_ip() const;
|
||||||
std::string get_peer_ip() const { return peer_ip; }
|
int get_peer_port() const;
|
||||||
int get_peer_port() const { return peer_port; }
|
std::string peer_id();
|
||||||
std::string get_peer_id();
|
|
||||||
public:
|
|
||||||
SrsUdpMuxSocket* copy_sendonly();
|
SrsUdpMuxSocket* copy_sendonly();
|
||||||
ISrsUdpSender* sender() { return handler; };
|
|
||||||
private:
|
|
||||||
// Don't allow copy, user copy_sendonly instead
|
|
||||||
SrsUdpMuxSocket(const SrsUdpMuxSocket& rhs);
|
|
||||||
SrsUdpMuxSocket& operator=(const SrsUdpMuxSocket& rhs);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class SrsUdpMuxListener : public ISrsCoroutineHandler
|
class SrsUdpMuxListener : public ISrsCoroutineHandler
|
||||||
{
|
{
|
||||||
protected:
|
private:
|
||||||
srs_netfd_t lfd;
|
srs_netfd_t lfd;
|
||||||
ISrsUdpSender* sender;
|
|
||||||
SrsCoroutine* trd;
|
SrsCoroutine* trd;
|
||||||
protected:
|
SrsContextId cid;
|
||||||
|
private:
|
||||||
char* buf;
|
char* buf;
|
||||||
int nb_buf;
|
int nb_buf;
|
||||||
protected:
|
private:
|
||||||
ISrsUdpMuxHandler* handler;
|
ISrsUdpMuxHandler* handler;
|
||||||
std::string ip;
|
std::string ip;
|
||||||
int port;
|
int port;
|
||||||
public:
|
public:
|
||||||
SrsUdpMuxListener(ISrsUdpMuxHandler* h, ISrsUdpSender* s, std::string i, int p);
|
SrsUdpMuxListener(ISrsUdpMuxHandler* h, std::string i, int p);
|
||||||
virtual ~SrsUdpMuxListener();
|
virtual ~SrsUdpMuxListener();
|
||||||
public:
|
public:
|
||||||
virtual int fd();
|
virtual int fd();
|
||||||
|
|
|
@ -37,14 +37,14 @@
|
||||||
#include <srs_kernel_utility.hpp>
|
#include <srs_kernel_utility.hpp>
|
||||||
|
|
||||||
// the max size of a line of log.
|
// the max size of a line of log.
|
||||||
#define LOG_MAX_SIZE 4096
|
#define LOG_MAX_SIZE 8192
|
||||||
|
|
||||||
// the tail append to each log.
|
// the tail append to each log.
|
||||||
#define LOG_TAIL '\n'
|
#define LOG_TAIL '\n'
|
||||||
// reserved for the end of log data, it must be strlen(LOG_TAIL)
|
// reserved for the end of log data, it must be strlen(LOG_TAIL)
|
||||||
#define LOG_TAIL_SIZE 1
|
#define LOG_TAIL_SIZE 1
|
||||||
|
|
||||||
SrsFastLog::SrsFastLog()
|
SrsFileLog::SrsFileLog()
|
||||||
{
|
{
|
||||||
level = SrsLogLevelTrace;
|
level = SrsLogLevelTrace;
|
||||||
log_data = new char[LOG_MAX_SIZE];
|
log_data = new char[LOG_MAX_SIZE];
|
||||||
|
@ -54,7 +54,7 @@ SrsFastLog::SrsFastLog()
|
||||||
utc = false;
|
utc = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsFastLog::~SrsFastLog()
|
SrsFileLog::~SrsFileLog()
|
||||||
{
|
{
|
||||||
srs_freepa(log_data);
|
srs_freepa(log_data);
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ SrsFastLog::~SrsFastLog()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsFastLog::initialize()
|
srs_error_t SrsFileLog::initialize()
|
||||||
{
|
{
|
||||||
if (_srs_config) {
|
if (_srs_config) {
|
||||||
_srs_config->subscribe(this);
|
_srs_config->subscribe(this);
|
||||||
|
@ -81,7 +81,7 @@ srs_error_t SrsFastLog::initialize()
|
||||||
return srs_success;
|
return srs_success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SrsFastLog::reopen()
|
void SrsFileLog::reopen()
|
||||||
{
|
{
|
||||||
if (fd > 0) {
|
if (fd > 0) {
|
||||||
::close(fd);
|
::close(fd);
|
||||||
|
@ -94,7 +94,7 @@ void SrsFastLog::reopen()
|
||||||
open_log_file();
|
open_log_file();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SrsFastLog::verbose(const char* tag, int context_id, const char* fmt, ...)
|
void SrsFileLog::verbose(const char* tag, SrsContextId context_id, const char* fmt, ...)
|
||||||
{
|
{
|
||||||
if (level > SrsLogLevelVerbose) {
|
if (level > SrsLogLevelVerbose) {
|
||||||
return;
|
return;
|
||||||
|
@ -114,7 +114,7 @@ void SrsFastLog::verbose(const char* tag, int context_id, const char* fmt, ...)
|
||||||
write_log(fd, log_data, size, SrsLogLevelVerbose);
|
write_log(fd, log_data, size, SrsLogLevelVerbose);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SrsFastLog::info(const char* tag, int context_id, const char* fmt, ...)
|
void SrsFileLog::info(const char* tag, SrsContextId context_id, const char* fmt, ...)
|
||||||
{
|
{
|
||||||
if (level > SrsLogLevelInfo) {
|
if (level > SrsLogLevelInfo) {
|
||||||
return;
|
return;
|
||||||
|
@ -134,7 +134,7 @@ void SrsFastLog::info(const char* tag, int context_id, const char* fmt, ...)
|
||||||
write_log(fd, log_data, size, SrsLogLevelInfo);
|
write_log(fd, log_data, size, SrsLogLevelInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SrsFastLog::trace(const char* tag, int context_id, const char* fmt, ...)
|
void SrsFileLog::trace(const char* tag, SrsContextId context_id, const char* fmt, ...)
|
||||||
{
|
{
|
||||||
if (level > SrsLogLevelTrace) {
|
if (level > SrsLogLevelTrace) {
|
||||||
return;
|
return;
|
||||||
|
@ -154,7 +154,7 @@ void SrsFastLog::trace(const char* tag, int context_id, const char* fmt, ...)
|
||||||
write_log(fd, log_data, size, SrsLogLevelTrace);
|
write_log(fd, log_data, size, SrsLogLevelTrace);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SrsFastLog::warn(const char* tag, int context_id, const char* fmt, ...)
|
void SrsFileLog::warn(const char* tag, SrsContextId context_id, const char* fmt, ...)
|
||||||
{
|
{
|
||||||
if (level > SrsLogLevelWarn) {
|
if (level > SrsLogLevelWarn) {
|
||||||
return;
|
return;
|
||||||
|
@ -174,7 +174,7 @@ void SrsFastLog::warn(const char* tag, int context_id, const char* fmt, ...)
|
||||||
write_log(fd, log_data, size, SrsLogLevelWarn);
|
write_log(fd, log_data, size, SrsLogLevelWarn);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SrsFastLog::error(const char* tag, int context_id, const char* fmt, ...)
|
void SrsFileLog::error(const char* tag, SrsContextId context_id, const char* fmt, ...)
|
||||||
{
|
{
|
||||||
if (level > SrsLogLevelError) {
|
if (level > SrsLogLevelError) {
|
||||||
return;
|
return;
|
||||||
|
@ -200,14 +200,14 @@ void SrsFastLog::error(const char* tag, int context_id, const char* fmt, ...)
|
||||||
write_log(fd, log_data, size, SrsLogLevelError);
|
write_log(fd, log_data, size, SrsLogLevelError);
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsFastLog::on_reload_utc_time()
|
srs_error_t SrsFileLog::on_reload_utc_time()
|
||||||
{
|
{
|
||||||
utc = _srs_config->get_utc_time();
|
utc = _srs_config->get_utc_time();
|
||||||
|
|
||||||
return srs_success;
|
return srs_success;
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsFastLog::on_reload_log_tank()
|
srs_error_t SrsFileLog::on_reload_log_tank()
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
@ -234,7 +234,7 @@ srs_error_t SrsFastLog::on_reload_log_tank()
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsFastLog::on_reload_log_level()
|
srs_error_t SrsFileLog::on_reload_log_level()
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
@ -247,7 +247,7 @@ srs_error_t SrsFastLog::on_reload_log_level()
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsFastLog::on_reload_log_file()
|
srs_error_t SrsFileLog::on_reload_log_file()
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
@ -267,7 +267,7 @@ srs_error_t SrsFastLog::on_reload_log_file()
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SrsFastLog::write_log(int& fd, char *str_log, int size, int level)
|
void SrsFileLog::write_log(int& fd, char *str_log, int size, int level)
|
||||||
{
|
{
|
||||||
// ensure the tail and EOF of string
|
// ensure the tail and EOF of string
|
||||||
// LOG_TAIL_SIZE for the TAIL char.
|
// LOG_TAIL_SIZE for the TAIL char.
|
||||||
|
@ -307,7 +307,7 @@ void SrsFastLog::write_log(int& fd, char *str_log, int size, int level)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SrsFastLog::open_log_file()
|
void SrsFileLog::open_log_file()
|
||||||
{
|
{
|
||||||
if (!_srs_config) {
|
if (!_srs_config) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -32,10 +32,18 @@
|
||||||
#include <srs_app_reload.hpp>
|
#include <srs_app_reload.hpp>
|
||||||
#include <srs_service_log.hpp>
|
#include <srs_service_log.hpp>
|
||||||
|
|
||||||
|
// For log TAGs.
|
||||||
|
#define TAG_MAIN "MAIN"
|
||||||
|
#define TAG_MAYBE "MAYBE"
|
||||||
|
#define TAG_DTLS_ALERT "DTLS_ALERT"
|
||||||
|
#define TAG_DTLS_HANG "DTLS_HANG"
|
||||||
|
#define TAG_RESOURCE_UNSUB "RESOURCE_UNSUB"
|
||||||
|
#define TAG_LARGE_TIMER "LARGE_TIMER"
|
||||||
|
|
||||||
// Use memory/disk cache and donot flush when write log.
|
// Use memory/disk cache and donot flush when write log.
|
||||||
// it's ok to use it without config, which will log to console, and default trace level.
|
// it's ok to use it without config, which will log to console, and default trace level.
|
||||||
// when you want to use different level, override this classs, set the protected _level.
|
// when you want to use different level, override this classs, set the protected _level.
|
||||||
class SrsFastLog : public ISrsLog, public ISrsReloadHandler
|
class SrsFileLog : public ISrsLog, public ISrsReloadHandler
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
// Defined in SrsLogLevel.
|
// Defined in SrsLogLevel.
|
||||||
|
@ -49,17 +57,17 @@ private:
|
||||||
// Whether use utc time.
|
// Whether use utc time.
|
||||||
bool utc;
|
bool utc;
|
||||||
public:
|
public:
|
||||||
SrsFastLog();
|
SrsFileLog();
|
||||||
virtual ~SrsFastLog();
|
virtual ~SrsFileLog();
|
||||||
// Interface ISrsLog
|
// Interface ISrsLog
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t initialize();
|
virtual srs_error_t initialize();
|
||||||
virtual void reopen();
|
virtual void reopen();
|
||||||
virtual void verbose(const char* tag, int context_id, const char* fmt, ...);
|
virtual void verbose(const char* tag, SrsContextId context_id, const char* fmt, ...);
|
||||||
virtual void info(const char* tag, int context_id, const char* fmt, ...);
|
virtual void info(const char* tag, SrsContextId context_id, const char* fmt, ...);
|
||||||
virtual void trace(const char* tag, int context_id, const char* fmt, ...);
|
virtual void trace(const char* tag, SrsContextId context_id, const char* fmt, ...);
|
||||||
virtual void warn(const char* tag, int context_id, const char* fmt, ...);
|
virtual void warn(const char* tag, SrsContextId context_id, const char* fmt, ...);
|
||||||
virtual void error(const char* tag, int context_id, const char* fmt, ...);
|
virtual void error(const char* tag, SrsContextId context_id, const char* fmt, ...);
|
||||||
// Interface ISrsReloadHandler.
|
// Interface ISrsReloadHandler.
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t on_reload_utc_time();
|
virtual srs_error_t on_reload_utc_time();
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <srs_app_thread.hpp>
|
#include <srs_app_st.hpp>
|
||||||
|
|
||||||
class SrsRequest;
|
class SrsRequest;
|
||||||
class SrsPithyPrint;
|
class SrsPithyPrint;
|
||||||
|
|
|
@ -24,18 +24,20 @@
|
||||||
#include <srs_app_pithy_print.hpp>
|
#include <srs_app_pithy_print.hpp>
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <map>
|
using namespace std;
|
||||||
|
|
||||||
#include <srs_kernel_log.hpp>
|
#include <srs_kernel_log.hpp>
|
||||||
#include <srs_app_config.hpp>
|
#include <srs_app_config.hpp>
|
||||||
#include <srs_kernel_error.hpp>
|
#include <srs_kernel_error.hpp>
|
||||||
#include <srs_kernel_utility.hpp>
|
#include <srs_kernel_utility.hpp>
|
||||||
|
|
||||||
SrsStageInfo::SrsStageInfo(int _stage_id)
|
SrsStageInfo::SrsStageInfo(int _stage_id, double ratio)
|
||||||
{
|
{
|
||||||
stage_id = _stage_id;
|
stage_id = _stage_id;
|
||||||
nb_clients = 0;
|
nb_clients = 0;
|
||||||
age = 0;
|
age = 0;
|
||||||
|
nn_count = 0;
|
||||||
|
interval_ratio = ratio;
|
||||||
|
|
||||||
update_print_time();
|
update_print_time();
|
||||||
|
|
||||||
|
@ -59,7 +61,7 @@ void SrsStageInfo::elapse(srs_utime_t diff)
|
||||||
|
|
||||||
bool SrsStageInfo::can_print()
|
bool SrsStageInfo::can_print()
|
||||||
{
|
{
|
||||||
srs_utime_t can_print_age = nb_clients * interval;
|
srs_utime_t can_print_age = nb_clients * (srs_utime_t)(interval_ratio * interval);
|
||||||
|
|
||||||
bool can_print = age >= can_print_age;
|
bool can_print = age >= can_print_age;
|
||||||
if (can_print) {
|
if (can_print) {
|
||||||
|
@ -75,7 +77,96 @@ srs_error_t SrsStageInfo::on_reload_pithy_print()
|
||||||
return srs_success;
|
return srs_success;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::map<int, SrsStageInfo*> _srs_stages;
|
SrsStageManager::SrsStageManager()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsStageManager::~SrsStageManager()
|
||||||
|
{
|
||||||
|
map<int, SrsStageInfo*>::iterator it;
|
||||||
|
for (it = stages.begin(); it != stages.end(); ++it) {
|
||||||
|
SrsStageInfo* stage = it->second;
|
||||||
|
srs_freep(stage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsStageInfo* SrsStageManager::fetch_or_create(int stage_id, bool* pnew)
|
||||||
|
{
|
||||||
|
std::map<int, SrsStageInfo*>::iterator it = stages.find(stage_id);
|
||||||
|
|
||||||
|
// Create one if not exists.
|
||||||
|
if (it == stages.end()) {
|
||||||
|
SrsStageInfo* stage = new SrsStageInfo(stage_id);
|
||||||
|
stages[stage_id] = stage;
|
||||||
|
|
||||||
|
if (pnew) {
|
||||||
|
*pnew = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stage;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exists, fetch it.
|
||||||
|
SrsStageInfo* stage = it->second;
|
||||||
|
|
||||||
|
if (pnew) {
|
||||||
|
*pnew = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stage;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsErrorPithyPrint::SrsErrorPithyPrint(double ratio)
|
||||||
|
{
|
||||||
|
nn_count = 0;
|
||||||
|
ratio_ = ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsErrorPithyPrint::~SrsErrorPithyPrint()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SrsErrorPithyPrint::can_print(srs_error_t err, uint32_t* pnn)
|
||||||
|
{
|
||||||
|
int error_code = srs_error_code(err);
|
||||||
|
return can_print(error_code, pnn);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SrsErrorPithyPrint::can_print(int error_code, uint32_t* pnn)
|
||||||
|
{
|
||||||
|
bool new_stage = false;
|
||||||
|
SrsStageInfo* stage = stages.fetch_or_create(error_code, &new_stage);
|
||||||
|
|
||||||
|
// Increase the count.
|
||||||
|
stage->nn_count++;
|
||||||
|
nn_count++;
|
||||||
|
|
||||||
|
if (pnn) {
|
||||||
|
*pnn = stage->nn_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always and only one client.
|
||||||
|
if (new_stage) {
|
||||||
|
stage->nb_clients = 1;
|
||||||
|
stage->interval_ratio = ratio_;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_utime_t tick = ticks[error_code];
|
||||||
|
if (!tick) {
|
||||||
|
ticks[error_code] = tick = srs_get_system_time();
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_utime_t diff = srs_get_system_time() - tick;
|
||||||
|
diff = srs_max(0, diff);
|
||||||
|
|
||||||
|
stage->elapse(diff);
|
||||||
|
ticks[error_code] = srs_get_system_time();
|
||||||
|
|
||||||
|
return new_stage || stage->can_print();
|
||||||
|
}
|
||||||
|
|
||||||
|
// The global stage manager for pithy print, multiple stages.
|
||||||
|
static SrsStageManager* _srs_stages = new SrsStageManager();
|
||||||
|
|
||||||
SrsPithyPrint::SrsPithyPrint(int _stage_id)
|
SrsPithyPrint::SrsPithyPrint(int _stage_id)
|
||||||
{
|
{
|
||||||
|
@ -194,16 +285,7 @@ SrsPithyPrint::~SrsPithyPrint()
|
||||||
|
|
||||||
int SrsPithyPrint::enter_stage()
|
int SrsPithyPrint::enter_stage()
|
||||||
{
|
{
|
||||||
SrsStageInfo* stage = NULL;
|
SrsStageInfo* stage = _srs_stages->fetch_or_create(stage_id);
|
||||||
|
|
||||||
std::map<int, SrsStageInfo*>::iterator it = _srs_stages.find(stage_id);
|
|
||||||
if (it == _srs_stages.end()) {
|
|
||||||
stage = new SrsStageInfo(stage_id);
|
|
||||||
_srs_stages[stage_id] = stage;
|
|
||||||
} else {
|
|
||||||
stage = it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_assert(stage != NULL);
|
srs_assert(stage != NULL);
|
||||||
client_id = stage->nb_clients++;
|
client_id = stage->nb_clients++;
|
||||||
|
|
||||||
|
@ -215,7 +297,7 @@ int SrsPithyPrint::enter_stage()
|
||||||
|
|
||||||
void SrsPithyPrint::leave_stage()
|
void SrsPithyPrint::leave_stage()
|
||||||
{
|
{
|
||||||
SrsStageInfo* stage = _srs_stages[stage_id];
|
SrsStageInfo* stage = _srs_stages->fetch_or_create(stage_id);
|
||||||
srs_assert(stage != NULL);
|
srs_assert(stage != NULL);
|
||||||
|
|
||||||
stage->nb_clients--;
|
stage->nb_clients--;
|
||||||
|
@ -226,7 +308,7 @@ void SrsPithyPrint::leave_stage()
|
||||||
|
|
||||||
void SrsPithyPrint::elapse()
|
void SrsPithyPrint::elapse()
|
||||||
{
|
{
|
||||||
SrsStageInfo* stage = _srs_stages[stage_id];
|
SrsStageInfo* stage = _srs_stages->fetch_or_create(stage_id);
|
||||||
srs_assert(stage != NULL);
|
srs_assert(stage != NULL);
|
||||||
|
|
||||||
srs_utime_t diff = srs_get_system_time() - previous_tick;
|
srs_utime_t diff = srs_get_system_time() - previous_tick;
|
||||||
|
@ -239,7 +321,7 @@ void SrsPithyPrint::elapse()
|
||||||
|
|
||||||
bool SrsPithyPrint::can_print()
|
bool SrsPithyPrint::can_print()
|
||||||
{
|
{
|
||||||
SrsStageInfo* stage = _srs_stages[stage_id];
|
SrsStageInfo* stage = _srs_stages->fetch_or_create(stage_id);
|
||||||
srs_assert(stage != NULL);
|
srs_assert(stage != NULL);
|
||||||
|
|
||||||
return stage->can_print();
|
return stage->can_print();
|
||||||
|
|
|
@ -26,6 +26,8 @@
|
||||||
|
|
||||||
#include <srs_core.hpp>
|
#include <srs_core.hpp>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
#include <srs_app_reload.hpp>
|
#include <srs_app_reload.hpp>
|
||||||
|
|
||||||
// The stage info to calc the age.
|
// The stage info to calc the age.
|
||||||
|
@ -35,10 +37,14 @@ public:
|
||||||
int stage_id;
|
int stage_id;
|
||||||
srs_utime_t interval;
|
srs_utime_t interval;
|
||||||
int nb_clients;
|
int nb_clients;
|
||||||
|
// The number of call of can_print().
|
||||||
|
uint32_t nn_count;
|
||||||
|
// The ratio for interval, 1.0 means no change.
|
||||||
|
double interval_ratio;
|
||||||
public:
|
public:
|
||||||
srs_utime_t age;
|
srs_utime_t age;
|
||||||
public:
|
public:
|
||||||
SrsStageInfo(int _stage_id);
|
SrsStageInfo(int _stage_id, double ratio = 1.0);
|
||||||
virtual ~SrsStageInfo();
|
virtual ~SrsStageInfo();
|
||||||
virtual void update_print_time();
|
virtual void update_print_time();
|
||||||
public:
|
public:
|
||||||
|
@ -48,6 +54,41 @@ public:
|
||||||
virtual srs_error_t on_reload_pithy_print();
|
virtual srs_error_t on_reload_pithy_print();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The manager for stages, it's used for a single client stage.
|
||||||
|
// Of course, we can add the multiple user support, which is SrsPithyPrint.
|
||||||
|
class SrsStageManager
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::map<int, SrsStageInfo*> stages;
|
||||||
|
public:
|
||||||
|
SrsStageManager();
|
||||||
|
virtual ~SrsStageManager();
|
||||||
|
public:
|
||||||
|
// Fetch a stage, create one if not exists.
|
||||||
|
SrsStageInfo* fetch_or_create(int stage_id, bool* pnew = NULL);
|
||||||
|
};
|
||||||
|
|
||||||
|
// The error pithy print is a single client stage manager, each stage only has one client.
|
||||||
|
// For example, we use it for error pithy print for each UDP packet processing.
|
||||||
|
class SrsErrorPithyPrint
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// The number of call of can_print().
|
||||||
|
uint32_t nn_count;
|
||||||
|
private:
|
||||||
|
double ratio_;
|
||||||
|
SrsStageManager stages;
|
||||||
|
std::map<int, srs_utime_t> ticks;
|
||||||
|
public:
|
||||||
|
SrsErrorPithyPrint(double ratio = 1.0);
|
||||||
|
virtual ~SrsErrorPithyPrint();
|
||||||
|
public:
|
||||||
|
// Whether specified stage is ready for print.
|
||||||
|
bool can_print(srs_error_t err, uint32_t* pnn = NULL);
|
||||||
|
// We also support int error code.
|
||||||
|
bool can_print(int err, uint32_t* pnn = NULL);
|
||||||
|
};
|
||||||
|
|
||||||
// The stage is used for a collection of object to do print,
|
// The stage is used for a collection of object to do print,
|
||||||
// the print time in a stage is constant and not changed,
|
// the print time in a stage is constant and not changed,
|
||||||
// that is, we always got one message to print every specified time.
|
// that is, we always got one message to print every specified time.
|
||||||
|
|
|
@ -159,12 +159,14 @@ srs_error_t srs_redirect_output(string from_file, int to_fd)
|
||||||
return srs_error_new(ERROR_FORK_OPEN_LOG, "open process %d %s failed", to_fd, from_file.c_str());
|
return srs_error_new(ERROR_FORK_OPEN_LOG, "open process %d %s failed", to_fd, from_file.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dup2(fd, to_fd) < 0) {
|
int r0 = dup2(fd, to_fd);
|
||||||
return srs_error_new(ERROR_FORK_DUP2_LOG, "dup2 process %d failed", to_fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
::close(fd);
|
::close(fd);
|
||||||
|
|
||||||
|
if (r0 < 0) {
|
||||||
|
return srs_error_new(ERROR_FORK_DUP2_LOG, "dup2 fd=%d, to=%d, file=%s failed, r0=%d",
|
||||||
|
fd, to_fd, from_file.c_str(), r0);
|
||||||
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,7 +182,7 @@ srs_error_t SrsProcess::start()
|
||||||
srs_info("fork process: %s", cli.c_str());
|
srs_info("fork process: %s", cli.c_str());
|
||||||
|
|
||||||
// for log
|
// for log
|
||||||
int cid = _srs_context->get_id();
|
SrsContextId cid = _srs_context->get_id();
|
||||||
int ppid = getpid();
|
int ppid = getpid();
|
||||||
|
|
||||||
// TODO: fork or vfork?
|
// TODO: fork or vfork?
|
||||||
|
@ -221,8 +223,8 @@ srs_error_t SrsProcess::start()
|
||||||
// log basic info to stderr.
|
// log basic info to stderr.
|
||||||
if (true) {
|
if (true) {
|
||||||
fprintf(stdout, "\n");
|
fprintf(stdout, "\n");
|
||||||
fprintf(stdout, "process ppid=%d, cid=%d, pid=%d, in=%d, out=%d, err=%d\n",
|
fprintf(stdout, "process ppid=%d, cid=%s, pid=%d, in=%d, out=%d, err=%d\n",
|
||||||
ppid, cid, getpid(), STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO);
|
ppid, cid.c_str(), getpid(), STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO);
|
||||||
fprintf(stdout, "process binary=%s, cli: %s\n", bin.c_str(), cli.c_str());
|
fprintf(stdout, "process binary=%s, cli: %s\n", bin.c_str(), cli.c_str());
|
||||||
fprintf(stdout, "process actual cli: %s\n", actual_cli.c_str());
|
fprintf(stdout, "process actual cli: %s\n", actual_cli.c_str());
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ ISrsMessagePumper::~ISrsMessagePumper()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsRecvThread::SrsRecvThread(ISrsMessagePumper* p, SrsRtmpServer* r, srs_utime_t tm, int parent_cid)
|
SrsRecvThread::SrsRecvThread(ISrsMessagePumper* p, SrsRtmpServer* r, srs_utime_t tm, SrsContextId parent_cid)
|
||||||
{
|
{
|
||||||
rtmp = r;
|
rtmp = r;
|
||||||
pumper = p;
|
pumper = p;
|
||||||
|
@ -71,7 +71,7 @@ SrsRecvThread::~SrsRecvThread()
|
||||||
srs_freep(trd);
|
srs_freep(trd);
|
||||||
}
|
}
|
||||||
|
|
||||||
int SrsRecvThread::cid()
|
SrsContextId SrsRecvThread::cid()
|
||||||
{
|
{
|
||||||
return trd->cid();
|
return trd->cid();
|
||||||
}
|
}
|
||||||
|
@ -83,6 +83,9 @@ srs_error_t SrsRecvThread::start()
|
||||||
srs_freep(trd);
|
srs_freep(trd);
|
||||||
trd = new SrsSTCoroutine("recv", this, _parent_cid);
|
trd = new SrsSTCoroutine("recv", this, _parent_cid);
|
||||||
|
|
||||||
|
//change stack size to 256K, fix crash when call some 3rd-part api.
|
||||||
|
((SrsSTCoroutine*)trd)->set_stack_size(1 << 18);
|
||||||
|
|
||||||
if ((err = trd->start()) != srs_success) {
|
if ((err = trd->start()) != srs_success) {
|
||||||
return srs_error_wrap(err, "recv thread");
|
return srs_error_wrap(err, "recv thread");
|
||||||
}
|
}
|
||||||
|
@ -161,7 +164,7 @@ srs_error_t SrsRecvThread::do_cycle()
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsQueueRecvThread::SrsQueueRecvThread(SrsConsumer* consumer, SrsRtmpServer* rtmp_sdk, srs_utime_t tm, int parent_cid)
|
SrsQueueRecvThread::SrsQueueRecvThread(SrsConsumer* consumer, SrsRtmpServer* rtmp_sdk, srs_utime_t tm, SrsContextId parent_cid)
|
||||||
: trd(this, rtmp_sdk, tm, parent_cid)
|
: trd(this, rtmp_sdk, tm, parent_cid)
|
||||||
{
|
{
|
||||||
_consumer = consumer;
|
_consumer = consumer;
|
||||||
|
@ -278,7 +281,7 @@ void SrsQueueRecvThread::on_stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsPublishRecvThread::SrsPublishRecvThread(SrsRtmpServer* rtmp_sdk, SrsRequest* _req,
|
SrsPublishRecvThread::SrsPublishRecvThread(SrsRtmpServer* rtmp_sdk, SrsRequest* _req,
|
||||||
int mr_sock_fd, srs_utime_t tm, SrsRtmpConn* conn, SrsSource* source, int parent_cid)
|
int mr_sock_fd, srs_utime_t tm, SrsRtmpConn* conn, SrsSource* source, SrsContextId parent_cid)
|
||||||
: trd(this, rtmp_sdk, tm, parent_cid)
|
: trd(this, rtmp_sdk, tm, parent_cid)
|
||||||
{
|
{
|
||||||
rtmp = rtmp_sdk;
|
rtmp = rtmp_sdk;
|
||||||
|
@ -290,7 +293,6 @@ SrsPublishRecvThread::SrsPublishRecvThread(SrsRtmpServer* rtmp_sdk, SrsRequest*
|
||||||
_nb_msgs = 0;
|
_nb_msgs = 0;
|
||||||
video_frames = 0;
|
video_frames = 0;
|
||||||
error = srs_cond_new();
|
error = srs_cond_new();
|
||||||
ncid = cid = 0;
|
|
||||||
|
|
||||||
req = _req;
|
req = _req;
|
||||||
mr_fd = mr_sock_fd;
|
mr_fd = mr_sock_fd;
|
||||||
|
@ -341,12 +343,12 @@ srs_error_t SrsPublishRecvThread::error_code()
|
||||||
return srs_error_copy(recv_error);
|
return srs_error_copy(recv_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SrsPublishRecvThread::set_cid(int v)
|
void SrsPublishRecvThread::set_cid(SrsContextId v)
|
||||||
{
|
{
|
||||||
ncid = v;
|
ncid = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SrsPublishRecvThread::get_cid()
|
SrsContextId SrsPublishRecvThread::get_cid()
|
||||||
{
|
{
|
||||||
return ncid;
|
return ncid;
|
||||||
}
|
}
|
||||||
|
@ -374,7 +376,7 @@ srs_error_t SrsPublishRecvThread::consume(SrsCommonMessage* msg)
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
// when cid changed, change it.
|
// when cid changed, change it.
|
||||||
if (ncid != cid) {
|
if (ncid.compare(cid)) {
|
||||||
_srs_context->set_id(ncid);
|
_srs_context->set_id(ncid);
|
||||||
cid = ncid;
|
cid = ncid;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,8 +27,9 @@
|
||||||
#include <srs_core.hpp>
|
#include <srs_core.hpp>
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include <srs_app_thread.hpp>
|
#include <srs_app_st.hpp>
|
||||||
#include <srs_protocol_stream.hpp>
|
#include <srs_protocol_stream.hpp>
|
||||||
#include <srs_core_performance.hpp>
|
#include <srs_core_performance.hpp>
|
||||||
#include <srs_app_reload.hpp>
|
#include <srs_app_reload.hpp>
|
||||||
|
@ -80,16 +81,16 @@ protected:
|
||||||
SrsCoroutine* trd;
|
SrsCoroutine* trd;
|
||||||
ISrsMessagePumper* pumper;
|
ISrsMessagePumper* pumper;
|
||||||
SrsRtmpServer* rtmp;
|
SrsRtmpServer* rtmp;
|
||||||
int _parent_cid;
|
SrsContextId _parent_cid;
|
||||||
// The recv timeout in srs_utime_t.
|
// The recv timeout in srs_utime_t.
|
||||||
srs_utime_t timeout;
|
srs_utime_t timeout;
|
||||||
public:
|
public:
|
||||||
// Constructor.
|
// Constructor.
|
||||||
// @param tm The receive timeout in srs_utime_t.
|
// @param tm The receive timeout in srs_utime_t.
|
||||||
SrsRecvThread(ISrsMessagePumper* p, SrsRtmpServer* r, srs_utime_t tm, int parent_cid);
|
SrsRecvThread(ISrsMessagePumper* p, SrsRtmpServer* r, srs_utime_t tm, SrsContextId parent_cid);
|
||||||
virtual ~SrsRecvThread();
|
virtual ~SrsRecvThread();
|
||||||
public:
|
public:
|
||||||
virtual int cid();
|
virtual SrsContextId cid();
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t start();
|
virtual srs_error_t start();
|
||||||
virtual void stop();
|
virtual void stop();
|
||||||
|
@ -116,7 +117,7 @@ private:
|
||||||
SrsConsumer* _consumer;
|
SrsConsumer* _consumer;
|
||||||
public:
|
public:
|
||||||
// TODO: FIXME: Refine timeout in time unit.
|
// TODO: FIXME: Refine timeout in time unit.
|
||||||
SrsQueueRecvThread(SrsConsumer* consumer, SrsRtmpServer* rtmp_sdk, srs_utime_t tm, int parent_cid);
|
SrsQueueRecvThread(SrsConsumer* consumer, SrsRtmpServer* rtmp_sdk, srs_utime_t tm, SrsContextId parent_cid);
|
||||||
virtual ~SrsQueueRecvThread();
|
virtual ~SrsQueueRecvThread();
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t start();
|
virtual srs_error_t start();
|
||||||
|
@ -167,11 +168,11 @@ private:
|
||||||
// @see https://github.com/ossrs/srs/issues/244
|
// @see https://github.com/ossrs/srs/issues/244
|
||||||
srs_cond_t error;
|
srs_cond_t error;
|
||||||
// The merged context id.
|
// The merged context id.
|
||||||
int cid;
|
SrsContextId cid;
|
||||||
int ncid;
|
SrsContextId ncid;
|
||||||
public:
|
public:
|
||||||
SrsPublishRecvThread(SrsRtmpServer* rtmp_sdk, SrsRequest* _req,
|
SrsPublishRecvThread(SrsRtmpServer* rtmp_sdk, SrsRequest* _req,
|
||||||
int mr_sock_fd, srs_utime_t tm, SrsRtmpConn* conn, SrsSource* source, int parent_cid);
|
int mr_sock_fd, srs_utime_t tm, SrsRtmpConn* conn, SrsSource* source, SrsContextId parent_cid);
|
||||||
virtual ~SrsPublishRecvThread();
|
virtual ~SrsPublishRecvThread();
|
||||||
public:
|
public:
|
||||||
// Wait for error for some timeout.
|
// Wait for error for some timeout.
|
||||||
|
@ -179,8 +180,8 @@ public:
|
||||||
virtual int64_t nb_msgs();
|
virtual int64_t nb_msgs();
|
||||||
virtual uint64_t nb_video_frames();
|
virtual uint64_t nb_video_frames();
|
||||||
virtual srs_error_t error_code();
|
virtual srs_error_t error_code();
|
||||||
virtual void set_cid(int v);
|
virtual void set_cid(SrsContextId v);
|
||||||
virtual int get_cid();
|
virtual SrsContextId get_cid();
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t start();
|
virtual srs_error_t start();
|
||||||
virtual void stop();
|
virtual void stop();
|
||||||
|
|
|
@ -85,6 +85,16 @@ srs_error_t ISrsReloadHandler::on_reload_http_api_disabled()
|
||||||
return srs_success;
|
return srs_success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
srs_error_t ISrsReloadHandler::on_reload_https_api_enabled()
|
||||||
|
{
|
||||||
|
return srs_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t ISrsReloadHandler::on_reload_https_api_disabled()
|
||||||
|
{
|
||||||
|
return srs_success;
|
||||||
|
}
|
||||||
|
|
||||||
srs_error_t ISrsReloadHandler::on_reload_http_api_crossdomain()
|
srs_error_t ISrsReloadHandler::on_reload_http_api_crossdomain()
|
||||||
{
|
{
|
||||||
return srs_success;
|
return srs_success;
|
||||||
|
|
|
@ -49,6 +49,8 @@ public:
|
||||||
virtual srs_error_t on_reload_pithy_print();
|
virtual srs_error_t on_reload_pithy_print();
|
||||||
virtual srs_error_t on_reload_http_api_enabled();
|
virtual srs_error_t on_reload_http_api_enabled();
|
||||||
virtual srs_error_t on_reload_http_api_disabled();
|
virtual srs_error_t on_reload_http_api_disabled();
|
||||||
|
virtual srs_error_t on_reload_https_api_enabled();
|
||||||
|
virtual srs_error_t on_reload_https_api_disabled();
|
||||||
virtual srs_error_t on_reload_http_api_crossdomain();
|
virtual srs_error_t on_reload_http_api_crossdomain();
|
||||||
virtual srs_error_t on_reload_http_api_raw_api();
|
virtual srs_error_t on_reload_http_api_raw_api();
|
||||||
virtual srs_error_t on_reload_http_stream_enabled();
|
virtual srs_error_t on_reload_http_stream_enabled();
|
||||||
|
|
|
@ -1,370 +0,0 @@
|
||||||
/**
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2013-2020 John
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
* this software and associated documentation files (the "Software"), to deal in
|
|
||||||
* the Software without restriction, including without limitation the rights to
|
|
||||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
||||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
* subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <srs_app_rtc.hpp>
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <sstream>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
#include <srs_kernel_buffer.hpp>
|
|
||||||
#include <srs_kernel_error.hpp>
|
|
||||||
#include <srs_kernel_codec.hpp>
|
|
||||||
#include <srs_kernel_flv.hpp>
|
|
||||||
#include <srs_kernel_rtp.hpp>
|
|
||||||
#include <srs_app_config.hpp>
|
|
||||||
#include <srs_app_source.hpp>
|
|
||||||
#include <srs_core_autofree.hpp>
|
|
||||||
#include <srs_app_pithy_print.hpp>
|
|
||||||
#include <srs_kernel_utility.hpp>
|
|
||||||
#include <srs_kernel_codec.hpp>
|
|
||||||
#include <srs_kernel_file.hpp>
|
|
||||||
#include <srs_app_utility.hpp>
|
|
||||||
#include <srs_app_http_hooks.hpp>
|
|
||||||
#include <srs_protocol_format.hpp>
|
|
||||||
#include <srs_rtmp_stack.hpp>
|
|
||||||
#include <openssl/rand.h>
|
|
||||||
#include <srs_app_audio_recode.hpp>
|
|
||||||
|
|
||||||
// TODO: Add this function into SrsRtpMux class.
|
|
||||||
srs_error_t aac_raw_append_adts_header(SrsSharedPtrMessage* shared_audio, SrsFormat* format, char** pbuf, int* pnn_buf)
|
|
||||||
{
|
|
||||||
srs_error_t err = srs_success;
|
|
||||||
|
|
||||||
if (format->is_aac_sequence_header()) {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (format->audio->nb_samples != 1) {
|
|
||||||
return srs_error_new(ERROR_RTC_RTP_MUXER, "adts");
|
|
||||||
}
|
|
||||||
|
|
||||||
int nb_buf = format->audio->samples[0].size + 7;
|
|
||||||
char* buf = new char[nb_buf];
|
|
||||||
SrsBuffer stream(buf, nb_buf);
|
|
||||||
|
|
||||||
// TODO: Add comment.
|
|
||||||
stream.write_1bytes(0xFF);
|
|
||||||
stream.write_1bytes(0xF9);
|
|
||||||
stream.write_1bytes(((format->acodec->aac_object - 1) << 6) | ((format->acodec->aac_sample_rate & 0x0F) << 2) | ((format->acodec->aac_channels & 0x04) >> 2));
|
|
||||||
stream.write_1bytes(((format->acodec->aac_channels & 0x03) << 6) | ((nb_buf >> 11) & 0x03));
|
|
||||||
stream.write_1bytes((nb_buf >> 3) & 0xFF);
|
|
||||||
stream.write_1bytes(((nb_buf & 0x07) << 5) | 0x1F);
|
|
||||||
stream.write_1bytes(0xFC);
|
|
||||||
|
|
||||||
stream.write_bytes(format->audio->samples[0].bytes, format->audio->samples[0].size);
|
|
||||||
|
|
||||||
*pbuf = buf;
|
|
||||||
*pnn_buf = nb_buf;
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
SrsRtpH264Muxer::SrsRtpH264Muxer()
|
|
||||||
{
|
|
||||||
discard_bframe = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SrsRtpH264Muxer::~SrsRtpH264Muxer()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_error_t SrsRtpH264Muxer::filter(SrsSharedPtrMessage* shared_frame, SrsFormat* format)
|
|
||||||
{
|
|
||||||
srs_error_t err = srs_success;
|
|
||||||
|
|
||||||
// If IDR, we will insert SPS/PPS before IDR frame.
|
|
||||||
if (format->video && format->video->has_idr) {
|
|
||||||
shared_frame->set_has_idr(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update samples to shared frame.
|
|
||||||
for (int i = 0; i < format->video->nb_samples; ++i) {
|
|
||||||
SrsSample* sample = &format->video->samples[i];
|
|
||||||
|
|
||||||
// Because RTC does not support B-frame, so we will drop them.
|
|
||||||
// TODO: Drop B-frame in better way, which not cause picture corruption.
|
|
||||||
if (discard_bframe) {
|
|
||||||
if ((err = sample->parse_bframe()) != srs_success) {
|
|
||||||
return srs_error_wrap(err, "parse bframe");
|
|
||||||
}
|
|
||||||
if (sample->bframe) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (format->video->nb_samples <= 0) {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
shared_frame->set_samples(format->video->samples, format->video->nb_samples);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
SrsRtpOpusMuxer::SrsRtpOpusMuxer()
|
|
||||||
{
|
|
||||||
codec = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
SrsRtpOpusMuxer::~SrsRtpOpusMuxer()
|
|
||||||
{
|
|
||||||
srs_freep(codec);
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_error_t SrsRtpOpusMuxer::initialize()
|
|
||||||
{
|
|
||||||
srs_error_t err = srs_success;
|
|
||||||
|
|
||||||
codec = new SrsAudioRecode(kChannel, kSamplerate);
|
|
||||||
if (!codec) {
|
|
||||||
return srs_error_new(ERROR_RTC_RTP_MUXER, "SrsAacOpus init failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((err = codec->initialize()) != srs_success) {
|
|
||||||
return srs_error_wrap(err, "init codec");
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
// An AAC packet may be transcoded to many OPUS packets.
|
|
||||||
const int kMaxOpusPackets = 8;
|
|
||||||
// The max size for each OPUS packet.
|
|
||||||
const int kMaxOpusPacketSize = 4096;
|
|
||||||
|
|
||||||
srs_error_t SrsRtpOpusMuxer::transcode(SrsSharedPtrMessage* shared_audio, char* adts_audio, int nn_adts_audio)
|
|
||||||
{
|
|
||||||
srs_error_t err = srs_success;
|
|
||||||
|
|
||||||
// Opus packet cache.
|
|
||||||
static char* opus_payloads[kMaxOpusPackets];
|
|
||||||
|
|
||||||
static bool initialized = false;
|
|
||||||
if (!initialized) {
|
|
||||||
initialized = true;
|
|
||||||
|
|
||||||
static char opus_packets_cache[kMaxOpusPackets][kMaxOpusPacketSize];
|
|
||||||
opus_payloads[0] = &opus_packets_cache[0][0];
|
|
||||||
for (int i = 1; i < kMaxOpusPackets; i++) {
|
|
||||||
opus_payloads[i] = opus_packets_cache[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transcode an aac packet to many opus packets.
|
|
||||||
SrsSample aac;
|
|
||||||
aac.bytes = adts_audio;
|
|
||||||
aac.size = nn_adts_audio;
|
|
||||||
|
|
||||||
int nn_opus_packets = 0;
|
|
||||||
int opus_sizes[kMaxOpusPackets];
|
|
||||||
if ((err = codec->recode(&aac, opus_payloads, opus_sizes, nn_opus_packets)) != srs_success) {
|
|
||||||
return srs_error_wrap(err, "recode error");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save OPUS packets in shared message.
|
|
||||||
if (nn_opus_packets <= 0) {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
int nn_max_extra_payload = 0;
|
|
||||||
SrsSample samples[nn_opus_packets];
|
|
||||||
for (int i = 0; i < nn_opus_packets; i++) {
|
|
||||||
SrsSample* p = samples + i;
|
|
||||||
p->size = opus_sizes[i];
|
|
||||||
p->bytes = new char[p->size];
|
|
||||||
memcpy(p->bytes, opus_payloads[i], p->size);
|
|
||||||
|
|
||||||
nn_max_extra_payload = srs_max(nn_max_extra_payload, p->size);
|
|
||||||
}
|
|
||||||
|
|
||||||
shared_audio->set_extra_payloads(samples, nn_opus_packets);
|
|
||||||
shared_audio->set_max_extra_payload(nn_max_extra_payload);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
SrsRtc::SrsRtc()
|
|
||||||
{
|
|
||||||
req = NULL;
|
|
||||||
hub = NULL;
|
|
||||||
|
|
||||||
enabled = false;
|
|
||||||
disposable = false;
|
|
||||||
last_update_time = 0;
|
|
||||||
|
|
||||||
discard_aac = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SrsRtc::~SrsRtc()
|
|
||||||
{
|
|
||||||
srs_freep(rtp_h264_muxer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SrsRtc::dispose()
|
|
||||||
{
|
|
||||||
if (enabled) {
|
|
||||||
on_unpublish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: FIXME: Dead code?
|
|
||||||
srs_error_t SrsRtc::cycle()
|
|
||||||
{
|
|
||||||
srs_error_t err = srs_success;
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_error_t SrsRtc::initialize(SrsOriginHub* h, SrsRequest* r)
|
|
||||||
{
|
|
||||||
srs_error_t err = srs_success;
|
|
||||||
|
|
||||||
hub = h;
|
|
||||||
req = r;
|
|
||||||
|
|
||||||
rtp_h264_muxer = new SrsRtpH264Muxer();
|
|
||||||
rtp_h264_muxer->discard_bframe = _srs_config->get_rtc_bframe_discard(req->vhost);
|
|
||||||
// TODO: FIXME: Support reload and log it.
|
|
||||||
discard_aac = _srs_config->get_rtc_aac_discard(req->vhost);
|
|
||||||
|
|
||||||
rtp_opus_muxer = new SrsRtpOpusMuxer();
|
|
||||||
if (!rtp_opus_muxer) {
|
|
||||||
return srs_error_wrap(err, "rtp_opus_muxer nullptr");
|
|
||||||
}
|
|
||||||
|
|
||||||
return rtp_opus_muxer->initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_error_t SrsRtc::on_publish()
|
|
||||||
{
|
|
||||||
srs_error_t err = srs_success;
|
|
||||||
|
|
||||||
// update the hls time, for hls_dispose.
|
|
||||||
last_update_time = srs_get_system_time();
|
|
||||||
|
|
||||||
// support multiple publish.
|
|
||||||
if (enabled) {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_srs_config->get_rtc_enabled(req->vhost)) {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if enabled, open the muxer.
|
|
||||||
enabled = true;
|
|
||||||
|
|
||||||
// ok, the hls can be dispose, or need to be dispose.
|
|
||||||
disposable = true;
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SrsRtc::on_unpublish()
|
|
||||||
{
|
|
||||||
// support multiple unpublish.
|
|
||||||
if (!enabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
enabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_error_t SrsRtc::on_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* format)
|
|
||||||
{
|
|
||||||
srs_error_t err = srs_success;
|
|
||||||
|
|
||||||
if (!enabled) {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore if no format->acodec, it means the codec is not parsed, or unknown codec.
|
|
||||||
// @issue https://github.com/ossrs/srs/issues/1506#issuecomment-562079474
|
|
||||||
if (!format->acodec) {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update the hls time, for hls_dispose.
|
|
||||||
last_update_time = srs_get_system_time();
|
|
||||||
|
|
||||||
// ts support audio codec: aac/mp3
|
|
||||||
SrsAudioCodecId acodec = format->acodec->id;
|
|
||||||
if (acodec != SrsAudioCodecIdAAC && acodec != SrsAudioCodecIdMP3) {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
// When drop aac audio packet, never transcode.
|
|
||||||
if (discard_aac && acodec == SrsAudioCodecIdAAC) {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ignore sequence header
|
|
||||||
srs_assert(format->audio);
|
|
||||||
|
|
||||||
char* adts_audio = NULL;
|
|
||||||
int nn_adts_audio = 0;
|
|
||||||
// TODO: FIXME: Reserve 7 bytes header when create shared message.
|
|
||||||
if ((err = aac_raw_append_adts_header(shared_audio, format, &adts_audio, &nn_adts_audio)) != srs_success) {
|
|
||||||
return srs_error_wrap(err, "aac append header");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (adts_audio) {
|
|
||||||
err = rtp_opus_muxer->transcode(shared_audio, adts_audio, nn_adts_audio);
|
|
||||||
srs_freep(adts_audio);
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_error_t SrsRtc::on_video(SrsSharedPtrMessage* shared_video, SrsFormat* format)
|
|
||||||
{
|
|
||||||
srs_error_t err = srs_success;
|
|
||||||
|
|
||||||
// TODO: FIXME: Maybe it should config on vhost level.
|
|
||||||
if (!enabled) {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore if no format->vcodec, it means the codec is not parsed, or unknown codec.
|
|
||||||
// @issue https://github.com/ossrs/srs/issues/1506#issuecomment-562079474
|
|
||||||
if (!format->vcodec) {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update the hls time, for hls_dispose.
|
|
||||||
last_update_time = srs_get_system_time();
|
|
||||||
|
|
||||||
// ignore info frame,
|
|
||||||
// @see https://github.com/ossrs/srs/issues/288#issuecomment-69863909
|
|
||||||
srs_assert(format->video);
|
|
||||||
return rtp_h264_muxer->filter(shared_video, format);
|
|
||||||
}
|
|
|
@ -1,106 +0,0 @@
|
||||||
/**
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2013-2020 John
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
* this software and associated documentation files (the "Software"), to deal in
|
|
||||||
* the Software without restriction, including without limitation the rights to
|
|
||||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
||||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
* subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SRS_APP_RTC_HPP
|
|
||||||
#define SRS_APP_RTC_HPP
|
|
||||||
|
|
||||||
#include <srs_core.hpp>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
class SrsFormat;
|
|
||||||
class SrsSample;
|
|
||||||
class SrsSharedPtrMessage;
|
|
||||||
class SrsRtpSharedPacket;
|
|
||||||
class SrsRequest;
|
|
||||||
class SrsOriginHub;
|
|
||||||
class SrsAudioRecode;
|
|
||||||
class SrsBuffer;
|
|
||||||
|
|
||||||
// The RTP packet max size, should never exceed this size.
|
|
||||||
const int kRtpPacketSize = 1500;
|
|
||||||
|
|
||||||
// Payload type will rewrite in srs_app_rtc_conn.cpp when send to client.
|
|
||||||
const uint8_t kOpusPayloadType = 111;
|
|
||||||
const uint8_t kH264PayloadType = 102;
|
|
||||||
|
|
||||||
const int kChannel = 2;
|
|
||||||
const int kSamplerate = 48000;
|
|
||||||
|
|
||||||
// SSRC will rewrite in srs_app_rtc_conn.cpp when send to client.
|
|
||||||
const uint32_t kAudioSSRC = 1;
|
|
||||||
const uint32_t kVideoSSRC = 2;
|
|
||||||
|
|
||||||
// TODO: Define interface class like ISrsRtpMuxer
|
|
||||||
class SrsRtpH264Muxer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
bool discard_bframe;
|
|
||||||
public:
|
|
||||||
SrsRtpH264Muxer();
|
|
||||||
virtual ~SrsRtpH264Muxer();
|
|
||||||
public:
|
|
||||||
srs_error_t filter(SrsSharedPtrMessage* shared_video, SrsFormat* format);
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: FIXME: It's not a muxer, but a transcoder.
|
|
||||||
class SrsRtpOpusMuxer
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
SrsAudioRecode* codec;
|
|
||||||
public:
|
|
||||||
SrsRtpOpusMuxer();
|
|
||||||
virtual ~SrsRtpOpusMuxer();
|
|
||||||
virtual srs_error_t initialize();
|
|
||||||
public:
|
|
||||||
srs_error_t transcode(SrsSharedPtrMessage* shared_audio, char* adts_audio, int nn_adts_audio);
|
|
||||||
};
|
|
||||||
|
|
||||||
class SrsRtc
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
SrsRequest* req;
|
|
||||||
bool enabled;
|
|
||||||
bool disposable;
|
|
||||||
bool discard_aac;
|
|
||||||
srs_utime_t last_update_time;
|
|
||||||
SrsRtpH264Muxer* rtp_h264_muxer;
|
|
||||||
SrsRtpOpusMuxer* rtp_opus_muxer;
|
|
||||||
SrsOriginHub* hub;
|
|
||||||
public:
|
|
||||||
SrsRtc();
|
|
||||||
virtual ~SrsRtc();
|
|
||||||
public:
|
|
||||||
virtual void dispose();
|
|
||||||
virtual srs_error_t cycle();
|
|
||||||
public:
|
|
||||||
virtual srs_error_t initialize(SrsOriginHub* h, SrsRequest* r);
|
|
||||||
virtual srs_error_t on_publish();
|
|
||||||
virtual void on_unpublish();
|
|
||||||
virtual srs_error_t on_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* format);
|
|
||||||
virtual srs_error_t on_video(SrsSharedPtrMessage* shared_video, SrsFormat* format);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
825
trunk/src/app/srs_app_rtc_api.cpp
Normal file
825
trunk/src/app/srs_app_rtc_api.cpp
Normal file
|
@ -0,0 +1,825 @@
|
||||||
|
/**
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2020 Winlin
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <srs_app_rtc_api.hpp>
|
||||||
|
|
||||||
|
#include <srs_app_rtc_conn.hpp>
|
||||||
|
#include <srs_app_rtc_server.hpp>
|
||||||
|
#include <srs_protocol_json.hpp>
|
||||||
|
#include <srs_core_autofree.hpp>
|
||||||
|
#include <srs_app_http_api.hpp>
|
||||||
|
#include <srs_protocol_utility.hpp>
|
||||||
|
#include <srs_app_config.hpp>
|
||||||
|
#include <srs_app_statistic.hpp>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <deque>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
uint32_t SrsGoApiRtcPlay::ssrc_num = 0;
|
||||||
|
|
||||||
|
SrsGoApiRtcPlay::SrsGoApiRtcPlay(SrsRtcServer* server)
|
||||||
|
{
|
||||||
|
server_ = server;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsGoApiRtcPlay::~SrsGoApiRtcPlay()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Request:
|
||||||
|
// POST /rtc/v1/play/
|
||||||
|
// {
|
||||||
|
// "sdp":"offer...", "streamurl":"webrtc://r.ossrs.net/live/livestream",
|
||||||
|
// "api":'http...", "clientip":"..."
|
||||||
|
// }
|
||||||
|
// Response:
|
||||||
|
// {"sdp":"answer...", "sid":"..."}
|
||||||
|
// @see https://github.com/rtcdn/rtcdn-draft
|
||||||
|
srs_error_t SrsGoApiRtcPlay::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
SrsJsonObject* res = SrsJsonAny::object();
|
||||||
|
SrsAutoFree(SrsJsonObject, res);
|
||||||
|
|
||||||
|
if ((err = do_serve_http(w, r, res)) != srs_success) {
|
||||||
|
srs_warn("RTC error %s", srs_error_desc(err).c_str()); srs_freep(err);
|
||||||
|
return srs_api_response_code(w, r, SRS_CONSTS_HTTP_BadRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
return srs_api_response(w, r, res->dumps());
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsGoApiRtcPlay::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, SrsJsonObject* res)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
// For each RTC session, we use short-term HTTP connection.
|
||||||
|
SrsHttpHeader* hdr = w->header();
|
||||||
|
hdr->set("Connection", "Close");
|
||||||
|
|
||||||
|
// Parse req, the request json object, from body.
|
||||||
|
SrsJsonObject* req = NULL;
|
||||||
|
SrsAutoFree(SrsJsonObject, req);
|
||||||
|
if (true) {
|
||||||
|
string req_json;
|
||||||
|
if ((err = r->body_read_all(req_json)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "read body");
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsJsonAny* json = SrsJsonAny::loads(req_json);
|
||||||
|
if (!json || !json->is_object()) {
|
||||||
|
return srs_error_new(ERROR_RTC_API_BODY, "invalid body %s", req_json.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
req = json->to_object();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch params from req object.
|
||||||
|
SrsJsonAny* prop = NULL;
|
||||||
|
if ((prop = req->ensure_property_string("sdp")) == NULL) {
|
||||||
|
return srs_error_wrap(err, "not sdp");
|
||||||
|
}
|
||||||
|
string remote_sdp_str = prop->to_str();
|
||||||
|
|
||||||
|
if ((prop = req->ensure_property_string("streamurl")) == NULL) {
|
||||||
|
return srs_error_wrap(err, "not streamurl");
|
||||||
|
}
|
||||||
|
string streamurl = prop->to_str();
|
||||||
|
|
||||||
|
string clientip;
|
||||||
|
if ((prop = req->ensure_property_string("clientip")) != NULL) {
|
||||||
|
clientip = prop->to_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
string api;
|
||||||
|
if ((prop = req->ensure_property_string("api")) != NULL) {
|
||||||
|
api = prop->to_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: FIXME: Parse vhost.
|
||||||
|
// Parse app and stream from streamurl.
|
||||||
|
string app;
|
||||||
|
string stream_name;
|
||||||
|
if (true) {
|
||||||
|
string tcUrl;
|
||||||
|
srs_parse_rtmp_url(streamurl, tcUrl, stream_name);
|
||||||
|
|
||||||
|
int port;
|
||||||
|
string schema, host, vhost, param;
|
||||||
|
srs_discovery_tc_url(tcUrl, schema, host, vhost, app, stream_name, port, param);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For client to specifies the EIP of server.
|
||||||
|
string eip = r->query_get("eip");
|
||||||
|
// For client to specifies whether encrypt by SRTP.
|
||||||
|
string srtp = r->query_get("encrypt");
|
||||||
|
string dtls = r->query_get("dtls");
|
||||||
|
|
||||||
|
srs_trace("RTC play %s, api=%s, clientip=%s, app=%s, stream=%s, offer=%dB, eip=%s, srtp=%s, dtls=%s",
|
||||||
|
streamurl.c_str(), api.c_str(), clientip.c_str(), app.c_str(), stream_name.c_str(), remote_sdp_str.length(), eip.c_str(),
|
||||||
|
srtp.c_str(), dtls.c_str());
|
||||||
|
|
||||||
|
// TODO: FIXME: It seems remote_sdp doesn't represents the full SDP information.
|
||||||
|
SrsSdp remote_sdp;
|
||||||
|
if ((err = remote_sdp.parse(remote_sdp_str)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "parse sdp failed: %s", remote_sdp_str.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = check_remote_sdp(remote_sdp)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "remote sdp check failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsRequest request;
|
||||||
|
request.app = app;
|
||||||
|
request.stream = stream_name;
|
||||||
|
|
||||||
|
// TODO: FIXME: Parse vhost.
|
||||||
|
// discovery vhost, resolve the vhost from config
|
||||||
|
SrsConfDirective* parsed_vhost = _srs_config->get_vhost("");
|
||||||
|
if (parsed_vhost) {
|
||||||
|
request.vhost = parsed_vhost->arg0();
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsSdp local_sdp;
|
||||||
|
|
||||||
|
// Config for SDP and session.
|
||||||
|
local_sdp.session_config_.dtls_role = _srs_config->get_rtc_dtls_role(request.vhost);
|
||||||
|
local_sdp.session_config_.dtls_version = _srs_config->get_rtc_dtls_version(request.vhost);
|
||||||
|
|
||||||
|
// Whether enabled.
|
||||||
|
bool server_enabled = _srs_config->get_rtc_server_enabled();
|
||||||
|
bool rtc_enabled = _srs_config->get_rtc_enabled(request.vhost);
|
||||||
|
if (server_enabled && !rtc_enabled) {
|
||||||
|
srs_warn("RTC disabled in vhost %s", request.vhost.c_str());
|
||||||
|
}
|
||||||
|
if (!server_enabled || !rtc_enabled) {
|
||||||
|
return srs_error_new(ERROR_RTC_DISABLED, "Disabled server=%d, rtc=%d, vhost=%s",
|
||||||
|
server_enabled, rtc_enabled, request.vhost.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool srtp_enabled = true;
|
||||||
|
if (srtp.empty()) {
|
||||||
|
srtp_enabled = _srs_config->get_rtc_server_encrypt();
|
||||||
|
} else {
|
||||||
|
srtp_enabled = (srtp != "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool dtls_enabled = (dtls != "false");
|
||||||
|
|
||||||
|
// TODO: FIXME: When server enabled, but vhost disabled, should report error.
|
||||||
|
SrsRtcConnection* session = NULL;
|
||||||
|
if ((err = server_->create_session(&request, remote_sdp, local_sdp, eip, false, dtls_enabled, srtp_enabled, &session)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "create session, dtls=%u, srtp=%u, eip=%s", dtls_enabled, srtp_enabled, eip.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
ostringstream os;
|
||||||
|
if ((err = local_sdp.encode(os)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "encode sdp");
|
||||||
|
}
|
||||||
|
|
||||||
|
string local_sdp_str = os.str();
|
||||||
|
// Filter the \r\n to \\r\\n for JSON.
|
||||||
|
local_sdp_str = srs_string_replace(local_sdp_str.c_str(), "\r\n", "\\r\\n");
|
||||||
|
|
||||||
|
res->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
||||||
|
res->set("server", SrsJsonAny::str(SrsStatistic::instance()->server_id().c_str()));
|
||||||
|
|
||||||
|
// TODO: add candidates in response json?
|
||||||
|
|
||||||
|
res->set("sdp", SrsJsonAny::str(local_sdp_str.c_str()));
|
||||||
|
res->set("sessionid", SrsJsonAny::str(session->username().c_str()));
|
||||||
|
|
||||||
|
srs_trace("RTC username=%s, dtls=%u, srtp=%u, offer=%dB, answer=%dB", session->username().c_str(),
|
||||||
|
dtls_enabled, srtp_enabled, remote_sdp_str.length(), local_sdp_str.length());
|
||||||
|
srs_trace("RTC remote offer: %s", srs_string_replace(remote_sdp_str.c_str(), "\r\n", "\\r\\n").c_str());
|
||||||
|
srs_trace("RTC local answer: %s", local_sdp_str.c_str());
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsGoApiRtcPlay::check_remote_sdp(const SrsSdp& remote_sdp)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
if (remote_sdp.group_policy_ != "BUNDLE") {
|
||||||
|
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "now only support BUNDLE, group policy=%s", remote_sdp.group_policy_.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remote_sdp.media_descs_.empty()) {
|
||||||
|
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no media descriptions");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::vector<SrsMediaDesc>::const_iterator iter = remote_sdp.media_descs_.begin(); iter != remote_sdp.media_descs_.end(); ++iter) {
|
||||||
|
if (iter->type_ != "audio" && iter->type_ != "video") {
|
||||||
|
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "unsupport media type=%s", iter->type_.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! iter->rtcp_mux_) {
|
||||||
|
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "now only suppor rtcp-mux");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::vector<SrsMediaPayloadType>::const_iterator iter_media = iter->payload_types_.begin(); iter_media != iter->payload_types_.end(); ++iter_media) {
|
||||||
|
if (iter->sendonly_) {
|
||||||
|
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "play API only support sendrecv/recvonly");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsGoApiRtcPlay::exchange_sdp(SrsRequest* req, const SrsSdp& remote_sdp, SrsSdp& local_sdp)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
local_sdp.version_ = "0";
|
||||||
|
|
||||||
|
local_sdp.username_ = RTMP_SIG_SRS_SERVER;
|
||||||
|
local_sdp.session_id_ = srs_int2str((int64_t)this);
|
||||||
|
local_sdp.session_version_ = "2";
|
||||||
|
local_sdp.nettype_ = "IN";
|
||||||
|
local_sdp.addrtype_ = "IP4";
|
||||||
|
local_sdp.unicast_address_ = "0.0.0.0";
|
||||||
|
|
||||||
|
local_sdp.session_name_ = "SRSPlaySession";
|
||||||
|
|
||||||
|
local_sdp.msid_semantic_ = "WMS";
|
||||||
|
local_sdp.msids_.push_back(req->app + "/" + req->stream);
|
||||||
|
|
||||||
|
local_sdp.group_policy_ = "BUNDLE";
|
||||||
|
|
||||||
|
bool nack_enabled = _srs_config->get_rtc_nack_enabled(req->vhost);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < remote_sdp.media_descs_.size(); ++i) {
|
||||||
|
const SrsMediaDesc& remote_media_desc = remote_sdp.media_descs_[i];
|
||||||
|
|
||||||
|
if (remote_media_desc.is_audio()) {
|
||||||
|
local_sdp.media_descs_.push_back(SrsMediaDesc("audio"));
|
||||||
|
} else if (remote_media_desc.is_video()) {
|
||||||
|
local_sdp.media_descs_.push_back(SrsMediaDesc("video"));
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsMediaDesc& local_media_desc = local_sdp.media_descs_.back();
|
||||||
|
|
||||||
|
if (remote_media_desc.is_audio()) {
|
||||||
|
// TODO: check opus format specific param
|
||||||
|
std::vector<SrsMediaPayloadType> payloads = remote_media_desc.find_media_with_encoding_name("opus");
|
||||||
|
for (std::vector<SrsMediaPayloadType>::iterator iter = payloads.begin(); iter != payloads.end(); ++iter) {
|
||||||
|
local_media_desc.payload_types_.push_back(*iter);
|
||||||
|
SrsMediaPayloadType& payload_type = local_media_desc.payload_types_.back();
|
||||||
|
|
||||||
|
// TODO: FIXME: Only support some transport algorithms.
|
||||||
|
vector<string> rtcp_fb;
|
||||||
|
payload_type.rtcp_fb_.swap(rtcp_fb);
|
||||||
|
for (int j = 0; j < (int)rtcp_fb.size(); j++) {
|
||||||
|
if (nack_enabled) {
|
||||||
|
if (rtcp_fb.at(j) == "nack" || rtcp_fb.at(j) == "nack pli") {
|
||||||
|
payload_type.rtcp_fb_.push_back(rtcp_fb.at(j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only choose one match opus codec.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (local_media_desc.payload_types_.empty()) {
|
||||||
|
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no found valid opus payload type");
|
||||||
|
}
|
||||||
|
} else if (remote_media_desc.is_video()) {
|
||||||
|
std::deque<SrsMediaPayloadType> backup_payloads;
|
||||||
|
std::vector<SrsMediaPayloadType> payloads = remote_media_desc.find_media_with_encoding_name("H264");
|
||||||
|
for (std::vector<SrsMediaPayloadType>::iterator iter = payloads.begin(); iter != payloads.end(); ++iter) {
|
||||||
|
if (iter->format_specific_param_.empty()) {
|
||||||
|
backup_payloads.push_front(*iter);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
H264SpecificParam h264_param;
|
||||||
|
if ((err = srs_parse_h264_fmtp(iter->format_specific_param_, h264_param)) != srs_success) {
|
||||||
|
srs_error_reset(err); continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to pick the "best match" H.264 payload type.
|
||||||
|
if (h264_param.packetization_mode == "1" && h264_param.level_asymmerty_allow == "1") {
|
||||||
|
local_media_desc.payload_types_.push_back(*iter);
|
||||||
|
SrsMediaPayloadType& payload_type = local_media_desc.payload_types_.back();
|
||||||
|
|
||||||
|
// TODO: FIXME: Only support some transport algorithms.
|
||||||
|
vector<string> rtcp_fb;
|
||||||
|
payload_type.rtcp_fb_.swap(rtcp_fb);
|
||||||
|
for (int j = 0; j < (int)rtcp_fb.size(); j++) {
|
||||||
|
if (nack_enabled) {
|
||||||
|
if (rtcp_fb.at(j) == "nack" || rtcp_fb.at(j) == "nack pli") {
|
||||||
|
payload_type.rtcp_fb_.push_back(rtcp_fb.at(j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only choose first match H.264 payload type.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
backup_payloads.push_back(*iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try my best to pick at least one media payload type.
|
||||||
|
if (local_media_desc.payload_types_.empty() && ! backup_payloads.empty()) {
|
||||||
|
srs_warn("choose backup H.264 payload type=%d", backup_payloads.front().payload_type_);
|
||||||
|
local_media_desc.payload_types_.push_back(backup_payloads.front());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (local_media_desc.payload_types_.empty()) {
|
||||||
|
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no found valid H.264 payload type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
local_media_desc.mid_ = remote_media_desc.mid_;
|
||||||
|
local_sdp.groups_.push_back(local_media_desc.mid_);
|
||||||
|
|
||||||
|
local_media_desc.port_ = 9;
|
||||||
|
local_media_desc.protos_ = "UDP/TLS/RTP/SAVPF";
|
||||||
|
|
||||||
|
if (remote_media_desc.session_info_.setup_ == "active") {
|
||||||
|
local_media_desc.session_info_.setup_ = "passive";
|
||||||
|
} else if (remote_media_desc.session_info_.setup_ == "passive") {
|
||||||
|
local_media_desc.session_info_.setup_ = "active";
|
||||||
|
} else if (remote_media_desc.session_info_.setup_ == "actpass") {
|
||||||
|
local_media_desc.session_info_.setup_ = local_sdp.session_config_.dtls_role;
|
||||||
|
} else {
|
||||||
|
// @see: https://tools.ietf.org/html/rfc4145#section-4.1
|
||||||
|
// The default value of the setup attribute in an offer/answer exchange
|
||||||
|
// is 'active' in the offer and 'passive' in the answer.
|
||||||
|
local_media_desc.session_info_.setup_ = "passive";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remote_media_desc.sendonly_) {
|
||||||
|
local_media_desc.recvonly_ = true;
|
||||||
|
} else if (remote_media_desc.recvonly_) {
|
||||||
|
local_media_desc.sendonly_ = true;
|
||||||
|
} else if (remote_media_desc.sendrecv_) {
|
||||||
|
local_media_desc.sendrecv_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
local_media_desc.rtcp_mux_ = true;
|
||||||
|
local_media_desc.rtcp_rsize_ = true;
|
||||||
|
|
||||||
|
// TODO: FIXME: Avoid SSRC collision.
|
||||||
|
if (!ssrc_num) {
|
||||||
|
ssrc_num = ::getpid() * 10000 + ::getpid() * 100 + ::getpid();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (local_media_desc.sendonly_ || local_media_desc.sendrecv_) {
|
||||||
|
SrsSSRCInfo ssrc_info;
|
||||||
|
ssrc_info.ssrc_ = ++ssrc_num;
|
||||||
|
// TODO:use formated cname
|
||||||
|
ssrc_info.cname_ = "stream";
|
||||||
|
local_media_desc.ssrc_infos_.push_back(ssrc_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t SrsGoApiRtcPublish::ssrc_num = 0;
|
||||||
|
|
||||||
|
SrsGoApiRtcPublish::SrsGoApiRtcPublish(SrsRtcServer* server)
|
||||||
|
{
|
||||||
|
server_ = server;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsGoApiRtcPublish::~SrsGoApiRtcPublish()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Request:
|
||||||
|
// POST /rtc/v1/publish/
|
||||||
|
// {
|
||||||
|
// "sdp":"offer...", "streamurl":"webrtc://r.ossrs.net/live/livestream",
|
||||||
|
// "api":'http...", "clientip":"..."
|
||||||
|
// }
|
||||||
|
// Response:
|
||||||
|
// {"sdp":"answer...", "sid":"..."}
|
||||||
|
// @see https://github.com/rtcdn/rtcdn-draft
|
||||||
|
srs_error_t SrsGoApiRtcPublish::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
SrsJsonObject* res = SrsJsonAny::object();
|
||||||
|
SrsAutoFree(SrsJsonObject, res);
|
||||||
|
|
||||||
|
if ((err = do_serve_http(w, r, res)) != srs_success) {
|
||||||
|
srs_warn("RTC error %s", srs_error_desc(err).c_str()); srs_freep(err);
|
||||||
|
return srs_api_response_code(w, r, SRS_CONSTS_HTTP_BadRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
return srs_api_response(w, r, res->dumps());
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsGoApiRtcPublish::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, SrsJsonObject* res)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
// For each RTC session, we use short-term HTTP connection.
|
||||||
|
SrsHttpHeader* hdr = w->header();
|
||||||
|
hdr->set("Connection", "Close");
|
||||||
|
|
||||||
|
// Parse req, the request json object, from body.
|
||||||
|
SrsJsonObject* req = NULL;
|
||||||
|
if (true) {
|
||||||
|
string req_json;
|
||||||
|
if ((err = r->body_read_all(req_json)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "read body");
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsJsonAny* json = SrsJsonAny::loads(req_json);
|
||||||
|
if (!json || !json->is_object()) {
|
||||||
|
return srs_error_new(ERROR_RTC_API_BODY, "invalid body %s", req_json.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
req = json->to_object();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch params from req object.
|
||||||
|
SrsJsonAny* prop = NULL;
|
||||||
|
if ((prop = req->ensure_property_string("sdp")) == NULL) {
|
||||||
|
return srs_error_wrap(err, "not sdp");
|
||||||
|
}
|
||||||
|
string remote_sdp_str = prop->to_str();
|
||||||
|
|
||||||
|
if ((prop = req->ensure_property_string("streamurl")) == NULL) {
|
||||||
|
return srs_error_wrap(err, "not streamurl");
|
||||||
|
}
|
||||||
|
string streamurl = prop->to_str();
|
||||||
|
|
||||||
|
string clientip;
|
||||||
|
if ((prop = req->ensure_property_string("clientip")) != NULL) {
|
||||||
|
clientip = prop->to_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
string api;
|
||||||
|
if ((prop = req->ensure_property_string("api")) != NULL) {
|
||||||
|
api = prop->to_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse app and stream from streamurl.
|
||||||
|
string app;
|
||||||
|
string stream_name;
|
||||||
|
if (true) {
|
||||||
|
string tcUrl;
|
||||||
|
srs_parse_rtmp_url(streamurl, tcUrl, stream_name);
|
||||||
|
|
||||||
|
int port;
|
||||||
|
string schema, host, vhost, param;
|
||||||
|
srs_discovery_tc_url(tcUrl, schema, host, vhost, app, stream_name, port, param);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For client to specifies the EIP of server.
|
||||||
|
string eip = r->query_get("eip");
|
||||||
|
|
||||||
|
srs_trace("RTC publish %s, api=%s, clientip=%s, app=%s, stream=%s, offer=%dB, eip=%s",
|
||||||
|
streamurl.c_str(), api.c_str(), clientip.c_str(), app.c_str(), stream_name.c_str(), remote_sdp_str.length(), eip.c_str());
|
||||||
|
|
||||||
|
// TODO: FIXME: It seems remote_sdp doesn't represents the full SDP information.
|
||||||
|
SrsSdp remote_sdp;
|
||||||
|
if ((err = remote_sdp.parse(remote_sdp_str)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "parse sdp failed: %s", remote_sdp_str.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = check_remote_sdp(remote_sdp)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "remote sdp check failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsRequest request;
|
||||||
|
request.app = app;
|
||||||
|
request.stream = stream_name;
|
||||||
|
|
||||||
|
// TODO: FIXME: Parse vhost.
|
||||||
|
// discovery vhost, resolve the vhost from config
|
||||||
|
SrsConfDirective* parsed_vhost = _srs_config->get_vhost("");
|
||||||
|
if (parsed_vhost) {
|
||||||
|
request.vhost = parsed_vhost->arg0();
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsSdp local_sdp;
|
||||||
|
|
||||||
|
// TODO: FIXME: move to create_session.
|
||||||
|
// Config for SDP and session.
|
||||||
|
local_sdp.session_config_.dtls_role = _srs_config->get_rtc_dtls_role(request.vhost);
|
||||||
|
local_sdp.session_config_.dtls_version = _srs_config->get_rtc_dtls_version(request.vhost);
|
||||||
|
|
||||||
|
// Whether enabled.
|
||||||
|
bool server_enabled = _srs_config->get_rtc_server_enabled();
|
||||||
|
bool rtc_enabled = _srs_config->get_rtc_enabled(request.vhost);
|
||||||
|
if (server_enabled && !rtc_enabled) {
|
||||||
|
srs_warn("RTC disabled in vhost %s", request.vhost.c_str());
|
||||||
|
}
|
||||||
|
if (!server_enabled || !rtc_enabled) {
|
||||||
|
return srs_error_new(ERROR_RTC_DISABLED, "Disabled server=%d, rtc=%d, vhost=%s",
|
||||||
|
server_enabled, rtc_enabled, request.vhost.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: FIXME: When server enabled, but vhost disabled, should report error.
|
||||||
|
SrsRtcConnection* session = NULL;
|
||||||
|
if ((err = server_->create_session(&request, remote_sdp, local_sdp, eip, true, true, true, &session)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "create session");
|
||||||
|
}
|
||||||
|
|
||||||
|
ostringstream os;
|
||||||
|
if ((err = local_sdp.encode(os)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "encode sdp");
|
||||||
|
}
|
||||||
|
|
||||||
|
string local_sdp_str = os.str();
|
||||||
|
// Filter the \r\n to \\r\\n for JSON.
|
||||||
|
local_sdp_str = srs_string_replace(local_sdp_str.c_str(), "\r\n", "\\r\\n");
|
||||||
|
|
||||||
|
res->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
||||||
|
res->set("server", SrsJsonAny::str(SrsStatistic::instance()->server_id().c_str()));
|
||||||
|
|
||||||
|
// TODO: add candidates in response json?
|
||||||
|
|
||||||
|
res->set("sdp", SrsJsonAny::str(local_sdp_str.c_str()));
|
||||||
|
res->set("sessionid", SrsJsonAny::str(session->username().c_str()));
|
||||||
|
|
||||||
|
srs_trace("RTC username=%s, offer=%dB, answer=%dB", session->username().c_str(),
|
||||||
|
remote_sdp_str.length(), local_sdp_str.length());
|
||||||
|
srs_trace("RTC remote offer: %s", srs_string_replace(remote_sdp_str.c_str(), "\r\n", "\\r\\n").c_str());
|
||||||
|
srs_trace("RTC local answer: %s", local_sdp_str.c_str());
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsGoApiRtcPublish::check_remote_sdp(const SrsSdp& remote_sdp)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
if (remote_sdp.group_policy_ != "BUNDLE") {
|
||||||
|
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "now only support BUNDLE, group policy=%s", remote_sdp.group_policy_.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remote_sdp.media_descs_.empty()) {
|
||||||
|
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no media descriptions");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::vector<SrsMediaDesc>::const_iterator iter = remote_sdp.media_descs_.begin(); iter != remote_sdp.media_descs_.end(); ++iter) {
|
||||||
|
if (iter->type_ != "audio" && iter->type_ != "video") {
|
||||||
|
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "unsupport media type=%s", iter->type_.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! iter->rtcp_mux_) {
|
||||||
|
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "now only suppor rtcp-mux");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::vector<SrsMediaPayloadType>::const_iterator iter_media = iter->payload_types_.begin(); iter_media != iter->payload_types_.end(); ++iter_media) {
|
||||||
|
if (iter->recvonly_) {
|
||||||
|
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "publish API only support sendrecv/sendonly");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsGoApiRtcPublish::exchange_sdp(SrsRequest* req, const SrsSdp& remote_sdp, SrsSdp& local_sdp)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
local_sdp.version_ = "0";
|
||||||
|
|
||||||
|
local_sdp.username_ = RTMP_SIG_SRS_SERVER;
|
||||||
|
local_sdp.session_id_ = srs_int2str((int64_t)this);
|
||||||
|
local_sdp.session_version_ = "2";
|
||||||
|
local_sdp.nettype_ = "IN";
|
||||||
|
local_sdp.addrtype_ = "IP4";
|
||||||
|
local_sdp.unicast_address_ = "0.0.0.0";
|
||||||
|
|
||||||
|
local_sdp.session_name_ = "SRSPublishSession";
|
||||||
|
|
||||||
|
local_sdp.msid_semantic_ = "WMS";
|
||||||
|
local_sdp.msids_.push_back(req->app + "/" + req->stream);
|
||||||
|
|
||||||
|
local_sdp.group_policy_ = "BUNDLE";
|
||||||
|
|
||||||
|
bool nack_enabled = _srs_config->get_rtc_nack_enabled(req->vhost);
|
||||||
|
bool twcc_enabled = _srs_config->get_rtc_twcc_enabled(req->vhost);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < remote_sdp.media_descs_.size(); ++i) {
|
||||||
|
const SrsMediaDesc& remote_media_desc = remote_sdp.media_descs_[i];
|
||||||
|
|
||||||
|
if (remote_media_desc.is_audio()) {
|
||||||
|
local_sdp.media_descs_.push_back(SrsMediaDesc("audio"));
|
||||||
|
} else if (remote_media_desc.is_video()) {
|
||||||
|
local_sdp.media_descs_.push_back(SrsMediaDesc("video"));
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsMediaDesc& local_media_desc = local_sdp.media_descs_.back();
|
||||||
|
|
||||||
|
// Whether feature enabled in remote extmap.
|
||||||
|
int remote_twcc_id = 0;
|
||||||
|
if (true) {
|
||||||
|
map<int, string> extmaps = remote_media_desc.get_extmaps();
|
||||||
|
for(map<int, string>::iterator it = extmaps.begin(); it != extmaps.end(); ++it) {
|
||||||
|
if (it->second == kTWCCExt) {
|
||||||
|
remote_twcc_id = it->first;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (twcc_enabled && remote_twcc_id) {
|
||||||
|
local_media_desc.extmaps_[remote_twcc_id] = kTWCCExt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remote_media_desc.is_audio()) {
|
||||||
|
// TODO: check opus format specific param
|
||||||
|
std::vector<SrsMediaPayloadType> payloads = remote_media_desc.find_media_with_encoding_name("opus");
|
||||||
|
for (std::vector<SrsMediaPayloadType>::iterator iter = payloads.begin(); iter != payloads.end(); ++iter) {
|
||||||
|
local_media_desc.payload_types_.push_back(*iter);
|
||||||
|
SrsMediaPayloadType& payload_type = local_media_desc.payload_types_.back();
|
||||||
|
|
||||||
|
// TODO: FIXME: Only support some transport algorithms.
|
||||||
|
vector<string> rtcp_fb;
|
||||||
|
payload_type.rtcp_fb_.swap(rtcp_fb);
|
||||||
|
for (int j = 0; j < (int)rtcp_fb.size(); j++) {
|
||||||
|
if (nack_enabled) {
|
||||||
|
if (rtcp_fb.at(j) == "nack" || rtcp_fb.at(j) == "nack pli") {
|
||||||
|
payload_type.rtcp_fb_.push_back(rtcp_fb.at(j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (twcc_enabled && remote_twcc_id) {
|
||||||
|
if (rtcp_fb.at(j) == "transport-cc") {
|
||||||
|
payload_type.rtcp_fb_.push_back(rtcp_fb.at(j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only choose one match opus codec.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (local_media_desc.payload_types_.empty()) {
|
||||||
|
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no valid found opus payload type");
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (remote_media_desc.is_video()) {
|
||||||
|
std::deque<SrsMediaPayloadType> backup_payloads;
|
||||||
|
std::vector<SrsMediaPayloadType> payloads = remote_media_desc.find_media_with_encoding_name("H264");
|
||||||
|
for (std::vector<SrsMediaPayloadType>::iterator iter = payloads.begin(); iter != payloads.end(); ++iter) {
|
||||||
|
if (iter->format_specific_param_.empty()) {
|
||||||
|
backup_payloads.push_front(*iter);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
H264SpecificParam h264_param;
|
||||||
|
if ((err = srs_parse_h264_fmtp(iter->format_specific_param_, h264_param)) != srs_success) {
|
||||||
|
srs_error_reset(err); continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to pick the "best match" H.264 payload type.
|
||||||
|
if (h264_param.packetization_mode == "1" && h264_param.level_asymmerty_allow == "1") {
|
||||||
|
local_media_desc.payload_types_.push_back(*iter);
|
||||||
|
SrsMediaPayloadType& payload_type = local_media_desc.payload_types_.back();
|
||||||
|
|
||||||
|
// TODO: FIXME: Only support some transport algorithms.
|
||||||
|
vector<string> rtcp_fb;
|
||||||
|
payload_type.rtcp_fb_.swap(rtcp_fb);
|
||||||
|
for (int j = 0; j < (int)rtcp_fb.size(); j++) {
|
||||||
|
if (nack_enabled) {
|
||||||
|
if (rtcp_fb.at(j) == "nack" || rtcp_fb.at(j) == "nack pli") {
|
||||||
|
payload_type.rtcp_fb_.push_back(rtcp_fb.at(j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (twcc_enabled && remote_twcc_id) {
|
||||||
|
if (rtcp_fb.at(j) == "transport-cc") {
|
||||||
|
payload_type.rtcp_fb_.push_back(rtcp_fb.at(j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only choose first match H.264 payload type.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
backup_payloads.push_back(*iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try my best to pick at least one media payload type.
|
||||||
|
if (local_media_desc.payload_types_.empty() && ! backup_payloads.empty()) {
|
||||||
|
srs_warn("choose backup H.264 payload type=%d", backup_payloads.front().payload_type_);
|
||||||
|
local_media_desc.payload_types_.push_back(backup_payloads.front());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (local_media_desc.payload_types_.empty()) {
|
||||||
|
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no found valid H.264 payload type");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: FIXME: Support RRTR?
|
||||||
|
//local_media_desc.payload_types_.back().rtcp_fb_.push_back("rrtr");
|
||||||
|
}
|
||||||
|
|
||||||
|
local_media_desc.mid_ = remote_media_desc.mid_;
|
||||||
|
local_sdp.groups_.push_back(local_media_desc.mid_);
|
||||||
|
|
||||||
|
local_media_desc.port_ = 9;
|
||||||
|
local_media_desc.protos_ = "UDP/TLS/RTP/SAVPF";
|
||||||
|
|
||||||
|
if (remote_media_desc.session_info_.setup_ == "active") {
|
||||||
|
local_media_desc.session_info_.setup_ = "passive";
|
||||||
|
} else if (remote_media_desc.session_info_.setup_ == "passive") {
|
||||||
|
local_media_desc.session_info_.setup_ = "active";
|
||||||
|
} else if (remote_media_desc.session_info_.setup_ == "actpass") {
|
||||||
|
local_media_desc.session_info_.setup_ = local_sdp.session_config_.dtls_role;
|
||||||
|
} else {
|
||||||
|
// @see: https://tools.ietf.org/html/rfc4145#section-4.1
|
||||||
|
// The default value of the setup attribute in an offer/answer exchange
|
||||||
|
// is 'active' in the offer and 'passive' in the answer.
|
||||||
|
local_media_desc.session_info_.setup_ = "passive";
|
||||||
|
}
|
||||||
|
|
||||||
|
local_media_desc.rtcp_mux_ = true;
|
||||||
|
|
||||||
|
// For publisher, we are always sendonly.
|
||||||
|
local_media_desc.sendonly_ = false;
|
||||||
|
local_media_desc.recvonly_ = true;
|
||||||
|
local_media_desc.sendrecv_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsGoApiRtcNACK::SrsGoApiRtcNACK(SrsRtcServer* server)
|
||||||
|
{
|
||||||
|
server_ = server;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsGoApiRtcNACK::~SrsGoApiRtcNACK()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsGoApiRtcNACK::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
SrsJsonObject* res = SrsJsonAny::object();
|
||||||
|
SrsAutoFree(SrsJsonObject, res);
|
||||||
|
|
||||||
|
res->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
|
||||||
|
|
||||||
|
if ((err = do_serve_http(w, r, res)) != srs_success) {
|
||||||
|
srs_warn("RTC NACK err %s", srs_error_desc(err).c_str());
|
||||||
|
res->set("code", SrsJsonAny::integer(srs_error_code(err)));
|
||||||
|
srs_freep(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return srs_api_response(w, r, res->dumps());
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsGoApiRtcNACK::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, SrsJsonObject* res)
|
||||||
|
{
|
||||||
|
string username = r->query_get("username");
|
||||||
|
string dropv = r->query_get("drop");
|
||||||
|
|
||||||
|
SrsJsonObject* query = SrsJsonAny::object();
|
||||||
|
res->set("query", query);
|
||||||
|
|
||||||
|
query->set("username", SrsJsonAny::str(username.c_str()));
|
||||||
|
query->set("drop", SrsJsonAny::str(dropv.c_str()));
|
||||||
|
query->set("help", SrsJsonAny::str("?username=string&drop=int"));
|
||||||
|
|
||||||
|
int drop = ::atoi(dropv.c_str());
|
||||||
|
if (drop <= 0) {
|
||||||
|
return srs_error_new(ERROR_RTC_INVALID_PARAMS, "invalid drop=%s/%d", dropv.c_str(), drop);
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsRtcConnection* session = server_->find_session_by_username(username);
|
||||||
|
if (!session) {
|
||||||
|
return srs_error_new(ERROR_RTC_NO_SESSION, "no session username=%s", username.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
session->simulate_nack_drop(drop);
|
||||||
|
|
||||||
|
srs_trace("RTC: NACK session username=%s, drop=%s/%d", username.c_str(), dropv.c_str(), drop);
|
||||||
|
|
||||||
|
return srs_success;
|
||||||
|
}
|
||||||
|
|
83
trunk/src/app/srs_app_rtc_api.hpp
Normal file
83
trunk/src/app/srs_app_rtc_api.hpp
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
/**
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2020 Winlin
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SRS_APP_RTC_API_HPP
|
||||||
|
#define SRS_APP_RTC_API_HPP
|
||||||
|
|
||||||
|
#include <srs_core.hpp>
|
||||||
|
|
||||||
|
#include <srs_http_stack.hpp>
|
||||||
|
|
||||||
|
class SrsRtcServer;
|
||||||
|
class SrsRequest;
|
||||||
|
class SrsSdp;
|
||||||
|
|
||||||
|
class SrsGoApiRtcPlay : public ISrsHttpHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static uint32_t ssrc_num;
|
||||||
|
private:
|
||||||
|
SrsRtcServer* server_;
|
||||||
|
public:
|
||||||
|
SrsGoApiRtcPlay(SrsRtcServer* server);
|
||||||
|
virtual ~SrsGoApiRtcPlay();
|
||||||
|
public:
|
||||||
|
virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
|
||||||
|
private:
|
||||||
|
virtual srs_error_t do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, SrsJsonObject* res);
|
||||||
|
srs_error_t exchange_sdp(SrsRequest* req, const SrsSdp& remote_sdp, SrsSdp& local_sdp);
|
||||||
|
srs_error_t check_remote_sdp(const SrsSdp& remote_sdp);
|
||||||
|
};
|
||||||
|
|
||||||
|
class SrsGoApiRtcPublish : public ISrsHttpHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static uint32_t ssrc_num;
|
||||||
|
private:
|
||||||
|
SrsRtcServer* server_;
|
||||||
|
public:
|
||||||
|
SrsGoApiRtcPublish(SrsRtcServer* server);
|
||||||
|
virtual ~SrsGoApiRtcPublish();
|
||||||
|
public:
|
||||||
|
virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
|
||||||
|
private:
|
||||||
|
virtual srs_error_t do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, SrsJsonObject* res);
|
||||||
|
srs_error_t exchange_sdp(SrsRequest* req, const SrsSdp& remote_sdp, SrsSdp& local_sdp);
|
||||||
|
srs_error_t check_remote_sdp(const SrsSdp& remote_sdp);
|
||||||
|
};
|
||||||
|
|
||||||
|
class SrsGoApiRtcNACK : public ISrsHttpHandler
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
SrsRtcServer* server_;
|
||||||
|
public:
|
||||||
|
SrsGoApiRtcNACK(SrsRtcServer* server);
|
||||||
|
virtual ~SrsGoApiRtcNACK();
|
||||||
|
public:
|
||||||
|
virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
|
||||||
|
private:
|
||||||
|
virtual srs_error_t do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, SrsJsonObject* res);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -24,16 +24,25 @@
|
||||||
|
|
||||||
#include <srs_kernel_codec.hpp>
|
#include <srs_kernel_codec.hpp>
|
||||||
#include <srs_kernel_error.hpp>
|
#include <srs_kernel_error.hpp>
|
||||||
#include <srs_app_audio_recode.hpp>
|
#include <srs_app_rtc_codec.hpp>
|
||||||
|
|
||||||
static const int kOpusPacketMs = 20;
|
|
||||||
static const int kOpusMaxbytes = 8000;
|
|
||||||
static const int kFrameBufMax = 40960;
|
static const int kFrameBufMax = 40960;
|
||||||
static const int kPacketBufMax = 8192;
|
static const int kPacketBufMax = 8192;
|
||||||
static const int kPcmBufMax = 4096*4;
|
|
||||||
|
|
||||||
SrsAudioDecoder::SrsAudioDecoder(std::string codec)
|
static const char* id2codec_name(SrsAudioCodecId id)
|
||||||
: codec_name_(codec)
|
{
|
||||||
|
switch (id) {
|
||||||
|
case SrsAudioCodecIdAAC:
|
||||||
|
return "aac";
|
||||||
|
case SrsAudioCodecIdOpus:
|
||||||
|
return "libopus";
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsAudioDecoder::SrsAudioDecoder(SrsAudioCodecId codec)
|
||||||
|
: codec_id_(codec)
|
||||||
{
|
{
|
||||||
frame_ = NULL;
|
frame_ = NULL;
|
||||||
packet_ = NULL;
|
packet_ = NULL;
|
||||||
|
@ -60,13 +69,15 @@ srs_error_t SrsAudioDecoder::initialize()
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
if (codec_name_.compare("aac")) {
|
//check codec name,only support "aac","opus"
|
||||||
return srs_error_new(ERROR_RTC_RTP_MUXER, "Invalid codec name");
|
if (codec_id_ != SrsAudioCodecIdAAC && codec_id_ != SrsAudioCodecIdOpus) {
|
||||||
|
return srs_error_new(ERROR_RTC_RTP_MUXER, "Invalid codec name %d", codec_id_);
|
||||||
}
|
}
|
||||||
|
|
||||||
const AVCodec *codec = avcodec_find_decoder_by_name(codec_name_.c_str());
|
const char* codec_name = id2codec_name(codec_id_);
|
||||||
|
const AVCodec *codec = avcodec_find_decoder_by_name(codec_name);
|
||||||
if (!codec) {
|
if (!codec) {
|
||||||
return srs_error_new(ERROR_RTC_RTP_MUXER, "Codec not found by name");
|
return srs_error_new(ERROR_RTC_RTP_MUXER, "Codec not found by name %d(%s)", codec_id_, codec_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
codec_ctx_ = avcodec_alloc_context3(codec);
|
codec_ctx_ = avcodec_alloc_context3(codec);
|
||||||
|
@ -135,82 +146,137 @@ AVCodecContext* SrsAudioDecoder::codec_ctx()
|
||||||
return codec_ctx_;
|
return codec_ctx_;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsAudioEncoder::SrsAudioEncoder(int samplerate, int channels, int fec, int complexity)
|
SrsAudioEncoder::SrsAudioEncoder(SrsAudioCodecId codec, int samplerate, int channels)
|
||||||
: inband_fec_(fec),
|
: channels_(channels),
|
||||||
channels_(channels),
|
|
||||||
sampling_rate_(samplerate),
|
sampling_rate_(samplerate),
|
||||||
complexity_(complexity)
|
codec_id_(codec),
|
||||||
|
want_bytes_(0)
|
||||||
{
|
{
|
||||||
opus_ = NULL;
|
codec_ctx_ = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsAudioEncoder::~SrsAudioEncoder()
|
SrsAudioEncoder::~SrsAudioEncoder()
|
||||||
{
|
{
|
||||||
if (opus_) {
|
if (codec_ctx_) {
|
||||||
opus_encoder_destroy(opus_);
|
avcodec_free_context(&codec_ctx_);
|
||||||
opus_ = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (frame_) {
|
||||||
|
av_frame_free(&frame_);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsAudioEncoder::initialize()
|
srs_error_t SrsAudioEncoder::initialize()
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
int error = 0;
|
if (codec_id_ != SrsAudioCodecIdAAC && codec_id_ != SrsAudioCodecIdOpus) {
|
||||||
opus_ = opus_encoder_create(sampling_rate_, channels_, OPUS_APPLICATION_VOIP, &error);
|
return srs_error_new(ERROR_RTC_RTP_MUXER, "Invalid codec name %d", codec_id_);
|
||||||
if (error != OPUS_OK) {
|
|
||||||
return srs_error_new(ERROR_RTC_RTP_MUXER, "Error create Opus encoder");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (sampling_rate_)
|
frame_ = av_frame_alloc();
|
||||||
{
|
if (!frame_) {
|
||||||
case 48000:
|
return srs_error_new(ERROR_RTC_RTP_MUXER, "Could not allocate audio frame");
|
||||||
opus_encoder_ctl(opus_, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_FULLBAND));
|
}
|
||||||
break;
|
|
||||||
|
const char* codec_name = id2codec_name(codec_id_);
|
||||||
case 24000:
|
const AVCodec *codec = avcodec_find_encoder_by_name(codec_name);
|
||||||
opus_encoder_ctl(opus_, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_SUPERWIDEBAND));
|
if (!codec) {
|
||||||
|
return srs_error_new(ERROR_RTC_RTP_MUXER, "Codec not found by name %d(%s)", codec_id_, codec_name);
|
||||||
case 16000:
|
}
|
||||||
opus_encoder_ctl(opus_, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_WIDEBAND));
|
|
||||||
break;
|
codec_ctx_ = avcodec_alloc_context3(codec);
|
||||||
|
if (!codec_ctx_) {
|
||||||
case 12000:
|
return srs_error_new(ERROR_RTC_RTP_MUXER, "Could not allocate audio codec context");
|
||||||
opus_encoder_ctl(opus_, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_MEDIUMBAND));
|
}
|
||||||
break;
|
|
||||||
|
codec_ctx_->sample_rate = sampling_rate_;
|
||||||
case 8000:
|
codec_ctx_->channels = channels_;
|
||||||
opus_encoder_ctl(opus_, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_NARROWBAND));
|
codec_ctx_->channel_layout = av_get_default_channel_layout(channels_);
|
||||||
break;
|
codec_ctx_->bit_rate = 48000;
|
||||||
|
if (codec_id_ == SrsAudioCodecIdOpus) {
|
||||||
default:
|
codec_ctx_->sample_fmt = AV_SAMPLE_FMT_S16;
|
||||||
sampling_rate_ = 16000;
|
//TODO: for more level setting
|
||||||
opus_encoder_ctl(opus_, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_WIDEBAND));
|
codec_ctx_->compression_level = 1;
|
||||||
break;
|
} else if (codec_id_ == SrsAudioCodecIdAAC) {
|
||||||
|
codec_ctx_->sample_fmt = AV_SAMPLE_FMT_FLTP;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: FIXME: Show detail error.
|
||||||
|
if (avcodec_open2(codec_ctx_, codec, NULL) < 0) {
|
||||||
|
return srs_error_new(ERROR_RTC_RTP_MUXER, "Could not open codec");
|
||||||
|
}
|
||||||
|
|
||||||
|
want_bytes_ = codec_ctx_->channels * codec_ctx_->frame_size * av_get_bytes_per_sample(codec_ctx_->sample_fmt);
|
||||||
|
|
||||||
|
frame_->format = codec_ctx_->sample_fmt;
|
||||||
|
frame_->nb_samples = codec_ctx_->frame_size;
|
||||||
|
frame_->channel_layout = codec_ctx_->channel_layout;
|
||||||
|
|
||||||
|
if (av_frame_get_buffer(frame_, 0) < 0) {
|
||||||
|
return srs_error_new(ERROR_RTC_RTP_MUXER, "Could not get audio frame buffer");
|
||||||
}
|
}
|
||||||
opus_encoder_ctl(opus_, OPUS_SET_INBAND_FEC(inband_fec_));
|
|
||||||
opus_encoder_ctl(opus_, OPUS_SET_COMPLEXITY(complexity_));
|
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int SrsAudioEncoder::want_bytes()
|
||||||
|
{
|
||||||
|
return want_bytes_;
|
||||||
|
}
|
||||||
|
|
||||||
srs_error_t SrsAudioEncoder::encode(SrsSample *frame, char *buf, int &size)
|
srs_error_t SrsAudioEncoder::encode(SrsSample *frame, char *buf, int &size)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
int nb_samples = sampling_rate_ * kOpusPacketMs / 1000;
|
if (want_bytes_ > 0 && frame->size != want_bytes_) {
|
||||||
if (frame->size != nb_samples * 2 * channels_) {
|
return srs_error_new(ERROR_RTC_RTP_MUXER, "invalid frame size %d, should be %d", frame->size, want_bytes_);
|
||||||
return srs_error_new(ERROR_RTC_RTP_MUXER, "invalid frame size %d, should be %d", frame->size, nb_samples * 2 * channels_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
opus_int16 *data = (opus_int16 *)frame->bytes;
|
// TODO: Directly use frame?
|
||||||
size = opus_encode(opus_, data, nb_samples, (unsigned char *)buf, kOpusMaxbytes);
|
memcpy(frame_->data[0], frame->bytes, frame->size);
|
||||||
|
|
||||||
|
/* send the frame for encoding */
|
||||||
|
int r0 = avcodec_send_frame(codec_ctx_, frame_);
|
||||||
|
if (r0 < 0) {
|
||||||
|
return srs_error_new(ERROR_RTC_RTP_MUXER, "Error sending the frame to the encoder, %d", r0);
|
||||||
|
}
|
||||||
|
|
||||||
|
AVPacket pkt;
|
||||||
|
av_init_packet(&pkt);
|
||||||
|
pkt.data = NULL;
|
||||||
|
pkt.size = 0;
|
||||||
|
|
||||||
|
/* read all the available output packets (in general there may be any
|
||||||
|
* number of them */
|
||||||
|
size = 0;
|
||||||
|
while (r0 >= 0) {
|
||||||
|
r0 = avcodec_receive_packet(codec_ctx_, &pkt);
|
||||||
|
if (r0 == AVERROR(EAGAIN) || r0 == AVERROR_EOF) {
|
||||||
|
break;
|
||||||
|
} else if (r0 < 0) {
|
||||||
|
return srs_error_new(ERROR_RTC_RTP_MUXER, "Error during decoding %d", r0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: fit encoder out more pkt
|
||||||
|
memcpy(buf, pkt.data, pkt.size);
|
||||||
|
size = pkt.size;
|
||||||
|
av_packet_unref(&pkt);
|
||||||
|
|
||||||
|
// TODO: FIXME: Refine api, got more than one packets.
|
||||||
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AVCodecContext* SrsAudioEncoder::codec_ctx()
|
||||||
|
{
|
||||||
|
return codec_ctx_;
|
||||||
|
}
|
||||||
|
|
||||||
SrsAudioResample::SrsAudioResample(int src_rate, int src_layout, enum AVSampleFormat src_fmt,
|
SrsAudioResample::SrsAudioResample(int src_rate, int src_layout, enum AVSampleFormat src_fmt,
|
||||||
int src_nb, int dst_rate, int dst_layout, enum AVSampleFormat dst_fmt)
|
int src_nb, int dst_rate, int dst_layout, AVSampleFormat dst_fmt)
|
||||||
: src_rate_(src_rate),
|
: src_rate_(src_rate),
|
||||||
src_ch_layout_(src_layout),
|
src_ch_layout_(src_layout),
|
||||||
src_sample_fmt_(src_fmt),
|
src_sample_fmt_(src_fmt),
|
||||||
|
@ -329,7 +395,7 @@ srs_error_t SrsAudioResample::resample(SrsSample *pcm, char *buf, int &size)
|
||||||
|
|
||||||
int max = size;
|
int max = size;
|
||||||
size = 0;
|
size = 0;
|
||||||
if (max > dst_bufsize) {
|
if (max >= dst_bufsize) {
|
||||||
memcpy(buf, dst_data_[0], dst_bufsize);
|
memcpy(buf, dst_data_[0], dst_bufsize);
|
||||||
size = dst_bufsize;
|
size = dst_bufsize;
|
||||||
}
|
}
|
||||||
|
@ -337,55 +403,52 @@ srs_error_t SrsAudioResample::resample(SrsSample *pcm, char *buf, int &size)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsAudioRecode::SrsAudioRecode(int channels, int samplerate)
|
SrsAudioRecode::SrsAudioRecode(SrsAudioCodecId src_codec, SrsAudioCodecId dst_codec,int channels, int samplerate)
|
||||||
: dst_channels_(channels),
|
: dst_channels_(channels),
|
||||||
dst_samplerate_(samplerate)
|
dst_samplerate_(samplerate),
|
||||||
|
src_codec_(src_codec),
|
||||||
|
dst_codec_(dst_codec)
|
||||||
{
|
{
|
||||||
size_ = 0;
|
size_ = 0;
|
||||||
data_ = new char[kPcmBufMax];
|
data_ = NULL;
|
||||||
|
|
||||||
|
dec_ = NULL;
|
||||||
|
enc_ = NULL;
|
||||||
|
resample_ = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsAudioRecode::~SrsAudioRecode()
|
SrsAudioRecode::~SrsAudioRecode()
|
||||||
{
|
{
|
||||||
if (dec_) {
|
srs_freep(dec_);
|
||||||
delete dec_;
|
srs_freep(enc_);
|
||||||
dec_ = NULL;
|
srs_freep(resample_);
|
||||||
}
|
srs_freepa(data_);
|
||||||
if (enc_) {
|
|
||||||
delete enc_;
|
|
||||||
enc_ = NULL;
|
|
||||||
}
|
|
||||||
if (resample_) {
|
|
||||||
delete resample_;
|
|
||||||
resample_ = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
delete[] data_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsAudioRecode::initialize()
|
srs_error_t SrsAudioRecode::initialize()
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
dec_ = new SrsAudioDecoder("aac");
|
dec_ = new SrsAudioDecoder(src_codec_);
|
||||||
if (!dec_) {
|
if ((err = dec_->initialize()) != srs_success) {
|
||||||
return srs_error_new(ERROR_RTC_RTP_MUXER, "SrsAudioDecoder failed");
|
return srs_error_wrap(err, "dec init");
|
||||||
}
|
}
|
||||||
dec_->initialize();
|
|
||||||
|
|
||||||
enc_ = new SrsAudioEncoder(dst_samplerate_, dst_channels_, 1, 1);
|
enc_ = new SrsAudioEncoder(dst_codec_, dst_samplerate_, dst_channels_);
|
||||||
if (!enc_) {
|
if ((err = enc_->initialize()) != srs_success) {
|
||||||
return srs_error_new(ERROR_RTC_RTP_MUXER, "SrsAudioEncoder failed");
|
return srs_error_wrap(err, "enc init");
|
||||||
}
|
}
|
||||||
enc_->initialize();
|
|
||||||
|
|
||||||
resample_ = NULL;
|
enc_want_bytes_ = enc_->want_bytes();
|
||||||
|
if (enc_want_bytes_ > 0) {
|
||||||
|
data_ = new char[enc_want_bytes_];
|
||||||
|
srs_assert(data_);
|
||||||
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: FIXME: Rename to transcode.
|
srs_error_t SrsAudioRecode::transcode(SrsSample *pkt, char **buf, int *buf_len, int &n)
|
||||||
srs_error_t SrsAudioRecode::recode(SrsSample *pkt, char **buf, int *buf_len, int &n)
|
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
@ -396,7 +459,7 @@ srs_error_t SrsAudioRecode::recode(SrsSample *pkt, char **buf, int *buf_len, int
|
||||||
int decode_len = kPacketBufMax;
|
int decode_len = kPacketBufMax;
|
||||||
static char decode_buffer[kPacketBufMax];
|
static char decode_buffer[kPacketBufMax];
|
||||||
if ((err = dec_->decode(pkt, decode_buffer, decode_len)) != srs_success) {
|
if ((err = dec_->decode(pkt, decode_buffer, decode_len)) != srs_success) {
|
||||||
return srs_error_new(ERROR_RTC_RTP_MUXER, "decode error");
|
return srs_error_wrap(err, "decode error");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!resample_) {
|
if (!resample_) {
|
||||||
|
@ -404,11 +467,7 @@ srs_error_t SrsAudioRecode::recode(SrsSample *pkt, char **buf, int *buf_len, int
|
||||||
AVCodecContext *codec_ctx = dec_->codec_ctx();
|
AVCodecContext *codec_ctx = dec_->codec_ctx();
|
||||||
resample_ = new SrsAudioResample(codec_ctx->sample_rate, (int)codec_ctx->channel_layout, \
|
resample_ = new SrsAudioResample(codec_ctx->sample_rate, (int)codec_ctx->channel_layout, \
|
||||||
codec_ctx->sample_fmt, codec_ctx->frame_size, dst_samplerate_, channel_layout, \
|
codec_ctx->sample_fmt, codec_ctx->frame_size, dst_samplerate_, channel_layout, \
|
||||||
AV_SAMPLE_FMT_S16);
|
enc_->codec_ctx()->sample_fmt);
|
||||||
|
|
||||||
if (!resample_) {
|
|
||||||
return srs_error_new(ERROR_RTC_RTP_MUXER, "SrsAudioResample failed");
|
|
||||||
}
|
|
||||||
if ((err = resample_->initialize()) != srs_success) {
|
if ((err = resample_->initialize()) != srs_success) {
|
||||||
return srs_error_wrap(err, "init resample");
|
return srs_error_wrap(err, "init resample");
|
||||||
}
|
}
|
||||||
|
@ -419,50 +478,66 @@ srs_error_t SrsAudioRecode::recode(SrsSample *pkt, char **buf, int *buf_len, int
|
||||||
pcm.size = decode_len;
|
pcm.size = decode_len;
|
||||||
int resample_len = kFrameBufMax;
|
int resample_len = kFrameBufMax;
|
||||||
static char resample_buffer[kFrameBufMax];
|
static char resample_buffer[kFrameBufMax];
|
||||||
|
static char encode_buffer[kPacketBufMax];
|
||||||
if ((err = resample_->resample(&pcm, resample_buffer, resample_len)) != srs_success) {
|
if ((err = resample_->resample(&pcm, resample_buffer, resample_len)) != srs_success) {
|
||||||
return srs_error_new(ERROR_RTC_RTP_MUXER, "resample error");
|
return srs_error_wrap(err, "resample error");
|
||||||
}
|
}
|
||||||
|
|
||||||
n = 0;
|
n = 0;
|
||||||
int data_left = resample_len;
|
|
||||||
int total;
|
|
||||||
total = (dst_samplerate_ * kOpusPacketMs / 1000) * 2 * dst_channels_;
|
|
||||||
|
|
||||||
if (size_ + data_left < total) {
|
// We can encode it in one time.
|
||||||
|
if (enc_want_bytes_ <= 0) {
|
||||||
|
int encode_len;
|
||||||
|
pcm.bytes = (char *)data_;
|
||||||
|
pcm.size = size_;
|
||||||
|
if ((err = enc_->encode(&pcm, encode_buffer, encode_len)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "encode error");
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(buf[n], encode_buffer, encode_len);
|
||||||
|
buf_len[n] = encode_len;
|
||||||
|
n++;
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need to refill the sample to data, because the frame size is not matched to encoder.
|
||||||
|
int data_left = resample_len;
|
||||||
|
if (size_ + data_left < enc_want_bytes_) {
|
||||||
memcpy(data_ + size_, resample_buffer, data_left);
|
memcpy(data_ + size_, resample_buffer, data_left);
|
||||||
size_ += data_left;
|
size_ += data_left;
|
||||||
} else {
|
return err;
|
||||||
int index = 0;
|
}
|
||||||
while (1) {
|
|
||||||
data_left = data_left - (total - size_);
|
|
||||||
memcpy(data_ + size_, resample_buffer + index, total - size_);
|
|
||||||
index += total - size_;
|
|
||||||
size_ += total - size_;
|
|
||||||
if (!enc_) {
|
|
||||||
return srs_error_new(ERROR_RTC_RTP_MUXER, "enc_ nullptr");
|
|
||||||
}
|
|
||||||
|
|
||||||
int encode_len;
|
int index = 0;
|
||||||
pcm.bytes = (char *)data_;
|
while (1) {
|
||||||
pcm.size = size_;
|
data_left = data_left - (enc_want_bytes_ - size_);
|
||||||
static char encode_buffer[kPacketBufMax];
|
memcpy(data_ + size_, resample_buffer + index, enc_want_bytes_ - size_);
|
||||||
if ((err = enc_->encode(&pcm, encode_buffer, encode_len)) != srs_success) {
|
index += enc_want_bytes_ - size_;
|
||||||
return srs_error_new(ERROR_RTC_RTP_MUXER, "encode error");
|
size_ += enc_want_bytes_ - size_;
|
||||||
}
|
|
||||||
|
|
||||||
|
int encode_len;
|
||||||
|
pcm.bytes = (char *)data_;
|
||||||
|
pcm.size = size_;
|
||||||
|
if ((err = enc_->encode(&pcm, encode_buffer, encode_len)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "encode error");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (encode_len > 0) {
|
||||||
memcpy(buf[n], encode_buffer, encode_len);
|
memcpy(buf[n], encode_buffer, encode_len);
|
||||||
buf_len[n] = encode_len;
|
buf_len[n] = encode_len;
|
||||||
n++;
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
size_ = 0;
|
size_ = 0;
|
||||||
if(!data_left)
|
if(!data_left) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if(data_left < total) {
|
if(data_left < enc_want_bytes_) {
|
||||||
memcpy(data_ + size_, resample_buffer + index, data_left);
|
memcpy(data_ + size_, resample_buffer + index, data_left);
|
||||||
size_ += data_left;
|
size_ += data_left;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,8 @@
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef SRS_APP_AUDIO_RECODE_HPP
|
#ifndef SRS_APP_RTC_CODEC_HPP
|
||||||
#define SRS_APP_AUDIO_RECODE_HPP
|
#define SRS_APP_RTC_CODEC_HPP
|
||||||
|
|
||||||
#include <srs_core.hpp>
|
#include <srs_core.hpp>
|
||||||
|
|
||||||
|
@ -40,8 +40,6 @@ extern "C" {
|
||||||
#include <libavutil/samplefmt.h>
|
#include <libavutil/samplefmt.h>
|
||||||
#include <libswresample/swresample.h>
|
#include <libswresample/swresample.h>
|
||||||
|
|
||||||
#include <opus/opus.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -54,9 +52,10 @@ private:
|
||||||
AVFrame* frame_;
|
AVFrame* frame_;
|
||||||
AVPacket* packet_;
|
AVPacket* packet_;
|
||||||
AVCodecContext* codec_ctx_;
|
AVCodecContext* codec_ctx_;
|
||||||
std::string codec_name_;
|
SrsAudioCodecId codec_id_;
|
||||||
public:
|
public:
|
||||||
SrsAudioDecoder(std::string codec);
|
//Only support "aac","opus"
|
||||||
|
SrsAudioDecoder(SrsAudioCodecId codec);
|
||||||
virtual ~SrsAudioDecoder();
|
virtual ~SrsAudioDecoder();
|
||||||
srs_error_t initialize();
|
srs_error_t initialize();
|
||||||
virtual srs_error_t decode(SrsSample *pkt, char *buf, int &size);
|
virtual srs_error_t decode(SrsSample *pkt, char *buf, int &size);
|
||||||
|
@ -66,16 +65,22 @@ public:
|
||||||
class SrsAudioEncoder
|
class SrsAudioEncoder
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
int inband_fec_;
|
|
||||||
int channels_;
|
int channels_;
|
||||||
int sampling_rate_;
|
int sampling_rate_;
|
||||||
int complexity_;
|
AVCodecContext* codec_ctx_;
|
||||||
OpusEncoder *opus_;
|
SrsAudioCodecId codec_id_;
|
||||||
|
int want_bytes_;
|
||||||
|
AVFrame* frame_;
|
||||||
public:
|
public:
|
||||||
SrsAudioEncoder(int samplerate, int channels, int fec, int complexity);
|
//Only support "aac","opus"
|
||||||
|
SrsAudioEncoder(SrsAudioCodecId codec, int samplerate, int channelsy);
|
||||||
virtual ~SrsAudioEncoder();
|
virtual ~SrsAudioEncoder();
|
||||||
srs_error_t initialize();
|
srs_error_t initialize();
|
||||||
|
//The encoder wanted bytes to call encode, if > 0, caller must feed the same bytes
|
||||||
|
//Call after initialize successed
|
||||||
|
int want_bytes();
|
||||||
virtual srs_error_t encode(SrsSample *frame, char *buf, int &size);
|
virtual srs_error_t encode(SrsSample *frame, char *buf, int &size);
|
||||||
|
AVCodecContext* codec_ctx();
|
||||||
};
|
};
|
||||||
|
|
||||||
class SrsAudioResample
|
class SrsAudioResample
|
||||||
|
@ -107,6 +112,7 @@ public:
|
||||||
virtual srs_error_t resample(SrsSample *pcm, char *buf, int &size);
|
virtual srs_error_t resample(SrsSample *pcm, char *buf, int &size);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: FIXME: Rename to Transcoder.
|
||||||
class SrsAudioRecode
|
class SrsAudioRecode
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
@ -117,11 +123,14 @@ private:
|
||||||
int dst_samplerate_;
|
int dst_samplerate_;
|
||||||
int size_;
|
int size_;
|
||||||
char *data_;
|
char *data_;
|
||||||
|
SrsAudioCodecId src_codec_;
|
||||||
|
SrsAudioCodecId dst_codec_;
|
||||||
|
int enc_want_bytes_;
|
||||||
public:
|
public:
|
||||||
SrsAudioRecode(int channels, int samplerate);
|
SrsAudioRecode(SrsAudioCodecId src_codec, SrsAudioCodecId dst_codec,int channels, int samplerate);
|
||||||
virtual ~SrsAudioRecode();
|
virtual ~SrsAudioRecode();
|
||||||
srs_error_t initialize();
|
srs_error_t initialize();
|
||||||
virtual srs_error_t recode(SrsSample *pkt, char **buf, int *buf_len, int &n);
|
virtual srs_error_t transcode(SrsSample *pkt, char **buf, int *buf_len, int &n);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* SRS_APP_AUDIO_RECODE_HPP */
|
#endif /* SRS_APP_AUDIO_RECODE_HPP */
|
File diff suppressed because it is too large
Load diff
|
@ -31,26 +31,37 @@
|
||||||
#include <srs_rtmp_stack.hpp>
|
#include <srs_rtmp_stack.hpp>
|
||||||
#include <srs_app_hybrid.hpp>
|
#include <srs_app_hybrid.hpp>
|
||||||
#include <srs_app_hourglass.hpp>
|
#include <srs_app_hourglass.hpp>
|
||||||
#include <srs_app_sdp.hpp>
|
#include <srs_app_rtc_sdp.hpp>
|
||||||
#include <srs_app_reload.hpp>
|
#include <srs_app_reload.hpp>
|
||||||
|
#include <srs_kernel_rtc_rtp.hpp>
|
||||||
|
#include <srs_kernel_rtc_rtcp.hpp>
|
||||||
|
#include <srs_app_rtc_queue.hpp>
|
||||||
|
#include <srs_app_rtc_source.hpp>
|
||||||
|
#include <srs_app_rtc_dtls.hpp>
|
||||||
|
#include <srs_service_conn.hpp>
|
||||||
|
#include <srs_app_conn.hpp>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
|
||||||
#include <openssl/ssl.h>
|
|
||||||
#include <srtp2/srtp.h>
|
|
||||||
|
|
||||||
class SrsUdpMuxSocket;
|
class SrsUdpMuxSocket;
|
||||||
class SrsConsumer;
|
class SrsConsumer;
|
||||||
class SrsStunPacket;
|
class SrsStunPacket;
|
||||||
class SrsRtcServer;
|
class SrsRtcServer;
|
||||||
class SrsRtcSession;
|
class SrsRtcConnection;
|
||||||
class SrsSharedPtrMessage;
|
class SrsSharedPtrMessage;
|
||||||
class SrsSource;
|
class SrsRtcStream;
|
||||||
class SrsRtpPacket2;
|
class SrsRtpPacket2;
|
||||||
class ISrsUdpSender;
|
class ISrsCodec;
|
||||||
|
class SrsRtpNackForReceiver;
|
||||||
|
class SrsRtpIncommingVideoFrame;
|
||||||
|
class SrsRtpRingBuffer;
|
||||||
|
class SrsRtcConsumer;
|
||||||
|
class SrsRtcAudioSendTrack;
|
||||||
|
class SrsRtcVideoSendTrack;
|
||||||
|
class SrsErrorPithyPrint;
|
||||||
|
|
||||||
const uint8_t kSR = 200;
|
const uint8_t kSR = 200;
|
||||||
const uint8_t kRR = 201;
|
const uint8_t kRR = 201;
|
||||||
|
@ -61,74 +72,148 @@ const uint8_t kApp = 204;
|
||||||
// @see: https://tools.ietf.org/html/rfc4585#section-6.1
|
// @see: https://tools.ietf.org/html/rfc4585#section-6.1
|
||||||
const uint8_t kRtpFb = 205;
|
const uint8_t kRtpFb = 205;
|
||||||
const uint8_t kPsFb = 206;
|
const uint8_t kPsFb = 206;
|
||||||
|
const uint8_t kXR = 207;
|
||||||
|
|
||||||
// @see: https://tools.ietf.org/html/rfc4585#section-6.3
|
enum SrsRtcConnectionStateType
|
||||||
const uint8_t kPLI = 1;
|
|
||||||
const uint8_t kSLI = 2;
|
|
||||||
const uint8_t kRPSI = 3;
|
|
||||||
const uint8_t kAFB = 15;
|
|
||||||
|
|
||||||
enum SrsRtcSessionStateType
|
|
||||||
{
|
{
|
||||||
// TODO: FIXME: Should prefixed by enum name.
|
// TODO: FIXME: Should prefixed by enum name.
|
||||||
INIT = -1,
|
INIT = -1,
|
||||||
WAITING_STUN = 1,
|
WAITING_ANSWER = 1,
|
||||||
DOING_DTLS_HANDSHAKE = 2,
|
WAITING_STUN = 2,
|
||||||
ESTABLISHED = 3,
|
DOING_DTLS_HANDSHAKE = 3,
|
||||||
CLOSED = 4,
|
ESTABLISHED = 4,
|
||||||
|
CLOSED = 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
class SrsDtlsSession
|
// The transport for RTC connection.
|
||||||
|
class ISrsRtcTransport : public ISrsDtlsCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ISrsRtcTransport();
|
||||||
|
virtual ~ISrsRtcTransport();
|
||||||
|
public:
|
||||||
|
virtual srs_error_t initialize(SrsSessionConfig* cfg) = 0;
|
||||||
|
virtual srs_error_t start_active_handshake() = 0;
|
||||||
|
virtual srs_error_t on_dtls(char* data, int nb_data) = 0;
|
||||||
|
virtual srs_error_t on_dtls_alert(std::string type, std::string desc) = 0;
|
||||||
|
public:
|
||||||
|
virtual srs_error_t protect_rtp(const char* plaintext, char* cipher, int& nb_cipher) = 0;
|
||||||
|
virtual srs_error_t protect_rtcp(const char* plaintext, char* cipher, int& nb_cipher) = 0;
|
||||||
|
virtual srs_error_t protect_rtp2(void* rtp_hdr, int* len_ptr) = 0;
|
||||||
|
virtual srs_error_t unprotect_rtp(const char* cipher, char* plaintext, int& nb_plaintext) = 0;
|
||||||
|
virtual srs_error_t unprotect_rtcp(const char* cipher, char* plaintext, int& nb_plaintext) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The security transport, use DTLS/SRTP to protect the data.
|
||||||
|
class SrsSecurityTransport : public ISrsRtcTransport
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
SrsRtcSession* rtc_session;
|
SrsRtcConnection* session_;
|
||||||
|
SrsDtls* dtls_;
|
||||||
SSL* dtls;
|
SrsSRTP* srtp_;
|
||||||
BIO* bio_in;
|
|
||||||
BIO* bio_out;
|
|
||||||
|
|
||||||
std::string client_key;
|
|
||||||
std::string server_key;
|
|
||||||
|
|
||||||
srtp_t srtp_send;
|
|
||||||
srtp_t srtp_recv;
|
|
||||||
|
|
||||||
bool handshake_done;
|
bool handshake_done;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SrsDtlsSession(SrsRtcSession* s);
|
SrsSecurityTransport(SrsRtcConnection* s);
|
||||||
virtual ~SrsDtlsSession();
|
virtual ~SrsSecurityTransport();
|
||||||
|
|
||||||
srs_error_t initialize(const SrsRequest& req);
|
srs_error_t initialize(SrsSessionConfig* cfg);
|
||||||
|
// When play role of dtls client, it send handshake.
|
||||||
srs_error_t on_dtls(SrsUdpMuxSocket* skt);
|
srs_error_t start_active_handshake();
|
||||||
srs_error_t on_dtls_handshake_done(SrsUdpMuxSocket* skt);
|
srs_error_t on_dtls(char* data, int nb_data);
|
||||||
srs_error_t on_dtls_application_data(const char* data, const int len);
|
srs_error_t on_dtls_alert(std::string type, std::string desc);
|
||||||
public:
|
public:
|
||||||
srs_error_t protect_rtp(char* protected_buf, const char* ori_buf, int& nb_protected_buf);
|
// Encrypt the input plaintext to output cipher with nb_cipher bytes.
|
||||||
|
// @remark Note that the nb_cipher is the size of input plaintext, and
|
||||||
|
// it also is the length of output cipher when return.
|
||||||
|
srs_error_t protect_rtp(const char* plaintext, char* cipher, int& nb_cipher);
|
||||||
|
srs_error_t protect_rtcp(const char* plaintext, char* cipher, int& nb_cipher);
|
||||||
|
// Encrypt the input rtp_hdr with *len_ptr bytes.
|
||||||
|
// @remark the input plaintext and out cipher reuse rtp_hdr.
|
||||||
srs_error_t protect_rtp2(void* rtp_hdr, int* len_ptr);
|
srs_error_t protect_rtp2(void* rtp_hdr, int* len_ptr);
|
||||||
srs_error_t unprotect_rtp(char* unprotected_buf, const char* ori_buf, int& nb_unprotected_buf);
|
// Decrypt the input cipher to output cipher with nb_cipher bytes.
|
||||||
srs_error_t protect_rtcp(char* protected_buf, const char* ori_buf, int& nb_protected_buf);
|
// @remark Note that the nb_plaintext is the size of input cipher, and
|
||||||
srs_error_t unprotect_rtcp(char* unprotected_buf, const char* ori_buf, int& nb_unprotected_buf);
|
// it also is the length of output plaintext when return.
|
||||||
private:
|
srs_error_t unprotect_rtp(const char* cipher, char* plaintext, int& nb_plaintext);
|
||||||
srs_error_t handshake(SrsUdpMuxSocket* skt);
|
srs_error_t unprotect_rtcp(const char* cipher, char* plaintext, int& nb_plaintext);
|
||||||
|
// implement ISrsDtlsCallback
|
||||||
|
public:
|
||||||
|
virtual srs_error_t on_dtls_handshake_done();
|
||||||
|
virtual srs_error_t on_dtls_application_data(const char* data, const int len);
|
||||||
|
virtual srs_error_t write_dtls_data(void* data, int size);
|
||||||
private:
|
private:
|
||||||
srs_error_t srtp_initialize();
|
srs_error_t srtp_initialize();
|
||||||
srs_error_t srtp_send_init();
|
|
||||||
srs_error_t srtp_recv_init();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// A group of RTP packets.
|
// Semi security transport, setup DTLS and SRTP, with SRTP decrypt, without SRTP encrypt.
|
||||||
class SrsRtcPackets
|
class SrsSemiSecurityTransport : public SrsSecurityTransport
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
bool use_gso;
|
SrsSemiSecurityTransport(SrsRtcConnection* s);
|
||||||
bool should_merge_nalus;
|
virtual ~SrsSemiSecurityTransport();
|
||||||
public:
|
public:
|
||||||
#if defined(SRS_DEBUG)
|
virtual srs_error_t protect_rtp(const char* plaintext, char* cipher, int& nb_cipher);
|
||||||
// Debug id.
|
virtual srs_error_t protect_rtcp(const char* plaintext, char* cipher, int& nb_cipher);
|
||||||
uint32_t debug_id;
|
virtual srs_error_t protect_rtp2(void* rtp_hdr, int* len_ptr);
|
||||||
#endif
|
};
|
||||||
|
|
||||||
|
// Plaintext transport, without DTLS or SRTP.
|
||||||
|
class SrsPlaintextTransport : public ISrsRtcTransport
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
SrsRtcConnection* session_;
|
||||||
|
public:
|
||||||
|
SrsPlaintextTransport(SrsRtcConnection* s);
|
||||||
|
virtual ~SrsPlaintextTransport();
|
||||||
|
public:
|
||||||
|
virtual srs_error_t initialize(SrsSessionConfig* cfg);
|
||||||
|
virtual srs_error_t start_active_handshake();
|
||||||
|
virtual srs_error_t on_dtls(char* data, int nb_data);
|
||||||
|
virtual srs_error_t on_dtls_alert(std::string type, std::string desc);
|
||||||
|
virtual srs_error_t on_dtls_handshake_done();
|
||||||
|
virtual srs_error_t on_dtls_application_data(const char* data, const int len);
|
||||||
|
virtual srs_error_t write_dtls_data(void* data, int size);
|
||||||
|
public:
|
||||||
|
virtual srs_error_t protect_rtp(const char* plaintext, char* cipher, int& nb_cipher);
|
||||||
|
virtual srs_error_t protect_rtcp(const char* plaintext, char* cipher, int& nb_cipher);
|
||||||
|
virtual srs_error_t protect_rtp2(void* rtp_hdr, int* len_ptr);
|
||||||
|
virtual srs_error_t unprotect_rtp(const char* cipher, char* plaintext, int& nb_plaintext);
|
||||||
|
virtual srs_error_t unprotect_rtcp(const char* cipher, char* plaintext, int& nb_plaintext);
|
||||||
|
};
|
||||||
|
|
||||||
|
// The handler for PLI worker coroutine.
|
||||||
|
class ISrsRtcPLIWorkerHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ISrsRtcPLIWorkerHandler();
|
||||||
|
virtual ~ISrsRtcPLIWorkerHandler();
|
||||||
|
public:
|
||||||
|
virtual srs_error_t do_request_keyframe(uint32_t ssrc, SrsContextId cid) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A worker coroutine to request the PLI.
|
||||||
|
class SrsRtcPLIWorker : virtual public ISrsCoroutineHandler
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
SrsCoroutine* trd_;
|
||||||
|
srs_cond_t wait_;
|
||||||
|
ISrsRtcPLIWorkerHandler* handler_;
|
||||||
|
private:
|
||||||
|
// Key is SSRC, value is the CID of subscriber which requests PLI.
|
||||||
|
std::map<uint32_t, SrsContextId> plis_;
|
||||||
|
public:
|
||||||
|
SrsRtcPLIWorker(ISrsRtcPLIWorkerHandler* h);
|
||||||
|
virtual ~SrsRtcPLIWorker();
|
||||||
|
public:
|
||||||
|
virtual srs_error_t start();
|
||||||
|
virtual void request_keyframe(uint32_t ssrc, SrsContextId cid);
|
||||||
|
// interface ISrsCoroutineHandler
|
||||||
|
public:
|
||||||
|
virtual srs_error_t cycle();
|
||||||
|
};
|
||||||
|
|
||||||
|
// A group of RTP packets for outgoing(send to players).
|
||||||
|
class SrsRtcPlayStreamStatistic
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
// The total bytes of AVFrame packets.
|
// The total bytes of AVFrame packets.
|
||||||
int nn_bytes;
|
int nn_bytes;
|
||||||
|
@ -141,9 +226,11 @@ public:
|
||||||
// one msghdr by GSO, it's only one RTP packet, because we only send once.
|
// one msghdr by GSO, it's only one RTP packet, because we only send once.
|
||||||
int nn_rtp_pkts;
|
int nn_rtp_pkts;
|
||||||
// For video, the samples or NALUs.
|
// For video, the samples or NALUs.
|
||||||
|
// TODO: FIXME: Remove it because we may don't know.
|
||||||
int nn_samples;
|
int nn_samples;
|
||||||
// For audio, the generated extra audio packets.
|
// For audio, the generated extra audio packets.
|
||||||
// For example, when transcoding AAC to opus, may many extra payloads for a audio.
|
// For example, when transcoding AAC to opus, may many extra payloads for a audio.
|
||||||
|
// TODO: FIXME: Remove it because we may don't know.
|
||||||
int nn_extras;
|
int nn_extras;
|
||||||
// The original audio messages.
|
// The original audio messages.
|
||||||
int nn_audios;
|
int nn_audios;
|
||||||
|
@ -151,248 +238,348 @@ public:
|
||||||
int nn_videos;
|
int nn_videos;
|
||||||
// The number of padded packet.
|
// The number of padded packet.
|
||||||
int nn_paddings;
|
int nn_paddings;
|
||||||
// The number of dropped messages.
|
|
||||||
int nn_dropped;
|
|
||||||
private:
|
|
||||||
int cursor;
|
|
||||||
int nn_cache;
|
|
||||||
SrsRtpPacket2* cache;
|
|
||||||
public:
|
public:
|
||||||
SrsRtcPackets(int nn_cache_max);
|
SrsRtcPlayStreamStatistic();
|
||||||
virtual ~SrsRtcPackets();
|
virtual ~SrsRtcPlayStreamStatistic();
|
||||||
public:
|
|
||||||
void reset(bool gso, bool merge_nalus);
|
|
||||||
SrsRtpPacket2* fetch();
|
|
||||||
SrsRtpPacket2* back();
|
|
||||||
int size();
|
|
||||||
int capacity();
|
|
||||||
SrsRtpPacket2* at(int index);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class SrsRtcSenderThread : virtual public ISrsCoroutineHandler, virtual public ISrsReloadHandler
|
// A RTC play stream, client pull and play stream from SRS.
|
||||||
|
class SrsRtcPlayStream : virtual public ISrsCoroutineHandler, virtual public ISrsReloadHandler
|
||||||
|
, virtual public ISrsHourGlass, virtual public ISrsRtcPLIWorkerHandler
|
||||||
{
|
{
|
||||||
protected:
|
private:
|
||||||
|
SrsContextId cid_;
|
||||||
SrsCoroutine* trd;
|
SrsCoroutine* trd;
|
||||||
int _parent_cid;
|
SrsRtcConnection* session_;
|
||||||
|
SrsRtcPLIWorker* pli_worker_;
|
||||||
private:
|
private:
|
||||||
SrsRtcSession* rtc_session;
|
SrsRequest* req_;
|
||||||
uint32_t video_ssrc;
|
SrsRtcStream* source_;
|
||||||
uint32_t audio_ssrc;
|
SrsHourGlass* timer_;
|
||||||
uint16_t video_payload_type;
|
// key: publish_ssrc, value: send track to process rtp/rtcp
|
||||||
uint16_t audio_payload_type;
|
std::map<uint32_t, SrsRtcAudioSendTrack*> audio_tracks_;
|
||||||
|
std::map<uint32_t, SrsRtcVideoSendTrack*> video_tracks_;
|
||||||
|
// The pithy print for special stage.
|
||||||
|
SrsErrorPithyPrint* nack_epp;
|
||||||
private:
|
private:
|
||||||
// TODO: FIXME: How to handle timestamp overflow?
|
// For merged-write messages.
|
||||||
uint32_t audio_timestamp;
|
|
||||||
uint16_t audio_sequence;
|
|
||||||
private:
|
|
||||||
uint16_t video_sequence;
|
|
||||||
public:
|
|
||||||
SrsUdpMuxSocket* sendonly_ukt;
|
|
||||||
private:
|
|
||||||
ISrsUdpSender* sender;
|
|
||||||
private:
|
|
||||||
bool merge_nalus;
|
|
||||||
bool gso;
|
|
||||||
int max_padding;
|
|
||||||
private:
|
|
||||||
srs_utime_t mw_sleep;
|
|
||||||
int mw_msgs;
|
int mw_msgs;
|
||||||
bool realtime;
|
bool realtime;
|
||||||
|
// Whether enabled nack.
|
||||||
|
bool nack_enabled_;
|
||||||
|
private:
|
||||||
|
// Whether palyer started.
|
||||||
|
bool is_started;
|
||||||
|
// The statistic for consumer to send packets to player.
|
||||||
|
SrsRtcPlayStreamStatistic info;
|
||||||
public:
|
public:
|
||||||
SrsRtcSenderThread(SrsRtcSession* s, SrsUdpMuxSocket* u, int parent_cid);
|
SrsRtcPlayStream(SrsRtcConnection* s, const SrsContextId& cid);
|
||||||
virtual ~SrsRtcSenderThread();
|
virtual ~SrsRtcPlayStream();
|
||||||
public:
|
public:
|
||||||
srs_error_t initialize(const uint32_t& vssrc, const uint32_t& assrc, const uint16_t& v_pt, const uint16_t& a_pt);
|
srs_error_t initialize(SrsRequest* request, std::map<uint32_t, SrsRtcTrackDescription*> sub_relations);
|
||||||
// interface ISrsReloadHandler
|
// interface ISrsReloadHandler
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t on_reload_rtc_server();
|
|
||||||
virtual srs_error_t on_reload_vhost_play(std::string vhost);
|
virtual srs_error_t on_reload_vhost_play(std::string vhost);
|
||||||
virtual srs_error_t on_reload_vhost_realtime(std::string vhost);
|
virtual srs_error_t on_reload_vhost_realtime(std::string vhost);
|
||||||
public:
|
virtual const SrsContextId& context_id();
|
||||||
virtual int cid();
|
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t start();
|
virtual srs_error_t start();
|
||||||
virtual void stop();
|
virtual void stop();
|
||||||
virtual void stop_loop();
|
|
||||||
public:
|
|
||||||
void update_sendonly_socket(SrsUdpMuxSocket* skt);
|
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t cycle();
|
virtual srs_error_t cycle();
|
||||||
private:
|
private:
|
||||||
srs_error_t send_messages(SrsSource* source, SrsSharedPtrMessage** msgs, int nb_msgs, SrsRtcPackets& packets);
|
srs_error_t send_packets(SrsRtcStream* source, const std::vector<SrsRtpPacket2*>& pkts, SrsRtcPlayStreamStatistic& info);
|
||||||
srs_error_t messages_to_packets(SrsSource* source, SrsSharedPtrMessage** msgs, int nb_msgs, SrsRtcPackets& packets);
|
void nack_fetch(std::vector<SrsRtpPacket2*>& pkts, uint32_t ssrc, uint16_t seq);
|
||||||
srs_error_t send_packets(SrsRtcPackets& packets);
|
|
||||||
srs_error_t send_packets_gso(SrsRtcPackets& packets);
|
|
||||||
private:
|
|
||||||
srs_error_t packet_opus(SrsSample* sample, SrsRtcPackets& packets, int nn_max_payload);
|
|
||||||
private:
|
|
||||||
srs_error_t packet_fu_a(SrsSharedPtrMessage* msg, SrsSample* sample, int fu_payload_size, SrsRtcPackets& packets);
|
|
||||||
srs_error_t packet_nalus(SrsSharedPtrMessage* msg, SrsRtcPackets& packets);
|
|
||||||
srs_error_t packet_single_nalu(SrsSharedPtrMessage* msg, SrsSample* sample, SrsRtcPackets& packets);
|
|
||||||
srs_error_t packet_stap_a(SrsSource* source, SrsSharedPtrMessage* msg, SrsRtcPackets& packets);
|
|
||||||
};
|
|
||||||
|
|
||||||
class SrsRtcSession
|
|
||||||
{
|
|
||||||
friend class SrsRtcSenderThread;
|
|
||||||
private:
|
|
||||||
SrsRtcServer* rtc_server;
|
|
||||||
SrsSdp remote_sdp;
|
|
||||||
SrsSdp local_sdp;
|
|
||||||
SrsRtcSessionStateType session_state;
|
|
||||||
SrsDtlsSession* dtls_session;
|
|
||||||
SrsRtcSenderThread* strd;
|
|
||||||
std::string username;
|
|
||||||
std::string peer_id;
|
|
||||||
srs_utime_t last_stun_time;
|
|
||||||
private:
|
|
||||||
// For each RTC session, we use a specified cid for debugging logs.
|
|
||||||
int cid;
|
|
||||||
// For each RTC session, whether requires encrypt.
|
|
||||||
// Read config value, rtc_server.encrypt, default to on.
|
|
||||||
// Sepcifies by HTTP API, query encrypt, optional.
|
|
||||||
// TODO: FIXME: Support reload.
|
|
||||||
bool encrypt;
|
|
||||||
// The timeout of session, keep alive by STUN ping pong.
|
|
||||||
srs_utime_t sessionStunTimeout;
|
|
||||||
public:
|
public:
|
||||||
SrsRequest request;
|
// Directly set the status of track, generally for init to set the default value.
|
||||||
SrsSource* source;
|
void set_all_tracks_status(bool status);
|
||||||
public:
|
|
||||||
SrsRtcSession(SrsRtcServer* rtc_svr, const SrsRequest& req, const std::string& un, int context_id);
|
|
||||||
virtual ~SrsRtcSession();
|
|
||||||
public:
|
|
||||||
SrsSdp* get_local_sdp() { return &local_sdp; }
|
|
||||||
void set_local_sdp(const SrsSdp& sdp);
|
|
||||||
|
|
||||||
SrsSdp* get_remote_sdp() { return &remote_sdp; }
|
|
||||||
void set_remote_sdp(const SrsSdp& sdp) { remote_sdp = sdp; }
|
|
||||||
|
|
||||||
SrsRtcSessionStateType get_session_state() { return session_state; }
|
|
||||||
void set_session_state(SrsRtcSessionStateType state) { session_state = state; }
|
|
||||||
|
|
||||||
std::string id() const { return peer_id + "_" + username; }
|
|
||||||
|
|
||||||
std::string get_peer_id() const { return peer_id; }
|
|
||||||
void set_peer_id(const std::string& id) { peer_id = id; }
|
|
||||||
|
|
||||||
void set_encrypt(bool v) { encrypt = v; }
|
|
||||||
|
|
||||||
void switch_to_context();
|
|
||||||
public:
|
|
||||||
srs_error_t on_stun(SrsUdpMuxSocket* skt, SrsStunPacket* stun_req);
|
|
||||||
srs_error_t on_dtls(SrsUdpMuxSocket* skt);
|
|
||||||
srs_error_t on_rtcp(SrsUdpMuxSocket* skt);
|
|
||||||
public:
|
|
||||||
srs_error_t send_client_hello(SrsUdpMuxSocket* skt);
|
|
||||||
srs_error_t on_connection_established(SrsUdpMuxSocket* skt);
|
|
||||||
srs_error_t start_play(SrsUdpMuxSocket* skt);
|
|
||||||
public:
|
|
||||||
bool is_stun_timeout();
|
|
||||||
private:
|
|
||||||
srs_error_t check_source();
|
|
||||||
private:
|
|
||||||
srs_error_t on_binding_request(SrsUdpMuxSocket* skt, SrsStunPacket* stun_req);
|
|
||||||
private:
|
|
||||||
srs_error_t on_rtcp_feedback(char* buf, int nb_buf, SrsUdpMuxSocket* skt);
|
|
||||||
srs_error_t on_rtcp_ps_feedback(char* buf, int nb_buf, SrsUdpMuxSocket* skt);
|
|
||||||
srs_error_t on_rtcp_receiver_report(char* buf, int nb_buf, SrsUdpMuxSocket* skt);
|
|
||||||
};
|
|
||||||
|
|
||||||
class SrsUdpMuxSender : virtual public ISrsUdpSender, virtual public ISrsCoroutineHandler, virtual public ISrsReloadHandler
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
srs_netfd_t lfd;
|
|
||||||
SrsRtcServer* server;
|
|
||||||
SrsCoroutine* trd;
|
|
||||||
private:
|
|
||||||
srs_cond_t cond;
|
|
||||||
bool waiting_msgs;
|
|
||||||
bool gso;
|
|
||||||
int nn_senders;
|
|
||||||
private:
|
|
||||||
// Hotspot msgs, we are working on it.
|
|
||||||
// @remark We will wait util all messages are ready.
|
|
||||||
std::vector<mmsghdr> hotspot;
|
|
||||||
// Cache msgs, for other coroutines to fill it.
|
|
||||||
std::vector<mmsghdr> cache;
|
|
||||||
int cache_pos;
|
|
||||||
// The max number of messages for sendmmsg. If 1, we use sendmsg to send.
|
|
||||||
int max_sendmmsg;
|
|
||||||
// The total queue length, for each sender.
|
|
||||||
int queue_length;
|
|
||||||
// The extra queue ratio.
|
|
||||||
int extra_ratio;
|
|
||||||
int extra_queue;
|
|
||||||
public:
|
|
||||||
SrsUdpMuxSender(SrsRtcServer* s);
|
|
||||||
virtual ~SrsUdpMuxSender();
|
|
||||||
public:
|
|
||||||
virtual srs_error_t initialize(srs_netfd_t fd, int senders);
|
|
||||||
private:
|
|
||||||
void free_mhdrs(std::vector<mmsghdr>& mhdrs);
|
|
||||||
public:
|
|
||||||
virtual srs_error_t fetch(mmsghdr** pphdr);
|
|
||||||
virtual srs_error_t sendmmsg(mmsghdr* hdr);
|
|
||||||
virtual bool overflow();
|
|
||||||
virtual void set_extra_ratio(int r);
|
|
||||||
public:
|
|
||||||
virtual srs_error_t cycle();
|
|
||||||
// interface ISrsReloadHandler
|
|
||||||
public:
|
|
||||||
virtual srs_error_t on_reload_rtc_server();
|
|
||||||
};
|
|
||||||
|
|
||||||
class SrsRtcServer : virtual public ISrsUdpMuxHandler, virtual public ISrsHourGlass
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
SrsHourGlass* timer;
|
|
||||||
std::vector<SrsUdpMuxListener*> listeners;
|
|
||||||
std::vector<SrsUdpMuxSender*> senders;
|
|
||||||
private:
|
|
||||||
std::map<std::string, SrsRtcSession*> map_username_session; // key: username(local_ufrag + ":" + remote_ufrag)
|
|
||||||
std::map<std::string, SrsRtcSession*> map_id_session; // key: peerip(ip + ":" + port)
|
|
||||||
public:
|
|
||||||
SrsRtcServer();
|
|
||||||
virtual ~SrsRtcServer();
|
|
||||||
public:
|
|
||||||
virtual srs_error_t initialize();
|
|
||||||
public:
|
|
||||||
// TODO: FIXME: Support gracefully quit.
|
|
||||||
// TODO: FIXME: Support reload.
|
|
||||||
virtual srs_error_t listen_udp();
|
|
||||||
virtual srs_error_t on_udp_packet(SrsUdpMuxSocket* skt);
|
|
||||||
public:
|
|
||||||
virtual srs_error_t listen_api();
|
|
||||||
SrsRtcSession* create_rtc_session(const SrsRequest& req, const SrsSdp& remote_sdp, SrsSdp& local_sdp, const std::string& mock_eip);
|
|
||||||
bool insert_into_id_sessions(const std::string& peer_id, SrsRtcSession* rtc_session);
|
|
||||||
void check_and_clean_timeout_session();
|
|
||||||
int nn_sessions() { return (int)map_username_session.size(); }
|
|
||||||
private:
|
|
||||||
srs_error_t on_stun(SrsUdpMuxSocket* skt);
|
|
||||||
srs_error_t on_dtls(SrsUdpMuxSocket* skt);
|
|
||||||
srs_error_t on_rtp_or_rtcp(SrsUdpMuxSocket* skt);
|
|
||||||
private:
|
|
||||||
SrsRtcSession* find_rtc_session_by_username(const std::string& ufrag);
|
|
||||||
SrsRtcSession* find_rtc_session_by_peer_id(const std::string& peer_id);
|
|
||||||
// interface ISrsHourGlass
|
// interface ISrsHourGlass
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t notify(int type, srs_utime_t interval, srs_utime_t tick);
|
virtual srs_error_t notify(int type, srs_utime_t interval, srs_utime_t tick);
|
||||||
|
public:
|
||||||
|
srs_error_t on_rtcp(SrsRtcpCommon* rtcp);
|
||||||
|
private:
|
||||||
|
srs_error_t on_rtcp_xr(SrsRtcpXr* rtcp);
|
||||||
|
srs_error_t on_rtcp_nack(SrsRtcpNack* rtcp);
|
||||||
|
srs_error_t on_rtcp_ps_feedback(SrsRtcpPsfbCommon* rtcp);
|
||||||
|
srs_error_t on_rtcp_rr(SrsRtcpRR* rtcp);
|
||||||
|
uint32_t get_video_publish_ssrc(uint32_t play_ssrc);
|
||||||
|
// inteface ISrsRtcPLIWorkerHandler
|
||||||
|
public:
|
||||||
|
virtual srs_error_t do_request_keyframe(uint32_t ssrc, SrsContextId cid);
|
||||||
};
|
};
|
||||||
|
|
||||||
// The RTC server adapter.
|
// A RTC publish stream, client push and publish stream to SRS.
|
||||||
class RtcServerAdapter : public ISrsHybridServer
|
class SrsRtcPublishStream : virtual public ISrsHourGlass, virtual public ISrsRtpPacketDecodeHandler
|
||||||
|
, virtual public ISrsRtcPublishStream, virtual public ISrsRtcPLIWorkerHandler
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
SrsRtcServer* rtc;
|
SrsContextId cid_;
|
||||||
|
SrsHourGlass* timer_;
|
||||||
|
uint64_t nn_audio_frames;
|
||||||
|
SrsRtcPLIWorker* pli_worker_;
|
||||||
|
private:
|
||||||
|
SrsRtcConnection* session_;
|
||||||
|
uint16_t pt_to_drop_;
|
||||||
|
// Whether enabled nack.
|
||||||
|
bool nack_enabled_;
|
||||||
|
private:
|
||||||
|
bool request_keyframe_;
|
||||||
|
SrsErrorPithyPrint* pli_epp;
|
||||||
|
private:
|
||||||
|
SrsRequest* req;
|
||||||
|
SrsRtcStream* source;
|
||||||
|
// Simulators.
|
||||||
|
int nn_simulate_nack_drop;
|
||||||
|
private:
|
||||||
|
// track vector
|
||||||
|
std::vector<SrsRtcAudioRecvTrack*> audio_tracks_;
|
||||||
|
std::vector<SrsRtcVideoRecvTrack*> video_tracks_;
|
||||||
|
private:
|
||||||
|
int twcc_id_;
|
||||||
|
uint8_t twcc_fb_count_;
|
||||||
|
SrsRtcpTWCC rtcp_twcc_;
|
||||||
|
SrsRtpExtensionTypes extension_types_;
|
||||||
|
bool is_started;
|
||||||
|
srs_utime_t last_time_send_twcc_;
|
||||||
public:
|
public:
|
||||||
RtcServerAdapter();
|
SrsRtcPublishStream(SrsRtcConnection* session, const SrsContextId& cid);
|
||||||
virtual ~RtcServerAdapter();
|
virtual ~SrsRtcPublishStream();
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t initialize();
|
srs_error_t initialize(SrsRequest* req, SrsRtcStreamDescription* stream_desc);
|
||||||
virtual srs_error_t run();
|
srs_error_t start();
|
||||||
virtual void stop();
|
// Directly set the status of track, generally for init to set the default value.
|
||||||
|
void set_all_tracks_status(bool status);
|
||||||
|
virtual const SrsContextId& context_id();
|
||||||
|
private:
|
||||||
|
srs_error_t send_rtcp_rr();
|
||||||
|
srs_error_t send_rtcp_xr_rrtr();
|
||||||
|
public:
|
||||||
|
srs_error_t on_rtp(char* buf, int nb_buf);
|
||||||
|
private:
|
||||||
|
srs_error_t do_on_rtp(char* plaintext, int nb_plaintext);
|
||||||
|
srs_error_t check_send_nacks();
|
||||||
|
public:
|
||||||
|
virtual void on_before_decode_payload(SrsRtpPacket2* pkt, SrsBuffer* buf, ISrsRtpPayloader** ppayload);
|
||||||
|
private:
|
||||||
|
srs_error_t send_periodic_twcc();
|
||||||
|
public:
|
||||||
|
srs_error_t on_rtcp(SrsRtcpCommon* rtcp);
|
||||||
|
private:
|
||||||
|
srs_error_t on_rtcp_sr(SrsRtcpSR* rtcp);
|
||||||
|
srs_error_t on_rtcp_xr(SrsRtcpXr* rtcp);
|
||||||
|
public:
|
||||||
|
void request_keyframe(uint32_t ssrc);
|
||||||
|
virtual srs_error_t do_request_keyframe(uint32_t ssrc, SrsContextId cid);
|
||||||
|
// interface ISrsHourGlass
|
||||||
|
public:
|
||||||
|
virtual srs_error_t notify(int type, srs_utime_t interval, srs_utime_t tick);
|
||||||
|
public:
|
||||||
|
void simulate_nack_drop(int nn);
|
||||||
|
private:
|
||||||
|
void simulate_drop_packet(SrsRtpHeader* h, int nn_bytes);
|
||||||
|
private:
|
||||||
|
srs_error_t on_twcc(uint16_t sn);
|
||||||
|
SrsRtcAudioRecvTrack* get_audio_track(uint32_t ssrc);
|
||||||
|
SrsRtcVideoRecvTrack* get_video_track(uint32_t ssrc);
|
||||||
|
void update_rtt(uint32_t ssrc, int rtt);
|
||||||
|
void update_send_report_time(uint32_t ssrc, const SrsNtp& ntp);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The statistics for RTC connection.
|
||||||
|
class SrsRtcConnectionStatistic
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int nn_publishers; int nn_subscribers;
|
||||||
|
int nn_rr; int nn_xr; int nn_sr; int nn_nack; int nn_pli;
|
||||||
|
uint64_t nn_in_twcc; uint64_t nn_in_rtp; uint64_t nn_in_audios; uint64_t nn_in_videos;
|
||||||
|
uint64_t nn_out_twcc; uint64_t nn_out_rtp; uint64_t nn_out_audios; uint64_t nn_out_videos;
|
||||||
|
private:
|
||||||
|
srs_utime_t born;
|
||||||
|
srs_utime_t dead;
|
||||||
|
public:
|
||||||
|
SrsRtcConnectionStatistic();
|
||||||
|
virtual ~SrsRtcConnectionStatistic();
|
||||||
|
public:
|
||||||
|
std::string summary();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Callback for RTC connection.
|
||||||
|
class ISrsRtcConnectionHijacker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ISrsRtcConnectionHijacker();
|
||||||
|
virtual ~ISrsRtcConnectionHijacker();
|
||||||
|
public:
|
||||||
|
virtual srs_error_t on_dtls_done() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A RTC Peer Connection, SDP level object.
|
||||||
|
class SrsRtcConnection : virtual public ISrsHourGlass, virtual public ISrsResource
|
||||||
|
, virtual public ISrsDisposingHandler
|
||||||
|
{
|
||||||
|
friend class SrsSecurityTransport;
|
||||||
|
friend class SrsRtcPlayStream;
|
||||||
|
friend class SrsRtcPublishStream;
|
||||||
|
public:
|
||||||
|
bool disposing_;
|
||||||
|
SrsRtcConnectionStatistic* stat_;
|
||||||
|
ISrsRtcConnectionHijacker* hijacker_;
|
||||||
|
private:
|
||||||
|
SrsRtcServer* server_;
|
||||||
|
SrsRtcConnectionStateType state_;
|
||||||
|
ISrsRtcTransport* transport_;
|
||||||
|
SrsHourGlass* timer_;
|
||||||
|
private:
|
||||||
|
// key: stream id
|
||||||
|
std::map<std::string, SrsRtcPlayStream*> players_;
|
||||||
|
//key: player track's ssrc
|
||||||
|
std::map<uint32_t, SrsRtcPlayStream*> players_ssrc_map_;
|
||||||
|
// key: stream id
|
||||||
|
std::map<std::string, SrsRtcPublishStream*> publishers_;
|
||||||
|
// key: publisher track's ssrc
|
||||||
|
std::map<uint32_t, SrsRtcPublishStream*> publishers_ssrc_map_;
|
||||||
|
private:
|
||||||
|
// The local:remote username, such as m5x0n128:jvOm where local name is m5x0n128.
|
||||||
|
std::string username_;
|
||||||
|
// The peer address, client maybe use more than one address, it's the current selected one.
|
||||||
|
SrsUdpMuxSocket* sendonly_skt;
|
||||||
|
// The address list, client may use multiple addresses.
|
||||||
|
std::map<std::string, SrsUdpMuxSocket*> peer_addresses_;
|
||||||
|
private:
|
||||||
|
// TODO: FIXME: Rename it.
|
||||||
|
// The timeout of session, keep alive by STUN ping pong.
|
||||||
|
srs_utime_t session_timeout;
|
||||||
|
// TODO: FIXME: Rename it.
|
||||||
|
srs_utime_t last_stun_time;
|
||||||
|
private:
|
||||||
|
// For each RTC session, we use a specified cid for debugging logs.
|
||||||
|
SrsContextId cid_;
|
||||||
|
// TODO: FIXME: Rename to req_.
|
||||||
|
SrsRequest* req;
|
||||||
|
SrsSdp remote_sdp;
|
||||||
|
SrsSdp local_sdp;
|
||||||
|
private:
|
||||||
|
// twcc handler
|
||||||
|
int twcc_id_;
|
||||||
|
// Simulators.
|
||||||
|
int nn_simulate_player_nack_drop;
|
||||||
|
// Pithy print for address change, use port as error code.
|
||||||
|
SrsErrorPithyPrint* pp_address_change;
|
||||||
|
// Pithy print for PLI request.
|
||||||
|
SrsErrorPithyPrint* pli_epp;
|
||||||
|
public:
|
||||||
|
SrsRtcConnection(SrsRtcServer* s, const SrsContextId& cid);
|
||||||
|
virtual ~SrsRtcConnection();
|
||||||
|
// interface ISrsDisposingHandler
|
||||||
|
public:
|
||||||
|
virtual void on_before_dispose(ISrsResource* c);
|
||||||
|
virtual void on_disposing(ISrsResource* c);
|
||||||
|
public:
|
||||||
|
// TODO: FIXME: save only connection info.
|
||||||
|
SrsSdp* get_local_sdp();
|
||||||
|
void set_local_sdp(const SrsSdp& sdp);
|
||||||
|
SrsSdp* get_remote_sdp();
|
||||||
|
void set_remote_sdp(const SrsSdp& sdp);
|
||||||
|
// Connection level state machine, for ARQ of UDP packets.
|
||||||
|
SrsRtcConnectionStateType state();
|
||||||
|
void set_state(SrsRtcConnectionStateType state);
|
||||||
|
// Get username pair for this connection, used as ID of session.
|
||||||
|
std::string username();
|
||||||
|
// Get all addresses client used.
|
||||||
|
std::vector<SrsUdpMuxSocket*> peer_addresses();
|
||||||
|
// Interface ISrsResource.
|
||||||
|
public:
|
||||||
|
virtual const SrsContextId& get_id();
|
||||||
|
virtual std::string desc();
|
||||||
|
public:
|
||||||
|
void switch_to_context();
|
||||||
|
const SrsContextId& context_id();
|
||||||
|
public:
|
||||||
|
srs_error_t add_publisher(SrsRequest* request, const SrsSdp& remote_sdp, SrsSdp& local_sdp);
|
||||||
|
srs_error_t add_player(SrsRequest* request, const SrsSdp& remote_sdp, SrsSdp& local_sdp);
|
||||||
|
// server send offer sdp to client, local sdp derivate from source stream desc.
|
||||||
|
srs_error_t add_player2(SrsRequest* request, bool unified_plan, SrsSdp& local_sdp);
|
||||||
|
public:
|
||||||
|
// Before initialize, user must set the local SDP, which is used to inititlize DTLS.
|
||||||
|
srs_error_t initialize(SrsRequest* r, bool dtls, bool srtp, std::string username);
|
||||||
|
// The peer address may change, we can identify that by STUN messages.
|
||||||
|
srs_error_t on_stun(SrsUdpMuxSocket* skt, SrsStunPacket* r);
|
||||||
|
srs_error_t on_dtls(char* data, int nb_data);
|
||||||
|
srs_error_t on_rtp(char* data, int nb_data);
|
||||||
|
srs_error_t on_rtcp(char* data, int nb_data);
|
||||||
|
private:
|
||||||
|
srs_error_t dispatch_rtcp(SrsRtcpCommon* rtcp);
|
||||||
|
public:
|
||||||
|
srs_error_t on_rtcp_feedback_twcc(char* buf, int nb_buf);
|
||||||
|
srs_error_t on_rtcp_feedback_remb(SrsRtcpPsfbCommon *rtcp);
|
||||||
|
public:
|
||||||
|
void set_hijacker(ISrsRtcConnectionHijacker* h);
|
||||||
|
public:
|
||||||
|
srs_error_t on_connection_established();
|
||||||
|
srs_error_t on_dtls_alert(std::string type, std::string desc);
|
||||||
|
srs_error_t start_play(std::string stream_uri);
|
||||||
|
srs_error_t start_publish(std::string stream_uri);
|
||||||
|
bool is_alive();
|
||||||
|
void alive();
|
||||||
|
void update_sendonly_socket(SrsUdpMuxSocket* skt);
|
||||||
|
// interface ISrsHourGlass
|
||||||
|
public:
|
||||||
|
virtual srs_error_t notify(int type, srs_utime_t interval, srs_utime_t tick);
|
||||||
|
public:
|
||||||
|
// send rtcp
|
||||||
|
srs_error_t send_rtcp(char *data, int nb_data);
|
||||||
|
void check_send_nacks(SrsRtpNackForReceiver* nack, uint32_t ssrc, uint32_t& sent_nacks, uint32_t& timeout_nacks);
|
||||||
|
srs_error_t send_rtcp_rr(uint32_t ssrc, SrsRtpRingBuffer* rtp_queue, const uint64_t& last_send_systime, const SrsNtp& last_send_ntp);
|
||||||
|
srs_error_t send_rtcp_xr_rrtr(uint32_t ssrc);
|
||||||
|
srs_error_t send_rtcp_fb_pli(uint32_t ssrc, const SrsContextId& cid_of_subscriber);
|
||||||
|
public:
|
||||||
|
// Simulate the NACK to drop nn packets.
|
||||||
|
void simulate_nack_drop(int nn);
|
||||||
|
void simulate_player_drop_packet(SrsRtpHeader* h, int nn_bytes);
|
||||||
|
srs_error_t do_send_packets(const std::vector<SrsRtpPacket2*>& pkts, SrsRtcPlayStreamStatistic& info);
|
||||||
|
// Directly set the status of play track, generally for init to set the default value.
|
||||||
|
void set_all_tracks_status(std::string stream_uri, bool is_publish, bool status);
|
||||||
|
private:
|
||||||
|
srs_error_t on_binding_request(SrsStunPacket* r);
|
||||||
|
// publish media capabilitiy negotiate
|
||||||
|
srs_error_t negotiate_publish_capability(SrsRequest* req, const SrsSdp& remote_sdp, SrsRtcStreamDescription* stream_desc);
|
||||||
|
srs_error_t generate_publish_local_sdp(SrsRequest* req, SrsSdp& local_sdp, SrsRtcStreamDescription* stream_desc, bool unified_plan);
|
||||||
|
// play media capabilitiy negotiate
|
||||||
|
//TODO: Use StreamDescription to negotiate and remove first negotiate_play_capability function
|
||||||
|
srs_error_t negotiate_play_capability(SrsRequest* req, const SrsSdp& remote_sdp, std::map<uint32_t, SrsRtcTrackDescription*>& sub_relations);
|
||||||
|
srs_error_t negotiate_play_capability(SrsRequest* req, SrsRtcStreamDescription* req_stream_desc, std::map<uint32_t, SrsRtcTrackDescription*>& sub_relations);
|
||||||
|
srs_error_t fetch_source_capability(SrsRequest* req, std::map<uint32_t, SrsRtcTrackDescription*>& sub_relations);
|
||||||
|
srs_error_t generate_play_local_sdp(SrsRequest* req, SrsSdp& local_sdp, SrsRtcStreamDescription* stream_desc, bool unified_plan);
|
||||||
|
srs_error_t create_player(SrsRequest* request, std::map<uint32_t, SrsRtcTrackDescription*> sub_relations);
|
||||||
|
srs_error_t create_publisher(SrsRequest* request, SrsRtcStreamDescription* stream_desc);
|
||||||
|
};
|
||||||
|
|
||||||
|
class ISrsRtcHijacker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ISrsRtcHijacker();
|
||||||
|
virtual ~ISrsRtcHijacker();
|
||||||
|
public:
|
||||||
|
// Initialize the hijacker.
|
||||||
|
virtual srs_error_t initialize() = 0;
|
||||||
|
// When create publisher, SDP is done, DTLS is not ready.
|
||||||
|
virtual srs_error_t on_create_publish(SrsRtcConnection* session, SrsRtcPublishStream* publisher, SrsRequest* req) = 0;
|
||||||
|
// When start publisher by RTC, SDP and DTLS are done.
|
||||||
|
virtual srs_error_t on_start_publish(SrsRtcConnection* session, SrsRtcPublishStream* publisher, SrsRequest* req) = 0;
|
||||||
|
// When stop publish by RTC.
|
||||||
|
virtual void on_stop_publish(SrsRtcConnection* session, SrsRtcPublishStream* publisher, SrsRequest* req) = 0;
|
||||||
|
// When got RTP plaintext packet.
|
||||||
|
virtual srs_error_t on_rtp_packet(SrsRtcConnection* session, SrsRtcPublishStream* publisher, SrsRequest* req, SrsRtpPacket2* pkt) = 0;
|
||||||
|
// When before play by RTC. (wait source to ready in cascade scenario)
|
||||||
|
virtual srs_error_t on_before_play(SrsRtcConnection* session, SrsRequest* req) = 0;
|
||||||
|
// When start player by RTC.
|
||||||
|
virtual srs_error_t on_start_play(SrsRtcConnection* session, SrsRtcPlayStream* player, SrsRequest* req) = 0;
|
||||||
|
// When start consuming for player for RTC.
|
||||||
|
virtual srs_error_t on_start_consume(SrsRtcConnection* session, SrsRtcPlayStream* player, SrsRequest* req, SrsRtcConsumer* consumer) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern ISrsRtcHijacker* _srs_rtc_hijacker;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
1050
trunk/src/app/srs_app_rtc_dtls.cpp
Normal file
1050
trunk/src/app/srs_app_rtc_dtls.cpp
Normal file
File diff suppressed because it is too large
Load diff
242
trunk/src/app/srs_app_rtc_dtls.hpp
Normal file
242
trunk/src/app/srs_app_rtc_dtls.hpp
Normal file
|
@ -0,0 +1,242 @@
|
||||||
|
/**
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2020 Winlin
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SRS_APP_RTC_DTLS_HPP
|
||||||
|
#define SRS_APP_RTC_DTLS_HPP
|
||||||
|
|
||||||
|
#include <srs_core.hpp>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <openssl/ssl.h>
|
||||||
|
#include <srtp2/srtp.h>
|
||||||
|
|
||||||
|
#include <srs_app_st.hpp>
|
||||||
|
|
||||||
|
class SrsRequest;
|
||||||
|
|
||||||
|
class SrsDtlsCertificate
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::string fingerprint;
|
||||||
|
bool ecdsa_mode;
|
||||||
|
X509* dtls_cert;
|
||||||
|
EVP_PKEY* dtls_pkey;
|
||||||
|
EC_KEY* eckey;
|
||||||
|
public:
|
||||||
|
SrsDtlsCertificate();
|
||||||
|
virtual ~SrsDtlsCertificate();
|
||||||
|
public:
|
||||||
|
// Initialize DTLS certificate.
|
||||||
|
srs_error_t initialize();
|
||||||
|
// dtls_cert
|
||||||
|
X509* get_cert();
|
||||||
|
// public key
|
||||||
|
EVP_PKEY* get_public_key();
|
||||||
|
// ECDSA key
|
||||||
|
EC_KEY* get_ecdsa_key();
|
||||||
|
// certificate fingerprint
|
||||||
|
std::string get_fingerprint();
|
||||||
|
// whether is ecdsa
|
||||||
|
bool is_ecdsa();
|
||||||
|
};
|
||||||
|
|
||||||
|
// @global config object.
|
||||||
|
extern SrsDtlsCertificate* _srs_rtc_dtls_certificate;
|
||||||
|
|
||||||
|
// @remark: play the role of DTLS_CLIENT, will send handshake
|
||||||
|
// packet first.
|
||||||
|
enum SrsDtlsRole {
|
||||||
|
SrsDtlsRoleClient,
|
||||||
|
SrsDtlsRoleServer
|
||||||
|
};
|
||||||
|
|
||||||
|
// @remark: DTLS_10 will all be ignored, and only DTLS1_2 will be accepted,
|
||||||
|
// DTLS_10 Support will be completely removed in M84 or later.
|
||||||
|
// TODO(https://bugs.webrtc.org/10261).
|
||||||
|
enum SrsDtlsVersion {
|
||||||
|
SrsDtlsVersionAuto = -1,
|
||||||
|
SrsDtlsVersion1_0,
|
||||||
|
SrsDtlsVersion1_2
|
||||||
|
};
|
||||||
|
|
||||||
|
class ISrsDtlsCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ISrsDtlsCallback();
|
||||||
|
virtual ~ISrsDtlsCallback();
|
||||||
|
public:
|
||||||
|
// DTLS handshake done callback.
|
||||||
|
virtual srs_error_t on_dtls_handshake_done() = 0;
|
||||||
|
// DTLS receive application data callback.
|
||||||
|
virtual srs_error_t on_dtls_application_data(const char* data, const int len) = 0;
|
||||||
|
// DTLS write dtls data.
|
||||||
|
virtual srs_error_t write_dtls_data(void* data, int size) = 0;
|
||||||
|
// Callback when DTLS Alert message.
|
||||||
|
virtual srs_error_t on_dtls_alert(std::string type, std::string desc) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The state for DTLS client.
|
||||||
|
enum SrsDtlsState {
|
||||||
|
SrsDtlsStateInit, // Start.
|
||||||
|
SrsDtlsStateClientHello, // Should start ARQ thread.
|
||||||
|
SrsDtlsStateServerHello, // We are in the first ARQ state.
|
||||||
|
SrsDtlsStateClientCertificate, // Should start ARQ thread again.
|
||||||
|
SrsDtlsStateServerDone, // We are in the second ARQ state.
|
||||||
|
SrsDtlsStateClientDone, // Done.
|
||||||
|
};
|
||||||
|
|
||||||
|
class SrsDtlsImpl
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
SSL_CTX* dtls_ctx;
|
||||||
|
SSL* dtls;
|
||||||
|
BIO* bio_in;
|
||||||
|
BIO* bio_out;
|
||||||
|
ISrsDtlsCallback* callback_;
|
||||||
|
// @remark: dtls_version_ default value is SrsDtlsVersionAuto.
|
||||||
|
SrsDtlsVersion version_;
|
||||||
|
protected:
|
||||||
|
// Whether the handhshake is done, for us only.
|
||||||
|
// @remark For us only, means peer maybe not done, we also need to handle the DTLS packet.
|
||||||
|
bool handshake_done_for_us;
|
||||||
|
// DTLS packet cache, only last out-going packet.
|
||||||
|
uint8_t* last_outgoing_packet_cache;
|
||||||
|
int nn_last_outgoing_packet;
|
||||||
|
// The stat for ARQ packets.
|
||||||
|
int nn_arq_packets;
|
||||||
|
public:
|
||||||
|
SrsDtlsImpl(ISrsDtlsCallback* callback);
|
||||||
|
virtual ~SrsDtlsImpl();
|
||||||
|
public:
|
||||||
|
virtual srs_error_t initialize(std::string version);
|
||||||
|
virtual srs_error_t start_active_handshake() = 0;
|
||||||
|
virtual srs_error_t on_dtls(char* data, int nb_data);
|
||||||
|
protected:
|
||||||
|
srs_error_t do_on_dtls(char* data, int nb_data);
|
||||||
|
srs_error_t do_handshake();
|
||||||
|
void state_trace(uint8_t* data, int length, bool incoming, int r0, int r1, bool cache, bool arq);
|
||||||
|
public:
|
||||||
|
srs_error_t get_srtp_key(std::string& recv_key, std::string& send_key);
|
||||||
|
void callback_by_ssl(std::string type, std::string desc);
|
||||||
|
protected:
|
||||||
|
virtual void on_ssl_out_data(uint8_t*& data, int& size, bool& cached) = 0;
|
||||||
|
virtual srs_error_t on_final_out_data(uint8_t* data, int size) = 0;
|
||||||
|
virtual srs_error_t on_handshake_done() = 0;
|
||||||
|
virtual bool is_dtls_client() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SrsDtlsClientImpl : virtual public SrsDtlsImpl, virtual public ISrsCoroutineHandler
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// ARQ thread, for role active(DTLS client).
|
||||||
|
// @note If passive(DTLS server), the ARQ is driven by DTLS client.
|
||||||
|
SrsCoroutine* trd;
|
||||||
|
// The DTLS-client state to drive the ARQ thread.
|
||||||
|
SrsDtlsState state_;
|
||||||
|
// The timeout for ARQ.
|
||||||
|
srs_utime_t arq_interval;
|
||||||
|
int arq_to_ratios[8];
|
||||||
|
public:
|
||||||
|
SrsDtlsClientImpl(ISrsDtlsCallback* callback);
|
||||||
|
virtual ~SrsDtlsClientImpl();
|
||||||
|
public:
|
||||||
|
virtual srs_error_t initialize(std::string version);
|
||||||
|
virtual srs_error_t start_active_handshake();
|
||||||
|
virtual srs_error_t on_dtls(char* data, int nb_data);
|
||||||
|
protected:
|
||||||
|
virtual void on_ssl_out_data(uint8_t*& data, int& size, bool& cached);
|
||||||
|
virtual srs_error_t on_final_out_data(uint8_t* data, int size);
|
||||||
|
virtual srs_error_t on_handshake_done();
|
||||||
|
virtual bool is_dtls_client();
|
||||||
|
private:
|
||||||
|
srs_error_t start_arq();
|
||||||
|
void stop_arq();
|
||||||
|
public:
|
||||||
|
virtual srs_error_t cycle();
|
||||||
|
};
|
||||||
|
|
||||||
|
class SrsDtlsServerImpl : public SrsDtlsImpl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SrsDtlsServerImpl(ISrsDtlsCallback* callback);
|
||||||
|
virtual ~SrsDtlsServerImpl();
|
||||||
|
public:
|
||||||
|
virtual srs_error_t initialize(std::string version);
|
||||||
|
virtual srs_error_t start_active_handshake();
|
||||||
|
protected:
|
||||||
|
virtual void on_ssl_out_data(uint8_t*& data, int& size, bool& cached);
|
||||||
|
virtual srs_error_t on_final_out_data(uint8_t* data, int size);
|
||||||
|
virtual srs_error_t on_handshake_done();
|
||||||
|
virtual bool is_dtls_client();
|
||||||
|
};
|
||||||
|
|
||||||
|
class SrsDtls
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
SrsDtlsImpl* impl;
|
||||||
|
ISrsDtlsCallback* callback_;
|
||||||
|
public:
|
||||||
|
SrsDtls(ISrsDtlsCallback* callback);
|
||||||
|
virtual ~SrsDtls();
|
||||||
|
public:
|
||||||
|
srs_error_t initialize(std::string role, std::string version);
|
||||||
|
public:
|
||||||
|
// As DTLS client, start handshake actively, send the ClientHello packet.
|
||||||
|
srs_error_t start_active_handshake();
|
||||||
|
// When got DTLS packet, may handshake packets or application data.
|
||||||
|
// @remark When we are passive(DTLS server), we start handshake when got DTLS packet.
|
||||||
|
srs_error_t on_dtls(char* data, int nb_data);
|
||||||
|
public:
|
||||||
|
srs_error_t get_srtp_key(std::string& recv_key, std::string& send_key);
|
||||||
|
};
|
||||||
|
|
||||||
|
class SrsSRTP
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
srtp_t recv_ctx_;
|
||||||
|
srtp_t send_ctx_;
|
||||||
|
public:
|
||||||
|
SrsSRTP();
|
||||||
|
virtual ~SrsSRTP();
|
||||||
|
public:
|
||||||
|
// Intialize srtp context with recv_key and send_key.
|
||||||
|
srs_error_t initialize(std::string recv_key, std::string send_key);
|
||||||
|
public:
|
||||||
|
// Encrypt the input plaintext to output cipher with nb_cipher bytes.
|
||||||
|
// @remark Note that the nb_cipher is the size of input plaintext, and
|
||||||
|
// it also is the length of output cipher when return.
|
||||||
|
srs_error_t protect_rtp(const char* plaintext, char* cipher, int& nb_cipher);
|
||||||
|
srs_error_t protect_rtcp(const char* plaintext, char* cipher, int& nb_cipher);
|
||||||
|
// Encrypt the input rtp_hdr with *len_ptr bytes.
|
||||||
|
// @remark the input plaintext and out cipher reuse rtp_hdr.
|
||||||
|
srs_error_t protect_rtp2(void* rtp_hdr, int* len_ptr);
|
||||||
|
// Decrypt the input cipher to output cipher with nb_cipher bytes.
|
||||||
|
// @remark Note that the nb_plaintext is the size of input cipher, and
|
||||||
|
// it also is the length of output plaintext when return.
|
||||||
|
srs_error_t unprotect_rtp(const char* cipher, char* plaintext, int& nb_plaintext);
|
||||||
|
srs_error_t unprotect_rtcp(const char* cipher, char* plaintext, int& nb_plaintext);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
296
trunk/src/app/srs_app_rtc_queue.cpp
Normal file
296
trunk/src/app/srs_app_rtc_queue.cpp
Normal file
|
@ -0,0 +1,296 @@
|
||||||
|
/**
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2020 John
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <srs_app_rtc_queue.hpp>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#include <srs_kernel_error.hpp>
|
||||||
|
#include <srs_kernel_rtc_rtp.hpp>
|
||||||
|
#include <srs_kernel_utility.hpp>
|
||||||
|
#include <srs_app_utility.hpp>
|
||||||
|
|
||||||
|
SrsRtpRingBuffer::SrsRtpRingBuffer(int capacity)
|
||||||
|
{
|
||||||
|
nn_seq_flip_backs = 0;
|
||||||
|
begin = end = 0;
|
||||||
|
capacity_ = (uint16_t)capacity;
|
||||||
|
initialized_ = false;
|
||||||
|
|
||||||
|
queue_ = new SrsRtpPacket2*[capacity_];
|
||||||
|
memset(queue_, 0, sizeof(SrsRtpPacket2*) * capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsRtpRingBuffer::~SrsRtpRingBuffer()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < capacity_; ++i) {
|
||||||
|
SrsRtpPacket2* pkt = queue_[i];
|
||||||
|
srs_freep(pkt);
|
||||||
|
}
|
||||||
|
srs_freepa(queue_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SrsRtpRingBuffer::empty()
|
||||||
|
{
|
||||||
|
return begin == end;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsRtpRingBuffer::size()
|
||||||
|
{
|
||||||
|
int size = srs_rtp_seq_distance(begin, end);
|
||||||
|
srs_assert(size >= 0);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsRtpRingBuffer::advance_to(uint16_t seq)
|
||||||
|
{
|
||||||
|
begin = seq;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsRtpRingBuffer::set(uint16_t at, SrsRtpPacket2* pkt)
|
||||||
|
{
|
||||||
|
SrsRtpPacket2* p = queue_[at % capacity_];
|
||||||
|
|
||||||
|
if (p) {
|
||||||
|
srs_freep(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
queue_[at % capacity_] = pkt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsRtpRingBuffer::remove(uint16_t at)
|
||||||
|
{
|
||||||
|
set(at, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t SrsRtpRingBuffer::get_extended_highest_sequence()
|
||||||
|
{
|
||||||
|
return nn_seq_flip_backs * 65536 + end - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SrsRtpRingBuffer::update(uint16_t seq, uint16_t& nack_first, uint16_t& nack_last)
|
||||||
|
{
|
||||||
|
if (!initialized_) {
|
||||||
|
initialized_ = true;
|
||||||
|
begin = seq;
|
||||||
|
end = seq + 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normal sequence, seq follows high_.
|
||||||
|
if (srs_rtp_seq_distance(end, seq) >= 0) {
|
||||||
|
//TODO: FIXME: if diff_upper > limit_max_size clear?
|
||||||
|
// int16_t diff_upper = srs_rtp_seq_distance(end, seq)
|
||||||
|
// notify_nack_list_full()
|
||||||
|
nack_first = end;
|
||||||
|
nack_last = seq;
|
||||||
|
|
||||||
|
// When distance(seq,high_)>0 and seq<high_, seq must flip back,
|
||||||
|
// for example, high_=65535, seq=1, distance(65535,1)>0 and 1<65535.
|
||||||
|
// TODO: FIXME: The first flip may be dropped.
|
||||||
|
if (seq < end) {
|
||||||
|
++nn_seq_flip_backs;
|
||||||
|
}
|
||||||
|
end = seq + 1;
|
||||||
|
// TODO: FIXME: check whether is neccessary?
|
||||||
|
// srs_rtp_seq_distance(begin, end) > max_size
|
||||||
|
// advance_to(), srs_rtp_seq_distance(begin, end) < max_size;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Out-of-order sequence, seq before low_.
|
||||||
|
if (srs_rtp_seq_distance(seq, begin) > 0) {
|
||||||
|
nack_first = seq;
|
||||||
|
nack_last = begin;
|
||||||
|
begin = seq;
|
||||||
|
|
||||||
|
// TODO: FIXME: Maybe should support startup drop.
|
||||||
|
return true;
|
||||||
|
// When startup, we may receive packets in chaos order.
|
||||||
|
// Because we don't know the ISN(initiazlie sequence number), the first packet
|
||||||
|
// we received maybe no the first packet client sent.
|
||||||
|
// @remark We only log a warning, because it seems ok for publisher.
|
||||||
|
//return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsRtpPacket2* SrsRtpRingBuffer::at(uint16_t seq) {
|
||||||
|
return queue_[seq % capacity_];
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsRtpRingBuffer::notify_nack_list_full()
|
||||||
|
{
|
||||||
|
while(begin <= end) {
|
||||||
|
remove(begin);
|
||||||
|
++begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
begin = end = 0;
|
||||||
|
initialized_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsRtpRingBuffer::notify_drop_seq(uint16_t seq)
|
||||||
|
{
|
||||||
|
remove(seq);
|
||||||
|
advance_to(seq+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsNackOption::SrsNackOption()
|
||||||
|
{
|
||||||
|
max_count = 15;
|
||||||
|
max_alive_time = 1000 * SRS_UTIME_MILLISECONDS;
|
||||||
|
first_nack_interval = 10 * SRS_UTIME_MILLISECONDS;
|
||||||
|
nack_interval = 50 * SRS_UTIME_MILLISECONDS;
|
||||||
|
max_nack_interval = 500 * SRS_UTIME_MILLISECONDS;
|
||||||
|
min_nack_interval = 20 * SRS_UTIME_MILLISECONDS;
|
||||||
|
|
||||||
|
nack_check_interval = 3 * SRS_UTIME_MILLISECONDS;
|
||||||
|
|
||||||
|
//TODO: FIXME: audio and video using diff nack strategy
|
||||||
|
// video:
|
||||||
|
// max_alive_time = 1 * SRS_UTIME_SECONDS
|
||||||
|
// max_count = 15;
|
||||||
|
// nack_interval = 50 * SRS_UTIME_MILLISECONDS
|
||||||
|
//
|
||||||
|
// audio:
|
||||||
|
// DefaultRequestNackDelay = 30; //ms
|
||||||
|
// DefaultLostPacketLifeTime = 600; //ms
|
||||||
|
// FirstRequestInterval = 50;//ms
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsRtpNackInfo::SrsRtpNackInfo()
|
||||||
|
{
|
||||||
|
generate_time_ = srs_update_system_time();
|
||||||
|
pre_req_nack_time_ = 0;
|
||||||
|
req_nack_count_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsRtpNackForReceiver::SrsRtpNackForReceiver(SrsRtpRingBuffer* rtp, size_t queue_size)
|
||||||
|
{
|
||||||
|
max_queue_size_ = queue_size;
|
||||||
|
rtp_ = rtp;
|
||||||
|
pre_check_time_ = 0;
|
||||||
|
rtt_ = 0;
|
||||||
|
|
||||||
|
srs_info("max_queue_size=%u, nack opt: max_count=%d, max_alive_time=%us, first_nack_interval=%" PRId64 ", nack_interval=%" PRId64,
|
||||||
|
max_queue_size_, opts_.max_count, opts_.max_alive_time, opts_.first_nack_interval, opts_.nack_interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsRtpNackForReceiver::~SrsRtpNackForReceiver()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsRtpNackForReceiver::insert(uint16_t first, uint16_t last)
|
||||||
|
{
|
||||||
|
for (uint16_t s = first; s != last; ++s) {
|
||||||
|
queue_[s] = SrsRtpNackInfo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsRtpNackForReceiver::remove(uint16_t seq)
|
||||||
|
{
|
||||||
|
queue_.erase(seq);
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsRtpNackInfo* SrsRtpNackForReceiver::find(uint16_t seq)
|
||||||
|
{
|
||||||
|
std::map<uint16_t, SrsRtpNackInfo>::iterator iter = queue_.find(seq);
|
||||||
|
|
||||||
|
if (iter == queue_.end()) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &(iter->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsRtpNackForReceiver::check_queue_size()
|
||||||
|
{
|
||||||
|
if (queue_.size() >= max_queue_size_) {
|
||||||
|
rtp_->notify_nack_list_full();
|
||||||
|
queue_.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsRtpNackForReceiver::get_nack_seqs(SrsRtcpNack& seqs, uint32_t& timeout_nacks)
|
||||||
|
{
|
||||||
|
// TODO: FIXME: Use packet as tick count, not clock.
|
||||||
|
srs_utime_t now = srs_update_system_time();
|
||||||
|
|
||||||
|
srs_utime_t interval = now - pre_check_time_;
|
||||||
|
if (interval < opts_.nack_check_interval) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pre_check_time_ = now;
|
||||||
|
|
||||||
|
std::map<uint16_t, SrsRtpNackInfo>::iterator iter = queue_.begin();
|
||||||
|
while (iter != queue_.end()) {
|
||||||
|
const uint16_t& seq = iter->first;
|
||||||
|
SrsRtpNackInfo& nack_info = iter->second;
|
||||||
|
|
||||||
|
int alive_time = now - nack_info.generate_time_;
|
||||||
|
if (alive_time > opts_.max_alive_time || nack_info.req_nack_count_ > opts_.max_count) {
|
||||||
|
++timeout_nacks;
|
||||||
|
rtp_->notify_drop_seq(seq);
|
||||||
|
queue_.erase(iter++);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO:Statistics unorder packet.
|
||||||
|
if (now - nack_info.generate_time_ < opts_.first_nack_interval) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_utime_t nack_interval = srs_max(opts_.min_nack_interval, opts_.nack_interval / 3);
|
||||||
|
if(opts_.nack_interval < 50 * SRS_UTIME_MILLISECONDS){
|
||||||
|
nack_interval = srs_max(opts_.min_nack_interval, opts_.nack_interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (now - nack_info.pre_req_nack_time_ >= nack_interval ) {
|
||||||
|
++nack_info.req_nack_count_;
|
||||||
|
nack_info.pre_req_nack_time_ = now;
|
||||||
|
seqs.add_lost_sn(seq);
|
||||||
|
}
|
||||||
|
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsRtpNackForReceiver::update_rtt(int rtt)
|
||||||
|
{
|
||||||
|
rtt_ = rtt * SRS_UTIME_MILLISECONDS;
|
||||||
|
|
||||||
|
if (rtt_ > opts_.nack_interval) {
|
||||||
|
opts_.nack_interval = opts_.nack_interval * 0.8 + rtt_ * 0.2;
|
||||||
|
} else {
|
||||||
|
opts_.nack_interval = rtt_;
|
||||||
|
}
|
||||||
|
|
||||||
|
opts_.nack_interval = srs_min(opts_.nack_interval, opts_.max_nack_interval);
|
||||||
|
}
|
||||||
|
|
145
trunk/src/app/srs_app_rtc_queue.hpp
Normal file
145
trunk/src/app/srs_app_rtc_queue.hpp
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
/**
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2020 John
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SRS_APP_RTC_QUEUE_HPP
|
||||||
|
#define SRS_APP_RTC_QUEUE_HPP
|
||||||
|
|
||||||
|
#include <srs_core.hpp>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include <srs_kernel_rtc_rtp.hpp>
|
||||||
|
#include <srs_kernel_rtc_rtcp.hpp>
|
||||||
|
|
||||||
|
class SrsRtpPacket2;
|
||||||
|
class SrsRtpQueue;
|
||||||
|
class SrsRtpRingBuffer;
|
||||||
|
|
||||||
|
// For UDP, the packets sequence may present as bellow:
|
||||||
|
// [seq1(done)|seq2|seq3 ... seq10|seq11(lost)|seq12|seq13]
|
||||||
|
// \___(head_sequence_) \ \___(highest_sequence_)
|
||||||
|
// \___(no received, in nack list)
|
||||||
|
// * seq1: The packet is done, we have already got and processed it.
|
||||||
|
// * seq2,seq3,...,seq10,seq12,seq13: Theses packets are in queue and wait to be processed.
|
||||||
|
// * seq10: This packet is lost or not received, we will put it in the nack list.
|
||||||
|
// We store the received packets in ring buffer.
|
||||||
|
class SrsRtpRingBuffer
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// Capacity of the ring-buffer.
|
||||||
|
uint16_t capacity_;
|
||||||
|
// Ring bufer.
|
||||||
|
SrsRtpPacket2** queue_;
|
||||||
|
// Increase one when uint16 flip back, for get_extended_highest_sequence.
|
||||||
|
uint64_t nn_seq_flip_backs;
|
||||||
|
// Whether initialized, because we use uint16 so we can't use -1.
|
||||||
|
bool initialized_;
|
||||||
|
public:
|
||||||
|
// The begin iterator for ring buffer.
|
||||||
|
// For example, when got 1 elems, the begin is 0.
|
||||||
|
uint16_t begin;
|
||||||
|
// The end iterator for ring buffer.
|
||||||
|
// For example, when got 1 elems, the end is 1.
|
||||||
|
uint16_t end;
|
||||||
|
public:
|
||||||
|
SrsRtpRingBuffer(int capacity);
|
||||||
|
virtual ~SrsRtpRingBuffer();
|
||||||
|
public:
|
||||||
|
// Whether the ring buffer is empty.
|
||||||
|
bool empty();
|
||||||
|
// Get the count of elems in ring buffer.
|
||||||
|
int size();
|
||||||
|
// Move the low position of buffer to seq.
|
||||||
|
void advance_to(uint16_t seq);
|
||||||
|
// Free the packet at position.
|
||||||
|
void set(uint16_t at, SrsRtpPacket2* pkt);
|
||||||
|
void remove(uint16_t at);
|
||||||
|
// The highest sequence number, calculate the flip back base.
|
||||||
|
uint32_t get_extended_highest_sequence();
|
||||||
|
// Update the sequence, got the nack range by [first, last).
|
||||||
|
// @return If false, the seq is too old.
|
||||||
|
bool update(uint16_t seq, uint16_t& nack_first, uint16_t& nack_last);
|
||||||
|
// Get the packet by seq.
|
||||||
|
SrsRtpPacket2* at(uint16_t seq);
|
||||||
|
public:
|
||||||
|
// TODO: FIXME: Refine it?
|
||||||
|
void notify_nack_list_full();
|
||||||
|
void notify_drop_seq(uint16_t seq);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SrsNackOption
|
||||||
|
{
|
||||||
|
int max_count;
|
||||||
|
srs_utime_t max_alive_time;
|
||||||
|
srs_utime_t first_nack_interval;
|
||||||
|
srs_utime_t nack_interval;
|
||||||
|
|
||||||
|
srs_utime_t max_nack_interval;
|
||||||
|
srs_utime_t min_nack_interval;
|
||||||
|
srs_utime_t nack_check_interval;
|
||||||
|
|
||||||
|
SrsNackOption();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SrsRtpNackInfo
|
||||||
|
{
|
||||||
|
// Use to control the time of first nack req and the life of seq.
|
||||||
|
srs_utime_t generate_time_;
|
||||||
|
// Use to control nack interval.
|
||||||
|
srs_utime_t pre_req_nack_time_;
|
||||||
|
// Use to control nack times.
|
||||||
|
int req_nack_count_;
|
||||||
|
|
||||||
|
SrsRtpNackInfo();
|
||||||
|
};
|
||||||
|
|
||||||
|
class SrsRtpNackForReceiver
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// Nack queue, seq order, oldest to newest.
|
||||||
|
std::map<uint16_t, SrsRtpNackInfo, SrsSeqCompareLess> queue_;
|
||||||
|
// Max nack count.
|
||||||
|
size_t max_queue_size_;
|
||||||
|
SrsRtpRingBuffer* rtp_;
|
||||||
|
SrsNackOption opts_;
|
||||||
|
private:
|
||||||
|
srs_utime_t pre_check_time_;
|
||||||
|
private:
|
||||||
|
int rtt_;
|
||||||
|
public:
|
||||||
|
SrsRtpNackForReceiver(SrsRtpRingBuffer* rtp, size_t queue_size);
|
||||||
|
virtual ~SrsRtpNackForReceiver();
|
||||||
|
public:
|
||||||
|
void insert(uint16_t first, uint16_t last);
|
||||||
|
void remove(uint16_t seq);
|
||||||
|
SrsRtpNackInfo* find(uint16_t seq);
|
||||||
|
void check_queue_size();
|
||||||
|
public:
|
||||||
|
void get_nack_seqs(SrsRtcpNack& seqs, uint32_t& timeout_nacks);
|
||||||
|
public:
|
||||||
|
void update_rtt(int rtt);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -21,31 +21,33 @@
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <srs_app_sdp.hpp>
|
#include <srs_app_rtc_sdp.hpp>
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
#include <srs_kernel_error.hpp>
|
#include <srs_kernel_error.hpp>
|
||||||
#include <srs_kernel_log.hpp>
|
#include <srs_kernel_log.hpp>
|
||||||
|
|
||||||
const std::string kCRLF = "\\r\\n";
|
// TODO: FIXME: Maybe we should use json.encode to escape it?
|
||||||
|
const std::string kCRLF = "\r\n";
|
||||||
|
|
||||||
#define FETCH(is,word) \
|
#define FETCH(is,word) \
|
||||||
if (! (is >> word)) {\
|
if (!(is >> word)) {\
|
||||||
return srs_error_new(ERROR_RTC_SDP_DECODE, "fetch failed");\
|
return srs_error_new(ERROR_RTC_SDP_DECODE, "fetch failed");\
|
||||||
}\
|
}\
|
||||||
|
|
||||||
#define FETCH_WITH_DELIM(is,word,delim) \
|
#define FETCH_WITH_DELIM(is,word,delim) \
|
||||||
if (! getline(is,word,delim)) {\
|
if (!getline(is,word,delim)) {\
|
||||||
return srs_error_new(ERROR_RTC_SDP_DECODE, "fetch with delim failed");\
|
return srs_error_new(ERROR_RTC_SDP_DECODE, "fetch with delim failed");\
|
||||||
}\
|
}\
|
||||||
|
|
||||||
static std::vector<std::string> split_str(const std::string& str, const std::string& delim)
|
std::vector<std::string> split_str(const std::string& str, const std::string& delim)
|
||||||
{
|
{
|
||||||
std::vector<std::string> ret;
|
std::vector<std::string> ret;
|
||||||
size_t pre_pos = 0;
|
size_t pre_pos = 0;
|
||||||
|
@ -68,7 +70,7 @@ static void skip_first_spaces(std::string& str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t parse_h264_fmtp(const std::string& fmtp, H264SpecificParam& h264_param)
|
srs_error_t srs_parse_h264_fmtp(const std::string& fmtp, H264SpecificParam& h264_param)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
std::vector<std::string> vec = split_str(fmtp, ";");
|
std::vector<std::string> vec = split_str(fmtp, ";");
|
||||||
|
@ -111,6 +113,7 @@ SrsSessionInfo::~SrsSessionInfo()
|
||||||
srs_error_t SrsSessionInfo::parse_attribute(const std::string& attribute, const std::string& value)
|
srs_error_t SrsSessionInfo::parse_attribute(const std::string& attribute, const std::string& value)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
if (attribute == "ice-ufrag") {
|
if (attribute == "ice-ufrag") {
|
||||||
ice_ufrag_ = value;
|
ice_ufrag_ = value;
|
||||||
} else if (attribute == "ice-pwd") {
|
} else if (attribute == "ice-pwd") {
|
||||||
|
@ -134,23 +137,25 @@ srs_error_t SrsSessionInfo::parse_attribute(const std::string& attribute, const
|
||||||
srs_error_t SrsSessionInfo::encode(std::ostringstream& os)
|
srs_error_t SrsSessionInfo::encode(std::ostringstream& os)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
if (! ice_ufrag_.empty()) {
|
|
||||||
|
if (!ice_ufrag_.empty()) {
|
||||||
os << "a=ice-ufrag:" << ice_ufrag_ << kCRLF;
|
os << "a=ice-ufrag:" << ice_ufrag_ << kCRLF;
|
||||||
}
|
}
|
||||||
if (! ice_pwd_.empty()) {
|
|
||||||
|
if (!ice_pwd_.empty()) {
|
||||||
os << "a=ice-pwd:" << ice_pwd_ << kCRLF;
|
os << "a=ice-pwd:" << ice_pwd_ << kCRLF;
|
||||||
}
|
}
|
||||||
if (! ice_options_.empty()) {
|
|
||||||
|
// For ICE-lite, we never set the trickle.
|
||||||
|
if (!ice_options_.empty()) {
|
||||||
os << "a=ice-options:" << ice_options_ << kCRLF;
|
os << "a=ice-options:" << ice_options_ << kCRLF;
|
||||||
} else {
|
|
||||||
// @see: https://webrtcglossary.com/trickle-ice/
|
|
||||||
// Trickle ICE is an optimization of the ICE specification for NAT traversal.
|
|
||||||
os << "a=ice-options:trickle" << kCRLF;
|
|
||||||
}
|
}
|
||||||
if (! fingerprint_algo_.empty() && ! fingerprint_.empty()) {
|
|
||||||
|
if (!fingerprint_algo_.empty() && ! fingerprint_.empty()) {
|
||||||
os << "a=fingerprint:" << fingerprint_algo_ << " " << fingerprint_ << kCRLF;
|
os << "a=fingerprint:" << fingerprint_algo_ << " " << fingerprint_ << kCRLF;
|
||||||
}
|
}
|
||||||
if (! setup_.empty()) {
|
|
||||||
|
if (!setup_.empty()) {
|
||||||
os << "a=setup:" << setup_ << kCRLF;
|
os << "a=setup:" << setup_ << kCRLF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,6 +177,16 @@ SrsSSRCInfo::SrsSSRCInfo()
|
||||||
ssrc_ = 0;
|
ssrc_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SrsSSRCInfo::SrsSSRCInfo(uint32_t ssrc, std::string cname, std::string stream_id, std::string track_id)
|
||||||
|
{
|
||||||
|
ssrc_ = ssrc;
|
||||||
|
cname_ = cname;
|
||||||
|
msid_ = stream_id;
|
||||||
|
msid_tracker_ = track_id;
|
||||||
|
mslabel_ = msid_;
|
||||||
|
label_ = msid_tracker_;
|
||||||
|
}
|
||||||
|
|
||||||
SrsSSRCInfo::~SrsSSRCInfo()
|
SrsSSRCInfo::~SrsSSRCInfo()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -179,31 +194,67 @@ SrsSSRCInfo::~SrsSSRCInfo()
|
||||||
srs_error_t SrsSSRCInfo::encode(std::ostringstream& os)
|
srs_error_t SrsSSRCInfo::encode(std::ostringstream& os)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
if (ssrc_ == 0) {
|
if (ssrc_ == 0) {
|
||||||
return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid ssrc");
|
return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid ssrc");
|
||||||
}
|
}
|
||||||
|
|
||||||
os << "a=ssrc:" << ssrc_ << " cname:" << cname_ << kCRLF;
|
os << "a=ssrc:" << ssrc_ << " cname:" << cname_ << kCRLF;
|
||||||
if (! msid_.empty()) {
|
if (!msid_.empty()) {
|
||||||
os << "a=ssrc:" << ssrc_ << " msid:" << msid_;
|
os << "a=ssrc:" << ssrc_ << " msid:" << msid_;
|
||||||
if (! msid_tracker_.empty()) {
|
if (!msid_tracker_.empty()) {
|
||||||
os << " " << msid_tracker_;
|
os << " " << msid_tracker_;
|
||||||
}
|
}
|
||||||
os << kCRLF;
|
os << kCRLF;
|
||||||
}
|
}
|
||||||
if (! mslabel_.empty()) {
|
if (!mslabel_.empty()) {
|
||||||
os << "a=ssrc:" << ssrc_ << " mslabel:" << mslabel_ << kCRLF;
|
os << "a=ssrc:" << ssrc_ << " mslabel:" << mslabel_ << kCRLF;
|
||||||
}
|
}
|
||||||
if (! label_.empty()) {
|
if (!label_.empty()) {
|
||||||
os << "a=ssrc:" << ssrc_ << " label:" << label_ << kCRLF;
|
os << "a=ssrc:" << ssrc_ << " label:" << label_ << kCRLF;
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SrsSSRCGroup::SrsSSRCGroup()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsSSRCGroup::~SrsSSRCGroup()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsSSRCGroup::SrsSSRCGroup(const std::string& semantic, const std::vector<uint32_t>& ssrcs)
|
||||||
|
{
|
||||||
|
semantic_ = semantic;
|
||||||
|
ssrcs_ = ssrcs;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsSSRCGroup::encode(std::ostringstream& os)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
if (semantic_.empty()) {
|
||||||
|
return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid semantics");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ssrcs_.size() == 0) {
|
||||||
|
return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid ssrcs");
|
||||||
|
}
|
||||||
|
|
||||||
|
os << "a=ssrc-group:" << semantic_;
|
||||||
|
for (int i = 0; i < (int)ssrcs_.size(); i++) {
|
||||||
|
os << " " << ssrcs_[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
SrsMediaPayloadType::SrsMediaPayloadType(int payload_type)
|
SrsMediaPayloadType::SrsMediaPayloadType(int payload_type)
|
||||||
{
|
{
|
||||||
payload_type_ = payload_type;
|
payload_type_ = payload_type;
|
||||||
|
clock_rate_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsMediaPayloadType::~SrsMediaPayloadType()
|
SrsMediaPayloadType::~SrsMediaPayloadType()
|
||||||
|
@ -215,7 +266,7 @@ srs_error_t SrsMediaPayloadType::encode(std::ostringstream& os)
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
os << "a=rtpmap:" << payload_type_ << " " << encoding_name_ << "/" << clock_rate_;
|
os << "a=rtpmap:" << payload_type_ << " " << encoding_name_ << "/" << clock_rate_;
|
||||||
if (! encoding_param_.empty()) {
|
if (!encoding_param_.empty()) {
|
||||||
os << "/" << encoding_param_;
|
os << "/" << encoding_param_;
|
||||||
}
|
}
|
||||||
os << kCRLF;
|
os << kCRLF;
|
||||||
|
@ -224,8 +275,11 @@ srs_error_t SrsMediaPayloadType::encode(std::ostringstream& os)
|
||||||
os << "a=rtcp-fb:" << payload_type_ << " " << *iter << kCRLF;
|
os << "a=rtcp-fb:" << payload_type_ << " " << *iter << kCRLF;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! format_specific_param_.empty()) {
|
if (!format_specific_param_.empty()) {
|
||||||
os << "a=fmtp:" << payload_type_ << " " << format_specific_param_ << kCRLF;
|
os << "a=fmtp:" << payload_type_ << " " << format_specific_param_
|
||||||
|
// TODO: FIXME: Remove the test code bellow.
|
||||||
|
// << ";x-google-max-bitrate=6000;x-google-min-bitrate=5100;x-google-start-bitrate=5000"
|
||||||
|
<< kCRLF;
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
@ -235,7 +289,9 @@ SrsMediaDesc::SrsMediaDesc(const std::string& type)
|
||||||
{
|
{
|
||||||
type_ = type;
|
type_ = type;
|
||||||
|
|
||||||
|
port_ = 0;
|
||||||
rtcp_mux_ = false;
|
rtcp_mux_ = false;
|
||||||
|
rtcp_rsize_ = false;
|
||||||
|
|
||||||
sendrecv_ = false;
|
sendrecv_ = false;
|
||||||
recvonly_ = false;
|
recvonly_ = false;
|
||||||
|
@ -262,8 +318,13 @@ vector<SrsMediaPayloadType> SrsMediaDesc::find_media_with_encoding_name(const st
|
||||||
{
|
{
|
||||||
std::vector<SrsMediaPayloadType> payloads;
|
std::vector<SrsMediaPayloadType> payloads;
|
||||||
|
|
||||||
|
std::string lower_name(encoding_name), upper_name(encoding_name);
|
||||||
|
transform(encoding_name.begin(), encoding_name.end(), lower_name.begin(), ::tolower);
|
||||||
|
transform(encoding_name.begin(), encoding_name.end(), upper_name.begin(), ::toupper);
|
||||||
|
|
||||||
for (size_t i = 0; i < payload_types_.size(); ++i) {
|
for (size_t i = 0; i < payload_types_.size(); ++i) {
|
||||||
if (payload_types_[i].encoding_name_ == encoding_name) {
|
if (payload_types_[i].encoding_name_ == std::string(lower_name.c_str()) ||
|
||||||
|
payload_types_[i].encoding_name_ == std::string(upper_name.c_str())) {
|
||||||
payloads.push_back(payload_types_[i]);
|
payloads.push_back(payload_types_[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -271,6 +332,20 @@ vector<SrsMediaPayloadType> SrsMediaDesc::find_media_with_encoding_name(const st
|
||||||
return payloads;
|
return payloads;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsMediaDesc::update_msid(string id)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
for(vector<SrsSSRCInfo>::iterator it = ssrc_infos_.begin(); it != ssrc_infos_.end(); ++it) {
|
||||||
|
SrsSSRCInfo& info = *it;
|
||||||
|
|
||||||
|
info.msid_ = id;
|
||||||
|
info.mslabel_ = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
srs_error_t SrsMediaDesc::parse_line(const std::string& line)
|
srs_error_t SrsMediaDesc::parse_line(const std::string& line)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
@ -312,16 +387,19 @@ srs_error_t SrsMediaDesc::encode(std::ostringstream& os)
|
||||||
}
|
}
|
||||||
|
|
||||||
os << "a=mid:" << mid_ << kCRLF;
|
os << "a=mid:" << mid_ << kCRLF;
|
||||||
if (! msid_.empty()) {
|
if (!msid_.empty()) {
|
||||||
os << "a=msid:" << msid_;
|
os << "a=msid:" << msid_;
|
||||||
|
|
||||||
if (! msid_tracker_.empty()) {
|
if (!msid_tracker_.empty()) {
|
||||||
os << " " << msid_tracker_;
|
os << " " << msid_tracker_;
|
||||||
}
|
}
|
||||||
|
|
||||||
os << kCRLF;
|
os << kCRLF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for(map<int, string>::iterator it = extmaps_.begin(); it != extmaps_.end(); ++it) {
|
||||||
|
os << "a=extmap:"<< it->first<< " "<< it->second<< kCRLF;
|
||||||
|
}
|
||||||
if (sendonly_) {
|
if (sendonly_) {
|
||||||
os << "a=sendonly" << kCRLF;
|
os << "a=sendonly" << kCRLF;
|
||||||
}
|
}
|
||||||
|
@ -359,7 +437,7 @@ srs_error_t SrsMediaDesc::encode(std::ostringstream& os)
|
||||||
|
|
||||||
int foundation = 0;
|
int foundation = 0;
|
||||||
int component_id = 1; /* RTP */
|
int component_id = 1; /* RTP */
|
||||||
for (std::vector<SrsCandidate>::iterator iter = candidates_.begin(); iter != candidates_.end(); ++iter) {
|
for (std::vector<SrsCandidate>::iterator iter = candidates_.begin(); iter != candidates_.end(); ++iter) {
|
||||||
// @see: https://tools.ietf.org/html/draft-ietf-ice-rfc5245bis-00#section-4.2
|
// @see: https://tools.ietf.org/html/draft-ietf-ice-rfc5245bis-00#section-4.2
|
||||||
uint32_t priority = (1<<24)*(126) + (1<<8)*(65535) + (1)*(256 - component_id);
|
uint32_t priority = (1<<24)*(126) + (1<<8)*(65535) + (1)*(256 - component_id);
|
||||||
|
|
||||||
|
@ -369,6 +447,8 @@ srs_error_t SrsMediaDesc::encode(std::ostringstream& os)
|
||||||
<< iter->ip_ << " " << iter->port_
|
<< iter->ip_ << " " << iter->port_
|
||||||
<< " typ " << iter->type_
|
<< " typ " << iter->type_
|
||||||
<< " generation 0" << kCRLF;
|
<< " generation 0" << kCRLF;
|
||||||
|
|
||||||
|
srs_verbose("local SDP candidate line=%s", os.str().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
@ -389,8 +469,7 @@ srs_error_t SrsMediaDesc::parse_attribute(const std::string& content)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attribute == "extmap") {
|
if (attribute == "extmap") {
|
||||||
// TODO:We don't parse "extmap" currently.
|
return parse_attr_extmap(value);
|
||||||
return 0;
|
|
||||||
} else if (attribute == "rtpmap") {
|
} else if (attribute == "rtpmap") {
|
||||||
return parse_attr_rtpmap(value);
|
return parse_attr_rtpmap(value);
|
||||||
} else if (attribute == "rtcp") {
|
} else if (attribute == "rtcp") {
|
||||||
|
@ -419,12 +498,26 @@ srs_error_t SrsMediaDesc::parse_attribute(const std::string& content)
|
||||||
sendrecv_ = true;
|
sendrecv_ = true;
|
||||||
} else if (attribute == "inactive") {
|
} else if (attribute == "inactive") {
|
||||||
inactive_ = true;
|
inactive_ = true;
|
||||||
} else {
|
} else {
|
||||||
return session_info_.parse_attribute(attribute, value);
|
return session_info_.parse_attribute(attribute, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
srs_error_t SrsMediaDesc::parse_attr_extmap(const std::string& value)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
std::istringstream is(value);
|
||||||
|
int id = 0;
|
||||||
|
FETCH(is, id);
|
||||||
|
if(extmaps_.end() != extmaps_.find(id)) {
|
||||||
|
return srs_error_new(ERROR_RTC_SDP_DECODE, "duplicate ext id: %d", id);
|
||||||
|
}
|
||||||
|
string ext;
|
||||||
|
FETCH(is, ext);
|
||||||
|
extmaps_[id] = ext;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
srs_error_t SrsMediaDesc::parse_attr_rtpmap(const std::string& value)
|
srs_error_t SrsMediaDesc::parse_attr_rtpmap(const std::string& value)
|
||||||
{
|
{
|
||||||
|
@ -523,7 +616,7 @@ srs_error_t SrsMediaDesc::parse_attr_mid(const std::string& value)
|
||||||
std::istringstream is(value);
|
std::istringstream is(value);
|
||||||
// mid_ means m-line id
|
// mid_ means m-line id
|
||||||
FETCH(is, mid_);
|
FETCH(is, mid_);
|
||||||
srs_trace("mid=%s", mid_.c_str());
|
srs_verbose("mid=%s", mid_.c_str());
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -563,6 +656,7 @@ srs_error_t SrsMediaDesc::parse_attr_ssrc(const std::string& value)
|
||||||
ssrc_info.cname_ = ssrc_value;
|
ssrc_info.cname_ = ssrc_value;
|
||||||
ssrc_info.ssrc_ = ssrc;
|
ssrc_info.ssrc_ = ssrc;
|
||||||
} else if (ssrc_attr == "msid") {
|
} else if (ssrc_attr == "msid") {
|
||||||
|
// @see: https://tools.ietf.org/html/draft-alvestrand-mmusic-msid-00#section-2
|
||||||
std::vector<std::string> vec = split_str(ssrc_value, " ");
|
std::vector<std::string> vec = split_str(ssrc_value, " ");
|
||||||
if (vec.empty()) {
|
if (vec.empty()) {
|
||||||
return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid ssrc line=%s", value.c_str());
|
return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid ssrc line=%s", value.c_str());
|
||||||
|
@ -592,10 +686,23 @@ srs_error_t SrsMediaDesc::parse_attr_ssrc_group(const std::string& value)
|
||||||
std::string semantics;
|
std::string semantics;
|
||||||
FETCH(is, semantics);
|
FETCH(is, semantics);
|
||||||
|
|
||||||
// TODO: ssrc group process
|
std::string ssrc_ids = is.str().substr(is.tellg());
|
||||||
if (semantics == "FID") {
|
skip_first_spaces(ssrc_ids);
|
||||||
|
|
||||||
|
std::vector<std::string> vec = split_str(ssrc_ids, " ");
|
||||||
|
if (vec.size() == 0) {
|
||||||
|
return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid ssrc-group line=%s", value.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<uint32_t> ssrcs;
|
||||||
|
for (size_t i = 0; i < vec.size(); ++i) {
|
||||||
|
std::istringstream in_stream(vec[i]);
|
||||||
|
uint32_t ssrc = 0;
|
||||||
|
in_stream >> ssrc;
|
||||||
|
ssrcs.push_back(ssrc);
|
||||||
|
}
|
||||||
|
ssrc_groups_.push_back(SrsSSRCGroup(semantics, ssrcs));
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -649,11 +756,11 @@ srs_error_t SrsSdp::parse(const std::string& sdp_str)
|
||||||
std::istringstream is(sdp_str);
|
std::istringstream is(sdp_str);
|
||||||
std::string line;
|
std::string line;
|
||||||
while (getline(is, line)) {
|
while (getline(is, line)) {
|
||||||
srs_trace("%s", line.c_str());
|
srs_verbose("%s", line.c_str());
|
||||||
if (line.size() < 2 || line[1] != '=') {
|
if (line.size() < 2 || line[1] != '=') {
|
||||||
return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid sdp line=%s", line.c_str());
|
return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid sdp line=%s", line.c_str());
|
||||||
}
|
}
|
||||||
if (! line.empty() && line[line.size()-1] == '\r') {
|
if (!line.empty() && line[line.size()-1] == '\r') {
|
||||||
line.erase(line.size()-1, 1);
|
line.erase(line.size()-1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -662,6 +769,31 @@ srs_error_t SrsSdp::parse(const std::string& sdp_str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The msid/tracker/mslabel is optional for SSRC, so we copy it when it's empty.
|
||||||
|
for (std::vector<SrsMediaDesc>::iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
|
||||||
|
SrsMediaDesc& media_desc = *iter;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < media_desc.ssrc_infos_.size(); ++i) {
|
||||||
|
SrsSSRCInfo& ssrc_info = media_desc.ssrc_infos_.at(i);
|
||||||
|
|
||||||
|
if (ssrc_info.msid_.empty()) {
|
||||||
|
ssrc_info.msid_ = media_desc.msid_;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ssrc_info.msid_tracker_.empty()) {
|
||||||
|
ssrc_info.msid_tracker_ = media_desc.msid_tracker_;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ssrc_info.mslabel_.empty()) {
|
||||||
|
ssrc_info.mslabel_ = media_desc.msid_;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ssrc_info.label_.empty()) {
|
||||||
|
ssrc_info.label_ = media_desc.msid_tracker_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -676,7 +808,7 @@ srs_error_t SrsSdp::encode(std::ostringstream& os)
|
||||||
// ice-lite is a minimal version of the ICE specification, intended for servers running on a public IP address.
|
// ice-lite is a minimal version of the ICE specification, intended for servers running on a public IP address.
|
||||||
os << "a=ice-lite" << kCRLF;
|
os << "a=ice-lite" << kCRLF;
|
||||||
|
|
||||||
if (! groups_.empty()) {
|
if (!groups_.empty()) {
|
||||||
os << "a=group:" << group_policy_;
|
os << "a=group:" << group_policy_;
|
||||||
for (std::vector<std::string>::iterator iter = groups_.begin(); iter != groups_.end(); ++iter) {
|
for (std::vector<std::string>::iterator iter = groups_.begin(); iter != groups_.end(); ++iter) {
|
||||||
os << " " << *iter;
|
os << " " << *iter;
|
||||||
|
@ -703,42 +835,57 @@ srs_error_t SrsSdp::encode(std::ostringstream& os)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SrsMediaDesc* SrsSdp::find_media_desc(const std::string& type) const
|
std::vector<SrsMediaDesc*> SrsSdp::find_media_descs(const std::string& type)
|
||||||
{
|
{
|
||||||
for (std::vector<SrsMediaDesc>::const_iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
|
std::vector<SrsMediaDesc*> descs;
|
||||||
if (iter->type_ == type) {
|
for (std::vector<SrsMediaDesc>::iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
|
||||||
return &(*iter);
|
SrsMediaDesc* desc = &(*iter);
|
||||||
|
|
||||||
|
if (desc->type_ == type) {
|
||||||
|
descs.push_back(desc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return descs;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SrsSdp::set_ice_ufrag(const std::string& ufrag)
|
void SrsSdp::set_ice_ufrag(const std::string& ufrag)
|
||||||
{
|
{
|
||||||
for (std::vector<SrsMediaDesc>::iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
|
for (std::vector<SrsMediaDesc>::iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
|
||||||
iter->session_info_.ice_ufrag_ = ufrag;
|
SrsMediaDesc* desc = &(*iter);
|
||||||
|
desc->session_info_.ice_ufrag_ = ufrag;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SrsSdp::set_ice_pwd(const std::string& pwd)
|
void SrsSdp::set_ice_pwd(const std::string& pwd)
|
||||||
{
|
{
|
||||||
for (std::vector<SrsMediaDesc>::iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
|
for (std::vector<SrsMediaDesc>::iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
|
||||||
iter->session_info_.ice_pwd_ = pwd;
|
SrsMediaDesc* desc = &(*iter);
|
||||||
|
desc->session_info_.ice_pwd_ = pwd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsSdp::set_dtls_role(const std::string& dtls_role)
|
||||||
|
{
|
||||||
|
for (std::vector<SrsMediaDesc>::iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
|
||||||
|
SrsMediaDesc* desc = &(*iter);
|
||||||
|
desc->session_info_.setup_ = dtls_role;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SrsSdp::set_fingerprint_algo(const std::string& algo)
|
void SrsSdp::set_fingerprint_algo(const std::string& algo)
|
||||||
{
|
{
|
||||||
for (std::vector<SrsMediaDesc>::iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
|
for (std::vector<SrsMediaDesc>::iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
|
||||||
iter->session_info_.fingerprint_algo_ = algo;
|
SrsMediaDesc* desc = &(*iter);
|
||||||
|
desc->session_info_.fingerprint_algo_ = algo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SrsSdp::set_fingerprint(const std::string& fingerprint)
|
void SrsSdp::set_fingerprint(const std::string& fingerprint)
|
||||||
{
|
{
|
||||||
for (std::vector<SrsMediaDesc>::iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
|
for (std::vector<SrsMediaDesc>::iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
|
||||||
iter->session_info_.fingerprint_ = fingerprint;
|
SrsMediaDesc* desc = &(*iter);
|
||||||
|
desc->session_info_.fingerprint_ = fingerprint;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -751,7 +898,8 @@ void SrsSdp::add_candidate(const std::string& ip, const int& port, const std::st
|
||||||
candidate.type_ = type;
|
candidate.type_ = type;
|
||||||
|
|
||||||
for (std::vector<SrsMediaDesc>::iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
|
for (std::vector<SrsMediaDesc>::iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
|
||||||
iter->candidates_.push_back(candidate);
|
SrsMediaDesc* desc = &(*iter);
|
||||||
|
desc->candidates_.push_back(candidate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -759,7 +907,8 @@ std::string SrsSdp::get_ice_ufrag() const
|
||||||
{
|
{
|
||||||
// Becaues we use BUNDLE, so we can choose the first element.
|
// Becaues we use BUNDLE, so we can choose the first element.
|
||||||
for (std::vector<SrsMediaDesc>::const_iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
|
for (std::vector<SrsMediaDesc>::const_iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
|
||||||
return iter->session_info_.ice_ufrag_;
|
const SrsMediaDesc* desc = &(*iter);
|
||||||
|
return desc->session_info_.ice_ufrag_;
|
||||||
}
|
}
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
|
@ -769,7 +918,19 @@ std::string SrsSdp::get_ice_pwd() const
|
||||||
{
|
{
|
||||||
// Becaues we use BUNDLE, so we can choose the first element.
|
// Becaues we use BUNDLE, so we can choose the first element.
|
||||||
for (std::vector<SrsMediaDesc>::const_iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
|
for (std::vector<SrsMediaDesc>::const_iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
|
||||||
return iter->session_info_.ice_pwd_;
|
const SrsMediaDesc* desc = &(*iter);
|
||||||
|
return desc->session_info_.ice_pwd_;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SrsSdp::get_dtls_role() const
|
||||||
|
{
|
||||||
|
// Becaues we use BUNDLE, so we can choose the first element.
|
||||||
|
for (std::vector<SrsMediaDesc>::const_iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
|
||||||
|
const SrsMediaDesc* desc = &(*iter);
|
||||||
|
return desc->session_info_.setup_;
|
||||||
}
|
}
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
|
@ -949,9 +1110,34 @@ srs_error_t SrsSdp::parse_media_description(const std::string& content)
|
||||||
media_descs_.back().payload_types_.push_back(SrsMediaPayloadType(fmt));
|
media_descs_.back().payload_types_.push_back(SrsMediaPayloadType(fmt));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! in_media_session_) {
|
if (!in_media_session_) {
|
||||||
in_media_session_ = true;
|
in_media_session_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SrsSdp::is_unified() const
|
||||||
|
{
|
||||||
|
// TODO: FIXME: Maybe we should consider other situations.
|
||||||
|
return media_descs_.size() > 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsSdp::update_msid(string id)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
msids_.clear();
|
||||||
|
msids_.push_back(id);
|
||||||
|
|
||||||
|
for (vector<SrsMediaDesc>::iterator it = media_descs_.begin(); it != media_descs_.end(); ++it) {
|
||||||
|
SrsMediaDesc& desc = *it;
|
||||||
|
|
||||||
|
if ((err = desc.update_msid(id)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "desc %s update msid %s", desc.mid_.c_str(), id.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
|
@ -21,8 +21,8 @@
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef SRS_APP_SDP_HPP
|
#ifndef SRS_APP_RTC_SDP_HPP
|
||||||
#define SRS_APP_SDP_HPP
|
#define SRS_APP_RTC_SDP_HPP
|
||||||
|
|
||||||
#include <srs_core.hpp>
|
#include <srs_core.hpp>
|
||||||
#include <srs_kernel_utility.hpp>
|
#include <srs_kernel_utility.hpp>
|
||||||
|
@ -31,6 +31,18 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
const std::string kTWCCExt = "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01";
|
||||||
|
|
||||||
|
// TDOO: FIXME: Rename it, and add utest.
|
||||||
|
extern std::vector<std::string> split_str(const std::string& str, const std::string& delim);
|
||||||
|
|
||||||
|
struct SrsSessionConfig
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::string dtls_role;
|
||||||
|
std::string dtls_version;
|
||||||
|
};
|
||||||
|
|
||||||
class SrsSessionInfo
|
class SrsSessionInfo
|
||||||
{
|
{
|
||||||
|
@ -55,6 +67,7 @@ class SrsSSRCInfo
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SrsSSRCInfo();
|
SrsSSRCInfo();
|
||||||
|
SrsSSRCInfo(uint32_t ssrc, std::string cname, std::string stream_id, std::string track_id);
|
||||||
virtual ~SrsSSRCInfo();
|
virtual ~SrsSSRCInfo();
|
||||||
public:
|
public:
|
||||||
srs_error_t encode(std::ostringstream& os);
|
srs_error_t encode(std::ostringstream& os);
|
||||||
|
@ -69,6 +82,17 @@ public:
|
||||||
|
|
||||||
class SrsSSRCGroup
|
class SrsSSRCGroup
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
SrsSSRCGroup();
|
||||||
|
SrsSSRCGroup(const std::string& usage, const std::vector<uint32_t>& ssrcs);
|
||||||
|
virtual ~SrsSSRCGroup();
|
||||||
|
public:
|
||||||
|
srs_error_t encode(std::ostringstream& os);
|
||||||
|
public:
|
||||||
|
// e.g FIX, FEC, SIM.
|
||||||
|
std::string semantic_;
|
||||||
|
// SSRCs of this type.
|
||||||
|
std::vector<uint32_t> ssrcs_;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct H264SpecificParam
|
struct H264SpecificParam
|
||||||
|
@ -78,7 +102,7 @@ struct H264SpecificParam
|
||||||
std::string level_asymmerty_allow;
|
std::string level_asymmerty_allow;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern srs_error_t parse_h264_fmtp(const std::string& fmtp, H264SpecificParam& h264_param);
|
extern srs_error_t srs_parse_h264_fmtp(const std::string& fmtp, H264SpecificParam& h264_param);
|
||||||
|
|
||||||
class SrsMediaPayloadType
|
class SrsMediaPayloadType
|
||||||
{
|
{
|
||||||
|
@ -115,6 +139,8 @@ public:
|
||||||
srs_error_t encode(std::ostringstream& os);
|
srs_error_t encode(std::ostringstream& os);
|
||||||
SrsMediaPayloadType* find_media_with_payload_type(int payload_type);
|
SrsMediaPayloadType* find_media_with_payload_type(int payload_type);
|
||||||
std::vector<SrsMediaPayloadType> find_media_with_encoding_name(const std::string& encoding_name) const;
|
std::vector<SrsMediaPayloadType> find_media_with_encoding_name(const std::string& encoding_name) const;
|
||||||
|
const std::map<int, std::string>& get_extmaps() const { return extmaps_; }
|
||||||
|
srs_error_t update_msid(std::string id);
|
||||||
|
|
||||||
bool is_audio() const { return type_ == "audio"; }
|
bool is_audio() const { return type_ == "audio"; }
|
||||||
bool is_video() const { return type_ == "video"; }
|
bool is_video() const { return type_ == "video"; }
|
||||||
|
@ -128,6 +154,7 @@ private:
|
||||||
srs_error_t parse_attr_msid(const std::string& value);
|
srs_error_t parse_attr_msid(const std::string& value);
|
||||||
srs_error_t parse_attr_ssrc(const std::string& value);
|
srs_error_t parse_attr_ssrc(const std::string& value);
|
||||||
srs_error_t parse_attr_ssrc_group(const std::string& value);
|
srs_error_t parse_attr_ssrc_group(const std::string& value);
|
||||||
|
srs_error_t parse_attr_extmap(const std::string& value);
|
||||||
private:
|
private:
|
||||||
SrsSSRCInfo& fetch_or_create_ssrc_info(uint32_t ssrc);
|
SrsSSRCInfo& fetch_or_create_ssrc_info(uint32_t ssrc);
|
||||||
|
|
||||||
|
@ -153,6 +180,7 @@ public:
|
||||||
std::vector<SrsCandidate> candidates_;
|
std::vector<SrsCandidate> candidates_;
|
||||||
std::vector<SrsSSRCGroup> ssrc_groups_;
|
std::vector<SrsSSRCGroup> ssrc_groups_;
|
||||||
std::vector<SrsSSRCInfo> ssrc_infos_;
|
std::vector<SrsSSRCInfo> ssrc_infos_;
|
||||||
|
std::map<int, std::string> extmaps_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SrsSdp
|
class SrsSdp
|
||||||
|
@ -164,17 +192,18 @@ public:
|
||||||
srs_error_t parse(const std::string& sdp_str);
|
srs_error_t parse(const std::string& sdp_str);
|
||||||
srs_error_t encode(std::ostringstream& os);
|
srs_error_t encode(std::ostringstream& os);
|
||||||
public:
|
public:
|
||||||
public:
|
std::vector<SrsMediaDesc*> find_media_descs(const std::string& type);
|
||||||
const SrsMediaDesc* find_media_desc(const std::string& type) const;
|
|
||||||
public:
|
public:
|
||||||
void set_ice_ufrag(const std::string& ufrag);
|
void set_ice_ufrag(const std::string& ufrag);
|
||||||
void set_ice_pwd(const std::string& pwd);
|
void set_ice_pwd(const std::string& pwd);
|
||||||
|
void set_dtls_role(const std::string& dtls_role);
|
||||||
void set_fingerprint_algo(const std::string& algo);
|
void set_fingerprint_algo(const std::string& algo);
|
||||||
void set_fingerprint(const std::string& fingerprint);
|
void set_fingerprint(const std::string& fingerprint);
|
||||||
void add_candidate(const std::string& ip, const int& port, const std::string& type);
|
void add_candidate(const std::string& ip, const int& port, const std::string& type);
|
||||||
|
|
||||||
std::string get_ice_ufrag() const;
|
std::string get_ice_ufrag() const;
|
||||||
std::string get_ice_pwd() const;
|
std::string get_ice_pwd() const;
|
||||||
|
std::string get_dtls_role() const;
|
||||||
private:
|
private:
|
||||||
srs_error_t parse_line(const std::string& line);
|
srs_error_t parse_line(const std::string& line);
|
||||||
private:
|
private:
|
||||||
|
@ -207,6 +236,7 @@ public:
|
||||||
int64_t end_time_;
|
int64_t end_time_;
|
||||||
|
|
||||||
SrsSessionInfo session_info_;
|
SrsSessionInfo session_info_;
|
||||||
|
SrsSessionConfig session_config_;
|
||||||
|
|
||||||
std::vector<std::string> groups_;
|
std::vector<std::string> groups_;
|
||||||
std::string group_policy_;
|
std::string group_policy_;
|
||||||
|
@ -216,6 +246,10 @@ public:
|
||||||
|
|
||||||
// m-line, media sessions
|
// m-line, media sessions
|
||||||
std::vector<SrsMediaDesc> media_descs_;
|
std::vector<SrsMediaDesc> media_descs_;
|
||||||
|
|
||||||
|
bool is_unified() const;
|
||||||
|
// TODO: FIXME: will be fixed when use single pc.
|
||||||
|
srs_error_t update_msid(std::string id);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
678
trunk/src/app/srs_app_rtc_server.cpp
Normal file
678
trunk/src/app/srs_app_rtc_server.cpp
Normal file
|
@ -0,0 +1,678 @@
|
||||||
|
/**
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2020 John
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <srs_app_rtc_server.hpp>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#include <srs_app_config.hpp>
|
||||||
|
#include <srs_kernel_error.hpp>
|
||||||
|
#include <srs_kernel_utility.hpp>
|
||||||
|
#include <srs_kernel_log.hpp>
|
||||||
|
#include <srs_app_statistic.hpp>
|
||||||
|
#include <srs_app_utility.hpp>
|
||||||
|
#include <srs_app_pithy_print.hpp>
|
||||||
|
#include <srs_core_autofree.hpp>
|
||||||
|
#include <srs_app_rtc_conn.hpp>
|
||||||
|
#include <srs_rtc_stun_stack.hpp>
|
||||||
|
#include <srs_http_stack.hpp>
|
||||||
|
#include <srs_app_server.hpp>
|
||||||
|
#include <srs_app_http_api.hpp>
|
||||||
|
#include <srs_app_rtc_dtls.hpp>
|
||||||
|
#include <srs_service_utility.hpp>
|
||||||
|
#include <srs_protocol_utility.hpp>
|
||||||
|
#include <srs_app_rtc_source.hpp>
|
||||||
|
#include <srs_app_rtc_api.hpp>
|
||||||
|
#include <srs_protocol_utility.hpp>
|
||||||
|
|
||||||
|
SrsRtcBlackhole::SrsRtcBlackhole()
|
||||||
|
{
|
||||||
|
blackhole = false;
|
||||||
|
blackhole_addr = NULL;
|
||||||
|
blackhole_stfd = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsRtcBlackhole::~SrsRtcBlackhole()
|
||||||
|
{
|
||||||
|
srs_close_stfd(blackhole_stfd);
|
||||||
|
srs_freep(blackhole_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsRtcBlackhole::initialize()
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
blackhole = _srs_config->get_rtc_server_black_hole();
|
||||||
|
if (!blackhole) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
string blackhole_ep = _srs_config->get_rtc_server_black_hole_addr();
|
||||||
|
if (blackhole_ep.empty()) {
|
||||||
|
blackhole = false;
|
||||||
|
srs_warn("disable black hole for no endpoint");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
string host; int port;
|
||||||
|
srs_parse_hostport(blackhole_ep, host, port);
|
||||||
|
|
||||||
|
srs_freep(blackhole_addr);
|
||||||
|
blackhole_addr = new sockaddr_in();
|
||||||
|
blackhole_addr->sin_family = AF_INET;
|
||||||
|
blackhole_addr->sin_addr.s_addr = inet_addr(host.c_str());
|
||||||
|
blackhole_addr->sin_port = htons(port);
|
||||||
|
|
||||||
|
int fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
|
blackhole_stfd = srs_netfd_open_socket(fd);
|
||||||
|
srs_assert(blackhole_stfd);
|
||||||
|
|
||||||
|
srs_trace("RTC blackhole %s:%d, fd=%d", host.c_str(), port, fd);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsRtcBlackhole::sendto(void* data, int len)
|
||||||
|
{
|
||||||
|
if (!blackhole) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For blackhole, we ignore any error.
|
||||||
|
srs_sendto(blackhole_stfd, data, len, (sockaddr*)blackhole_addr, sizeof(sockaddr_in), SRS_UTIME_NO_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsRtcBlackhole* _srs_blackhole = new SrsRtcBlackhole();
|
||||||
|
|
||||||
|
// @global dtls certficate for rtc module.
|
||||||
|
SrsDtlsCertificate* _srs_rtc_dtls_certificate = new SrsDtlsCertificate();
|
||||||
|
|
||||||
|
// TODO: Should support error response.
|
||||||
|
// For STUN packet, 0x00 is binding request, 0x01 is binding success response.
|
||||||
|
bool srs_is_stun(const uint8_t* data, size_t size)
|
||||||
|
{
|
||||||
|
return size > 0 && (data[0] == 0 || data[0] == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// change_cipher_spec(20), alert(21), handshake(22), application_data(23)
|
||||||
|
// @see https://tools.ietf.org/html/rfc2246#section-6.2.1
|
||||||
|
bool srs_is_dtls(const uint8_t* data, size_t len)
|
||||||
|
{
|
||||||
|
return (len >= 13 && (data[0] > 19 && data[0] < 64));
|
||||||
|
}
|
||||||
|
|
||||||
|
// For RTP or RTCP, the V=2 which is in the high 2bits, 0xC0 (1100 0000)
|
||||||
|
bool srs_is_rtp_or_rtcp(const uint8_t* data, size_t len)
|
||||||
|
{
|
||||||
|
return (len >= 12 && (data[0] & 0xC0) == 0x80);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For RTCP, PT is [128, 223] (or without marker [0, 95]).
|
||||||
|
// Literally, RTCP starts from 64 not 0, so PT is [192, 223] (or without marker [64, 95]).
|
||||||
|
// @note For RTP, the PT is [96, 127], or [224, 255] with marker.
|
||||||
|
bool srs_is_rtcp(const uint8_t* data, size_t len)
|
||||||
|
{
|
||||||
|
return (len >= 12) && (data[0] & 0x80) && (data[1] >= 192 && data[1] <= 223);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<std::string> get_candidate_ips()
|
||||||
|
{
|
||||||
|
std::vector<std::string> candidate_ips;
|
||||||
|
|
||||||
|
string candidate = _srs_config->get_rtc_server_candidates();
|
||||||
|
if (candidate != "*" && candidate != "0.0.0.0") {
|
||||||
|
candidate_ips.push_back(candidate);
|
||||||
|
return candidate_ips;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For * or 0.0.0.0, auto discovery expose ip addresses.
|
||||||
|
std::vector<SrsIPAddress*>& ips = srs_get_local_ips();
|
||||||
|
if (ips.empty()) {
|
||||||
|
return candidate_ips;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We try to find the best match candidates, no loopback.
|
||||||
|
string family = _srs_config->get_rtc_server_ip_family();
|
||||||
|
for (int i = 0; i < (int)ips.size(); ++i) {
|
||||||
|
SrsIPAddress* ip = ips[i];
|
||||||
|
if (ip->is_loopback) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (family == "ipv4" && !ip->is_ipv4) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (family == "ipv6" && ip->is_ipv4) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
candidate_ips.push_back(ip->ip);
|
||||||
|
srs_trace("Best matched ip=%s, ifname=%s", ip->ip.c_str(), ip->ifname.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!candidate_ips.empty()) {
|
||||||
|
return candidate_ips;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then, we use the ipv4 address.
|
||||||
|
for (int i = 0; i < (int)ips.size(); ++i) {
|
||||||
|
SrsIPAddress* ip = ips[i];
|
||||||
|
if (!ip->is_ipv4) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
candidate_ips.push_back(ip->ip);
|
||||||
|
srs_trace("No best matched, use first ip=%s, ifname=%s", ip->ip.c_str(), ip->ifname.c_str());
|
||||||
|
return candidate_ips;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We use the first one.
|
||||||
|
if (candidate_ips.empty()) {
|
||||||
|
SrsIPAddress* ip = ips[0];
|
||||||
|
candidate_ips.push_back(ip->ip);
|
||||||
|
srs_warn("No best matched, use first ip=%s, ifname=%s", ip->ip.c_str(), ip->ifname.c_str());
|
||||||
|
return candidate_ips;
|
||||||
|
}
|
||||||
|
|
||||||
|
return candidate_ips;
|
||||||
|
}
|
||||||
|
|
||||||
|
ISrsRtcServerHandler::ISrsRtcServerHandler()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ISrsRtcServerHandler::~ISrsRtcServerHandler()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ISrsRtcServerHijacker::ISrsRtcServerHijacker()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ISrsRtcServerHijacker::~ISrsRtcServerHijacker()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsRtcServer::SrsRtcServer()
|
||||||
|
{
|
||||||
|
handler = NULL;
|
||||||
|
hijacker = NULL;
|
||||||
|
timer = new SrsHourGlass(this, 1 * SRS_UTIME_SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsRtcServer::~SrsRtcServer()
|
||||||
|
{
|
||||||
|
srs_freep(timer);
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
vector<SrsUdpMuxListener*>::iterator it;
|
||||||
|
for (it = listeners.begin(); it != listeners.end(); ++it) {
|
||||||
|
SrsUdpMuxListener* listener = *it;
|
||||||
|
srs_freep(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsRtcServer::initialize()
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
if ((err = timer->tick(5 * SRS_UTIME_SECONDS)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "hourglass tick");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = timer->start()) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "start timer");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = _srs_blackhole->initialize()) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "black hole");
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_trace("RTC server init ok");
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsRtcServer::set_handler(ISrsRtcServerHandler* h)
|
||||||
|
{
|
||||||
|
handler = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsRtcServer::set_hijacker(ISrsRtcServerHijacker* h)
|
||||||
|
{
|
||||||
|
hijacker = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsRtcServer::listen_udp()
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
if (!_srs_config->get_rtc_server_enabled()) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
int port = _srs_config->get_rtc_server_listen();
|
||||||
|
if (port <= 0) {
|
||||||
|
return srs_error_new(ERROR_RTC_PORT, "invalid port=%d", port);
|
||||||
|
}
|
||||||
|
|
||||||
|
string ip = srs_any_address_for_listener();
|
||||||
|
srs_assert(listeners.empty());
|
||||||
|
|
||||||
|
int nn_listeners = _srs_config->get_rtc_server_reuseport();
|
||||||
|
for (int i = 0; i < nn_listeners; i++) {
|
||||||
|
SrsUdpMuxListener* listener = new SrsUdpMuxListener(this, ip, port);
|
||||||
|
|
||||||
|
if ((err = listener->listen()) != srs_success) {
|
||||||
|
srs_freep(listener);
|
||||||
|
return srs_error_wrap(err, "listen %s:%d", ip.c_str(), port);
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_trace("rtc listen at udp://%s:%d, fd=%d", ip.c_str(), port, listener->fd());
|
||||||
|
listeners.push_back(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsRtcServer::on_udp_packet(SrsUdpMuxSocket* skt)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
string peer_id = skt->peer_id();
|
||||||
|
char* data = skt->data(); int size = skt->size();
|
||||||
|
|
||||||
|
SrsRtcConnection* session = NULL;
|
||||||
|
if (true) {
|
||||||
|
ISrsResource* conn = _srs_rtc_manager->find_by_id(peer_id);
|
||||||
|
if (conn) {
|
||||||
|
// Switch to the session to write logs to the context.
|
||||||
|
session = dynamic_cast<SrsRtcConnection*>(conn);
|
||||||
|
session->switch_to_context();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// When got any packet, the session is alive now.
|
||||||
|
if (session) {
|
||||||
|
session->alive();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify hijack to handle the UDP packet.
|
||||||
|
if (hijacker) {
|
||||||
|
bool consumed = false;
|
||||||
|
if ((err = hijacker->on_udp_packet(skt, session, &consumed)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "hijack consumed=%u", consumed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (consumed) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For STUN, the peer address may change.
|
||||||
|
if (srs_is_stun((uint8_t*)data, size)) {
|
||||||
|
SrsStunPacket ping;
|
||||||
|
if ((err = ping.decode(data, size)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "decode stun packet failed");
|
||||||
|
}
|
||||||
|
srs_info("recv stun packet from %s, use-candidate=%d, ice-controlled=%d, ice-controlling=%d",
|
||||||
|
peer_id.c_str(), ping.get_use_candidate(), ping.get_ice_controlled(), ping.get_ice_controlling());
|
||||||
|
|
||||||
|
if (!session) {
|
||||||
|
session = find_session_by_username(ping.get_username());
|
||||||
|
|
||||||
|
// Switch to the session to write logs to the context.
|
||||||
|
if (session) {
|
||||||
|
session->switch_to_context();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: FIXME: For ICE trickle, we may get STUN packets before SDP answer, so maybe should response it.
|
||||||
|
if (!session) {
|
||||||
|
return srs_error_new(ERROR_RTC_STUN, "no session, stun username=%s, peer_id=%s",
|
||||||
|
ping.get_username().c_str(), peer_id.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return session->on_stun(skt, &ping);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For DTLS, RTCP or RTP, which does not support peer address changing.
|
||||||
|
if (!session) {
|
||||||
|
return srs_error_new(ERROR_RTC_STUN, "no session, peer_id=%s", peer_id.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (srs_is_dtls((uint8_t*)data, size)) {
|
||||||
|
return session->on_dtls(data, size);
|
||||||
|
} else if (srs_is_rtp_or_rtcp((uint8_t*)data, size)) {
|
||||||
|
if (srs_is_rtcp((uint8_t*)data, size)) {
|
||||||
|
return session->on_rtcp(data, size);
|
||||||
|
}
|
||||||
|
return session->on_rtp(data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return srs_error_new(ERROR_RTC_UDP, "unknown packet");
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsRtcServer::listen_api()
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
// TODO: FIXME: Fetch api from hybrid manager, not from SRS.
|
||||||
|
SrsHttpServeMux* http_api_mux = _srs_hybrid->srs()->instance()->api_server();
|
||||||
|
|
||||||
|
if ((err = http_api_mux->handle("/rtc/v1/play/", new SrsGoApiRtcPlay(this))) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "handle play");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = http_api_mux->handle("/rtc/v1/publish/", new SrsGoApiRtcPublish(this))) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "handle publish");
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SRS_SIMULATOR
|
||||||
|
if ((err = http_api_mux->handle("/rtc/v1/nack/", new SrsGoApiRtcNACK(this))) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "handle nack");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsRtcServer::create_session(
|
||||||
|
SrsRequest* req, const SrsSdp& remote_sdp, SrsSdp& local_sdp, const std::string& mock_eip,
|
||||||
|
bool publish, bool dtls, bool srtp,
|
||||||
|
SrsRtcConnection** psession
|
||||||
|
) {
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
SrsContextId cid = _srs_context->get_id();
|
||||||
|
|
||||||
|
SrsRtcStream* source = NULL;
|
||||||
|
if ((err = _srs_rtc_sources->fetch_or_create(req, &source)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "create source");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (publish && !source->can_publish()) {
|
||||||
|
return srs_error_new(ERROR_RTC_SOURCE_BUSY, "stream %s busy", req->get_stream_url().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: FIXME: add do_create_session to error process.
|
||||||
|
SrsRtcConnection* session = new SrsRtcConnection(this, cid);
|
||||||
|
if ((err = do_create_session(session, req, remote_sdp, local_sdp, mock_eip, publish, dtls, srtp)) != srs_success) {
|
||||||
|
srs_freep(session);
|
||||||
|
return srs_error_wrap(err, "create session");
|
||||||
|
}
|
||||||
|
|
||||||
|
*psession = session;
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsRtcServer::do_create_session(
|
||||||
|
SrsRtcConnection* session, SrsRequest* req, const SrsSdp& remote_sdp, SrsSdp& local_sdp, const std::string& mock_eip,
|
||||||
|
bool publish, bool dtls, bool srtp
|
||||||
|
)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
// first add publisher/player for negotiate sdp media info
|
||||||
|
if (publish) {
|
||||||
|
if ((err = session->add_publisher(req, remote_sdp, local_sdp)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "add publisher");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((err = session->add_player(req, remote_sdp, local_sdp)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "add player");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// All tracks default as inactive, so we must enable them.
|
||||||
|
session->set_all_tracks_status(req->get_stream_url(), publish, true);
|
||||||
|
|
||||||
|
std::string local_pwd = srs_random_str(32);
|
||||||
|
std::string local_ufrag = "";
|
||||||
|
// TODO: FIXME: Rename for a better name, it's not an username.
|
||||||
|
std::string username = "";
|
||||||
|
while (true) {
|
||||||
|
local_ufrag = srs_random_str(8);
|
||||||
|
|
||||||
|
username = local_ufrag + ":" + remote_sdp.get_ice_ufrag();
|
||||||
|
if (!_srs_rtc_manager->find_by_name(username)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
local_sdp.set_ice_ufrag(local_ufrag);
|
||||||
|
local_sdp.set_ice_pwd(local_pwd);
|
||||||
|
local_sdp.set_fingerprint_algo("sha-256");
|
||||||
|
local_sdp.set_fingerprint(_srs_rtc_dtls_certificate->get_fingerprint());
|
||||||
|
|
||||||
|
// We allows to mock the eip of server.
|
||||||
|
if (!mock_eip.empty()) {
|
||||||
|
local_sdp.add_candidate(mock_eip, _srs_config->get_rtc_server_listen(), "host");
|
||||||
|
srs_trace("RTC: Use candidate mock_eip %s", mock_eip.c_str());
|
||||||
|
} else {
|
||||||
|
std::vector<string> candidate_ips = get_candidate_ips();
|
||||||
|
for (int i = 0; i < (int)candidate_ips.size(); ++i) {
|
||||||
|
local_sdp.add_candidate(candidate_ips[i], _srs_config->get_rtc_server_listen(), "host");
|
||||||
|
}
|
||||||
|
srs_trace("RTC: Use candidates %s", srs_join_vector_string(candidate_ips, ", ").c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remote_sdp.get_dtls_role() == "active") {
|
||||||
|
local_sdp.set_dtls_role("passive");
|
||||||
|
} else if (remote_sdp.get_dtls_role() == "passive") {
|
||||||
|
local_sdp.set_dtls_role("active");
|
||||||
|
} else if (remote_sdp.get_dtls_role() == "actpass") {
|
||||||
|
local_sdp.set_dtls_role(local_sdp.session_config_.dtls_role);
|
||||||
|
} else {
|
||||||
|
// @see: https://tools.ietf.org/html/rfc4145#section-4.1
|
||||||
|
// The default value of the setup attribute in an offer/answer exchange
|
||||||
|
// is 'active' in the offer and 'passive' in the answer.
|
||||||
|
local_sdp.set_dtls_role("passive");
|
||||||
|
}
|
||||||
|
|
||||||
|
session->set_remote_sdp(remote_sdp);
|
||||||
|
// We must setup the local SDP, then initialize the session object.
|
||||||
|
session->set_local_sdp(local_sdp);
|
||||||
|
session->set_state(WAITING_STUN);
|
||||||
|
|
||||||
|
// Before session initialize, we must setup the local SDP.
|
||||||
|
if ((err = session->initialize(req, dtls, srtp, username)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "init");
|
||||||
|
}
|
||||||
|
|
||||||
|
// We allows username is optional, but it never empty here.
|
||||||
|
_srs_rtc_manager->add_with_name(username, session);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsRtcServer::create_session2(SrsRequest* req, SrsSdp& local_sdp, const std::string& mock_eip, bool unified_plan, SrsRtcConnection** psession)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
SrsContextId cid = _srs_context->get_id();
|
||||||
|
|
||||||
|
std::string local_pwd = srs_random_str(32);
|
||||||
|
// TODO: FIXME: Collision detect.
|
||||||
|
std::string local_ufrag = srs_random_str(8);
|
||||||
|
|
||||||
|
SrsRtcConnection* session = new SrsRtcConnection(this, cid);
|
||||||
|
// first add player for negotiate local sdp media info
|
||||||
|
if ((err = session->add_player2(req, unified_plan, local_sdp)) != srs_success) {
|
||||||
|
srs_freep(session);
|
||||||
|
return srs_error_wrap(err, "add player2");
|
||||||
|
}
|
||||||
|
*psession = session;
|
||||||
|
|
||||||
|
local_sdp.set_dtls_role("actpass");
|
||||||
|
local_sdp.set_ice_ufrag(local_ufrag);
|
||||||
|
local_sdp.set_ice_pwd(local_pwd);
|
||||||
|
local_sdp.set_fingerprint_algo("sha-256");
|
||||||
|
local_sdp.set_fingerprint(_srs_rtc_dtls_certificate->get_fingerprint());
|
||||||
|
|
||||||
|
// We allows to mock the eip of server.
|
||||||
|
if (!mock_eip.empty()) {
|
||||||
|
local_sdp.add_candidate(mock_eip, _srs_config->get_rtc_server_listen(), "host");
|
||||||
|
srs_trace("RTC: Use candidate mock_eip %s", mock_eip.c_str());
|
||||||
|
} else {
|
||||||
|
std::vector<string> candidate_ips = get_candidate_ips();
|
||||||
|
for (int i = 0; i < (int)candidate_ips.size(); ++i) {
|
||||||
|
local_sdp.add_candidate(candidate_ips[i], _srs_config->get_rtc_server_listen(), "host");
|
||||||
|
}
|
||||||
|
srs_trace("RTC: Use candidates %s", srs_join_vector_string(candidate_ips, ", ").c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
session->set_local_sdp(local_sdp);
|
||||||
|
session->set_state(WAITING_ANSWER);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsRtcServer::setup_session2(SrsRtcConnection* session, SrsRequest* req, const SrsSdp& remote_sdp)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
if (session->state() != WAITING_ANSWER) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: FIXME: Collision detect.
|
||||||
|
string username = session->get_local_sdp()->get_ice_ufrag() + ":" + remote_sdp.get_ice_ufrag();
|
||||||
|
|
||||||
|
if ((err = session->initialize(req, true, true, username)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "init");
|
||||||
|
}
|
||||||
|
|
||||||
|
// We allows username is optional, but it never empty here.
|
||||||
|
_srs_rtc_manager->add_with_name(username, session);
|
||||||
|
|
||||||
|
session->set_remote_sdp(remote_sdp);
|
||||||
|
session->set_state(WAITING_STUN);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsRtcServer::insert_into_id_sessions(const string& peer_id, SrsRtcConnection* session)
|
||||||
|
{
|
||||||
|
_srs_rtc_manager->add_with_id(peer_id, session);
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsRtcConnection* SrsRtcServer::find_session_by_username(const std::string& username)
|
||||||
|
{
|
||||||
|
ISrsResource* conn = _srs_rtc_manager->find_by_name(username);
|
||||||
|
return dynamic_cast<SrsRtcConnection*>(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsRtcServer::notify(int type, srs_utime_t interval, srs_utime_t tick)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
// Alive RTC sessions, for stat.
|
||||||
|
int nn_rtc_conns = 0;
|
||||||
|
|
||||||
|
// Check all sessions and dispose the dead sessions.
|
||||||
|
for (int i = 0; i < (int)_srs_rtc_manager->size(); i++) {
|
||||||
|
SrsRtcConnection* session = dynamic_cast<SrsRtcConnection*>(_srs_rtc_manager->at(i));
|
||||||
|
if (!session || !session->is_alive() || session->disposing_) {
|
||||||
|
nn_rtc_conns++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsContextRestore(_srs_context->get_id());
|
||||||
|
session->switch_to_context();
|
||||||
|
|
||||||
|
string username = session->username();
|
||||||
|
srs_trace("RTC: session destroy by timeout, username=%s, summary: %s", username.c_str(),
|
||||||
|
session->stat_->summary().c_str());
|
||||||
|
|
||||||
|
// Use manager to free session and notify other objects.
|
||||||
|
_srs_rtc_manager->remove(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore stats if no RTC connections.
|
||||||
|
if (!nn_rtc_conns) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show statistics for RTC server.
|
||||||
|
SrsProcSelfStat* u = srs_get_self_proc_stat();
|
||||||
|
// Resident Set Size: number of pages the process has in real memory.
|
||||||
|
int memory = (int)(u->rss * 4 / 1024);
|
||||||
|
// TODO: FIXME: Show more data for RTC server.
|
||||||
|
srs_trace("RTC: Server conns=%u, cpu=%.2f%%, rss=%dMB", nn_rtc_conns, u->percent * 100, memory);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
RtcServerAdapter::RtcServerAdapter()
|
||||||
|
{
|
||||||
|
rtc = new SrsRtcServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
RtcServerAdapter::~RtcServerAdapter()
|
||||||
|
{
|
||||||
|
srs_freep(rtc);
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t RtcServerAdapter::initialize()
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
if ((err = _srs_rtc_dtls_certificate->initialize()) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "rtc dtls certificate initialize");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = rtc->initialize()) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "rtc server initialize");
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t RtcServerAdapter::run()
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
if ((err = rtc->listen_udp()) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "listen udp");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = rtc->listen_api()) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "listen api");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = _srs_rtc_manager->start()) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "start manager");
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RtcServerAdapter::stop()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsResourceManager* _srs_rtc_manager = new SrsResourceManager("RTC", true);
|
||||||
|
|
152
trunk/src/app/srs_app_rtc_server.hpp
Normal file
152
trunk/src/app/srs_app_rtc_server.hpp
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
/**
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2020 John
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SRS_APP_RTC_SERVER_HPP
|
||||||
|
#define SRS_APP_RTC_SERVER_HPP
|
||||||
|
|
||||||
|
#include <srs_core.hpp>
|
||||||
|
|
||||||
|
#include <srs_app_listener.hpp>
|
||||||
|
#include <srs_app_st.hpp>
|
||||||
|
#include <srs_app_reload.hpp>
|
||||||
|
#include <srs_app_hourglass.hpp>
|
||||||
|
#include <srs_app_hybrid.hpp>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class SrsRtcServer;
|
||||||
|
class SrsHourGlass;
|
||||||
|
class SrsRtcConnection;
|
||||||
|
class SrsRequest;
|
||||||
|
class SrsSdp;
|
||||||
|
class SrsRtcStream;
|
||||||
|
class SrsResourceManager;
|
||||||
|
|
||||||
|
// The UDP black hole, for developer to use wireshark to catch plaintext packets.
|
||||||
|
// For example, server receive UDP packets at udp://8000, and forward the plaintext packet to black hole,
|
||||||
|
// we can use wireshark to capture the plaintext.
|
||||||
|
class SrsRtcBlackhole
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool blackhole;
|
||||||
|
private:
|
||||||
|
sockaddr_in* blackhole_addr;
|
||||||
|
srs_netfd_t blackhole_stfd;
|
||||||
|
public:
|
||||||
|
SrsRtcBlackhole();
|
||||||
|
virtual ~SrsRtcBlackhole();
|
||||||
|
public:
|
||||||
|
srs_error_t initialize();
|
||||||
|
void sendto(void* data, int len);
|
||||||
|
};
|
||||||
|
|
||||||
|
extern SrsRtcBlackhole* _srs_blackhole;
|
||||||
|
|
||||||
|
// The handler for RTC server to call.
|
||||||
|
class ISrsRtcServerHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ISrsRtcServerHandler();
|
||||||
|
virtual ~ISrsRtcServerHandler();
|
||||||
|
public:
|
||||||
|
// When server detect the timeout for session object.
|
||||||
|
virtual void on_timeout(SrsRtcConnection* session) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The hijacker to hook server.
|
||||||
|
class ISrsRtcServerHijacker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ISrsRtcServerHijacker();
|
||||||
|
virtual ~ISrsRtcServerHijacker();
|
||||||
|
public:
|
||||||
|
// If consumed set to true, server will ignore the packet.
|
||||||
|
virtual srs_error_t on_udp_packet(SrsUdpMuxSocket* skt, SrsRtcConnection* session, bool* pconsumed) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The RTC server instance, listen UDP port, handle UDP packet, manage RTC connections.
|
||||||
|
class SrsRtcServer : virtual public ISrsUdpMuxHandler, virtual public ISrsHourGlass
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
SrsHourGlass* timer;
|
||||||
|
std::vector<SrsUdpMuxListener*> listeners;
|
||||||
|
ISrsRtcServerHandler* handler;
|
||||||
|
ISrsRtcServerHijacker* hijacker;
|
||||||
|
public:
|
||||||
|
SrsRtcServer();
|
||||||
|
virtual ~SrsRtcServer();
|
||||||
|
public:
|
||||||
|
virtual srs_error_t initialize();
|
||||||
|
// Set the handler for server events.
|
||||||
|
void set_handler(ISrsRtcServerHandler* h);
|
||||||
|
void set_hijacker(ISrsRtcServerHijacker* h);
|
||||||
|
public:
|
||||||
|
// TODO: FIXME: Support gracefully quit.
|
||||||
|
// TODO: FIXME: Support reload.
|
||||||
|
srs_error_t listen_udp();
|
||||||
|
virtual srs_error_t on_udp_packet(SrsUdpMuxSocket* skt);
|
||||||
|
srs_error_t listen_api();
|
||||||
|
public:
|
||||||
|
// Peer start offering, we answer it.
|
||||||
|
srs_error_t create_session(
|
||||||
|
SrsRequest* req, const SrsSdp& remote_sdp, SrsSdp& local_sdp, const std::string& mock_eip,
|
||||||
|
bool publish, bool dtls, bool srtp,
|
||||||
|
SrsRtcConnection** psession
|
||||||
|
);
|
||||||
|
private:
|
||||||
|
srs_error_t do_create_session(
|
||||||
|
SrsRtcConnection* session, SrsRequest* req, const SrsSdp& remote_sdp, SrsSdp& local_sdp,
|
||||||
|
const std::string& mock_eip, bool publish, bool dtls, bool srtp
|
||||||
|
);
|
||||||
|
public:
|
||||||
|
// We start offering, create_session2 to generate offer, setup_session2 to handle answer.
|
||||||
|
srs_error_t create_session2(SrsRequest* req, SrsSdp& local_sdp, const std::string& mock_eip, bool unified_plan, SrsRtcConnection** psession);
|
||||||
|
srs_error_t setup_session2(SrsRtcConnection* session, SrsRequest* req, const SrsSdp& remote_sdp);
|
||||||
|
public:
|
||||||
|
void insert_into_id_sessions(const std::string& peer_id, SrsRtcConnection* session);
|
||||||
|
public:
|
||||||
|
SrsRtcConnection* find_session_by_username(const std::string& ufrag);
|
||||||
|
// interface ISrsHourGlass
|
||||||
|
public:
|
||||||
|
virtual srs_error_t notify(int type, srs_utime_t interval, srs_utime_t tick);
|
||||||
|
};
|
||||||
|
|
||||||
|
// The RTC server adapter.
|
||||||
|
class RtcServerAdapter : public ISrsHybridServer
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
SrsRtcServer* rtc;
|
||||||
|
public:
|
||||||
|
RtcServerAdapter();
|
||||||
|
virtual ~RtcServerAdapter();
|
||||||
|
public:
|
||||||
|
virtual srs_error_t initialize();
|
||||||
|
virtual srs_error_t run();
|
||||||
|
virtual void stop();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Manager for RTC connections.
|
||||||
|
extern SrsResourceManager* _srs_rtc_manager;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
2158
trunk/src/app/srs_app_rtc_source.cpp
Normal file
2158
trunk/src/app/srs_app_rtc_source.cpp
Normal file
File diff suppressed because it is too large
Load diff
618
trunk/src/app/srs_app_rtc_source.hpp
Normal file
618
trunk/src/app/srs_app_rtc_source.hpp
Normal file
|
@ -0,0 +1,618 @@
|
||||||
|
/**
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2020 John
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SRS_APP_RTC_SOURCE_HPP
|
||||||
|
#define SRS_APP_RTC_SOURCE_HPP
|
||||||
|
|
||||||
|
#include <srs_core.hpp>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include <srs_app_rtc_sdp.hpp>
|
||||||
|
#include <srs_service_st.hpp>
|
||||||
|
#include <srs_app_source.hpp>
|
||||||
|
|
||||||
|
class SrsRequest;
|
||||||
|
class SrsMetaCache;
|
||||||
|
class SrsSharedPtrMessage;
|
||||||
|
class SrsCommonMessage;
|
||||||
|
class SrsMessageArray;
|
||||||
|
class SrsRtcStream;
|
||||||
|
class SrsRtcFromRtmpBridger;
|
||||||
|
class SrsAudioRecode;
|
||||||
|
class SrsRtpPacket2;
|
||||||
|
class SrsSample;
|
||||||
|
class SrsRtcStreamDescription;
|
||||||
|
class SrsRtcTrackDescription;
|
||||||
|
class SrsRtcConnection;
|
||||||
|
class SrsRtpRingBuffer;
|
||||||
|
class SrsRtpNackForReceiver;
|
||||||
|
class SrsJsonObject;
|
||||||
|
class SrsRtcPlayStreamStatistic;
|
||||||
|
class SrsErrorPithyPrint;
|
||||||
|
class SrsRtcDummyBridger;
|
||||||
|
|
||||||
|
class SrsNtp
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
uint64_t system_ms_;
|
||||||
|
uint64_t ntp_;
|
||||||
|
uint32_t ntp_second_;
|
||||||
|
uint32_t ntp_fractions_;
|
||||||
|
public:
|
||||||
|
SrsNtp();
|
||||||
|
virtual ~SrsNtp();
|
||||||
|
public:
|
||||||
|
static SrsNtp from_time_ms(uint64_t ms);
|
||||||
|
static SrsNtp to_time_ms(uint64_t ntp);
|
||||||
|
public:
|
||||||
|
static uint64_t kMagicNtpFractionalUnit;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SrsRtcConsumer
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
SrsRtcStream* source;
|
||||||
|
std::vector<SrsRtpPacket2*> queue;
|
||||||
|
// when source id changed, notice all consumers
|
||||||
|
bool should_update_source_id;
|
||||||
|
// The cond wait for mw.
|
||||||
|
// @see https://github.com/ossrs/srs/issues/251
|
||||||
|
srs_cond_t mw_wait;
|
||||||
|
bool mw_waiting;
|
||||||
|
int mw_min_msgs;
|
||||||
|
public:
|
||||||
|
SrsRtcConsumer(SrsRtcStream* s);
|
||||||
|
virtual ~SrsRtcConsumer();
|
||||||
|
public:
|
||||||
|
// When source id changed, notice client to print.
|
||||||
|
virtual void update_source_id();
|
||||||
|
// Put RTP packet into queue.
|
||||||
|
// @note We do not drop packet here, but drop it in sender.
|
||||||
|
srs_error_t enqueue(SrsRtpPacket2* pkt);
|
||||||
|
// Get all RTP packets from queue.
|
||||||
|
virtual srs_error_t dump_packets(std::vector<SrsRtpPacket2*>& pkts);
|
||||||
|
// Wait for at-least some messages incoming in queue.
|
||||||
|
virtual void wait(int nb_msgs);
|
||||||
|
};
|
||||||
|
|
||||||
|
class SrsRtcStreamManager
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
srs_mutex_t lock;
|
||||||
|
std::map<std::string, SrsRtcStream*> pool;
|
||||||
|
public:
|
||||||
|
SrsRtcStreamManager();
|
||||||
|
virtual ~SrsRtcStreamManager();
|
||||||
|
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, SrsRtcStream** pps);
|
||||||
|
private:
|
||||||
|
// Get the exists source, NULL when not exists.
|
||||||
|
// update the request and return the exists source.
|
||||||
|
virtual SrsRtcStream* fetch(SrsRequest* r);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Global singleton instance.
|
||||||
|
extern SrsRtcStreamManager* _srs_rtc_sources;
|
||||||
|
|
||||||
|
// A publish stream interface, for source to callback with.
|
||||||
|
class ISrsRtcPublishStream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ISrsRtcPublishStream();
|
||||||
|
virtual ~ISrsRtcPublishStream();
|
||||||
|
public:
|
||||||
|
// Request keyframe(PLI) from publisher, for fresh consumer.
|
||||||
|
virtual void request_keyframe(uint32_t ssrc) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ISrsRtcStreamEventHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ISrsRtcStreamEventHandler();
|
||||||
|
virtual ~ISrsRtcStreamEventHandler();
|
||||||
|
public:
|
||||||
|
// stream unpublish, sync API.
|
||||||
|
virtual void on_unpublish() = 0;
|
||||||
|
// no player subscribe this stream, sync API
|
||||||
|
virtual void on_consumers_finished() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A Source is a stream, to publish and to play with, binding to SrsRtcPublishStream and SrsRtcPlayStream.
|
||||||
|
class SrsRtcStream
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// For publish, it's the publish client id.
|
||||||
|
// For edge, it's the edge ingest id.
|
||||||
|
// when source id changed, for example, the edge reconnect,
|
||||||
|
// invoke the on_source_id_changed() to let all clients know.
|
||||||
|
SrsContextId _source_id;
|
||||||
|
// previous source id.
|
||||||
|
SrsContextId _pre_source_id;
|
||||||
|
SrsRequest* req;
|
||||||
|
ISrsRtcPublishStream* publish_stream_;
|
||||||
|
// Transmux RTMP to RTC.
|
||||||
|
SrsRtcDummyBridger* bridger_;
|
||||||
|
// Steam description for this steam.
|
||||||
|
SrsRtcStreamDescription* stream_desc_;
|
||||||
|
private:
|
||||||
|
// To delivery stream to clients.
|
||||||
|
std::vector<SrsRtcConsumer*> consumers;
|
||||||
|
// Whether stream is created, that is, SDP is done.
|
||||||
|
bool is_created_;
|
||||||
|
// Whether stream is delivering data, that is, DTLS is done.
|
||||||
|
bool is_delivering_packets_;
|
||||||
|
// Notify stream event to event handler
|
||||||
|
std::vector<ISrsRtcStreamEventHandler*> event_handlers_;
|
||||||
|
public:
|
||||||
|
SrsRtcStream();
|
||||||
|
virtual ~SrsRtcStream();
|
||||||
|
public:
|
||||||
|
virtual srs_error_t initialize(SrsRequest* r);
|
||||||
|
// Update the authentication information in request.
|
||||||
|
virtual void update_auth(SrsRequest* r);
|
||||||
|
// 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();
|
||||||
|
// Get the bridger.
|
||||||
|
ISrsSourceBridger* bridger();
|
||||||
|
public:
|
||||||
|
// Create consumer
|
||||||
|
// @param consumer, output the create consumer.
|
||||||
|
virtual srs_error_t create_consumer(SrsRtcConsumer*& consumer);
|
||||||
|
// Dumps packets in cache to consumer.
|
||||||
|
// @param ds, whether dumps the sequence header.
|
||||||
|
// @param dm, whether dumps the metadata.
|
||||||
|
// @param dg, whether dumps the gop cache.
|
||||||
|
virtual srs_error_t consumer_dumps(SrsRtcConsumer* consumer, bool ds = true, bool dm = true, bool dg = true);
|
||||||
|
virtual void on_consumer_destroy(SrsRtcConsumer* consumer);
|
||||||
|
// Whether we can publish stream to the source, return false if it exists.
|
||||||
|
// @remark Note that when SDP is done, we set the stream is not able to publish.
|
||||||
|
virtual bool can_publish();
|
||||||
|
// For RTC, the stream is created when SDP is done, and then do DTLS
|
||||||
|
virtual void set_stream_created();
|
||||||
|
// When start publish stream.
|
||||||
|
virtual srs_error_t on_publish();
|
||||||
|
// When stop publish stream.
|
||||||
|
virtual void on_unpublish();
|
||||||
|
public:
|
||||||
|
// For event handler
|
||||||
|
void subscribe(ISrsRtcStreamEventHandler* h);
|
||||||
|
void unsubscribe(ISrsRtcStreamEventHandler* h);
|
||||||
|
public:
|
||||||
|
// Get and set the publisher, passed to consumer to process requests such as PLI.
|
||||||
|
ISrsRtcPublishStream* publish_stream();
|
||||||
|
void set_publish_stream(ISrsRtcPublishStream* v);
|
||||||
|
// Consume the shared RTP packet, user must free it.
|
||||||
|
srs_error_t on_rtp(SrsRtpPacket2* pkt);
|
||||||
|
// Set and get stream description for souce
|
||||||
|
bool has_stream_desc();
|
||||||
|
void set_stream_desc(SrsRtcStreamDescription* stream_desc);
|
||||||
|
std::vector<SrsRtcTrackDescription*> get_track_desc(std::string type, std::string media_type);
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef SRS_FFMPEG_FIT
|
||||||
|
class SrsRtcFromRtmpBridger : public ISrsSourceBridger
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
SrsRequest* req;
|
||||||
|
SrsRtcStream* source_;
|
||||||
|
// The format, codec information.
|
||||||
|
SrsRtmpFormat* format;
|
||||||
|
// The metadata cache.
|
||||||
|
SrsMetaCache* meta;
|
||||||
|
private:
|
||||||
|
bool discard_aac;
|
||||||
|
SrsAudioRecode* codec;
|
||||||
|
bool discard_bframe;
|
||||||
|
bool merge_nalus;
|
||||||
|
uint32_t audio_timestamp;
|
||||||
|
uint16_t audio_sequence;
|
||||||
|
uint16_t video_sequence;
|
||||||
|
uint32_t audio_ssrc;
|
||||||
|
uint32_t video_ssrc;
|
||||||
|
public:
|
||||||
|
SrsRtcFromRtmpBridger(SrsRtcStream* source);
|
||||||
|
virtual ~SrsRtcFromRtmpBridger();
|
||||||
|
public:
|
||||||
|
virtual srs_error_t initialize(SrsRequest* r);
|
||||||
|
virtual srs_error_t on_publish();
|
||||||
|
virtual void on_unpublish();
|
||||||
|
virtual srs_error_t on_audio(SrsSharedPtrMessage* msg);
|
||||||
|
private:
|
||||||
|
srs_error_t transcode(char* adts_audio, int nn_adts_audio);
|
||||||
|
srs_error_t package_opus(char* data, int size, SrsRtpPacket2** ppkt);
|
||||||
|
public:
|
||||||
|
virtual srs_error_t on_video(SrsSharedPtrMessage* msg);
|
||||||
|
private:
|
||||||
|
srs_error_t filter(SrsSharedPtrMessage* msg, SrsFormat* format, bool& has_idr, std::vector<SrsSample*>& samples);
|
||||||
|
srs_error_t package_stap_a(SrsRtcStream* source, SrsSharedPtrMessage* msg, SrsRtpPacket2** ppkt);
|
||||||
|
srs_error_t package_nalus(SrsSharedPtrMessage* msg, const std::vector<SrsSample*>& samples, std::vector<SrsRtpPacket2*>& pkts);
|
||||||
|
srs_error_t package_single_nalu(SrsSharedPtrMessage* msg, SrsSample* sample, std::vector<SrsRtpPacket2*>& pkts);
|
||||||
|
srs_error_t package_fu_a(SrsSharedPtrMessage* msg, SrsSample* sample, int fu_payload_size, std::vector<SrsRtpPacket2*>& pkts);
|
||||||
|
srs_error_t consume_packets(std::vector<SrsRtpPacket2*>& pkts);
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class SrsRtcDummyBridger : public ISrsSourceBridger
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
SrsRtcStream* rtc_;
|
||||||
|
// The optional implementation bridger, ignore if NULL.
|
||||||
|
ISrsSourceBridger* impl_;
|
||||||
|
public:
|
||||||
|
SrsRtcDummyBridger(SrsRtcStream* s);
|
||||||
|
virtual ~SrsRtcDummyBridger();
|
||||||
|
public:
|
||||||
|
virtual srs_error_t on_publish();
|
||||||
|
virtual srs_error_t on_audio(SrsSharedPtrMessage* audio);
|
||||||
|
virtual srs_error_t on_video(SrsSharedPtrMessage* video);
|
||||||
|
virtual void on_unpublish();
|
||||||
|
public:
|
||||||
|
// Setup a new implementation bridger, which might be NULL to free previous one.
|
||||||
|
void setup(ISrsSourceBridger* impl);
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: FIXME: Rename it.
|
||||||
|
class SrsCodecPayload
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::string type_;
|
||||||
|
uint8_t pt_;
|
||||||
|
// for publish, equals to PT of itself;
|
||||||
|
// for subscribe, is the PT of publisher;
|
||||||
|
uint8_t pt_of_publisher_;
|
||||||
|
std::string name_;
|
||||||
|
int sample_;
|
||||||
|
|
||||||
|
std::vector<std::string> rtcp_fbs_;
|
||||||
|
public:
|
||||||
|
SrsCodecPayload();
|
||||||
|
SrsCodecPayload(uint8_t pt, std::string encode_name, int sample);
|
||||||
|
virtual ~SrsCodecPayload();
|
||||||
|
public:
|
||||||
|
virtual SrsCodecPayload* copy();
|
||||||
|
virtual SrsMediaPayloadType generate_media_payload_type();
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: FIXME: Rename it.
|
||||||
|
class SrsVideoPayload : public SrsCodecPayload
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct H264SpecificParameter
|
||||||
|
{
|
||||||
|
std::string profile_level_id;
|
||||||
|
std::string packetization_mode;
|
||||||
|
std::string level_asymmerty_allow;
|
||||||
|
};
|
||||||
|
H264SpecificParameter h264_param_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
SrsVideoPayload();
|
||||||
|
SrsVideoPayload(uint8_t pt, std::string encode_name, int sample);
|
||||||
|
virtual ~SrsVideoPayload();
|
||||||
|
public:
|
||||||
|
virtual SrsVideoPayload* copy();
|
||||||
|
virtual SrsMediaPayloadType generate_media_payload_type();
|
||||||
|
public:
|
||||||
|
srs_error_t set_h264_param_desc(std::string fmtp);
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: FIXME: Rename it.
|
||||||
|
class SrsAudioPayload : public SrsCodecPayload
|
||||||
|
{
|
||||||
|
struct SrsOpusParameter
|
||||||
|
{
|
||||||
|
int minptime;
|
||||||
|
bool use_inband_fec;
|
||||||
|
bool usedtx;
|
||||||
|
|
||||||
|
SrsOpusParameter() {
|
||||||
|
minptime = 0;
|
||||||
|
use_inband_fec = false;
|
||||||
|
usedtx = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
int channel_;
|
||||||
|
SrsOpusParameter opus_param_;
|
||||||
|
public:
|
||||||
|
SrsAudioPayload();
|
||||||
|
SrsAudioPayload(uint8_t pt, std::string encode_name, int sample, int channel);
|
||||||
|
virtual ~SrsAudioPayload();
|
||||||
|
public:
|
||||||
|
virtual SrsAudioPayload* copy();
|
||||||
|
virtual SrsMediaPayloadType generate_media_payload_type();
|
||||||
|
public:
|
||||||
|
srs_error_t set_opus_param_desc(std::string fmtp);
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: FIXME: Rename it.
|
||||||
|
class SrsRedPayload : public SrsCodecPayload
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int channel_;
|
||||||
|
public:
|
||||||
|
SrsRedPayload();
|
||||||
|
SrsRedPayload(uint8_t pt, std::string encode_name, int sample, int channel);
|
||||||
|
virtual ~SrsRedPayload();
|
||||||
|
public:
|
||||||
|
virtual SrsRedPayload* copy();
|
||||||
|
virtual SrsMediaPayloadType generate_media_payload_type();
|
||||||
|
};
|
||||||
|
|
||||||
|
class SrsRtxPayloadDes : public SrsCodecPayload
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
uint8_t apt_;
|
||||||
|
public:
|
||||||
|
SrsRtxPayloadDes();
|
||||||
|
SrsRtxPayloadDes(uint8_t pt, uint8_t apt);
|
||||||
|
virtual ~SrsRtxPayloadDes();
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual SrsRtxPayloadDes* copy();
|
||||||
|
virtual SrsMediaPayloadType generate_media_payload_type();
|
||||||
|
};
|
||||||
|
|
||||||
|
class SrsRtcTrackDescription
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// type: audio, video
|
||||||
|
std::string type_;
|
||||||
|
// track_id
|
||||||
|
std::string id_;
|
||||||
|
// ssrc is the primary ssrc for this track,
|
||||||
|
// if sdp has ssrc-group, it is the first ssrc of the ssrc-group
|
||||||
|
uint32_t ssrc_;
|
||||||
|
// rtx ssrc is the second ssrc of "FEC" src-group,
|
||||||
|
// if no rtx ssrc, rtx_ssrc_ = 0.
|
||||||
|
uint32_t fec_ssrc_;
|
||||||
|
// rtx ssrc is the second ssrc of "FID" src-group,
|
||||||
|
// if no rtx ssrc, rtx_ssrc_ = 0.
|
||||||
|
uint32_t rtx_ssrc_;
|
||||||
|
// key: rtp header extension id, value: rtp header extension uri.
|
||||||
|
std::map<int, std::string> extmaps_;
|
||||||
|
// Whether this track active. default: active.
|
||||||
|
bool is_active_;
|
||||||
|
// direction
|
||||||
|
std::string direction_;
|
||||||
|
// mid is used in BOUNDLE
|
||||||
|
std::string mid_;
|
||||||
|
// msid_: track stream id
|
||||||
|
std::string msid_;
|
||||||
|
|
||||||
|
// meida payload, such as opus, h264.
|
||||||
|
SrsCodecPayload* media_;
|
||||||
|
SrsCodecPayload* red_;
|
||||||
|
SrsCodecPayload* rtx_;
|
||||||
|
SrsCodecPayload* ulpfec_;
|
||||||
|
public:
|
||||||
|
SrsRtcTrackDescription();
|
||||||
|
virtual ~SrsRtcTrackDescription();
|
||||||
|
public:
|
||||||
|
// whether or not the track has ssrc.
|
||||||
|
// for example:
|
||||||
|
// we need check track has the ssrc in the ssrc_group, then add ssrc_group to the track,
|
||||||
|
bool has_ssrc(uint32_t ssrc);
|
||||||
|
public:
|
||||||
|
void add_rtp_extension_desc(int id, std::string uri);
|
||||||
|
void set_direction(std::string direction);
|
||||||
|
void set_codec_payload(SrsCodecPayload* payload);
|
||||||
|
// auxiliary paylod include red, rtx, ulpfec.
|
||||||
|
void create_auxiliary_payload(const std::vector<SrsMediaPayloadType> payload_types);
|
||||||
|
void set_rtx_ssrc(uint32_t ssrc);
|
||||||
|
void set_fec_ssrc(uint32_t ssrc);
|
||||||
|
void set_mid(std::string mid);
|
||||||
|
int get_rtp_extension_id(std::string uri);
|
||||||
|
public:
|
||||||
|
SrsRtcTrackDescription* copy();
|
||||||
|
};
|
||||||
|
|
||||||
|
class SrsRtcStreamDescription
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// the id for this stream;
|
||||||
|
std::string id_;
|
||||||
|
|
||||||
|
SrsRtcTrackDescription* audio_track_desc_;
|
||||||
|
std::vector<SrsRtcTrackDescription*> video_track_descs_;
|
||||||
|
public:
|
||||||
|
SrsRtcStreamDescription();
|
||||||
|
virtual ~SrsRtcStreamDescription();
|
||||||
|
|
||||||
|
public:
|
||||||
|
SrsRtcStreamDescription* copy();
|
||||||
|
SrsRtcTrackDescription* find_track_description_by_ssrc(uint32_t ssrc);
|
||||||
|
};
|
||||||
|
|
||||||
|
class SrsRtcTrackStatistic
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// packets received or sent.
|
||||||
|
uint32_t packets;
|
||||||
|
// packets received or sent at last statistic time.
|
||||||
|
uint32_t last_packets;
|
||||||
|
// bytes received or sent.
|
||||||
|
uint64_t bytes;
|
||||||
|
// bytes received or sent at last statistic time.
|
||||||
|
uint32_t last_bytes;
|
||||||
|
|
||||||
|
// nacks received or sent.
|
||||||
|
uint32_t nacks;
|
||||||
|
// nacks received or sent at last statistic time.
|
||||||
|
uint32_t last_nacks;
|
||||||
|
|
||||||
|
// padding packets received or sent.
|
||||||
|
uint32_t padding_packets;
|
||||||
|
// padding packets received or sent at last statistic time.
|
||||||
|
uint32_t last_padding_packets;
|
||||||
|
// padding bytes received or sent.
|
||||||
|
uint32_t padding_bytes;
|
||||||
|
// padding bytes received or sent at last statistic time.
|
||||||
|
uint32_t last_padding_bytes;
|
||||||
|
|
||||||
|
// replay packets received or sent.
|
||||||
|
uint32_t replay_packets;
|
||||||
|
// replay packets received or sent at last statistic time.
|
||||||
|
uint32_t last_replay_packets;
|
||||||
|
// replay bytes received or sent.
|
||||||
|
uint64_t replay_bytes;
|
||||||
|
// replay bytes received or sent at last statistic time.
|
||||||
|
uint64_t last_replay_bytes;
|
||||||
|
|
||||||
|
public:
|
||||||
|
SrsRtcTrackStatistic();
|
||||||
|
};
|
||||||
|
|
||||||
|
class SrsRtcRecvTrack
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
SrsRtcTrackDescription* track_desc_;
|
||||||
|
SrsRtcTrackStatistic* statistic_;
|
||||||
|
|
||||||
|
SrsRtcConnection* session_;
|
||||||
|
SrsRtpRingBuffer* rtp_queue_;
|
||||||
|
SrsRtpNackForReceiver* nack_receiver_;
|
||||||
|
|
||||||
|
// send report ntp and received time.
|
||||||
|
SrsNtp last_sender_report_ntp;
|
||||||
|
uint64_t last_sender_report_sys_time;
|
||||||
|
public:
|
||||||
|
SrsRtcRecvTrack(SrsRtcConnection* session, SrsRtcTrackDescription* stream_descs, bool is_audio);
|
||||||
|
virtual ~SrsRtcRecvTrack();
|
||||||
|
public:
|
||||||
|
bool has_ssrc(uint32_t ssrc);
|
||||||
|
uint32_t get_ssrc();
|
||||||
|
void update_rtt(int rtt);
|
||||||
|
void update_send_report_time(const SrsNtp& ntp);
|
||||||
|
srs_error_t send_rtcp_rr();
|
||||||
|
srs_error_t send_rtcp_xr_rrtr();
|
||||||
|
bool set_track_status(bool active);
|
||||||
|
bool get_track_status();
|
||||||
|
std::string get_track_id();
|
||||||
|
protected:
|
||||||
|
srs_error_t on_nack(SrsRtpPacket2* pkt);
|
||||||
|
public:
|
||||||
|
virtual srs_error_t on_rtp(SrsRtcStream* source, SrsRtpPacket2* pkt) = 0;
|
||||||
|
virtual srs_error_t check_send_nacks() = 0;
|
||||||
|
protected:
|
||||||
|
virtual srs_error_t do_check_send_nacks(uint32_t& timeout_nacks);
|
||||||
|
};
|
||||||
|
|
||||||
|
class SrsRtcAudioRecvTrack : public SrsRtcRecvTrack
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SrsRtcAudioRecvTrack(SrsRtcConnection* session, SrsRtcTrackDescription* track_desc);
|
||||||
|
virtual ~SrsRtcAudioRecvTrack();
|
||||||
|
public:
|
||||||
|
virtual srs_error_t on_rtp(SrsRtcStream* source, SrsRtpPacket2* pkt);
|
||||||
|
virtual srs_error_t check_send_nacks();
|
||||||
|
};
|
||||||
|
|
||||||
|
class SrsRtcVideoRecvTrack : public SrsRtcRecvTrack
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SrsRtcVideoRecvTrack(SrsRtcConnection* session, SrsRtcTrackDescription* stream_descs);
|
||||||
|
virtual ~SrsRtcVideoRecvTrack();
|
||||||
|
public:
|
||||||
|
virtual srs_error_t on_rtp(SrsRtcStream* source, SrsRtpPacket2* pkt);
|
||||||
|
virtual srs_error_t check_send_nacks();
|
||||||
|
};
|
||||||
|
|
||||||
|
class SrsRtcSendTrack
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
// send track description
|
||||||
|
SrsRtcTrackDescription* track_desc_;
|
||||||
|
SrsRtcTrackStatistic* statistic_;
|
||||||
|
protected:
|
||||||
|
// The owner connection for this track.
|
||||||
|
SrsRtcConnection* session_;
|
||||||
|
// NACK ARQ ring buffer.
|
||||||
|
SrsRtpRingBuffer* rtp_queue_;
|
||||||
|
private:
|
||||||
|
// The pithy print for special stage.
|
||||||
|
SrsErrorPithyPrint* nack_epp;
|
||||||
|
public:
|
||||||
|
SrsRtcSendTrack(SrsRtcConnection* session, SrsRtcTrackDescription* track_desc, bool is_audio);
|
||||||
|
virtual ~SrsRtcSendTrack();
|
||||||
|
public:
|
||||||
|
bool has_ssrc(uint32_t ssrc);
|
||||||
|
SrsRtpPacket2* fetch_rtp_packet(uint16_t seq);
|
||||||
|
bool set_track_status(bool active);
|
||||||
|
bool get_track_status();
|
||||||
|
std::string get_track_id();
|
||||||
|
public:
|
||||||
|
virtual srs_error_t on_rtp(SrsRtpPacket2* pkt, SrsRtcPlayStreamStatistic& info) = 0;
|
||||||
|
virtual srs_error_t on_rtcp(SrsRtpPacket2* pkt) = 0;
|
||||||
|
virtual void on_recv_nack();
|
||||||
|
};
|
||||||
|
|
||||||
|
class SrsRtcAudioSendTrack : public SrsRtcSendTrack
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SrsRtcAudioSendTrack(SrsRtcConnection* session, SrsRtcTrackDescription* track_desc);
|
||||||
|
virtual ~SrsRtcAudioSendTrack();
|
||||||
|
public:
|
||||||
|
virtual srs_error_t on_rtp(SrsRtpPacket2* pkt, SrsRtcPlayStreamStatistic& info);
|
||||||
|
virtual srs_error_t on_rtcp(SrsRtpPacket2* pkt);
|
||||||
|
};
|
||||||
|
|
||||||
|
class SrsRtcVideoSendTrack : public SrsRtcSendTrack
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SrsRtcVideoSendTrack(SrsRtcConnection* session, SrsRtcTrackDescription* track_desc);
|
||||||
|
virtual ~SrsRtcVideoSendTrack();
|
||||||
|
public:
|
||||||
|
virtual srs_error_t on_rtp(SrsRtpPacket2* pkt, SrsRtcPlayStreamStatistic& info);
|
||||||
|
virtual srs_error_t on_rtcp(SrsRtpPacket2* pkt);
|
||||||
|
};
|
||||||
|
|
||||||
|
class SrsRtcSSRCGenerator
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
static SrsRtcSSRCGenerator* _instance;
|
||||||
|
private:
|
||||||
|
uint32_t ssrc_num;
|
||||||
|
private:
|
||||||
|
SrsRtcSSRCGenerator();
|
||||||
|
virtual ~SrsRtcSSRCGenerator();
|
||||||
|
public:
|
||||||
|
static SrsRtcSSRCGenerator* instance();
|
||||||
|
uint32_t generate_ssrc();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -81,13 +81,13 @@ SrsSimpleRtmpClient::~SrsSimpleRtmpClient()
|
||||||
|
|
||||||
srs_error_t SrsSimpleRtmpClient::connect_app()
|
srs_error_t SrsSimpleRtmpClient::connect_app()
|
||||||
{
|
{
|
||||||
std::vector<std::string> ips = srs_get_local_ips();
|
std::vector<SrsIPAddress*>& ips = srs_get_local_ips();
|
||||||
assert(_srs_config->get_stats_network() < (int)ips.size());
|
assert(_srs_config->get_stats_network() < (int)ips.size());
|
||||||
std::string local_ip = ips[_srs_config->get_stats_network()];
|
SrsIPAddress* local_ip = ips[_srs_config->get_stats_network()];
|
||||||
|
|
||||||
bool debug_srs_upnode = _srs_config->get_debug_srs_upnode(req->vhost);
|
bool debug_srs_upnode = _srs_config->get_debug_srs_upnode(req->vhost);
|
||||||
|
|
||||||
return do_connect_app(local_ip, debug_srs_upnode);
|
return do_connect_app(local_ip->ip, debug_srs_upnode);
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsClientInfo::SrsClientInfo()
|
SrsClientInfo::SrsClientInfo()
|
||||||
|
@ -104,10 +104,24 @@ SrsClientInfo::~SrsClientInfo()
|
||||||
srs_freep(res);
|
srs_freep(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsRtmpConn::SrsRtmpConn(SrsServer* svr, srs_netfd_t c, string cip) : SrsConnection(svr, c, cip)
|
SrsRtmpConn::SrsRtmpConn(SrsServer* svr, srs_netfd_t c, string cip, int cport)
|
||||||
{
|
{
|
||||||
|
// Create a identify for this client.
|
||||||
|
_srs_context->set_id(_srs_context->generate_id());
|
||||||
|
|
||||||
server = svr;
|
server = svr;
|
||||||
|
|
||||||
|
stfd = c;
|
||||||
|
skt = new SrsTcpConnection(c);
|
||||||
|
manager = svr;
|
||||||
|
ip = cip;
|
||||||
|
port = cport;
|
||||||
|
create_time = srsu2ms(srs_get_system_time());
|
||||||
|
clk = new SrsWallClock();
|
||||||
|
kbps = new SrsKbps(clk);
|
||||||
|
kbps->set_io(skt, skt);
|
||||||
|
trd = new SrsSTCoroutine("rtmp", this, _srs_context->get_id());
|
||||||
|
|
||||||
rtmp = new SrsRtmpServer(skt);
|
rtmp = new SrsRtmpServer(skt);
|
||||||
refer = new SrsRefer();
|
refer = new SrsRefer();
|
||||||
bandwidth = new SrsBandwidth();
|
bandwidth = new SrsBandwidth();
|
||||||
|
@ -122,6 +136,9 @@ SrsRtmpConn::SrsRtmpConn(SrsServer* svr, srs_netfd_t c, string cip) : SrsConnect
|
||||||
tcp_nodelay = false;
|
tcp_nodelay = false;
|
||||||
info = new SrsClientInfo();
|
info = new SrsClientInfo();
|
||||||
|
|
||||||
|
publish_1stpkt_timeout = 0;
|
||||||
|
publish_normal_timeout = 0;
|
||||||
|
|
||||||
_srs_config->subscribe(this);
|
_srs_config->subscribe(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,6 +146,17 @@ SrsRtmpConn::~SrsRtmpConn()
|
||||||
{
|
{
|
||||||
_srs_config->unsubscribe(this);
|
_srs_config->unsubscribe(this);
|
||||||
|
|
||||||
|
trd->interrupt();
|
||||||
|
// wakeup the handler which need to notice.
|
||||||
|
if (wakable) {
|
||||||
|
wakable->wakeup();
|
||||||
|
}
|
||||||
|
srs_freep(trd);
|
||||||
|
|
||||||
|
srs_freep(kbps);
|
||||||
|
srs_freep(clk);
|
||||||
|
srs_freep(skt);
|
||||||
|
|
||||||
srs_freep(info);
|
srs_freep(info);
|
||||||
srs_freep(rtmp);
|
srs_freep(rtmp);
|
||||||
srs_freep(refer);
|
srs_freep(refer);
|
||||||
|
@ -136,14 +164,9 @@ SrsRtmpConn::~SrsRtmpConn()
|
||||||
srs_freep(security);
|
srs_freep(security);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SrsRtmpConn::dispose()
|
std::string SrsRtmpConn::desc()
|
||||||
{
|
{
|
||||||
SrsConnection::dispose();
|
return "RtmpConn";
|
||||||
|
|
||||||
// wakeup the handler which need to notice.
|
|
||||||
if (wakable) {
|
|
||||||
wakable->wakeup();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: return detail message when error for client.
|
// TODO: return detail message when error for client.
|
||||||
|
@ -151,7 +174,7 @@ srs_error_t SrsRtmpConn::do_cycle()
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
srs_trace("RTMP client ip=%s, fd=%d", ip.c_str(), srs_netfd_fileno(stfd));
|
srs_trace("RTMP client ip=%s:%d, fd=%d", ip.c_str(), port, srs_netfd_fileno(stfd));
|
||||||
|
|
||||||
rtmp->set_recv_timeout(SRS_CONSTS_RTMP_TIMEOUT);
|
rtmp->set_recv_timeout(SRS_CONSTS_RTMP_TIMEOUT);
|
||||||
rtmp->set_send_timeout(SRS_CONSTS_RTMP_TIMEOUT);
|
rtmp->set_send_timeout(SRS_CONSTS_RTMP_TIMEOUT);
|
||||||
|
@ -267,7 +290,7 @@ srs_error_t SrsRtmpConn::on_reload_vhost_play(string vhost)
|
||||||
|
|
||||||
mw_msgs = _srs_config->get_mw_msgs(req->vhost, realtime);
|
mw_msgs = _srs_config->get_mw_msgs(req->vhost, realtime);
|
||||||
mw_sleep = _srs_config->get_mw_sleep(req->vhost);
|
mw_sleep = _srs_config->get_mw_sleep(req->vhost);
|
||||||
set_socket_buffer(mw_sleep);
|
skt->set_socket_buffer(mw_sleep);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -305,7 +328,7 @@ srs_error_t SrsRtmpConn::on_reload_vhost_realtime(string vhost)
|
||||||
|
|
||||||
mw_msgs = _srs_config->get_mw_msgs(req->vhost, realtime);
|
mw_msgs = _srs_config->get_mw_msgs(req->vhost, realtime);
|
||||||
mw_sleep = _srs_config->get_mw_sleep(req->vhost);
|
mw_sleep = _srs_config->get_mw_sleep(req->vhost);
|
||||||
set_socket_buffer(mw_sleep);
|
skt->set_socket_buffer(mw_sleep);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -515,8 +538,8 @@ srs_error_t SrsRtmpConn::stream_service_cycle()
|
||||||
}
|
}
|
||||||
|
|
||||||
bool enabled_cache = _srs_config->get_gop_cache(req->vhost);
|
bool enabled_cache = _srs_config->get_gop_cache(req->vhost);
|
||||||
srs_trace("source url=%s, ip=%s, cache=%d, is_edge=%d, source_id=[%d][%d]",
|
srs_trace("source url=%s, ip=%s, cache=%d, is_edge=%d, source_id=%s/%s",
|
||||||
req->get_stream_url().c_str(), ip.c_str(), enabled_cache, info->edge, ::getpid(), source->source_id());
|
req->get_stream_url().c_str(), ip.c_str(), enabled_cache, info->edge, source->source_id().c_str(), source->pre_source_id().c_str());
|
||||||
source->set_cache(enabled_cache);
|
source->set_cache(enabled_cache);
|
||||||
|
|
||||||
switch (info->type) {
|
switch (info->type) {
|
||||||
|
@ -654,10 +677,13 @@ srs_error_t SrsRtmpConn::playing(SrsSource* source)
|
||||||
|
|
||||||
// Create a consumer of source.
|
// Create a consumer of source.
|
||||||
SrsConsumer* consumer = NULL;
|
SrsConsumer* consumer = NULL;
|
||||||
if ((err = source->create_consumer(this, consumer)) != srs_success) {
|
SrsAutoFree(SrsConsumer, consumer);
|
||||||
|
if ((err = source->create_consumer(consumer)) != srs_success) {
|
||||||
return srs_error_wrap(err, "rtmp: create consumer");
|
return srs_error_wrap(err, "rtmp: create consumer");
|
||||||
}
|
}
|
||||||
SrsAutoFree(SrsConsumer, consumer);
|
if ((err = source->consumer_dumps(consumer)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "rtmp: dumps consumer");
|
||||||
|
}
|
||||||
|
|
||||||
// Use receiving thread to receive packets from peer.
|
// Use receiving thread to receive packets from peer.
|
||||||
// @see: https://github.com/ossrs/srs/issues/217
|
// @see: https://github.com/ossrs/srs/issues/217
|
||||||
|
@ -704,7 +730,7 @@ srs_error_t SrsRtmpConn::do_playing(SrsSource* source, SrsConsumer* consumer, Sr
|
||||||
// when mw_sleep changed, resize the socket send buffer.
|
// when mw_sleep changed, resize the socket send buffer.
|
||||||
mw_msgs = _srs_config->get_mw_msgs(req->vhost, realtime);
|
mw_msgs = _srs_config->get_mw_msgs(req->vhost, realtime);
|
||||||
mw_sleep = _srs_config->get_mw_sleep(req->vhost);
|
mw_sleep = _srs_config->get_mw_sleep(req->vhost);
|
||||||
set_socket_buffer(mw_sleep);
|
skt->set_socket_buffer(mw_sleep);
|
||||||
// initialize the send_min_interval
|
// initialize the send_min_interval
|
||||||
send_min_interval = _srs_config->get_send_min_interval(req->vhost);
|
send_min_interval = _srs_config->get_send_min_interval(req->vhost);
|
||||||
|
|
||||||
|
@ -1125,7 +1151,7 @@ void SrsRtmpConn::set_sock_options()
|
||||||
if (nvalue != tcp_nodelay) {
|
if (nvalue != tcp_nodelay) {
|
||||||
tcp_nodelay = nvalue;
|
tcp_nodelay = nvalue;
|
||||||
|
|
||||||
srs_error_t err = set_tcp_nodelay(tcp_nodelay);
|
srs_error_t err = skt->set_tcp_nodelay(tcp_nodelay);
|
||||||
if (err != srs_success) {
|
if (err != srs_success) {
|
||||||
srs_warn("ignore err %s", srs_error_desc(err).c_str());
|
srs_warn("ignore err %s", srs_error_desc(err).c_str());
|
||||||
srs_freep(err);
|
srs_freep(err);
|
||||||
|
@ -1399,3 +1425,68 @@ void SrsRtmpConn::http_hooks_on_stop()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsRtmpConn::start()
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
if ((err = skt->initialize()) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "init socket");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = trd->start()) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "coroutine");
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsRtmpConn::cycle()
|
||||||
|
{
|
||||||
|
srs_error_t err = do_cycle();
|
||||||
|
|
||||||
|
// Notify manager to remove it.
|
||||||
|
// Note that we create this object, so we use manager to remove it.
|
||||||
|
manager->remove(this);
|
||||||
|
|
||||||
|
// success.
|
||||||
|
if (err == srs_success) {
|
||||||
|
srs_trace("client finished.");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// It maybe success with message.
|
||||||
|
if (srs_error_code(err) == ERROR_SUCCESS) {
|
||||||
|
srs_trace("client finished%s.", srs_error_summary(err).c_str());
|
||||||
|
srs_freep(err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// client close peer.
|
||||||
|
// TODO: FIXME: Only reset the error when client closed it.
|
||||||
|
if (srs_is_client_gracefully_close(err)) {
|
||||||
|
srs_warn("client disconnect peer. ret=%d", srs_error_code(err));
|
||||||
|
} else if (srs_is_server_gracefully_close(err)) {
|
||||||
|
srs_warn("server disconnect. ret=%d", srs_error_code(err));
|
||||||
|
} else {
|
||||||
|
srs_error("serve error %s", srs_error_desc(err).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_freep(err);
|
||||||
|
return srs_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
string SrsRtmpConn::remote_ip()
|
||||||
|
{
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SrsContextId& SrsRtmpConn::get_id()
|
||||||
|
{
|
||||||
|
return trd->cid();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsRtmpConn::expire()
|
||||||
|
{
|
||||||
|
trd->interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,7 +83,8 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
// The client provides the main logic control for RTMP clients.
|
// The client provides the main logic control for RTMP clients.
|
||||||
class SrsRtmpConn : virtual public SrsConnection, virtual public ISrsReloadHandler
|
class SrsRtmpConn : virtual public ISrsStartableConneciton, virtual public ISrsReloadHandler
|
||||||
|
, virtual public ISrsCoroutineHandler, virtual public ISrsExpire
|
||||||
{
|
{
|
||||||
// For the thread to directly access any field of connection.
|
// For the thread to directly access any field of connection.
|
||||||
friend class SrsPublishRecvThread;
|
friend class SrsPublishRecvThread;
|
||||||
|
@ -116,11 +117,32 @@ private:
|
||||||
bool tcp_nodelay;
|
bool tcp_nodelay;
|
||||||
// About the rtmp client.
|
// About the rtmp client.
|
||||||
SrsClientInfo* info;
|
SrsClientInfo* info;
|
||||||
|
private:
|
||||||
|
srs_netfd_t stfd;
|
||||||
|
SrsTcpConnection* skt;
|
||||||
|
// Each connection start a green thread,
|
||||||
|
// when thread stop, the connection will be delete by server.
|
||||||
|
SrsCoroutine* trd;
|
||||||
|
// The manager object to manage the connection.
|
||||||
|
ISrsResourceManager* manager;
|
||||||
|
// The ip and port of client.
|
||||||
|
std::string ip;
|
||||||
|
int port;
|
||||||
|
// The connection total kbps.
|
||||||
|
// not only the rtmp or http connection, all type of connection are
|
||||||
|
// need to statistic the kbps of io.
|
||||||
|
// The SrsStatistic will use it indirectly to statistic the bytes delta of current connection.
|
||||||
|
SrsKbps* kbps;
|
||||||
|
SrsWallClock* clk;
|
||||||
|
// The create time in milliseconds.
|
||||||
|
// for current connection to log self create time and calculate the living time.
|
||||||
|
int64_t create_time;
|
||||||
public:
|
public:
|
||||||
SrsRtmpConn(SrsServer* svr, srs_netfd_t c, std::string cip);
|
SrsRtmpConn(SrsServer* svr, srs_netfd_t c, std::string cip, int port);
|
||||||
virtual ~SrsRtmpConn();
|
virtual ~SrsRtmpConn();
|
||||||
|
// Interface ISrsResource.
|
||||||
public:
|
public:
|
||||||
virtual void dispose();
|
virtual std::string desc();
|
||||||
protected:
|
protected:
|
||||||
virtual srs_error_t do_cycle();
|
virtual srs_error_t do_cycle();
|
||||||
// Interface ISrsReloadHandler
|
// Interface ISrsReloadHandler
|
||||||
|
@ -163,6 +185,30 @@ private:
|
||||||
virtual void http_hooks_on_unpublish();
|
virtual void http_hooks_on_unpublish();
|
||||||
virtual srs_error_t http_hooks_on_play();
|
virtual srs_error_t http_hooks_on_play();
|
||||||
virtual void http_hooks_on_stop();
|
virtual void http_hooks_on_stop();
|
||||||
|
// Extract APIs from SrsTcpConnection.
|
||||||
|
// Interface ISrsStartable
|
||||||
|
public:
|
||||||
|
// Start the client green thread.
|
||||||
|
// when server get a client from listener,
|
||||||
|
// 1. server will create an concrete connection(for instance, RTMP connection),
|
||||||
|
// 2. then add connection to its connection manager,
|
||||||
|
// 3. start the client thread by invoke this start()
|
||||||
|
// when client cycle thread stop, invoke the on_thread_stop(), which will use server
|
||||||
|
// To remove the client by server->remove(this).
|
||||||
|
virtual srs_error_t start();
|
||||||
|
// Interface ISrsOneCycleThreadHandler
|
||||||
|
public:
|
||||||
|
// The thread cycle function,
|
||||||
|
// when serve connection completed, terminate the loop which will terminate the thread,
|
||||||
|
// thread will invoke the on_thread_stop() when it terminated.
|
||||||
|
virtual srs_error_t cycle();
|
||||||
|
// Interface ISrsConnection.
|
||||||
|
public:
|
||||||
|
virtual std::string remote_ip();
|
||||||
|
virtual const SrsContextId& get_id();
|
||||||
|
// Interface ISrsExpire.
|
||||||
|
public:
|
||||||
|
virtual void expire();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -121,8 +121,10 @@ srs_error_t SrsRtpConn::on_udp_packet(const sockaddr* from, const int fromlen, c
|
||||||
// always free it.
|
// always free it.
|
||||||
SrsAutoFree(SrsRtpPacket, cache);
|
SrsAutoFree(SrsRtpPacket, cache);
|
||||||
|
|
||||||
if ((err = rtsp->on_rtp_packet(cache, stream_id)) != srs_success) {
|
err = rtsp->on_rtp_packet(cache, stream_id);
|
||||||
return srs_error_wrap(err, "process rtp packet");
|
if (err != srs_success) {
|
||||||
|
srs_warn("ignore RTP packet err %s", srs_error_desc(err).c_str());
|
||||||
|
srs_freep(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
@ -192,6 +194,11 @@ SrsRtspConn::SrsRtspConn(SrsRtspCaster* c, srs_netfd_t fd, std::string o)
|
||||||
rtsp = new SrsRtspStack(skt);
|
rtsp = new SrsRtspStack(skt);
|
||||||
trd = new SrsSTCoroutine("rtsp", this);
|
trd = new SrsSTCoroutine("rtsp", this);
|
||||||
|
|
||||||
|
audio_id = 0;
|
||||||
|
video_id = 0;
|
||||||
|
audio_sample_rate = 0;
|
||||||
|
audio_channel = 0;
|
||||||
|
|
||||||
req = NULL;
|
req = NULL;
|
||||||
sdk = NULL;
|
sdk = NULL;
|
||||||
vjitter = new SrsRtspJitter();
|
vjitter = new SrsRtspJitter();
|
||||||
|
@ -221,6 +228,9 @@ SrsRtspConn::~SrsRtspConn()
|
||||||
|
|
||||||
srs_freep(vjitter);
|
srs_freep(vjitter);
|
||||||
srs_freep(ajitter);
|
srs_freep(ajitter);
|
||||||
|
|
||||||
|
srs_freep(avc);
|
||||||
|
srs_freep(aac);
|
||||||
srs_freep(acodec);
|
srs_freep(acodec);
|
||||||
srs_freep(acache);
|
srs_freep(acache);
|
||||||
}
|
}
|
||||||
|
@ -246,16 +256,29 @@ std::string SrsRtspConn::remote_ip()
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string SrsRtspConn::desc()
|
||||||
|
{
|
||||||
|
return "RtspConn";
|
||||||
|
}
|
||||||
|
|
||||||
|
const SrsContextId& SrsRtspConn::get_id()
|
||||||
|
{
|
||||||
|
return _srs_context->get_id();
|
||||||
|
}
|
||||||
|
|
||||||
srs_error_t SrsRtspConn::do_cycle()
|
srs_error_t SrsRtspConn::do_cycle()
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
// retrieve ip of client.
|
// retrieve ip of client.
|
||||||
std::string ip = srs_get_peer_ip(srs_netfd_fileno(stfd));
|
int fd = srs_netfd_fileno(stfd);
|
||||||
|
std::string ip = srs_get_peer_ip(fd);
|
||||||
|
int port = srs_get_peer_port(fd);
|
||||||
|
|
||||||
if (ip.empty() && !_srs_config->empty_ip_ok()) {
|
if (ip.empty() && !_srs_config->empty_ip_ok()) {
|
||||||
srs_warn("empty ip for fd=%d", srs_netfd_fileno(stfd));
|
srs_warn("empty ip for fd=%d", srs_netfd_fileno(stfd));
|
||||||
}
|
}
|
||||||
srs_trace("rtsp: serve %s", ip.c_str());
|
srs_trace("rtsp: serve %s:%d", ip.c_str(), port);
|
||||||
|
|
||||||
// consume all rtsp messages.
|
// consume all rtsp messages.
|
||||||
while (true) {
|
while (true) {
|
||||||
|
@ -495,46 +518,49 @@ srs_error_t SrsRtspConn::write_sequence_header()
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate audio sh by audio specific config.
|
// generate audio sh by audio specific config.
|
||||||
if (true) {
|
if (aac_specific_config.empty()) {
|
||||||
std::string sh = aac_specific_config;
|
srs_warn("no audio asc");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
SrsFormat* format = new SrsFormat();
|
std::string sh = aac_specific_config;
|
||||||
SrsAutoFree(SrsFormat, format);
|
|
||||||
|
|
||||||
if ((err = format->on_aac_sequence_header((char*)sh.c_str(), (int)sh.length())) != srs_success) {
|
SrsFormat* format = new SrsFormat();
|
||||||
return srs_error_wrap(err, "on aac sequence header");
|
SrsAutoFree(SrsFormat, format);
|
||||||
}
|
|
||||||
|
|
||||||
SrsAudioCodecConfig* dec = format->acodec;
|
if ((err = format->on_aac_sequence_header((char*)sh.c_str(), (int)sh.length())) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "on aac sequence header");
|
||||||
|
}
|
||||||
|
|
||||||
acodec->sound_format = SrsAudioCodecIdAAC;
|
SrsAudioCodecConfig* dec = format->acodec;
|
||||||
acodec->sound_type = (dec->aac_channels == 2)? SrsAudioChannelsStereo : SrsAudioChannelsMono;
|
|
||||||
acodec->sound_size = SrsAudioSampleBits16bit;
|
|
||||||
acodec->aac_packet_type = 0;
|
|
||||||
|
|
||||||
static int srs_aac_srates[] = {
|
acodec->sound_format = SrsAudioCodecIdAAC;
|
||||||
96000, 88200, 64000, 48000,
|
acodec->sound_type = (dec->aac_channels == 2)? SrsAudioChannelsStereo : SrsAudioChannelsMono;
|
||||||
44100, 32000, 24000, 22050,
|
acodec->sound_size = SrsAudioSampleBits16bit;
|
||||||
16000, 12000, 11025, 8000,
|
acodec->aac_packet_type = 0;
|
||||||
7350, 0, 0, 0
|
|
||||||
};
|
|
||||||
switch (srs_aac_srates[dec->aac_sample_rate]) {
|
|
||||||
case 11025:
|
|
||||||
acodec->sound_rate = SrsAudioSampleRate11025;
|
|
||||||
break;
|
|
||||||
case 22050:
|
|
||||||
acodec->sound_rate = SrsAudioSampleRate22050;
|
|
||||||
break;
|
|
||||||
case 44100:
|
|
||||||
acodec->sound_rate = SrsAudioSampleRate44100;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
|
|
||||||
if ((err = write_audio_raw_frame((char*)sh.data(), (int)sh.length(), acodec, (uint32_t)dts)) != srs_success) {
|
static int srs_aac_srates[] = {
|
||||||
return srs_error_wrap(err, "write audio raw frame");
|
96000, 88200, 64000, 48000,
|
||||||
}
|
44100, 32000, 24000, 22050,
|
||||||
|
16000, 12000, 11025, 8000,
|
||||||
|
7350, 0, 0, 0
|
||||||
|
};
|
||||||
|
switch (srs_aac_srates[dec->aac_sample_rate]) {
|
||||||
|
case 11025:
|
||||||
|
acodec->sound_rate = SrsAudioSampleRate11025;
|
||||||
|
break;
|
||||||
|
case 22050:
|
||||||
|
acodec->sound_rate = SrsAudioSampleRate22050;
|
||||||
|
break;
|
||||||
|
case 44100:
|
||||||
|
acodec->sound_rate = SrsAudioSampleRate44100;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
if ((err = write_audio_raw_frame((char*)sh.data(), (int)sh.length(), acodec, (uint32_t)dts)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "write audio raw frame");
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
@ -544,6 +570,11 @@ srs_error_t SrsRtspConn::write_h264_sps_pps(uint32_t dts, uint32_t pts)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
if (h264_sps.empty() || h264_pps.empty()) {
|
||||||
|
srs_warn("no sps=%dB or pps=%dB", (int)h264_sps.size(), (int)h264_pps.size());
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
// h264 raw to h264 packet.
|
// h264 raw to h264 packet.
|
||||||
std::string sh;
|
std::string sh;
|
||||||
if ((err = avc->mux_sequence_header(h264_sps, h264_pps, dts, pts, sh)) != srs_success) {
|
if ((err = avc->mux_sequence_header(h264_sps, h264_pps, dts, pts, sh)) != srs_success) {
|
||||||
|
@ -687,10 +718,11 @@ void SrsRtspConn::close()
|
||||||
SrsRtspCaster::SrsRtspCaster(SrsConfDirective* c)
|
SrsRtspCaster::SrsRtspCaster(SrsConfDirective* c)
|
||||||
{
|
{
|
||||||
// TODO: FIXME: support reload.
|
// TODO: FIXME: support reload.
|
||||||
|
engine = _srs_config->get_stream_caster_engine(c);
|
||||||
output = _srs_config->get_stream_caster_output(c);
|
output = _srs_config->get_stream_caster_output(c);
|
||||||
local_port_min = _srs_config->get_stream_caster_rtp_port_min(c);
|
local_port_min = _srs_config->get_stream_caster_rtp_port_min(c);
|
||||||
local_port_max = _srs_config->get_stream_caster_rtp_port_max(c);
|
local_port_max = _srs_config->get_stream_caster_rtp_port_max(c);
|
||||||
manager = new SrsCoroutineManager();
|
manager = new SrsResourceManager("CRTSP");
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsRtspCaster::~SrsRtspCaster()
|
SrsRtspCaster::~SrsRtspCaster()
|
||||||
|
@ -728,7 +760,7 @@ srs_error_t SrsRtspCaster::alloc_port(int* pport)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
srs_info("rtsp: alloc port=%d-%d", *pport, *pport + 1);
|
srs_trace("rtsp: %s alloc port=%d-%d", engine.c_str(), *pport, *pport + 1);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -738,7 +770,7 @@ void SrsRtspCaster::free_port(int lpmin, int lpmax)
|
||||||
for (int i = lpmin; i < lpmax; i++) {
|
for (int i = lpmin; i < lpmax; i++) {
|
||||||
used_ports[i] = false;
|
used_ports[i] = false;
|
||||||
}
|
}
|
||||||
srs_trace("rtsp: free rtp port=%d-%d", lpmin, lpmax);
|
srs_trace("rtsp: %s free rtp port=%d-%d", engine.c_str(), lpmin, lpmax);
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsRtspCaster::on_tcp_client(srs_netfd_t stfd)
|
srs_error_t SrsRtspCaster::on_tcp_client(srs_netfd_t stfd)
|
||||||
|
|
|
@ -31,8 +31,8 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include <srs_app_st.hpp>
|
#include <srs_app_st.hpp>
|
||||||
#include <srs_app_thread.hpp>
|
|
||||||
#include <srs_app_listener.hpp>
|
#include <srs_app_listener.hpp>
|
||||||
|
#include <srs_service_conn.hpp>
|
||||||
|
|
||||||
class SrsStSocket;
|
class SrsStSocket;
|
||||||
class SrsRtspConn;
|
class SrsRtspConn;
|
||||||
|
@ -51,6 +51,7 @@ class SrsAudioFrame;
|
||||||
class SrsSimpleStream;
|
class SrsSimpleStream;
|
||||||
class SrsPithyPrint;
|
class SrsPithyPrint;
|
||||||
class SrsSimpleRtmpClient;
|
class SrsSimpleRtmpClient;
|
||||||
|
class SrsResourceManager;
|
||||||
|
|
||||||
// A rtp connection which transport a stream.
|
// A rtp connection which transport a stream.
|
||||||
class SrsRtpConn: public ISrsUdpHandler
|
class SrsRtpConn: public ISrsUdpHandler
|
||||||
|
@ -143,7 +144,11 @@ public:
|
||||||
virtual ~SrsRtspConn();
|
virtual ~SrsRtspConn();
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t serve();
|
virtual srs_error_t serve();
|
||||||
|
// Interface ISrsConnection.
|
||||||
|
public:
|
||||||
virtual std::string remote_ip();
|
virtual std::string remote_ip();
|
||||||
|
virtual const SrsContextId& get_id();
|
||||||
|
virtual std::string desc();
|
||||||
private:
|
private:
|
||||||
virtual srs_error_t do_cycle();
|
virtual srs_error_t do_cycle();
|
||||||
// internal methods
|
// internal methods
|
||||||
|
@ -173,6 +178,7 @@ private:
|
||||||
class SrsRtspCaster : public ISrsTcpHandler
|
class SrsRtspCaster : public ISrsTcpHandler
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
std::string engine;
|
||||||
std::string output;
|
std::string output;
|
||||||
int local_port_min;
|
int local_port_min;
|
||||||
int local_port_max;
|
int local_port_max;
|
||||||
|
@ -180,7 +186,7 @@ private:
|
||||||
std::map<int, bool> used_ports;
|
std::map<int, bool> used_ports;
|
||||||
private:
|
private:
|
||||||
std::vector<SrsRtspConn*> clients;
|
std::vector<SrsRtspConn*> clients;
|
||||||
SrsCoroutineManager* manager;
|
SrsResourceManager* manager;
|
||||||
public:
|
public:
|
||||||
SrsRtspCaster(SrsConfDirective* c);
|
SrsRtspCaster(SrsConfDirective* c);
|
||||||
virtual ~SrsRtspCaster();
|
virtual ~SrsRtspCaster();
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#ifndef SRS_AUTO_OSX
|
#ifndef SRS_OSX
|
||||||
#include <sys/inotify.h>
|
#include <sys/inotify.h>
|
||||||
#endif
|
#endif
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
@ -52,7 +52,6 @@ using namespace std;
|
||||||
#include <srs_app_caster_flv.hpp>
|
#include <srs_app_caster_flv.hpp>
|
||||||
#include <srs_core_mem_watch.hpp>
|
#include <srs_core_mem_watch.hpp>
|
||||||
#include <srs_kernel_consts.hpp>
|
#include <srs_kernel_consts.hpp>
|
||||||
#include <srs_app_thread.hpp>
|
|
||||||
#include <srs_app_coworkers.hpp>
|
#include <srs_app_coworkers.hpp>
|
||||||
#include <srs_app_gb28181.hpp>
|
#include <srs_app_gb28181.hpp>
|
||||||
#include <srs_app_gb28181_sip.hpp>
|
#include <srs_app_gb28181_sip.hpp>
|
||||||
|
@ -105,8 +104,12 @@ std::string srs_listener_type2string(SrsListenerType type)
|
||||||
return "RTMP";
|
return "RTMP";
|
||||||
case SrsListenerHttpApi:
|
case SrsListenerHttpApi:
|
||||||
return "HTTP-API";
|
return "HTTP-API";
|
||||||
|
case SrsListenerHttpsApi:
|
||||||
|
return "HTTPS-API";
|
||||||
case SrsListenerHttpStream:
|
case SrsListenerHttpStream:
|
||||||
return "HTTP-Server";
|
return "HTTP-Server";
|
||||||
|
case SrsListenerHttpsStream:
|
||||||
|
return "HTTP-Server";
|
||||||
case SrsListenerMpegTsOverUdp:
|
case SrsListenerMpegTsOverUdp:
|
||||||
return "MPEG-TS over UDP";
|
return "MPEG-TS over UDP";
|
||||||
case SrsListenerRtsp:
|
case SrsListenerRtsp:
|
||||||
|
@ -349,7 +352,7 @@ SrsUdpCasterListener::~SrsUdpCasterListener()
|
||||||
srs_freep(caster);
|
srs_freep(caster);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SRS_AUTO_GB28181
|
#ifdef SRS_GB28181
|
||||||
|
|
||||||
SrsGb28181Listener::SrsGb28181Listener(SrsServer* svr, SrsListenerType t, SrsConfDirective* c) : SrsUdpStreamListener(svr, t, NULL)
|
SrsGb28181Listener::SrsGb28181Listener(SrsServer* svr, SrsListenerType t, SrsConfDirective* c) : SrsUdpStreamListener(svr, t, NULL)
|
||||||
{
|
{
|
||||||
|
@ -370,6 +373,62 @@ SrsGb28181Listener::~SrsGb28181Listener()
|
||||||
srs_freep(caster);
|
srs_freep(caster);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SrsGb28181TcpListener::SrsGb28181TcpListener(SrsServer* svr, SrsListenerType t, SrsConfDirective* c) : SrsListener(svr, t)
|
||||||
|
{
|
||||||
|
// the caller already ensure the type is ok,
|
||||||
|
// we just assert here for unknown stream caster.
|
||||||
|
srs_assert(type == SrsListenerGb28181RtpMux);
|
||||||
|
|
||||||
|
caster = new SrsGb28181Caster(c);
|
||||||
|
listener = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsGb28181TcpListener::~SrsGb28181TcpListener()
|
||||||
|
{
|
||||||
|
srs_freep(caster);
|
||||||
|
srs_freep(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsGb28181TcpListener::listen(std::string i, int p)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
// the caller already ensure the type is ok,
|
||||||
|
// we just assert here for unknown stream caster.
|
||||||
|
srs_assert(type == SrsListenerGb28181RtpMux);
|
||||||
|
|
||||||
|
ip = i;
|
||||||
|
port = p;
|
||||||
|
|
||||||
|
if ((err = caster->initialize()) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "init caster");
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_freep(listener);
|
||||||
|
listener = new SrsTcpListener(this, ip, port);
|
||||||
|
|
||||||
|
if ((err = listener->listen()) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "rtsp listen %s:%d", ip.c_str(), port);
|
||||||
|
}
|
||||||
|
|
||||||
|
string v = srs_listener_type2string(type);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsGb28181TcpListener::on_tcp_client(srs_netfd_t stfd)
|
||||||
|
{
|
||||||
|
int fd = srs_netfd_fileno(stfd);
|
||||||
|
string ip = srs_get_peer_ip(fd);
|
||||||
|
|
||||||
|
srs_error_t err = caster->on_tcp_client(stfd);
|
||||||
|
if (err != srs_success) {
|
||||||
|
srs_warn("accept client failed, err is %s", srs_error_desc(err).c_str());
|
||||||
|
srs_freep(err);
|
||||||
|
}
|
||||||
|
return srs_success;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SrsSignalManager* SrsSignalManager::instance = NULL;
|
SrsSignalManager* SrsSignalManager::instance = NULL;
|
||||||
|
@ -380,7 +439,7 @@ SrsSignalManager::SrsSignalManager(SrsServer* s)
|
||||||
|
|
||||||
server = s;
|
server = s;
|
||||||
sig_pipe[0] = sig_pipe[1] = -1;
|
sig_pipe[0] = sig_pipe[1] = -1;
|
||||||
trd = new SrsSTCoroutine("signal", this);
|
trd = new SrsSTCoroutine("signal", this, _srs_context->get_id());
|
||||||
signal_read_stfd = NULL;
|
signal_read_stfd = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -514,7 +573,7 @@ srs_error_t SrsInotifyWorker::start()
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
#ifndef SRS_AUTO_OSX
|
#ifndef SRS_OSX
|
||||||
// Whether enable auto reload config.
|
// Whether enable auto reload config.
|
||||||
bool auto_reload = _srs_config->inotify_auto_reload();
|
bool auto_reload = _srs_config->inotify_auto_reload();
|
||||||
if (!auto_reload && _srs_in_docker && _srs_config->auto_reload_for_docker()) {
|
if (!auto_reload && _srs_in_docker && _srs_config->auto_reload_for_docker()) {
|
||||||
|
@ -539,7 +598,7 @@ srs_error_t SrsInotifyWorker::start()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (((err = srs_fd_closeexec(fd))) != srs_success) {
|
if (((err = srs_fd_closeexec(fd))) != srs_success) {
|
||||||
return srs_error_new(ERROR_INOTIFY_OPENFD, "closeexec fd=%d", fd);
|
return srs_error_wrap(err, "closeexec fd=%d", fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
// /* the following are legal, implemented events that user-space can watch for */
|
// /* the following are legal, implemented events that user-space can watch for */
|
||||||
|
@ -594,7 +653,7 @@ srs_error_t SrsInotifyWorker::cycle()
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
#ifndef SRS_AUTO_OSX
|
#ifndef SRS_OSX
|
||||||
string config_path = _srs_config->config();
|
string config_path = _srs_config->config();
|
||||||
string config_file = srs_path_basename(config_path);
|
string config_file = srs_path_basename(config_path);
|
||||||
string k8s_file = "..data";
|
string k8s_file = "..data";
|
||||||
|
@ -657,7 +716,7 @@ SrsServer::SrsServer()
|
||||||
pid_fd = -1;
|
pid_fd = -1;
|
||||||
|
|
||||||
signal_manager = new SrsSignalManager(this);
|
signal_manager = new SrsSignalManager(this);
|
||||||
conn_manager = new SrsCoroutineManager();
|
conn_manager = new SrsResourceManager("TCP", true);
|
||||||
|
|
||||||
handler = NULL;
|
handler = NULL;
|
||||||
ppid = ::getppid();
|
ppid = ::getppid();
|
||||||
|
@ -695,7 +754,7 @@ void SrsServer::destroy()
|
||||||
srs_freep(signal_manager);
|
srs_freep(signal_manager);
|
||||||
srs_freep(conn_manager);
|
srs_freep(conn_manager);
|
||||||
|
|
||||||
#ifdef SRS_AUTO_GB28181
|
#ifdef SRS_GB28181
|
||||||
//free global gb28181 manager
|
//free global gb28181 manager
|
||||||
srs_freep(_srs_gb28181);
|
srs_freep(_srs_gb28181);
|
||||||
#endif
|
#endif
|
||||||
|
@ -708,7 +767,9 @@ void SrsServer::dispose()
|
||||||
// prevent fresh clients.
|
// prevent fresh clients.
|
||||||
close_listeners(SrsListenerRtmpStream);
|
close_listeners(SrsListenerRtmpStream);
|
||||||
close_listeners(SrsListenerHttpApi);
|
close_listeners(SrsListenerHttpApi);
|
||||||
|
close_listeners(SrsListenerHttpsApi);
|
||||||
close_listeners(SrsListenerHttpStream);
|
close_listeners(SrsListenerHttpStream);
|
||||||
|
close_listeners(SrsListenerHttpsStream);
|
||||||
close_listeners(SrsListenerMpegTsOverUdp);
|
close_listeners(SrsListenerMpegTsOverUdp);
|
||||||
close_listeners(SrsListenerRtsp);
|
close_listeners(SrsListenerRtsp);
|
||||||
close_listeners(SrsListenerFlv);
|
close_listeners(SrsListenerFlv);
|
||||||
|
@ -721,7 +782,7 @@ void SrsServer::dispose()
|
||||||
|
|
||||||
// @remark don't dispose all connections, for too slow.
|
// @remark don't dispose all connections, for too slow.
|
||||||
|
|
||||||
#ifdef SRS_AUTO_MEM_WATCH
|
#ifdef SRS_MEM_WATCH
|
||||||
srs_memory_report();
|
srs_memory_report();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -737,7 +798,9 @@ void SrsServer::gracefully_dispose()
|
||||||
// prevent fresh clients.
|
// prevent fresh clients.
|
||||||
close_listeners(SrsListenerRtmpStream);
|
close_listeners(SrsListenerRtmpStream);
|
||||||
close_listeners(SrsListenerHttpApi);
|
close_listeners(SrsListenerHttpApi);
|
||||||
|
close_listeners(SrsListenerHttpsApi);
|
||||||
close_listeners(SrsListenerHttpStream);
|
close_listeners(SrsListenerHttpStream);
|
||||||
|
close_listeners(SrsListenerHttpsStream);
|
||||||
close_listeners(SrsListenerMpegTsOverUdp);
|
close_listeners(SrsListenerMpegTsOverUdp);
|
||||||
close_listeners(SrsListenerRtsp);
|
close_listeners(SrsListenerRtsp);
|
||||||
close_listeners(SrsListenerFlv);
|
close_listeners(SrsListenerFlv);
|
||||||
|
@ -750,20 +813,20 @@ void SrsServer::gracefully_dispose()
|
||||||
// Wait for connections to quit.
|
// Wait for connections to quit.
|
||||||
// While gracefully quiting, user can requires SRS to fast quit.
|
// While gracefully quiting, user can requires SRS to fast quit.
|
||||||
int wait_step = 1;
|
int wait_step = 1;
|
||||||
while (!conns.empty() && !signal_fast_quit) {
|
while (!conn_manager->empty() && !signal_fast_quit) {
|
||||||
for (int i = 0; i < wait_step && !conns.empty() && !signal_fast_quit; i++) {
|
for (int i = 0; i < wait_step && !conn_manager->empty() && !signal_fast_quit; i++) {
|
||||||
srs_usleep(1000 * SRS_UTIME_MILLISECONDS);
|
srs_usleep(1000 * SRS_UTIME_MILLISECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
wait_step = (wait_step * 2) % 33;
|
wait_step = (wait_step * 2) % 33;
|
||||||
srs_trace("wait for %d conns to quit", conns.size());
|
srs_trace("wait for %d conns to quit", (int)conn_manager->size());
|
||||||
}
|
}
|
||||||
|
|
||||||
// dispose the source for hls and dvr.
|
// dispose the source for hls and dvr.
|
||||||
_srs_sources->dispose();
|
_srs_sources->dispose();
|
||||||
srs_trace("source disposed");
|
srs_trace("source disposed");
|
||||||
|
|
||||||
#ifdef SRS_AUTO_MEM_WATCH
|
#ifdef SRS_MEM_WATCH
|
||||||
srs_memory_report();
|
srs_memory_report();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -804,29 +867,14 @@ srs_error_t SrsServer::initialize_st()
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
// @remark, st alloc segment use mmap, which only support 32757 threads,
|
|
||||||
// if need to support more, for instance, 100k threads, define the macro MALLOC_STACK.
|
|
||||||
// TODO: FIXME: maybe can use "sysctl vm.max_map_count" to refine.
|
|
||||||
#define __MMAP_MAX_CONNECTIONS 32756
|
|
||||||
if (_srs_config->get_max_connections() > __MMAP_MAX_CONNECTIONS) {
|
|
||||||
srs_error("st mmap for stack allocation must <= %d threads, "
|
|
||||||
"@see Makefile of st for MALLOC_STACK, please build st manually by "
|
|
||||||
"\"make EXTRA_CFLAGS=-DMALLOC_STACK linux-debug\"", __MMAP_MAX_CONNECTIONS);
|
|
||||||
return srs_error_new(ERROR_ST_EXCEED_THREADS, "%d exceed max %d threads",
|
|
||||||
_srs_config->get_max_connections(), __MMAP_MAX_CONNECTIONS);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set current log id.
|
|
||||||
_srs_context->generate_id();
|
|
||||||
|
|
||||||
// check asprocess.
|
// check asprocess.
|
||||||
bool asprocess = _srs_config->get_asprocess();
|
bool asprocess = _srs_config->get_asprocess();
|
||||||
if (asprocess && ppid == 1) {
|
if (asprocess && ppid == 1) {
|
||||||
return srs_error_new(ERROR_SYSTEM_ASSERT_FAILED, "ppid=%d illegal for asprocess", ppid);
|
return srs_error_new(ERROR_SYSTEM_ASSERT_FAILED, "ppid=%d illegal for asprocess", ppid);
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_trace("server main cid=%d, pid=%d, ppid=%d, asprocess=%d",
|
srs_trace("server main cid=%s, pid=%d, ppid=%d, asprocess=%d",
|
||||||
_srs_context->get_id(), ::getpid(), ppid, asprocess);
|
_srs_context->get_id().c_str(), ::getpid(), ppid, asprocess);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -880,7 +928,7 @@ srs_error_t SrsServer::acquire_pid_file()
|
||||||
// write the pid
|
// write the pid
|
||||||
string pid = srs_int2str(getpid());
|
string pid = srs_int2str(getpid());
|
||||||
if (write(fd, pid.c_str(), pid.length()) != (int)pid.length()) {
|
if (write(fd, pid.c_str(), pid.length()) != (int)pid.length()) {
|
||||||
return srs_error_new(ERROR_SYSTEM_PID_WRITE_FILE, "write pid=%d to file=%s", pid.c_str(), pid_file.c_str());
|
return srs_error_new(ERROR_SYSTEM_PID_WRITE_FILE, "write pid=%s to file=%s", pid.c_str(), pid_file.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// auto close when fork child process.
|
// auto close when fork child process.
|
||||||
|
@ -911,10 +959,18 @@ srs_error_t SrsServer::listen()
|
||||||
return srs_error_wrap(err, "http api listen");
|
return srs_error_wrap(err, "http api listen");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((err = listen_https_api()) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "https api listen");
|
||||||
|
}
|
||||||
|
|
||||||
if ((err = listen_http_stream()) != srs_success) {
|
if ((err = listen_http_stream()) != srs_success) {
|
||||||
return srs_error_wrap(err, "http stream listen");
|
return srs_error_wrap(err, "http stream listen");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((err = listen_https_stream()) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "https stream listen");
|
||||||
|
}
|
||||||
|
|
||||||
if ((err = listen_stream_caster()) != srs_success) {
|
if ((err = listen_stream_caster()) != srs_success) {
|
||||||
return srs_error_wrap(err, "stream caster listen");
|
return srs_error_wrap(err, "stream caster listen");
|
||||||
}
|
}
|
||||||
|
@ -941,8 +997,8 @@ srs_error_t SrsServer::http_handle()
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
if ((err = http_api_mux->handle("/", new SrsHttpNotFoundHandler())) != srs_success) {
|
if ((err = http_api_mux->handle("/", new SrsGoApiRoot())) != srs_success) {
|
||||||
return srs_error_wrap(err, "handle not found");
|
return srs_error_wrap(err, "handle /");
|
||||||
}
|
}
|
||||||
if ((err = http_api_mux->handle("/api/", new SrsGoApiApi())) != srs_success) {
|
if ((err = http_api_mux->handle("/api/", new SrsGoApiApi())) != srs_success) {
|
||||||
return srs_error_wrap(err, "handle api");
|
return srs_error_wrap(err, "handle api");
|
||||||
|
@ -992,7 +1048,7 @@ srs_error_t SrsServer::http_handle()
|
||||||
if ((err = http_api_mux->handle("/api/v1/perf", new SrsGoApiPerf())) != srs_success) {
|
if ((err = http_api_mux->handle("/api/v1/perf", new SrsGoApiPerf())) != srs_success) {
|
||||||
return srs_error_wrap(err, "handle perf");
|
return srs_error_wrap(err, "handle perf");
|
||||||
}
|
}
|
||||||
#ifdef SRS_AUTO_GB28181
|
#ifdef SRS_GB28181
|
||||||
if ((err = http_api_mux->handle("/api/v1/gb28181", new SrsGoApiGb28181())) != srs_success) {
|
if ((err = http_api_mux->handle("/api/v1/gb28181", new SrsGoApiGb28181())) != srs_success) {
|
||||||
return srs_error_wrap(err, "handle raw");
|
return srs_error_wrap(err, "handle raw");
|
||||||
}
|
}
|
||||||
|
@ -1015,7 +1071,7 @@ srs_error_t SrsServer::http_handle()
|
||||||
return srs_error_wrap(err, "handle tests errors for error.srs.com");
|
return srs_error_wrap(err, "handle tests errors for error.srs.com");
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SRS_AUTO_GPERF
|
#ifdef SRS_GPERF
|
||||||
// The test api for get tcmalloc stats.
|
// The test api for get tcmalloc stats.
|
||||||
// @see Memory Introspection in https://gperftools.github.io/gperftools/tcmalloc.html
|
// @see Memory Introspection in https://gperftools.github.io/gperftools/tcmalloc.html
|
||||||
if ((err = http_api_mux->handle("/api/v1/tcmalloc", new SrsGoApiTcmalloc())) != srs_success) {
|
if ((err = http_api_mux->handle("/api/v1/tcmalloc", new SrsGoApiTcmalloc())) != srs_success) {
|
||||||
|
@ -1058,7 +1114,7 @@ srs_error_t SrsServer::cycle()
|
||||||
// Do server main cycle.
|
// Do server main cycle.
|
||||||
err = do_cycle();
|
err = do_cycle();
|
||||||
|
|
||||||
#ifdef SRS_AUTO_GPERF_MC
|
#ifdef SRS_GPERF_MC
|
||||||
destroy();
|
destroy();
|
||||||
|
|
||||||
// remark, for gmc, never invoke the exit().
|
// remark, for gmc, never invoke the exit().
|
||||||
|
@ -1074,7 +1130,7 @@ srs_error_t SrsServer::cycle()
|
||||||
|
|
||||||
// fast quit, do some essential cleanup.
|
// fast quit, do some essential cleanup.
|
||||||
if (signal_fast_quit) {
|
if (signal_fast_quit) {
|
||||||
dispose();
|
dispose(); // TODO: FIXME: Rename to essential_dispose.
|
||||||
srs_trace("srs disposed");
|
srs_trace("srs disposed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1104,15 +1160,20 @@ void SrsServer::on_signal(int signo)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef SRS_AUTO_GPERF_MC
|
#ifndef SRS_GPERF_MC
|
||||||
if (signo == SRS_SIGNAL_REOPEN_LOG) {
|
if (signo == SRS_SIGNAL_REOPEN_LOG) {
|
||||||
_srs_log->reopen();
|
_srs_log->reopen();
|
||||||
|
|
||||||
|
if (handler) {
|
||||||
|
handler->on_logrotate();
|
||||||
|
}
|
||||||
|
|
||||||
srs_warn("reopen log file, signo=%d", signo);
|
srs_warn("reopen log file, signo=%d", signo);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRS_AUTO_GPERF_MC
|
#ifdef SRS_GPERF_MC
|
||||||
if (signo == SRS_SIGNAL_REOPEN_LOG) {
|
if (signo == SRS_SIGNAL_REOPEN_LOG) {
|
||||||
signal_gmc_stop = true;
|
signal_gmc_stop = true;
|
||||||
srs_warn("for gmc, the SIGUSR1 used as SIGINT, signo=%d", signo);
|
srs_warn("for gmc, the SIGUSR1 used as SIGINT, signo=%d", signo);
|
||||||
|
@ -1126,11 +1187,11 @@ void SrsServer::on_signal(int signo)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (signo == SIGINT) {
|
if (signo == SIGINT) {
|
||||||
#ifdef SRS_AUTO_GPERF_MC
|
#ifdef SRS_GPERF_MC
|
||||||
srs_trace("gmc is on, main cycle will terminate normally, signo=%d", signo);
|
srs_trace("gmc is on, main cycle will terminate normally, signo=%d", signo);
|
||||||
signal_gmc_stop = true;
|
signal_gmc_stop = true;
|
||||||
#else
|
#else
|
||||||
#ifdef SRS_AUTO_MEM_WATCH
|
#ifdef SRS_MEM_WATCH
|
||||||
srs_memory_report();
|
srs_memory_report();
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
@ -1206,7 +1267,7 @@ srs_error_t SrsServer::do_cycle()
|
||||||
// if user interrupt the program, exit to check mem leak.
|
// if user interrupt the program, exit to check mem leak.
|
||||||
// but, if gperf, use reload to ensure main return normally,
|
// but, if gperf, use reload to ensure main return normally,
|
||||||
// because directly exit will cause core-dump.
|
// because directly exit will cause core-dump.
|
||||||
#ifdef SRS_AUTO_GPERF_MC
|
#ifdef SRS_GPERF_MC
|
||||||
if (signal_gmc_stop) {
|
if (signal_gmc_stop) {
|
||||||
srs_warn("gmc got singal to stop server.");
|
srs_warn("gmc got singal to stop server.");
|
||||||
return err;
|
return err;
|
||||||
|
@ -1336,6 +1397,29 @@ srs_error_t SrsServer::listen_http_api()
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsServer::listen_https_api()
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
close_listeners(SrsListenerHttpsApi);
|
||||||
|
if (_srs_config->get_https_api_enabled()) {
|
||||||
|
SrsListener* listener = new SrsBufferListener(this, SrsListenerHttpsApi);
|
||||||
|
listeners.push_back(listener);
|
||||||
|
|
||||||
|
std::string ep = _srs_config->get_https_api_listen();
|
||||||
|
|
||||||
|
std::string ip;
|
||||||
|
int port;
|
||||||
|
srs_parse_endpoint(ep, ip, port);
|
||||||
|
|
||||||
|
if ((err = listener->listen(ip, port)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "https api listen %s:%d", ip.c_str(), port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
srs_error_t SrsServer::listen_http_stream()
|
srs_error_t SrsServer::listen_http_stream()
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
@ -1359,7 +1443,30 @@ srs_error_t SrsServer::listen_http_stream()
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SRS_AUTO_GB28181
|
srs_error_t SrsServer::listen_https_stream()
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
close_listeners(SrsListenerHttpsStream);
|
||||||
|
if (_srs_config->get_https_stream_enabled()) {
|
||||||
|
SrsListener* listener = new SrsBufferListener(this, SrsListenerHttpsStream);
|
||||||
|
listeners.push_back(listener);
|
||||||
|
|
||||||
|
std::string ep = _srs_config->get_https_stream_listen();
|
||||||
|
|
||||||
|
std::string ip;
|
||||||
|
int port;
|
||||||
|
srs_parse_endpoint(ep, ip, port);
|
||||||
|
|
||||||
|
if ((err = listener->listen(ip, port)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "https stream listen %s:%d", ip.c_str(), port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SRS_GB28181
|
||||||
srs_error_t SrsServer::listen_gb28181_sip(SrsConfDirective* stream_caster)
|
srs_error_t SrsServer::listen_gb28181_sip(SrsConfDirective* stream_caster)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
@ -1409,11 +1516,11 @@ srs_error_t SrsServer::listen_stream_caster()
|
||||||
listener = new SrsRtspListener(this, SrsListenerRtsp, stream_caster);
|
listener = new SrsRtspListener(this, SrsListenerRtsp, stream_caster);
|
||||||
} else if (srs_stream_caster_is_flv(caster)) {
|
} else if (srs_stream_caster_is_flv(caster)) {
|
||||||
listener = new SrsHttpFlvListener(this, SrsListenerFlv, stream_caster);
|
listener = new SrsHttpFlvListener(this, SrsListenerFlv, stream_caster);
|
||||||
|
#ifdef SRS_GB28181
|
||||||
} else if (srs_stream_caster_is_gb28181(caster)) {
|
} else if (srs_stream_caster_is_gb28181(caster)) {
|
||||||
#ifdef SRS_AUTO_GB28181
|
|
||||||
//init global gb28181 manger
|
//init global gb28181 manger
|
||||||
if (_srs_gb28181 == NULL){
|
if (_srs_gb28181 == NULL){
|
||||||
_srs_gb28181 = new SrsGb28181Manger(stream_caster);
|
_srs_gb28181 = new SrsGb28181Manger(this, stream_caster);
|
||||||
if ((err = _srs_gb28181->initialize()) != srs_success){
|
if ((err = _srs_gb28181->initialize()) != srs_success){
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -1427,7 +1534,11 @@ srs_error_t SrsServer::listen_stream_caster()
|
||||||
}
|
}
|
||||||
|
|
||||||
//gb28181 stream listener
|
//gb28181 stream listener
|
||||||
listener = new SrsGb28181Listener(this, SrsListenerGb28181RtpMux, stream_caster);
|
if (!_srs_config->get_stream_caster_tcp_enable(stream_caster)) {
|
||||||
|
listener = new SrsGb28181Listener(this, SrsListenerGb28181RtpMux, stream_caster);
|
||||||
|
} else {
|
||||||
|
listener = new SrsGb28181TcpListener(this, SrsListenerGb28181RtpMux, stream_caster);
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
srs_warn("gb28181 is disabled, please enable it by: ./configure --with-gb28181");
|
srs_warn("gb28181 is disabled, please enable it by: ./configure --with-gb28181");
|
||||||
continue;
|
continue;
|
||||||
|
@ -1472,12 +1583,13 @@ void SrsServer::resample_kbps()
|
||||||
SrsStatistic* stat = SrsStatistic::instance();
|
SrsStatistic* stat = SrsStatistic::instance();
|
||||||
|
|
||||||
// collect delta from all clients.
|
// collect delta from all clients.
|
||||||
for (std::vector<SrsConnection*>::iterator it = conns.begin(); it != conns.end(); ++it) {
|
for (int i = 0; i < (int)conn_manager->size(); i++) {
|
||||||
SrsConnection* conn = *it;
|
ISrsResource* c = conn_manager->at(i);
|
||||||
|
ISrsKbpsDelta* conn = dynamic_cast<ISrsKbpsDelta*>(conn_manager->at(i));
|
||||||
|
|
||||||
// add delta of connection to server kbps.,
|
// add delta of connection to server kbps.,
|
||||||
// for next sample() of server kbps can get the stat.
|
// for next sample() of server kbps can get the stat.
|
||||||
stat->kbps_add_delta(conn);
|
stat->kbps_add_delta(c->get_id(), conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: FXME: support all other connections.
|
// TODO: FXME: support all other connections.
|
||||||
|
@ -1485,29 +1597,27 @@ void SrsServer::resample_kbps()
|
||||||
// sample the kbps, get the stat.
|
// sample the kbps, get the stat.
|
||||||
SrsKbps* kbps = stat->kbps_sample();
|
SrsKbps* kbps = stat->kbps_sample();
|
||||||
|
|
||||||
srs_update_rtmp_server((int)conns.size(), kbps);
|
srs_update_rtmp_server((int)conn_manager->size(), kbps);
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsServer::accept_client(SrsListenerType type, srs_netfd_t stfd)
|
srs_error_t SrsServer::accept_client(SrsListenerType type, srs_netfd_t stfd)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
SrsConnection* conn = NULL;
|
ISrsStartableConneciton* conn = NULL;
|
||||||
|
|
||||||
if ((err = fd2conn(type, stfd, &conn)) != srs_success) {
|
if ((err = fd_to_resource(type, stfd, &conn)) != srs_success) {
|
||||||
if (srs_error_code(err) == ERROR_SOCKET_GET_PEER_IP && _srs_config->empty_ip_ok()) {
|
if (srs_error_code(err) == ERROR_SOCKET_GET_PEER_IP && _srs_config->empty_ip_ok()) {
|
||||||
srs_close_stfd(stfd); srs_error_reset(err);
|
srs_close_stfd(stfd); srs_error_reset(err);
|
||||||
return srs_success;
|
return srs_success;
|
||||||
}
|
}
|
||||||
return srs_error_wrap(err, "fd2conn");
|
return srs_error_wrap(err, "fd to resource");
|
||||||
}
|
}
|
||||||
srs_assert(conn);
|
srs_assert(conn);
|
||||||
|
|
||||||
// directly enqueue, the cycle thread will remove the client.
|
// directly enqueue, the cycle thread will remove the client.
|
||||||
conns.push_back(conn);
|
conn_manager->add(conn);
|
||||||
|
|
||||||
// cycle will start process thread and when finished remove the client.
|
|
||||||
// @remark never use the conn, for it maybe destroyed.
|
|
||||||
if ((err = conn->start()) != srs_success) {
|
if ((err = conn->start()) != srs_success) {
|
||||||
return srs_error_wrap(err, "start conn coroutine");
|
return srs_error_wrap(err, "start conn coroutine");
|
||||||
}
|
}
|
||||||
|
@ -1520,12 +1630,13 @@ SrsHttpServeMux* SrsServer::api_server()
|
||||||
return http_api_mux;
|
return http_api_mux;
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsServer::fd2conn(SrsListenerType type, srs_netfd_t stfd, SrsConnection** pconn)
|
srs_error_t SrsServer::fd_to_resource(SrsListenerType type, srs_netfd_t stfd, ISrsStartableConneciton** pr)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
int fd = srs_netfd_fileno(stfd);
|
int fd = srs_netfd_fileno(stfd);
|
||||||
string ip = srs_get_peer_ip(fd);
|
string ip = srs_get_peer_ip(fd);
|
||||||
|
int port = srs_get_peer_port(fd);
|
||||||
|
|
||||||
// for some keep alive application, for example, the keepalived,
|
// for some keep alive application, for example, the keepalived,
|
||||||
// will send some tcp packet which we cann't got the ip,
|
// will send some tcp packet which we cann't got the ip,
|
||||||
|
@ -1536,14 +1647,13 @@ srs_error_t SrsServer::fd2conn(SrsListenerType type, srs_netfd_t stfd, SrsConnec
|
||||||
|
|
||||||
// check connection limitation.
|
// check connection limitation.
|
||||||
int max_connections = _srs_config->get_max_connections();
|
int max_connections = _srs_config->get_max_connections();
|
||||||
if (handler && (err = handler->on_accept_client(max_connections, (int)conns.size())) != srs_success) {
|
if (handler && (err = handler->on_accept_client(max_connections, (int)conn_manager->size())) != srs_success) {
|
||||||
return srs_error_wrap(err, "drop client fd=%d, max=%d, cur=%d for err: %s",
|
return srs_error_wrap(err, "drop client fd=%d, ip=%s:%d, max=%d, cur=%d for err: %s",
|
||||||
fd, max_connections, (int)conns.size(), srs_error_desc(err).c_str());
|
fd, ip.c_str(), port, max_connections, (int)conn_manager->size(), srs_error_desc(err).c_str());
|
||||||
}
|
}
|
||||||
if ((int)conns.size() >= max_connections) {
|
if ((int)conn_manager->size() >= max_connections) {
|
||||||
return srs_error_new(ERROR_EXCEED_CONNECTIONS,
|
return srs_error_new(ERROR_EXCEED_CONNECTIONS, "drop fd=%d, ip=%s:%d, max=%d, cur=%d for exceed connection limits",
|
||||||
"drop fd=%d, max=%d, cur=%d for exceed connection limits",
|
fd, ip.c_str(), port, max_connections, (int)conn_manager->size());
|
||||||
fd, max_connections, (int)conns.size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// avoid fd leak when fork.
|
// avoid fd leak when fork.
|
||||||
|
@ -1559,14 +1669,21 @@ srs_error_t SrsServer::fd2conn(SrsListenerType type, srs_netfd_t stfd, SrsConnec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The context id may change during creating the bellow objects.
|
||||||
|
SrsContextRestore(_srs_context->get_id());
|
||||||
|
|
||||||
if (type == SrsListenerRtmpStream) {
|
if (type == SrsListenerRtmpStream) {
|
||||||
*pconn = new SrsRtmpConn(this, stfd, ip);
|
*pr = new SrsRtmpConn(this, stfd, ip, port);
|
||||||
} else if (type == SrsListenerHttpApi) {
|
} else if (type == SrsListenerHttpApi) {
|
||||||
*pconn = new SrsHttpApi(this, stfd, http_api_mux, ip);
|
*pr = new SrsHttpApi(false, this, stfd, http_api_mux, ip, port);
|
||||||
|
} else if (type == SrsListenerHttpsApi) {
|
||||||
|
*pr = new SrsHttpApi(true, this, stfd, http_api_mux, ip, port);
|
||||||
} else if (type == SrsListenerHttpStream) {
|
} else if (type == SrsListenerHttpStream) {
|
||||||
*pconn = new SrsResponseOnlyHttpConn(this, stfd, http_server, ip);
|
*pr = new SrsResponseOnlyHttpConn(false, this, stfd, http_server, ip, port);
|
||||||
|
} else if (type == SrsListenerHttpsStream) {
|
||||||
|
*pr = new SrsResponseOnlyHttpConn(true, this, stfd, http_server, ip, port);
|
||||||
} else {
|
} else {
|
||||||
srs_warn("close for no service handler. fd=%d, ip=%s", fd, ip.c_str());
|
srs_warn("close for no service handler. fd=%d, ip=%s:%d", fd, ip.c_str(), port);
|
||||||
srs_close_stfd(stfd);
|
srs_close_stfd(stfd);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -1574,24 +1691,13 @@ srs_error_t SrsServer::fd2conn(SrsListenerType type, srs_netfd_t stfd, SrsConnec
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SrsServer::remove(ISrsConnection* c)
|
void SrsServer::remove(ISrsResource* c)
|
||||||
{
|
{
|
||||||
SrsConnection* conn = dynamic_cast<SrsConnection*>(c);
|
ISrsStartableConneciton* conn = dynamic_cast<ISrsStartableConneciton*>(c);
|
||||||
std::vector<SrsConnection*>::iterator it = std::find(conns.begin(), conns.end(), conn);
|
|
||||||
|
|
||||||
// removed by destroy, ignore.
|
|
||||||
if (it == conns.end()) {
|
|
||||||
srs_warn("server moved connection, ignore.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
conns.erase(it);
|
|
||||||
|
|
||||||
srs_info("conn removed. conns=%d", (int)conns.size());
|
|
||||||
|
|
||||||
SrsStatistic* stat = SrsStatistic::instance();
|
SrsStatistic* stat = SrsStatistic::instance();
|
||||||
stat->kbps_add_delta(conn);
|
stat->kbps_add_delta(c->get_id(), conn);
|
||||||
stat->on_disconnect(conn->srs_id());
|
stat->on_disconnect(c->get_id());
|
||||||
|
|
||||||
// use manager to free it async.
|
// use manager to free it async.
|
||||||
conn_manager->remove(c);
|
conn_manager->remove(c);
|
||||||
|
@ -1660,12 +1766,17 @@ srs_error_t SrsServer::on_reload_http_api_enabled()
|
||||||
return srs_error_wrap(err, "reload http_api");
|
return srs_error_wrap(err, "reload http_api");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((err = listen_https_api()) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "reload https_api");
|
||||||
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsServer::on_reload_http_api_disabled()
|
srs_error_t SrsServer::on_reload_http_api_disabled()
|
||||||
{
|
{
|
||||||
close_listeners(SrsListenerHttpApi);
|
close_listeners(SrsListenerHttpApi);
|
||||||
|
close_listeners(SrsListenerHttpsApi);
|
||||||
return srs_success;
|
return srs_success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1677,12 +1788,17 @@ srs_error_t SrsServer::on_reload_http_stream_enabled()
|
||||||
return srs_error_wrap(err, "reload http_stream enabled");
|
return srs_error_wrap(err, "reload http_stream enabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((err = listen_https_stream()) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "reload https_stream enabled");
|
||||||
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsServer::on_reload_http_stream_disabled()
|
srs_error_t SrsServer::on_reload_http_stream_disabled()
|
||||||
{
|
{
|
||||||
close_listeners(SrsListenerHttpStream);
|
close_listeners(SrsListenerHttpStream);
|
||||||
|
close_listeners(SrsListenerHttpsStream);
|
||||||
return srs_success;
|
return srs_success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,6 @@
|
||||||
#include <srs_app_gb28181_sip.hpp>
|
#include <srs_app_gb28181_sip.hpp>
|
||||||
|
|
||||||
class SrsServer;
|
class SrsServer;
|
||||||
class SrsConnection;
|
|
||||||
class SrsHttpServeMux;
|
class SrsHttpServeMux;
|
||||||
class SrsHttpServer;
|
class SrsHttpServer;
|
||||||
class SrsIngester;
|
class SrsIngester;
|
||||||
|
@ -53,7 +52,7 @@ class SrsUdpListener;
|
||||||
class SrsTcpListener;
|
class SrsTcpListener;
|
||||||
class SrsAppCasterFlv;
|
class SrsAppCasterFlv;
|
||||||
class SrsRtspCaster;
|
class SrsRtspCaster;
|
||||||
class SrsCoroutineManager;
|
class SrsResourceManager;
|
||||||
class SrsGb28181Caster;
|
class SrsGb28181Caster;
|
||||||
|
|
||||||
|
|
||||||
|
@ -77,6 +76,10 @@ enum SrsListenerType
|
||||||
SrsListenerGb28181RtpMux = 6,
|
SrsListenerGb28181RtpMux = 6,
|
||||||
// UDP gb28181 sip server
|
// UDP gb28181 sip server
|
||||||
SrsListenerGb28181Sip = 7,
|
SrsListenerGb28181Sip = 7,
|
||||||
|
// HTTPS api,
|
||||||
|
SrsListenerHttpsApi = 8,
|
||||||
|
// HTTPS stream,
|
||||||
|
SrsListenerHttpsStream = 9,
|
||||||
};
|
};
|
||||||
|
|
||||||
// A common tcp listener, for RTMP/HTTP server.
|
// A common tcp listener, for RTMP/HTTP server.
|
||||||
|
@ -164,7 +167,7 @@ public:
|
||||||
virtual ~SrsUdpCasterListener();
|
virtual ~SrsUdpCasterListener();
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef SRS_AUTO_GB28181
|
#ifdef SRS_GB28181
|
||||||
|
|
||||||
// A UDP gb28181 listener, for sip and rtp stream mux server.
|
// A UDP gb28181 listener, for sip and rtp stream mux server.
|
||||||
class SrsGb28181Listener : public SrsUdpStreamListener
|
class SrsGb28181Listener : public SrsUdpStreamListener
|
||||||
|
@ -174,6 +177,21 @@ public:
|
||||||
virtual ~SrsGb28181Listener();
|
virtual ~SrsGb28181Listener();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class SrsGb28181TcpListener : virtual public SrsListener, virtual public ISrsTcpHandler
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
SrsTcpListener* listener;
|
||||||
|
SrsGb28181Caster* caster;
|
||||||
|
public:
|
||||||
|
SrsGb28181TcpListener(SrsServer* svr, SrsListenerType t, SrsConfDirective* c);
|
||||||
|
virtual ~SrsGb28181TcpListener();
|
||||||
|
public:
|
||||||
|
virtual srs_error_t listen(std::string i, int p);
|
||||||
|
// Interface ISrsTcpHandler
|
||||||
|
public:
|
||||||
|
virtual srs_error_t on_tcp_client(srs_netfd_t stfd);
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Convert signal to io,
|
// Convert signal to io,
|
||||||
|
@ -236,26 +254,27 @@ public:
|
||||||
virtual srs_error_t on_cycle() = 0;
|
virtual srs_error_t on_cycle() = 0;
|
||||||
// Callback the handler when got client.
|
// Callback the handler when got client.
|
||||||
virtual srs_error_t on_accept_client(int max, int cur) = 0;
|
virtual srs_error_t on_accept_client(int max, int cur) = 0;
|
||||||
|
// Callback for logrotate.
|
||||||
|
virtual void on_logrotate() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: FIXME: Rename to SrsLiveServer.
|
||||||
// SRS RTMP server, initialize and listen, start connection service thread, destroy client.
|
// SRS RTMP server, initialize and listen, start connection service thread, destroy client.
|
||||||
class SrsServer : virtual public ISrsReloadHandler, virtual public ISrsSourceHandler, virtual public IConnectionManager
|
class SrsServer : virtual public ISrsReloadHandler, virtual public ISrsSourceHandler, virtual public ISrsResourceManager
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
// TODO: FIXME: rename to http_api
|
// TODO: FIXME: Extract an HttpApiServer.
|
||||||
SrsHttpServeMux* http_api_mux;
|
SrsHttpServeMux* http_api_mux;
|
||||||
SrsHttpServer* http_server;
|
SrsHttpServer* http_server;
|
||||||
SrsHttpHeartbeat* http_heartbeat;
|
SrsHttpHeartbeat* http_heartbeat;
|
||||||
SrsIngester* ingester;
|
SrsIngester* ingester;
|
||||||
SrsCoroutineManager* conn_manager;
|
SrsResourceManager* conn_manager;
|
||||||
private:
|
private:
|
||||||
// The pid file fd, lock the file write when server is running.
|
// The pid file fd, lock the file write when server is running.
|
||||||
// @remark the init.d script should cleanup the pid file, when stop service,
|
// @remark the init.d script should cleanup the pid file, when stop service,
|
||||||
// for the server never delete the file; when system startup, the pid in pid file
|
// for the server never delete the file; when system startup, the pid in pid file
|
||||||
// maybe valid but the process is not SRS, the init.d script will never start server.
|
// maybe valid but the process is not SRS, the init.d script will never start server.
|
||||||
int pid_fd;
|
int pid_fd;
|
||||||
// All connections, connection manager
|
|
||||||
std::vector<SrsConnection*> conns;
|
|
||||||
// All listners, listener manager.
|
// All listners, listener manager.
|
||||||
std::vector<SrsListener*> listeners;
|
std::vector<SrsListener*> listeners;
|
||||||
// Signal manager which convert gignal to io message.
|
// Signal manager which convert gignal to io message.
|
||||||
|
@ -321,9 +340,11 @@ private:
|
||||||
// listen at specified protocol.
|
// listen at specified protocol.
|
||||||
virtual srs_error_t listen_rtmp();
|
virtual srs_error_t listen_rtmp();
|
||||||
virtual srs_error_t listen_http_api();
|
virtual srs_error_t listen_http_api();
|
||||||
|
virtual srs_error_t listen_https_api();
|
||||||
virtual srs_error_t listen_http_stream();
|
virtual srs_error_t listen_http_stream();
|
||||||
|
virtual srs_error_t listen_https_stream();
|
||||||
virtual srs_error_t listen_stream_caster();
|
virtual srs_error_t listen_stream_caster();
|
||||||
#ifdef SRS_AUTO_GB28181
|
#ifdef SRS_GB28181
|
||||||
virtual srs_error_t listen_gb28181_sip(SrsConfDirective* c);
|
virtual srs_error_t listen_gb28181_sip(SrsConfDirective* c);
|
||||||
#endif
|
#endif
|
||||||
// Close the listeners for specified type,
|
// Close the listeners for specified type,
|
||||||
|
@ -341,13 +362,13 @@ public:
|
||||||
// TODO: FIXME: Fetch from hybrid server manager.
|
// TODO: FIXME: Fetch from hybrid server manager.
|
||||||
virtual SrsHttpServeMux* api_server();
|
virtual SrsHttpServeMux* api_server();
|
||||||
private:
|
private:
|
||||||
virtual srs_error_t fd2conn(SrsListenerType type, srs_netfd_t stfd, SrsConnection** pconn);
|
virtual srs_error_t fd_to_resource(SrsListenerType type, srs_netfd_t stfd, ISrsStartableConneciton** pr);
|
||||||
// Interface IConnectionManager
|
// Interface ISrsResourceManager
|
||||||
public:
|
public:
|
||||||
// A callback for connection to remove itself.
|
// A callback for connection to remove itself.
|
||||||
// When connection thread cycle terminated, callback this to delete connection.
|
// When connection thread cycle terminated, callback this to delete connection.
|
||||||
// @see SrsConnection.on_thread_stop().
|
// @see SrsTcpConnection.on_thread_stop().
|
||||||
virtual void remove(ISrsConnection* c);
|
virtual void remove(ISrsResource* c);
|
||||||
// Interface ISrsReloadHandler.
|
// Interface ISrsReloadHandler.
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t on_reload_listen();
|
virtual srs_error_t on_reload_listen();
|
||||||
|
|
|
@ -31,7 +31,7 @@ using namespace std;
|
||||||
#include <srs_rtmp_stack.hpp>
|
#include <srs_rtmp_stack.hpp>
|
||||||
#include <srs_protocol_amf0.hpp>
|
#include <srs_protocol_amf0.hpp>
|
||||||
#include <srs_kernel_codec.hpp>
|
#include <srs_kernel_codec.hpp>
|
||||||
#include <srs_kernel_rtp.hpp>
|
#include <srs_kernel_rtc_rtp.hpp>
|
||||||
#include <srs_app_hls.hpp>
|
#include <srs_app_hls.hpp>
|
||||||
#include <srs_app_forward.hpp>
|
#include <srs_app_forward.hpp>
|
||||||
#include <srs_app_config.hpp>
|
#include <srs_app_config.hpp>
|
||||||
|
@ -50,9 +50,7 @@ using namespace std;
|
||||||
#include <srs_app_ng_exec.hpp>
|
#include <srs_app_ng_exec.hpp>
|
||||||
#include <srs_app_dash.hpp>
|
#include <srs_app_dash.hpp>
|
||||||
#include <srs_protocol_format.hpp>
|
#include <srs_protocol_format.hpp>
|
||||||
#ifdef SRS_AUTO_RTC
|
#include <srs_app_rtc_source.hpp>
|
||||||
#include <srs_app_rtc.hpp>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define CONST_MAX_JITTER_MS 250
|
#define CONST_MAX_JITTER_MS 250
|
||||||
#define CONST_MAX_JITTER_MS_NEG -250
|
#define CONST_MAX_JITTER_MS_NEG -250
|
||||||
|
@ -68,7 +66,7 @@ using namespace std;
|
||||||
// the time to cleanup source.
|
// the time to cleanup source.
|
||||||
#define SRS_SOURCE_CLEANUP (30 * SRS_UTIME_SECONDS)
|
#define SRS_SOURCE_CLEANUP (30 * SRS_UTIME_SECONDS)
|
||||||
|
|
||||||
int _srs_time_jitter_string2int(std::string time_jitter)
|
int srs_time_jitter_string2int(std::string time_jitter)
|
||||||
{
|
{
|
||||||
if (time_jitter == "full") {
|
if (time_jitter == "full") {
|
||||||
return SrsRtmpJitterAlgorithmFULL;
|
return SrsRtmpJitterAlgorithmFULL;
|
||||||
|
@ -269,18 +267,12 @@ void SrsMessageQueue::set_queue_size(srs_utime_t queue_size)
|
||||||
max_queue_size = queue_size;
|
max_queue_size = queue_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsMessageQueue::enqueue(SrsSharedPtrMessage* msg, bool* is_overflow, bool pass_timestamp)
|
srs_error_t SrsMessageQueue::enqueue(SrsSharedPtrMessage* msg, bool* is_overflow)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
msgs.push_back(msg);
|
msgs.push_back(msg);
|
||||||
|
|
||||||
// For RTC, we never care about the timestamp and duration, so we never shrink queue here,
|
|
||||||
// but we will drop messages in each consumer coroutine.
|
|
||||||
if (pass_timestamp) {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (msg->is_av()) {
|
if (msg->is_av()) {
|
||||||
if (av_start_time == -1) {
|
if (av_start_time == -1) {
|
||||||
av_start_time = srs_utime_t(msg->timestamp * SRS_UTIME_MILLISECONDS);
|
av_start_time = srs_utime_t(msg->timestamp * SRS_UTIME_MILLISECONDS);
|
||||||
|
@ -289,6 +281,10 @@ srs_error_t SrsMessageQueue::enqueue(SrsSharedPtrMessage* msg, bool* is_overflow
|
||||||
av_end_time = srs_utime_t(msg->timestamp * SRS_UTIME_MILLISECONDS);
|
av_end_time = srs_utime_t(msg->timestamp * SRS_UTIME_MILLISECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (max_queue_size <= 0) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
while (av_end_time - av_start_time > max_queue_size) {
|
while (av_end_time - av_start_time > max_queue_size) {
|
||||||
// notice the caller queue already overflow and shrinked.
|
// notice the caller queue already overflow and shrinked.
|
||||||
if (is_overflow) {
|
if (is_overflow) {
|
||||||
|
@ -301,7 +297,7 @@ srs_error_t SrsMessageQueue::enqueue(SrsSharedPtrMessage* msg, bool* is_overflow
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsMessageQueue::dump_packets(int max_count, SrsSharedPtrMessage** pmsgs, int& count, bool pass_timestamp)
|
srs_error_t SrsMessageQueue::dump_packets(int max_count, SrsSharedPtrMessage** pmsgs, int& count)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
@ -316,12 +312,8 @@ srs_error_t SrsMessageQueue::dump_packets(int max_count, SrsSharedPtrMessage** p
|
||||||
SrsSharedPtrMessage** omsgs = msgs.data();
|
SrsSharedPtrMessage** omsgs = msgs.data();
|
||||||
memcpy(pmsgs, omsgs, count * sizeof(SrsSharedPtrMessage*));
|
memcpy(pmsgs, omsgs, count * sizeof(SrsSharedPtrMessage*));
|
||||||
|
|
||||||
// For RTC, we enable pass_timestamp mode, which never care about the timestamp and duration,
|
SrsSharedPtrMessage* last = omsgs[count - 1];
|
||||||
// so we do not have to update the start time here.
|
av_start_time = srs_utime_t(last->timestamp * SRS_UTIME_MILLISECONDS);
|
||||||
if (!pass_timestamp) {
|
|
||||||
SrsSharedPtrMessage* last = omsgs[count - 1];
|
|
||||||
av_start_time = srs_utime_t(last->timestamp * SRS_UTIME_MILLISECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count >= nb_msgs) {
|
if (count >= nb_msgs) {
|
||||||
// the pmsgs is big enough and clear msgs at most time.
|
// the pmsgs is big enough and clear msgs at most time.
|
||||||
|
@ -426,10 +418,9 @@ ISrsWakable::~ISrsWakable()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsConsumer::SrsConsumer(SrsSource* s, SrsConnection* c)
|
SrsConsumer::SrsConsumer(SrsSource* s)
|
||||||
{
|
{
|
||||||
source = s;
|
source = s;
|
||||||
conn = c;
|
|
||||||
paused = false;
|
paused = false;
|
||||||
jitter = new SrsRtmpJitter();
|
jitter = new SrsRtmpJitter();
|
||||||
queue = new SrsMessageQueue();
|
queue = new SrsMessageQueue();
|
||||||
|
@ -441,8 +432,6 @@ SrsConsumer::SrsConsumer(SrsSource* s, SrsConnection* c)
|
||||||
mw_duration = 0;
|
mw_duration = 0;
|
||||||
mw_waiting = false;
|
mw_waiting = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
pass_timestamp = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsConsumer::~SrsConsumer()
|
SrsConsumer::~SrsConsumer()
|
||||||
|
@ -477,33 +466,19 @@ srs_error_t SrsConsumer::enqueue(SrsSharedPtrMessage* shared_msg, bool atc, SrsR
|
||||||
|
|
||||||
SrsSharedPtrMessage* msg = shared_msg->copy();
|
SrsSharedPtrMessage* msg = shared_msg->copy();
|
||||||
|
|
||||||
// For RTC, we enable pass_timestamp mode, which never correct or depends on monotonic increasing of
|
if (!atc) {
|
||||||
// timestamp. And in RTC, the audio and video timebase can be different, so we ignore time_jitter here.
|
|
||||||
if (!pass_timestamp && !atc) {
|
|
||||||
if ((err = jitter->correct(msg, ag)) != srs_success) {
|
if ((err = jitter->correct(msg, ag)) != srs_success) {
|
||||||
return srs_error_wrap(err, "consume message");
|
return srs_error_wrap(err, "consume message");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put message in queue, here we may enable pass_timestamp mode.
|
if ((err = queue->enqueue(msg, NULL)) != srs_success) {
|
||||||
if ((err = queue->enqueue(msg, NULL, pass_timestamp)) != srs_success) {
|
|
||||||
return srs_error_wrap(err, "enqueue message");
|
return srs_error_wrap(err, "enqueue message");
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SRS_PERF_QUEUE_COND_WAIT
|
#ifdef SRS_PERF_QUEUE_COND_WAIT
|
||||||
// fire the mw when msgs is enough.
|
// fire the mw when msgs is enough.
|
||||||
if (mw_waiting) {
|
if (mw_waiting) {
|
||||||
// For RTC, we use pass_timestamp mode, we don't care about the timestamp in queue,
|
|
||||||
// so we only check the messages in queue.
|
|
||||||
if (pass_timestamp) {
|
|
||||||
if (queue->size() > mw_min_msgs) {
|
|
||||||
srs_cond_signal(mw_wait);
|
|
||||||
mw_waiting = false;
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For RTMP, we wait for messages and duration.
|
// For RTMP, we wait for messages and duration.
|
||||||
srs_utime_t duration = queue->duration();
|
srs_utime_t duration = queue->duration();
|
||||||
bool match_min_msgs = queue->size() > mw_min_msgs;
|
bool match_min_msgs = queue->size() > mw_min_msgs;
|
||||||
|
@ -544,7 +519,7 @@ srs_error_t SrsConsumer::dump_packets(SrsMessageArray* msgs, int& count)
|
||||||
count = 0;
|
count = 0;
|
||||||
|
|
||||||
if (should_update_source_id) {
|
if (should_update_source_id) {
|
||||||
srs_trace("update source_id=%d[%d]", source->source_id(), source->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;
|
should_update_source_id = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -554,7 +529,7 @@ srs_error_t SrsConsumer::dump_packets(SrsMessageArray* msgs, int& count)
|
||||||
}
|
}
|
||||||
|
|
||||||
// pump msgs from queue.
|
// pump msgs from queue.
|
||||||
if ((err = queue->dump_packets(max, msgs->msgs, count, pass_timestamp)) != srs_success) {
|
if ((err = queue->dump_packets(max, msgs->msgs, count)) != srs_success) {
|
||||||
return srs_error_wrap(err, "dump packets");
|
return srs_error_wrap(err, "dump packets");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -843,58 +818,6 @@ SrsSharedPtrMessage* SrsMixQueue::pop()
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SRS_AUTO_RTC
|
|
||||||
SrsRtpPacketQueue::SrsRtpPacketQueue()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
SrsRtpPacketQueue::~SrsRtpPacketQueue()
|
|
||||||
{
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SrsRtpPacketQueue::clear()
|
|
||||||
{
|
|
||||||
map<uint16_t, SrsRtpSharedPacket*>::iterator iter = pkt_queue.begin();
|
|
||||||
while (iter != pkt_queue.end()) {
|
|
||||||
srs_freep(iter->second);
|
|
||||||
pkt_queue.erase(iter++);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SrsRtpPacketQueue::push(std::vector<SrsRtpSharedPacket*>& pkts)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < (int)pkts.size(); ++i) {
|
|
||||||
insert(pkts[i]->rtp_header.get_sequence(), pkts[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SrsRtpPacketQueue::insert(const uint16_t& sequence, SrsRtpSharedPacket* pkt)
|
|
||||||
{
|
|
||||||
pkt_queue.insert(make_pair(sequence, pkt->copy()));
|
|
||||||
// TODO: 3000 is magic number.
|
|
||||||
if (pkt_queue.size() >= 3000) {
|
|
||||||
srs_freep(pkt_queue.begin()->second);
|
|
||||||
pkt_queue.erase(pkt_queue.begin());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SrsRtpSharedPacket* SrsRtpPacketQueue::find(const uint16_t& sequence)
|
|
||||||
{
|
|
||||||
if (pkt_queue.empty()) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
SrsRtpSharedPacket* pkt = NULL;
|
|
||||||
map<uint16_t, SrsRtpSharedPacket*>::iterator iter = pkt_queue.find(sequence);
|
|
||||||
if (iter != pkt_queue.end()) {
|
|
||||||
pkt = iter->second->copy();
|
|
||||||
}
|
|
||||||
|
|
||||||
return pkt;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
SrsOriginHub::SrsOriginHub()
|
SrsOriginHub::SrsOriginHub()
|
||||||
{
|
{
|
||||||
source = NULL;
|
source = NULL;
|
||||||
|
@ -905,10 +828,7 @@ SrsOriginHub::SrsOriginHub()
|
||||||
dash = new SrsDash();
|
dash = new SrsDash();
|
||||||
dvr = new SrsDvr();
|
dvr = new SrsDvr();
|
||||||
encoder = new SrsEncoder();
|
encoder = new SrsEncoder();
|
||||||
#ifdef SRS_AUTO_RTC
|
#ifdef SRS_HDS
|
||||||
rtc = new SrsRtc();
|
|
||||||
#endif
|
|
||||||
#ifdef SRS_AUTO_HDS
|
|
||||||
hds = new SrsHds();
|
hds = new SrsHds();
|
||||||
#endif
|
#endif
|
||||||
ng_exec = new SrsNgExec();
|
ng_exec = new SrsNgExec();
|
||||||
|
@ -936,7 +856,7 @@ SrsOriginHub::~SrsOriginHub()
|
||||||
srs_freep(dash);
|
srs_freep(dash);
|
||||||
srs_freep(dvr);
|
srs_freep(dvr);
|
||||||
srs_freep(encoder);
|
srs_freep(encoder);
|
||||||
#ifdef SRS_AUTO_HDS
|
#ifdef SRS_HDS
|
||||||
srs_freep(hds);
|
srs_freep(hds);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -952,12 +872,6 @@ srs_error_t SrsOriginHub::initialize(SrsSource* s, SrsRequest* r)
|
||||||
return srs_error_wrap(err, "format initialize");
|
return srs_error_wrap(err, "format initialize");
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SRS_AUTO_RTC
|
|
||||||
if ((err = rtc->initialize(this, req)) != srs_success) {
|
|
||||||
return srs_error_wrap(err, "rtc initialize");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if ((err = hls->initialize(this, req)) != srs_success) {
|
if ((err = hls->initialize(this, req)) != srs_success) {
|
||||||
return srs_error_wrap(err, "hls initialize");
|
return srs_error_wrap(err, "hls initialize");
|
||||||
}
|
}
|
||||||
|
@ -1030,6 +944,7 @@ srs_error_t SrsOriginHub::on_audio(SrsSharedPtrMessage* shared_audio)
|
||||||
|
|
||||||
SrsSharedPtrMessage* msg = shared_audio;
|
SrsSharedPtrMessage* msg = shared_audio;
|
||||||
|
|
||||||
|
// TODO: FIXME: Support parsing OPUS for RTC.
|
||||||
if ((err = format->on_audio(msg)) != srs_success) {
|
if ((err = format->on_audio(msg)) != srs_success) {
|
||||||
return srs_error_wrap(err, "format consume audio");
|
return srs_error_wrap(err, "format consume audio");
|
||||||
}
|
}
|
||||||
|
@ -1056,14 +971,6 @@ srs_error_t SrsOriginHub::on_audio(SrsSharedPtrMessage* shared_audio)
|
||||||
srs_flv_srates[c->sound_rate]);
|
srs_flv_srates[c->sound_rate]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SRS_AUTO_RTC
|
|
||||||
if ((err = rtc->on_audio(msg, format)) != srs_success) {
|
|
||||||
srs_warn("rtc: ignore audio error %s", srs_error_desc(err).c_str());
|
|
||||||
srs_error_reset(err);
|
|
||||||
rtc->on_unpublish();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if ((err = hls->on_audio(msg, format)) != srs_success) {
|
if ((err = hls->on_audio(msg, format)) != srs_success) {
|
||||||
// apply the error strategy for hls.
|
// apply the error strategy for hls.
|
||||||
// @see https://github.com/ossrs/srs/issues/264
|
// @see https://github.com/ossrs/srs/issues/264
|
||||||
|
@ -1095,7 +1002,7 @@ srs_error_t SrsOriginHub::on_audio(SrsSharedPtrMessage* shared_audio)
|
||||||
dvr->on_unpublish();
|
dvr->on_unpublish();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SRS_AUTO_HDS
|
#ifdef SRS_HDS
|
||||||
if ((err = hds->on_audio(msg)) != srs_success) {
|
if ((err = hds->on_audio(msg)) != srs_success) {
|
||||||
srs_warn("hds: ignore audio error %s", srs_error_desc(err).c_str());
|
srs_warn("hds: ignore audio error %s", srs_error_desc(err).c_str());
|
||||||
srs_error_reset(err);
|
srs_error_reset(err);
|
||||||
|
@ -1157,21 +1064,6 @@ srs_error_t SrsOriginHub::on_video(SrsSharedPtrMessage* shared_video, bool is_se
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SRS_AUTO_RTC
|
|
||||||
// Parse RTMP message to RTP packets, in FU-A if too large.
|
|
||||||
if ((err = rtc->on_video(msg, format)) != srs_success) {
|
|
||||||
// TODO: We should support more strategies.
|
|
||||||
srs_warn("rtc: ignore video error %s", srs_error_desc(err).c_str());
|
|
||||||
srs_error_reset(err);
|
|
||||||
rtc->on_unpublish();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: FIXME: Refactor to move to rtp?
|
|
||||||
// Save the RTP packets for find_rtp_packet() to rtx or restore it.
|
|
||||||
// TODO: FIXME: Remove dead code.
|
|
||||||
//source->rtp_queue->push(msg->rtp_packets);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if ((err = hls->on_video(msg, format)) != srs_success) {
|
if ((err = hls->on_video(msg, format)) != srs_success) {
|
||||||
// TODO: We should support more strategies.
|
// TODO: We should support more strategies.
|
||||||
// apply the error strategy for hls.
|
// apply the error strategy for hls.
|
||||||
|
@ -1204,7 +1096,7 @@ srs_error_t SrsOriginHub::on_video(SrsSharedPtrMessage* shared_video, bool is_se
|
||||||
dvr->on_unpublish();
|
dvr->on_unpublish();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SRS_AUTO_HDS
|
#ifdef SRS_HDS
|
||||||
if ((err = hds->on_video(msg)) != srs_success) {
|
if ((err = hds->on_video(msg)) != srs_success) {
|
||||||
srs_warn("hds: ignore video error %s", srs_error_desc(err).c_str());
|
srs_warn("hds: ignore video error %s", srs_error_desc(err).c_str());
|
||||||
srs_error_reset(err);
|
srs_error_reset(err);
|
||||||
|
@ -1240,12 +1132,6 @@ srs_error_t SrsOriginHub::on_publish()
|
||||||
return srs_error_wrap(err, "encoder publish");
|
return srs_error_wrap(err, "encoder publish");
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SRS_AUTO_RTC
|
|
||||||
if ((err = rtc->on_publish()) != srs_success) {
|
|
||||||
return srs_error_wrap(err, "rtc publish");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if ((err = hls->on_publish()) != srs_success) {
|
if ((err = hls->on_publish()) != srs_success) {
|
||||||
return srs_error_wrap(err, "hls publish");
|
return srs_error_wrap(err, "hls publish");
|
||||||
}
|
}
|
||||||
|
@ -1259,7 +1145,7 @@ srs_error_t SrsOriginHub::on_publish()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: FIXME: use initialize to set req.
|
// TODO: FIXME: use initialize to set req.
|
||||||
#ifdef SRS_AUTO_HDS
|
#ifdef SRS_HDS
|
||||||
if ((err = hds->on_publish(req)) != srs_success) {
|
if ((err = hds->on_publish(req)) != srs_success) {
|
||||||
return srs_error_wrap(err, "hds publish");
|
return srs_error_wrap(err, "hds publish");
|
||||||
}
|
}
|
||||||
|
@ -1283,14 +1169,11 @@ void SrsOriginHub::on_unpublish()
|
||||||
destroy_forwarders();
|
destroy_forwarders();
|
||||||
|
|
||||||
encoder->on_unpublish();
|
encoder->on_unpublish();
|
||||||
#ifdef SRS_AUTO_RTC
|
|
||||||
rtc->on_unpublish();
|
|
||||||
#endif
|
|
||||||
hls->on_unpublish();
|
hls->on_unpublish();
|
||||||
dash->on_unpublish();
|
dash->on_unpublish();
|
||||||
dvr->on_unpublish();
|
dvr->on_unpublish();
|
||||||
|
|
||||||
#ifdef SRS_AUTO_HDS
|
#ifdef SRS_HDS
|
||||||
hds->on_unpublish();
|
hds->on_unpublish();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -1479,7 +1362,7 @@ srs_error_t SrsOriginHub::on_reload_vhost_hds(string vhost)
|
||||||
|
|
||||||
// TODO: FIXME: maybe should ignore when publish already stopped?
|
// TODO: FIXME: maybe should ignore when publish already stopped?
|
||||||
|
|
||||||
#ifdef SRS_AUTO_HDS
|
#ifdef SRS_HDS
|
||||||
hds->on_unpublish();
|
hds->on_unpublish();
|
||||||
|
|
||||||
// Don't start HDS when source is not active.
|
// Don't start HDS when source is not active.
|
||||||
|
@ -1824,6 +1707,7 @@ srs_error_t SrsSourceManager::fetch_or_create(SrsRequest* r, ISrsSourceHandler*
|
||||||
|
|
||||||
// Use lock to protect coroutine switch.
|
// Use lock to protect coroutine switch.
|
||||||
// @bug https://github.com/ossrs/srs/issues/1230
|
// @bug https://github.com/ossrs/srs/issues/1230
|
||||||
|
// TODO: FIXME: Use smaller lock.
|
||||||
SrsLocker(lock);
|
SrsLocker(lock);
|
||||||
|
|
||||||
SrsSource* source = NULL;
|
SrsSource* source = NULL;
|
||||||
|
@ -1838,17 +1722,41 @@ srs_error_t SrsSourceManager::fetch_or_create(SrsRequest* r, ISrsSourceHandler*
|
||||||
// should always not exists for create a source.
|
// should always not exists for create a source.
|
||||||
srs_assert (pool.find(stream_url) == pool.end());
|
srs_assert (pool.find(stream_url) == pool.end());
|
||||||
|
|
||||||
|
#ifdef SRS_RTC
|
||||||
|
bool rtc_server_enabled = _srs_config->get_rtc_server_enabled();
|
||||||
|
bool rtc_enabled = _srs_config->get_rtc_enabled(r->vhost);
|
||||||
|
|
||||||
|
// Get the RTC source and bridger.
|
||||||
|
SrsRtcStream* rtc = NULL;
|
||||||
|
if (rtc_server_enabled && rtc_enabled) {
|
||||||
|
if ((err = _srs_rtc_sources->fetch_or_create(r, &rtc)) != srs_success) {
|
||||||
|
err = srs_error_wrap(err, "init rtc %s", r->get_stream_url().c_str());
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
srs_trace("new source, stream_url=%s", stream_url.c_str());
|
srs_trace("new source, stream_url=%s", stream_url.c_str());
|
||||||
|
|
||||||
source = new SrsSource();
|
source = new SrsSource();
|
||||||
if ((err = source->initialize(r, h)) != srs_success) {
|
if ((err = source->initialize(r, h)) != srs_success) {
|
||||||
return srs_error_wrap(err, "init source %s", r->get_stream_url().c_str());
|
err = srs_error_wrap(err, "init source %s", r->get_stream_url().c_str());
|
||||||
|
goto failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef SRS_RTC
|
||||||
|
// If rtc enabled, bridge RTMP source to RTC,
|
||||||
|
// all RTMP packets will be forwarded to RTC source.
|
||||||
|
if (source && rtc) {
|
||||||
|
source->bridge_to(rtc->bridger());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
pool[stream_url] = source;
|
pool[stream_url] = source;
|
||||||
|
|
||||||
*pps = source;
|
*pps = source;
|
||||||
|
return err;
|
||||||
|
|
||||||
|
failed:
|
||||||
|
srs_freep(source);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1883,7 +1791,7 @@ void SrsSourceManager::dispose()
|
||||||
|
|
||||||
srs_error_t SrsSourceManager::cycle()
|
srs_error_t SrsSourceManager::cycle()
|
||||||
{
|
{
|
||||||
int cid = _srs_context->get_id();
|
SrsContextId cid = _srs_context->get_id();
|
||||||
srs_error_t err = do_cycle();
|
srs_error_t err = do_cycle();
|
||||||
_srs_context->set_id(cid);
|
_srs_context->set_id(cid);
|
||||||
|
|
||||||
|
@ -1900,7 +1808,7 @@ srs_error_t SrsSourceManager::do_cycle()
|
||||||
|
|
||||||
// Do cycle source to cleanup components, such as hls dispose.
|
// Do cycle source to cleanup components, such as hls dispose.
|
||||||
if ((err = source->cycle()) != srs_success) {
|
if ((err = source->cycle()) != srs_success) {
|
||||||
return srs_error_wrap(err, "source=%d/%d cycle", source->source_id(), source->pre_source_id());
|
return srs_error_wrap(err, "source=%s/%s cycle", source->source_id().c_str(), source->pre_source_id().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: FIXME: support source cleanup.
|
// TODO: FIXME: support source cleanup.
|
||||||
|
@ -1941,20 +1849,27 @@ void SrsSourceManager::destroy()
|
||||||
pool.clear();
|
pool.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ISrsSourceBridger::ISrsSourceBridger()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ISrsSourceBridger::~ISrsSourceBridger()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
SrsSource::SrsSource()
|
SrsSource::SrsSource()
|
||||||
{
|
{
|
||||||
req = NULL;
|
req = NULL;
|
||||||
jitter_algorithm = SrsRtmpJitterAlgorithmOFF;
|
jitter_algorithm = SrsRtmpJitterAlgorithmOFF;
|
||||||
mix_correct = false;
|
mix_correct = false;
|
||||||
mix_queue = new SrsMixQueue();
|
mix_queue = new SrsMixQueue();
|
||||||
#ifdef SRS_AUTO_RTC
|
|
||||||
rtp_queue = new SrsRtpPacketQueue();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
_can_publish = true;
|
_can_publish = true;
|
||||||
_pre_source_id = _source_id = -1;
|
|
||||||
die_at = 0;
|
die_at = 0;
|
||||||
|
|
||||||
|
handler = NULL;
|
||||||
|
bridger = NULL;
|
||||||
|
|
||||||
play_edge = new SrsPlayEdge();
|
play_edge = new SrsPlayEdge();
|
||||||
publish_edge = new SrsPublishEdge();
|
publish_edge = new SrsPublishEdge();
|
||||||
gop_cache = new SrsGopCache();
|
gop_cache = new SrsGopCache();
|
||||||
|
@ -1979,9 +1894,6 @@ SrsSource::~SrsSource()
|
||||||
srs_freep(hub);
|
srs_freep(hub);
|
||||||
srs_freep(meta);
|
srs_freep(meta);
|
||||||
srs_freep(mix_queue);
|
srs_freep(mix_queue);
|
||||||
#ifdef SRS_AUTO_RTC
|
|
||||||
srs_freep(rtp_queue);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
srs_freep(play_edge);
|
srs_freep(play_edge);
|
||||||
srs_freep(publish_edge);
|
srs_freep(publish_edge);
|
||||||
|
@ -2063,6 +1975,11 @@ srs_error_t SrsSource::initialize(SrsRequest* r, ISrsSourceHandler* h)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SrsSource::bridge_to(ISrsSourceBridger* v)
|
||||||
|
{
|
||||||
|
bridger = v;
|
||||||
|
}
|
||||||
|
|
||||||
srs_error_t SrsSource::on_reload_vhost_play(string vhost)
|
srs_error_t SrsSource::on_reload_vhost_play(string vhost)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
@ -2146,20 +2063,17 @@ srs_error_t SrsSource::on_reload_vhost_play(string vhost)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsSource::on_source_id_changed(int id)
|
srs_error_t SrsSource::on_source_id_changed(SrsContextId id)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
if (_source_id == id) {
|
if (!_source_id.compare(id)) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_pre_source_id == -1) {
|
if (_pre_source_id.empty()) {
|
||||||
_pre_source_id = id;
|
_pre_source_id = id;
|
||||||
} else if (_pre_source_id != _source_id) {
|
|
||||||
_pre_source_id = _source_id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_source_id = id;
|
_source_id = id;
|
||||||
|
|
||||||
// notice all consumer
|
// notice all consumer
|
||||||
|
@ -2172,12 +2086,12 @@ srs_error_t SrsSource::on_source_id_changed(int id)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SrsSource::source_id()
|
SrsContextId SrsSource::source_id()
|
||||||
{
|
{
|
||||||
return _source_id;
|
return _source_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SrsSource::pre_source_id()
|
SrsContextId SrsSource::pre_source_id()
|
||||||
{
|
{
|
||||||
return _pre_source_id;
|
return _pre_source_id;
|
||||||
}
|
}
|
||||||
|
@ -2313,6 +2227,11 @@ srs_error_t SrsSource::on_audio_imp(SrsSharedPtrMessage* msg)
|
||||||
return srs_error_wrap(err, "consume audio");
|
return srs_error_wrap(err, "consume audio");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For bridger to consume the message.
|
||||||
|
if (bridger && (err = bridger->on_audio(msg)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "bridger consume audio");
|
||||||
|
}
|
||||||
|
|
||||||
// copy to all consumer
|
// copy to all consumer
|
||||||
if (!drop_for_reduce) {
|
if (!drop_for_reduce) {
|
||||||
for (int i = 0; i < (int)consumers.size(); i++) {
|
for (int i = 0; i < (int)consumers.size(); i++) {
|
||||||
|
@ -2438,6 +2357,11 @@ srs_error_t SrsSource::on_video_imp(SrsSharedPtrMessage* msg)
|
||||||
return srs_error_wrap(err, "hub consume video");
|
return srs_error_wrap(err, "hub consume video");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For bridger to consume the message.
|
||||||
|
if (bridger && (err = bridger->on_video(msg)) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "bridger consume video");
|
||||||
|
}
|
||||||
|
|
||||||
// copy to all consumer
|
// copy to all consumer
|
||||||
if (!drop_for_reduce) {
|
if (!drop_for_reduce) {
|
||||||
for (int i = 0; i < (int)consumers.size(); i++) {
|
for (int i = 0; i < (int)consumers.size(); i++) {
|
||||||
|
@ -2597,6 +2521,11 @@ srs_error_t SrsSource::on_publish()
|
||||||
if ((err = handler->on_publish(this, req)) != srs_success) {
|
if ((err = handler->on_publish(this, req)) != srs_success) {
|
||||||
return srs_error_wrap(err, "handle publish");
|
return srs_error_wrap(err, "handle publish");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (bridger && (err = bridger->on_publish()) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "bridger publish");
|
||||||
|
}
|
||||||
|
|
||||||
SrsStatistic* stat = SrsStatistic::instance();
|
SrsStatistic* stat = SrsStatistic::instance();
|
||||||
stat->on_stream_publish(req, _source_id);
|
stat->on_stream_publish(req, _source_id);
|
||||||
|
|
||||||
|
@ -2626,27 +2555,50 @@ void SrsSource::on_unpublish()
|
||||||
srs_trace("cleanup when unpublish");
|
srs_trace("cleanup when unpublish");
|
||||||
|
|
||||||
_can_publish = true;
|
_can_publish = true;
|
||||||
_source_id = -1;
|
if (!_source_id.empty()) {
|
||||||
|
_pre_source_id = _source_id;
|
||||||
|
}
|
||||||
|
_source_id = SrsContextId();
|
||||||
|
|
||||||
// notify the handler.
|
// notify the handler.
|
||||||
srs_assert(handler);
|
srs_assert(handler);
|
||||||
SrsStatistic* stat = SrsStatistic::instance();
|
SrsStatistic* stat = SrsStatistic::instance();
|
||||||
stat->on_stream_close(req);
|
stat->on_stream_close(req);
|
||||||
|
|
||||||
handler->on_unpublish(this, req);
|
handler->on_unpublish(this, req);
|
||||||
|
|
||||||
|
if (bridger) {
|
||||||
|
bridger->on_unpublish();
|
||||||
|
}
|
||||||
|
|
||||||
// no consumer, stream is die.
|
// no consumer, stream is die.
|
||||||
if (consumers.empty()) {
|
if (consumers.empty()) {
|
||||||
die_at = srs_get_system_time();
|
die_at = srs_get_system_time();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsSource::create_consumer(SrsConnection* conn, SrsConsumer*& consumer, bool ds, bool dm, bool dg)
|
srs_error_t SrsSource::create_consumer(SrsConsumer*& consumer)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
consumer = new SrsConsumer(this, conn);
|
consumer = new SrsConsumer(this);
|
||||||
consumers.push_back(consumer);
|
consumers.push_back(consumer);
|
||||||
|
|
||||||
|
// for edge, when play edge stream, check the state
|
||||||
|
if (_srs_config->get_vhost_is_edge(req->vhost)) {
|
||||||
|
// notice edge to start for the first client.
|
||||||
|
if ((err = play_edge->on_client_play()) != srs_success) {
|
||||||
|
return srs_error_wrap(err, "play edge");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsSource::consumer_dumps(SrsConsumer* consumer, bool ds, bool dm, bool dg)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
srs_utime_t queue_size = _srs_config->get_queue_length(req->vhost);
|
srs_utime_t queue_size = _srs_config->get_queue_length(req->vhost);
|
||||||
consumer->set_queue_size(queue_size);
|
consumer->set_queue_size(queue_size);
|
||||||
|
|
||||||
|
@ -2683,14 +2635,6 @@ srs_error_t SrsSource::create_consumer(SrsConnection* conn, SrsConsumer*& consum
|
||||||
srs_trace("create consumer, active=%d, ignore gop cache, jitter=%d", hub->active(), jitter_algorithm);
|
srs_trace("create consumer, active=%d, ignore gop cache, jitter=%d", hub->active(), jitter_algorithm);
|
||||||
}
|
}
|
||||||
|
|
||||||
// for edge, when play edge stream, check the state
|
|
||||||
if (_srs_config->get_vhost_is_edge(req->vhost)) {
|
|
||||||
// notice edge to start for the first client.
|
|
||||||
if ((err = play_edge->on_client_play()) != srs_success) {
|
|
||||||
return srs_error_wrap(err, "play edge");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2739,14 +2683,3 @@ string SrsSource::get_curr_origin()
|
||||||
return play_edge->get_curr_origin();
|
return play_edge->get_curr_origin();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SRS_AUTO_RTC
|
|
||||||
SrsRtpSharedPacket* SrsSource::find_rtp_packet(const uint16_t& seq)
|
|
||||||
{
|
|
||||||
return rtp_queue->find(seq);
|
|
||||||
}
|
|
||||||
|
|
||||||
SrsMetaCache* SrsSource::cached_meta()
|
|
||||||
{
|
|
||||||
return meta;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -51,7 +51,6 @@ class SrsRtmpServer;
|
||||||
class SrsEdgeProxyContext;
|
class SrsEdgeProxyContext;
|
||||||
class SrsMessageArray;
|
class SrsMessageArray;
|
||||||
class SrsNgExec;
|
class SrsNgExec;
|
||||||
class SrsConnection;
|
|
||||||
class SrsMessageHeader;
|
class SrsMessageHeader;
|
||||||
class SrsHls;
|
class SrsHls;
|
||||||
class SrsRtc;
|
class SrsRtc;
|
||||||
|
@ -59,10 +58,9 @@ class SrsDvr;
|
||||||
class SrsDash;
|
class SrsDash;
|
||||||
class SrsEncoder;
|
class SrsEncoder;
|
||||||
class SrsBuffer;
|
class SrsBuffer;
|
||||||
#ifdef SRS_AUTO_HDS
|
#ifdef SRS_HDS
|
||||||
class SrsHds;
|
class SrsHds;
|
||||||
#endif
|
#endif
|
||||||
class SrsRtpSharedPacket;
|
|
||||||
|
|
||||||
// The time jitter algorithm:
|
// The time jitter algorithm:
|
||||||
// 1. full, to ensure stream start at zero, and ensure stream monotonically increasing.
|
// 1. full, to ensure stream start at zero, and ensure stream monotonically increasing.
|
||||||
|
@ -74,7 +72,7 @@ enum SrsRtmpJitterAlgorithm
|
||||||
SrsRtmpJitterAlgorithmZERO,
|
SrsRtmpJitterAlgorithmZERO,
|
||||||
SrsRtmpJitterAlgorithmOFF
|
SrsRtmpJitterAlgorithmOFF
|
||||||
};
|
};
|
||||||
int _srs_time_jitter_string2int(std::string time_jitter);
|
int srs_time_jitter_string2int(std::string time_jitter);
|
||||||
|
|
||||||
// Time jitter detect and correct, to ensure the rtmp stream is monotonically.
|
// Time jitter detect and correct, to ensure the rtmp stream is monotonically.
|
||||||
class SrsRtmpJitter
|
class SrsRtmpJitter
|
||||||
|
@ -151,13 +149,12 @@ public:
|
||||||
// Enqueue the message, the timestamp always monotonically.
|
// Enqueue the message, the timestamp always monotonically.
|
||||||
// @param msg, the msg to enqueue, user never free it whatever the return code.
|
// @param msg, the msg to enqueue, user never free it whatever the return code.
|
||||||
// @param is_overflow, whether overflow and shrinked. NULL to ignore.
|
// @param is_overflow, whether overflow and shrinked. NULL to ignore.
|
||||||
// @remark If pass_timestamp, we never shrink and never care about the timestamp or duration.
|
virtual srs_error_t enqueue(SrsSharedPtrMessage* msg, bool* is_overflow = NULL);
|
||||||
virtual srs_error_t enqueue(SrsSharedPtrMessage* msg, bool* is_overflow = NULL, bool pass_timestamp = false);
|
|
||||||
// Get packets in consumer queue.
|
// Get packets in consumer queue.
|
||||||
// @pmsgs SrsSharedPtrMessage*[], used to store the msgs, user must alloc it.
|
// @pmsgs SrsSharedPtrMessage*[], used to store the msgs, user must alloc it.
|
||||||
// @count the count in array, output param.
|
// @count the count in array, output param.
|
||||||
// @max_count the max count to dequeue, must be positive.
|
// @max_count the max count to dequeue, must be positive.
|
||||||
virtual srs_error_t dump_packets(int max_count, SrsSharedPtrMessage** pmsgs, int& count, bool pass_timestamp = false);
|
virtual srs_error_t dump_packets(int max_count, SrsSharedPtrMessage** pmsgs, int& count);
|
||||||
// Dumps packets to consumer, use specified args.
|
// Dumps packets to consumer, use specified args.
|
||||||
// @remark the atc/tba/tbv/ag are same to SrsConsumer.enqueue().
|
// @remark the atc/tba/tbv/ag are same to SrsConsumer.enqueue().
|
||||||
virtual srs_error_t dump_packets(SrsConsumer* consumer, bool atc, SrsRtmpJitterAlgorithm ag);
|
virtual srs_error_t dump_packets(SrsConsumer* consumer, bool atc, SrsRtmpJitterAlgorithm ag);
|
||||||
|
@ -185,14 +182,12 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
// The consumer for SrsSource, that is a play client.
|
// The consumer for SrsSource, that is a play client.
|
||||||
class SrsConsumer : public ISrsWakable
|
class SrsConsumer : virtual public ISrsWakable
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
SrsRtmpJitter* jitter;
|
SrsRtmpJitter* jitter;
|
||||||
SrsSource* source;
|
SrsSource* source;
|
||||||
SrsMessageQueue* queue;
|
SrsMessageQueue* queue;
|
||||||
// The owner connection for debug, maybe NULL.
|
|
||||||
SrsConnection* conn;
|
|
||||||
bool paused;
|
bool paused;
|
||||||
// when source id changed, notice all consumers
|
// when source id changed, notice all consumers
|
||||||
bool should_update_source_id;
|
bool should_update_source_id;
|
||||||
|
@ -204,17 +199,10 @@ private:
|
||||||
int mw_min_msgs;
|
int mw_min_msgs;
|
||||||
srs_utime_t mw_duration;
|
srs_utime_t mw_duration;
|
||||||
#endif
|
#endif
|
||||||
private:
|
|
||||||
// For RTC, we never use jitter to correct timestamp.
|
|
||||||
// But we should not change the atc or time_jitter for source or RTMP.
|
|
||||||
// @remark In this mode, we also never check the queue by timstamp, but only by count.
|
|
||||||
bool pass_timestamp;
|
|
||||||
public:
|
public:
|
||||||
SrsConsumer(SrsSource* s, SrsConnection* c);
|
SrsConsumer(SrsSource* s);
|
||||||
virtual ~SrsConsumer();
|
virtual ~SrsConsumer();
|
||||||
public:
|
public:
|
||||||
// Use pass timestamp mode.
|
|
||||||
void enable_pass_timestamp() { pass_timestamp = true; }
|
|
||||||
// Set the size of queue.
|
// Set the size of queue.
|
||||||
virtual void set_queue_size(srs_utime_t queue_size);
|
virtual void set_queue_size(srs_utime_t queue_size);
|
||||||
// when source id changed, notice client to print.
|
// when source id changed, notice client to print.
|
||||||
|
@ -333,32 +321,6 @@ public:
|
||||||
virtual SrsSharedPtrMessage* pop();
|
virtual SrsSharedPtrMessage* pop();
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef SRS_AUTO_RTC
|
|
||||||
// To find the RTP packet for RTX or restore.
|
|
||||||
// TODO: FIXME: Should queue RTP packets in connection level.
|
|
||||||
class SrsRtpPacketQueue
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
struct SeqComp
|
|
||||||
{
|
|
||||||
bool operator()(const uint16_t& l, const uint16_t& r) const
|
|
||||||
{
|
|
||||||
return ((int16_t)(r - l)) > 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
private:
|
|
||||||
std::map<uint16_t, SrsRtpSharedPacket*, SeqComp> pkt_queue;
|
|
||||||
public:
|
|
||||||
SrsRtpPacketQueue();
|
|
||||||
virtual ~SrsRtpPacketQueue();
|
|
||||||
public:
|
|
||||||
void clear();
|
|
||||||
void push(std::vector<SrsRtpSharedPacket*>& pkts);
|
|
||||||
void insert(const uint16_t& sequence, SrsRtpSharedPacket* pkt);
|
|
||||||
SrsRtpSharedPacket* find(const uint16_t& sequence);
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// The hub for origin is a collection of utilities for origin only,
|
// The hub for origin is a collection of utilities for origin only,
|
||||||
// For example, DVR, HLS, Forward and Transcode are only available for origin,
|
// For example, DVR, HLS, Forward and Transcode are only available for origin,
|
||||||
// they are meanless for edge server.
|
// they are meanless for edge server.
|
||||||
|
@ -371,10 +333,6 @@ private:
|
||||||
private:
|
private:
|
||||||
// The format, codec information.
|
// The format, codec information.
|
||||||
SrsRtmpFormat* format;
|
SrsRtmpFormat* format;
|
||||||
#ifdef SRS_AUTO_RTC
|
|
||||||
// rtc handler
|
|
||||||
SrsRtc* rtc;
|
|
||||||
#endif
|
|
||||||
// hls handler.
|
// hls handler.
|
||||||
SrsHls* hls;
|
SrsHls* hls;
|
||||||
// The DASH encoder.
|
// The DASH encoder.
|
||||||
|
@ -383,7 +341,7 @@ private:
|
||||||
SrsDvr* dvr;
|
SrsDvr* dvr;
|
||||||
// transcoding handler.
|
// transcoding handler.
|
||||||
SrsEncoder* encoder;
|
SrsEncoder* encoder;
|
||||||
#ifdef SRS_AUTO_HDS
|
#ifdef SRS_HDS
|
||||||
// adobe hds(http dynamic streaming).
|
// adobe hds(http dynamic streaming).
|
||||||
SrsHds *hds;
|
SrsHds *hds;
|
||||||
#endif
|
#endif
|
||||||
|
@ -517,7 +475,7 @@ public:
|
||||||
private:
|
private:
|
||||||
virtual srs_error_t do_cycle();
|
virtual srs_error_t do_cycle();
|
||||||
public:
|
public:
|
||||||
// when system exit, destroy the sources,
|
// when system exit, destroy th`e sources,
|
||||||
// For gmc to analysis mem leaks.
|
// For gmc to analysis mem leaks.
|
||||||
virtual void destroy();
|
virtual void destroy();
|
||||||
};
|
};
|
||||||
|
@ -525,6 +483,19 @@ public:
|
||||||
// Global singleton instance.
|
// Global singleton instance.
|
||||||
extern SrsSourceManager* _srs_sources;
|
extern SrsSourceManager* _srs_sources;
|
||||||
|
|
||||||
|
// For two sources to bridge with each other.
|
||||||
|
class ISrsSourceBridger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ISrsSourceBridger();
|
||||||
|
virtual ~ISrsSourceBridger();
|
||||||
|
public:
|
||||||
|
virtual srs_error_t on_publish() = 0;
|
||||||
|
virtual srs_error_t on_audio(SrsSharedPtrMessage* audio) = 0;
|
||||||
|
virtual srs_error_t on_video(SrsSharedPtrMessage* video) = 0;
|
||||||
|
virtual void on_unpublish() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
// live streaming source.
|
// live streaming source.
|
||||||
class SrsSource : public ISrsReloadHandler
|
class SrsSource : public ISrsReloadHandler
|
||||||
{
|
{
|
||||||
|
@ -534,9 +505,9 @@ private:
|
||||||
// For edge, it's the edge ingest id.
|
// For edge, it's the edge ingest id.
|
||||||
// when source id changed, for example, the edge reconnect,
|
// when source id changed, for example, the edge reconnect,
|
||||||
// invoke the on_source_id_changed() to let all clients know.
|
// invoke the on_source_id_changed() to let all clients know.
|
||||||
int _source_id;
|
SrsContextId _source_id;
|
||||||
// previous source id.
|
// previous source id.
|
||||||
int _pre_source_id;
|
SrsContextId _pre_source_id;
|
||||||
// deep copy of client request.
|
// deep copy of client request.
|
||||||
SrsRequest* req;
|
SrsRequest* req;
|
||||||
// To delivery stream to clients.
|
// To delivery stream to clients.
|
||||||
|
@ -547,10 +518,6 @@ private:
|
||||||
bool mix_correct;
|
bool mix_correct;
|
||||||
// The mix queue to implements the mix correct algorithm.
|
// The mix queue to implements the mix correct algorithm.
|
||||||
SrsMixQueue* mix_queue;
|
SrsMixQueue* mix_queue;
|
||||||
#ifdef SRS_AUTO_RTC
|
|
||||||
// rtp packet queue
|
|
||||||
SrsRtpPacketQueue* rtp_queue;
|
|
||||||
#endif
|
|
||||||
// For play, whether enabled atc.
|
// For play, whether enabled atc.
|
||||||
// The atc(use absolute time and donot adjust time),
|
// The atc(use absolute time and donot adjust time),
|
||||||
// directly use msg time and donot adjust if atc is true,
|
// directly use msg time and donot adjust if atc is true,
|
||||||
|
@ -562,6 +529,8 @@ private:
|
||||||
int64_t last_packet_time;
|
int64_t last_packet_time;
|
||||||
// The event handler.
|
// The event handler.
|
||||||
ISrsSourceHandler* handler;
|
ISrsSourceHandler* handler;
|
||||||
|
// The source bridger for other source.
|
||||||
|
ISrsSourceBridger* bridger;
|
||||||
// The edge control service
|
// The edge control service
|
||||||
SrsPlayEdge* play_edge;
|
SrsPlayEdge* play_edge;
|
||||||
SrsPublishEdge* publish_edge;
|
SrsPublishEdge* publish_edge;
|
||||||
|
@ -588,15 +557,17 @@ public:
|
||||||
public:
|
public:
|
||||||
// Initialize the hls with handlers.
|
// Initialize the hls with handlers.
|
||||||
virtual srs_error_t initialize(SrsRequest* r, ISrsSourceHandler* h);
|
virtual srs_error_t initialize(SrsRequest* r, ISrsSourceHandler* h);
|
||||||
|
// Bridge to other source, forward packets to it.
|
||||||
|
void bridge_to(ISrsSourceBridger* v);
|
||||||
// Interface ISrsReloadHandler
|
// Interface ISrsReloadHandler
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t on_reload_vhost_play(std::string vhost);
|
virtual srs_error_t on_reload_vhost_play(std::string vhost);
|
||||||
public:
|
public:
|
||||||
// The source id changed.
|
// The source id changed.
|
||||||
virtual srs_error_t on_source_id_changed(int id);
|
virtual srs_error_t on_source_id_changed(SrsContextId id);
|
||||||
// Get current source id.
|
// Get current source id.
|
||||||
virtual int source_id();
|
virtual SrsContextId source_id();
|
||||||
virtual int pre_source_id();
|
virtual SrsContextId pre_source_id();
|
||||||
// Whether source is inactive, which means there is no publishing stream source.
|
// Whether source is inactive, which means there is no publishing stream source.
|
||||||
// @remark For edge, it's inactive util stream has been pulled from origin.
|
// @remark For edge, it's inactive util stream has been pulled from origin.
|
||||||
virtual bool inactive();
|
virtual bool inactive();
|
||||||
|
@ -606,10 +577,12 @@ public:
|
||||||
virtual bool can_publish(bool is_edge);
|
virtual bool can_publish(bool is_edge);
|
||||||
virtual srs_error_t on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* metadata);
|
virtual srs_error_t on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* metadata);
|
||||||
public:
|
public:
|
||||||
|
// TODO: FIXME: Use SrsSharedPtrMessage instead.
|
||||||
virtual srs_error_t on_audio(SrsCommonMessage* audio);
|
virtual srs_error_t on_audio(SrsCommonMessage* audio);
|
||||||
private:
|
private:
|
||||||
virtual srs_error_t on_audio_imp(SrsSharedPtrMessage* audio);
|
virtual srs_error_t on_audio_imp(SrsSharedPtrMessage* audio);
|
||||||
public:
|
public:
|
||||||
|
// TODO: FIXME: Use SrsSharedPtrMessage instead.
|
||||||
virtual srs_error_t on_video(SrsCommonMessage* video);
|
virtual srs_error_t on_video(SrsCommonMessage* video);
|
||||||
private:
|
private:
|
||||||
virtual srs_error_t on_video_imp(SrsSharedPtrMessage* video);
|
virtual srs_error_t on_video_imp(SrsSharedPtrMessage* video);
|
||||||
|
@ -621,12 +594,14 @@ public:
|
||||||
virtual srs_error_t on_publish();
|
virtual srs_error_t on_publish();
|
||||||
virtual void on_unpublish();
|
virtual void on_unpublish();
|
||||||
public:
|
public:
|
||||||
// Create consumer and dumps packets in cache.
|
// Create consumer
|
||||||
// @param consumer, output the create consumer.
|
// @param consumer, output the create consumer.
|
||||||
|
virtual srs_error_t create_consumer(SrsConsumer*& consumer);
|
||||||
|
// Dumps packets in cache to consumer.
|
||||||
// @param ds, whether dumps the sequence header.
|
// @param ds, whether dumps the sequence header.
|
||||||
// @param dm, whether dumps the metadata.
|
// @param dm, whether dumps the metadata.
|
||||||
// @param dg, whether dumps the gop cache.
|
// @param dg, whether dumps the gop cache.
|
||||||
virtual srs_error_t create_consumer(SrsConnection* conn, SrsConsumer*& consumer, bool ds = true, bool dm = true, bool dg = true);
|
virtual srs_error_t consumer_dumps(SrsConsumer* consumer, bool ds = true, bool dm = true, bool dg = true);
|
||||||
virtual void on_consumer_destroy(SrsConsumer* consumer);
|
virtual void on_consumer_destroy(SrsConsumer* consumer);
|
||||||
virtual void set_cache(bool enabled);
|
virtual void set_cache(bool enabled);
|
||||||
virtual SrsRtmpJitterAlgorithm jitter();
|
virtual SrsRtmpJitterAlgorithm jitter();
|
||||||
|
@ -639,13 +614,6 @@ public:
|
||||||
virtual void on_edge_proxy_unpublish();
|
virtual void on_edge_proxy_unpublish();
|
||||||
public:
|
public:
|
||||||
virtual std::string get_curr_origin();
|
virtual std::string get_curr_origin();
|
||||||
public:
|
|
||||||
#ifdef SRS_AUTO_RTC
|
|
||||||
// Find rtp packet by sequence
|
|
||||||
SrsRtpSharedPacket* find_rtp_packet(const uint16_t& seq);
|
|
||||||
// Get the cached meta, as such the sps/pps.
|
|
||||||
SrsMetaCache* cached_meta();
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -40,6 +40,14 @@ ISrsCoroutineHandler::~ISrsCoroutineHandler()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ISrsStartable::ISrsStartable()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ISrsStartable::~ISrsStartable()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
SrsCoroutine::SrsCoroutine()
|
SrsCoroutine::SrsCoroutine()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -74,21 +82,37 @@ srs_error_t SrsDummyCoroutine::pull()
|
||||||
return srs_error_new(ERROR_THREAD_DUMMY, "dummy pull");
|
return srs_error_new(ERROR_THREAD_DUMMY, "dummy pull");
|
||||||
}
|
}
|
||||||
|
|
||||||
int SrsDummyCoroutine::cid()
|
const SrsContextId& SrsDummyCoroutine::cid()
|
||||||
{
|
{
|
||||||
return 0;
|
return _srs_context->get_id();
|
||||||
}
|
}
|
||||||
|
|
||||||
_ST_THREAD_CREATE_PFN _pfn_st_thread_create = (_ST_THREAD_CREATE_PFN)st_thread_create;
|
_ST_THREAD_CREATE_PFN _pfn_st_thread_create = (_ST_THREAD_CREATE_PFN)st_thread_create;
|
||||||
|
|
||||||
SrsSTCoroutine::SrsSTCoroutine(string n, ISrsCoroutineHandler* h, int cid)
|
SrsSTCoroutine::SrsSTCoroutine(string n, ISrsCoroutineHandler* h)
|
||||||
{
|
{
|
||||||
|
// TODO: FIXME: Reduce duplicated code.
|
||||||
name = n;
|
name = n;
|
||||||
handler = h;
|
handler = h;
|
||||||
context = cid;
|
|
||||||
trd = NULL;
|
trd = NULL;
|
||||||
trd_err = srs_success;
|
trd_err = srs_success;
|
||||||
started = interrupted = disposed = cycle_done = false;
|
started = interrupted = disposed = cycle_done = false;
|
||||||
|
|
||||||
|
// 0 use default, default is 64K.
|
||||||
|
stack_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsSTCoroutine::SrsSTCoroutine(string n, ISrsCoroutineHandler* h, SrsContextId cid)
|
||||||
|
{
|
||||||
|
name = n;
|
||||||
|
handler = h;
|
||||||
|
cid_ = cid;
|
||||||
|
trd = NULL;
|
||||||
|
trd_err = srs_success;
|
||||||
|
started = interrupted = disposed = cycle_done = false;
|
||||||
|
|
||||||
|
// 0 use default, default is 64K.
|
||||||
|
stack_size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsSTCoroutine::~SrsSTCoroutine()
|
SrsSTCoroutine::~SrsSTCoroutine()
|
||||||
|
@ -98,6 +122,11 @@ SrsSTCoroutine::~SrsSTCoroutine()
|
||||||
srs_freep(trd_err);
|
srs_freep(trd_err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SrsSTCoroutine::set_stack_size(int v)
|
||||||
|
{
|
||||||
|
stack_size = v;
|
||||||
|
}
|
||||||
|
|
||||||
srs_error_t SrsSTCoroutine::start()
|
srs_error_t SrsSTCoroutine::start()
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
@ -116,7 +145,7 @@ srs_error_t SrsSTCoroutine::start()
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((trd = (srs_thread_t)_pfn_st_thread_create(pfn, this, 1, 0)) == NULL) {
|
if ((trd = (srs_thread_t)_pfn_st_thread_create(pfn, this, 1, stack_size)) == NULL) {
|
||||||
err = srs_error_new(ERROR_ST_CREATE_CYCLE_THREAD, "create failed");
|
err = srs_error_new(ERROR_ST_CREATE_CYCLE_THREAD, "create failed");
|
||||||
|
|
||||||
srs_freep(trd_err);
|
srs_freep(trd_err);
|
||||||
|
@ -180,19 +209,18 @@ srs_error_t SrsSTCoroutine::pull()
|
||||||
return srs_error_copy(trd_err);
|
return srs_error_copy(trd_err);
|
||||||
}
|
}
|
||||||
|
|
||||||
int SrsSTCoroutine::cid()
|
const SrsContextId& SrsSTCoroutine::cid()
|
||||||
{
|
{
|
||||||
return context;
|
return cid_;
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsSTCoroutine::cycle()
|
srs_error_t SrsSTCoroutine::cycle()
|
||||||
{
|
{
|
||||||
if (_srs_context) {
|
if (_srs_context) {
|
||||||
if (context) {
|
if (cid_.empty()) {
|
||||||
_srs_context->set_id(context);
|
cid_ = _srs_context->generate_id();
|
||||||
} else {
|
|
||||||
context = _srs_context->generate_id();
|
|
||||||
}
|
}
|
||||||
|
_srs_context->set_id(cid_);
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t err = handler->cycle();
|
srs_error_t err = handler->cycle();
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include <srs_kernel_log.hpp>
|
||||||
#include <srs_service_st.hpp>
|
#include <srs_service_st.hpp>
|
||||||
#include <srs_protocol_io.hpp>
|
#include <srs_protocol_io.hpp>
|
||||||
|
|
||||||
|
@ -67,20 +68,29 @@ public:
|
||||||
virtual srs_error_t cycle() = 0;
|
virtual srs_error_t cycle() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Start the object, generally a croutine.
|
||||||
|
class ISrsStartable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ISrsStartable();
|
||||||
|
virtual ~ISrsStartable();
|
||||||
|
public:
|
||||||
|
virtual srs_error_t start() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
// The corotine object.
|
// The corotine object.
|
||||||
class SrsCoroutine
|
class SrsCoroutine : public ISrsStartable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SrsCoroutine();
|
SrsCoroutine();
|
||||||
virtual ~SrsCoroutine();
|
virtual ~SrsCoroutine();
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t start() = 0;
|
|
||||||
virtual void stop() = 0;
|
virtual void stop() = 0;
|
||||||
virtual void interrupt() = 0;
|
virtual void interrupt() = 0;
|
||||||
// @return a copy of error, which should be freed by user.
|
// @return a copy of error, which should be freed by user.
|
||||||
// NULL if not terminated and user should pull again.
|
// NULL if not terminated and user should pull again.
|
||||||
virtual srs_error_t pull() = 0;
|
virtual srs_error_t pull() = 0;
|
||||||
virtual int cid() = 0;
|
virtual const SrsContextId& cid() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// An empty coroutine, user can default to this object before create any real coroutine.
|
// An empty coroutine, user can default to this object before create any real coroutine.
|
||||||
|
@ -95,7 +105,7 @@ public:
|
||||||
virtual void stop();
|
virtual void stop();
|
||||||
virtual void interrupt();
|
virtual void interrupt();
|
||||||
virtual srs_error_t pull();
|
virtual srs_error_t pull();
|
||||||
virtual int cid();
|
virtual const SrsContextId& cid();
|
||||||
};
|
};
|
||||||
|
|
||||||
// For utest to mock the thread create.
|
// For utest to mock the thread create.
|
||||||
|
@ -118,10 +128,11 @@ class SrsSTCoroutine : public SrsCoroutine
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
std::string name;
|
std::string name;
|
||||||
|
int stack_size;
|
||||||
ISrsCoroutineHandler* handler;
|
ISrsCoroutineHandler* handler;
|
||||||
private:
|
private:
|
||||||
srs_thread_t trd;
|
srs_thread_t trd;
|
||||||
int context;
|
SrsContextId cid_;
|
||||||
srs_error_t trd_err;
|
srs_error_t trd_err;
|
||||||
private:
|
private:
|
||||||
bool started;
|
bool started;
|
||||||
|
@ -132,8 +143,12 @@ private:
|
||||||
public:
|
public:
|
||||||
// Create a thread with name n and handler h.
|
// Create a thread with name n and handler h.
|
||||||
// @remark User can specify a cid for thread to use, or we will allocate a new one.
|
// @remark User can specify a cid for thread to use, or we will allocate a new one.
|
||||||
SrsSTCoroutine(std::string n, ISrsCoroutineHandler* h, int cid = 0);
|
SrsSTCoroutine(std::string n, ISrsCoroutineHandler* h);
|
||||||
|
SrsSTCoroutine(std::string n, ISrsCoroutineHandler* h, SrsContextId cid);
|
||||||
virtual ~SrsSTCoroutine();
|
virtual ~SrsSTCoroutine();
|
||||||
|
public:
|
||||||
|
// Set the stack size of coroutine, default to 0(64KB).
|
||||||
|
void set_stack_size(int v);
|
||||||
public:
|
public:
|
||||||
// Start the thread.
|
// Start the thread.
|
||||||
// @remark Should never start it when stopped or terminated.
|
// @remark Should never start it when stopped or terminated.
|
||||||
|
@ -154,7 +169,7 @@ public:
|
||||||
// @remark Return ERROR_THREAD_INTERRUPED when thread is interrupted.
|
// @remark Return ERROR_THREAD_INTERRUPED when thread is interrupted.
|
||||||
virtual srs_error_t pull();
|
virtual srs_error_t pull();
|
||||||
// Get the context id of thread.
|
// Get the context id of thread.
|
||||||
virtual int cid();
|
virtual const SrsContextId& cid();
|
||||||
private:
|
private:
|
||||||
virtual srs_error_t cycle();
|
virtual srs_error_t cycle();
|
||||||
static void* pfn(void* arg);
|
static void* pfn(void* arg);
|
||||||
|
|
|
@ -35,14 +35,17 @@ using namespace std;
|
||||||
#include <srs_kernel_utility.hpp>
|
#include <srs_kernel_utility.hpp>
|
||||||
#include <srs_protocol_amf0.hpp>
|
#include <srs_protocol_amf0.hpp>
|
||||||
|
|
||||||
int64_t srs_gvid = 0;
|
string srs_generate_id()
|
||||||
|
|
||||||
int64_t srs_generate_id()
|
|
||||||
{
|
{
|
||||||
|
static int64_t srs_gvid = 0;
|
||||||
|
|
||||||
if (srs_gvid == 0) {
|
if (srs_gvid == 0) {
|
||||||
srs_gvid = getpid() * 3;
|
srs_gvid = getpid();
|
||||||
}
|
}
|
||||||
return srs_gvid++;
|
|
||||||
|
string prefix = "vid";
|
||||||
|
string rand_id = srs_int2str(srs_get_system_time() % 1000);
|
||||||
|
return prefix + "-" + srs_int2str(srs_gvid++) + "-" + rand_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsStatisticVhost::SrsStatisticVhost()
|
SrsStatisticVhost::SrsStatisticVhost()
|
||||||
|
@ -71,7 +74,7 @@ srs_error_t SrsStatisticVhost::dumps(SrsJsonObject* obj)
|
||||||
bool hls_enabled = _srs_config->get_hls_enabled(vhost);
|
bool hls_enabled = _srs_config->get_hls_enabled(vhost);
|
||||||
bool enabled = _srs_config->get_vhost_enabled(vhost);
|
bool enabled = _srs_config->get_vhost_enabled(vhost);
|
||||||
|
|
||||||
obj->set("id", SrsJsonAny::integer(id));
|
obj->set("id", SrsJsonAny::str(id.c_str()));
|
||||||
obj->set("name", SrsJsonAny::str(vhost.c_str()));
|
obj->set("name", SrsJsonAny::str(vhost.c_str()));
|
||||||
obj->set("enabled", SrsJsonAny::boolean(enabled));
|
obj->set("enabled", SrsJsonAny::boolean(enabled));
|
||||||
obj->set("clients", SrsJsonAny::integer(nb_clients));
|
obj->set("clients", SrsJsonAny::integer(nb_clients));
|
||||||
|
@ -101,7 +104,6 @@ SrsStatisticStream::SrsStatisticStream()
|
||||||
id = srs_generate_id();
|
id = srs_generate_id();
|
||||||
vhost = NULL;
|
vhost = NULL;
|
||||||
active = false;
|
active = false;
|
||||||
connection_cid = -1;
|
|
||||||
|
|
||||||
has_video = false;
|
has_video = false;
|
||||||
vcodec = SrsVideoCodecIdReserved;
|
vcodec = SrsVideoCodecIdReserved;
|
||||||
|
@ -134,9 +136,9 @@ srs_error_t SrsStatisticStream::dumps(SrsJsonObject* obj)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
obj->set("id", SrsJsonAny::integer(id));
|
obj->set("id", SrsJsonAny::str(id.c_str()));
|
||||||
obj->set("name", SrsJsonAny::str(stream.c_str()));
|
obj->set("name", SrsJsonAny::str(stream.c_str()));
|
||||||
obj->set("vhost", SrsJsonAny::integer(vhost->id));
|
obj->set("vhost", SrsJsonAny::str(vhost->id.c_str()));
|
||||||
obj->set("app", SrsJsonAny::str(app.c_str()));
|
obj->set("app", SrsJsonAny::str(app.c_str()));
|
||||||
obj->set("live_ms", SrsJsonAny::integer(srsu2ms(srs_get_system_time())));
|
obj->set("live_ms", SrsJsonAny::integer(srsu2ms(srs_get_system_time())));
|
||||||
obj->set("clients", SrsJsonAny::integer(nb_clients));
|
obj->set("clients", SrsJsonAny::integer(nb_clients));
|
||||||
|
@ -154,7 +156,7 @@ srs_error_t SrsStatisticStream::dumps(SrsJsonObject* obj)
|
||||||
obj->set("publish", publish);
|
obj->set("publish", publish);
|
||||||
|
|
||||||
publish->set("active", SrsJsonAny::boolean(active));
|
publish->set("active", SrsJsonAny::boolean(active));
|
||||||
publish->set("cid", SrsJsonAny::integer(connection_cid));
|
publish->set("cid", SrsJsonAny::str(connection_cid.c_str()));
|
||||||
|
|
||||||
if (!has_video) {
|
if (!has_video) {
|
||||||
obj->set("video", SrsJsonAny::null());
|
obj->set("video", SrsJsonAny::null());
|
||||||
|
@ -184,7 +186,7 @@ srs_error_t SrsStatisticStream::dumps(SrsJsonObject* obj)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SrsStatisticStream::publish(int cid)
|
void SrsStatisticStream::publish(SrsContextId cid)
|
||||||
{
|
{
|
||||||
connection_cid = cid;
|
connection_cid = cid;
|
||||||
active = true;
|
active = true;
|
||||||
|
@ -203,7 +205,6 @@ void SrsStatisticStream::close()
|
||||||
|
|
||||||
SrsStatisticClient::SrsStatisticClient()
|
SrsStatisticClient::SrsStatisticClient()
|
||||||
{
|
{
|
||||||
id = 0;
|
|
||||||
stream = NULL;
|
stream = NULL;
|
||||||
conn = NULL;
|
conn = NULL;
|
||||||
req = NULL;
|
req = NULL;
|
||||||
|
@ -219,9 +220,9 @@ srs_error_t SrsStatisticClient::dumps(SrsJsonObject* obj)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
obj->set("id", SrsJsonAny::integer(id));
|
obj->set("id", SrsJsonAny::str(id.c_str()));
|
||||||
obj->set("vhost", SrsJsonAny::integer(stream->vhost->id));
|
obj->set("vhost", SrsJsonAny::str(stream->vhost->id.c_str()));
|
||||||
obj->set("stream", SrsJsonAny::integer(stream->id));
|
obj->set("stream", SrsJsonAny::str(stream->id.c_str()));
|
||||||
obj->set("ip", SrsJsonAny::str(req->ip.c_str()));
|
obj->set("ip", SrsJsonAny::str(req->ip.c_str()));
|
||||||
obj->set("pageUrl", SrsJsonAny::str(req->pageUrl.c_str()));
|
obj->set("pageUrl", SrsJsonAny::str(req->pageUrl.c_str()));
|
||||||
obj->set("swfUrl", SrsJsonAny::str(req->swfUrl.c_str()));
|
obj->set("swfUrl", SrsJsonAny::str(req->swfUrl.c_str()));
|
||||||
|
@ -267,12 +268,9 @@ SrsStatistic::SrsStatistic()
|
||||||
|
|
||||||
perf_iovs = new SrsStatisticCategory();
|
perf_iovs = new SrsStatisticCategory();
|
||||||
perf_msgs = new SrsStatisticCategory();
|
perf_msgs = new SrsStatisticCategory();
|
||||||
perf_sendmmsg = new SrsStatisticCategory();
|
|
||||||
perf_gso = new SrsStatisticCategory();
|
|
||||||
perf_rtp = new SrsStatisticCategory();
|
perf_rtp = new SrsStatisticCategory();
|
||||||
perf_rtc = new SrsStatisticCategory();
|
perf_rtc = new SrsStatisticCategory();
|
||||||
perf_bytes = new SrsStatisticCategory();
|
perf_bytes = new SrsStatisticCategory();
|
||||||
perf_dropped = new SrsStatisticCategory();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsStatistic::~SrsStatistic()
|
SrsStatistic::~SrsStatistic()
|
||||||
|
@ -281,21 +279,21 @@ SrsStatistic::~SrsStatistic()
|
||||||
srs_freep(clk);
|
srs_freep(clk);
|
||||||
|
|
||||||
if (true) {
|
if (true) {
|
||||||
std::map<int64_t, SrsStatisticVhost*>::iterator it;
|
std::map<std::string, SrsStatisticVhost*>::iterator it;
|
||||||
for (it = vhosts.begin(); it != vhosts.end(); it++) {
|
for (it = vhosts.begin(); it != vhosts.end(); it++) {
|
||||||
SrsStatisticVhost* vhost = it->second;
|
SrsStatisticVhost* vhost = it->second;
|
||||||
srs_freep(vhost);
|
srs_freep(vhost);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (true) {
|
if (true) {
|
||||||
std::map<int64_t, SrsStatisticStream*>::iterator it;
|
std::map<std::string, SrsStatisticStream*>::iterator it;
|
||||||
for (it = streams.begin(); it != streams.end(); it++) {
|
for (it = streams.begin(); it != streams.end(); it++) {
|
||||||
SrsStatisticStream* stream = it->second;
|
SrsStatisticStream* stream = it->second;
|
||||||
srs_freep(stream);
|
srs_freep(stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (true) {
|
if (true) {
|
||||||
std::map<int, SrsStatisticClient*>::iterator it;
|
std::map<std::string, SrsStatisticClient*>::iterator it;
|
||||||
for (it = clients.begin(); it != clients.end(); it++) {
|
for (it = clients.begin(); it != clients.end(); it++) {
|
||||||
SrsStatisticClient* client = it->second;
|
SrsStatisticClient* client = it->second;
|
||||||
srs_freep(client);
|
srs_freep(client);
|
||||||
|
@ -309,12 +307,9 @@ SrsStatistic::~SrsStatistic()
|
||||||
|
|
||||||
srs_freep(perf_iovs);
|
srs_freep(perf_iovs);
|
||||||
srs_freep(perf_msgs);
|
srs_freep(perf_msgs);
|
||||||
srs_freep(perf_sendmmsg);
|
|
||||||
srs_freep(perf_gso);
|
|
||||||
srs_freep(perf_rtp);
|
srs_freep(perf_rtp);
|
||||||
srs_freep(perf_rtc);
|
srs_freep(perf_rtc);
|
||||||
srs_freep(perf_bytes);
|
srs_freep(perf_bytes);
|
||||||
srs_freep(perf_dropped);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsStatistic* SrsStatistic::instance()
|
SrsStatistic* SrsStatistic::instance()
|
||||||
|
@ -325,16 +320,16 @@ SrsStatistic* SrsStatistic::instance()
|
||||||
return _instance;
|
return _instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsStatisticVhost* SrsStatistic::find_vhost(int vid)
|
SrsStatisticVhost* SrsStatistic::find_vhost_by_id(std::string vid)
|
||||||
{
|
{
|
||||||
std::map<int64_t, SrsStatisticVhost*>::iterator it;
|
std::map<string, SrsStatisticVhost*>::iterator it;
|
||||||
if ((it = vhosts.find(vid)) != vhosts.end()) {
|
if ((it = vhosts.find(vid)) != vhosts.end()) {
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsStatisticVhost* SrsStatistic::find_vhost(string name)
|
SrsStatisticVhost* SrsStatistic::find_vhost_by_name(string name)
|
||||||
{
|
{
|
||||||
if (rvhosts.empty()) {
|
if (rvhosts.empty()) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -347,19 +342,19 @@ SrsStatisticVhost* SrsStatistic::find_vhost(string name)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsStatisticStream* SrsStatistic::find_stream(int sid)
|
SrsStatisticStream* SrsStatistic::find_stream(string sid)
|
||||||
{
|
{
|
||||||
std::map<int64_t, SrsStatisticStream*>::iterator it;
|
std::map<std::string, SrsStatisticStream*>::iterator it;
|
||||||
if ((it = streams.find(sid)) != streams.end()) {
|
if ((it = streams.find(sid)) != streams.end()) {
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsStatisticClient* SrsStatistic::find_client(int cid)
|
SrsStatisticClient* SrsStatistic::find_client(string client_id)
|
||||||
{
|
{
|
||||||
std::map<int, SrsStatisticClient*>::iterator it;
|
std::map<std::string, SrsStatisticClient*>::iterator it;
|
||||||
if ((it = clients.find(cid)) != clients.end()) {
|
if ((it = clients.find(client_id)) != clients.end()) {
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -411,7 +406,7 @@ srs_error_t SrsStatistic::on_video_frames(SrsRequest* req, int nb_frames)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SrsStatistic::on_stream_publish(SrsRequest* req, int cid)
|
void SrsStatistic::on_stream_publish(SrsRequest* req, SrsContextId cid)
|
||||||
{
|
{
|
||||||
SrsStatisticVhost* vhost = create_vhost(req);
|
SrsStatisticVhost* vhost = create_vhost(req);
|
||||||
SrsStatisticStream* stream = create_stream(vhost, req);
|
SrsStatisticStream* stream = create_stream(vhost, req);
|
||||||
|
@ -427,7 +422,7 @@ void SrsStatistic::on_stream_close(SrsRequest* req)
|
||||||
|
|
||||||
// TODO: FIXME: Should fix https://github.com/ossrs/srs/issues/803
|
// TODO: FIXME: Should fix https://github.com/ossrs/srs/issues/803
|
||||||
if (true) {
|
if (true) {
|
||||||
std::map<int64_t, SrsStatisticStream*>::iterator it;
|
std::map<std::string, SrsStatisticStream*>::iterator it;
|
||||||
if ((it=streams.find(stream->id)) != streams.end()) {
|
if ((it=streams.find(stream->id)) != streams.end()) {
|
||||||
streams.erase(it);
|
streams.erase(it);
|
||||||
}
|
}
|
||||||
|
@ -436,16 +431,19 @@ void SrsStatistic::on_stream_close(SrsRequest* req)
|
||||||
// TODO: FIXME: Should fix https://github.com/ossrs/srs/issues/803
|
// TODO: FIXME: Should fix https://github.com/ossrs/srs/issues/803
|
||||||
if (true) {
|
if (true) {
|
||||||
std::map<std::string, SrsStatisticStream*>::iterator it;
|
std::map<std::string, SrsStatisticStream*>::iterator it;
|
||||||
if ((it=rstreams.find(stream->url)) != rstreams.end()) {
|
if ((it = rstreams.find(stream->url)) != rstreams.end()) {
|
||||||
rstreams.erase(it);
|
rstreams.erase(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsStatistic::on_client(int id, SrsRequest* req, SrsConnection* conn, SrsRtmpConnType type)
|
srs_error_t SrsStatistic::on_client(SrsContextId cid, SrsRequest* req, ISrsExpire* conn, SrsRtmpConnType type)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
// TODO: FIXME: We should use UUID for client ID.
|
||||||
|
std::string id = cid.c_str();
|
||||||
|
|
||||||
SrsStatisticVhost* vhost = create_vhost(req);
|
SrsStatisticVhost* vhost = create_vhost(req);
|
||||||
SrsStatisticStream* stream = create_stream(vhost, req);
|
SrsStatisticStream* stream = create_stream(vhost, req);
|
||||||
|
|
||||||
|
@ -470,9 +468,12 @@ srs_error_t SrsStatistic::on_client(int id, SrsRequest* req, SrsConnection* conn
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SrsStatistic::on_disconnect(int id)
|
void SrsStatistic::on_disconnect(const SrsContextId& cid)
|
||||||
{
|
{
|
||||||
std::map<int, SrsStatisticClient*>::iterator it;
|
// TODO: FIXME: We should use UUID for client ID.
|
||||||
|
std::string id = cid.c_str();
|
||||||
|
|
||||||
|
std::map<std::string, SrsStatisticClient*>::iterator it;
|
||||||
if ((it = clients.find(id)) == clients.end()) {
|
if ((it = clients.find(id)) == clients.end()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -488,9 +489,10 @@ void SrsStatistic::on_disconnect(int id)
|
||||||
vhost->nb_clients--;
|
vhost->nb_clients--;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SrsStatistic::kbps_add_delta(SrsConnection* conn)
|
void SrsStatistic::kbps_add_delta(const SrsContextId& cid, ISrsKbpsDelta* delta)
|
||||||
{
|
{
|
||||||
int id = conn->srs_id();
|
// TODO: FIXME: Should not use context id as connection id.
|
||||||
|
std::string id = cid.c_str();
|
||||||
if (clients.find(id) == clients.end()) {
|
if (clients.find(id) == clients.end()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -499,7 +501,7 @@ void SrsStatistic::kbps_add_delta(SrsConnection* conn)
|
||||||
|
|
||||||
// resample the kbps to collect the delta.
|
// resample the kbps to collect the delta.
|
||||||
int64_t in, out;
|
int64_t in, out;
|
||||||
conn->remark(&in, &out);
|
delta->remark(&in, &out);
|
||||||
|
|
||||||
// add delta of connection to kbps.
|
// add delta of connection to kbps.
|
||||||
// for next sample() of server kbps can get the stat.
|
// for next sample() of server kbps can get the stat.
|
||||||
|
@ -512,14 +514,14 @@ SrsKbps* SrsStatistic::kbps_sample()
|
||||||
{
|
{
|
||||||
kbps->sample();
|
kbps->sample();
|
||||||
if (true) {
|
if (true) {
|
||||||
std::map<int64_t, SrsStatisticVhost*>::iterator it;
|
std::map<std::string, SrsStatisticVhost*>::iterator it;
|
||||||
for (it = vhosts.begin(); it != vhosts.end(); it++) {
|
for (it = vhosts.begin(); it != vhosts.end(); it++) {
|
||||||
SrsStatisticVhost* vhost = it->second;
|
SrsStatisticVhost* vhost = it->second;
|
||||||
vhost->kbps->sample();
|
vhost->kbps->sample();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (true) {
|
if (true) {
|
||||||
std::map<int64_t, SrsStatisticStream*>::iterator it;
|
std::map<std::string, SrsStatisticStream*>::iterator it;
|
||||||
for (it = streams.begin(); it != streams.end(); it++) {
|
for (it = streams.begin(); it != streams.end(); it++) {
|
||||||
SrsStatisticStream* stream = it->second;
|
SrsStatisticStream* stream = it->second;
|
||||||
stream->kbps->sample();
|
stream->kbps->sample();
|
||||||
|
@ -529,7 +531,7 @@ SrsKbps* SrsStatistic::kbps_sample()
|
||||||
return kbps;
|
return kbps;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t SrsStatistic::server_id()
|
std::string SrsStatistic::server_id()
|
||||||
{
|
{
|
||||||
return _server_id;
|
return _server_id;
|
||||||
}
|
}
|
||||||
|
@ -538,7 +540,7 @@ srs_error_t SrsStatistic::dumps_vhosts(SrsJsonArray* arr)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
std::map<int64_t, SrsStatisticVhost*>::iterator it;
|
std::map<std::string, SrsStatisticVhost*>::iterator it;
|
||||||
for (it = vhosts.begin(); it != vhosts.end(); it++) {
|
for (it = vhosts.begin(); it != vhosts.end(); it++) {
|
||||||
SrsStatisticVhost* vhost = it->second;
|
SrsStatisticVhost* vhost = it->second;
|
||||||
|
|
||||||
|
@ -557,7 +559,7 @@ srs_error_t SrsStatistic::dumps_streams(SrsJsonArray* arr)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
std::map<int64_t, SrsStatisticStream*>::iterator it;
|
std::map<std::string, SrsStatisticStream*>::iterator it;
|
||||||
for (it = streams.begin(); it != streams.end(); it++) {
|
for (it = streams.begin(); it != streams.end(); it++) {
|
||||||
SrsStatisticStream* stream = it->second;
|
SrsStatisticStream* stream = it->second;
|
||||||
|
|
||||||
|
@ -576,7 +578,7 @@ srs_error_t SrsStatistic::dumps_clients(SrsJsonArray* arr, int start, int count)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
std::map<int, SrsStatisticClient*>::iterator it = clients.begin();
|
std::map<std::string, SrsStatisticClient*>::iterator it = clients.begin();
|
||||||
for (int i = 0; i < start + count && it != clients.end(); it++, i++) {
|
for (int i = 0; i < start + count && it != clients.end(); it++, i++) {
|
||||||
if (i < start) {
|
if (i < start) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -625,16 +627,6 @@ srs_error_t SrsStatistic::dumps_perf_rtp_packets(SrsJsonObject* obj)
|
||||||
return dumps_perf(perf_rtp, obj);
|
return dumps_perf(perf_rtp, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SrsStatistic::perf_on_gso_packets(int nb_packets)
|
|
||||||
{
|
|
||||||
perf_on_packets(perf_gso, nb_packets);
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_error_t SrsStatistic::dumps_perf_gso(SrsJsonObject* obj)
|
|
||||||
{
|
|
||||||
return dumps_perf(perf_gso, obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SrsStatistic::perf_on_writev_iovs(int nb_iovs)
|
void SrsStatistic::perf_on_writev_iovs(int nb_iovs)
|
||||||
{
|
{
|
||||||
perf_on_packets(perf_iovs, nb_iovs);
|
perf_on_packets(perf_iovs, nb_iovs);
|
||||||
|
@ -645,16 +637,6 @@ srs_error_t SrsStatistic::dumps_perf_writev_iovs(SrsJsonObject* obj)
|
||||||
return dumps_perf(perf_iovs, obj);
|
return dumps_perf(perf_iovs, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SrsStatistic::perf_on_sendmmsg_packets(int nb_packets)
|
|
||||||
{
|
|
||||||
perf_on_packets(perf_sendmmsg, nb_packets);
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_error_t SrsStatistic::dumps_perf_sendmmsg(SrsJsonObject* obj)
|
|
||||||
{
|
|
||||||
return dumps_perf(perf_sendmmsg, obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SrsStatistic::perf_on_rtc_bytes(int nn_bytes, int nn_rtp_bytes, int nn_padding)
|
void SrsStatistic::perf_on_rtc_bytes(int nn_bytes, int nn_rtp_bytes, int nn_padding)
|
||||||
{
|
{
|
||||||
// a: AVFrame bytes.
|
// a: AVFrame bytes.
|
||||||
|
@ -678,48 +660,19 @@ srs_error_t SrsStatistic::dumps_perf_bytes(SrsJsonObject* obj)
|
||||||
return srs_success;
|
return srs_success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SrsStatistic::perf_on_dropped(int nn_msgs, int nn_rtc, int nn_dropped)
|
|
||||||
{
|
|
||||||
// a: System AVFrames.
|
|
||||||
// b: RTC frames.
|
|
||||||
// c: Dropped frames.
|
|
||||||
perf_dropped->a += nn_msgs;
|
|
||||||
perf_dropped->b += nn_rtc;
|
|
||||||
perf_dropped->c += nn_dropped;
|
|
||||||
|
|
||||||
perf_dropped->nn += nn_dropped;
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_error_t SrsStatistic::dumps_perf_dropped(SrsJsonObject* obj)
|
|
||||||
{
|
|
||||||
obj->set("avframes", SrsJsonAny::integer(perf_dropped->a));
|
|
||||||
obj->set("rtc_frames", SrsJsonAny::integer(perf_dropped->b));
|
|
||||||
obj->set("rtc_dropeed", SrsJsonAny::integer(perf_dropped->c));
|
|
||||||
|
|
||||||
obj->set("nn", SrsJsonAny::integer(perf_dropped->nn));
|
|
||||||
|
|
||||||
return srs_success;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SrsStatistic::reset_perf()
|
void SrsStatistic::reset_perf()
|
||||||
{
|
{
|
||||||
srs_freep(perf_iovs);
|
srs_freep(perf_iovs);
|
||||||
srs_freep(perf_msgs);
|
srs_freep(perf_msgs);
|
||||||
srs_freep(perf_sendmmsg);
|
|
||||||
srs_freep(perf_gso);
|
|
||||||
srs_freep(perf_rtp);
|
srs_freep(perf_rtp);
|
||||||
srs_freep(perf_rtc);
|
srs_freep(perf_rtc);
|
||||||
srs_freep(perf_bytes);
|
srs_freep(perf_bytes);
|
||||||
srs_freep(perf_dropped);
|
|
||||||
|
|
||||||
perf_iovs = new SrsStatisticCategory();
|
perf_iovs = new SrsStatisticCategory();
|
||||||
perf_msgs = new SrsStatisticCategory();
|
perf_msgs = new SrsStatisticCategory();
|
||||||
perf_sendmmsg = new SrsStatisticCategory();
|
|
||||||
perf_gso = new SrsStatisticCategory();
|
|
||||||
perf_rtp = new SrsStatisticCategory();
|
perf_rtp = new SrsStatisticCategory();
|
||||||
perf_rtc = new SrsStatisticCategory();
|
perf_rtc = new SrsStatisticCategory();
|
||||||
perf_bytes = new SrsStatisticCategory();
|
perf_bytes = new SrsStatisticCategory();
|
||||||
perf_dropped = new SrsStatisticCategory();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SrsStatistic::perf_on_packets(SrsStatisticCategory* p, int nb_msgs)
|
void SrsStatistic::perf_on_packets(SrsStatisticCategory* p, int nb_msgs)
|
||||||
|
|
|
@ -36,14 +36,15 @@
|
||||||
class SrsKbps;
|
class SrsKbps;
|
||||||
class SrsWallClock;
|
class SrsWallClock;
|
||||||
class SrsRequest;
|
class SrsRequest;
|
||||||
class SrsConnection;
|
class ISrsExpire;
|
||||||
class SrsJsonObject;
|
class SrsJsonObject;
|
||||||
class SrsJsonArray;
|
class SrsJsonArray;
|
||||||
|
class ISrsKbpsDelta;
|
||||||
|
|
||||||
struct SrsStatisticVhost
|
struct SrsStatisticVhost
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
int64_t id;
|
std::string id;
|
||||||
std::string vhost;
|
std::string vhost;
|
||||||
int nb_streams;
|
int nb_streams;
|
||||||
int nb_clients;
|
int nb_clients;
|
||||||
|
@ -61,13 +62,13 @@ public:
|
||||||
struct SrsStatisticStream
|
struct SrsStatisticStream
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
int64_t id;
|
std::string id;
|
||||||
SrsStatisticVhost* vhost;
|
SrsStatisticVhost* vhost;
|
||||||
std::string app;
|
std::string app;
|
||||||
std::string stream;
|
std::string stream;
|
||||||
std::string url;
|
std::string url;
|
||||||
bool active;
|
bool active;
|
||||||
int connection_cid;
|
SrsContextId connection_cid;
|
||||||
int nb_clients;
|
int nb_clients;
|
||||||
uint64_t nb_frames;
|
uint64_t nb_frames;
|
||||||
public:
|
public:
|
||||||
|
@ -101,7 +102,7 @@ public:
|
||||||
virtual srs_error_t dumps(SrsJsonObject* obj);
|
virtual srs_error_t dumps(SrsJsonObject* obj);
|
||||||
public:
|
public:
|
||||||
// Publish the stream.
|
// Publish the stream.
|
||||||
virtual void publish(int cid);
|
virtual void publish(SrsContextId cid);
|
||||||
// Close the stream.
|
// Close the stream.
|
||||||
virtual void close();
|
virtual void close();
|
||||||
};
|
};
|
||||||
|
@ -109,11 +110,11 @@ public:
|
||||||
struct SrsStatisticClient
|
struct SrsStatisticClient
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
ISrsExpire* conn;
|
||||||
SrsStatisticStream* stream;
|
SrsStatisticStream* stream;
|
||||||
SrsConnection* conn;
|
|
||||||
SrsRequest* req;
|
SrsRequest* req;
|
||||||
SrsRtmpConnType type;
|
SrsRtmpConnType type;
|
||||||
int id;
|
std::string id;
|
||||||
srs_utime_t create;
|
srs_utime_t create;
|
||||||
public:
|
public:
|
||||||
SrsStatisticClient();
|
SrsStatisticClient();
|
||||||
|
@ -148,44 +149,41 @@ class SrsStatistic : public ISrsProtocolPerf
|
||||||
private:
|
private:
|
||||||
static SrsStatistic *_instance;
|
static SrsStatistic *_instance;
|
||||||
// The id to identify the sever.
|
// The id to identify the sever.
|
||||||
int64_t _server_id;
|
std::string _server_id;
|
||||||
private:
|
private:
|
||||||
// The key: vhost id, value: vhost object.
|
// The key: vhost id, value: vhost object.
|
||||||
std::map<int64_t, SrsStatisticVhost*> vhosts;
|
std::map<std::string, SrsStatisticVhost*> vhosts;
|
||||||
// The key: vhost url, value: vhost Object.
|
// The key: vhost url, value: vhost Object.
|
||||||
// @remark a fast index for vhosts.
|
// @remark a fast index for vhosts.
|
||||||
std::map<std::string, SrsStatisticVhost*> rvhosts;
|
std::map<std::string, SrsStatisticVhost*> rvhosts;
|
||||||
private:
|
private:
|
||||||
// The key: stream id, value: stream Object.
|
// The key: stream id, value: stream Object.
|
||||||
std::map<int64_t, SrsStatisticStream*> streams;
|
std::map<std::string, SrsStatisticStream*> streams;
|
||||||
// The key: stream url, value: stream Object.
|
// The key: stream url, value: stream Object.
|
||||||
// @remark a fast index for streams.
|
// @remark a fast index for streams.
|
||||||
std::map<std::string, SrsStatisticStream*> rstreams;
|
std::map<std::string, SrsStatisticStream*> rstreams;
|
||||||
private:
|
private:
|
||||||
// The key: client id, value: stream object.
|
// The key: client id, value: stream object.
|
||||||
std::map<int, SrsStatisticClient*> clients;
|
std::map<std::string, SrsStatisticClient*> clients;
|
||||||
// The server total kbps.
|
// The server total kbps.
|
||||||
SrsKbps* kbps;
|
SrsKbps* kbps;
|
||||||
SrsWallClock* clk;
|
SrsWallClock* clk;
|
||||||
// The perf stat for mw(merged write).
|
// The perf stat for mw(merged write).
|
||||||
SrsStatisticCategory* perf_iovs;
|
SrsStatisticCategory* perf_iovs;
|
||||||
SrsStatisticCategory* perf_msgs;
|
SrsStatisticCategory* perf_msgs;
|
||||||
SrsStatisticCategory* perf_sendmmsg;
|
|
||||||
SrsStatisticCategory* perf_gso;
|
|
||||||
SrsStatisticCategory* perf_rtp;
|
SrsStatisticCategory* perf_rtp;
|
||||||
SrsStatisticCategory* perf_rtc;
|
SrsStatisticCategory* perf_rtc;
|
||||||
SrsStatisticCategory* perf_bytes;
|
SrsStatisticCategory* perf_bytes;
|
||||||
SrsStatisticCategory* perf_dropped;
|
|
||||||
private:
|
private:
|
||||||
SrsStatistic();
|
SrsStatistic();
|
||||||
virtual ~SrsStatistic();
|
virtual ~SrsStatistic();
|
||||||
public:
|
public:
|
||||||
static SrsStatistic* instance();
|
static SrsStatistic* instance();
|
||||||
public:
|
public:
|
||||||
virtual SrsStatisticVhost* find_vhost(int vid);
|
virtual SrsStatisticVhost* find_vhost_by_id(std::string vid);
|
||||||
virtual SrsStatisticVhost* find_vhost(std::string name);
|
virtual SrsStatisticVhost* find_vhost_by_name(std::string name);
|
||||||
virtual SrsStatisticStream* find_stream(int sid);
|
virtual SrsStatisticStream* find_stream(std::string sid);
|
||||||
virtual SrsStatisticClient* find_client(int cid);
|
virtual SrsStatisticClient* find_client(std::string client_id);
|
||||||
public:
|
public:
|
||||||
// When got video info for stream.
|
// When got video info for stream.
|
||||||
virtual srs_error_t on_video_info(SrsRequest* req, SrsVideoCodecId vcodec, SrsAvcProfile avc_profile,
|
virtual srs_error_t on_video_info(SrsRequest* req, SrsVideoCodecId vcodec, SrsAvcProfile avc_profile,
|
||||||
|
@ -199,7 +197,7 @@ public:
|
||||||
// When publish stream.
|
// When publish stream.
|
||||||
// @param req the request object of publish connection.
|
// @param req the request object of publish connection.
|
||||||
// @param cid the cid of publish connection.
|
// @param cid the cid of publish connection.
|
||||||
virtual void on_stream_publish(SrsRequest* req, int cid);
|
virtual void on_stream_publish(SrsRequest* req, SrsContextId cid);
|
||||||
// When close stream.
|
// When close stream.
|
||||||
virtual void on_stream_close(SrsRequest* req);
|
virtual void on_stream_close(SrsRequest* req);
|
||||||
public:
|
public:
|
||||||
|
@ -208,23 +206,24 @@ public:
|
||||||
// @param req, the client request object.
|
// @param req, the client request object.
|
||||||
// @param conn, the physical absract connection object.
|
// @param conn, the physical absract connection object.
|
||||||
// @param type, the type of connection.
|
// @param type, the type of connection.
|
||||||
virtual srs_error_t on_client(int id, SrsRequest* req, SrsConnection* conn, SrsRtmpConnType type);
|
// TODO: FIXME: We should not use context id as client id.
|
||||||
|
virtual srs_error_t on_client(SrsContextId id, SrsRequest* req, ISrsExpire* conn, SrsRtmpConnType type);
|
||||||
// Client disconnect
|
// Client disconnect
|
||||||
// @remark the on_disconnect always call, while the on_client is call when
|
// @remark the on_disconnect always call, while the on_client is call when
|
||||||
// only got the request object, so the client specified by id maybe not
|
// only got the request object, so the client specified by id maybe not
|
||||||
// exists in stat.
|
// exists in stat.
|
||||||
virtual void on_disconnect(int id);
|
// TODO: FIXME: We should not use context id as client id.
|
||||||
|
virtual void on_disconnect(const SrsContextId& id);
|
||||||
// Sample the kbps, add delta bytes of conn.
|
// Sample the kbps, add delta bytes of conn.
|
||||||
// Use kbps_sample() to get all result of kbps stat.
|
// Use kbps_sample() to get all result of kbps stat.
|
||||||
// TODO: FIXME: the add delta must use ISrsKbpsDelta interface instead.
|
virtual void kbps_add_delta(const SrsContextId& cid, ISrsKbpsDelta* delta);
|
||||||
virtual void kbps_add_delta(SrsConnection* conn);
|
|
||||||
// Calc the result for all kbps.
|
// Calc the result for all kbps.
|
||||||
// @return the server kbps.
|
// @return the server kbps.
|
||||||
virtual SrsKbps* kbps_sample();
|
virtual SrsKbps* kbps_sample();
|
||||||
public:
|
public:
|
||||||
// Get the server id, used to identify the server.
|
// Get the server id, used to identify the server.
|
||||||
// For example, when restart, the server id must changed.
|
// For example, when restart, the server id must changed.
|
||||||
virtual int64_t server_id();
|
virtual std::string server_id();
|
||||||
// Dumps the vhosts to amf0 array.
|
// Dumps the vhosts to amf0 array.
|
||||||
virtual srs_error_t dumps_vhosts(SrsJsonArray* arr);
|
virtual srs_error_t dumps_vhosts(SrsJsonArray* arr);
|
||||||
// Dumps the streams to amf0 array.
|
// Dumps the streams to amf0 array.
|
||||||
|
@ -248,27 +247,14 @@ public:
|
||||||
// For example, a RTC/opus packet maybe package to three RTP packets.
|
// For example, a RTC/opus packet maybe package to three RTP packets.
|
||||||
virtual void perf_on_rtp_packets(int nb_packets);
|
virtual void perf_on_rtp_packets(int nb_packets);
|
||||||
virtual srs_error_t dumps_perf_rtp_packets(SrsJsonObject* obj);
|
virtual srs_error_t dumps_perf_rtp_packets(SrsJsonObject* obj);
|
||||||
public:
|
|
||||||
// Stat for packets UDP GSO, nb_packets is the merged RTP packets.
|
|
||||||
// For example, three RTP/audio packets maybe GSO to one msghdr.
|
|
||||||
virtual void perf_on_gso_packets(int nb_packets);
|
|
||||||
virtual srs_error_t dumps_perf_gso(SrsJsonObject* obj);
|
|
||||||
public:
|
public:
|
||||||
// Stat for TCP writev, nb_iovs is the total number of iovec.
|
// Stat for TCP writev, nb_iovs is the total number of iovec.
|
||||||
virtual void perf_on_writev_iovs(int nb_iovs);
|
virtual void perf_on_writev_iovs(int nb_iovs);
|
||||||
virtual srs_error_t dumps_perf_writev_iovs(SrsJsonObject* obj);
|
virtual srs_error_t dumps_perf_writev_iovs(SrsJsonObject* obj);
|
||||||
public:
|
|
||||||
// Stat for packets UDP sendmmsg, nb_packets is the vlen for sendmmsg.
|
|
||||||
virtual void perf_on_sendmmsg_packets(int nb_packets);
|
|
||||||
virtual srs_error_t dumps_perf_sendmmsg(SrsJsonObject* obj);
|
|
||||||
public:
|
public:
|
||||||
// Stat for bytes, nn_bytes is the size of bytes, nb_padding is padding bytes.
|
// Stat for bytes, nn_bytes is the size of bytes, nb_padding is padding bytes.
|
||||||
virtual void perf_on_rtc_bytes(int nn_bytes, int nn_rtp_bytes, int nn_padding);
|
virtual void perf_on_rtc_bytes(int nn_bytes, int nn_rtp_bytes, int nn_padding);
|
||||||
virtual srs_error_t dumps_perf_bytes(SrsJsonObject* obj);
|
virtual srs_error_t dumps_perf_bytes(SrsJsonObject* obj);
|
||||||
public:
|
|
||||||
// Stat for rtc messages, nn_rtc is rtc messages, nn_dropped is dropped messages.
|
|
||||||
virtual void perf_on_dropped(int nn_msgs, int nn_rtc, int nn_dropped);
|
|
||||||
virtual srs_error_t dumps_perf_dropped(SrsJsonObject* obj);
|
|
||||||
public:
|
public:
|
||||||
// Reset all perf stat data.
|
// Reset all perf stat data.
|
||||||
virtual void reset_perf();
|
virtual void reset_perf();
|
||||||
|
|
|
@ -1,92 +0,0 @@
|
||||||
/**
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2013-2020 Winlin
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
* this software and associated documentation files (the "Software"), to deal in
|
|
||||||
* the Software without restriction, including without limitation the rights to
|
|
||||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
||||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
* subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <srs_app_thread.hpp>
|
|
||||||
|
|
||||||
#include <srs_kernel_error.hpp>
|
|
||||||
#include <srs_kernel_log.hpp>
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
SrsCoroutineManager::SrsCoroutineManager()
|
|
||||||
{
|
|
||||||
cond = srs_cond_new();
|
|
||||||
trd = new SrsSTCoroutine("manager", this);
|
|
||||||
}
|
|
||||||
|
|
||||||
SrsCoroutineManager::~SrsCoroutineManager()
|
|
||||||
{
|
|
||||||
srs_freep(trd);
|
|
||||||
srs_cond_destroy(cond);
|
|
||||||
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_error_t SrsCoroutineManager::start()
|
|
||||||
{
|
|
||||||
srs_error_t err = srs_success;
|
|
||||||
|
|
||||||
if ((err = trd->start()) != srs_success) {
|
|
||||||
return srs_error_wrap(err, "coroutine manager");
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_error_t SrsCoroutineManager::cycle()
|
|
||||||
{
|
|
||||||
srs_error_t err = srs_success;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
if ((err = trd->pull()) != srs_success) {
|
|
||||||
return srs_error_wrap(err, "coroutine mansger");
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_cond_wait(cond);
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SrsCoroutineManager::remove(ISrsConnection* c)
|
|
||||||
{
|
|
||||||
conns.push_back(c);
|
|
||||||
srs_cond_signal(cond);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SrsCoroutineManager::clear()
|
|
||||||
{
|
|
||||||
// To prevent thread switch when delete connection,
|
|
||||||
// we copy all connections then free one by one.
|
|
||||||
vector<ISrsConnection*> copy = conns;
|
|
||||||
conns.clear();
|
|
||||||
|
|
||||||
vector<ISrsConnection*>::iterator it;
|
|
||||||
for (it = copy.begin(); it != copy.end(); ++it) {
|
|
||||||
ISrsConnection* conn = *it;
|
|
||||||
srs_freep(conn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
/**
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2013-2020 Winlin
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
* this software and associated documentation files (the "Software"), to deal in
|
|
||||||
* the Software without restriction, including without limitation the rights to
|
|
||||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
||||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
* subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SRS_APP_THREAD_HPP
|
|
||||||
#define SRS_APP_THREAD_HPP
|
|
||||||
|
|
||||||
#include <srs_core.hpp>
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <srs_app_st.hpp>
|
|
||||||
#include <srs_service_conn.hpp>
|
|
||||||
|
|
||||||
// The coroutine manager use a thread to delete a connection, which will stop the service
|
|
||||||
// thread, for example, when the RTMP connection thread cycle terminated, it will notify
|
|
||||||
// the manager(the server) to remove the connection from list of server and push it to
|
|
||||||
// the manager thread to delete it, finally the thread of connection will stop.
|
|
||||||
class SrsCoroutineManager : virtual public ISrsCoroutineHandler, virtual public IConnectionManager
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
SrsCoroutine* trd;
|
|
||||||
std::vector<ISrsConnection*> conns;
|
|
||||||
srs_cond_t cond;
|
|
||||||
public:
|
|
||||||
SrsCoroutineManager();
|
|
||||||
virtual ~SrsCoroutineManager();
|
|
||||||
public:
|
|
||||||
srs_error_t start();
|
|
||||||
// Interface ISrsCoroutineHandler
|
|
||||||
public:
|
|
||||||
virtual srs_error_t cycle();
|
|
||||||
// Interface IConnectionManager
|
|
||||||
public:
|
|
||||||
virtual void remove(ISrsConnection* c);
|
|
||||||
private:
|
|
||||||
void clear();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
#ifdef SRS_AUTO_OSX
|
#ifdef SRS_OSX
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
#endif
|
#endif
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
@ -235,7 +235,7 @@ void srs_update_system_rusage()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_srs_system_rusage.sample_time = srsu2ms(srs_get_system_time());
|
_srs_system_rusage.sample_time = srsu2ms(srs_update_system_time());
|
||||||
|
|
||||||
_srs_system_rusage.ok = true;
|
_srs_system_rusage.ok = true;
|
||||||
}
|
}
|
||||||
|
@ -329,7 +329,7 @@ SrsProcSystemStat* srs_get_system_proc_stat()
|
||||||
|
|
||||||
bool get_proc_system_stat(SrsProcSystemStat& r)
|
bool get_proc_system_stat(SrsProcSystemStat& r)
|
||||||
{
|
{
|
||||||
#ifndef SRS_AUTO_OSX
|
#ifndef SRS_OSX
|
||||||
FILE* f = fopen("/proc/stat", "r");
|
FILE* f = fopen("/proc/stat", "r");
|
||||||
if (f == NULL) {
|
if (f == NULL) {
|
||||||
srs_warn("open system cpu stat failed, ignore");
|
srs_warn("open system cpu stat failed, ignore");
|
||||||
|
@ -368,7 +368,7 @@ bool get_proc_system_stat(SrsProcSystemStat& r)
|
||||||
|
|
||||||
bool get_proc_self_stat(SrsProcSelfStat& r)
|
bool get_proc_self_stat(SrsProcSelfStat& r)
|
||||||
{
|
{
|
||||||
#ifndef SRS_AUTO_OSX
|
#ifndef SRS_OSX
|
||||||
FILE* f = fopen("/proc/self/stat", "r");
|
FILE* f = fopen("/proc/self/stat", "r");
|
||||||
if (f == NULL) {
|
if (f == NULL) {
|
||||||
srs_warn("open self cpu stat failed, ignore");
|
srs_warn("open self cpu stat failed, ignore");
|
||||||
|
@ -420,7 +420,7 @@ void srs_update_proc_stat()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
r.sample_time = srsu2ms(srs_get_system_time());
|
r.sample_time = srsu2ms(srs_update_system_time());
|
||||||
|
|
||||||
// calc usage in percent
|
// calc usage in percent
|
||||||
SrsProcSystemStat& o = _srs_system_cpu_system_stat;
|
SrsProcSystemStat& o = _srs_system_cpu_system_stat;
|
||||||
|
@ -446,7 +446,7 @@ void srs_update_proc_stat()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
r.sample_time = srsu2ms(srs_get_system_time());
|
r.sample_time = srsu2ms(srs_update_system_time());
|
||||||
|
|
||||||
// calc usage in percent
|
// calc usage in percent
|
||||||
SrsProcSelfStat& o = _srs_system_cpu_self_stat;
|
SrsProcSelfStat& o = _srs_system_cpu_self_stat;
|
||||||
|
@ -491,14 +491,14 @@ SrsDiskStat* srs_get_disk_stat()
|
||||||
|
|
||||||
bool srs_get_disk_vmstat_stat(SrsDiskStat& r)
|
bool srs_get_disk_vmstat_stat(SrsDiskStat& r)
|
||||||
{
|
{
|
||||||
#ifndef SRS_AUTO_OSX
|
#ifndef SRS_OSX
|
||||||
FILE* f = fopen("/proc/vmstat", "r");
|
FILE* f = fopen("/proc/vmstat", "r");
|
||||||
if (f == NULL) {
|
if (f == NULL) {
|
||||||
srs_warn("open vmstat failed, ignore");
|
srs_warn("open vmstat failed, ignore");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
r.sample_time = srsu2ms(srs_get_system_time());
|
r.sample_time = srsu2ms(srs_update_system_time());
|
||||||
|
|
||||||
static char buf[1024];
|
static char buf[1024];
|
||||||
while (fgets(buf, sizeof(buf), f)) {
|
while (fgets(buf, sizeof(buf), f)) {
|
||||||
|
@ -521,9 +521,9 @@ bool srs_get_disk_vmstat_stat(SrsDiskStat& r)
|
||||||
bool srs_get_disk_diskstats_stat(SrsDiskStat& r)
|
bool srs_get_disk_diskstats_stat(SrsDiskStat& r)
|
||||||
{
|
{
|
||||||
r.ok = true;
|
r.ok = true;
|
||||||
r.sample_time = srsu2ms(srs_get_system_time());
|
r.sample_time = srsu2ms(srs_update_system_time());
|
||||||
|
|
||||||
#ifndef SRS_AUTO_OSX
|
#ifndef SRS_OSX
|
||||||
// if disabled, ignore all devices.
|
// if disabled, ignore all devices.
|
||||||
SrsConfDirective* conf = _srs_config->get_stats_disk_device();
|
SrsConfDirective* conf = _srs_config->get_stats_disk_device();
|
||||||
if (conf == NULL) {
|
if (conf == NULL) {
|
||||||
|
@ -687,7 +687,7 @@ void srs_update_meminfo()
|
||||||
{
|
{
|
||||||
SrsMemInfo& r = _srs_system_meminfo;
|
SrsMemInfo& r = _srs_system_meminfo;
|
||||||
|
|
||||||
#ifndef SRS_AUTO_OSX
|
#ifndef SRS_OSX
|
||||||
FILE* f = fopen("/proc/meminfo", "r");
|
FILE* f = fopen("/proc/meminfo", "r");
|
||||||
if (f == NULL) {
|
if (f == NULL) {
|
||||||
srs_warn("open meminfo failed, ignore");
|
srs_warn("open meminfo failed, ignore");
|
||||||
|
@ -715,7 +715,7 @@ void srs_update_meminfo()
|
||||||
fclose(f);
|
fclose(f);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
r.sample_time = srsu2ms(srs_get_system_time());
|
r.sample_time = srsu2ms(srs_update_system_time());
|
||||||
r.MemActive = r.MemTotal - r.MemFree;
|
r.MemActive = r.MemTotal - r.MemFree;
|
||||||
r.RealInUse = r.MemActive - r.Buffers - r.Cached;
|
r.RealInUse = r.MemActive - r.Buffers - r.Cached;
|
||||||
r.NotInUse = r.MemTotal - r.RealInUse;
|
r.NotInUse = r.MemTotal - r.RealInUse;
|
||||||
|
@ -781,7 +781,7 @@ void srs_update_platform_info()
|
||||||
|
|
||||||
r.srs_startup_time = srsu2ms(srs_get_system_startup_time());
|
r.srs_startup_time = srsu2ms(srs_get_system_startup_time());
|
||||||
|
|
||||||
#ifndef SRS_AUTO_OSX
|
#ifndef SRS_OSX
|
||||||
if (true) {
|
if (true) {
|
||||||
FILE* f = fopen("/proc/uptime", "r");
|
FILE* f = fopen("/proc/uptime", "r");
|
||||||
if (f == NULL) {
|
if (f == NULL) {
|
||||||
|
@ -893,7 +893,7 @@ int srs_get_network_devices_count()
|
||||||
|
|
||||||
void srs_update_network_devices()
|
void srs_update_network_devices()
|
||||||
{
|
{
|
||||||
#ifndef SRS_AUTO_OSX
|
#ifndef SRS_OSX
|
||||||
if (true) {
|
if (true) {
|
||||||
FILE* f = fopen("/proc/net/dev", "r");
|
FILE* f = fopen("/proc/net/dev", "r");
|
||||||
if (f == NULL) {
|
if (f == NULL) {
|
||||||
|
@ -924,7 +924,7 @@ void srs_update_network_devices()
|
||||||
_nb_srs_system_network_devices = i + 1;
|
_nb_srs_system_network_devices = i + 1;
|
||||||
srs_info("scan network device ifname=%s, total=%d", r.name, _nb_srs_system_network_devices);
|
srs_info("scan network device ifname=%s, total=%d", r.name, _nb_srs_system_network_devices);
|
||||||
|
|
||||||
r.sample_time = srsu2ms(srs_get_system_time());
|
r.sample_time = srsu2ms(srs_update_system_time());
|
||||||
r.ok = true;
|
r.ok = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -940,6 +940,9 @@ SrsNetworkRtmpServer::SrsNetworkRtmpServer()
|
||||||
nb_conn_sys = nb_conn_srs = 0;
|
nb_conn_sys = nb_conn_srs = 0;
|
||||||
nb_conn_sys_et = nb_conn_sys_tw = 0;
|
nb_conn_sys_et = nb_conn_sys_tw = 0;
|
||||||
nb_conn_sys_udp = 0;
|
nb_conn_sys_udp = 0;
|
||||||
|
rkbps = skbps = 0;
|
||||||
|
rkbps_30s = skbps_30s = 0;
|
||||||
|
rkbps_5m = skbps_5m = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static SrsNetworkRtmpServer _srs_network_rtmp_server;
|
static SrsNetworkRtmpServer _srs_network_rtmp_server;
|
||||||
|
@ -978,7 +981,7 @@ void srs_update_rtmp_server(int nb_conn, SrsKbps* kbps)
|
||||||
int nb_tcp_mem = 0;
|
int nb_tcp_mem = 0;
|
||||||
int nb_udp4 = 0;
|
int nb_udp4 = 0;
|
||||||
|
|
||||||
#ifndef SRS_AUTO_OSX
|
#ifndef SRS_OSX
|
||||||
if (true) {
|
if (true) {
|
||||||
FILE* f = fopen("/proc/net/sockstat", "r");
|
FILE* f = fopen("/proc/net/sockstat", "r");
|
||||||
if (f == NULL) {
|
if (f == NULL) {
|
||||||
|
@ -1021,7 +1024,7 @@ void srs_update_rtmp_server(int nb_conn, SrsKbps* kbps)
|
||||||
|
|
||||||
int nb_tcp_estab = 0;
|
int nb_tcp_estab = 0;
|
||||||
|
|
||||||
#ifndef SRS_AUTO_OSX
|
#ifndef SRS_OSX
|
||||||
if (true) {
|
if (true) {
|
||||||
FILE* f = fopen("/proc/net/snmp", "r");
|
FILE* f = fopen("/proc/net/snmp", "r");
|
||||||
if (f == NULL) {
|
if (f == NULL) {
|
||||||
|
@ -1067,7 +1070,7 @@ void srs_update_rtmp_server(int nb_conn, SrsKbps* kbps)
|
||||||
r.ok = true;
|
r.ok = true;
|
||||||
|
|
||||||
r.nb_conn_srs = nb_conn;
|
r.nb_conn_srs = nb_conn;
|
||||||
r.sample_time = srsu2ms(srs_get_system_time());
|
r.sample_time = srsu2ms(srs_update_system_time());
|
||||||
|
|
||||||
r.rbytes = kbps->get_recv_bytes();
|
r.rbytes = kbps->get_recv_bytes();
|
||||||
r.rkbps = kbps->get_recv_kbps();
|
r.rkbps = kbps->get_recv_kbps();
|
||||||
|
@ -1143,6 +1146,28 @@ string srs_get_peer_ip(int fd)
|
||||||
return std::string(saddr);
|
return std::string(saddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int srs_get_peer_port(int fd)
|
||||||
|
{
|
||||||
|
// discovery client information
|
||||||
|
sockaddr_storage addr;
|
||||||
|
socklen_t addrlen = sizeof(addr);
|
||||||
|
if (getpeername(fd, (sockaddr*)&addr, &addrlen) == -1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int port = 0;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
bool srs_is_boolean(string str)
|
bool srs_is_boolean(string str)
|
||||||
{
|
{
|
||||||
return str == "true" || str == "false";
|
return str == "true" || str == "false";
|
||||||
|
@ -1165,7 +1190,7 @@ void srs_api_dump_summaries(SrsJsonObject* obj)
|
||||||
self_mem_percent = (float)(r->r.ru_maxrss / (double)m->MemTotal);
|
self_mem_percent = (float)(r->r.ru_maxrss / (double)m->MemTotal);
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t now = srsu2ms(srs_get_system_time());
|
int64_t now = srsu2ms(srs_update_system_time());
|
||||||
double srs_uptime = (now - p->srs_startup_time) / 100 / 10.0;
|
double srs_uptime = (now - p->srs_startup_time) / 100 / 10.0;
|
||||||
|
|
||||||
int64_t n_sample_time = 0;
|
int64_t n_sample_time = 0;
|
||||||
|
@ -1259,37 +1284,63 @@ void srs_api_dump_summaries(SrsJsonObject* obj)
|
||||||
sys->set("conn_srs", SrsJsonAny::integer(nrs->nb_conn_srs));
|
sys->set("conn_srs", SrsJsonAny::integer(nrs->nb_conn_srs));
|
||||||
}
|
}
|
||||||
|
|
||||||
string srs_string_dumps_hex(const std::string& str, const int& limit)
|
string srs_string_dumps_hex(const std::string& str)
|
||||||
{
|
{
|
||||||
return srs_string_dumps_hex(str.c_str(), str.size(), limit);
|
return srs_string_dumps_hex(str.c_str(), str.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
string srs_string_dumps_hex(const char* buf, const int length, const int& limit)
|
string srs_string_dumps_hex(const char* str, int length)
|
||||||
{
|
{
|
||||||
string ret;
|
return srs_string_dumps_hex(str, length, INT_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
char tmp_buf[1024*16];
|
string srs_string_dumps_hex(const char* str, int length, int limit)
|
||||||
tmp_buf[0] = '\n';
|
{
|
||||||
int len = 1;
|
return srs_string_dumps_hex(str, length, limit, ' ', 128, '\n');
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < length && i < limit; ++i) {
|
string srs_string_dumps_hex(const char* str, int length, int limit, char seperator, int line_limit, char newline)
|
||||||
int nb = snprintf(tmp_buf + len, sizeof(tmp_buf) - len - 2, "%02X ", (uint8_t)buf[i]);
|
{
|
||||||
if (nb <= 0)
|
// 1 byte trailing '\0'.
|
||||||
|
const int LIMIT = 1024*16 + 1;
|
||||||
|
static char buf[LIMIT];
|
||||||
|
|
||||||
|
int len = 0;
|
||||||
|
for (int i = 0; i < length && i < limit && len < LIMIT; ++i) {
|
||||||
|
int nb = snprintf(buf + len, LIMIT - len, "%02x", (uint8_t)str[i]);
|
||||||
|
if (nb < 0 || nb >= LIMIT - len) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
len += nb;
|
len += nb;
|
||||||
|
|
||||||
if (i % 48 == 47) {
|
// Only append seperator and newline when not last byte.
|
||||||
tmp_buf[len++] = '\n';
|
if (i < length - 1 && i < limit - 1 && len < LIMIT) {
|
||||||
ret.append(tmp_buf, len);
|
if (seperator) {
|
||||||
len = 0;
|
buf[len++] = seperator;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newline && line_limit && i > 0 && ((i + 1) % line_limit) == 0) {
|
||||||
|
buf[len++] = newline;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tmp_buf[len] = '\0';
|
|
||||||
ret.append(tmp_buf, len);
|
|
||||||
|
|
||||||
return ret;
|
// Empty string.
|
||||||
|
if (len <= 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// If overflow, cut the trailing newline.
|
||||||
|
if (newline && len >= LIMIT - 2 && buf[len - 1] == newline) {
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If overflow, cut the trailing seperator.
|
||||||
|
if (seperator && len >= LIMIT - 3 && buf[len - 1] == seperator) {
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(buf, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
string srs_getenv(string key)
|
string srs_getenv(string key)
|
||||||
|
|
|
@ -640,6 +640,7 @@ extern std::string srs_get_local_ip(int fd);
|
||||||
extern int srs_get_local_port(int fd);
|
extern int srs_get_local_port(int fd);
|
||||||
// Where peer ip is the client public ip which connected to server.
|
// Where peer ip is the client public ip which connected to server.
|
||||||
extern std::string srs_get_peer_ip(int fd);
|
extern std::string srs_get_peer_ip(int fd);
|
||||||
|
extern int srs_get_peer_port(int fd);
|
||||||
|
|
||||||
// Whether string is boolean
|
// Whether string is boolean
|
||||||
// is_bool("true") == true
|
// is_bool("true") == true
|
||||||
|
@ -651,8 +652,11 @@ extern bool srs_is_boolean(std::string str);
|
||||||
extern void srs_api_dump_summaries(SrsJsonObject* obj);
|
extern void srs_api_dump_summaries(SrsJsonObject* obj);
|
||||||
|
|
||||||
// Dump string(str in length) to hex, it will process min(limit, length) chars.
|
// Dump string(str in length) to hex, it will process min(limit, length) chars.
|
||||||
extern std::string srs_string_dumps_hex(const std::string& str, const int& limit = INT_MAX);
|
// Append seperator between each elem, and newline when exceed line_limit, '\0' to ignore.
|
||||||
extern std::string srs_string_dumps_hex(const char* str, const int length, const int& limit = INT_MAX);
|
extern std::string srs_string_dumps_hex(const std::string& str);
|
||||||
|
extern std::string srs_string_dumps_hex(const char* str, int length);
|
||||||
|
extern std::string srs_string_dumps_hex(const char* str, int length, int limit);
|
||||||
|
extern std::string srs_string_dumps_hex(const char* str, int length, int limit, char seperator, int line_limit, char newline);
|
||||||
|
|
||||||
// Get ENV variable, which may starts with $.
|
// Get ENV variable, which may starts with $.
|
||||||
// srs_getenv("EIP") === srs_getenv("$EIP")
|
// srs_getenv("EIP") === srs_getenv("$EIP")
|
||||||
|
|
|
@ -23,4 +23,43 @@
|
||||||
|
|
||||||
#include <srs_core.hpp>
|
#include <srs_core.hpp>
|
||||||
|
|
||||||
|
_SrsContextId::_SrsContextId()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
_SrsContextId::_SrsContextId(const _SrsContextId& cp)
|
||||||
|
{
|
||||||
|
v_ = cp.v_;
|
||||||
|
}
|
||||||
|
|
||||||
|
_SrsContextId& _SrsContextId::operator=(const _SrsContextId& cp)
|
||||||
|
{
|
||||||
|
v_ = cp.v_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
_SrsContextId::~_SrsContextId()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* _SrsContextId::c_str() const
|
||||||
|
{
|
||||||
|
return v_.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _SrsContextId::empty() const
|
||||||
|
{
|
||||||
|
return v_.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
int _SrsContextId::compare(const _SrsContextId& to) const
|
||||||
|
{
|
||||||
|
return v_.compare(to.v_);
|
||||||
|
}
|
||||||
|
|
||||||
|
_SrsContextId& _SrsContextId::set_value(const std::string& v)
|
||||||
|
{
|
||||||
|
v_ = v;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,11 +33,6 @@
|
||||||
// The macros generated by configure script.
|
// The macros generated by configure script.
|
||||||
#include <srs_auto_headers.hpp>
|
#include <srs_auto_headers.hpp>
|
||||||
|
|
||||||
// Alias for debug.
|
|
||||||
#ifdef SRS_AUTO_DEBUG
|
|
||||||
#define SRS_DEBUG
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// To convert macro values to string.
|
// To convert macro values to string.
|
||||||
// @see https://gcc.gnu.org/onlinedocs/cpp/Stringification.html#Stringification
|
// @see https://gcc.gnu.org/onlinedocs/cpp/Stringification.html#Stringification
|
||||||
#define SRS_INTERNAL_STR(v) #v
|
#define SRS_INTERNAL_STR(v) #v
|
||||||
|
@ -51,10 +46,11 @@
|
||||||
#define RTMP_SIG_SRS_AUTHORS "Winlin,Wenjie,Runner365,John,B.P.Y,Lixin"
|
#define RTMP_SIG_SRS_AUTHORS "Winlin,Wenjie,Runner365,John,B.P.Y,Lixin"
|
||||||
#define RTMP_SIG_SRS_VERSION SRS_XSTR(VERSION_MAJOR) "." SRS_XSTR(VERSION_MINOR) "." SRS_XSTR(VERSION_REVISION)
|
#define RTMP_SIG_SRS_VERSION SRS_XSTR(VERSION_MAJOR) "." SRS_XSTR(VERSION_MINOR) "." SRS_XSTR(VERSION_REVISION)
|
||||||
#define RTMP_SIG_SRS_SERVER RTMP_SIG_SRS_KEY "/" RTMP_SIG_SRS_VERSION "(" RTMP_SIG_SRS_CODE ")"
|
#define RTMP_SIG_SRS_SERVER RTMP_SIG_SRS_KEY "/" RTMP_SIG_SRS_VERSION "(" RTMP_SIG_SRS_CODE ")"
|
||||||
|
#define RTMP_SIG_SRS_DOMAIN "ossrs.net"
|
||||||
|
|
||||||
// The current stable release.
|
// The current stable release.
|
||||||
#define VERSION_STABLE 3
|
#define VERSION_STABLE 3
|
||||||
#define VERSION_STABLE_BRANCH SRS_XSTR(VERSION_STABLE)".0release"
|
#define VERSION_STABLE_BRANCH SRS_XSTR(VERSION_STABLE) ".0release"
|
||||||
|
|
||||||
// For 32bit os, 2G big file limit for unistd io,
|
// For 32bit os, 2G big file limit for unistd io,
|
||||||
// ie. read/write/lseek to use 64bits size for huge file.
|
// ie. read/write/lseek to use 64bits size for huge file.
|
||||||
|
@ -68,7 +64,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// For RTC/FFMPEG build.
|
// For RTC/FFMPEG build.
|
||||||
#if defined(SRS_AUTO_RTC) && !defined(__STDC_CONSTANT_MACROS)
|
#if defined(SRS_RTC) && !defined(__STDC_CONSTANT_MACROS)
|
||||||
#define __STDC_CONSTANT_MACROS
|
#define __STDC_CONSTANT_MACROS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -118,4 +114,34 @@
|
||||||
class SrsCplxError;
|
class SrsCplxError;
|
||||||
typedef SrsCplxError* srs_error_t;
|
typedef SrsCplxError* srs_error_t;
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
// The context ID, it default to a string object, we can also use other objects.
|
||||||
|
// @remark User can directly user string as SrsContextId, we user struct to ensure the context is an object.
|
||||||
|
#if 1
|
||||||
|
class _SrsContextId
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::string v_;
|
||||||
|
public:
|
||||||
|
_SrsContextId();
|
||||||
|
_SrsContextId(const _SrsContextId& cp);
|
||||||
|
_SrsContextId& operator=(const _SrsContextId& cp);
|
||||||
|
virtual ~_SrsContextId();
|
||||||
|
public:
|
||||||
|
const char* c_str() const;
|
||||||
|
bool empty() const;
|
||||||
|
// Compare the two context id. @see http://www.cplusplus.com/reference/string/string/compare/
|
||||||
|
// 0 They compare equal
|
||||||
|
// <0 Either the value of the first character that does not match is lower in the compared string, or all compared characters match but the compared string is shorter.
|
||||||
|
// >0 Either the value of the first character that does not match is greater in the compared string, or all compared characters match but the compared string is longer.
|
||||||
|
int compare(const _SrsContextId& to) const;
|
||||||
|
// Set the value of context id.
|
||||||
|
_SrsContextId& set_value(const std::string& v);
|
||||||
|
};
|
||||||
|
typedef _SrsContextId SrsContextId;
|
||||||
|
#else
|
||||||
|
// Actually, we can directly user string as SrsContextId.
|
||||||
|
typedef std::string SrsContextId;
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
|
|
||||||
#include <srs_core_mem_watch.hpp>
|
#include <srs_core_mem_watch.hpp>
|
||||||
|
|
||||||
#ifdef SRS_AUTO_MEM_WATCH
|
#ifdef SRS_MEM_WATCH
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
|
|
||||||
#include <srs_core.hpp>
|
#include <srs_core.hpp>
|
||||||
|
|
||||||
#ifdef SRS_AUTO_MEM_WATCH
|
#ifdef SRS_MEM_WATCH
|
||||||
|
|
||||||
#warning "MemoryWatch is deprecated."
|
#warning "MemoryWatch is deprecated."
|
||||||
|
|
||||||
|
|
|
@ -125,6 +125,7 @@
|
||||||
* @remark this improve performance for large connectios.
|
* @remark this improve performance for large connectios.
|
||||||
* @see https://github.com/ossrs/srs/issues/251
|
* @see https://github.com/ossrs/srs/issues/251
|
||||||
*/
|
*/
|
||||||
|
// TODO: FIXME: Should always enable it.
|
||||||
#define SRS_PERF_QUEUE_COND_WAIT
|
#define SRS_PERF_QUEUE_COND_WAIT
|
||||||
#ifdef SRS_PERF_QUEUE_COND_WAIT
|
#ifdef SRS_PERF_QUEUE_COND_WAIT
|
||||||
// For RTMP, use larger wait queue.
|
// For RTMP, use larger wait queue.
|
||||||
|
@ -192,27 +193,5 @@
|
||||||
#define SRS_PERF_GLIBC_MEMORY_CHECK
|
#define SRS_PERF_GLIBC_MEMORY_CHECK
|
||||||
#undef SRS_PERF_GLIBC_MEMORY_CHECK
|
#undef SRS_PERF_GLIBC_MEMORY_CHECK
|
||||||
|
|
||||||
// For RTC, how many iovs we alloc for each mmsghdr for GSO.
|
|
||||||
// Assume that there are 3300 clients, say, 10000 msgs in queue to send, the memory is:
|
|
||||||
// 2 # We have two queue, cache and hotspot.
|
|
||||||
// * 4 # We have reuseport, each have msg cache queue.
|
|
||||||
// * (64 + 16*SRS_PERF_RTC_GSO_MAX + SRS_PERF_RTC_GSO_IOVS * 1500) # Each message size.
|
|
||||||
// * 10000 # Total messages.
|
|
||||||
// = 197MB # For SRS_PERF_RTC_GSO_IOVS = 1
|
|
||||||
// = 311MB # For SRS_PERF_RTC_GSO_IOVS = 2
|
|
||||||
// = 540MB # For SRS_PERF_RTC_GSO_IOVS = 4
|
|
||||||
// = 998MB # For SRS_PERF_RTC_GSO_IOVS = 8
|
|
||||||
#if defined(__linux__)
|
|
||||||
#define SRS_PERF_RTC_GSO_IOVS 4
|
|
||||||
#else
|
|
||||||
#define SRS_PERF_RTC_GSO_IOVS 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// For RTC, the max iovs in msghdr, the max packets sent in a msghdr.
|
|
||||||
#define SRS_PERF_RTC_GSO_MAX 64
|
|
||||||
|
|
||||||
// For RTC, the max count of RTP packets we process in one loop.
|
|
||||||
#define SRS_PERF_RTC_RTP_PACKETS 1024
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -23,3 +23,12 @@
|
||||||
|
|
||||||
#include <srs_core_time.hpp>
|
#include <srs_core_time.hpp>
|
||||||
|
|
||||||
|
srs_utime_t srs_duration(srs_utime_t start, srs_utime_t end)
|
||||||
|
{
|
||||||
|
if (start == 0 || end == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return end - start;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,9 @@ typedef int64_t srs_utime_t;
|
||||||
#define srsu2ms(us) ((us) / SRS_UTIME_MILLISECONDS)
|
#define srsu2ms(us) ((us) / SRS_UTIME_MILLISECONDS)
|
||||||
#define srsu2msi(us) int((us) / SRS_UTIME_MILLISECONDS)
|
#define srsu2msi(us) int((us) / SRS_UTIME_MILLISECONDS)
|
||||||
|
|
||||||
|
// Them time duration = end - start. return 0, if start or end is 0.
|
||||||
|
srs_utime_t srs_duration(srs_utime_t start, srs_utime_t end);
|
||||||
|
|
||||||
// The time unit in ms, for example 120 * SRS_UTIME_SECONDS means 120s.
|
// The time unit in ms, for example 120 * SRS_UTIME_SECONDS means 120s.
|
||||||
#define SRS_UTIME_SECONDS 1000000LL
|
#define SRS_UTIME_SECONDS 1000000LL
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,6 @@
|
||||||
#ifndef SRS_CORE_VERSION3_HPP
|
#ifndef SRS_CORE_VERSION3_HPP
|
||||||
#define SRS_CORE_VERSION3_HPP
|
#define SRS_CORE_VERSION3_HPP
|
||||||
|
|
||||||
#define SRS_VERSION3_REVISION 140
|
#define SRS_VERSION3_REVISION 157
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -24,6 +24,6 @@
|
||||||
#ifndef SRS_CORE_VERSION4_HPP
|
#ifndef SRS_CORE_VERSION4_HPP
|
||||||
#define SRS_CORE_VERSION4_HPP
|
#define SRS_CORE_VERSION4_HPP
|
||||||
|
|
||||||
#define SRS_VERSION4_REVISION 23
|
#define SRS_VERSION4_REVISION 62
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue