1
0
Fork 0
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:
winlin 2021-01-18 11:30:47 +08:00
parent 00395588bc
commit 5e3e013c60
183 changed files with 27373 additions and 13949 deletions

View file

@ -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.

View 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.
--export-librtmp-single=<path> Export srs-librtmp to a single file(.h+.cpp) in path.
--build-tag=<TAG> Set the build object directory suffix. --build-tag=<TAG> Set the build object directory suffix.
--with-clean Configure SRS and do make clean if possible. --clean=on|off Whether do 'make clean' when configure.
--without-clean Configure SRS and never make clean even possible.. --simulator=on|off Whether enable RTC network simulator. Default: off
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,59 +518,57 @@ 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_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 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_GPERF = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --gperf=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --gperf=off"; 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_GPERF_MD = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --gmd=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --gmd=off"; 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_GPERF_CP = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --gcp=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --gcp=off"; 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_STATIC = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --static"; fi if [ $SRS_STATIC = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --static"; fi
if [ $SRS_SHARED_ST = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --use-shared-st"; fi if [ $SRS_SHARED_ST = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --use-shared-st"; fi
if [ $SRS_SHARED_SRT = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --use-shared-srt"; fi if [ $SRS_SHARED_SRT = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --use-shared-srt"; fi
@ -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

643
trunk/configure vendored
View file

@ -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,41 +247,30 @@ 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")
MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "SERVICE") ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSTRoot} ${LibSSLRoot})
ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibSSLRoot} ${LibGperfRoot}) if [ $SRS_GPERF = YES ]; then
if [[ $SRS_RTC == YES ]]; then ModuleLibIncs+=(${LibGperfRoot})
ModuleLibIncs+=("${LibFfmpegRoot[*]}" ${LibSrtpRoot}) fi
fi if [[ $SRS_RTC == YES ]]; then
MODULE_FILES=("srs_app_server" "srs_app_conn" "srs_app_rtmp_conn" "srs_app_source" 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_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_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_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_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_heartbeat" "srs_app_empty" "srs_app_http_client" "srs_app_http_static"
@ -278,124 +279,122 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
"srs_app_caster_flv" "srs_app_process" "srs_app_ng_exec" "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_hourglass" "srs_app_dash" "srs_app_fragment" "srs_app_dvr"
"srs_app_coworkers" "srs_app_hybrid") "srs_app_coworkers" "srs_app_hybrid")
if [[ $SRS_RTC == YES ]]; then if [[ $SRS_RTC == YES ]]; then
MODULE_FILES+=("srs_app_rtc" "srs_app_rtc_conn" "srs_app_dtls" "srs_app_audio_recode" "srs_app_sdp") MODULE_FILES+=("srs_app_rtc_conn" "srs_app_rtc_dtls" "srs_app_rtc_sdp"
fi "srs_app_rtc_queue" "srs_app_rtc_server" "srs_app_rtc_source" "srs_app_rtc_api")
if [[ $SRS_GB28181 == YES ]]; then fi
MODULE_FILES+=("srs_app_gb28181" "srs_app_gb28181_sip") if [[ $SRS_FFMPEG_FIT == YES ]]; then
fi MODULE_FILES+=("srs_app_rtc_codec")
DEFINES="" fi
# add each modules for app if [[ $SRS_GB28181 == YES ]]; then
for SRS_MODULE in ${SRS_MODULES[*]}; do 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 . $SRS_MODULE/config
MODULE_FILES+=("${SRS_MODULE_APP[*]}") MODULE_FILES+=("${SRS_MODULE_APP[*]}")
DEFINES="${DEFINES} ${SRS_MODULE_DEFINES}" DEFINES="${DEFINES} ${SRS_MODULE_DEFINES}"
done done
APP_INCS="src/app"; MODULE_DIR=${APP_INCS} . auto/modules.sh APP_INCS="src/app"; MODULE_DIR=${APP_INCS} . auto/modules.sh
APP_OBJS="${MODULE_OBJS[@]}" APP_OBJS="${MODULE_OBJS[@]}"
fi
#
#LIBS Module, build libsrs.a for static link.
MODULE_ID="LIBS"
MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL")
ModuleLibIncs=(${SRS_OBJS_DIR})
MODULE_FILES=("srs_librtmp" "srs_lib_simple_socket" "srs_lib_bandwidth")
LIBS_INCS="src/libs"; MODULE_DIR=${LIBS_INCS} . auto/modules.sh
LIBS_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
fi if [[ $SRS_FFMPEG_FIT == YES ]]; then
MODULE_FILES=() ModuleLibIncs+=("${LibFfmpegRoot[*]}")
DEFINES="" fi
# add each modules for main MODULE_FILES=()
for SRS_MODULE in ${SRS_MODULES[*]}; do DEFINES=""
# add each modules for main
for SRS_MODULE in ${SRS_MODULES[*]}; do
. $SRS_MODULE/config . $SRS_MODULE/config
MODULE_FILES+=("${SRS_MODULE_MAIN[*]}") MODULE_FILES+=("${SRS_MODULE_MAIN[*]}")
DEFINES="${DEFINES} ${SRS_MODULE_DEFINES}" DEFINES="${DEFINES} ${SRS_MODULE_DEFINES}"
done done
MAIN_INCS="src/main"; MODULE_DIR=${MAIN_INCS} . auto/modules.sh MAIN_INCS="src/main"; MODULE_DIR=${MAIN_INCS} . auto/modules.sh
MAIN_OBJS="${MODULE_OBJS[@]}" MAIN_OBJS="${MODULE_OBJS[@]}"
fi
##################################################################################### #####################################################################################
# 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")
for SRS_MODULE in ${SRS_MODULES[*]}; do
. $SRS_MODULE/config . $SRS_MODULE/config
MAIN_ENTRANCES+=("${SRS_MODULE_MAIN[*]}") MAIN_ENTRANCES+=("${SRS_MODULE_MAIN[*]}")
done done
# #
# all depends libraries # all depends libraries
ModuleLibFiles=(${LibSTfile} ${LibSSLfile} ${LibGperfFile}) ModuleLibFiles=(${LibSTfile} ${LibSSLfile} ${LibGperfFile})
if [[ $SRS_RTC == YES ]]; then if [[ $SRS_RTC == YES ]]; then
ModuleLibFiles+=("${LibFfmpegFile[*]}" ${LibSrtpFile}) ModuleLibFiles+=(${LibSrtpFile})
fi fi
if [[ $SRS_SRT == YES ]]; then if [[ $SRS_FFMPEG_FIT == YES ]]; then
ModuleLibFiles+=("${LibFfmpegFile[*]}")
fi
if [[ $SRS_SRT == YES ]]; then
ModuleLibFiles+=("${LibSRTfile[*]}") ModuleLibFiles+=("${LibSRTfile[*]}")
fi fi
# all depends objects # all depends objects
MODULE_OBJS="${CORE_OBJS[@]} ${KERNEL_OBJS[@]} ${PROTOCOL_OBJS[@]} ${SERVICE_OBJS[@]} ${APP_OBJS[@]} ${SERVER_OBJS[@]}" MODULE_OBJS="${CORE_OBJS[@]} ${KERNEL_OBJS[@]} ${PROTOCOL_OBJS[@]} ${APP_OBJS[@]} ${SERVER_OBJS[@]}"
ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot}) ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSTRoot} ${LibGperfRoot} ${LibSSLRoot})
if [[ $SRS_RTC == YES ]]; then if [[ $SRS_RTC == YES ]]; then
ModuleLibIncs+=("${LibFfmpegRoot[*]}" ${LibSrtpRoot}) ModuleLibIncs+=(${LibSrtpRoot})
fi fi
if [[ $SRS_SRT == YES ]]; then if [[ $SRS_FFMPEG_FIT == YES ]]; then
ModuleLibIncs+=("${LibFfmpegRoot[*]}")
fi
if [[ $SRS_SRT == YES ]]; then
MODULE_OBJS="${MODULE_OBJS} ${SRT_OBJS[@]}" MODULE_OBJS="${MODULE_OBJS} ${SRT_OBJS[@]}"
fi fi
LINK_OPTIONS="${SrsLinkOptions}${SrsGprofLink}${SrsGperfLink}" LINK_OPTIONS="${SrsLinkOptions}${SrsGprofLink}${SrsGperfLink}"
# #
# srs: srs(simple rtmp server) over st(state-threads) # srs: srs(simple rtmp server) over st(state-threads)
BUILD_KEY="srs" APP_MAIN="srs_main_server" APP_NAME="srs" . auto/apps.sh BUILD_KEY="srs" APP_MAIN="srs_main_server" APP_NAME="srs" . auto/apps.sh
# #
# For modules, without the app module. # For modules, without the app module.
MODULE_OBJS="${CORE_OBJS[@]} ${KERNEL_OBJS[@]} ${PROTOCOL_OBJS[@]} ${SERVICE_OBJS[@]} ${MAIN_OBJS[@]}" MODULE_OBJS="${CORE_OBJS[@]} ${KERNEL_OBJS[@]} ${PROTOCOL_OBJS[@]} ${MAIN_OBJS[@]}"
ModuleLibFiles=(${LibSTfile} ${LibSSLfile} ${LibGperfFile}) ModuleLibFiles=(${LibSTfile} ${LibSSLfile} ${LibGperfFile})
if [[ $SRS_RTC == YES ]]; then if [[ $SRS_RTC == YES ]]; then
ModuleLibFiles+=("${LibFfmpegFile[*]}" ${LibSrtpFile}) ModuleLibFiles+=(${LibSrtpFile})
fi fi
# if [[ $SRS_FFMPEG_FIT == YES ]]; then
for SRS_MODULE in ${SRS_MODULES[*]}; do ModuleLibFiles+=("${LibFfmpegFile[*]}")
fi
#
for SRS_MODULE in ${SRS_MODULES[*]}; do
. $SRS_MODULE/config . $SRS_MODULE/config
# no SRS_MODULE_MAIN # no SRS_MODULE_MAIN
if [[ 0 -eq ${#SRS_MODULE_MAIN[@]} ]]; then continue; fi 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 BUILD_KEY="$SRS_MODULE_NAME" APP_MAIN="${SRS_MODULE_MAIN[0]}" APP_NAME="$SRS_MODULE_NAME" . auto/apps.sh
done done
fi
# srs librtmp
if [ $SRS_LIBRTMP = YES ]; then
MODULE_OBJS="${CORE_OBJS[@]} ${KERNEL_OBJS[@]} ${PROTOCOL_OBJS[@]} ${LIBS_OBJS[@]}"
BUILD_KEY="librtmp" LIB_NAME="lib/srs_librtmp" . auto/libs.sh
fi
# 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.
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} 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"

View file

@ -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,

View file

@ -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);
} }

