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 "#define SRS_EXPORT_LIBRTMP" >> $SRS_AUTO_HEADERS_H
else
echo "#undef SRS_EXPORT_LIBRTMP" >> $SRS_AUTO_HEADERS_H 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

289
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" "SERVICE") MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL")
ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibSSLRoot} ${LibGperfRoot}) ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSTRoot} ${LibSSLRoot})
if [ $SRS_GPERF = YES ]; then
ModuleLibIncs+=(${LibGperfRoot})
fi
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
MODULE_FILES=("srs_app_server" "srs_app_conn" "srs_app_rtmp_conn" "srs_app_source" 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"
@ -279,10 +280,14 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
"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"
"srs_app_rtc_queue" "srs_app_rtc_server" "srs_app_rtc_source" "srs_app_rtc_api")
fi
if [[ $SRS_FFMPEG_FIT == YES ]]; then
MODULE_FILES+=("srs_app_rtc_codec")
fi fi
if [[ $SRS_GB28181 == YES ]]; then if [[ $SRS_GB28181 == YES ]]; then
MODULE_FILES+=("srs_app_gb28181" "srs_app_gb28181_sip") MODULE_FILES+=("srs_app_gb28181" "srs_app_gb28181_sip" "srs_app_gb28181_jitbuffer")
fi fi
DEFINES="" DEFINES=""
# add each modules for app # add each modules for app
@ -293,26 +298,19 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
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" "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
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
if [[ $SRS_FFMPEG_FIT == YES ]]; then
ModuleLibIncs+=("${LibFfmpegRoot[*]}")
fi fi
if [[ $SRS_SRT == YES ]]; then if [[ $SRS_SRT == YES ]]; then
ModuleLibIncs+=("${LibSRTRoot[*]}") ModuleLibIncs+=("${LibSRTRoot[*]}")
@ -320,15 +318,16 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
MODULE_FILES=("srs_main_server") MODULE_FILES=("srs_main_server")
SERVER_INCS="src/main"; MODULE_DIR=${SERVER_INCS} . auto/modules.sh SERVER_INCS="src/main"; MODULE_DIR=${SERVER_INCS} . auto/modules.sh
SERVER_OBJS="${MODULE_OBJS[@]}" SERVER_OBJS="${MODULE_OBJS[@]}"
fi
# #
#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" "SERVICE") MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL")
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
if [[ $SRS_FFMPEG_FIT == YES ]]; then
ModuleLibIncs+=("${LibFfmpegRoot[*]}")
fi fi
MODULE_FILES=() MODULE_FILES=()
DEFINES="" DEFINES=""
@ -340,14 +339,11 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
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.
if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
# all main entrances # all main entrances
MAIN_ENTRANCES=("srs_main_server") MAIN_ENTRANCES=("srs_main_server")
for SRS_MODULE in ${SRS_MODULES[*]}; do for SRS_MODULE in ${SRS_MODULES[*]}; do
@ -358,16 +354,22 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
# 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
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
# 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
if [[ $SRS_FFMPEG_FIT == YES ]]; then
ModuleLibIncs+=("${LibFfmpegRoot[*]}")
fi fi
if [[ $SRS_SRT == YES ]]; then if [[ $SRS_SRT == YES ]]; then
MODULE_OBJS="${MODULE_OBJS} ${SRT_OBJS[@]}" MODULE_OBJS="${MODULE_OBJS} ${SRT_OBJS[@]}"
@ -378,10 +380,13 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
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
if [[ $SRS_FFMPEG_FIT == YES ]]; then
ModuleLibFiles+=("${LibFfmpegFile[*]}")
fi fi
# #
for SRS_MODULE in ${SRS_MODULES[*]}; do for SRS_MODULE in ${SRS_MODULES[*]}; do
@ -390,12 +395,6 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
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,55 +556,24 @@ 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.
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} 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}
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} cat << END >> ${SRS_WORKDIR}/${SRS_MAKEFILE}
uninstall: uninstall:
@echo "rmdir \$(SRS_PREFIX)" @echo "rmdir \$(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,15 +667,12 @@ 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}"
@ -755,6 +702,11 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
else else
echo -e "${GREEN}Warning: RTC is disabled.${BLACK}" echo -e "${GREEN}Warning: RTC is disabled.${BLACK}"
fi 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 if [ $SRS_DVR = YES ]; then
echo -e "${GREEN}DVR is enabled.${BLACK}" echo -e "${GREEN}DVR is enabled.${BLACK}"
else else
@ -790,16 +742,6 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
else else
echo -e "${YELLOW}Warning: The HTTP API is disabled.${BLACK}" echo -e "${YELLOW}Warning: The HTTP API is disabled.${BLACK}"
fi 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 if [ $SRS_UTEST = YES ]; then
echo -e "${GREEN}The utests are enabled.${BLACK}" echo -e "${GREEN}The utests are enabled.${BLACK}"
else else
@ -844,20 +786,18 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
for SRS_MODULE in ${SRS_MODULES[*]}; do for SRS_MODULE in ${SRS_MODULES[*]}; do
echo -e "${GREEN}Enable module: $SRS_MODULE${BLACK}" echo -e "${GREEN}Enable module: $SRS_MODULE${BLACK}"
done done
fi
##################################################################################### #####################################################################################
# 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
@ -867,23 +807,4 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
echo "You can build SRS:" echo "You can build SRS:"
echo "\" make \" to build the srs(simple rtmp server)." echo "\" make \" to build the srs(simple rtmp server)."
echo "\" make help \" to get the usage of make" 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

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();
}
srs_error_t SrsTcpConnection::read_fully(void* buf, size_t size, ssize_t* nread)
{
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");
// success.
if (err == srs_success) {
srs_trace("client finished.");
return err; return err;
} }
// It maybe success with message. void SrsSslConnection::set_recv_timeout(srs_utime_t tm)
if (srs_error_code(err) == ERROR_SUCCESS) { {
srs_trace("client finished%s.", srs_error_summary(err).c_str()); transport->set_recv_timeout(tm);
srs_freep(err); }
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;
} }
// client close peer. // Need to read more data to feed SSL.
// TODO: FIXME: Only reset the error when client closed it. if (r0 == -1 && r1 == SSL_ERROR_WANT_READ) {
if (srs_is_client_gracefully_close(err)) { // TODO: Can we avoid copy?
srs_warn("client disconnect peer. ret=%d", srs_error_code(err)); int nn_cipher = nn_plaintext;
} else if (srs_is_server_gracefully_close(err)) { char* cipher = new char[nn_cipher];
srs_warn("server disconnect. ret=%d", srs_error_code(err)); SrsAutoFreeA(char, cipher);
} else {
srs_error("serve error %s", srs_error_desc(err).c_str()); // 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");
} }
srs_freep(err); int r0 = BIO_write(bio_in, cipher, nn);
return srs_success; 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;
} }
int SrsConnection::srs_id() // 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)
{ {
return trd->cid(); transport->set_send_timeout(tm);
} }
string SrsConnection::remote_ip() { srs_utime_t SrsSslConnection::get_send_timeout()
return ip;
}
void SrsConnection::expire()
{ {
trd->interrupt(); return transport->get_send_timeout();
} }
srs_error_t SrsSslConnection::write(void* plaintext, size_t nn_plaintext, ssize_t* nwrite)
{
srs_error_t err = srs_success;
for (char* p = (char*)plaintext; p < (char*)plaintext + nn_plaintext;) {
int left = (int)nn_plaintext - (p - (char*)plaintext);
int r0 = SSL_write(ssl, (const void*)p, left);
int r1 = SSL_get_error(ssl, r0);
if (r0 <= 0) {
return srs_error_new(ERROR_HTTPS_WRITE, "https: write data=%p, size=%d, r0=%d, r1=%d", p, left, r0, r1);
}
// Move p to the next writing position.
p += r0;
if (nwrite) {
*nwrite += (ssize_t)r0;
}
uint8_t* data = NULL;
int size = BIO_get_mem_data(bio_out, &data);
if ((err = transport->write(data, size, NULL)) != srs_success) {
return srs_error_wrap(err, "https: write data=%p, size=%d", data, size);
}
if ((r0 = BIO_reset(bio_out)) != 1) {
return srs_error_new(ERROR_HTTPS_WRITE, "BIO_reset r0=%d", r0);
}
}
return err;
}
srs_error_t SrsSslConnection::writev(const iovec *iov, int iov_size, ssize_t* nwrite)
{
srs_error_t err = srs_success;
for (int i = 0; i < iov_size; i++) {
const iovec* p = iov + i;
if ((err = write((void*)p->iov_base, (size_t)p->iov_len, nwrite)) != srs_success) {
return srs_error_wrap(err, "write iov #%d base=%p, size=%d", i, p->iov_base, p->iov_len);
}
}
return err;
}

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.
SrsHttpMessage* hreq = (SrsHttpMessage*)req;
hreq->set_connection(this);
// ok, handle http request.
SrsHttpResponseWriter writer(skt);
if ((err = process_request(&writer, req)) != srs_success) {
return srs_error_wrap(err, "process request");
} }
srs_error_t SrsHttpApi::on_http_message(ISrsHttpMessage* r, SrsHttpResponseWriter* w)
{
srs_error_t err = srs_success;
// After parsed the message, set the schema to https.
if (ssl) {
SrsHttpMessage* hm = dynamic_cast<SrsHttpMessage*>(r);
hm->set_https(true);
}
// TODO: For each API session, we use short-term HTTP connection.
//SrsHttpHeader* hdr = w->header();
//hdr->set("Connection", "Close");
return err;
}
srs_error_t SrsHttpApi::on_message_done(ISrsHttpMessage* r, SrsHttpResponseWriter* w)
{
srs_error_t err = srs_success;
// read all rest bytes in request body. // 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()
{
bool v = _srs_config->get_http_api_crossdomain();
return conn->set_crossdomain_enabled(v);
}
srs_error_t SrsHttpApi::start()
{ {
srs_error_t err = srs_success; srs_error_t err = srs_success;
bool crossdomain_enabled = _srs_config->get_http_api_crossdomain(); bool v = _srs_config->get_http_api_crossdomain();
if ((err = cors->initialize(mux, crossdomain_enabled)) != srs_success) { if ((err = conn->set_crossdomain_enabled(v)) != srs_success) {
return srs_error_wrap(err, "reload"); return srs_error_wrap(err, "set cors=%d", v);
} }
return err; 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);
}
// when not specified the content length, ignore.
if (msg->content_length() == -1) {
return err; return err;
} }
// drop all request body. 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.
if (r->content_length() == -1) {
return err;
}
// 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,19 +21,21 @@
* 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)) {\
@ -45,7 +47,7 @@ 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,22 +137,24 @@ 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;
} }
// For ICE-lite, we never set the trickle.
if (!ice_options_.empty()) { 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,6 +194,7 @@ 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");
} }
@ -201,9 +217,44 @@ srs_error_t SrsSSRCInfo::encode(std::ostringstream& os)
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()
@ -225,7 +276,10 @@ srs_error_t SrsMediaPayloadType::encode(std::ostringstream& os)
} }
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;
@ -322,6 +397,9 @@ srs_error_t SrsMediaDesc::encode(std::ostringstream& os)
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,7 +756,7 @@ 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());
} }
@ -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;
} }
@ -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 "";
@ -955,3 +1116,28 @@ srs_error_t SrsSdp::parse_media_description(const std::string& content)
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);
} }
@ -442,10 +437,13 @@ void SrsStatistic::on_stream_close(SrsRequest* req)
} }
} }
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,6 +46,7 @@
#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
@ -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