View file

@ -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;

View file

@ -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.

View 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)
if (v > 1) {
srs_warn("REUSEPORT not supported, reset %d to %d", reuseport, DEFAULT); srs_warn("REUSEPORT not supported, reset %d to %d", reuseport, DEFAULT);
v = 1 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;

View file

@ -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);

View file

@ -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(); }
SrsResourceManager::SrsResourceManager(const std::string& label, bool verbose)
{
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(kbps);
srs_freep(clk);
srs_freep(skt);
srs_freep(trd); 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_ = &copy;
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_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);
}
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;
}
if (ssl_ctx) {
SSL_CTX_free(ssl_ctx);
ssl_ctx = NULL;
}
}
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");
}
if ((bio_in = BIO_new(BIO_s_mem())) == NULL) {
return srs_error_new(ERROR_HTTPS_HANDSHAKE, "BIO_new in");
}
if ((bio_out = BIO_new(BIO_s_mem())) == NULL) {
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;
}
void SrsSslConnection::set_recv_timeout(srs_utime_t tm)
{
transport->set_recv_timeout(tm);
}
srs_utime_t SrsSslConnection::get_recv_timeout()
{
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; return err;
} }
// It maybe success with message. // Need to read more data to feed SSL.
if (srs_error_code(err) == ERROR_SUCCESS) { if (r0 == -1 && r1 == SSL_ERROR_WANT_READ) {
srs_trace("client finished%s.", srs_error_summary(err).c_str()); // TODO: Can we avoid copy?
srs_freep(err); 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; 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);
}
} }
// client close peer. return err;
// 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;
} }
int SrsConnection::srs_id()
{
return trd->cid();
}
string SrsConnection::remote_ip() {
return ip;
}
void SrsConnection::expire()
{
trd->interrupt();
}

View file

@ -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

View file

@ -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()

View file

@ -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;
}

View file

@ -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

View file

@ -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();

View file

@ -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();

View file

@ -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;
} }

View file

@ -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

View file

@ -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;

View file

@ -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"

View file

@ -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>

View file

@ -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;
} }

View file

@ -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

View file

@ -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);
}; };
@ -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

File diff suppressed because it is too large Load diff

View 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

View file

@ -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;
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_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];
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_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);

View file

@ -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);

View file

@ -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>

View file

@ -26,7 +26,7 @@
#include <srs_core.hpp> #include <srs_core.hpp>
#ifdef SRS_AUTO_HDS #ifdef SRS_HDS
#include <list> #include <list>

View file

@ -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());
} }

View file

@ -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
/** /**

View file

@ -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();

View file

@ -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);
} }
void SrsHttpApi::remark(int64_t* in, int64_t* out) srs_error_t SrsHttpApi::on_start()
{
// TODO: FIXME: implements it
}
srs_error_t SrsHttpApi::do_cycle()
{ {
srs_error_t err = srs_success; srs_error_t err = srs_success;
srs_trace("API server client, ip=%s", ip.c_str()); if ((err = conn->set_jsonp(true)) != srs_success) {
return srs_error_wrap(err, "set jsonp");
// 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. if (ssl) {
// @see https://github.com/ossrs/srs/issues/398 srs_utime_t starttime = srs_update_system_time();
skt->set_recv_timeout(SRS_HTTP_RECV_TIMEOUT); string crt_file = _srs_config->get_https_api_ssl_cert();
string key_file = _srs_config->get_https_api_ssl_key();
// initialize the cors, which will proxy to mux. if ((err = ssl->handshake(key_file, crt_file)) != srs_success) {
bool crossdomain_enabled = _srs_config->get_http_api_crossdomain(); return srs_error_wrap(err, "handshake");
if ((err = cors->initialize(mux, crossdomain_enabled)) != srs_success) {
return srs_error_wrap(err, "init cors");
} }
// process http messages. int cost = srsu2msi(srs_update_system_time() - starttime);
while ((err = trd->pull()) == srs_success) { srs_trace("https: api server done, use key %s and cert %s, cost=%dms",
ISrsHttpMessage* req = NULL; key_file.c_str(), crt_file.c_str(), cost);
// 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. return err;
// always free it in this scope. }
srs_assert(req);
SrsAutoFree(ISrsHttpMessage, req);
// Attach owner connection to message. srs_error_t SrsHttpApi::on_http_message(ISrsHttpMessage* r, SrsHttpResponseWriter* w)
SrsHttpMessage* hreq = (SrsHttpMessage*)req; {
hreq->set_connection(this); srs_error_t err = srs_success;
// ok, handle http request. // After parsed the message, set the schema to https.
SrsHttpResponseWriter writer(skt); if (ssl) {
if ((err = process_request(&writer, req)) != srs_success) { SrsHttpMessage* hm = dynamic_cast<SrsHttpMessage*>(r);
return srs_error_wrap(err, "process request"); 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. // read all rest bytes in request body.
char buf[SRS_HTTP_READ_CACHE_BYTES]; char buf[SRS_HTTP_READ_CACHE_BYTES];
ISrsHttpResponseReader* br = req->body_reader(); ISrsHttpResponseReader* br = r->body_reader();
while (!br->eof()) { while (!br->eof()) {
if ((err = br->read(buf, SRS_HTTP_READ_CACHE_BYTES, NULL)) != srs_success) { if ((err = br->read(buf, SRS_HTTP_READ_CACHE_BYTES, NULL)) != srs_success) {
return srs_error_wrap(err, "read response"); 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; return err;
} }
srs_error_t SrsHttpApi::process_request(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) srs_error_t SrsHttpApi::on_conn_done(srs_error_t r0)
{ {
srs_error_t err = srs_success; // Because we use manager to manage this object,
// not the http connection object, so we must remove it here.
manager->remove(this);
SrsHttpMessage* hm = dynamic_cast<SrsHttpMessage*>(r); // For HTTP-API timeout, we think it's done successfully,
srs_assert(hm); // because there may be no request or response for HTTP-API.
if (srs_error_code(r0) == ERROR_SOCKET_TIMEOUT) {
srs_trace("HTTP API %s %s, content-length=%" PRId64 ", chunked=%d/%d", srs_freep(r0);
r->method_str().c_str(), r->url().c_str(), r->content_length(), return srs_success;
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; return r0;
}
std::string SrsHttpApi::desc()
{
if (ssl) {
return "HttpsConn";
}
return "HttpConn";
}
void SrsHttpApi::remark(int64_t* in, int64_t* out)
{
conn->remark(in, out);
} }
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();
} }

View file

@ -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

View file

@ -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) {

View file

@ -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.

View file

@ -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;

View file

@ -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:

View file

@ -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;

View file

@ -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;
} }

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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();

View file

@ -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();

View file

@ -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;

View file

@ -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();

View file

@ -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;

View file

@ -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();

View file

@ -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.

View file

@ -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());
} }

View file

@ -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;
} }

View file

@ -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();

View file

@ -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;

View file

@ -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();

View file

@ -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);
}

View file

@ -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

View 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;
}

View 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

View file

@ -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,52 +478,68 @@ 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) {
memcpy(data_ + size_, resample_buffer, data_left);
size_ += data_left;
} else {
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");
}
// We can encode it in one time.
if (enc_want_bytes_ <= 0) {
int encode_len; int encode_len;
pcm.bytes = (char *)data_; pcm.bytes = (char *)data_;
pcm.size = size_; pcm.size = size_;
static char encode_buffer[kPacketBufMax];
if ((err = enc_->encode(&pcm, encode_buffer, encode_len)) != srs_success) { if ((err = enc_->encode(&pcm, encode_buffer, encode_len)) != srs_success) {
return srs_error_new(ERROR_RTC_RTP_MUXER, "encode error"); return srs_error_wrap(err, "encode error");
} }
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; return err;
if(!data_left) }
break;
if(data_left < total) { // 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);
size_ += data_left;
return err;
}
int index = 0;
while (1) {
data_left = data_left - (enc_want_bytes_ - size_);
memcpy(data_ + size_, resample_buffer + index, enc_want_bytes_ - size_);
index += enc_want_bytes_ - size_;
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);
buf_len[n] = encode_len;
n++;
}
size_ = 0;
if(!data_left) {
break;
}
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;
} }
} }
}
return err; return err;
} }

View file

@ -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

View file

@ -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

File diff suppressed because it is too large Load diff

View 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

View 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);
}

View 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

View file

@ -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;
} }
@ -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") {
@ -425,6 +504,20 @@ srs_error_t SrsMediaDesc::parse_attribute(const std::string& content)
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;
}

View file

@ -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

View 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);

View 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

File diff suppressed because it is too large Load diff

View 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

View file

@ -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();
}

View file

@ -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

View file

@ -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,7 +518,11 @@ 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()) {
srs_warn("no audio asc");
return err;
}
std::string sh = aac_specific_config; std::string sh = aac_specific_config;
SrsFormat* format = new SrsFormat(); SrsFormat* format = new SrsFormat();
@ -535,7 +562,6 @@ srs_error_t SrsRtspConn::write_sequence_header()
if ((err = write_audio_raw_frame((char*)sh.data(), (int)sh.length(), acodec, (uint32_t)dts)) != srs_success) { 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 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)

View file

@ -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();

View file

@ -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
if (!_srs_config->get_stream_caster_tcp_enable(stream_caster)) {
listener = new SrsGb28181Listener(this, SrsListenerGb28181RtpMux, 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;
} }

View file

@ -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();

View file

@ -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,
// so we do not have to update the start time here.
if (!pass_timestamp) {
SrsSharedPtrMessage* last = omsgs[count - 1]; SrsSharedPtrMessage* last = omsgs[count - 1];
av_start_time = srs_utime_t(last->timestamp * SRS_UTIME_MILLISECONDS); 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

View file

@ -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

View file

@ -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();

View file

@ -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);

View file

@ -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)

View file

@ -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();

View file

@ -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);
}
}

View file

@ -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

View file

@ -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)

View file

@ -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")

View file

@ -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;
}

View file

@ -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

View file

@ -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>

View file

@ -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."

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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