diff --git a/README.md b/README.md index 507fab89a..270a7569a 100755 --- a/README.md +++ b/README.md @@ -149,7 +149,6 @@ For previous versions, please read: - [ ] Support publishing stream by WebRTC, [#307][bug #307]. - [ ] Support change user to run SRS, [#1111][bug #1111]. - [ ] Support HLS variant, [#463][bug #463]. -- [ ] Support playing stream by WebRTC. > Remark: About the milestone and product plan, please read ([CN][v1_CN_Product], [EN][v1_EN_Product]) wiki. @@ -158,6 +157,7 @@ For previous versions, please read: ## V4 changes +* v4.0, 2020-04-14, For [#307][bug #307], support sendmmsg, GSO and reuseport. 4.0.23 * v4.0, 2020-04-05, For [#307][bug #307], SRTP ASM only works with openssl-1.0, auto detect it. 4.0.22 * v4.0, 2020-04-04, Merge RTC and GB28181, with bugs fixed. 4.0.21 * v4.0, 2020-04-04, For [#307][bug #307], refine RTC latency from 600ms to 200ms. 4.0.20 diff --git a/trunk/3rdparty/st-srs/README.md b/trunk/3rdparty/st-srs/README.md index 60fc1a651..522d3c761 100644 --- a/trunk/3rdparty/st-srs/README.md +++ b/trunk/3rdparty/st-srs/README.md @@ -22,6 +22,8 @@ The branch [srs](https://github.com/ossrs/state-threads/tree/srs) will be patche - [x] Patch [st.osx10.14.build.patch](https://github.com/ossrs/srs/blob/2.0release/trunk/3rdparty/patches/6.st.osx10.14.build.patch), for osx 10.14 build. - [x] Support macro `MD_ST_NO_ASM` to disable ASM, [#8](https://github.com/ossrs/state-threads/issues/8). - [x] Merge patch [srs#1282](https://github.com/ossrs/srs/issues/1282#issuecomment-445539513) to support aarch64, [#9](https://github.com/ossrs/state-threads/issues/9). +- [x] Support OSX for Apple Darwin, macOS, [#11](https://github.com/ossrs/state-threads/issues/11). +- [x] Support sendmmsg for UDP, [#12](https://github.com/ossrs/state-threads/issues/12). ## Docs @@ -85,4 +87,10 @@ Important cli options: 1. `--track-origins= [default: no]`, Controls whether Memcheck tracks the origin of uninitialised values. By default, it does not, which means that although it can tell you that an uninitialised value is being used in a dangerous way, it cannot tell you where the uninitialised value came from. This often makes it difficult to track down the root problem. 1. `--show-reachable= , --show-possibly-lost=`, to show the using memory. +## Analysis + +1. About setjmp and longjmp, read [setjmp](https://gitee.com/winlinvip/srs-wiki/raw/master/images/st-setjmp.jpg). +1. About the stack structure, read [stack](https://gitee.com/winlinvip/srs-wiki/raw/master/images/st-stack.jpg) +1. About asm code comments, read [#91d530e](https://github.com/ossrs/state-threads/commit/91d530e#diff-ed9428b14ff6afda0e9ab04cc91d4445R25). + Winlin 2016 diff --git a/trunk/auto/auto_headers.sh b/trunk/auto/auto_headers.sh index 84b64319b..d77325395 100755 --- a/trunk/auto/auto_headers.sh +++ b/trunk/auto/auto_headers.sh @@ -171,6 +171,12 @@ else srs_undefine_macro "SRS_AUTO_HAS_SENDMMSG" $SRS_AUTO_HEADERS_H fi +if [ $SRS_DEBUG = YES ]; then + srs_define_macro "SRS_AUTO_DEBUG" $SRS_AUTO_HEADERS_H +else + srs_undefine_macro "SRS_AUTO_DEBUG" $SRS_AUTO_HEADERS_H +fi + # prefix echo "" >> $SRS_AUTO_HEADERS_H echo "#define SRS_AUTO_PREFIX \"${SRS_PREFIX}\"" >> $SRS_AUTO_HEADERS_H diff --git a/trunk/auto/depends.sh b/trunk/auto/depends.sh index 52558be5e..557de4bb3 100755 --- a/trunk/auto/depends.sh +++ b/trunk/auto/depends.sh @@ -84,15 +84,6 @@ function Ubuntu_prepare() echo "The unzip is installed." fi - if [[ $SRS_NASM == YES ]]; then - nasm -v >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then - echo "Installing nasm." - require_sudoer "sudo apt-get install -y --force-yes nasm" - sudo apt-get install -y --force-yes nasm; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi - echo "The nasm is installed." - fi - fi - if [[ $SRS_VALGRIND == YES ]]; then valgrind --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then echo "Installing valgrind." @@ -171,13 +162,6 @@ function Centos_prepare() echo "The unzip is installed." fi - nasm -v >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then - echo "Installing nasm." - require_sudoer "sudo yum install -y nasm" - sudo yum install -y nasm; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi - echo "The nasm is installed." - fi - if [[ $SRS_VALGRIND == YES ]]; then valgrind --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then echo "Installing valgrind." @@ -230,15 +214,16 @@ function OSX_prepare() fi OS_IS_OSX=YES - echo "OSX detected, install tools if needed" # requires the osx when os if [ $OS_IS_OSX = YES ]; then if [ $SRS_OSX = NO ]; then - echo "OSX detected, must specifies the --osx" + echo "OSX detected, please use: ./configure --osx" exit 1 fi fi + echo "OSX detected, install tools if needed" + brew --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then echo "install brew" echo "ruby -e \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)\"" @@ -281,6 +266,10 @@ function OSX_prepare() echo "install unzip success" fi + pkg-config --version >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then + echo "Please install pkg-config"; exit -1; + fi + echo "OSX install tools success" return 0 } @@ -614,9 +603,16 @@ fi ##################################################################################### if [[ $SRS_EXPORT_LIBRTMP_PROJECT == NO && $SRS_RTC == YES ]]; then FFMPEG_OPTIONS="" + + # If disable nasm, disable all ASMs. if [[ $SRS_NASM == NO ]]; then FFMPEG_OPTIONS="--disable-asm --disable-x86asm --disable-inline-asm" fi + # If no nasm, we disable the x86asm. + nasm -v >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then + FFMPEG_OPTIONS="--disable-x86asm" + fi + if [[ -f ${SRS_OBJS}/${SRS_PLATFORM}/ffmpeg/lib/libavcodec.a ]]; then echo "The ffmpeg-4.2-fit is ok."; else @@ -647,10 +643,11 @@ if [[ $SRS_EXPORT_LIBRTMP_PROJECT == NO && $SRS_RTC == YES ]]; then --disable-programs --disable-doc --disable-htmlpages --disable-manpages --disable-podpages --disable-txtpages \ --disable-avdevice --disable-avformat --disable-swscale --disable-postproc --disable-avfilter --disable-network \ --disable-dct --disable-dwt --disable-error-resilience --disable-lsp --disable-lzo --disable-faan --disable-pixelutils \ - --disable-hwaccels --disable-devices --disable-audiotoolbox --disable-videotoolbox --disable-appkit --disable-coreimage \ - --disable-avfoundation --disable-securetransport --disable-iconv --disable-lzma --disable-sdl2 --disable-everything \ - --enable-decoder=aac --enable-decoder=aac_fixed --enable-decoder=aac_latm --enable-decoder=libopus --enable-encoder=aac \ - --enable-encoder=opus --enable-encoder=libopus --enable-libopus && + --disable-hwaccels --disable-devices --disable-audiotoolbox --disable-videotoolbox --disable-cuda-llvm --disable-cuvid \ + --disable-d3d11va --disable-dxva2 --disable-ffnvcodec --disable-nvdec --disable-nvenc --disable-v4l2-m2m --disable-vaapi \ + --disable-vdpau --disable-appkit --disable-coreimage --disable-avfoundation --disable-securetransport --disable-iconv \ + --disable-lzma --disable-sdl2 --disable-everything --enable-decoder=aac --enable-decoder=aac_fixed --enable-decoder=aac_latm \ + --enable-decoder=libopus --enable-encoder=aac --enable-encoder=opus --enable-encoder=libopus --enable-libopus && make ${SRS_JOBS} && make install && cd .. && rm -rf ffmpeg && ln -sf ffmpeg-4.2-fit/${SRS_PLATFORM}/_release ffmpeg ) diff --git a/trunk/auto/options.sh b/trunk/auto/options.sh index 0e31b05fd..f2b0cb7e6 100755 --- a/trunk/auto/options.sh +++ b/trunk/auto/options.sh @@ -122,6 +122,7 @@ SRS_NASM=YES SRS_SRTP_ASM=YES SRS_SENDMMSG=YES SRS_HAS_SENDMMSG=YES +SRS_DEBUG=NO ##################################################################################### # menu @@ -162,6 +163,7 @@ Features: --prefix= The absolute installation path for srs. Default: $SRS_PREFIX --static Whether add '-static' to link options. --gcov Whether enable the GCOV compiler options. + --debug Whether enable the debug code, may hurt performance. --jobs[=N] Allow N jobs at once; infinite jobs with no arg. Used for make in the configure, for example, to make ffmpeg. --log-verbose Whether enable the log verbose level. default: no. @@ -293,6 +295,7 @@ function parse_user_option() { --log-info) SRS_LOG_INFO=YES ;; --log-trace) SRS_LOG_TRACE=YES ;; --gcov) SRS_GCOV=YES ;; + --debug) SRS_DEBUG=YES ;; --arm) SRS_CROSS_BUILD=YES ;; --mips) SRS_CROSS_BUILD=YES ;; @@ -566,6 +569,7 @@ function apply_user_detail_options() { # Detect whether has sendmmsg. # @see http://man7.org/linux/man-pages/man2/sendmmsg.2.html + mkdir -p ${SRS_OBJS} && echo "#include " > ${SRS_OBJS}/_tmp_sendmmsg_detect.c echo "int main(int argc, char** argv) {" >> ${SRS_OBJS}/_tmp_sendmmsg_detect.c echo " struct mmsghdr hdr;" >> ${SRS_OBJS}/_tmp_sendmmsg_detect.c @@ -623,6 +627,7 @@ function regenerate_options() { if [ $SRS_LOG_INFO = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --log-info"; fi if [ $SRS_LOG_TRACE = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --log-trace"; fi if [ $SRS_GCOV = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --gcov"; fi + if [ $SRS_DEBUG = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --debug"; fi if [[ $SRS_EXTRA_FLAGS != '' ]]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --extra-flags=\\\"$SRS_EXTRA_FLAGS\\\""; fi if [[ $SRS_BUILD_TAG != '' ]]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --build-tag=\\\"$SRS_BUILD_TAG\\\""; fi if [[ $SRS_TOOL_CC != '' ]]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --cc=$SRS_TOOL_CC"; fi diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index 513a55b87..2c1089665 100644 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -430,6 +430,34 @@ rtc_server { # @remark Should always turn it on, or Chrome will fail. # default: on encrypt on; + # We listen multiple times at the same port, by REUSEPORT, to increase the UDP queue. + # Note that you can set to 1 and increase the system UDP buffer size by net.core.rmem_max + # and net.core.rmem_default or just increase this to get larger UDP recv and send buffer. + # default: 4 + reuseport 4; + # Whether merge multiple NALUs into one. + # @see https://github.com/ossrs/srs/issues/307#issuecomment-612806318 + # default: on + merge_nalus on; + # Whether enable GSO to send out RTP packets. + # @remark Linux 4.18+ only, for other OS always disabled. + # default: on + gso on; + # Whether pad first packet for GSO for padding bytes. + # If 0, disable padding for GSO. + # @remark The max padding size is 0x7f(127). + # default: 127 + padding 127; + # Whether enable the perf stat at http://localhost:1985/api/v1/perf + # default: on + perf_stat on; + # The queue length, in number of mmsghdr, in messages. + # For example, 30 means we will cache 30K messages at most. + # If exceed, we will drop messages. + # @remark Each reuseport use a dedicated queue, if queue is 2000, reuseport is 4, + # then system queue is 2000*4 = 8k, user can incrase reuseport to incrase the queue. + # default: 2000 + queue_length 2000; } vhost rtc.vhost.srs.com { @@ -456,12 +484,17 @@ vhost rtc.vhost.srs.com { stun_strict_check on; } # whether enable min delay mode for vhost. - # For RTC, we recommend to set to on. + # default: on, for RTC. min_latency on; play { # set the MW(merged-write) latency in ms. - # For RTC, we recommend lower value, such as 0. + # @remark For WebRTC, we enable pass-timestamp mode, so we ignore this config. + # default: 0 (For WebRTC) mw_latency 0; + # Set the MW(merged-write) min messages. + # default: 0 (For Real-Time, min_latency on) + # default: 1 (For WebRTC, min_latency off) + mw_msgs 0; } } @@ -688,10 +721,17 @@ vhost play.srs.com { # SRS always set mw on, so we just set the latency value. # the latency of stream >= mw_latency + mr_latency # the value recomment is [300, 1800] - # default: 350 (for RTMP/HTTP-FLV) - # default: 0 (for WebRTC) + # @remark For WebRTC, we enable pass-timestamp mode, so we ignore this config. + # default: 350 (For RTMP/HTTP-FLV) + # default: 0 (For WebRTC) mw_latency 350; + # Set the MW(merged-write) min messages. + # default: 0 (For Real-Time, min_latency on) + # default: 1 (For WebRTC, min_latency off) + # default: 8 (For RTMP/HTTP-FLV, min_latency off). + mw_msgs 8; + # the minimal packets send interval in ms, # used to control the ndiff of stream by srs_rtmp_dump, # for example, some device can only accept some stream which @@ -748,6 +788,7 @@ vhost mrw.srs.com { # @see play.srs.com play { mw_latency 350; + mw_msgs 8; } # @see publish.srs.com @@ -767,6 +808,7 @@ vhost min.delay.com { # @see play.srs.com play { mw_latency 100; + mw_msgs 4; gop_cache off; queue_length 10; } @@ -795,6 +837,7 @@ vhost stream.control.com { # @see play.srs.com play { mw_latency 100; + mw_msgs 4; queue_length 10; send_min_interval 10.0; reduce_sequence_header on; diff --git a/trunk/configure b/trunk/configure index 3c8e71976..ed76da4d5 100755 --- a/trunk/configure +++ b/trunk/configure @@ -444,7 +444,7 @@ mv ${SRS_WORKDIR}/${SRS_MAKEFILE} ${SRS_WORKDIR}/${SRS_MAKEFILE}.bk # generate phony header 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: clean_srs clean_modules clean_openssl clean_nginx clean_cherrypy clean_srtp2 clean_opus +.PHONY: clean_srs clean_modules clean_openssl clean_nginx clean_cherrypy clean_srtp2 clean_opus clean_ffmpeg clean_st .PHONY: st ffmpeg # install prefix. @@ -481,6 +481,8 @@ help: @echo " clean_openssl Remove the openssl cache." @echo " clean_srtp2 Remove the libsrtp2 cache." @echo " clean_opus Remove the opus cache." + @echo " clean_ffmpeg Remove the FFmpeg cache." + @echo " clean_st Remove the ST cache." @echo "For example:" @echo " make" @echo " make help" @@ -519,6 +521,14 @@ clean_opus: (cd ${SRS_OBJS_DIR}/${SRS_PLATFORM} && rm -rf opus-1.3.1) @echo "Please rebuild opus by: ./configure" +clean_ffmpeg: + (cd ${SRS_OBJS_DIR}/${SRS_PLATFORM} && rm -rf ffmpeg-4.2-fit) + @echo "Please rebuild FFmpeg by: ./configure" + +clean_st: + (cd ${SRS_OBJS_DIR}/${SRS_PLATFORM} && rm -rf st-srs) + @echo "Please rebuild ST by: ./configure" + clean_nginx: (cd ${SRS_OBJS_DIR} && rm -rf nginx) diff --git a/trunk/research/players/js/srs.page.js b/trunk/research/players/js/srs.page.js index 56ff81e45..3f3b1f6ca 100755 --- a/trunk/research/players/js/srs.page.js +++ b/trunk/research/players/js/srs.page.js @@ -207,6 +207,14 @@ function build_default_hls_url() { } function build_default_rtc_url(query) { + // Use target to overwrite server, vhost and eip. + console.log('?target=x.x.x.x to overwrite server, vhost and eip.'); + if (query.target) { + query.server = query.vhost = query.eip = query.target; + query.user_query.eip = query.target; + delete query.target; + } + var server = (!query.server)? window.location.hostname:query.server; var vhost = (!query.vhost)? window.location.hostname:query.vhost; var app = (!query.app)? "live":query.app; diff --git a/trunk/scripts/perf_gso.py b/trunk/scripts/perf_gso.py new file mode 100755 index 000000000..02590e759 --- /dev/null +++ b/trunk/scripts/perf_gso.py @@ -0,0 +1,97 @@ +#!/usr/bin/python +''' +The MIT License (MIT) + +Copyright (c) 2013-2016 SRS(ossrs) + +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. +''' + +import urllib, sys, json + +url = "http://localhost:1985/api/v1/perf" +if len(sys.argv) < 2: + print "Usage: %s "%(sys.argv[0]) + print "For example:" + print " %s http://localhost:1985/api/v1/perf"%(sys.argv[0]) + sys.exit(-1) + +url = sys.argv[1] +print "Open %s"%(url) + +f = urllib.urlopen(url) +s = f.read() +f.close() +print "Repsonse %s"%(s) + +obj = json.loads(s) + +print "" +p = obj['data']['dropped'] +print('Frame-Dropped: %.1f%s'%(10000.0 * p['rtc_dropeed'] / p['rtc_frames'], '%%')) +p = obj['data']['bytes'] +print('Padding-Overload: %.1f%s %dMB'%(10000.0 * p['rtc_padding'] / p['rtc_bytes'], '%%', p['rtc_padding']/1024/1024)) + +# 2, 3, 5, 9, 16, 32, 64, 128, 256 +keys = ['lt_2', 'lt_3', 'lt_5', 'lt_9', 'lt_16', 'lt_32', 'lt_64', 'lt_128', 'lt_256', 'gt_256'] +print("\n----------- 1 2 [3,4] [5,8] [9,15] [16,31] [32,63] [64,127] [128,255] [256,+) Packets"), + +print "" +print("AV---Frames"), +p = obj['data']['avframes'] +for k in keys: + k2 = '%s'%(k) + if k2 in p: + print(p[k2]), + else: + print(0), +print(p['nn']), + +print "" +print("RTC--Frames"), +p = obj['data']['rtc'] +for k in keys: + k2 = '%s'%(k) + if k2 in p: + print(p[k2]), + else: + print(0), +print(p['nn']), + +print "" +print("RTP-Packets"), +p = obj['data']['rtp'] +for k in keys: + k2 = '%s'%(k) + if k2 in p: + print(p[k2]), + else: + print(0), +print(p['nn']), + +print "" +print("GSO-Packets"), +p = obj['data']['gso'] +for k in keys: + k2 = '%s'%(k) + if k2 in p: + print(p[k2]), + else: + print(0), +print(p['nn']), + diff --git a/trunk/src/app/srs_app_audio_recode.cpp b/trunk/src/app/srs_app_audio_recode.cpp index 77a2a440d..6e60c2e96 100644 --- a/trunk/src/app/srs_app_audio_recode.cpp +++ b/trunk/src/app/srs_app_audio_recode.cpp @@ -388,16 +388,13 @@ srs_error_t SrsAudioRecode::initialize() srs_error_t SrsAudioRecode::recode(SrsSample *pkt, char **buf, int *buf_len, int &n) { srs_error_t err = srs_success; - - static char decode_buffer[kPacketBufMax]; - static char resample_buffer[kFrameBufMax]; - static char encode_buffer[kPacketBufMax]; if (!dec_) { return srs_error_new(ERROR_RTC_RTP_MUXER, "dec_ nullptr"); } int decode_len = kPacketBufMax; + static char decode_buffer[kPacketBufMax]; if ((err = dec_->decode(pkt, decode_buffer, decode_len)) != srs_success) { return srs_error_new(ERROR_RTC_RTP_MUXER, "decode error"); } @@ -412,15 +409,18 @@ srs_error_t SrsAudioRecode::recode(SrsSample *pkt, char **buf, int *buf_len, int if (!resample_) { return srs_error_new(ERROR_RTC_RTP_MUXER, "SrsAudioResample failed"); } - resample_->initialize(); + if ((err = resample_->initialize()) != srs_success) { + return srs_error_wrap(err, "init resample"); + } } SrsSample pcm; pcm.bytes = decode_buffer; pcm.size = decode_len; int resample_len = kFrameBufMax; + static char resample_buffer[kFrameBufMax]; if ((err = resample_->resample(&pcm, resample_buffer, resample_len)) != srs_success) { - return srs_error_new(ERROR_RTC_RTP_MUXER, "decode error"); + return srs_error_new(ERROR_RTC_RTP_MUXER, "resample error"); } n = 0; @@ -445,8 +445,9 @@ srs_error_t SrsAudioRecode::recode(SrsSample *pkt, char **buf, int *buf_len, int int encode_len; pcm.bytes = (char *)data_; pcm.size = size_; + static char encode_buffer[kPacketBufMax]; if ((err = enc_->encode(&pcm, encode_buffer, encode_len)) != srs_success) { - return srs_error_new(ERROR_RTC_RTP_MUXER, "decode error"); + return srs_error_new(ERROR_RTC_RTP_MUXER, "encode error"); } memcpy(buf[n], encode_buffer, encode_len); diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index f63c3a07c..2a8454633 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -33,6 +33,10 @@ #include #include #include +#ifdef __linux__ +#include +#include +#endif #include #include @@ -1540,6 +1544,11 @@ srs_error_t SrsConfig::reload_conf(SrsConfig* conf) if ((err = reload_http_stream(old_root)) != srs_success) { return srs_error_wrap(err, "http steram");; } + + // Merge config: rtc_server + if ((err = reload_rtc_server(old_root)) != srs_success) { + return srs_error_wrap(err, "http steram");; + } // TODO: FIXME: support reload stream_caster. @@ -1697,6 +1706,40 @@ srs_error_t SrsConfig::reload_http_stream(SrsConfDirective* old_root) return err; } +srs_error_t SrsConfig::reload_rtc_server(SrsConfDirective* old_root) +{ + srs_error_t err = srs_success; + + // merge config. + std::vector::iterator it; + + // state graph + // old_rtc_server new_rtc_server + // ENABLED => ENABLED (modified) + + SrsConfDirective* new_rtc_server = root->get("rtc_server"); + SrsConfDirective* old_rtc_server = old_root->get("rtc_server"); + + // TODO: FIXME: Support disable or enable reloading. + + // ENABLED => ENABLED (modified) + if (get_rtc_server_enabled(old_rtc_server) && get_rtc_server_enabled(new_rtc_server) + && !srs_directive_equals(old_rtc_server, new_rtc_server) + ) { + for (it = subscribes.begin(); it != subscribes.end(); ++it) { + ISrsReloadHandler* subscribe = *it; + if ((err = subscribe->on_reload_rtc_server()) != srs_success) { + return srs_error_wrap(err, "rtc server enabled"); + } + } + srs_trace("reload rtc server success."); + return err; + } + + srs_trace("reload rtc server success, nothing changed."); + return err; +} + srs_error_t SrsConfig::reload_transcode(SrsConfDirective* new_vhost, SrsConfDirective* old_vhost) { srs_error_t err = srs_success; @@ -3575,7 +3618,8 @@ srs_error_t SrsConfig::check_normal_config() for (int i = 0; conf && i < (int)conf->directives.size(); i++) { string n = conf->at(i)->name; if (n != "enabled" && n != "listen" && n != "dir" && n != "candidate" && n != "ecdsa" - && n != "sendmmsg" && n != "encrypt") { + && n != "sendmmsg" && n != "encrypt" && n != "reuseport" && n != "gso" && n != "merge_nalus" + && n != "padding" && n != "perf_stat" && n != "queue_length") { return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal rtc_server.%s", n.c_str()); } } @@ -3741,7 +3785,8 @@ srs_error_t SrsConfig::check_normal_config() for (int j = 0; j < (int)conf->directives.size(); j++) { string m = conf->at(j)->name; if (m != "time_jitter" && m != "mix_correct" && m != "atc" && m != "atc_auto" && m != "mw_latency" - && m != "gop_cache" && m != "queue_length" && m != "send_min_interval" && m != "reduce_sequence_header") { + && m != "gop_cache" && m != "queue_length" && m != "send_min_interval" && m != "reduce_sequence_header" + && m != "mw_msgs") { return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal vhost.play.%s of %s", m.c_str(), vhost->arg0().c_str()); } } @@ -4706,6 +4751,148 @@ int SrsConfig::get_rtc_server_sendmmsg() #endif } +int SrsConfig::get_rtc_server_reuseport() +{ + int v = get_rtc_server_reuseport2(); + +#if !defined(SO_REUSEPORT) + srs_warn("REUSEPORT not supported, reset %d to %d", reuseport, DEFAULT); + v = 1 +#endif + + return v; +} + +int SrsConfig::get_rtc_server_reuseport2() +{ + static int DEFAULT = 4; + + SrsConfDirective* conf = root->get("rtc_server"); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("reuseport"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + return ::atoi(conf->arg0().c_str()); +} + +bool SrsConfig::get_rtc_server_merge_nalus() +{ + static int DEFAULT = true; + + SrsConfDirective* conf = root->get("rtc_server"); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("merge_nalus"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + 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() +{ + static bool DEFAULT = true; + + SrsConfDirective* conf = root->get("rtc_server"); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("perf_stat"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + return SRS_CONF_PERFER_TRUE(conf->arg0()); +} + +int SrsConfig::get_rtc_server_queue_length() +{ + static int DEFAULT = 2000; + + SrsConfDirective* conf = root->get("rtc_server"); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("queue_length"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + return ::atoi(conf->arg0().c_str()); +} + SrsConfDirective* SrsConfig::get_rtc(string vhost) { SrsConfDirective* conf = get_vhost(vhost); @@ -5233,8 +5420,48 @@ srs_utime_t SrsConfig::get_mw_sleep(string vhost, bool is_rtc) if (!conf || conf->arg0().empty()) { return DEFAULT; } + + int v = ::atoi(conf->arg0().c_str()); + if (is_rtc && v > 0) { + srs_warn("For RTC, we ignore mw_latency"); + return 0; + } - return (srs_utime_t)(::atoi(conf->arg0().c_str()) * SRS_UTIME_MILLISECONDS); + return (srs_utime_t)(v * SRS_UTIME_MILLISECONDS); +} + +int SrsConfig::get_mw_msgs(string vhost, bool is_realtime, bool is_rtc) +{ + int DEFAULT = SRS_PERF_MW_MIN_MSGS; + if (is_rtc) { + DEFAULT = SRS_PERF_MW_MIN_MSGS_FOR_RTC; + } + if (is_realtime) { + DEFAULT = SRS_PERF_MW_MIN_MSGS_REALTIME; + } + + SrsConfDirective* conf = get_vhost(vhost); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("play"); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("mw_msgs"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + int v = ::atoi(conf->arg0().c_str()); + if (v > SRS_PERF_MW_MSGS) { + srs_warn("reset mw_msgs %d to max %d", v, SRS_PERF_MW_MSGS); + v = SRS_PERF_MW_MSGS; + } + + return v; } bool SrsConfig::get_realtime_enabled(string vhost, bool is_rtc) diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index f7f449b08..a905949b1 100644 --- a/trunk/src/app/srs_app_config.hpp +++ b/trunk/src/app/srs_app_config.hpp @@ -333,6 +333,8 @@ private: // Reload the http_stream section of config. // TODO: FIXME: rename to http_server. virtual srs_error_t reload_http_stream(SrsConfDirective* old_root); + // Reload the rtc_server section of config. + virtual srs_error_t reload_rtc_server(SrsConfDirective* old_root); // Reload the transcode section of vhost of config. virtual srs_error_t reload_transcode(SrsConfDirective* new_vhost, SrsConfDirective* old_vhost); // Reload the ingest section of vhost of config. @@ -525,7 +527,20 @@ public: virtual bool get_rtc_server_ecdsa(); virtual int get_rtc_server_sendmmsg(); virtual bool get_rtc_server_encrypt(); + virtual int get_rtc_server_reuseport(); +private: + virtual int get_rtc_server_reuseport2(); +public: + virtual bool get_rtc_server_merge_nalus(); + virtual bool get_rtc_server_gso(); +private: + virtual bool get_rtc_server_gso2(); +public: + virtual int get_rtc_server_padding(); + virtual bool get_rtc_server_perf_stat(); + virtual int get_rtc_server_queue_length(); +public: SrsConfDirective* get_rtc(std::string vhost); bool get_rtc_enabled(std::string vhost); bool get_rtc_bframe_discard(std::string vhost); @@ -615,6 +630,10 @@ public: // @param vhost, the vhost to get the mw sleep time. // TODO: FIXME: add utest for mw config. virtual srs_utime_t get_mw_sleep(std::string vhost, bool is_rtc = false); + // Get the mw_msgs, mw wait time in packets for vhost. + // @param vhost, the vhost to get the mw sleep msgs. + // TODO: FIXME: add utest for mw config. + virtual int get_mw_msgs(std::string vhost, bool is_realtime, bool is_rtc = false); // Whether min latency mode enabled. // @param vhost, the vhost to get the min_latency. // TODO: FIXME: add utest for min_latency. diff --git a/trunk/src/app/srs_app_http_api.cpp b/trunk/src/app/srs_app_http_api.cpp index 1f3749b7a..61552def2 100644 --- a/trunk/src/app/srs_app_http_api.cpp +++ b/trunk/src/app/srs_app_http_api.cpp @@ -869,6 +869,7 @@ srs_error_t SrsGoApiRtcPlay::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMe api = prop->to_str(); } + // TODO: FIXME: Parse vhost. // Parse app and stream from streamurl. string app; string stream_name; @@ -909,6 +910,13 @@ srs_error_t SrsGoApiRtcPlay::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMe 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); @@ -1615,20 +1623,56 @@ srs_error_t SrsGoApiPerf::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* SrsStatistic* stat = SrsStatistic::instance(); string target = r->query_get("target"); - srs_trace("query target=%s", target.c_str()); + string reset = r->query_get("reset"); + srs_trace("query target=%s, reset=%s, rtc_stat_enabled=%d", target.c_str(), reset.c_str(), + _srs_config->get_rtc_server_perf_stat()); if (true) { SrsJsonObject* p = SrsJsonAny::object(); data->set("query", p); p->set("target", SrsJsonAny::str(target.c_str())); - p->set("help", SrsJsonAny::str("?target=writev|sendmmsg")); + 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("help2", SrsJsonAny::str("?reset=all")); } - if (target.empty() || target == "writev") { + if (!reset.empty()) { + stat->reset_perf(); + return srs_api_response(w, r, obj->dumps()); + } + + if (target.empty() || target == "avframes") { SrsJsonObject* p = SrsJsonAny::object(); - data->set("writev", p); - if ((err = stat->dumps_perf_writev(p)) != srs_success) { + data->set("avframes", p); + if ((err = stat->dumps_perf_msgs(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 == "rtc") { + SrsJsonObject* p = SrsJsonAny::object(); + data->set("rtc", p); + if ((err = stat->dumps_perf_rtc_packets(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 == "rtp") { + SrsJsonObject* p = SrsJsonAny::object(); + data->set("rtp", p); + if ((err = stat->dumps_perf_rtp_packets(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 == "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); } @@ -1643,6 +1687,33 @@ srs_error_t SrsGoApiPerf::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* } } + if (target.empty() || target == "writev_iovs") { + SrsJsonObject* p = SrsJsonAny::object(); + data->set("writev_iovs", p); + if ((err = stat->dumps_perf_writev_iovs(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 == "bytes") { + SrsJsonObject* p = SrsJsonAny::object(); + data->set("bytes", p); + if ((err = stat->dumps_perf_bytes(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 == "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()); } diff --git a/trunk/src/app/srs_app_http_stream.cpp b/trunk/src/app/srs_app_http_stream.cpp index 6609c3852..95a67c21a 100755 --- a/trunk/src/app/srs_app_http_stream.cpp +++ b/trunk/src/app/srs_app_http_stream.cpp @@ -660,7 +660,8 @@ srs_error_t SrsLiveStream::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMess if ((err = consumer->dump_packets(&msgs, count)) != srs_success) { return srs_error_wrap(err, "consumer dump packets"); } - + + // TODO: FIXME: Support merged-write wait. if (count <= 0) { // Directly use sleep, donot use consumer wait, because we couldn't awake consumer. srs_usleep(mw_sleep); @@ -680,6 +681,8 @@ srs_error_t SrsLiveStream::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMess err = streaming_send_messages(enc, msgs.msgs, count); } + // TODO: FIXME: Update the stat. + // free the messages. for (int i = 0; i < count; i++) { SrsSharedPtrMessage* msg = msgs.msgs[i]; diff --git a/trunk/src/app/srs_app_listener.cpp b/trunk/src/app/srs_app_listener.cpp index a88bf1af3..3294c79f5 100755 --- a/trunk/src/app/srs_app_listener.cpp +++ b/trunk/src/app/srs_app_listener.cpp @@ -40,6 +40,7 @@ using namespace std; #include #include #include +#include // set the max packet size. #define SRS_UDP_MAX_PACKET_SIZE 65535 @@ -235,12 +236,21 @@ srs_error_t SrsTcpListener::cycle() return err; } -SrsUdpMuxSocket::SrsUdpMuxSocket(srs_netfd_t fd) +ISrsUdpSender::ISrsUdpSender() +{ +} + +ISrsUdpSender::~ISrsUdpSender() +{ +} + +SrsUdpMuxSocket::SrsUdpMuxSocket(ISrsUdpSender* h, srs_netfd_t fd) { nb_buf = SRS_UDP_MAX_PACKET_SIZE; buf = new char[nb_buf]; nread = 0; + handler = h; lfd = fd; fromlen = 0; @@ -253,7 +263,7 @@ SrsUdpMuxSocket::~SrsUdpMuxSocket() SrsUdpMuxSocket* SrsUdpMuxSocket::copy_sendonly() { - SrsUdpMuxSocket* sendonly = new SrsUdpMuxSocket(lfd); + SrsUdpMuxSocket* sendonly = new SrsUdpMuxSocket(handler, lfd); // Don't copy buffer srs_freepa(sendonly->buf); @@ -339,16 +349,18 @@ std::string SrsUdpMuxSocket::get_peer_id() return string(id_buf, len); } -SrsUdpMuxListener::SrsUdpMuxListener(ISrsUdpMuxHandler* h, std::string i, int p) +SrsUdpMuxListener::SrsUdpMuxListener(ISrsUdpMuxHandler* h, ISrsUdpSender* s, std::string i, int p) { handler = h; + sender = s; + ip = i; port = p; lfd = NULL; nb_buf = SRS_UDP_MAX_PACKET_SIZE; buf = new char[nb_buf]; - + trd = new SrsDummyCoroutine(); } @@ -390,60 +402,108 @@ srs_error_t SrsUdpMuxListener::listen() void SrsUdpMuxListener::set_socket_buffer() { - int sndbuf_size = 0; - socklen_t opt_len = sizeof(sndbuf_size); - getsockopt(fd(), SOL_SOCKET, SO_SNDBUF, (void*)&sndbuf_size, &opt_len); - srs_trace("default udp remux socket sndbuf=%d", sndbuf_size); + 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); + getsockopt(fd(), SOL_SOCKET, SO_SNDBUF, (void*)&default_sndbuf, &opt_len); - sndbuf_size = 1024*1024*10; // 10M - if (setsockopt(fd(), SOL_SOCKET, SO_SNDBUF, (void*)&sndbuf_size, sizeof(sndbuf_size)) < 0) { - srs_warn("set sock opt SO_SNDBUFFORCE failed"); + 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); + getsockopt(fd(), SOL_SOCKET, SO_SNDBUF, (void*)&actual_sndbuf, &opt_len); } - opt_len = sizeof(sndbuf_size); - getsockopt(fd(), SOL_SOCKET, SO_SNDBUF, (void*)&sndbuf_size, &opt_len); - srs_trace("udp remux socket sndbuf=%d", sndbuf_size); + 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); + getsockopt(fd(), SOL_SOCKET, SO_RCVBUF, (void*)&default_rcvbuf, &opt_len); - int rcvbuf_size = 0; - opt_len = sizeof(rcvbuf_size); - getsockopt(fd(), SOL_SOCKET, SO_RCVBUF, (void*)&rcvbuf_size, &opt_len); - srs_trace("default udp remux socket rcvbuf=%d", rcvbuf_size); + 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); + } - rcvbuf_size = 1024*1024*10; // 10M - if (setsockopt(fd(), SOL_SOCKET, SO_RCVBUF, (void*)&rcvbuf_size, sizeof(rcvbuf_size)) < 0) { - srs_warn("set sock opt SO_RCVBUFFORCE failed"); + opt_len = sizeof(actual_rcvbuf); + getsockopt(fd(), SOL_SOCKET, SO_RCVBUF, (void*)&actual_rcvbuf, &opt_len); } - opt_len = sizeof(rcvbuf_size); - getsockopt(fd(), SOL_SOCKET, SO_RCVBUF, (void*)&rcvbuf_size, &opt_len); - srs_trace("udp remux socket rcvbuf=%d", rcvbuf_size); + 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 SrsUdpMuxListener::cycle() { srs_error_t err = srs_success; + + SrsPithyPrint* pprint = SrsPithyPrint::create_rtc_recv(srs_netfd_fileno(lfd)); + SrsAutoFree(SrsPithyPrint, pprint); + + uint64_t nn_msgs = 0; + uint64_t nn_msgs_stage = 0; + uint64_t nn_msgs_last = 0; + uint64_t nn_loop = 0; + srs_utime_t time_last = srs_get_system_time(); while (true) { if ((err = trd->pull()) != srs_success) { return srs_error_wrap(err, "udp listener"); - } + } - SrsUdpMuxSocket udp_mux_skt(lfd); + nn_loop++; - int nread = udp_mux_skt.recvfrom(SRS_UTIME_NO_TIMEOUT); + SrsUdpMuxSocket skt(sender, lfd); + + int nread = skt.recvfrom(SRS_UTIME_NO_TIMEOUT); if (nread <= 0) { if (nread < 0) { srs_warn("udp recv error"); } // remux udp never return continue; - } + } + + nn_msgs++; + nn_msgs_stage++; - if ((err = handler->on_udp_packet(&udp_mux_skt)) != srs_success) { + if ((err = handler->on_udp_packet(&skt)) != srs_success) { // remux udp never return srs_warn("udp packet handler error:%s", srs_error_desc(err).c_str()); - continue; - } + srs_error_reset(err); + } + + pprint->elapse(); + if (pprint->can_print()) { + int pps_average = 0; int pps_last = 0; + if (true) { + if (srs_get_system_time() > srs_get_system_startup_time()) { + pps_average = (int)(nn_msgs * SRS_UTIME_SECONDS / (srs_get_system_time() - srs_get_system_startup_time())); + } + if (srs_get_system_time() > time_last) { + pps_last = (int)((nn_msgs - nn_msgs_last) * SRS_UTIME_SECONDS / (srs_get_system_time() - time_last)); + } + } + + string pps_unit = ""; + if (pps_last > 10000 || pps_average > 10000) { + pps_unit = "(w)"; pps_last /= 10000; pps_average /= 10000; + } else if (pps_last > 1000 || pps_average > 1000) { + pps_unit = "(k)"; pps_last /= 10000; pps_average /= 10000; + } + + srs_trace("<- RTC RECV #%d, udp %" PRId64 ", pps %d/%d%s, schedule %" PRId64, + srs_netfd_fileno(lfd), nn_msgs_stage, pps_average, pps_last, pps_unit.c_str(), nn_loop); + nn_msgs_last = nn_msgs; time_last = srs_get_system_time(); + nn_loop = 0; nn_msgs_stage = 0; + } if (SrsUdpPacketRecvCycleInterval > 0) { srs_usleep(SrsUdpPacketRecvCycleInterval); diff --git a/trunk/src/app/srs_app_listener.hpp b/trunk/src/app/srs_app_listener.hpp index 0222413c1..cc20cc756 100644 --- a/trunk/src/app/srs_app_listener.hpp +++ b/trunk/src/app/srs_app_listener.hpp @@ -69,7 +69,7 @@ public: virtual ~ISrsUdpMuxHandler(); public: virtual srs_error_t on_stfd_change(srs_netfd_t fd); - virtual srs_error_t on_udp_packet(SrsUdpMuxSocket* udp_mux_skt) = 0; + virtual srs_error_t on_udp_packet(SrsUdpMuxSocket* skt) = 0; }; // The tcp connection handler. @@ -131,9 +131,27 @@ public: virtual srs_error_t cycle(); }; +class ISrsUdpSender +{ +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 { private: + ISrsUdpSender* handler; char* buf; int nb_buf; int nread; @@ -143,7 +161,7 @@ private: std::string peer_ip; int peer_port; public: - SrsUdpMuxSocket(srs_netfd_t fd); + SrsUdpMuxSocket(ISrsUdpSender* h, srs_netfd_t fd); virtual ~SrsUdpMuxSocket(); int recvfrom(srs_utime_t timeout); @@ -160,6 +178,7 @@ public: std::string get_peer_id(); public: SrsUdpMuxSocket* copy_sendonly(); + ISrsUdpSender* sender() { return handler; }; private: // Don't allow copy, user copy_sendonly instead SrsUdpMuxSocket(const SrsUdpMuxSocket& rhs); @@ -170,6 +189,7 @@ class SrsUdpMuxListener : public ISrsCoroutineHandler { protected: srs_netfd_t lfd; + ISrsUdpSender* sender; SrsCoroutine* trd; protected: char* buf; @@ -179,7 +199,7 @@ protected: std::string ip; int port; public: - SrsUdpMuxListener(ISrsUdpMuxHandler* h, std::string i, int p); + SrsUdpMuxListener(ISrsUdpMuxHandler* h, ISrsUdpSender* s, std::string i, int p); virtual ~SrsUdpMuxListener(); public: virtual int fd(); diff --git a/trunk/src/app/srs_app_pithy_print.cpp b/trunk/src/app/srs_app_pithy_print.cpp index cef07f1de..58f8bca4a 100644 --- a/trunk/src/app/srs_app_pithy_print.cpp +++ b/trunk/src/app/srs_app_pithy_print.cpp @@ -112,6 +112,10 @@ SrsPithyPrint::SrsPithyPrint(int _stage_id) #define SRS_CONSTS_STAGE_EXEC 11 // for the rtc play #define SRS_CONSTS_STAGE_RTC_PLAY 12 +// for the rtc send +#define SRS_CONSTS_STAGE_RTC_SEND 13 +// for the rtc recv +#define SRS_CONSTS_STAGE_RTC_RECV 14 SrsPithyPrint* SrsPithyPrint::create_rtmp_play() { @@ -173,6 +177,16 @@ SrsPithyPrint* SrsPithyPrint::create_rtc_play() return new SrsPithyPrint(SRS_CONSTS_STAGE_RTC_PLAY); } +SrsPithyPrint* SrsPithyPrint::create_rtc_send(int fd) +{ + return new SrsPithyPrint(fd<<16 | SRS_CONSTS_STAGE_RTC_SEND); +} + +SrsPithyPrint* SrsPithyPrint::create_rtc_recv(int fd) +{ + return new SrsPithyPrint(fd<<16 | SRS_CONSTS_STAGE_RTC_RECV); +} + SrsPithyPrint::~SrsPithyPrint() { leave_stage(); diff --git a/trunk/src/app/srs_app_pithy_print.hpp b/trunk/src/app/srs_app_pithy_print.hpp index c3b9f1219..ab289e0e7 100644 --- a/trunk/src/app/srs_app_pithy_print.hpp +++ b/trunk/src/app/srs_app_pithy_print.hpp @@ -88,6 +88,9 @@ public: static SrsPithyPrint* create_http_stream(); static SrsPithyPrint* create_http_stream_cache(); static SrsPithyPrint* create_rtc_play(); + // For RTC sender and receiver, we create printer for each fd. + static SrsPithyPrint* create_rtc_send(int fd); + static SrsPithyPrint* create_rtc_recv(int fd); virtual ~SrsPithyPrint(); private: // Enter the specified stage, return the client id. diff --git a/trunk/src/app/srs_app_reload.cpp b/trunk/src/app/srs_app_reload.cpp index 175cbfc14..3546f44de 100644 --- a/trunk/src/app/srs_app_reload.cpp +++ b/trunk/src/app/srs_app_reload.cpp @@ -115,6 +115,11 @@ srs_error_t ISrsReloadHandler::on_reload_http_stream_crossdomain() return srs_success; } +srs_error_t ISrsReloadHandler::on_reload_rtc_server() +{ + return srs_success; +} + srs_error_t ISrsReloadHandler::on_reload_vhost_http_updated() { return srs_success; diff --git a/trunk/src/app/srs_app_reload.hpp b/trunk/src/app/srs_app_reload.hpp index 442f44ebd..a7af5e790 100644 --- a/trunk/src/app/srs_app_reload.hpp +++ b/trunk/src/app/srs_app_reload.hpp @@ -55,6 +55,7 @@ public: virtual srs_error_t on_reload_http_stream_disabled(); virtual srs_error_t on_reload_http_stream_updated(); virtual srs_error_t on_reload_http_stream_crossdomain(); + virtual srs_error_t on_reload_rtc_server(); public: // TODO: FIXME: should rename to http_static virtual srs_error_t on_reload_vhost_http_updated(); diff --git a/trunk/src/app/srs_app_rtc.cpp b/trunk/src/app/srs_app_rtc.cpp index 8424216ce..95cfea597 100644 --- a/trunk/src/app/srs_app_rtc.cpp +++ b/trunk/src/app/srs_app_rtc.cpp @@ -54,7 +54,7 @@ using namespace std; #include // TODO: Add this function into SrsRtpMux class. -srs_error_t aac_raw_append_adts_header(SrsSharedPtrMessage* shared_audio, SrsFormat* format, SrsBuffer** stream_ptr) +srs_error_t aac_raw_append_adts_header(SrsSharedPtrMessage* shared_audio, SrsFormat* format, char** pbuf, int* pnn_buf) { srs_error_t err = srs_success; @@ -62,37 +62,33 @@ srs_error_t aac_raw_append_adts_header(SrsSharedPtrMessage* shared_audio, SrsFor return err; } - if (stream_ptr == NULL) { - return srs_error_new(ERROR_RTC_RTP_MUXER, "adts"); - } - 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 = new SrsBuffer(buf, 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_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); + stream.write_bytes(format->audio->samples[0].bytes, format->audio->samples[0].size); - *stream_ptr = stream; + *pbuf = buf; + *pnn_buf = nb_buf; return err; } SrsRtpH264Muxer::SrsRtpH264Muxer() { - sequence = 0; discard_bframe = false; } @@ -100,269 +96,118 @@ SrsRtpH264Muxer::~SrsRtpH264Muxer() { } -srs_error_t SrsRtpH264Muxer::frame_to_packet(SrsSharedPtrMessage* shared_frame, SrsFormat* format) +srs_error_t SrsRtpH264Muxer::filter(SrsSharedPtrMessage* shared_frame, SrsFormat* format) { srs_error_t err = srs_success; - if (format->is_avc_sequence_header()) { - sps.assign(format->vcodec->sequenceParameterSetNALUnit.data(), format->vcodec->sequenceParameterSetNALUnit.size()); - pps.assign(format->vcodec->pictureParameterSetNALUnit.data(), format->vcodec->pictureParameterSetNALUnit.size()); - // only collect SPS/PPS. + // 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; } - vector rtp_packet_vec; - - for (int i = 0; i < format->video->nb_samples; ++i) { - SrsSample sample = format->video->samples[i]; - - uint8_t header = sample.bytes[0]; - uint8_t nal_type = header & kNalTypeMask; - - // TODO: Use config to determine should check avc stream. - if (nal_type == SrsAvcNaluTypeNonIDR || nal_type == SrsAvcNaluTypeDataPartitionA || nal_type == SrsAvcNaluTypeIDR) { - SrsBuffer* stream = new SrsBuffer(sample.bytes, sample.size); - SrsAutoFree(SrsBuffer, stream); - - // Skip nalu header. - stream->skip(1); - - SrsBitBuffer bitstream(stream); - int32_t first_mb_in_slice = 0; - if ((err = srs_avc_nalu_read_uev(&bitstream, first_mb_in_slice)) != srs_success) { - return srs_error_wrap(err, "nalu read uev"); - } - - int32_t slice_type = 0; - if ((err = srs_avc_nalu_read_uev(&bitstream, slice_type)) != srs_success) { - return srs_error_wrap(err, "nalu read uev"); - } - - srs_verbose("nal_type=%d, slice type=%d", nal_type, slice_type); - if (slice_type == SrsAvcSliceTypeB || slice_type == SrsAvcSliceTypeB1) { - if (discard_bframe) { - continue; - } - } - } - - if (sample.size <= kRtpMaxPayloadSize) { - if ((err = packet_single_nalu(shared_frame, format, &sample, rtp_packet_vec)) != srs_success) { - return srs_error_wrap(err, "packet single nalu"); - } - } else { - if ((err = packet_fu_a(shared_frame, format, &sample, rtp_packet_vec)) != srs_success) { - return srs_error_wrap(err, "packet fu-a"); - } - } - } - - if (! rtp_packet_vec.empty()) { - // At the end of the frame, set marker bit. - // One frame may have multi nals. Set the marker bit in the last nal end, no the end of the nal. - if ((err = rtp_packet_vec.back()->modify_rtp_header_marker(true)) != srs_success) { - return srs_error_wrap(err, "set marker"); - } - } - - shared_frame->set_rtp_packets(rtp_packet_vec); - - return err; -} - -srs_error_t SrsRtpH264Muxer::packet_fu_a(SrsSharedPtrMessage* shared_frame, SrsFormat* format, SrsSample* sample, vector& rtp_packet_vec) -{ - srs_error_t err = srs_success; - - char* p = sample->bytes + 1; - int nb_left = sample->size - 1; - uint8_t header = sample->bytes[0]; - uint8_t nal_type = header & kNalTypeMask; - - if (nal_type == SrsAvcNaluTypeIDR) { - if ((err = packet_stap_a(sps, pps, shared_frame, rtp_packet_vec)) != srs_success) { - return srs_error_wrap(err, "packet stap-a"); - } - } - - int num_of_packet = (sample->size - 1 + kRtpMaxPayloadSize) / kRtpMaxPayloadSize; - for (int i = 0; i < num_of_packet; ++i) { - char buf[kRtpPacketSize]; - SrsBuffer* stream = new SrsBuffer(buf, kRtpPacketSize); - SrsAutoFree(SrsBuffer, stream); - - int packet_size = min(nb_left, kRtpMaxPayloadSize); - - // fu-indicate - uint8_t fu_indicate = kFuA; - fu_indicate |= (header & (~kNalTypeMask)); - stream->write_1bytes(fu_indicate); - - uint8_t fu_header = nal_type; - if (i == 0) - fu_header |= kStart; - if (i == num_of_packet - 1) - fu_header |= kEnd; - stream->write_1bytes(fu_header); - - stream->write_bytes(p, packet_size); - p += packet_size; - nb_left -= packet_size; - - srs_verbose("rtp fu-a nalu, size=%u, seq=%u, timestamp=%lu", sample->size, sequence, (shared_frame->timestamp * 90)); - - SrsRtpSharedPacket* rtp_shared_pkt = new SrsRtpSharedPacket(); - if ((err = rtp_shared_pkt->create((shared_frame->timestamp * 90), sequence++, kVideoSSRC, kH264PayloadType, stream->data(), stream->pos())) != srs_success) { - return srs_error_wrap(err, "rtp packet encode"); - } - - rtp_packet_vec.push_back(rtp_shared_pkt); - } - - return err; -} - -srs_error_t SrsRtpH264Muxer::packet_single_nalu(SrsSharedPtrMessage* shared_frame, SrsFormat* format, SrsSample* sample, vector& rtp_packet_vec) -{ - srs_error_t err = srs_success; - - uint8_t header = sample->bytes[0]; - uint8_t nal_type = header & kNalTypeMask; - - - if (nal_type == SrsAvcNaluTypeIDR) { - if ((err = packet_stap_a(sps, pps, shared_frame, rtp_packet_vec)) != srs_success) { - return srs_error_wrap(err, "packet stap-a"); - } - } - - srs_verbose("rtp single nalu, size=%u, seq=%u, timestamp=%lu", sample->size, sequence, (shared_frame->timestamp * 90)); - - SrsRtpSharedPacket* rtp_shared_pkt = new SrsRtpSharedPacket(); - if ((err = rtp_shared_pkt->create((shared_frame->timestamp * 90), sequence++, kVideoSSRC, kH264PayloadType, sample->bytes, sample->size)) != srs_success) { - return srs_error_wrap(err, "rtp packet encode"); - } - - rtp_packet_vec.push_back(rtp_shared_pkt); - - return err; -} - -srs_error_t SrsRtpH264Muxer::packet_stap_a(const string &sps, const string& pps, SrsSharedPtrMessage* shared_frame, vector& rtp_packet_vec) -{ - srs_error_t err = srs_success; - - if (sps.empty() || pps.empty()) { - return srs_error_new(ERROR_RTC_RTP_MUXER, "sps/pps empty"); - } - - uint8_t header = sps[0]; - uint8_t nal_type = header & kNalTypeMask; - - char buf[kRtpPacketSize]; - SrsBuffer* stream = new SrsBuffer(buf, kRtpPacketSize); - SrsAutoFree(SrsBuffer, stream); - - // stap-a header - uint8_t stap_a_header = kStapA; - stap_a_header |= (nal_type & (~kNalTypeMask)); - stream->write_1bytes(stap_a_header); - - stream->write_2bytes(sps.size()); - stream->write_bytes((char*)sps.data(), sps.size()); - - stream->write_2bytes(pps.size()); - stream->write_bytes((char*)pps.data(), pps.size()); - - srs_verbose("rtp stap-a nalu, size=%u, seq=%u, timestamp=%lu", (sps.size() + pps.size()), sequence, (shared_frame->timestamp * 90)); - - SrsRtpSharedPacket* rtp_shared_pkt = new SrsRtpSharedPacket(); - if ((err = rtp_shared_pkt->create((shared_frame->timestamp * 90), sequence++, kVideoSSRC, kH264PayloadType, stream->data(), stream->pos())) != srs_success) { - return srs_error_wrap(err, "rtp packet encode"); - } - - rtp_packet_vec.push_back(rtp_shared_pkt); + shared_frame->set_samples(format->video->samples, format->video->nb_samples); return err; } SrsRtpOpusMuxer::SrsRtpOpusMuxer() { - sequence = 0; - timestamp = 0; - transcode = NULL; + codec = NULL; } SrsRtpOpusMuxer::~SrsRtpOpusMuxer() { - if (transcode) { - delete transcode; - transcode = NULL; - } + srs_freep(codec); } srs_error_t SrsRtpOpusMuxer::initialize() { srs_error_t err = srs_success; - transcode = new SrsAudioRecode(kChannel, kSamplerate); - if (!transcode) { + codec = new SrsAudioRecode(kChannel, kSamplerate); + if (!codec) { return srs_error_new(ERROR_RTC_RTP_MUXER, "SrsAacOpus init failed"); } - transcode->initialize(); + + if ((err = codec->initialize()) != srs_success) { + return srs_error_wrap(err, "init codec"); + } return err; } -srs_error_t SrsRtpOpusMuxer::frame_to_packet(SrsSharedPtrMessage* shared_audio, SrsFormat* format, SrsBuffer* stream) +// 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; - vector rtp_packet_vec; + // Opus packet cache. + static char* opus_payloads[kMaxOpusPackets]; - char* data_ptr[kArrayLength]; - static char data_array[kArrayLength][kArrayBuffer]; - int elen[kArrayLength], number = 0; + static bool initialized = false; + if (!initialized) { + initialized = true; - data_ptr[0] = &data_array[0][0]; - for (int i = 1; i < kArrayLength; i++) { - data_ptr[i] = data_array[i]; + 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]; + } } - SrsSample pkt; - pkt.bytes = stream->data(); - pkt.size = stream->pos(); + // Transcode an aac packet to many opus packets. + SrsSample aac; + aac.bytes = adts_audio; + aac.size = nn_adts_audio; - if ((err = transcode->recode(&pkt, data_ptr, elen, number)) != srs_success) { + 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"); } - for (int i = 0; i < number; i++) { - SrsSample sample; - sample.size = elen[i]; - sample.bytes = data_ptr[i]; - packet_opus(shared_audio, &sample, rtp_packet_vec); + // Save OPUS packets in shared message. + if (nn_opus_packets <= 0) { + return err; } - shared_audio->set_rtp_packets(rtp_packet_vec); + 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); - return err; -} - -srs_error_t SrsRtpOpusMuxer::packet_opus(SrsSharedPtrMessage* shared_frame, SrsSample* sample, std::vector& rtp_packet_vec) -{ - srs_error_t err = srs_success; - - SrsRtpSharedPacket* rtp_shared_pkt = new SrsRtpSharedPacket(); - rtp_shared_pkt->rtp_header.set_marker(true); - if ((err = rtp_shared_pkt->create(timestamp, sequence++, kAudioSSRC, kOpusPayloadType, sample->bytes, sample->size)) != srs_success) { - return srs_error_wrap(err, "rtp packet encode"); + nn_max_extra_payload = srs_max(nn_max_extra_payload, p->size); } - // TODO: FIXME: Why 960? Need Refactoring? - timestamp += 960; - - rtp_packet_vec.push_back(rtp_shared_pkt); + shared_audio->set_extra_payloads(samples, nn_opus_packets); + shared_audio->set_max_extra_payload(nn_max_extra_payload); return err; } @@ -485,17 +330,16 @@ srs_error_t SrsRtc::on_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* forma // ignore sequence header srs_assert(format->audio); - SrsBuffer* stream = NULL; - SrsAutoFree(SrsBuffer, stream); - if ((err = aac_raw_append_adts_header(shared_audio, format, &stream)) != srs_success) { + 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 (stream) { - char* stream_data = stream->data(); - SrsAutoFreeA(char, stream_data); - - return rtp_opus_muxer->frame_to_packet(shared_audio, format, stream); + if (adts_audio) { + err = rtp_opus_muxer->transcode(shared_audio, adts_audio, nn_adts_audio); + srs_freep(adts_audio); } return err; @@ -522,5 +366,5 @@ srs_error_t SrsRtc::on_video(SrsSharedPtrMessage* shared_video, SrsFormat* forma // ignore info frame, // @see https://github.com/ossrs/srs/issues/288#issuecomment-69863909 srs_assert(format->video); - return rtp_h264_muxer->frame_to_packet(shared_video, format); + return rtp_h264_muxer->filter(shared_video, format); } diff --git a/trunk/src/app/srs_app_rtc.hpp b/trunk/src/app/srs_app_rtc.hpp index cf3e36a31..d232ca31f 100644 --- a/trunk/src/app/srs_app_rtc.hpp +++ b/trunk/src/app/srs_app_rtc.hpp @@ -39,30 +39,15 @@ class SrsOriginHub; class SrsAudioRecode; class SrsBuffer; -// Rtp packet max payload size, not include rtp header. -// Must left some bytes to payload header, rtp header, udp header, ip header. -const int kRtpMaxPayloadSize = 1200; +// 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; -// H.264 nalu header type mask. -const uint8_t kNalTypeMask = 0x1F; - -// @see: https://tools.ietf.org/html/rfc6184#section-5.2 -const uint8_t kStapA = 24; -const uint8_t kFuA = 28; - -// @see: https://tools.ietf.org/html/rfc6184#section-5.8 -const uint8_t kStart = 0x80; // Fu-header start bit -const uint8_t kEnd = 0x40; // Fu-header end bit - const int kChannel = 2; const int kSamplerate = 48000; -const int kArrayLength = 8; -const int kArrayBuffer = 4096; // SSRC will rewrite in srs_app_rtc_conn.cpp when send to client. const uint32_t kAudioSSRC = 1; @@ -71,39 +56,26 @@ const uint32_t kVideoSSRC = 2; // TODO: Define interface class like ISrsRtpMuxer class SrsRtpH264Muxer { -private: - uint16_t sequence; - std::string sps; - std::string pps; public: bool discard_bframe; public: SrsRtpH264Muxer(); virtual ~SrsRtpH264Muxer(); public: - srs_error_t frame_to_packet(SrsSharedPtrMessage* shared_video, SrsFormat* format); -private: - srs_error_t packet_fu_a(SrsSharedPtrMessage* shared_frame, SrsFormat* format, SrsSample* sample, std::vector& rtp_packet_vec); - srs_error_t packet_single_nalu(SrsSharedPtrMessage* shared_frame, SrsFormat* format, SrsSample* sample, std::vector& rtp_packet_vec); - srs_error_t packet_stap_a(const std::string &sps, const std::string& pps, SrsSharedPtrMessage* shared_frame, std::vector& rtp_packet_vec); + srs_error_t filter(SrsSharedPtrMessage* shared_video, SrsFormat* format); }; // TODO: FIXME: It's not a muxer, but a transcoder. class SrsRtpOpusMuxer { private: - // TODO: FIXME: How to handle timestamp overflow? - uint32_t timestamp; - uint16_t sequence; - SrsAudioRecode* transcode; + SrsAudioRecode* codec; public: SrsRtpOpusMuxer(); virtual ~SrsRtpOpusMuxer(); virtual srs_error_t initialize(); public: - srs_error_t frame_to_packet(SrsSharedPtrMessage* shared_audio, SrsFormat* format, SrsBuffer* stream); -private: - srs_error_t packet_opus(SrsSharedPtrMessage* shared_frame, SrsSample* sample, std::vector& rtp_packet_vec); + srs_error_t transcode(SrsSharedPtrMessage* shared_audio, char* adts_audio, int nn_adts_audio); }; class SrsRtc diff --git a/trunk/src/app/srs_app_rtc_conn.cpp b/trunk/src/app/srs_app_rtc_conn.cpp index 9c6c3cf8f..dd9a791b2 100644 --- a/trunk/src/app/srs_app_rtc_conn.cpp +++ b/trunk/src/app/srs_app_rtc_conn.cpp @@ -33,6 +33,11 @@ using namespace std; #include #include +#include +#ifndef UDP_SEGMENT +#define UDP_SEGMENT 103 +#endif + #include #include @@ -55,6 +60,12 @@ using namespace std; #include #include +// The RTP payload max size, reserved some paddings for SRTP as such: +// kRtpPacketSize = kRtpMaxPayloadSize + paddings +// For example, if kRtpPacketSize is 1500, recommend to set kRtpMaxPayloadSize to 1400, +// which reserves 100 bytes for SRTP or paddings. +const int kRtpMaxPayloadSize = kRtpPacketSize - 200; + static bool is_stun(const uint8_t* data, const int size) { return data != NULL && size > 0 && (data[0] == 0 || data[0] == 1); @@ -174,7 +185,7 @@ srs_error_t SrsDtlsSession::initialize(const SrsRequest& req) return err; } -srs_error_t SrsDtlsSession::handshake(SrsUdpMuxSocket* udp_mux_skt) +srs_error_t SrsDtlsSession::handshake(SrsUdpMuxSocket* skt) { srs_error_t err = srs_success; @@ -186,7 +197,7 @@ srs_error_t SrsDtlsSession::handshake(SrsUdpMuxSocket* udp_mux_skt) int ssl_err = SSL_get_error(dtls, ret); switch(ssl_err) { case SSL_ERROR_NONE: { - if ((err = on_dtls_handshake_done(udp_mux_skt)) != srs_success) { + if ((err = on_dtls_handshake_done(skt)) != srs_success) { return srs_error_wrap(err, "dtls handshake done handle"); } break; @@ -206,7 +217,7 @@ srs_error_t SrsDtlsSession::handshake(SrsUdpMuxSocket* udp_mux_skt) } if (out_bio_len) { - if ((err = udp_mux_skt->sendto(out_bio_data, out_bio_len, 0)) != srs_success) { + if ((err = skt->sendto(out_bio_data, out_bio_len, 0)) != srs_success) { return srs_error_wrap(err, "send dtls packet"); } } @@ -214,7 +225,7 @@ srs_error_t SrsDtlsSession::handshake(SrsUdpMuxSocket* udp_mux_skt) return err; } -srs_error_t SrsDtlsSession::on_dtls(SrsUdpMuxSocket* udp_mux_skt) +srs_error_t SrsDtlsSession::on_dtls(SrsUdpMuxSocket* skt) { srs_error_t err = srs_success; if (BIO_reset(bio_in) != 1) { @@ -224,13 +235,13 @@ srs_error_t SrsDtlsSession::on_dtls(SrsUdpMuxSocket* udp_mux_skt) return srs_error_new(ERROR_OpenSslBIOReset, "BIO_reset"); } - if (BIO_write(bio_in, udp_mux_skt->data(), udp_mux_skt->size()) <= 0) { + if (BIO_write(bio_in, skt->data(), skt->size()) <= 0) { // TODO: 0 or -1 maybe block, use BIO_should_retry to check. return srs_error_new(ERROR_OpenSslBIOWrite, "BIO_write"); } if (! handshake_done) { - err = handshake(udp_mux_skt); + err = handshake(skt); } else { while (BIO_ctrl_pending(bio_in) > 0) { char dtls_read_buf[8092]; @@ -247,7 +258,7 @@ srs_error_t SrsDtlsSession::on_dtls(SrsUdpMuxSocket* udp_mux_skt) return err; } -srs_error_t SrsDtlsSession::on_dtls_handshake_done(SrsUdpMuxSocket* udp_mux_skt) +srs_error_t SrsDtlsSession::on_dtls_handshake_done(SrsUdpMuxSocket* skt) { srs_error_t err = srs_success; srs_trace("dtls handshake done"); @@ -257,7 +268,7 @@ srs_error_t SrsDtlsSession::on_dtls_handshake_done(SrsUdpMuxSocket* udp_mux_skt) return srs_error_wrap(err, "srtp init failed"); } - return rtc_session->on_connection_established(udp_mux_skt); + return rtc_session->on_connection_established(skt); } srs_error_t SrsDtlsSession::on_dtls_application_data(const char* buf, const int nb_buf) @@ -386,6 +397,21 @@ srs_error_t SrsDtlsSession::protect_rtp(char* out_buf, const char* in_buf, int& return srs_error_new(ERROR_RTC_SRTP_PROTECT, "rtp protect failed"); } +srs_error_t SrsDtlsSession::protect_rtp2(void* rtp_hdr, int* len_ptr) +{ + srs_error_t err = srs_success; + + if (!srtp_send) { + return srs_error_new(ERROR_RTC_SRTP_PROTECT, "rtp protect"); + } + + if (srtp_protect(srtp_send, rtp_hdr, len_ptr) != 0) { + return srs_error_new(ERROR_RTC_SRTP_PROTECT, "rtp protect"); + } + + return err; +} + srs_error_t SrsDtlsSession::unprotect_rtp(char* out_buf, const char* in_buf, int& nb_out_buf) { srs_error_t err = srs_success; @@ -434,6 +460,88 @@ srs_error_t SrsDtlsSession::unprotect_rtcp(char* out_buf, const char* in_buf, in return srs_error_new(ERROR_RTC_SRTP_UNPROTECT, "rtcp unprotect failed"); } +SrsRtcPackets::SrsRtcPackets(int nn_cache_max) +{ +#if defined(SRS_DEBUG) + debug_id = 0; +#endif + + use_gso = false; + should_merge_nalus = false; + + nn_rtp_pkts = 0; + nn_audios = nn_extras = 0; + nn_videos = nn_samples = 0; + nn_bytes = nn_rtp_bytes = 0; + nn_padding_bytes = nn_paddings = 0; + nn_dropped = 0; + + cursor = 0; + nn_cache = nn_cache_max; + // TODO: FIXME: We should allocate a smaller cache, and increase it when exhausted. + cache = new SrsRtpPacket2[nn_cache]; +} + +SrsRtcPackets::~SrsRtcPackets() +{ + srs_freepa(cache); + nn_cache = 0; +} + +void SrsRtcPackets::reset(bool gso, bool merge_nalus) +{ + for (int i = 0; i < cursor; i++) { + SrsRtpPacket2* packet = cache + i; + packet->reset(); + } + +#if defined(SRS_DEBUG) + debug_id++; +#endif + + use_gso = gso; + should_merge_nalus = merge_nalus; + + nn_rtp_pkts = 0; + nn_audios = nn_extras = 0; + nn_videos = nn_samples = 0; + nn_bytes = nn_rtp_bytes = 0; + nn_padding_bytes = nn_paddings = 0; + nn_dropped = 0; + + cursor = 0; +} + +SrsRtpPacket2* SrsRtcPackets::fetch() +{ + if (cursor >= nn_cache) { + return NULL; + } + return cache + (cursor++); +} + +SrsRtpPacket2* SrsRtcPackets::back() +{ + srs_assert(cursor > 0); + return cache + cursor - 1; +} + +int SrsRtcPackets::size() +{ + return cursor; +} + +int SrsRtcPackets::capacity() +{ + return nn_cache; +} + +SrsRtpPacket2* SrsRtcPackets::at(int index) +{ + srs_assert(index < cursor); + return cache + index; +} + SrsRtcSenderThread::SrsRtcSenderThread(SrsRtcSession* s, SrsUdpMuxSocket* u, int parent_cid) : sendonly_ukt(NULL) { @@ -442,10 +550,28 @@ SrsRtcSenderThread::SrsRtcSenderThread(SrsRtcSession* s, SrsUdpMuxSocket* u, int rtc_session = s; sendonly_ukt = u->copy_sendonly(); + sender = u->sender(); + + gso = false; + merge_nalus = false; + max_padding = 0; + + audio_timestamp = 0; + audio_sequence = 0; + + video_sequence = 0; + + mw_sleep = 0; + mw_msgs = 0; + realtime = true; + + _srs_config->subscribe(this); } SrsRtcSenderThread::~SrsRtcSenderThread() { + _srs_config->unsubscribe(this); + srs_freep(trd); srs_freep(sendonly_ukt); } @@ -460,9 +586,48 @@ srs_error_t SrsRtcSenderThread::initialize(const uint32_t& vssrc, const uint32_t video_payload_type = v_pt; audio_payload_type = a_pt; + gso = _srs_config->get_rtc_server_gso(); + merge_nalus = _srs_config->get_rtc_server_merge_nalus(); + max_padding = _srs_config->get_rtc_server_padding(); + srs_trace("RTC sender video(ssrc=%d, pt=%d), audio(ssrc=%d, pt=%d), package(gso=%d, merge_nalus=%d), padding=%d", + video_ssrc, video_payload_type, audio_ssrc, audio_payload_type, gso, merge_nalus, max_padding); + return err; } +srs_error_t SrsRtcSenderThread::on_reload_rtc_server() +{ + gso = _srs_config->get_rtc_server_gso(); + merge_nalus = _srs_config->get_rtc_server_merge_nalus(); + max_padding = _srs_config->get_rtc_server_padding(); + + srs_trace("Reload rtc_server gso=%d, merge_nalus=%d, max_padding=%d", gso, merge_nalus, max_padding); + + return srs_success; +} + +srs_error_t SrsRtcSenderThread::on_reload_vhost_play(string vhost) +{ + SrsRequest* req = &rtc_session->request; + + if (req->vhost != vhost) { + return srs_success; + } + + realtime = _srs_config->get_realtime_enabled(req->vhost, true); + mw_msgs = _srs_config->get_mw_msgs(req->vhost, realtime, true); + mw_sleep = _srs_config->get_mw_sleep(req->vhost, true); + + srs_trace("Reload play realtime=%d, mw_msgs=%d, mw_sleep=%d", realtime, mw_msgs, mw_sleep); + + return srs_success; +} + +srs_error_t SrsRtcSenderThread::on_reload_vhost_realtime(string vhost) +{ + return on_reload_vhost_play(vhost); +} + int SrsRtcSenderThread::cid() { return trd->cid(); @@ -492,141 +657,759 @@ void SrsRtcSenderThread::stop_loop() trd->interrupt(); } +void SrsRtcSenderThread::update_sendonly_socket(SrsUdpMuxSocket* skt) +{ + srs_trace("session %s address changed, update %s -> %s", + rtc_session->id().c_str(), sendonly_ukt->get_peer_id().c_str(), skt->get_peer_id().c_str()); + + srs_freep(sendonly_ukt); + sendonly_ukt = skt->copy_sendonly(); + sender = skt->sender(); +} srs_error_t SrsRtcSenderThread::cycle() { srs_error_t err = srs_success; SrsSource* source = NULL; + SrsRequest* req = &rtc_session->request; // TODO: FIXME: Should refactor it, directly use http server as handler. ISrsSourceHandler* handler = _srs_hybrid->srs()->instance(); - if ((err = _srs_sources->fetch_or_create(&rtc_session->request, handler, &source)) != srs_success) { + if ((err = _srs_sources->fetch_or_create(req, handler, &source)) != srs_success) { return srs_error_wrap(err, "rtc fetch source failed"); } - srs_trace("source url=%s, source_id=[%d][%d], encrypt=%d", - rtc_session->request.get_stream_url().c_str(), ::getpid(), source->source_id(), rtc_session->encrypt); - SrsConsumer* consumer = NULL; SrsAutoFree(SrsConsumer, consumer); if ((err = source->create_consumer(NULL, consumer)) != srs_success) { - return srs_error_wrap(err, "rtc create consumer, source url=%s", rtc_session->request.get_stream_url().c_str()); + return srs_error_wrap(err, "rtc create consumer, source url=%s", req->get_stream_url().c_str()); } - // TODO: FIXME: Support reload. - SrsRequest* req = &rtc_session->request; - bool realtime = _srs_config->get_realtime_enabled(req->vhost, true); - srs_utime_t mw_sleep = _srs_config->get_mw_sleep(req->vhost, true); + // For RTC, we enable pass-timestamp mode, ignore the timestamp in queue, never depends on the duration, + // because RTC allows the audio and video has its own timebase, that is the audio timestamp and video timestamp + // maybe not monotonically increase. + // In this mode, we use mw_msgs to set the delay. We never shrink the consumer queue, instead, we dumps the + // messages and drop them if the shared sender queue is full. + consumer->enable_pass_timestamp(); + + realtime = _srs_config->get_realtime_enabled(req->vhost, true); + mw_sleep = _srs_config->get_mw_sleep(req->vhost, true); + mw_msgs = _srs_config->get_mw_msgs(req->vhost, realtime, true); + + // We merged write more messages, so we need larger queue. + if (mw_msgs > 2) { + sender->set_extra_ratio(150); + } else if (mw_msgs > 0) { + sender->set_extra_ratio(80); + } + + srs_trace("RTC source url=%s, source_id=[%d][%d], encrypt=%d, realtime=%d, mw_sleep=%dms, mw_msgs=%d", req->get_stream_url().c_str(), + ::getpid(), source->source_id(), rtc_session->encrypt, realtime, srsu2msi(mw_sleep), mw_msgs); SrsMessageArray msgs(SRS_PERF_MW_MSGS); + SrsRtcPackets pkts(SRS_PERF_RTC_RTP_PACKETS); SrsPithyPrint* pprint = SrsPithyPrint::create_rtc_play(); SrsAutoFree(SrsPithyPrint, pprint); + bool stat_enabled = _srs_config->get_rtc_server_perf_stat(); + SrsStatistic* stat = SrsStatistic::instance(); + while (true) { if ((err = trd->pull()) != srs_success) { return srs_error_wrap(err, "rtc sender thread"); } - pprint->elapse(); - - if (pprint->can_print()) { - // TODO: FIXME: - // Print stat like frame/s, packet/s, loss_packets. - } - #ifdef SRS_PERF_QUEUE_COND_WAIT - if (realtime) { - // for realtime, min required msgs is 0, send when got one+ msgs. - consumer->wait(0, mw_sleep); - } else { - // for no-realtime, got some msgs then send. - consumer->wait(SRS_PERF_MW_MIN_MSGS_FOR_RTC, mw_sleep); - } + // Wait for amount of messages or a duration. + consumer->wait(mw_msgs, mw_sleep); #endif + // Try to read some messages. int msg_count = 0; if ((err = consumer->dump_packets(&msgs, msg_count)) != srs_success) { continue; } - if (msg_count <= 0) { + if (msg_count <= 0) { #ifndef SRS_PERF_QUEUE_COND_WAIT srs_usleep(mw_sleep); #endif - // ignore when nothing got. continue; } - send_and_free_messages(msgs.msgs, msg_count, sendonly_ukt); + // Transmux and send out messages. + pkts.reset(gso, merge_nalus); + + if ((err = send_messages(source, msgs.msgs, msg_count, pkts)) != srs_success) { + srs_warn("send err %s", srs_error_summary(err).c_str()); srs_error_reset(err); + } + + // Do cleanup messages. + for (int i = 0; i < msg_count; i++) { + SrsSharedPtrMessage* msg = msgs.msgs[i]; + srs_freep(msg); + } + + // Stat for performance analysis. + if (!stat_enabled) { + continue; + } + + // Stat the original RAW AV frame, maybe h264+aac. + stat->perf_on_msgs(msg_count); + // Stat the RTC packets, RAW AV frame, maybe h.264+opus. + int nn_rtc_packets = srs_max(pkts.nn_audios, pkts.nn_extras) + pkts.nn_videos; + stat->perf_on_rtc_packets(nn_rtc_packets); + // Stat the RAW RTP packets, which maybe group by GSO. + stat->perf_on_rtp_packets(pkts.size()); + // Stat the RTP packets going into kernel. + stat->perf_on_gso_packets(pkts.nn_rtp_pkts); + // Stat the bytes and paddings. + stat->perf_on_rtc_bytes(pkts.nn_bytes, pkts.nn_rtp_bytes, pkts.nn_padding_bytes); + // Stat the messages and dropped count. + stat->perf_on_dropped(msg_count, nn_rtc_packets, pkts.nn_dropped); + +#if defined(SRS_DEBUG) + srs_trace("RTC PLAY perf, msgs %d/%d, rtp %d, gso %d, %d audios, %d extras, %d videos, %d samples, %d/%d/%d bytes", + msg_count, nn_rtc_packets, pkts.size(), pkts.nn_rtp_pkts, pkts.nn_audios, pkts.nn_extras, pkts.nn_videos, + pkts.nn_samples, pkts.nn_bytes, pkts.nn_rtp_bytes, pkts.nn_padding_bytes); +#endif + + pprint->elapse(); + if (pprint->can_print()) { + // TODO: FIXME: Print stat like frame/s, packet/s, loss_packets. + srs_trace("-> RTC PLAY %d/%d msgs, %d/%d packets, %d audios, %d extras, %d videos, %d samples, %d/%d/%d bytes, %d pad, %d/%d cache", + msg_count, pkts.nn_dropped, pkts.size(), pkts.nn_rtp_pkts, pkts.nn_audios, pkts.nn_extras, pkts.nn_videos, pkts.nn_samples, pkts.nn_bytes, + pkts.nn_rtp_bytes, pkts.nn_padding_bytes, pkts.nn_paddings, pkts.size(), pkts.capacity()); + } } } -void SrsRtcSenderThread::update_sendonly_socket(SrsUdpMuxSocket* ukt) -{ - srs_trace("session %s address changed, update %s -> %s", - rtc_session->id().c_str(), sendonly_ukt->get_peer_id().c_str(), ukt->get_peer_id().c_str()); - - srs_freep(sendonly_ukt); - sendonly_ukt = ukt->copy_sendonly(); -} - -void SrsRtcSenderThread::send_and_free_messages(SrsSharedPtrMessage** msgs, int nb_msgs, SrsUdpMuxSocket* udp_mux_skt) -{ +srs_error_t SrsRtcSenderThread::send_messages( + SrsSource* source, SrsSharedPtrMessage** msgs, int nb_msgs, SrsRtcPackets& packets +) { + srs_error_t err = srs_success; + + // If DTLS is not OK, drop all messages. + if (!rtc_session->dtls_session) { + return err; + } + + // Covert kernel messages to RTP packets. + if ((err = messages_to_packets(source, msgs, nb_msgs, packets)) != srs_success) { + return srs_error_wrap(err, "messages to packets"); + } + +#ifndef SRS_AUTO_OSX + // If enabled GSO, send out some packets in a msghdr. + if (packets.use_gso) { + if ((err = send_packets_gso(packets)) != srs_success) { + return srs_error_wrap(err, "gso send"); + } + return err; + } +#endif + + // By default, we send packets by sendmmsg. + if ((err = send_packets(packets)) != srs_success) { + return srs_error_wrap(err, "raw send"); + } + + return err; +} + +srs_error_t SrsRtcSenderThread::messages_to_packets( + SrsSource* source, SrsSharedPtrMessage** msgs, int nb_msgs, SrsRtcPackets& packets +) { srs_error_t err = srs_success; - vector mhdrs; for (int i = 0; i < nb_msgs; i++) { SrsSharedPtrMessage* msg = msgs[i]; - for (int i = 0; i < (int)msg->rtp_packets.size(); ++i) { - if (!rtc_session->dtls_session) { + // If overflow, drop all messages. + if (sender->overflow()) { + packets.nn_dropped += nb_msgs - i; + return err; + } + + // Update stats. + packets.nn_bytes += msg->size; + + int nn_extra_payloads = msg->nn_extra_payloads(); + packets.nn_extras += nn_extra_payloads; + + int nn_samples = msg->nn_samples(); + packets.nn_samples += nn_samples; + + // For audio, we transcoded AAC to opus in extra payloads. + if (msg->is_audio()) { + packets.nn_audios++; + + for (int i = 0; i < nn_extra_payloads; i++) { + SrsSample* sample = msg->extra_payloads() + i; + if ((err = packet_opus(sample, packets, msg->nn_max_extra_payloads())) != srs_success) { + return srs_error_wrap(err, "opus package"); + } + } + continue; + } + + // For video, we should process all NALUs in samples. + packets.nn_videos++; + + // Well, for each IDR, we append a SPS/PPS before it, which is packaged in STAP-A. + if (msg->has_idr()) { + if ((err = packet_stap_a(source, msg, packets)) != srs_success) { + return srs_error_wrap(err, "packet stap-a"); + } + } + + // If merge Nalus, we pcakges all NALUs(samples) as one NALU, in a RTP or FUA packet. + if (packets.should_merge_nalus && nn_samples > 1) { + if ((err = packet_nalus(msg, packets)) != srs_success) { + return srs_error_wrap(err, "packet stap-a"); + } + continue; + } + + // By default, we package each NALU(sample) to a RTP or FUA packet. + for (int i = 0; i < nn_samples; i++) { + SrsSample* sample = msg->samples() + i; + + // We always ignore bframe here, if config to discard bframe, + // the bframe flag will not be set. + if (sample->bframe) { continue; } - SrsRtpSharedPacket* pkt = msg->rtp_packets[i]; - - if (msg->is_video()) { - pkt->modify_rtp_header_payload_type(video_payload_type); - pkt->modify_rtp_header_ssrc(video_ssrc); - srs_verbose("send video, ssrc=%u, seq=%u, timestamp=%u", video_ssrc, pkt->rtp_header.get_sequence(), pkt->rtp_header.get_timestamp()); - } - - if (msg->is_audio()) { - pkt->modify_rtp_header_payload_type(audio_payload_type); - pkt->modify_rtp_header_ssrc(audio_ssrc); - } - - int length = pkt->size; - char* buf = new char[kRtpPacketSize]; - if (rtc_session->encrypt) { - if ((err = rtc_session->dtls_session->protect_rtp(buf, pkt->payload, length)) != srs_success) { - srs_warn("srtp err %s", srs_error_desc(err).c_str()); srs_freep(err); srs_freepa(buf); - continue; + if (sample->size <= kRtpMaxPayloadSize) { + if ((err = packet_single_nalu(msg, sample, packets)) != srs_success) { + return srs_error_wrap(err, "packet single nalu"); } } else { - memcpy(buf, pkt->payload, length); + if ((err = packet_fu_a(msg, sample, kRtpMaxPayloadSize, packets)) != srs_success) { + return srs_error_wrap(err, "packet fu-a"); + } } - mmsghdr mhdr; - memset(&mhdr, 0, sizeof(mmsghdr)); - mhdr.msg_hdr.msg_name = (sockaddr_in*)udp_mux_skt->peer_addr(); - mhdr.msg_hdr.msg_namelen = udp_mux_skt->peer_addrlen(); - mhdr.msg_hdr.msg_iovlen = 1; - mhdr.msg_hdr.msg_iov = new iovec(); - mhdr.msg_hdr.msg_iov->iov_base = buf; - mhdr.msg_hdr.msg_iov->iov_len = length; - mhdrs.push_back(mhdr); + if (i == nn_samples - 1) { + packets.back()->rtp_header.set_marker(true); + } + } + } + + return err; +} + +srs_error_t SrsRtcSenderThread::send_packets(SrsRtcPackets& packets) +{ + srs_error_t err = srs_success; + + // Cache the encrypt flag. + bool encrypt = rtc_session->encrypt; + + int nn_packets = packets.size(); + for (int i = 0; i < nn_packets; i++) { + SrsRtpPacket2* packet = packets.at(i); + + // Fetch a cached message from queue. + // TODO: FIXME: Maybe encrypt in async, so the state of mhdr maybe not ready. + mmsghdr* mhdr = NULL; + if ((err = sender->fetch(&mhdr)) != srs_success) { + return srs_error_wrap(err, "fetch msghdr"); } - srs_freep(msg); + // For this message, select the first iovec. + iovec* iov = mhdr->msg_hdr.msg_iov; + mhdr->msg_hdr.msg_iovlen = 1; + + if (!iov->iov_base) { + iov->iov_base = new char[kRtpPacketSize]; + } + iov->iov_len = kRtpPacketSize; + + // Marshal packet to bytes in iovec. + if (true) { + SrsBuffer stream((char*)iov->iov_base, iov->iov_len); + if ((err = packet->encode(&stream)) != srs_success) { + return srs_error_wrap(err, "encode packet"); + } + iov->iov_len = stream.pos(); + } + + // Whether encrypt the RTP bytes. + if (encrypt) { + int nn_encrypt = (int)iov->iov_len; + if ((err = rtc_session->dtls_session->protect_rtp2(iov->iov_base, &nn_encrypt)) != srs_success) { + return srs_error_wrap(err, "srtp protect"); + } + iov->iov_len = (size_t)nn_encrypt; + } + + packets.nn_rtp_bytes += (int)iov->iov_len; + + // Set the address and control information. + sockaddr_in* addr = (sockaddr_in*)sendonly_ukt->peer_addr(); + socklen_t addrlen = (socklen_t)sendonly_ukt->peer_addrlen(); + + mhdr->msg_hdr.msg_name = (sockaddr_in*)addr; + mhdr->msg_hdr.msg_namelen = (socklen_t)addrlen; + mhdr->msg_hdr.msg_controllen = 0; + + // When we send out a packet, we commit a RTP packet. + packets.nn_rtp_pkts++; + + if ((err = sender->sendmmsg(mhdr)) != srs_success) { + return srs_error_wrap(err, "send msghdr"); + } } - if ((err = rtc_session->rtc_server->send_and_free_messages(udp_mux_skt->stfd(), mhdrs)) != srs_success) { - srs_warn("sendmsg %d msgs, err %s", mhdrs.size(), srs_error_summary(err).c_str()); - srs_freep(err); + return err; +} + +// TODO: FIXME: We can gather and pad audios, because they have similar size. +srs_error_t SrsRtcSenderThread::send_packets_gso(SrsRtcPackets& packets) +{ + srs_error_t err = srs_success; + + // Cache the encrypt flag. + bool encrypt = rtc_session->encrypt; + + // Previous handler, if has the same size, we can use GSO. + mmsghdr* gso_mhdr = NULL; int gso_size = 0; int gso_encrypt = 0; int gso_cursor = 0; + // GSO, N packets has same length, the final one may not. + bool using_gso = false; bool gso_final = false; + // The message will marshal in iovec. + iovec* iov = NULL; + + int nn_packets = packets.size(); + for (int i = 0; i < nn_packets; i++) { + SrsRtpPacket2* packet = packets.at(i); + int nn_packet = packet->nb_bytes(); + int padding = 0; + + SrsRtpPacket2* next_packet = NULL; + int nn_next_packet = 0; + if (max_padding > 0) { + if (i < nn_packets - 1) { + next_packet = (i < nn_packets - 1)? packets.at(i + 1):NULL; + nn_next_packet = next_packet? next_packet->nb_bytes() : 0; + } + + // Padding the packet to next or GSO size. + if (next_packet) { + if (!using_gso) { + // Padding to the next packet to merge with it. + if (nn_next_packet > nn_packet) { + padding = nn_next_packet - nn_packet; + } + } else { + // Padding to GSO size for next one to merge with us. + if (nn_next_packet < gso_size) { + padding = gso_size - nn_packet; + } + } + + // Reset padding if exceed max. + if (padding > max_padding) { + padding = 0; + } + + if (padding > 0) { +#if defined(SRS_DEBUG) + srs_trace("#%d, Padding %d bytes %d=>%d, packets %d, max_padding %d", packets.debug_id, + padding, nn_packet, nn_packet + padding, nn_packets, max_padding); +#endif + packet->add_padding(padding); + nn_packet += padding; + packets.nn_paddings++; + packets.nn_padding_bytes += padding; + } + } + } + + // Check whether we can use GSO to send it. + if (using_gso && !gso_final) { + gso_final = (gso_size != nn_packet); + } + + if (next_packet) { + // If not GSO, maybe the first fresh packet, we should see whether the next packet is smaller than this one, + // if smaller, we can still enter GSO. + if (!using_gso) { + using_gso = (nn_packet >= nn_next_packet); + } + + // If GSO, but next is bigger than this one, we must enter the final state. + if (using_gso && !gso_final) { + gso_final = (nn_packet < nn_next_packet); + } + } + + // For GSO, reuse mhdr if possible. + mmsghdr* mhdr = gso_mhdr; + if (!mhdr) { + // Fetch a cached message from queue. + // TODO: FIXME: Maybe encrypt in async, so the state of mhdr maybe not ready. + if ((err = sender->fetch(&mhdr)) != srs_success) { + return srs_error_wrap(err, "fetch msghdr"); + } + + // Now, GSO will use this message and size. + gso_mhdr = mhdr; + gso_size = nn_packet; + } + + // For this message, select a new iovec. + if (!iov) { + iov = mhdr->msg_hdr.msg_iov; + } else { + iov++; + } + gso_cursor++; + mhdr->msg_hdr.msg_iovlen = gso_cursor; + + if (gso_cursor > SRS_PERF_RTC_GSO_IOVS && !iov->iov_base) { + iov->iov_base = new char[kRtpPacketSize]; + } + iov->iov_len = kRtpPacketSize; + + // Marshal packet to bytes in iovec. + if (true) { + SrsBuffer stream((char*)iov->iov_base, iov->iov_len); + if ((err = packet->encode(&stream)) != srs_success) { + return srs_error_wrap(err, "encode packet"); + } + iov->iov_len = stream.pos(); + } + + // Whether encrypt the RTP bytes. + if (encrypt) { + int nn_encrypt = (int)iov->iov_len; + if ((err = rtc_session->dtls_session->protect_rtp2(iov->iov_base, &nn_encrypt)) != srs_success) { + return srs_error_wrap(err, "srtp protect"); + } + iov->iov_len = (size_t)nn_encrypt; + } + + packets.nn_rtp_bytes += (int)iov->iov_len; + + // If GSO, they must has same size, except the final one. + if (using_gso && !gso_final && gso_encrypt && gso_encrypt != (int)iov->iov_len) { + return srs_error_new(ERROR_RTC_RTP_MUXER, "GSO size=%d/%d, encrypt=%d/%d", gso_size, nn_packet, gso_encrypt, iov->iov_len); + } + + if (using_gso && !gso_final) { + gso_encrypt = iov->iov_len; + } + + // If exceed the max GSO size, set to final. + if (using_gso && gso_cursor + 1 >= SRS_PERF_RTC_GSO_MAX) { + gso_final = true; + } + + // For last message, or final gso, or determined not using GSO, send it now. + bool do_send = (i == nn_packets - 1 || gso_final || !using_gso); + +#if defined(SRS_DEBUG) + bool is_video = packet->rtp_header.get_payload_type() == video_payload_type; + srs_trace("#%d, Packet %s SSRC=%d, SN=%d, %d/%d bytes", packets.debug_id, is_video? "Video":"Audio", + packet->rtp_header.get_ssrc(), packet->rtp_header.get_sequence(), nn_packet - padding, padding); + if (do_send) { + for (int j = 0; j < (int)mhdr->msg_hdr.msg_iovlen; j++) { + iovec* iov = mhdr->msg_hdr.msg_iov + j; + srs_trace("#%d, %s #%d/%d/%d, %d/%d bytes, size %d/%d", packets.debug_id, (using_gso? "GSO":"RAW"), j, + gso_cursor + 1, mhdr->msg_hdr.msg_iovlen, iov->iov_len, padding, gso_size, gso_encrypt); + } + } +#endif + + if (do_send) { + // Set the address and control information. + sockaddr_in* addr = (sockaddr_in*)sendonly_ukt->peer_addr(); + socklen_t addrlen = (socklen_t)sendonly_ukt->peer_addrlen(); + + mhdr->msg_hdr.msg_name = (sockaddr_in*)addr; + mhdr->msg_hdr.msg_namelen = (socklen_t)addrlen; + mhdr->msg_hdr.msg_controllen = 0; + +#ifndef SRS_AUTO_OSX + if (using_gso) { + mhdr->msg_hdr.msg_controllen = CMSG_SPACE(sizeof(uint16_t)); + if (!mhdr->msg_hdr.msg_control) { + mhdr->msg_hdr.msg_control = new char[mhdr->msg_hdr.msg_controllen]; + } + + cmsghdr* cm = CMSG_FIRSTHDR(&mhdr->msg_hdr); + cm->cmsg_level = SOL_UDP; + cm->cmsg_type = UDP_SEGMENT; + cm->cmsg_len = CMSG_LEN(sizeof(uint16_t)); + *((uint16_t*)CMSG_DATA(cm)) = gso_encrypt; + } +#endif + + // When we send out a packet, we commit a RTP packet. + packets.nn_rtp_pkts++; + + if ((err = sender->sendmmsg(mhdr)) != srs_success) { + return srs_error_wrap(err, "send msghdr"); + } + + // Reset the GSO flag. + gso_mhdr = NULL; gso_size = 0; gso_encrypt = 0; gso_cursor = 0; + using_gso = gso_final = false; iov = NULL; + } } + +#if defined(SRS_DEBUG) + srs_trace("#%d, RTC PLAY summary, rtp %d/%d, videos %d/%d, audios %d/%d, pad %d/%d/%d", packets.debug_id, packets.size(), + packets.nn_rtp_pkts, packets.nn_videos, packets.nn_samples, packets.nn_audios, packets.nn_extras, packets.nn_paddings, + packets.nn_padding_bytes, packets.nn_rtp_bytes); +#endif + + return err; +} + +srs_error_t SrsRtcSenderThread::packet_nalus(SrsSharedPtrMessage* msg, SrsRtcPackets& packets) +{ + srs_error_t err = srs_success; + + SrsRtpRawNALUs* raw = new SrsRtpRawNALUs(); + + for (int i = 0; i < msg->nn_samples(); i++) { + SrsSample* sample = msg->samples() + i; + + // We always ignore bframe here, if config to discard bframe, + // the bframe flag will not be set. + if (sample->bframe) { + continue; + } + + raw->push_back(sample->copy()); + } + + // Ignore empty. + int nn_bytes = raw->nb_bytes(); + if (nn_bytes <= 0) { + srs_freep(raw); + return err; + } + + if (nn_bytes < kRtpMaxPayloadSize) { + // Package NALUs in a single RTP packet. + SrsRtpPacket2* packet = packets.fetch(); + if (!packet) { + srs_freep(raw); + return srs_error_new(ERROR_RTC_RTP_MUXER, "cache empty"); + } + + packet->rtp_header.set_timestamp(msg->timestamp * 90); + packet->rtp_header.set_sequence(video_sequence++); + packet->rtp_header.set_ssrc(video_ssrc); + packet->rtp_header.set_payload_type(video_payload_type); + + packet->payload = raw; + } else { + // We must free it, should never use RTP packets to free it, + // because more than one RTP packet will refer to it. + SrsAutoFree(SrsRtpRawNALUs, raw); + + // Package NALUs in FU-A RTP packets. + int fu_payload_size = kRtpMaxPayloadSize; + + // The first byte is store in FU-A header. + uint8_t header = raw->skip_first_byte(); + uint8_t nal_type = header & kNalTypeMask; + int nb_left = nn_bytes - 1; + + int num_of_packet = 1 + (nn_bytes - 1) / fu_payload_size; + for (int i = 0; i < num_of_packet; ++i) { + int packet_size = srs_min(nb_left, fu_payload_size); + + SrsRtpPacket2* packet = packets.fetch(); + if (!packet) { + srs_freep(raw); + return srs_error_new(ERROR_RTC_RTP_MUXER, "cache empty"); + } + + packet->rtp_header.set_timestamp(msg->timestamp * 90); + packet->rtp_header.set_sequence(video_sequence++); + packet->rtp_header.set_ssrc(video_ssrc); + packet->rtp_header.set_payload_type(video_payload_type); + + SrsRtpFUAPayload* fua = new SrsRtpFUAPayload(); + packet->payload = fua; + + fua->nri = (SrsAvcNaluType)header; + fua->nalu_type = (SrsAvcNaluType)nal_type; + fua->start = bool(i == 0); + fua->end = bool(i == num_of_packet - 1); + + if ((err = raw->read_samples(fua->nalus, packet_size)) != srs_success) { + return srs_error_wrap(err, "read samples %d bytes, left %d, total %d", packet_size, nb_left, nn_bytes); + } + + nb_left -= packet_size; + } + } + + if (packets.size() > 0) { + packets.back()->rtp_header.set_marker(true); + } + + return err; +} + +srs_error_t SrsRtcSenderThread::packet_opus(SrsSample* sample, SrsRtcPackets& packets, int nn_max_payload) +{ + srs_error_t err = srs_success; + + SrsRtpPacket2* packet = packets.fetch(); + if (!packet) { + return srs_error_new(ERROR_RTC_RTP_MUXER, "cache empty"); + } + packet->rtp_header.set_marker(true); + packet->rtp_header.set_timestamp(audio_timestamp); + packet->rtp_header.set_sequence(audio_sequence++); + packet->rtp_header.set_ssrc(audio_ssrc); + packet->rtp_header.set_payload_type(audio_payload_type); + + SrsRtpRawPayload* raw = packet->reuse_raw(); + raw->payload = sample->bytes; + raw->nn_payload = sample->size; + + if (max_padding > 0) { + if (sample->size < nn_max_payload && nn_max_payload - sample->size < max_padding) { + int padding = nn_max_payload - sample->size; + packet->set_padding(padding); + +#if defined(SRS_DEBUG) + srs_trace("#%d, Fast Padding %d bytes %d=>%d, SN=%d, max_payload %d, max_padding %d", packets.debug_id, + padding, sample->size, sample->size + padding, packet->rtp_header.get_sequence(), nn_max_payload, max_padding); +#endif + } + } + + // TODO: FIXME: Why 960? Need Refactoring? + audio_timestamp += 960; + + return err; +} + +srs_error_t SrsRtcSenderThread::packet_fu_a(SrsSharedPtrMessage* msg, SrsSample* sample, int fu_payload_size, SrsRtcPackets& packets) +{ + srs_error_t err = srs_success; + + char* p = sample->bytes + 1; + int nb_left = sample->size - 1; + uint8_t header = sample->bytes[0]; + uint8_t nal_type = header & kNalTypeMask; + + int num_of_packet = 1 + (sample->size - 1) / fu_payload_size; + for (int i = 0; i < num_of_packet; ++i) { + int packet_size = srs_min(nb_left, fu_payload_size); + + SrsRtpPacket2* packet = packets.fetch(); + if (!packet) { + return srs_error_new(ERROR_RTC_RTP_MUXER, "cache empty"); + } + + packet->rtp_header.set_timestamp(msg->timestamp * 90); + packet->rtp_header.set_sequence(video_sequence++); + packet->rtp_header.set_ssrc(video_ssrc); + packet->rtp_header.set_payload_type(video_payload_type); + + SrsRtpFUAPayload2* fua = packet->reuse_fua(); + + fua->nri = (SrsAvcNaluType)header; + fua->nalu_type = (SrsAvcNaluType)nal_type; + fua->start = bool(i == 0); + fua->end = bool(i == num_of_packet - 1); + + fua->payload = p; + fua->size = packet_size; + + p += packet_size; + nb_left -= packet_size; + } + + return err; +} + +// Single NAL Unit Packet @see https://tools.ietf.org/html/rfc6184#section-5.6 +srs_error_t SrsRtcSenderThread::packet_single_nalu(SrsSharedPtrMessage* msg, SrsSample* sample, SrsRtcPackets& packets) +{ + srs_error_t err = srs_success; + + SrsRtpPacket2* packet = packets.fetch(); + if (!packet) { + return srs_error_new(ERROR_RTC_RTP_MUXER, "cache empty"); + } + packet->rtp_header.set_timestamp(msg->timestamp * 90); + packet->rtp_header.set_sequence(video_sequence++); + packet->rtp_header.set_ssrc(video_ssrc); + packet->rtp_header.set_payload_type(video_payload_type); + + SrsRtpRawPayload* raw = packet->reuse_raw(); + raw->payload = sample->bytes; + raw->nn_payload = sample->size; + + return err; +} + +srs_error_t SrsRtcSenderThread::packet_stap_a(SrsSource* source, SrsSharedPtrMessage* msg, SrsRtcPackets& packets) +{ + srs_error_t err = srs_success; + + SrsMetaCache* meta = source->cached_meta(); + if (!meta) { + return err; + } + + SrsFormat* format = meta->vsh_format(); + if (!format || !format->vcodec) { + return err; + } + + const vector& sps = format->vcodec->sequenceParameterSetNALUnit; + const vector& pps = format->vcodec->pictureParameterSetNALUnit; + if (sps.empty() || pps.empty()) { + return srs_error_new(ERROR_RTC_RTP_MUXER, "sps/pps empty"); + } + + SrsRtpPacket2* packet = packets.fetch(); + if (!packet) { + return srs_error_new(ERROR_RTC_RTP_MUXER, "cache empty"); + } + packet->rtp_header.set_marker(false); + packet->rtp_header.set_timestamp(msg->timestamp * 90); + packet->rtp_header.set_sequence(video_sequence++); + packet->rtp_header.set_ssrc(video_ssrc); + packet->rtp_header.set_payload_type(video_payload_type); + + SrsRtpSTAPPayload* stap = new SrsRtpSTAPPayload(); + packet->payload = stap; + + uint8_t header = sps[0]; + stap->nri = (SrsAvcNaluType)header; + + if (true) { + SrsSample* sample = new SrsSample(); + sample->bytes = (char*)&sps[0]; + sample->size = (int)sps.size(); + stap->nalus.push_back(sample); + } + + if (true) { + SrsSample* sample = new SrsSample(); + sample->bytes = (char*)&pps[0]; + sample->size = (int)pps.size(); + stap->nalus.push_back(sample); + } + + return err; } SrsRtcSession::SrsRtcSession(SrsRtcServer* rtc_svr, const SrsRequest& req, const std::string& un, int context_id) @@ -671,12 +1454,12 @@ void SrsRtcSession::switch_to_context() _srs_context->set_id(cid); } -srs_error_t SrsRtcSession::on_stun(SrsUdpMuxSocket* udp_mux_skt, SrsStunPacket* stun_req) +srs_error_t SrsRtcSession::on_stun(SrsUdpMuxSocket* skt, SrsStunPacket* stun_req) { srs_error_t err = srs_success; if (stun_req->is_binding_request()) { - if ((err = on_binding_request(udp_mux_skt, stun_req)) != srs_success) { + if ((err = on_binding_request(skt, stun_req)) != srs_success) { return srs_error_wrap(err, "stun binding request failed"); } @@ -685,8 +1468,8 @@ srs_error_t SrsRtcSession::on_stun(SrsUdpMuxSocket* udp_mux_skt, SrsStunPacket* if (strd && strd->sendonly_ukt) { // We are running in the ice-lite(server) mode. If client have multi network interface, // we only choose one candidate pair which is determined by client. - if (stun_req->get_use_candidate() && strd->sendonly_ukt->get_peer_id() != udp_mux_skt->get_peer_id()) { - strd->update_sendonly_socket(udp_mux_skt); + if (stun_req->get_use_candidate() && strd->sendonly_ukt->get_peer_id() != skt->get_peer_id()) { + strd->update_sendonly_socket(skt); } } } @@ -716,7 +1499,7 @@ srs_error_t SrsRtcSession::check_source() #define be32toh ntohl #endif -srs_error_t SrsRtcSession::on_binding_request(SrsUdpMuxSocket* udp_mux_skt, SrsStunPacket* stun_req) +srs_error_t SrsRtcSession::on_binding_request(SrsUdpMuxSocket* skt, SrsStunPacket* stun_req) { srs_error_t err = srs_success; @@ -728,7 +1511,7 @@ srs_error_t SrsRtcSession::on_binding_request(SrsUdpMuxSocket* udp_mux_skt, SrsS } SrsStunPacket stun_binding_response; - char buf[1460]; + char buf[kRtpPacketSize]; SrsBuffer* stream = new SrsBuffer(buf, sizeof(buf)); SrsAutoFree(SrsBuffer, stream); @@ -737,28 +1520,28 @@ srs_error_t SrsRtcSession::on_binding_request(SrsUdpMuxSocket* udp_mux_skt, SrsS stun_binding_response.set_remote_ufrag(stun_req->get_local_ufrag()); stun_binding_response.set_transcation_id(stun_req->get_transcation_id()); // FIXME: inet_addr is deprecated, IPV6 support - stun_binding_response.set_mapped_address(be32toh(inet_addr(udp_mux_skt->get_peer_ip().c_str()))); - stun_binding_response.set_mapped_port(udp_mux_skt->get_peer_port()); + stun_binding_response.set_mapped_address(be32toh(inet_addr(skt->get_peer_ip().c_str()))); + stun_binding_response.set_mapped_port(skt->get_peer_port()); if ((err = stun_binding_response.encode(get_local_sdp()->get_ice_pwd(), stream)) != srs_success) { return srs_error_wrap(err, "stun binding response encode failed"); } - if ((err = udp_mux_skt->sendto(stream->data(), stream->pos(), 0)) != srs_success) { + if ((err = skt->sendto(stream->data(), stream->pos(), 0)) != srs_success) { return srs_error_wrap(err, "stun binding response send failed"); } if (get_session_state() == WAITING_STUN) { set_session_state(DOING_DTLS_HANDSHAKE); - peer_id = udp_mux_skt->get_peer_id(); + peer_id = skt->get_peer_id(); rtc_server->insert_into_id_sessions(peer_id, this); } return err; } -srs_error_t SrsRtcSession::on_rtcp_feedback(char* buf, int nb_buf, SrsUdpMuxSocket* udp_mux_skt) +srs_error_t SrsRtcSession::on_rtcp_feedback(char* buf, int nb_buf, SrsUdpMuxSocket* skt) { srs_error_t err = srs_success; @@ -843,14 +1626,14 @@ srs_error_t SrsRtcSession::on_rtcp_feedback(char* buf, int nb_buf, SrsUdpMuxSock srs_verbose("resend pkt sequence=%u", resend_pkts[i]->rtp_header.get_sequence()); dtls_session->protect_rtp(protected_buf, resend_pkts[i]->payload, nb_protected_buf); - udp_mux_skt->sendto(protected_buf, nb_protected_buf, 0); + skt->sendto(protected_buf, nb_protected_buf, 0); } } return err; } -srs_error_t SrsRtcSession::on_rtcp_ps_feedback(char* buf, int nb_buf, SrsUdpMuxSocket* udp_mux_skt) +srs_error_t SrsRtcSession::on_rtcp_ps_feedback(char* buf, int nb_buf, SrsUdpMuxSocket* skt) { srs_error_t err = srs_success; @@ -897,7 +1680,7 @@ srs_error_t SrsRtcSession::on_rtcp_ps_feedback(char* buf, int nb_buf, SrsUdpMuxS return err; } -srs_error_t SrsRtcSession::on_rtcp_receiver_report(char* buf, int nb_buf, SrsUdpMuxSocket* udp_mux_skt) +srs_error_t SrsRtcSession::on_rtcp_receiver_report(char* buf, int nb_buf, SrsUdpMuxSocket* skt) { srs_error_t err = srs_success; @@ -966,18 +1749,18 @@ block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ return err; } -srs_error_t SrsRtcSession::on_connection_established(SrsUdpMuxSocket* udp_mux_skt) +srs_error_t SrsRtcSession::on_connection_established(SrsUdpMuxSocket* skt) { - srs_trace("rtc session=%s, timeout=%dms connection established", id().c_str(), srsu2msi(sessionStunTimeout)); - return start_play(udp_mux_skt); + srs_trace("rtc session=%s, to=%dms connection established", id().c_str(), srsu2msi(sessionStunTimeout)); + return start_play(skt); } -srs_error_t SrsRtcSession::start_play(SrsUdpMuxSocket* udp_mux_skt) +srs_error_t SrsRtcSession::start_play(SrsUdpMuxSocket* skt) { srs_error_t err = srs_success; srs_freep(strd); - strd = new SrsRtcSenderThread(this, udp_mux_skt, _srs_context->get_id()); + strd = new SrsRtcSenderThread(this, skt, _srs_context->get_id()); uint32_t video_ssrc = 0; uint32_t audio_ssrc = 0; @@ -1010,12 +1793,12 @@ bool SrsRtcSession::is_stun_timeout() return last_stun_time + sessionStunTimeout < srs_get_system_time(); } -srs_error_t SrsRtcSession::on_dtls(SrsUdpMuxSocket* udp_mux_skt) +srs_error_t SrsRtcSession::on_dtls(SrsUdpMuxSocket* skt) { - return dtls_session->on_dtls(udp_mux_skt); + return dtls_session->on_dtls(skt); } -srs_error_t SrsRtcSession::on_rtcp(SrsUdpMuxSocket* udp_mux_skt) +srs_error_t SrsRtcSession::on_rtcp(SrsUdpMuxSocket* skt) { srs_error_t err = srs_success; @@ -1023,9 +1806,9 @@ srs_error_t SrsRtcSession::on_rtcp(SrsUdpMuxSocket* udp_mux_skt) return srs_error_new(ERROR_RTC_RTCP, "recv unexpect rtp packet before dtls done"); } - char unprotected_buf[1460]; - int nb_unprotected_buf = udp_mux_skt->size(); - if ((err = dtls_session->unprotect_rtcp(unprotected_buf, udp_mux_skt->data(), nb_unprotected_buf)) != srs_success) { + char unprotected_buf[kRtpPacketSize]; + int nb_unprotected_buf = skt->size(); + if ((err = dtls_session->unprotect_rtcp(unprotected_buf, skt->data(), nb_unprotected_buf)) != srs_success) { return srs_error_wrap(err, "rtcp unprotect failed"); } @@ -1048,7 +1831,7 @@ srs_error_t SrsRtcSession::on_rtcp(SrsUdpMuxSocket* udp_mux_skt) break; } case kRR: { - err = on_rtcp_receiver_report(ph, length, udp_mux_skt); + err = on_rtcp_receiver_report(ph, length, skt); break; } case kSDES: { @@ -1061,11 +1844,11 @@ srs_error_t SrsRtcSession::on_rtcp(SrsUdpMuxSocket* udp_mux_skt) break; } case kRtpFb: { - err = on_rtcp_feedback(ph, length, udp_mux_skt); + err = on_rtcp_feedback(ph, length, skt); break; } case kPsFb: { - err = on_rtcp_ps_feedback(ph, length, udp_mux_skt); + err = on_rtcp_ps_feedback(ph, length, skt); break; } default:{ @@ -1085,27 +1868,311 @@ srs_error_t SrsRtcSession::on_rtcp(SrsUdpMuxSocket* udp_mux_skt) return err; } -SrsRtcServer::SrsRtcServer() +SrsUdpMuxSender::SrsUdpMuxSender(SrsRtcServer* s) { - listener = NULL; - timer = new SrsHourGlass(this, 1 * SRS_UTIME_SECONDS); + lfd = NULL; + server = s; - mmstfd = NULL; waiting_msgs = false; cond = srs_cond_new(); trd = new SrsDummyCoroutine(); + + cache_pos = 0; + max_sendmmsg = 0; + queue_length = 0; + extra_ratio = 0; + extra_queue = 0; + gso = false; + nn_senders = 0; + + _srs_config->subscribe(this); } -SrsRtcServer::~SrsRtcServer() +SrsUdpMuxSender::~SrsUdpMuxSender() { - srs_freep(listener); - srs_freep(timer); + _srs_config->unsubscribe(this); srs_freep(trd); srs_cond_destroy(cond); - free_messages(mmhdrs); - mmhdrs.clear(); + free_mhdrs(hotspot); + hotspot.clear(); + + free_mhdrs(cache); + cache.clear(); +} + +srs_error_t SrsUdpMuxSender::initialize(srs_netfd_t fd, int senders) +{ + srs_error_t err = srs_success; + + lfd = fd; + + srs_freep(trd); + trd = new SrsSTCoroutine("udp", this); + if ((err = trd->start()) != srs_success) { + return srs_error_wrap(err, "start coroutine"); + } + + max_sendmmsg = _srs_config->get_rtc_server_sendmmsg(); + gso = _srs_config->get_rtc_server_gso(); + queue_length = srs_max(128, _srs_config->get_rtc_server_queue_length()); + nn_senders = senders; + + // For no GSO, we need larger queue. + if (!gso) { + queue_length *= 2; + } + + srs_trace("RTC sender #%d init ok, max_sendmmsg=%d, gso=%d, queue_max=%dx%d, extra_ratio=%d/%d", srs_netfd_fileno(fd), + max_sendmmsg, gso, queue_length, nn_senders, extra_ratio, extra_queue); + + return err; +} + +void SrsUdpMuxSender::free_mhdrs(std::vector& mhdrs) +{ + int nn_mhdrs = (int)mhdrs.size(); + for (int i = 0; i < nn_mhdrs; i++) { + // @see https://linux.die.net/man/2/sendmmsg + // @see https://linux.die.net/man/2/sendmsg + mmsghdr* hdr = &mhdrs[i]; + + // Free control for GSO. + char* msg_control = (char*)hdr->msg_hdr.msg_control; + srs_freepa(msg_control); + + // Free iovec. + for (int j = SRS_PERF_RTC_GSO_MAX - 1; j >= 0 ; j--) { + iovec* iov = hdr->msg_hdr.msg_iov + j; + char* data = (char*)iov->iov_base; + srs_freepa(data); + srs_freepa(iov); + } + } + mhdrs.clear(); +} + +srs_error_t SrsUdpMuxSender::fetch(mmsghdr** pphdr) +{ + // TODO: FIXME: Maybe need to shrink? + if (cache_pos >= (int)cache.size()) { + // @see https://linux.die.net/man/2/sendmmsg + // @see https://linux.die.net/man/2/sendmsg + mmsghdr mhdr; + + mhdr.msg_len = 0; + mhdr.msg_hdr.msg_flags = 0; + mhdr.msg_hdr.msg_control = NULL; + + mhdr.msg_hdr.msg_iovlen = SRS_PERF_RTC_GSO_MAX; + mhdr.msg_hdr.msg_iov = new iovec[mhdr.msg_hdr.msg_iovlen]; + memset((void*)mhdr.msg_hdr.msg_iov, 0, sizeof(iovec) * mhdr.msg_hdr.msg_iovlen); + + for (int i = 0; i < SRS_PERF_RTC_GSO_IOVS; i++) { + iovec* p = mhdr.msg_hdr.msg_iov + i; + p->iov_base = new char[kRtpPacketSize]; + } + + cache.push_back(mhdr); + } + + *pphdr = &cache[cache_pos++]; + return srs_success; +} + +bool SrsUdpMuxSender::overflow() +{ + return cache_pos > queue_length + extra_queue; +} + +void SrsUdpMuxSender::set_extra_ratio(int r) +{ + // We use the larger extra ratio, because all vhosts shares the senders. + if (extra_ratio > r) { + return; + } + + extra_ratio = r; + extra_queue = queue_length * r / 100; + + srs_trace("RTC sender #%d extra queue, max_sendmmsg=%d, gso=%d, queue_max=%dx%d, extra_ratio=%d/%d, cache=%d/%d/%d", srs_netfd_fileno(lfd), + max_sendmmsg, gso, queue_length, nn_senders, extra_ratio, extra_queue, cache_pos, (int)cache.size(), (int)hotspot.size()); +} + +srs_error_t SrsUdpMuxSender::sendmmsg(mmsghdr* hdr) +{ + if (waiting_msgs) { + waiting_msgs = false; + srs_cond_signal(cond); + } + + return srs_success; +} + +srs_error_t SrsUdpMuxSender::cycle() +{ + srs_error_t err = srs_success; + + uint64_t nn_msgs = 0; uint64_t nn_msgs_last = 0; int nn_msgs_max = 0; + uint64_t nn_bytes = 0; int nn_bytes_max = 0; + uint64_t nn_gso_msgs = 0; uint64_t nn_gso_iovs = 0; int nn_gso_msgs_max = 0; int nn_gso_iovs_max = 0; + int nn_loop = 0; int nn_wait = 0; + srs_utime_t time_last = srs_get_system_time(); + + bool stat_enabled = _srs_config->get_rtc_server_perf_stat(); + SrsStatistic* stat = SrsStatistic::instance(); + + SrsPithyPrint* pprint = SrsPithyPrint::create_rtc_send(srs_netfd_fileno(lfd)); + SrsAutoFree(SrsPithyPrint, pprint); + + while (true) { + if ((err = trd->pull()) != srs_success) { + return err; + } + + nn_loop++; + + int pos = cache_pos; + int gso_iovs = 0; + if (pos <= 0) { + waiting_msgs = true; + nn_wait++; + srs_cond_wait(cond); + continue; + } + + // We are working on hotspot now. + cache.swap(hotspot); + cache_pos = 0; + + int gso_pos = 0; + int nn_writen = 0; + if (pos > 0) { + // Send out all messages. + // @see https://linux.die.net/man/2/sendmmsg + // @see https://linux.die.net/man/2/sendmsg + mmsghdr* p = &hotspot[0]; mmsghdr* end = p + pos; + for (p = &hotspot[0]; p < end; p += max_sendmmsg) { + int vlen = (int)(end - p); + vlen = srs_min(max_sendmmsg, vlen); + + int r0 = srs_sendmmsg(lfd, p, (unsigned int)vlen, 0, SRS_UTIME_NO_TIMEOUT); + if (r0 != vlen) { + srs_warn("sendmmsg %d msgs, %d done", vlen, r0); + } + + if (stat_enabled) { + stat->perf_on_sendmmsg_packets(vlen); + } + } + + // Collect informations for GSO. + if (stat_enabled) { + // Stat the messages, iovs and bytes. + // @see https://linux.die.net/man/2/sendmmsg + // @see https://linux.die.net/man/2/sendmsg + for (int i = 0; i < pos; i++) { + mmsghdr* mhdr = &hotspot[i]; + + nn_writen += (int)mhdr->msg_len; + + int real_iovs = mhdr->msg_hdr.msg_iovlen; + gso_pos++; nn_gso_msgs++; nn_gso_iovs += real_iovs; + gso_iovs += real_iovs; + } + } + } + + if (!stat_enabled) { + continue; + } + + // Increase total messages. + nn_msgs += pos + gso_iovs; + nn_msgs_max = srs_max(pos, nn_msgs_max); + nn_bytes += nn_writen; + nn_bytes_max = srs_max(nn_bytes_max, nn_writen); + nn_gso_msgs_max = srs_max(gso_pos, nn_gso_msgs_max); + nn_gso_iovs_max = srs_max(gso_iovs, nn_gso_iovs_max); + + pprint->elapse(); + if (pprint->can_print()) { + // TODO: FIXME: Extract a PPS calculator. + int pps_average = 0; int pps_last = 0; + if (true) { + if (srs_get_system_time() > srs_get_system_startup_time()) { + pps_average = (int)(nn_msgs * SRS_UTIME_SECONDS / (srs_get_system_time() - srs_get_system_startup_time())); + } + if (srs_get_system_time() > time_last) { + pps_last = (int)((nn_msgs - nn_msgs_last) * SRS_UTIME_SECONDS / (srs_get_system_time() - time_last)); + } + } + + string pps_unit = ""; + if (pps_last > 10000 || pps_average > 10000) { + pps_unit = "(w)"; pps_last /= 10000; pps_average /= 10000; + } else if (pps_last > 1000 || pps_average > 1000) { + pps_unit = "(k)"; pps_last /= 1000; pps_average /= 1000; + } + + int nn_cache = 0; + int nn_hotspot_size = (int)hotspot.size(); + for (int i = 0; i < nn_hotspot_size; i++) { + mmsghdr* hdr = &hotspot[i]; + nn_cache += hdr->msg_hdr.msg_iovlen; + } + + srs_trace("-> RTC SEND #%d, sessions %d, udp %d/%d/%" PRId64 ", gso %d/%d/%" PRId64 ", iovs %d/%d/%" PRId64 ", pps %d/%d%s, cache %d/%d, bytes %d/%" PRId64, + srs_netfd_fileno(lfd), (int)server->nn_sessions(), pos, nn_msgs_max, nn_msgs, gso_pos, nn_gso_msgs_max, nn_gso_msgs, gso_iovs, + nn_gso_iovs_max, nn_gso_iovs, pps_average, pps_last, pps_unit.c_str(), (int)hotspot.size(), nn_cache, nn_bytes_max, nn_bytes); + nn_msgs_last = nn_msgs; time_last = srs_get_system_time(); + nn_loop = nn_wait = nn_msgs_max = 0; + nn_gso_msgs_max = 0; nn_gso_iovs_max = 0; + nn_bytes_max = 0; + } + } + + return err; +} + +srs_error_t SrsUdpMuxSender::on_reload_rtc_server() +{ + if (true) { + int v = _srs_config->get_rtc_server_sendmmsg(); + if (max_sendmmsg != v) { + srs_trace("Reload max_sendmmsg %d=>%d", max_sendmmsg, v); + max_sendmmsg = v; + } + } + + return srs_success; +} + +SrsRtcServer::SrsRtcServer() +{ + timer = new SrsHourGlass(this, 1 * SRS_UTIME_SECONDS); +} + +SrsRtcServer::~SrsRtcServer() +{ + srs_freep(timer); + + if (true) { + vector::iterator it; + for (it = listeners.begin(); it != listeners.end(); ++it) { + SrsUdpMuxListener* listener = *it; + srs_freep(listener); + } + } + + if (true) { + vector::iterator it; + for (it = senders.begin(); it != senders.end(); ++it) { + SrsUdpMuxSender* sender = *it; + srs_freep(sender); + } + } } srs_error_t SrsRtcServer::initialize() @@ -1120,11 +2187,7 @@ srs_error_t SrsRtcServer::initialize() return srs_error_wrap(err, "start timer"); } - srs_freep(trd); - trd = new SrsSTCoroutine("udp", this); - if ((err = trd->start()) != srs_success) { - return srs_error_wrap(err, "start coroutine"); - } + srs_trace("RTC server init ok"); return err; } @@ -1143,27 +2206,38 @@ srs_error_t SrsRtcServer::listen_udp() } string ip = srs_any_address_for_listener(); + srs_assert(listeners.empty()); - srs_freep(listener); - listener = new SrsUdpMuxListener(this, ip, port); + int nn_listeners = _srs_config->get_rtc_server_reuseport(); + for (int i = 0; i < nn_listeners; i++) { + SrsUdpMuxSender* sender = new SrsUdpMuxSender(this); + SrsUdpMuxListener* listener = new SrsUdpMuxListener(this, sender, ip, port); - if ((err = listener->listen()) != srs_success) { - return srs_error_wrap(err, "listen %s:%d", ip.c_str(), port); + if ((err = listener->listen()) != srs_success) { + srs_freep(listener); + return srs_error_wrap(err, "listen %s:%d", ip.c_str(), port); + } + + if ((err = sender->initialize(listener->stfd(), nn_listeners)) != srs_success) { + return srs_error_wrap(err, "init sender"); + } + + srs_trace("rtc listen at udp://%s:%d, fd=%d", ip.c_str(), port, listener->fd()); + listeners.push_back(listener); + senders.push_back(sender); } - srs_trace("rtc listen at udp://%s:%d, fd=%d", ip.c_str(), port, listener->fd()); - return err; } -srs_error_t SrsRtcServer::on_udp_packet(SrsUdpMuxSocket* udp_mux_skt) +srs_error_t SrsRtcServer::on_udp_packet(SrsUdpMuxSocket* skt) { - if (is_stun(reinterpret_cast(udp_mux_skt->data()), udp_mux_skt->size())) { - return on_stun(udp_mux_skt); - } else if (is_dtls(reinterpret_cast(udp_mux_skt->data()), udp_mux_skt->size())) { - return on_dtls(udp_mux_skt); - } else if (is_rtp_or_rtcp(reinterpret_cast(udp_mux_skt->data()), udp_mux_skt->size())) { - return on_rtp_or_rtcp(udp_mux_skt); + if (is_stun(reinterpret_cast(skt->data()), skt->size())) { + return on_stun(skt); + } else if (is_dtls(reinterpret_cast(skt->data()), skt->size())) { + return on_dtls(skt); + } else if (is_rtp_or_rtcp(reinterpret_cast(skt->data()), skt->size())) { + return on_rtp_or_rtcp(skt); } return srs_error_new(ERROR_RTC_UDP, "unknown udp packet type"); @@ -1232,17 +2306,17 @@ SrsRtcSession* SrsRtcServer::find_rtc_session_by_peer_id(const string& peer_id) return iter->second; } -srs_error_t SrsRtcServer::on_stun(SrsUdpMuxSocket* udp_mux_skt) +srs_error_t SrsRtcServer::on_stun(SrsUdpMuxSocket* skt) { srs_error_t err = srs_success; SrsStunPacket stun_req; - if ((err = stun_req.decode(udp_mux_skt->data(), udp_mux_skt->size())) != srs_success) { + if ((err = stun_req.decode(skt->data(), skt->size())) != srs_success) { return srs_error_wrap(err, "decode stun packet failed"); } srs_verbose("recv stun packet from %s, use-candidate=%d, ice-controlled=%d, ice-controlling=%d", - udp_mux_skt->get_peer_id().c_str(), stun_req.get_use_candidate(), stun_req.get_ice_controlled(), stun_req.get_ice_controlling()); + skt->get_peer_id().c_str(), stun_req.get_use_candidate(), stun_req.get_ice_controlled(), stun_req.get_ice_controlling()); std::string username = stun_req.get_username(); SrsRtcSession* rtc_session = find_rtc_session_by_username(username); @@ -1254,44 +2328,44 @@ srs_error_t SrsRtcServer::on_stun(SrsUdpMuxSocket* udp_mux_skt) // to make all logs write to the "correct" pid+cid. rtc_session->switch_to_context(); - return rtc_session->on_stun(udp_mux_skt, &stun_req); + return rtc_session->on_stun(skt, &stun_req); } -srs_error_t SrsRtcServer::on_dtls(SrsUdpMuxSocket* udp_mux_skt) +srs_error_t SrsRtcServer::on_dtls(SrsUdpMuxSocket* skt) { - SrsRtcSession* rtc_session = find_rtc_session_by_peer_id(udp_mux_skt->get_peer_id()); + SrsRtcSession* rtc_session = find_rtc_session_by_peer_id(skt->get_peer_id()); if (rtc_session == NULL) { - return srs_error_new(ERROR_RTC_DTLS, "can not find rtc session by peer_id=%s", udp_mux_skt->get_peer_id().c_str()); + return srs_error_new(ERROR_RTC_DTLS, "can not find rtc session by peer_id=%s", skt->get_peer_id().c_str()); } // Now, we got the RTC session to handle the packet, switch to its context // to make all logs write to the "correct" pid+cid. rtc_session->switch_to_context(); - return rtc_session->on_dtls(udp_mux_skt); + return rtc_session->on_dtls(skt); } -srs_error_t SrsRtcServer::on_rtp_or_rtcp(SrsUdpMuxSocket* udp_mux_skt) +srs_error_t SrsRtcServer::on_rtp_or_rtcp(SrsUdpMuxSocket* skt) { srs_error_t err = srs_success; - SrsRtcSession* rtc_session = find_rtc_session_by_peer_id(udp_mux_skt->get_peer_id()); + SrsRtcSession* rtc_session = find_rtc_session_by_peer_id(skt->get_peer_id()); if (rtc_session == NULL) { - return srs_error_new(ERROR_RTC_RTP, "can not find rtc session by peer_id=%s", udp_mux_skt->get_peer_id().c_str()); + return srs_error_new(ERROR_RTC_RTP, "can not find rtc session by peer_id=%s", skt->get_peer_id().c_str()); } // Now, we got the RTC session to handle the packet, switch to its context // to make all logs write to the "correct" pid+cid. rtc_session->switch_to_context(); - if (is_rtcp(reinterpret_cast(udp_mux_skt->data()), udp_mux_skt->size())) { - err = rtc_session->on_rtcp(udp_mux_skt); + if (is_rtcp(reinterpret_cast(skt->data()), skt->size())) { + err = rtc_session->on_rtcp(skt); } else { // We disable it because no RTP for player. // see https://github.com/ossrs/srs/blob/018577e685a07d9de7a47354e7a9c5f77f5f4202/trunk/src/app/srs_app_rtc_conn.cpp#L1081 - // err = rtc_session->on_rtp(udp_mux_skt); + // err = rtc_session->on_rtp(skt); } return err; @@ -1344,86 +2418,6 @@ srs_error_t SrsRtcServer::notify(int type, srs_utime_t interval, srs_utime_t tic return srs_success; } -srs_error_t SrsRtcServer::send_and_free_messages(srs_netfd_t stfd, const vector& msgs) -{ - srs_error_t err = srs_success; - - mmstfd = stfd; - mmhdrs.insert(mmhdrs.end(), msgs.begin(), msgs.end()); - - if (waiting_msgs) { - waiting_msgs = false; - srs_cond_signal(cond); - } - - return err; -} - -void SrsRtcServer::free_messages(vector& hdrs) -{ - for (int i = 0; i < (int)hdrs.size(); i++) { - msghdr* hdr = &hdrs[i].msg_hdr; - for (int j = (int)hdr->msg_iovlen - 1; j >= 0 ; j--) { - iovec* iov = hdr->msg_iov + j; - char* data = (char*)iov->iov_base; - srs_freep(data); - srs_freep(iov); - } - } -} - -srs_error_t SrsRtcServer::cycle() -{ - srs_error_t err = srs_success; - - // TODO: FIXME: Use pithy print. - uint32_t cnt = 1; - - SrsStatistic* stat = SrsStatistic::instance(); - - // TODO: FIXME: Support reload. - int max_sendmmsg = _srs_config->get_rtc_server_sendmmsg(); - - while (true) { - if ((err = trd->pull()) != srs_success) { - return err; - } - - // TODO: FIXME: Use cond trigger. - if (mmhdrs.empty()) { - waiting_msgs = true; - srs_cond_wait(cond); - } - - vector mhdrs; - mmhdrs.swap(mhdrs); - - mmsghdr* p = &mhdrs[0]; - for (mmsghdr* end = p + mhdrs.size(); p < end; p += max_sendmmsg) { - int vlen = (int)(end - p); - vlen = srs_min(max_sendmmsg, vlen); - - int r0 = srs_sendmmsg(mmstfd, p, (unsigned int)vlen, 0, SRS_UTIME_NO_TIMEOUT); - if (r0 != vlen) { - srs_warn("sendmsg %d msgs, %d done", vlen, r0); - } - - stat->perf_mw_on_packets(vlen); - } - - // TODO: FIXME: Use pithy print. - if ((cnt++ % 100) == 0) { - // TODO: FIXME: Support reload. - max_sendmmsg = _srs_config->get_rtc_server_sendmmsg(); - srs_trace("-> RTC SEND %d msgs, by sendmmsg %d", mhdrs.size(), max_sendmmsg); - } - - free_messages(mhdrs); - } - - return err; -} - RtcServerAdapter::RtcServerAdapter() { rtc = new SrsRtcServer(); diff --git a/trunk/src/app/srs_app_rtc_conn.hpp b/trunk/src/app/srs_app_rtc_conn.hpp index e1bb21fff..c3796c711 100644 --- a/trunk/src/app/srs_app_rtc_conn.hpp +++ b/trunk/src/app/srs_app_rtc_conn.hpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -48,6 +49,8 @@ class SrsRtcServer; class SrsRtcSession; class SrsSharedPtrMessage; class SrsSource; +class SrsRtpPacket2; +class ISrsUdpSender; const uint8_t kSR = 200; const uint8_t kRR = 201; @@ -98,23 +101,75 @@ public: srs_error_t initialize(const SrsRequest& req); - srs_error_t on_dtls(SrsUdpMuxSocket* udp_mux_skt); - srs_error_t on_dtls_handshake_done(SrsUdpMuxSocket* udp_mux_skt); + srs_error_t on_dtls(SrsUdpMuxSocket* skt); + srs_error_t on_dtls_handshake_done(SrsUdpMuxSocket* skt); srs_error_t on_dtls_application_data(const char* data, const int len); public: srs_error_t protect_rtp(char* protected_buf, const char* ori_buf, int& nb_protected_buf); + 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); srs_error_t protect_rtcp(char* protected_buf, const char* ori_buf, int& nb_protected_buf); srs_error_t unprotect_rtcp(char* unprotected_buf, const char* ori_buf, int& nb_unprotected_buf); private: - srs_error_t handshake(SrsUdpMuxSocket* udp_mux_skt); + srs_error_t handshake(SrsUdpMuxSocket* skt); private: srs_error_t srtp_initialize(); srs_error_t srtp_send_init(); srs_error_t srtp_recv_init(); }; -class SrsRtcSenderThread : public ISrsCoroutineHandler +// A group of RTP packets. +class SrsRtcPackets +{ +public: + bool use_gso; + bool should_merge_nalus; +public: +#if defined(SRS_DEBUG) + // Debug id. + uint32_t debug_id; +#endif +public: + // The total bytes of AVFrame packets. + int nn_bytes; + // The total bytes of RTP packets. + int nn_rtp_bytes; + // The total padded bytes. + int nn_padding_bytes; +public: + // The RTP packets send out by sendmmsg or sendmsg. Note that if many packets group to + // one msghdr by GSO, it's only one RTP packet, because we only send once. + int nn_rtp_pkts; + // For video, the samples or NALUs. + int nn_samples; + // For audio, the generated extra audio packets. + // For example, when transcoding AAC to opus, may many extra payloads for a audio. + int nn_extras; + // The original audio messages. + int nn_audios; + // The original video messages. + int nn_videos; + // The number of padded packet. + int nn_paddings; + // The number of dropped messages. + int nn_dropped; +private: + int cursor; + int nn_cache; + SrsRtpPacket2* cache; +public: + SrsRtcPackets(int nn_cache_max); + virtual ~SrsRtcPackets(); +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 { protected: SrsCoroutine* trd; @@ -125,13 +180,34 @@ private: uint32_t audio_ssrc; uint16_t video_payload_type; uint16_t audio_payload_type; +private: + // TODO: FIXME: How to handle timestamp overflow? + 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; + bool realtime; public: SrsRtcSenderThread(SrsRtcSession* s, SrsUdpMuxSocket* u, int parent_cid); virtual ~SrsRtcSenderThread(); public: srs_error_t initialize(const uint32_t& vssrc, const uint32_t& assrc, const uint16_t& v_pt, const uint16_t& a_pt); +// interface ISrsReloadHandler +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_realtime(std::string vhost); public: virtual int cid(); public: @@ -139,11 +215,21 @@ public: virtual void stop(); virtual void stop_loop(); public: - virtual srs_error_t cycle(); + void update_sendonly_socket(SrsUdpMuxSocket* skt); public: - void update_sendonly_socket(SrsUdpMuxSocket* ukt); + virtual srs_error_t cycle(); private: - void send_and_free_messages(SrsSharedPtrMessage** msgs, int nb_msgs, SrsUdpMuxSocket* udp_mux_skt); + srs_error_t send_messages(SrsSource* source, SrsSharedPtrMessage** msgs, int nb_msgs, SrsRtcPackets& packets); + srs_error_t messages_to_packets(SrsSource* source, SrsSharedPtrMessage** msgs, int nb_msgs, SrsRtcPackets& packets); + 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 @@ -194,37 +280,75 @@ public: void switch_to_context(); public: - srs_error_t on_stun(SrsUdpMuxSocket* udp_mux_skt, SrsStunPacket* stun_req); - srs_error_t on_dtls(SrsUdpMuxSocket* udp_mux_skt); - srs_error_t on_rtcp(SrsUdpMuxSocket* udp_mux_skt); + 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* udp_mux_skt); - srs_error_t on_connection_established(SrsUdpMuxSocket* udp_mux_skt); - srs_error_t start_play(SrsUdpMuxSocket* udp_mux_skt); + 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* udp_mux_skt, SrsStunPacket* stun_req); + srs_error_t on_binding_request(SrsUdpMuxSocket* skt, SrsStunPacket* stun_req); private: - srs_error_t on_rtcp_feedback(char* buf, int nb_buf, SrsUdpMuxSocket* udp_mux_skt); - srs_error_t on_rtcp_ps_feedback(char* buf, int nb_buf, SrsUdpMuxSocket* udp_mux_skt); - srs_error_t on_rtcp_receiver_report(char* buf, int nb_buf, SrsUdpMuxSocket* udp_mux_skt); + 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 SrsRtcServer : virtual public ISrsUdpMuxHandler, virtual public ISrsHourGlass, virtual public ISrsCoroutineHandler +class SrsUdpMuxSender : virtual public ISrsUdpSender, virtual public ISrsCoroutineHandler, virtual public ISrsReloadHandler { private: - SrsUdpMuxListener* listener; - SrsHourGlass* timer; -private: + srs_netfd_t lfd; + SrsRtcServer* server; SrsCoroutine* trd; +private: srs_cond_t cond; bool waiting_msgs; - // TODO: FIXME: Support multiple stfd. - srs_netfd_t mmstfd; - std::vector mmhdrs; + bool gso; + int nn_senders; +private: + // Hotspot msgs, we are working on it. + // @remark We will wait util all messages are ready. + std::vector hotspot; + // Cache msgs, for other coroutines to fill it. + std::vector 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& 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 listeners; + std::vector senders; private: std::map map_username_session; // key: username(local_ufrag + ":" + remote_ufrag) std::map map_id_session; // key: peerip(ip + ":" + port) @@ -237,27 +361,23 @@ public: // TODO: FIXME: Support gracefully quit. // TODO: FIXME: Support reload. virtual srs_error_t listen_udp(); - virtual srs_error_t on_udp_packet(SrsUdpMuxSocket* udp_mux_skt); + 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* udp_mux_skt); - srs_error_t on_dtls(SrsUdpMuxSocket* udp_mux_skt); - srs_error_t on_rtp_or_rtcp(SrsUdpMuxSocket* udp_mux_skt); + 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 public: virtual srs_error_t notify(int type, srs_utime_t interval, srs_utime_t tick); -// Internal only. -public: - srs_error_t send_and_free_messages(srs_netfd_t stfd, const std::vector& msgs); - void free_messages(std::vector& hdrs); - virtual srs_error_t cycle(); }; // The RTC server adapter. diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index d312d4ab2..0f8b1012a 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -116,7 +116,7 @@ SrsRtmpConn::SrsRtmpConn(SrsServer* svr, srs_netfd_t c, string cip) : SrsConnect wakable = NULL; mw_sleep = SRS_PERF_MW_SLEEP; - mw_enabled = false; + mw_msgs = 0; realtime = SRS_PERF_MIN_LATENCY_ENABLED; send_min_interval = 0; tcp_nodelay = false; @@ -264,6 +264,10 @@ srs_error_t SrsRtmpConn::on_reload_vhost_play(string vhost) send_min_interval = v; } } + + mw_msgs = _srs_config->get_mw_msgs(req->vhost, realtime); + mw_sleep = _srs_config->get_mw_sleep(req->vhost); + set_socket_buffer(mw_sleep); return err; } @@ -298,6 +302,10 @@ srs_error_t SrsRtmpConn::on_reload_vhost_realtime(string vhost) srs_trace("realtime changed %d=>%d", realtime, realtime_enabled); realtime = realtime_enabled; } + + mw_msgs = _srs_config->get_mw_msgs(req->vhost, realtime); + mw_sleep = _srs_config->get_mw_sleep(req->vhost); + set_socket_buffer(mw_sleep); return err; } @@ -689,18 +697,19 @@ srs_error_t SrsRtmpConn::do_playing(SrsSource* source, SrsConsumer* consumer, Sr SrsMessageArray msgs(SRS_PERF_MW_MSGS); bool user_specified_duration_to_stop = (req->duration > 0); int64_t starttime = -1; - + // setup the realtime. realtime = _srs_config->get_realtime_enabled(req->vhost); // setup the mw config. // when mw_sleep changed, resize the socket send buffer. - mw_enabled = true; - change_mw_sleep(_srs_config->get_mw_sleep(req->vhost)); + mw_msgs = _srs_config->get_mw_msgs(req->vhost, realtime); + mw_sleep = _srs_config->get_mw_sleep(req->vhost); + set_socket_buffer(mw_sleep); // initialize the send_min_interval send_min_interval = _srs_config->get_send_min_interval(req->vhost); - srs_trace("start play smi=%dms, mw_sleep=%d, mw_enabled=%d, realtime=%d, tcp_nodelay=%d", - srsu2msi(send_min_interval), srsu2msi(mw_sleep), mw_enabled, realtime, tcp_nodelay); + srs_trace("start play smi=%dms, mw_sleep=%d, mw_msgs=%d, realtime=%d, tcp_nodelay=%d", + srsu2msi(send_min_interval), srsu2msi(mw_sleep), mw_msgs, realtime, tcp_nodelay); while (true) { // when source is set to expired, disconnect it. @@ -730,13 +739,7 @@ srs_error_t SrsRtmpConn::do_playing(SrsSource* source, SrsConsumer* consumer, Sr // wait for message to incoming. // @see https://github.com/ossrs/srs/issues/251 // @see https://github.com/ossrs/srs/issues/257 - if (realtime) { - // for realtime, min required msgs is 0, send when got one+ msgs. - consumer->wait(0, mw_sleep); - } else { - // for no-realtime, got some msgs then send. - consumer->wait(SRS_PERF_MW_MIN_MSGS, mw_sleep); - } + consumer->wait(mw_msgs, mw_sleep); #endif // get messages from consumer. @@ -750,9 +753,9 @@ srs_error_t SrsRtmpConn::do_playing(SrsSource* source, SrsConsumer* consumer, Sr // reportable if (pprint->can_print()) { kbps->sample(); - srs_trace("-> " SRS_CONSTS_LOG_PLAY " time=%d, msgs=%d, okbps=%d,%d,%d, ikbps=%d,%d,%d, mw=%d", + srs_trace("-> " SRS_CONSTS_LOG_PLAY " time=%d, msgs=%d, okbps=%d,%d,%d, ikbps=%d,%d,%d, mw=%d/%d", (int)pprint->age(), count, kbps->get_send_kbps(), kbps->get_send_kbps_30s(), kbps->get_send_kbps_5m(), - kbps->get_recv_kbps(), kbps->get_recv_kbps_30s(), kbps->get_recv_kbps_5m(), srsu2msi(mw_sleep)); + kbps->get_recv_kbps(), kbps->get_recv_kbps_30s(), kbps->get_recv_kbps_5m(), srsu2msi(mw_sleep), mw_msgs); } if (count <= 0) { @@ -1114,16 +1117,6 @@ srs_error_t SrsRtmpConn::process_play_control_msg(SrsConsumer* consumer, SrsComm return err; } -void SrsRtmpConn::change_mw_sleep(srs_utime_t sleep_v) -{ - if (!mw_enabled) { - return; - } - - set_socket_buffer(sleep_v); - mw_sleep = sleep_v; -} - void SrsRtmpConn::set_sock_options() { SrsRequest* req = info->req; diff --git a/trunk/src/app/srs_app_rtmp_conn.hpp b/trunk/src/app/srs_app_rtmp_conn.hpp index ac4b7efb9..c58111f04 100644 --- a/trunk/src/app/srs_app_rtmp_conn.hpp +++ b/trunk/src/app/srs_app_rtmp_conn.hpp @@ -102,8 +102,7 @@ private: srs_utime_t duration; // The MR(merged-write) sleep time in srs_utime_t. srs_utime_t mw_sleep; - // The MR(merged-write) only enabled for play. - int mw_enabled; + int mw_msgs; // For realtime // @see https://github.com/ossrs/srs/issues/257 bool realtime; @@ -149,7 +148,6 @@ private: virtual srs_error_t handle_publish_message(SrsSource* source, SrsCommonMessage* msg); virtual srs_error_t process_publish_message(SrsSource* source, SrsCommonMessage* msg); virtual srs_error_t process_play_control_msg(SrsConsumer* consumer, SrsCommonMessage* msg); - virtual void change_mw_sleep(srs_utime_t sleep_v); virtual void set_sock_options(); private: virtual srs_error_t check_edge_token_traverse_auth(); diff --git a/trunk/src/app/srs_app_sdp.cpp b/trunk/src/app/srs_app_sdp.cpp index f5f4ee47f..c67dbd9a4 100644 --- a/trunk/src/app/srs_app_sdp.cpp +++ b/trunk/src/app/srs_app_sdp.cpp @@ -78,6 +78,14 @@ srs_error_t parse_h264_fmtp(const std::string& fmtp, H264SpecificParam& h264_par if (kv[0] == "profile-level-id") { h264_param.profile_level_id = kv[1]; } else if (kv[0] == "packetization-mode") { + // 6.3. Non-Interleaved Mode + // This mode is in use when the value of the OPTIONAL packetization-mode + // media type parameter is equal to 1. This mode SHOULD be supported. + // It is primarily intended for low-delay applications. Only single NAL + // unit packets, STAP-As, and FU-As MAY be used in this mode. STAP-Bs, + // MTAPs, and FU-Bs MUST NOT be used. The transmission order of NAL + // units MUST comply with the NAL unit decoding order. + // @see https://tools.ietf.org/html/rfc6184#section-6.3 h264_param.packetization_mode = kv[1]; } else if (kv[0] == "level-asymmetry-allowed") { h264_param.level_asymmerty_allow = kv[1]; diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index cb7d1b505..c0548708f 100755 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -269,9 +269,17 @@ void SrsMessageQueue::set_queue_size(srs_utime_t queue_size) max_queue_size = queue_size; } -srs_error_t SrsMessageQueue::enqueue(SrsSharedPtrMessage* msg, bool* is_overflow) +srs_error_t SrsMessageQueue::enqueue(SrsSharedPtrMessage* msg, bool* is_overflow, bool pass_timestamp) { srs_error_t err = srs_success; + + 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 (av_start_time == -1) { @@ -281,8 +289,6 @@ srs_error_t SrsMessageQueue::enqueue(SrsSharedPtrMessage* msg, bool* is_overflow av_end_time = srs_utime_t(msg->timestamp * SRS_UTIME_MILLISECONDS); } - msgs.push_back(msg); - while (av_end_time - av_start_time > max_queue_size) { // notice the caller queue already overflow and shrinked. if (is_overflow) { @@ -295,7 +301,7 @@ srs_error_t SrsMessageQueue::enqueue(SrsSharedPtrMessage* msg, bool* is_overflow return err; } -srs_error_t SrsMessageQueue::dump_packets(int max_count, SrsSharedPtrMessage** pmsgs, int& count) +srs_error_t SrsMessageQueue::dump_packets(int max_count, SrsSharedPtrMessage** pmsgs, int& count, bool pass_timestamp) { srs_error_t err = srs_success; @@ -308,13 +314,15 @@ srs_error_t SrsMessageQueue::dump_packets(int max_count, SrsSharedPtrMessage** p count = srs_min(max_count, nb_msgs); SrsSharedPtrMessage** omsgs = msgs.data(); - for (int i = 0; i < count; i++) { - pmsgs[i] = omsgs[i]; + 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]; + av_start_time = srs_utime_t(last->timestamp * SRS_UTIME_MILLISECONDS); } - SrsSharedPtrMessage* last = omsgs[count - 1]; - av_start_time = srs_utime_t(last->timestamp * SRS_UTIME_MILLISECONDS); - if (count >= nb_msgs) { // the pmsgs is big enough and clear msgs at most time. msgs.clear(); @@ -433,6 +441,8 @@ SrsConsumer::SrsConsumer(SrsSource* s, SrsConnection* c) mw_duration = 0; mw_waiting = false; #endif + + pass_timestamp = false; } SrsConsumer::~SrsConsumer() @@ -466,20 +476,35 @@ srs_error_t SrsConsumer::enqueue(SrsSharedPtrMessage* shared_msg, bool atc, SrsR srs_error_t err = srs_success; SrsSharedPtrMessage* msg = shared_msg->copy(); - - if (!atc) { + + // For RTC, we enable pass_timestamp mode, which never correct or depends on monotonic increasing of + // 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) { return srs_error_wrap(err, "consume message"); } } - - if ((err = queue->enqueue(msg, NULL)) != srs_success) { + + // Put message in queue, here we may enable pass_timestamp mode. + if ((err = queue->enqueue(msg, NULL, pass_timestamp)) != srs_success) { return srs_error_wrap(err, "enqueue message"); } #ifdef SRS_PERF_QUEUE_COND_WAIT // fire the mw when msgs is enough. 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. srs_utime_t duration = queue->duration(); bool match_min_msgs = queue->size() > mw_min_msgs; @@ -529,7 +554,7 @@ srs_error_t SrsConsumer::dump_packets(SrsMessageArray* msgs, int& count) } // pump msgs from queue. - if ((err = queue->dump_packets(max, msgs->msgs, count)) != srs_success) { + if ((err = queue->dump_packets(max, msgs->msgs, count, pass_timestamp)) != srs_success) { return srs_error_wrap(err, "dump packets"); } @@ -1143,7 +1168,8 @@ srs_error_t SrsOriginHub::on_video(SrsSharedPtrMessage* shared_video, bool is_se // TODO: FIXME: Refactor to move to rtp? // Save the RTP packets for find_rtp_packet() to rtx or restore it. - source->rtp_queue->push(msg->rtp_packets); + // TODO: FIXME: Remove dead code. + //source->rtp_queue->push(msg->rtp_packets); #endif if ((err = hls->on_video(msg, format)) != srs_success) { @@ -2718,4 +2744,9 @@ SrsRtpSharedPacket* SrsSource::find_rtp_packet(const uint16_t& seq) { return rtp_queue->find(seq); } + +SrsMetaCache* SrsSource::cached_meta() +{ + return meta; +} #endif diff --git a/trunk/src/app/srs_app_source.hpp b/trunk/src/app/srs_app_source.hpp index ac04f5a05..fdfe9c789 100644 --- a/trunk/src/app/srs_app_source.hpp +++ b/trunk/src/app/srs_app_source.hpp @@ -151,12 +151,13 @@ public: // Enqueue the message, the timestamp always monotonically. // @param msg, the msg to enqueue, user never free it whatever the return code. // @param is_overflow, whether overflow and shrinked. NULL to ignore. - virtual srs_error_t enqueue(SrsSharedPtrMessage* msg, bool* is_overflow = NULL); + // @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, bool pass_timestamp = false); // Get packets in consumer queue. // @pmsgs SrsSharedPtrMessage*[], used to store the msgs, user must alloc it. // @count the count in array, output param. // @max_count the max count to dequeue, must be positive. - virtual srs_error_t dump_packets(int max_count, SrsSharedPtrMessage** pmsgs, int& count); + virtual srs_error_t dump_packets(int max_count, SrsSharedPtrMessage** pmsgs, int& count, bool pass_timestamp = false); // Dumps packets to consumer, use specified args. // @remark the atc/tba/tbv/ag are same to SrsConsumer.enqueue(). virtual srs_error_t dump_packets(SrsConsumer* consumer, bool atc, SrsRtmpJitterAlgorithm ag); @@ -203,10 +204,17 @@ private: int mw_min_msgs; srs_utime_t mw_duration; #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: SrsConsumer(SrsSource* s, SrsConnection* c); virtual ~SrsConsumer(); public: + // Use pass timestamp mode. + void enable_pass_timestamp() { pass_timestamp = true; } // Set the size of queue. virtual void set_queue_size(srs_utime_t queue_size); // when source id changed, notice client to print. @@ -327,6 +335,7 @@ public: #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: @@ -634,6 +643,8 @@ 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 }; diff --git a/trunk/src/app/srs_app_statistic.cpp b/trunk/src/app/srs_app_statistic.cpp index be3874ff7..890e43746 100644 --- a/trunk/src/app/srs_app_statistic.cpp +++ b/trunk/src/app/srs_app_statistic.cpp @@ -236,6 +236,8 @@ srs_error_t SrsStatisticClient::dumps(SrsJsonObject* obj) SrsStatisticCategory::SrsStatisticCategory() { + nn = 0; + a = 0; b = 0; c = 0; @@ -265,8 +267,12 @@ SrsStatistic::SrsStatistic() perf_iovs = new SrsStatisticCategory(); perf_msgs = new SrsStatisticCategory(); - perf_sys = new SrsStatisticCategory(); perf_sendmmsg = new SrsStatisticCategory(); + perf_gso = new SrsStatisticCategory(); + perf_rtp = new SrsStatisticCategory(); + perf_rtc = new SrsStatisticCategory(); + perf_bytes = new SrsStatisticCategory(); + perf_dropped = new SrsStatisticCategory(); } SrsStatistic::~SrsStatistic() @@ -303,8 +309,12 @@ SrsStatistic::~SrsStatistic() srs_freep(perf_iovs); srs_freep(perf_msgs); - srs_freep(perf_sys); srs_freep(perf_sendmmsg); + srs_freep(perf_gso); + srs_freep(perf_rtp); + srs_freep(perf_rtc); + srs_freep(perf_bytes); + srs_freep(perf_dropped); } SrsStatistic* SrsStatistic::instance() @@ -585,225 +595,202 @@ srs_error_t SrsStatistic::dumps_clients(SrsJsonArray* arr, int start, int count) return err; } -void SrsStatistic::perf_mw_on_msgs(int nb_msgs, int bytes_msgs, int nb_iovs) +void SrsStatistic::perf_on_msgs(int nb_msgs) { - // For perf msgs, the nb_msgs stat. - // a: =1 - // b: <10 - // c: <100 - // d: <200 - // e: <300 - // f: <400 - // g: <500 - // h: <600 - // i: <1000 - // j: >=1000 - if (nb_msgs == 1) { - perf_msgs->a++; - } else if (nb_msgs < 10) { - perf_msgs->b++; - } else if (nb_msgs < 100) { - perf_msgs->c++; - } else if (nb_msgs < 200) { - perf_msgs->d++; - } else if (nb_msgs < 300) { - perf_msgs->e++; - } else if (nb_msgs < 400) { - perf_msgs->f++; - } else if (nb_msgs < 500) { - perf_msgs->g++; - } else if (nb_msgs < 600) { - perf_msgs->h++; - } else if (nb_msgs < 1000) { - perf_msgs->i++; - } else { - perf_msgs->j++; - } - - // For perf iovs, the nb_iovs stat. - // a: <=2 - // b: <10 - // c: <20 - // d: <200 - // e: <300 - // f: <500 - // g: <700 - // h: <900 - // i: <1024 - // j: >=1024 - if (nb_iovs <= 2) { - perf_iovs->a++; - } else if (nb_iovs < 10) { - perf_iovs->b++; - } else if (nb_iovs < 20) { - perf_iovs->c++; - } else if (nb_iovs < 200) { - perf_iovs->d++; - } else if (nb_iovs < 300) { - perf_iovs->e++; - } else if (nb_iovs < 500) { - perf_iovs->f++; - } else if (nb_iovs < 700) { - perf_iovs->g++; - } else if (nb_iovs < 900) { - perf_iovs->h++; - } else if (nb_iovs < 1024) { - perf_iovs->i++; - } else { - perf_iovs->j++; - } - - // Stat the syscalls. - // a: number of syscalls of msgs. - perf_sys->a++; + perf_on_packets(perf_msgs, nb_msgs); } -void SrsStatistic::perf_mw_on_packets(int nb_pkts, int bytes_pkts, int nb_iovs) +srs_error_t SrsStatistic::dumps_perf_msgs(SrsJsonObject* obj) { - // Stat the syscalls. - // a: number of syscalls of msgs. - // b: number of syscalls of pkts. - perf_sys->b++; + return dumps_perf(perf_msgs, obj); } -srs_error_t SrsStatistic::dumps_perf_writev(SrsJsonObject* obj) +void SrsStatistic::perf_on_rtc_packets(int nb_packets) { - srs_error_t err = srs_success; - - if (true) { - SrsJsonObject* p = SrsJsonAny::object(); - obj->set("msgs", p); - - // For perf msgs, the nb_msgs stat. - // a: =1 - // b: <10 - // c: <100 - // d: <200 - // e: <300 - // f: <400 - // g: <500 - // h: <600 - // i: <1000 - // j: >=1000 - p->set("lt_2", SrsJsonAny::integer(perf_msgs->a)); - p->set("lt_10", SrsJsonAny::integer(perf_msgs->b)); - p->set("lt_100", SrsJsonAny::integer(perf_msgs->c)); - p->set("lt_200", SrsJsonAny::integer(perf_msgs->d)); - p->set("lt_300", SrsJsonAny::integer(perf_msgs->e)); - p->set("lt_400", SrsJsonAny::integer(perf_msgs->f)); - p->set("lt_500", SrsJsonAny::integer(perf_msgs->g)); - p->set("lt_600", SrsJsonAny::integer(perf_msgs->h)); - p->set("lt_1000", SrsJsonAny::integer(perf_msgs->i)); - p->set("gt_1000", SrsJsonAny::integer(perf_msgs->j)); - } - - if (true) { - SrsJsonObject* p = SrsJsonAny::object(); - obj->set("iovs", p); - - // For perf iovs, the nb_iovs stat. - // a: <=2 - // b: <10 - // c: <20 - // d: <200 - // e: <300 - // f: <500 - // g: <700 - // h: <900 - // i: <1024 - // j: >=1024 - p->set("lt_3", SrsJsonAny::integer(perf_iovs->a)); - p->set("lt_10", SrsJsonAny::integer(perf_iovs->b)); - p->set("lt_20", SrsJsonAny::integer(perf_iovs->c)); - p->set("lt_200", SrsJsonAny::integer(perf_iovs->d)); - p->set("lt_300", SrsJsonAny::integer(perf_iovs->e)); - p->set("lt_500", SrsJsonAny::integer(perf_iovs->f)); - p->set("lt_700", SrsJsonAny::integer(perf_iovs->g)); - p->set("lt_900", SrsJsonAny::integer(perf_iovs->h)); - p->set("lt_1024", SrsJsonAny::integer(perf_iovs->i)); - p->set("gt_1024", SrsJsonAny::integer(perf_iovs->j)); - } - - if (true) { - SrsJsonObject* p = SrsJsonAny::object(); - obj->set("sys", p); - - // Stat the syscalls. - // a: number of syscalls of msgs. - // b: number of syscalls of pkts. - p->set("msgs", SrsJsonAny::integer(perf_sys->a)); - p->set("pkts", SrsJsonAny::integer(perf_sys->b)); - } - - return err; + perf_on_packets(perf_rtc, nb_packets); } -void SrsStatistic::perf_mw_on_packets(int nb_msgs) +srs_error_t SrsStatistic::dumps_perf_rtc_packets(SrsJsonObject* obj) { - // For perf msgs, the nb_msgs stat. - // a: =1 - // b: <10 - // c: <100 - // d: <200 - // e: <300 - // f: <400 - // g: <500 - // h: <600 - // i: <1000 - // j: >=1000 - if (nb_msgs == 1) { - perf_sendmmsg->a++; - } else if (nb_msgs < 10) { - perf_sendmmsg->b++; - } else if (nb_msgs < 100) { - perf_sendmmsg->c++; - } else if (nb_msgs < 200) { - perf_sendmmsg->d++; - } else if (nb_msgs < 300) { - perf_sendmmsg->e++; - } else if (nb_msgs < 400) { - perf_sendmmsg->f++; - } else if (nb_msgs < 500) { - perf_sendmmsg->g++; - } else if (nb_msgs < 600) { - perf_sendmmsg->h++; - } else if (nb_msgs < 1000) { - perf_sendmmsg->i++; - } else { - perf_sendmmsg->j++; - } + return dumps_perf(perf_rtc, obj); +} + +void SrsStatistic::perf_on_rtp_packets(int nb_packets) +{ + perf_on_packets(perf_rtp, nb_packets); +} + +srs_error_t SrsStatistic::dumps_perf_rtp_packets(SrsJsonObject* 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) +{ + perf_on_packets(perf_iovs, nb_iovs); +} + +srs_error_t SrsStatistic::dumps_perf_writev_iovs(SrsJsonObject* 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) +{ + // a: AVFrame bytes. + // b: RTC bytes. + // c: RTC paddings. + perf_bytes->a += nn_bytes; + perf_bytes->b += nn_rtp_bytes; + perf_bytes->c += nn_padding; + + perf_bytes->nn += nn_rtp_bytes; +} + +srs_error_t SrsStatistic::dumps_perf_bytes(SrsJsonObject* obj) +{ + obj->set("avframe_bytes", SrsJsonAny::integer(perf_bytes->a)); + obj->set("rtc_bytes", SrsJsonAny::integer(perf_bytes->b)); + obj->set("rtc_padding", SrsJsonAny::integer(perf_bytes->c)); + + obj->set("nn", SrsJsonAny::integer(perf_bytes->nn)); + + 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() +{ + srs_freep(perf_iovs); + srs_freep(perf_msgs); + srs_freep(perf_sendmmsg); + srs_freep(perf_gso); + srs_freep(perf_rtp); + srs_freep(perf_rtc); + srs_freep(perf_bytes); + srs_freep(perf_dropped); + + perf_iovs = new SrsStatisticCategory(); + perf_msgs = new SrsStatisticCategory(); + perf_sendmmsg = new SrsStatisticCategory(); + perf_gso = new SrsStatisticCategory(); + perf_rtp = new SrsStatisticCategory(); + perf_rtc = new SrsStatisticCategory(); + perf_bytes = new SrsStatisticCategory(); + perf_dropped = new SrsStatisticCategory(); +} + +void SrsStatistic::perf_on_packets(SrsStatisticCategory* p, int nb_msgs) +{ + // The range for stat: + // 2, 3, 5, 9, 16, 32, 64, 128, 256 + // that is: + // a: <2 + // b: <3 + // c: <5 + // d: <9 + // e: <16 + // f: <32 + // g: <64 + // h: <128 + // i: <256 + // j: >=256 + if (nb_msgs < 2) { + p->a++; + } else if (nb_msgs < 3) { + p->b++; + } else if (nb_msgs < 5) { + p->c++; + } else if (nb_msgs < 9) { + p->d++; + } else if (nb_msgs < 16) { + p->e++; + } else if (nb_msgs < 32) { + p->f++; + } else if (nb_msgs < 64) { + p->g++; + } else if (nb_msgs < 128) { + p->h++; + } else if (nb_msgs < 256) { + p->i++; + } else { + p->j++; + } + + p->nn += nb_msgs; +} + +srs_error_t SrsStatistic::dumps_perf(SrsStatisticCategory* p, SrsJsonObject* obj) { srs_error_t err = srs_success; - if (true) { - SrsJsonObject* p = SrsJsonAny::object(); - obj->set("msgs", p); + // The range for stat: + // 2, 3, 5, 9, 16, 32, 64, 128, 256 + // that is: + // a: <2 + // b: <3 + // c: <5 + // d: <9 + // e: <16 + // f: <32 + // g: <64 + // h: <128 + // i: <256 + // j: >=256 + if (p->a) obj->set("lt_2", SrsJsonAny::integer(p->a)); + if (p->b) obj->set("lt_3", SrsJsonAny::integer(p->b)); + if (p->c) obj->set("lt_5", SrsJsonAny::integer(p->c)); + if (p->d) obj->set("lt_9", SrsJsonAny::integer(p->d)); + if (p->e) obj->set("lt_16", SrsJsonAny::integer(p->e)); + if (p->f) obj->set("lt_32", SrsJsonAny::integer(p->f)); + if (p->g) obj->set("lt_64", SrsJsonAny::integer(p->g)); + if (p->h) obj->set("lt_128", SrsJsonAny::integer(p->h)); + if (p->i) obj->set("lt_256", SrsJsonAny::integer(p->i)); + if (p->j) obj->set("gt_256", SrsJsonAny::integer(p->j)); - // For perf msgs, the nb_msgs stat. - // a: =1 - // b: <10 - // c: <100 - // d: <200 - // e: <300 - // f: <400 - // g: <500 - // h: <600 - // i: <1000 - // j: >=1000 - p->set("lt_2", SrsJsonAny::integer(perf_sendmmsg->a)); - p->set("lt_10", SrsJsonAny::integer(perf_sendmmsg->b)); - p->set("lt_100", SrsJsonAny::integer(perf_sendmmsg->c)); - p->set("lt_200", SrsJsonAny::integer(perf_sendmmsg->d)); - p->set("lt_300", SrsJsonAny::integer(perf_sendmmsg->e)); - p->set("lt_400", SrsJsonAny::integer(perf_sendmmsg->f)); - p->set("lt_500", SrsJsonAny::integer(perf_sendmmsg->g)); - p->set("lt_600", SrsJsonAny::integer(perf_sendmmsg->h)); - p->set("lt_1000", SrsJsonAny::integer(perf_sendmmsg->i)); - p->set("gt_1000", SrsJsonAny::integer(perf_sendmmsg->j)); - } + obj->set("nn", SrsJsonAny::integer(p->nn)); return err; } diff --git a/trunk/src/app/srs_app_statistic.hpp b/trunk/src/app/srs_app_statistic.hpp index 339e95b26..81ed9e27d 100644 --- a/trunk/src/app/srs_app_statistic.hpp +++ b/trunk/src/app/srs_app_statistic.hpp @@ -124,6 +124,8 @@ public: class SrsStatisticCategory { +public: + uint64_t nn; public: uint64_t a; uint64_t b; @@ -168,8 +170,12 @@ private: // The perf stat for mw(merged write). SrsStatisticCategory* perf_iovs; SrsStatisticCategory* perf_msgs; - SrsStatisticCategory* perf_sys; SrsStatisticCategory* perf_sendmmsg; + SrsStatisticCategory* perf_gso; + SrsStatisticCategory* perf_rtp; + SrsStatisticCategory* perf_rtc; + SrsStatisticCategory* perf_bytes; + SrsStatisticCategory* perf_dropped; private: SrsStatistic(); virtual ~SrsStatistic(); @@ -228,19 +234,47 @@ public: // @param count the max count of clients to dump. virtual srs_error_t dumps_clients(SrsJsonArray* arr, int start, int count); public: - // Stat for packets merged written, nb_msgs is the number of RTMP messages, - // bytes_msgs is the total bytes of RTMP messages, nb_iovs is the total number of iovec. - virtual void perf_mw_on_msgs(int nb_msgs, int bytes_msgs, int nb_iovs); - // Stat for packets merged written, nb_pkts is the number of or chunk packets, - // bytes_pkts is the total bytes of or chunk packets, nb_iovs is the total number of iovec. - virtual void perf_mw_on_packets(int nb_pkts, int bytes_pkts, int nb_iovs); - // Dumps the perf statistic data for TCP writev, for performance analysis. - virtual srs_error_t dumps_perf_writev(SrsJsonObject* obj); + // Stat for packets merged written, nb_msgs is the number of RTMP messages. + // For example, publish by FFMPEG, Audio and Video frames. + virtual void perf_on_msgs(int nb_msgs); + virtual srs_error_t dumps_perf_msgs(SrsJsonObject* obj); public: - // Stat for packets UDP sendmmsg, nb_msgs is the vlen for sendmmsg. - virtual void perf_mw_on_packets(int nb_msgs); - // Dumps the perf statistic data for UDP sendmmsg, for performance analysis. + // Stat for packets merged written, nb_packets is the number of RTC packets. + // For example, a RTMP/AAC audio packet maybe transcoded to two RTC/opus packets. + virtual void perf_on_rtc_packets(int nb_packets); + virtual srs_error_t dumps_perf_rtc_packets(SrsJsonObject* obj); +public: + // Stat for packets merged written, nb_packets is the number of RTP packets. + // For example, a RTC/opus packet maybe package to three RTP packets. + virtual void perf_on_rtp_packets(int nb_packets); + 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: + // Stat for TCP writev, nb_iovs is the total number of iovec. + virtual void perf_on_writev_iovs(int nb_iovs); + 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: + // 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 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: + // Reset all perf stat data. + virtual void reset_perf(); +private: + virtual void perf_on_packets(SrsStatisticCategory* p, int nb_msgs); + virtual srs_error_t dumps_perf(SrsStatisticCategory* p, SrsJsonObject* obj); private: virtual SrsStatisticVhost* create_vhost(SrsRequest* req); virtual SrsStatisticStream* create_stream(SrsStatisticVhost* vhost, SrsRequest* req); diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index c846214b2..725c1bff4 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -33,6 +33,11 @@ // The macros generated by configure script. #include +// Alias for debug. +#ifdef SRS_AUTO_DEBUG +#define SRS_DEBUG +#endif + // To convert macro values to string. // @see https://gcc.gnu.org/onlinedocs/cpp/Stringification.html#Stringification #define SRS_INTERNAL_STR(v) #v diff --git a/trunk/src/core/srs_core_performance.hpp b/trunk/src/core/srs_core_performance.hpp index 07c1e1539..39493646a 100644 --- a/trunk/src/core/srs_core_performance.hpp +++ b/trunk/src/core/srs_core_performance.hpp @@ -127,8 +127,12 @@ */ #define SRS_PERF_QUEUE_COND_WAIT #ifdef SRS_PERF_QUEUE_COND_WAIT + // For RTMP, use larger wait queue. #define SRS_PERF_MW_MIN_MSGS 8 - #define SRS_PERF_MW_MIN_MSGS_FOR_RTC 4 + // For RTC, use smaller wait queue. + #define SRS_PERF_MW_MIN_MSGS_FOR_RTC 1 + // For Real-Time, never wait messages. + #define SRS_PERF_MW_MIN_MSGS_REALTIME 0 #endif /** * the default value of vhost for @@ -188,5 +192,27 @@ #define 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 diff --git a/trunk/src/core/srs_core_version4.hpp b/trunk/src/core/srs_core_version4.hpp index 34fd22900..e47dfdc68 100644 --- a/trunk/src/core/srs_core_version4.hpp +++ b/trunk/src/core/srs_core_version4.hpp @@ -24,6 +24,6 @@ #ifndef SRS_CORE_VERSION4_HPP #define SRS_CORE_VERSION4_HPP -#define SRS_VERSION4_REVISION 22 +#define SRS_VERSION4_REVISION 23 #endif diff --git a/trunk/src/kernel/srs_kernel_buffer.cpp b/trunk/src/kernel/srs_kernel_buffer.cpp index e6fcfe1fa..be94d0bcb 100644 --- a/trunk/src/kernel/srs_kernel_buffer.cpp +++ b/trunk/src/kernel/srs_kernel_buffer.cpp @@ -29,6 +29,14 @@ using namespace std; #include #include +ISrsEncoder::ISrsEncoder() +{ +} + +ISrsEncoder::~ISrsEncoder() +{ +} + ISrsCodec::ISrsCodec() { } @@ -59,11 +67,6 @@ SrsBuffer::~SrsBuffer() { } -char* SrsBuffer::data() -{ - return bytes; -} - int SrsBuffer::size() { return nb_bytes; diff --git a/trunk/src/kernel/srs_kernel_buffer.hpp b/trunk/src/kernel/srs_kernel_buffer.hpp index 3fdb7e74b..532375f8d 100644 --- a/trunk/src/kernel/srs_kernel_buffer.hpp +++ b/trunk/src/kernel/srs_kernel_buffer.hpp @@ -31,6 +31,24 @@ class SrsBuffer; +// Encoder. +class ISrsEncoder +{ +public: + ISrsEncoder(); + virtual ~ISrsEncoder(); +public: + /** + * get the number of bytes to code to. + */ + // TODO: FIXME: change to uint64_t. + virtual int nb_bytes() = 0; + /** + * encode object to bytes in SrsBuffer. + */ + virtual srs_error_t encode(SrsBuffer* buf) = 0; +}; + /** * the srs codec, to code and decode object with bytes: * code: to encode/serialize object to bytes in buffer, @@ -56,21 +74,11 @@ class SrsBuffer; * @remark protocol or amf0 or json should implements this interface. */ // TODO: FIXME: protocol, amf0, json should implements it. -class ISrsCodec +class ISrsCodec : public ISrsEncoder { public: ISrsCodec(); virtual ~ISrsCodec(); -public: - /** - * get the number of bytes to code to. - */ - // TODO: FIXME: change to uint64_t. - virtual int nb_bytes() = 0; - /** - * encode object to bytes in SrsBuffer. - */ - virtual srs_error_t encode(SrsBuffer* buf) = 0; public: /** * decode object from bytes in SrsBuffer. @@ -105,7 +113,8 @@ public: * get data of stream, set by initialize. * current bytes = data() + pos() */ - virtual char* data(); + inline char* data() { return bytes; } + inline char* head() { return p; } /** * the total stream size, set by initialize. * left bytes = size() - pos(). diff --git a/trunk/src/kernel/srs_kernel_codec.cpp b/trunk/src/kernel/srs_kernel_codec.cpp index 2f6c14bff..e0789c236 100644 --- a/trunk/src/kernel/srs_kernel_codec.cpp +++ b/trunk/src/kernel/srs_kernel_codec.cpp @@ -32,6 +32,7 @@ using namespace std; #include #include #include +#include string srs_video_codec_id2str(SrsVideoCodecId codec) { @@ -364,12 +365,58 @@ SrsSample::SrsSample() { size = 0; bytes = NULL; + bframe = false; } SrsSample::~SrsSample() { } +srs_error_t SrsSample::parse_bframe() +{ + srs_error_t err = srs_success; + + uint8_t header = bytes[0]; + SrsAvcNaluType nal_type = (SrsAvcNaluType)(header & kNalTypeMask); + + if (nal_type != SrsAvcNaluTypeNonIDR && nal_type != SrsAvcNaluTypeDataPartitionA && nal_type != SrsAvcNaluTypeIDR) { + return err; + } + + SrsBuffer* stream = new SrsBuffer(bytes, size); + SrsAutoFree(SrsBuffer, stream); + + // Skip nalu header. + stream->skip(1); + + SrsBitBuffer bitstream(stream); + int32_t first_mb_in_slice = 0; + if ((err = srs_avc_nalu_read_uev(&bitstream, first_mb_in_slice)) != srs_success) { + return srs_error_wrap(err, "nalu read uev"); + } + + int32_t slice_type_v = 0; + if ((err = srs_avc_nalu_read_uev(&bitstream, slice_type_v)) != srs_success) { + return srs_error_wrap(err, "nalu read uev"); + } + SrsAvcSliceType slice_type = (SrsAvcSliceType)slice_type_v; + + if (slice_type == SrsAvcSliceTypeB || slice_type == SrsAvcSliceTypeB1) { + bframe = true; + srs_verbose("nal_type=%d, slice type=%d", nal_type, slice_type); + } + + return err; +} + +SrsSample* SrsSample::copy() +{ + SrsSample* p = new SrsSample(); + p->bytes = bytes; + p->size = size; + return p; +} + SrsCodecConfig::SrsCodecConfig() { } @@ -458,6 +505,7 @@ srs_error_t SrsFrame::add_sample(char* bytes, int size) SrsSample* sample = &samples[nb_samples++]; sample->bytes = bytes; sample->size = size; + sample->bframe = false; return err; } @@ -488,6 +536,13 @@ SrsVideoFrame::~SrsVideoFrame() { } +srs_error_t SrsVideoFrame::initialize(SrsCodecConfig* c) +{ + first_nalu_type = SrsAvcNaluTypeForbidden; + has_idr = has_sps_pps = has_aud = false; + return SrsFrame::initialize(c); +} + srs_error_t SrsVideoFrame::add_sample(char* bytes, int size) { srs_error_t err = srs_success; diff --git a/trunk/src/kernel/srs_kernel_codec.hpp b/trunk/src/kernel/srs_kernel_codec.hpp index c820cbeef..9c74e1387 100644 --- a/trunk/src/kernel/srs_kernel_codec.hpp +++ b/trunk/src/kernel/srs_kernel_codec.hpp @@ -532,11 +532,18 @@ class SrsSample public: // The size of unit. int size; - // The ptr of unit, user must manage it. + // The ptr of unit, user must free it. char* bytes; + // Whether is B frame. + bool bframe; public: SrsSample(); - virtual ~SrsSample(); + ~SrsSample(); +public: + // If we need to know whether sample is bframe, we have to parse the NALU payload. + srs_error_t parse_bframe(); + // Copy sample, share the bytes pointer. + SrsSample* copy(); }; /** @@ -703,6 +710,8 @@ public: SrsVideoFrame(); virtual ~SrsVideoFrame(); public: + // Initialize the frame, to parse sampels. + virtual srs_error_t initialize(SrsCodecConfig* c); // Add the sample without ANNEXB or IBMF header, or RAW AAC or MP3 data. virtual srs_error_t add_sample(char* bytes, int size); public: diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index 5a6b8c6e5..c5421d640 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -422,7 +422,7 @@ public: }; // Error helpers, should use these functions to new or wrap an error. -#define srs_success SrsCplxError::success() +#define srs_success 0 // SrsCplxError::success() #define srs_error_new(ret, fmt, ...) SrsCplxError::create(__FUNCTION__, __FILE__, __LINE__, ret, fmt, ##__VA_ARGS__) #define srs_error_wrap(err, fmt, ...) SrsCplxError::wrap(__FUNCTION__, __FILE__, __LINE__, err, fmt, ##__VA_ARGS__) #define srs_error_copy(err) SrsCplxError::copy(err) diff --git a/trunk/src/kernel/srs_kernel_flv.cpp b/trunk/src/kernel/srs_kernel_flv.cpp index f24dc2047..eead02374 100644 --- a/trunk/src/kernel/srs_kernel_flv.cpp +++ b/trunk/src/kernel/srs_kernel_flv.cpp @@ -194,8 +194,11 @@ srs_error_t SrsCommonMessage::create(SrsMessageHeader* pheader, char* body, int return srs_success; } -SrsSharedMessageHeader::SrsSharedMessageHeader() : payload_length(0), message_type(0), perfer_cid(0) +SrsSharedMessageHeader::SrsSharedMessageHeader() { + payload_length = 0; + message_type = 0; + perfer_cid = 0; } SrsSharedMessageHeader::~SrsSharedMessageHeader() @@ -207,6 +210,16 @@ SrsSharedPtrMessage::SrsSharedPtrPayload::SrsSharedPtrPayload() payload = NULL; size = 0; shared_count = 0; + +#ifdef SRS_AUTO_RTC + samples = NULL; + nn_samples = 0; + has_idr = false; + + extra_payloads = NULL; + nn_extra_payloads = 0; + nn_max_extra_payloads = 0; +#endif } SrsSharedPtrMessage::SrsSharedPtrPayload::~SrsSharedPtrPayload() @@ -215,6 +228,17 @@ SrsSharedPtrMessage::SrsSharedPtrPayload::~SrsSharedPtrPayload() srs_memory_unwatch(payload); #endif srs_freepa(payload); + +#ifdef SRS_AUTO_RTC + srs_freepa(samples); + + for (int i = 0; i < nn_extra_payloads; i++) { + SrsSample* p = extra_payloads + i; + srs_freep(p->bytes); + } + srs_freepa(extra_payloads); + nn_extra_payloads = 0; +#endif } SrsSharedPtrMessage::SrsSharedPtrMessage() : timestamp(0), stream_id(0), size(0), payload(NULL) @@ -231,12 +255,6 @@ SrsSharedPtrMessage::~SrsSharedPtrMessage() ptr->shared_count--; } } - -#ifdef SRS_AUTO_RTC - for (int i = 0; i < (int)rtp_packets.size(); ++i) { - srs_freep(rtp_packets[i]); - } -#endif } srs_error_t SrsSharedPtrMessage::create(SrsCommonMessage* msg) @@ -355,19 +373,30 @@ SrsSharedPtrMessage* SrsSharedPtrMessage::copy() copy->payload = ptr->payload; copy->size = ptr->size; -#ifdef SRS_AUTO_RTC - for (int i = 0; i < (int)rtp_packets.size(); ++i) { - copy->rtp_packets.push_back(rtp_packets[i]->copy()); - } -#endif - return copy; } #ifdef SRS_AUTO_RTC -void SrsSharedPtrMessage::set_rtp_packets(const std::vector& pkts) +void SrsSharedPtrMessage::set_extra_payloads(SrsSample* payloads, int nn_payloads) { - rtp_packets = pkts; + srs_assert(nn_payloads); + srs_assert(!ptr->extra_payloads); + + ptr->nn_extra_payloads = nn_payloads; + + ptr->extra_payloads = new SrsSample[nn_payloads]; + memcpy((void*)ptr->extra_payloads, payloads, nn_payloads * sizeof(SrsSample)); +} + +void SrsSharedPtrMessage::set_samples(SrsSample* samples, int nn_samples) +{ + srs_assert(nn_samples); + srs_assert(!ptr->samples); + + ptr->nn_samples = nn_samples; + + ptr->samples = new SrsSample[nn_samples]; + memcpy((void*)ptr->samples, samples, nn_samples * sizeof(SrsSample)); } #endif diff --git a/trunk/src/kernel/srs_kernel_flv.hpp b/trunk/src/kernel/srs_kernel_flv.hpp index 23fa0baf0..8cfda3557 100644 --- a/trunk/src/kernel/srs_kernel_flv.hpp +++ b/trunk/src/kernel/srs_kernel_flv.hpp @@ -40,6 +40,7 @@ class ISrsReader; class SrsFileReader; class SrsPacket; class SrsRtpSharedPacket; +class SrsSample; #define SRS_FLV_TAG_HEADER_SIZE 11 #define SRS_FLV_PREVIOUS_TAG_SIZE 4 @@ -288,10 +289,6 @@ public: // video/audio packet use raw bytes, no video/audio packet. char* payload; -#ifdef SRS_AUTO_RTC - std::vector rtp_packets; -#endif - private: class SrsSharedPtrPayload { @@ -305,6 +302,23 @@ private: int size; // The reference count int shared_count; +#ifdef SRS_AUTO_RTC + public: + // For RTC video, we need to know the NALU structures, + // because the RTP STAP-A or FU-A based on NALU. + SrsSample* samples; + int nn_samples; + // For RTC video, whether NALUs has IDR. + bool has_idr; + public: + // For RTC audio, we may need to transcode AAC to opus, + // so there must be an extra payloads, which is transformed from payload. + SrsSample* extra_payloads; + int nn_extra_payloads; + // The max size payload in extras. + // @remark For GSO to fast guess the best padding. + int nn_max_extra_payloads; +#endif public: SrsSharedPtrPayload(); virtual ~SrsSharedPtrPayload(); @@ -348,7 +362,21 @@ public: virtual SrsSharedPtrMessage* copy(); public: #ifdef SRS_AUTO_RTC - virtual void set_rtp_packets(const std::vector& pkts); + // Set extra samples, for example, when we transcode an AAC audio packet to OPUS, + // we may get more than one OPUS packets, we set these OPUS packets in extra payloads. + void set_extra_payloads(SrsSample* payloads, int nn_payloads); + int nn_extra_payloads() { return ptr->nn_extra_payloads; } + SrsSample* extra_payloads() { return ptr->extra_payloads; } + // The max extra payload size. + void set_max_extra_payload(int v) { ptr->nn_max_extra_payloads = v; } + int nn_max_extra_payloads() { return ptr->nn_max_extra_payloads; } + // Whether samples has idr. + bool has_idr() { return ptr->has_idr; } + void set_has_idr(bool v) { ptr->has_idr = v; } + // Set samples, each sample points to the address of payload. + void set_samples(SrsSample* samples, int nn_samples); + int nn_samples() { return ptr->nn_samples; } + SrsSample* samples() { return ptr->samples; } #endif }; diff --git a/trunk/src/kernel/srs_kernel_rtp.cpp b/trunk/src/kernel/srs_kernel_rtp.cpp index 59ad7ca93..5c23cbd34 100644 --- a/trunk/src/kernel/srs_kernel_rtp.cpp +++ b/trunk/src/kernel/srs_kernel_rtp.cpp @@ -32,6 +32,15 @@ using namespace std; #include #include +// @see: https://tools.ietf.org/html/rfc6184#section-5.2 +const uint8_t kStapA = 24; + +// @see: https://tools.ietf.org/html/rfc6184#section-5.2 +const uint8_t kFuA = 28; + +// @see: https://tools.ietf.org/html/rfc6184#section-5.8 +const uint8_t kStart = 0x80; // Fu-header start bit +const uint8_t kEnd = 0x40; // Fu-header end bit SrsRtpHeader::SrsRtpHeader() { @@ -46,27 +55,15 @@ SrsRtpHeader::SrsRtpHeader() extension_length = 0; } -SrsRtpHeader::SrsRtpHeader(const SrsRtpHeader& rhs) +void SrsRtpHeader::reset() { - operator=(rhs); -} - -SrsRtpHeader& SrsRtpHeader::operator=(const SrsRtpHeader& rhs) -{ - padding = rhs.padding; - extension = rhs.extension; - cc = rhs.cc; - marker = rhs.marker; - payload_type = rhs.payload_type; - sequence = rhs.sequence; - timestamp = rhs.timestamp; - ssrc = rhs.ssrc; - for (size_t i = 0; i < cc; ++i) { - csrc[i] = rhs.csrc[i]; - } - extension_length = rhs.extension_length; - - return *this; + // We only reset the optional fields, the required field such as ssrc + // will always be set by user. + padding = false; + extension = false; + cc = 0; + marker = false; + extension_length = 0; } SrsRtpHeader::~SrsRtpHeader() @@ -77,7 +74,7 @@ srs_error_t SrsRtpHeader::decode(SrsBuffer* stream) { srs_error_t err = srs_success; - // TODO: + // TODO: FIXME: Implements it. return err; } @@ -86,30 +83,63 @@ srs_error_t SrsRtpHeader::encode(SrsBuffer* stream) { srs_error_t err = srs_success; - uint8_t first = 0x80 | cc; + // Encode the RTP fix header, 12bytes. + // @see https://tools.ietf.org/html/rfc1889#section-5.1 + char* op = stream->head(); + char* p = op; + + // The version, padding, extension and cc, total 1 byte. + uint8_t v = 0x80 | cc; if (padding) { - first |= 0x40; + v |= 0x20; } if (extension) { - first |= 0x10; + v |= 0x10; } - stream->write_1bytes(first); - uint8_t second = payload_type; + *p++ = v; + + // The marker and payload type, total 1 byte. + v = payload_type; if (marker) { - payload_type |= kRtpMarker; + v |= kRtpMarker; } - stream->write_1bytes(second); - stream->write_2bytes(sequence); - stream->write_4bytes(timestamp); - stream->write_4bytes(ssrc); + *p++ = v; + + // The sequence number, 2 bytes. + char* pp = (char*)&sequence; + *p++ = pp[1]; + *p++ = pp[0]; + + // The timestamp, 4 bytes. + pp = (char*)×tamp; + *p++ = pp[3]; + *p++ = pp[2]; + *p++ = pp[1]; + *p++ = pp[0]; + + // The SSRC, 4 bytes. + pp = (char*)&ssrc; + *p++ = pp[3]; + *p++ = pp[2]; + *p++ = pp[1]; + *p++ = pp[0]; + + // The CSRC list: 0 to 15 items, each is 4 bytes. for (size_t i = 0; i < cc; ++i) { - stream->write_4bytes(csrc[i]); + pp = (char*)&csrc[i]; + *p++ = pp[3]; + *p++ = pp[2]; + *p++ = pp[1]; + *p++ = pp[0]; } // TODO: Write exteinsion field. if (extension) { } + // Consume the data. + stream->skip(p - op); + return err; } @@ -118,29 +148,408 @@ size_t SrsRtpHeader::header_size() return kRtpHeaderFixedSize + cc * 4 + (extension ? (extension_length + 1) * 4 : 0); } -void SrsRtpHeader::set_marker(bool marker) +SrsRtpPacket2::SrsRtpPacket2() { - this->marker = marker; + payload = NULL; + padding = 0; + + cache_raw = new SrsRtpRawPayload(); + cache_fua = new SrsRtpFUAPayload2(); + cache_payload = 0; } -void SrsRtpHeader::set_payload_type(uint8_t payload_type) +SrsRtpPacket2::~SrsRtpPacket2() { - this->payload_type = payload_type; + // We may use the cache as payload. + if (payload == cache_raw || payload == cache_fua) { + payload = NULL; + } + + srs_freep(payload); + srs_freep(cache_raw); + srs_freep(cache_fua); } -void SrsRtpHeader::set_sequence(uint16_t sequence) +void SrsRtpPacket2::set_padding(int size) { - this->sequence = sequence; + rtp_header.set_padding(size > 0); + if (cache_payload) { + cache_payload += size - padding; + } + padding = size; } -void SrsRtpHeader::set_timestamp(int64_t timestamp) +void SrsRtpPacket2::add_padding(int size) { - this->timestamp = timestamp; + rtp_header.set_padding(padding + size > 0); + if (cache_payload) { + cache_payload += size; + } + padding += size; } -void SrsRtpHeader::set_ssrc(uint32_t ssrc) +void SrsRtpPacket2::reset() { - this->ssrc = ssrc; + rtp_header.reset(); + padding = 0; + cache_payload = 0; + + // We may use the cache as payload. + if (payload == cache_raw || payload == cache_fua) { + payload = NULL; + } + srs_freep(payload); +} + +SrsRtpRawPayload* SrsRtpPacket2::reuse_raw() +{ + payload = cache_raw; + return cache_raw; +} + +SrsRtpFUAPayload2* SrsRtpPacket2::reuse_fua() +{ + payload = cache_fua; + return cache_fua; +} + +int SrsRtpPacket2::nb_bytes() +{ + if (!cache_payload) { + cache_payload = rtp_header.header_size() + (payload? payload->nb_bytes():0) + padding; + } + return cache_payload; +} + +srs_error_t SrsRtpPacket2::encode(SrsBuffer* buf) +{ + srs_error_t err = srs_success; + + if ((err = rtp_header.encode(buf)) != srs_success) { + return srs_error_wrap(err, "rtp header"); + } + + if (payload && (err = payload->encode(buf)) != srs_success) { + return srs_error_wrap(err, "encode payload"); + } + + if (padding > 0) { + if (!buf->require(padding)) { + return srs_error_new(ERROR_RTC_RTP_MUXER, "requires %d bytes", padding); + } + memset(buf->data() + buf->pos(), padding, padding); + buf->skip(padding); + } + + return err; +} + +SrsRtpRawPayload::SrsRtpRawPayload() +{ + payload = NULL; + nn_payload = 0; +} + +SrsRtpRawPayload::~SrsRtpRawPayload() +{ +} + +int SrsRtpRawPayload::nb_bytes() +{ + return nn_payload; +} + +srs_error_t SrsRtpRawPayload::encode(SrsBuffer* buf) +{ + if (nn_payload <= 0) { + return srs_success; + } + + if (!buf->require(nn_payload)) { + return srs_error_new(ERROR_RTC_RTP_MUXER, "requires %d bytes", nn_payload); + } + + buf->write_bytes(payload, nn_payload); + + return srs_success; +} + +SrsRtpRawNALUs::SrsRtpRawNALUs() +{ + cursor = 0; + nn_bytes = 0; +} + +SrsRtpRawNALUs::~SrsRtpRawNALUs() +{ + if (true) { + int nn_nalus = (int)nalus.size(); + for (int i = 0; i < nn_nalus; i++) { + SrsSample* p = nalus[i]; + srs_freep(p); + } + } +} + +void SrsRtpRawNALUs::push_back(SrsSample* sample) +{ + if (sample->size <= 0) { + return; + } + + if (!nalus.empty()) { + SrsSample* p = new SrsSample(); + p->bytes = (char*)"\0\0\1"; + p->size = 3; + nn_bytes += 3; + nalus.push_back(p); + } + + nn_bytes += sample->size; + nalus.push_back(sample); +} + +uint8_t SrsRtpRawNALUs::skip_first_byte() +{ + srs_assert (cursor >= 0 && nn_bytes > 0 && cursor < nn_bytes); + cursor++; + return uint8_t(nalus[0]->bytes[0]); +} + +srs_error_t SrsRtpRawNALUs::read_samples(vector& samples, int packet_size) +{ + if (cursor + packet_size < 0 || cursor + packet_size > nn_bytes) { + return srs_error_new(ERROR_RTC_RTP_MUXER, "cursor=%d, max=%d, size=%d", cursor, nn_bytes, packet_size); + } + + int pos = cursor; + cursor += packet_size; + int left = packet_size; + + int nn_nalus = (int)nalus.size(); + for (int i = 0; left > 0 && i < nn_nalus; i++) { + SrsSample* p = nalus[i]; + + // Ignore previous consumed samples. + if (pos && pos - p->size >= 0) { + pos -= p->size; + continue; + } + + // Now, we are working at the sample. + int nn = srs_min(left, p->size - pos); + srs_assert(nn > 0); + + SrsSample* sample = new SrsSample(); + samples.push_back(sample); + + sample->bytes = p->bytes + pos; + sample->size = nn; + + left -= nn; + pos = 0; + } + + return srs_success; +} + +int SrsRtpRawNALUs::nb_bytes() +{ + int size = 0; + + int nn_nalus = (int)nalus.size(); + for (int i = 0; i < nn_nalus; i++) { + SrsSample* p = nalus[i]; + size += p->size; + } + + return size; +} + +srs_error_t SrsRtpRawNALUs::encode(SrsBuffer* buf) +{ + int nn_nalus = (int)nalus.size(); + for (int i = 0; i < nn_nalus; i++) { + SrsSample* p = nalus[i]; + + if (!buf->require(p->size)) { + return srs_error_new(ERROR_RTC_RTP_MUXER, "requires %d bytes", p->size); + } + + buf->write_bytes(p->bytes, p->size); + } + + return srs_success; +} + +SrsRtpSTAPPayload::SrsRtpSTAPPayload() +{ + nri = (SrsAvcNaluType)0; +} + +SrsRtpSTAPPayload::~SrsRtpSTAPPayload() +{ + int nn_nalus = (int)nalus.size(); + for (int i = 0; i < nn_nalus; i++) { + SrsSample* p = nalus[i]; + srs_freep(p); + } +} + +int SrsRtpSTAPPayload::nb_bytes() +{ + int size = 1; + + int nn_nalus = (int)nalus.size(); + for (int i = 0; i < nn_nalus; i++) { + SrsSample* p = nalus[i]; + size += 2 + p->size; + } + + return size; +} + +srs_error_t SrsRtpSTAPPayload::encode(SrsBuffer* buf) +{ + if (!buf->require(1)) { + return srs_error_new(ERROR_RTC_RTP_MUXER, "requires %d bytes", 1); + } + + // STAP header, RTP payload format for aggregation packets + // @see https://tools.ietf.org/html/rfc6184#section-5.7 + uint8_t v = kStapA; + v |= (nri & (~kNalTypeMask)); + buf->write_1bytes(v); + + // NALUs. + int nn_nalus = (int)nalus.size(); + for (int i = 0; i < nn_nalus; i++) { + SrsSample* p = nalus[i]; + + if (!buf->require(2 + p->size)) { + return srs_error_new(ERROR_RTC_RTP_MUXER, "requires %d bytes", 2 + p->size); + } + + buf->write_2bytes(p->size); + buf->write_bytes(p->bytes, p->size); + } + + return srs_success; +} + +SrsRtpFUAPayload::SrsRtpFUAPayload() +{ + start = end = false; + nri = nalu_type = (SrsAvcNaluType)0; +} + +SrsRtpFUAPayload::~SrsRtpFUAPayload() +{ + int nn_nalus = (int)nalus.size(); + for (int i = 0; i < nn_nalus; i++) { + SrsSample* p = nalus[i]; + srs_freep(p); + } +} + +int SrsRtpFUAPayload::nb_bytes() +{ + int size = 2; + + int nn_nalus = (int)nalus.size(); + for (int i = 0; i < nn_nalus; i++) { + SrsSample* p = nalus[i]; + size += p->size; + } + + return size; +} + +srs_error_t SrsRtpFUAPayload::encode(SrsBuffer* buf) +{ + if (!buf->require(2)) { + return srs_error_new(ERROR_RTC_RTP_MUXER, "requires %d bytes", 1); + } + + // FU indicator, @see https://tools.ietf.org/html/rfc6184#section-5.8 + uint8_t fu_indicate = kFuA; + fu_indicate |= (nri & (~kNalTypeMask)); + buf->write_1bytes(fu_indicate); + + // FU header, @see https://tools.ietf.org/html/rfc6184#section-5.8 + uint8_t fu_header = nalu_type; + if (start) { + fu_header |= kStart; + } + if (end) { + fu_header |= kEnd; + } + buf->write_1bytes(fu_header); + + // FU payload, @see https://tools.ietf.org/html/rfc6184#section-5.8 + int nn_nalus = (int)nalus.size(); + for (int i = 0; i < nn_nalus; i++) { + SrsSample* p = nalus[i]; + + if (!buf->require(p->size)) { + return srs_error_new(ERROR_RTC_RTP_MUXER, "requires %d bytes", p->size); + } + + buf->write_bytes(p->bytes, p->size); + } + + return srs_success; +} + +SrsRtpFUAPayload2::SrsRtpFUAPayload2() +{ + start = end = false; + nri = nalu_type = (SrsAvcNaluType)0; + + payload = NULL; + size = 0; +} + +SrsRtpFUAPayload2::~SrsRtpFUAPayload2() +{ +} + +int SrsRtpFUAPayload2::nb_bytes() +{ + return 2 + size; +} + +srs_error_t SrsRtpFUAPayload2::encode(SrsBuffer* buf) +{ + if (!buf->require(2 + size)) { + return srs_error_new(ERROR_RTC_RTP_MUXER, "requires %d bytes", 1); + } + + // Fast encoding. + char* p = buf->head(); + + // FU indicator, @see https://tools.ietf.org/html/rfc6184#section-5.8 + uint8_t fu_indicate = kFuA; + fu_indicate |= (nri & (~kNalTypeMask)); + *p++ = fu_indicate; + + // FU header, @see https://tools.ietf.org/html/rfc6184#section-5.8 + uint8_t fu_header = nalu_type; + if (start) { + fu_header |= kStart; + } + if (end) { + fu_header |= kEnd; + } + *p++ = fu_header; + + // FU payload, @see https://tools.ietf.org/html/rfc6184#section-5.8 + memcpy(p, payload, size); + + // Consume bytes. + buf->skip(2 + size); + + return srs_success; } SrsRtpSharedPacket::SrsRtpSharedPacketPayload::SrsRtpSharedPacketPayload() diff --git a/trunk/src/kernel/srs_kernel_rtp.hpp b/trunk/src/kernel/srs_kernel_rtp.hpp index 396cb2687..349c388a5 100644 --- a/trunk/src/kernel/srs_kernel_rtp.hpp +++ b/trunk/src/kernel/srs_kernel_rtp.hpp @@ -26,12 +26,20 @@ #include +#include +#include + #include const int kRtpHeaderFixedSize = 12; const uint8_t kRtpMarker = 0x80; +// H.264 nalu header type mask. +const uint8_t kNalTypeMask = 0x1F; + class SrsBuffer; +class SrsRtpRawPayload; +class SrsRtpFUAPayload2; class SrsRtpHeader { @@ -42,7 +50,7 @@ private: bool marker; uint8_t payload_type; uint16_t sequence; - int64_t timestamp; + int32_t timestamp; uint32_t ssrc; uint32_t csrc[15]; uint16_t extension_length; @@ -50,24 +58,158 @@ private: public: SrsRtpHeader(); virtual ~SrsRtpHeader(); - SrsRtpHeader(const SrsRtpHeader& rhs); - SrsRtpHeader& operator=(const SrsRtpHeader& rhs); + void reset(); public: srs_error_t decode(SrsBuffer* stream); srs_error_t encode(SrsBuffer* stream); public: size_t header_size(); public: - void set_marker(bool marker); + inline void set_marker(bool v) { marker = v; } bool get_marker() const { return marker; } - void set_payload_type(uint8_t payload_type); + inline void set_payload_type(uint8_t v) { payload_type = v; } uint8_t get_payload_type() const { return payload_type; } - void set_sequence(uint16_t sequence); + inline void set_sequence(uint16_t v) { sequence = v; } uint16_t get_sequence() const { return sequence; } - void set_timestamp(int64_t timestamp); + inline void set_timestamp(int64_t v) { timestamp = (uint32_t)v; } int64_t get_timestamp() const { return timestamp; } - void set_ssrc(uint32_t ssrc); + inline void set_ssrc(uint32_t v) { ssrc = v; } uint32_t get_ssrc() const { return ssrc; } + inline void set_padding(bool v) { padding = v; } +}; + +class SrsRtpPacket2 +{ +public: + SrsRtpHeader rtp_header; + ISrsEncoder* payload; + int padding; +private: + SrsRtpRawPayload* cache_raw; + SrsRtpFUAPayload2* cache_fua; + int cache_payload; +public: + SrsRtpPacket2(); + virtual ~SrsRtpPacket2(); +public: + // Set the padding of RTP packet. + void set_padding(int size); + // Increase the padding of RTP packet. + void add_padding(int size); + // Reset RTP packet. + void reset(); + // Reuse the cached raw message as payload. + SrsRtpRawPayload* reuse_raw(); + // Reuse the cached fua message as payload. + SrsRtpFUAPayload2* reuse_fua(); +// interface ISrsEncoder +public: + virtual int nb_bytes(); + virtual srs_error_t encode(SrsBuffer* buf); +}; + +// Single payload data. +class SrsRtpRawPayload : public ISrsEncoder +{ +public: + // The RAW payload, directly point to the shared memory. + // @remark We only refer to the memory, user must free its bytes. + char* payload; + int nn_payload; +public: + SrsRtpRawPayload(); + virtual ~SrsRtpRawPayload(); +// interface ISrsEncoder +public: + virtual int nb_bytes(); + virtual srs_error_t encode(SrsBuffer* buf); +}; + +// Multiple NALUs, automatically insert 001 between NALUs. +class SrsRtpRawNALUs : public ISrsEncoder +{ +private: + // We will manage the samples, but the sample itself point to the shared memory. + std::vector nalus; + int nn_bytes; + int cursor; +public: + SrsRtpRawNALUs(); + virtual ~SrsRtpRawNALUs(); +public: + void push_back(SrsSample* sample); +public: + uint8_t skip_first_byte(); + // We will manage the returned samples, if user want to manage it, please copy it. + srs_error_t read_samples(std::vector& samples, int packet_size); +// interface ISrsEncoder +public: + virtual int nb_bytes(); + virtual srs_error_t encode(SrsBuffer* buf); +}; + +// STAP-A, for multiple NALUs. +class SrsRtpSTAPPayload : public ISrsEncoder +{ +public: + // The NRI in NALU type. + SrsAvcNaluType nri; + // The NALU samples, we will manage the samples. + // @remark We only refer to the memory, user must free its bytes. + std::vector nalus; +public: + SrsRtpSTAPPayload(); + virtual ~SrsRtpSTAPPayload(); +// interface ISrsEncoder +public: + virtual int nb_bytes(); + virtual srs_error_t encode(SrsBuffer* buf); +}; + +// FU-A, for one NALU with multiple fragments. +// With more than one payload. +class SrsRtpFUAPayload : public ISrsEncoder +{ +public: + // The NRI in NALU type. + SrsAvcNaluType nri; + // The FUA header. + bool start; + bool end; + SrsAvcNaluType nalu_type; + // The NALU samples, we manage the samples. + // @remark We only refer to the memory, user must free its bytes. + std::vector nalus; +public: + SrsRtpFUAPayload(); + virtual ~SrsRtpFUAPayload(); +// interface ISrsEncoder +public: + virtual int nb_bytes(); + virtual srs_error_t encode(SrsBuffer* buf); +}; + +// FU-A, for one NALU with multiple fragments. +// With only one payload. +class SrsRtpFUAPayload2 : public ISrsEncoder +{ +public: + // The NRI in NALU type. + SrsAvcNaluType nri; + // The FUA header. + bool start; + bool end; + SrsAvcNaluType nalu_type; + // The payload and size, + char* payload; + int size; +public: + SrsRtpFUAPayload2(); + virtual ~SrsRtpFUAPayload2(); +// interface ISrsEncoder +public: + virtual int nb_bytes(); + virtual srs_error_t encode(SrsBuffer* buf); }; class SrsRtpSharedPacket diff --git a/trunk/src/main/srs_main_server.cpp b/trunk/src/main/srs_main_server.cpp index e4641285c..8195d0020 100644 --- a/trunk/src/main/srs_main_server.cpp +++ b/trunk/src/main/srs_main_server.cpp @@ -209,7 +209,7 @@ srs_error_t do_main(int argc, char** argv) int main(int argc, char** argv) { srs_error_t err = do_main(argc, argv); - + if (err != srs_success) { srs_error("Failed, %s", srs_error_desc(err).c_str()); } diff --git a/trunk/src/protocol/srs_rtmp_stack.cpp b/trunk/src/protocol/srs_rtmp_stack.cpp index 8d3a2378e..4b11e5ec3 100644 --- a/trunk/src/protocol/srs_rtmp_stack.cpp +++ b/trunk/src/protocol/srs_rtmp_stack.cpp @@ -548,7 +548,8 @@ srs_error_t SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msg // Notify about perf stat. if (perf) { - perf->perf_mw_on_msgs(nb_msgs_merged_written, bytes_msgs_merged_written, iov_index); + perf->perf_on_msgs(nb_msgs_merged_written); + perf->perf_on_writev_iovs(iov_index); nb_msgs_merged_written = 0; bytes_msgs_merged_written = 0; } @@ -576,7 +577,8 @@ srs_error_t SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msg // Notify about perf stat. if (perf) { - perf->perf_mw_on_msgs(nb_msgs_merged_written, bytes_msgs_merged_written, iov_index); + perf->perf_on_msgs(nb_msgs_merged_written); + perf->perf_on_writev_iovs(iov_index); nb_msgs_merged_written = 0; bytes_msgs_merged_written = 0; } @@ -627,11 +629,6 @@ srs_error_t SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msg if ((er = skt->writev(iovs, 2, NULL)) != srs_success) { return srs_error_wrap(err, "writev"); } - - // Notify about perf stat. - if (perf) { - perf->perf_mw_on_packets(1, payload_size, 2); - } } } @@ -4539,6 +4536,11 @@ srs_error_t SrsOnMetaDataPacket::decode(SrsBuffer* stream) return srs_error_wrap(err, "name"); } } + + // Allows empty body metadata. + if (stream->empty()) { + return err; + } // the metadata maybe object or ecma array SrsAmf0Any* any = NULL; diff --git a/trunk/src/protocol/srs_rtmp_stack.hpp b/trunk/src/protocol/srs_rtmp_stack.hpp index 470347cac..6f311383f 100644 --- a/trunk/src/protocol/srs_rtmp_stack.hpp +++ b/trunk/src/protocol/srs_rtmp_stack.hpp @@ -155,11 +155,9 @@ public: virtual ~ISrsProtocolPerf(); public: // Stat for packets merged written, nb_msgs is the number of RTMP messages, - // bytes_msgs is the total bytes of RTMP messages, nb_iovs is the total number of iovec. - virtual void perf_mw_on_msgs(int nb_msgs, int bytes_msgs, int nb_iovs) = 0; - // Stat for packets merged written, nb_pkts is the number of or chunk packets, - // bytes_pkts is the total bytes of or chunk packets, nb_iovs is the total number of iovec. - virtual void perf_mw_on_packets(int nb_pkts, int bytes_pkts, int nb_iovs) = 0; + virtual void perf_on_msgs(int nb_msgs) = 0; + // Stat for TCP writev, nb_iovs is the total number of iovec. + virtual void perf_on_writev_iovs(int nb_iovs) = 0; }; // The protocol provides the rtmp-message-protocol services, diff --git a/trunk/src/service/srs_service_st.cpp b/trunk/src/service/srs_service_st.cpp index 4e1e1bc01..da54a5dae 100644 --- a/trunk/src/service/srs_service_st.cpp +++ b/trunk/src/service/srs_service_st.cpp @@ -436,6 +436,8 @@ int srs_sendmmsg(srs_netfd_t stfd, struct mmsghdr *msgvec, unsigned int vlen, in } msgvec->msg_len = r0; #else + msgvec->msg_len = 0; + int tolen = (int)msgvec->msg_hdr.msg_namelen; const struct sockaddr* to = (const struct sockaddr*)msgvec->msg_hdr.msg_name; for (int i = 0; i < (int)msgvec->msg_hdr.msg_iovlen; i++) { diff --git a/trunk/src/utest/srs_utest_avc.cpp b/trunk/src/utest/srs_utest_avc.cpp index 4aa8f037f..60b4bb29d 100644 --- a/trunk/src/utest/srs_utest_avc.cpp +++ b/trunk/src/utest/srs_utest_avc.cpp @@ -462,7 +462,7 @@ VOID TEST(SrsAVCTest, AACMuxSequenceHeader) codec.sound_rate = SrsAudioSampleRate44100; codec.sampling_frequency_index = 4; HELPER_ASSERT_SUCCESS(h.mux_sequence_header(&codec, sh)); - EXPECT_EQ(2, sh.length()); + EXPECT_EQ(2, (int)sh.length()); EXPECT_EQ(0x0a, (uint8_t)sh.at(0)); EXPECT_EQ(0x08, (uint8_t)sh.at(1)); } @@ -475,7 +475,7 @@ VOID TEST(SrsAVCTest, AACMuxSequenceHeader) codec.sound_rate = SrsAudioSampleRate22050; codec.sampling_frequency_index = 4; HELPER_ASSERT_SUCCESS(h.mux_sequence_header(&codec, sh)); - EXPECT_EQ(2, sh.length()); + EXPECT_EQ(2, (int)sh.length()); EXPECT_EQ(0x0a, (uint8_t)sh.at(0)); EXPECT_EQ(0x08, (uint8_t)sh.at(1)); } diff --git a/trunk/src/utest/srs_utest_config.cpp b/trunk/src/utest/srs_utest_config.cpp index 1d03cefda..ec6bef58f 100644 --- a/trunk/src/utest/srs_utest_config.cpp +++ b/trunk/src/utest/srs_utest_config.cpp @@ -2010,7 +2010,7 @@ VOID TEST(ConfigUnitTest, CheckDefaultValuesVhost) if (true) { HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF)); - EXPECT_EQ(0, conf.get_hls_dispose("")); + EXPECT_EQ(0, (int)conf.get_hls_dispose("")); EXPECT_EQ(10 * SRS_UTIME_SECONDS, conf.get_hls_fragment("")); EXPECT_EQ(60 * SRS_UTIME_SECONDS, conf.get_hls_window("")); @@ -2033,7 +2033,7 @@ VOID TEST(ConfigUnitTest, CheckDefaultValuesVhost) if (true) { HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF)); EXPECT_EQ(30 * SRS_UTIME_SECONDS, conf.get_queue_length("")); - EXPECT_EQ(0, conf.get_send_min_interval("")); + EXPECT_EQ(0, (int)conf.get_send_min_interval("")); HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "vhost v{play{queue_length 100;send_min_interval 10;}}")); EXPECT_EQ(100 * SRS_UTIME_SECONDS, conf.get_queue_length("v")); @@ -2042,7 +2042,7 @@ VOID TEST(ConfigUnitTest, CheckDefaultValuesVhost) if (true) { HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF)); - EXPECT_EQ(0, conf.get_vhost_http_remux_fast_cache("")); + EXPECT_EQ(0, (int)conf.get_vhost_http_remux_fast_cache("")); HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "vhost v{http_remux{fast_cache 10;}}")); EXPECT_EQ(10 * SRS_UTIME_SECONDS, conf.get_vhost_http_remux_fast_cache("v")); @@ -2768,19 +2768,19 @@ VOID TEST(ConfigMainTest, CheckStreamCaster) if (true) { MockSrsConfig conf; HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF)); - EXPECT_EQ(0, conf.get_stream_casters().size()); + EXPECT_EQ(0, (int)conf.get_stream_casters().size()); } if (true) { MockSrsConfig conf; HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "stream_caster;")); - EXPECT_EQ(1, conf.get_stream_casters().size()); + EXPECT_EQ(1, (int)conf.get_stream_casters().size()); } if (true) { MockSrsConfig conf; HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "stream_caster; stream_caster;")); - EXPECT_EQ(2, conf.get_stream_casters().size()); + EXPECT_EQ(2, (int)conf.get_stream_casters().size()); } if (true) { @@ -2788,7 +2788,7 @@ VOID TEST(ConfigMainTest, CheckStreamCaster) HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "stream_caster;")); vector arr = conf.get_stream_casters(); - ASSERT_EQ(1, arr.size()); + ASSERT_EQ(1, (int)arr.size()); EXPECT_FALSE(conf.get_stream_caster_enabled(arr.at(0))); } @@ -2798,7 +2798,7 @@ VOID TEST(ConfigMainTest, CheckStreamCaster) HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "stream_caster {enabled off;}")); vector arr = conf.get_stream_casters(); - ASSERT_EQ(1, arr.size()); + ASSERT_EQ(1, (int)arr.size()); EXPECT_FALSE(conf.get_stream_caster_enabled(arr.at(0))); } @@ -2808,7 +2808,7 @@ VOID TEST(ConfigMainTest, CheckStreamCaster) HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "stream_caster {enabled on;}")); vector arr = conf.get_stream_casters(); - ASSERT_EQ(1, arr.size()); + ASSERT_EQ(1, (int)arr.size()); EXPECT_TRUE(conf.get_stream_caster_enabled(arr.at(0))); } @@ -2818,7 +2818,7 @@ VOID TEST(ConfigMainTest, CheckStreamCaster) HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "stream_caster;")); vector arr = conf.get_stream_casters(); - ASSERT_EQ(1, arr.size()); + ASSERT_EQ(1, (int)arr.size()); EXPECT_TRUE(conf.get_stream_caster_output(arr.at(0)).empty()); } @@ -2828,7 +2828,7 @@ VOID TEST(ConfigMainTest, CheckStreamCaster) HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "stream_caster {output xxx;}")); vector arr = conf.get_stream_casters(); - ASSERT_EQ(1, arr.size()); + ASSERT_EQ(1, (int)arr.size()); EXPECT_STREQ("xxx", conf.get_stream_caster_output(arr.at(0)).c_str()); } @@ -2838,9 +2838,9 @@ VOID TEST(ConfigMainTest, CheckStreamCaster) HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "stream_caster;")); vector arr = conf.get_stream_casters(); - ASSERT_EQ(1, arr.size()); + ASSERT_EQ(1, (int)arr.size()); - EXPECT_EQ(0, conf.get_stream_caster_listen(arr.at(0))); + EXPECT_EQ(0, (int)conf.get_stream_caster_listen(arr.at(0))); } if (true) { @@ -2848,7 +2848,7 @@ VOID TEST(ConfigMainTest, CheckStreamCaster) HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "stream_caster {listen 8080;}")); vector arr = conf.get_stream_casters(); - ASSERT_EQ(1, arr.size()); + ASSERT_EQ(1, (int)arr.size()); EXPECT_EQ(8080, conf.get_stream_caster_listen(arr.at(0))); } @@ -2858,9 +2858,9 @@ VOID TEST(ConfigMainTest, CheckStreamCaster) HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "stream_caster;")); vector arr = conf.get_stream_casters(); - ASSERT_EQ(1, arr.size()); + ASSERT_EQ(1, (int)arr.size()); - EXPECT_EQ(0, conf.get_stream_caster_rtp_port_min(arr.at(0))); + EXPECT_EQ(0, (int)conf.get_stream_caster_rtp_port_min(arr.at(0))); } if (true) { @@ -2868,7 +2868,7 @@ VOID TEST(ConfigMainTest, CheckStreamCaster) HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "stream_caster {rtp_port_min 8080;}")); vector arr = conf.get_stream_casters(); - ASSERT_EQ(1, arr.size()); + ASSERT_EQ(1, (int)arr.size()); EXPECT_EQ(8080, conf.get_stream_caster_rtp_port_min(arr.at(0))); } @@ -2878,9 +2878,9 @@ VOID TEST(ConfigMainTest, CheckStreamCaster) HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "stream_caster;")); vector arr = conf.get_stream_casters(); - ASSERT_EQ(1, arr.size()); + ASSERT_EQ(1, (int)arr.size()); - EXPECT_EQ(0, conf.get_stream_caster_rtp_port_max(arr.at(0))); + EXPECT_EQ(0, (int)conf.get_stream_caster_rtp_port_max(arr.at(0))); } if (true) { @@ -2888,7 +2888,7 @@ VOID TEST(ConfigMainTest, CheckStreamCaster) HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "stream_caster {rtp_port_max 8080;}")); vector arr = conf.get_stream_casters(); - ASSERT_EQ(1, arr.size()); + ASSERT_EQ(1, (int)arr.size()); EXPECT_EQ(8080, conf.get_stream_caster_rtp_port_max(arr.at(0))); } @@ -2912,14 +2912,14 @@ VOID TEST(ConfigMainTest, CheckVhostConfig2) EXPECT_TRUE(conf.get_debug_srs_upnode("ossrs.net")); EXPECT_FALSE(conf.get_atc("ossrs.net")); EXPECT_FALSE(conf.get_atc_auto("ossrs.net")); - EXPECT_EQ(1, conf.get_time_jitter("ossrs.net")); + EXPECT_EQ(1, (int)conf.get_time_jitter("ossrs.net")); EXPECT_FALSE(conf.get_mix_correct("ossrs.net")); EXPECT_EQ(30 * SRS_UTIME_SECONDS, conf.get_queue_length("ossrs.net")); EXPECT_FALSE(conf.get_refer_enabled("ossrs.net")); EXPECT_TRUE(conf.get_refer_all("ossrs.net") == NULL); EXPECT_TRUE(conf.get_refer_play("ossrs.net") == NULL); EXPECT_TRUE(conf.get_refer_publish("ossrs.net") == NULL); - EXPECT_EQ(0, conf.get_in_ack_size("ossrs.net")); + EXPECT_EQ(0, (int)conf.get_in_ack_size("ossrs.net")); EXPECT_EQ(2500000, conf.get_out_ack_size("ossrs.net")); EXPECT_EQ(60000, conf.get_chunk_size("ossrs.net")); EXPECT_TRUE(conf.get_parse_sps("ossrs.net")); @@ -2928,7 +2928,7 @@ VOID TEST(ConfigMainTest, CheckVhostConfig2) EXPECT_EQ(350 * SRS_UTIME_MILLISECONDS, conf.get_mw_sleep("ossrs.net")); EXPECT_FALSE(conf.get_realtime_enabled("ossrs.net")); EXPECT_FALSE(conf.get_tcp_nodelay("ossrs.net")); - EXPECT_EQ(0, conf.get_send_min_interval("ossrs.net")); + EXPECT_EQ(0, (int)conf.get_send_min_interval("ossrs.net")); EXPECT_FALSE(conf.get_reduce_sequence_header("ossrs.net")); EXPECT_EQ(20000000, conf.get_publish_1stpkt_timeout("ossrs.net")); EXPECT_EQ(5000000, conf.get_publish_normal_timeout("ossrs.net")); @@ -3065,7 +3065,7 @@ VOID TEST(ConfigMainTest, CheckVhostConfig2) if (true) { MockSrsConfig conf; HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "vhost ossrs.net{play{time_jitter zero;}}")); - EXPECT_EQ(2, conf.get_time_jitter("ossrs.net")); + EXPECT_EQ(2, (int)conf.get_time_jitter("ossrs.net")); } if (true) { @@ -3132,7 +3132,7 @@ VOID TEST(ConfigMainTest, CheckVhostConfig3) EXPECT_FALSE(conf.get_vhost_edge_token_traverse("ossrs.net")); EXPECT_STREQ("[vhost]", conf.get_vhost_edge_transform_vhost("ossrs.net").c_str()); EXPECT_FALSE(conf.get_vhost_origin_cluster("ossrs.net")); - EXPECT_EQ(0, conf.get_vhost_coworkers("ossrs.net").size()); + EXPECT_EQ(0, (int)conf.get_vhost_coworkers("ossrs.net").size()); EXPECT_FALSE(conf.get_security_enabled("ossrs.net")); EXPECT_TRUE(conf.get_security_rules("ossrs.net") == NULL); } @@ -3152,7 +3152,7 @@ VOID TEST(ConfigMainTest, CheckVhostConfig3) if (true) { MockSrsConfig conf; HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "vhost ossrs.net{cluster{coworkers xxx;}}")); - EXPECT_EQ(1, conf.get_vhost_coworkers("ossrs.net").size()); + EXPECT_EQ(1, (int)conf.get_vhost_coworkers("ossrs.net").size()); } if (true) { @@ -3286,14 +3286,14 @@ VOID TEST(ConfigMainTest, CheckVhostConfig4) EXPECT_TRUE(conf.get_transcode("ossrs.net", "") == NULL); EXPECT_FALSE(conf.get_transcode_enabled(conf.get_transcode("ossrs.net", ""))); EXPECT_TRUE(conf.get_transcode_ffmpeg(conf.get_transcode("ossrs.net", "")).empty()); - EXPECT_EQ(0, conf.get_transcode_engines(conf.get_transcode("ossrs.net", "")).size()); + EXPECT_EQ(0, (int)conf.get_transcode_engines(conf.get_transcode("ossrs.net", "")).size()); } if (true) { MockSrsConfig conf; HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "vhost ossrs.net{transcode xxx{engine{output xxx;}}}")); vector arr = conf.get_transcode_engines(conf.get_transcode("ossrs.net", "xxx")); - ASSERT_EQ(1, arr.size()); + ASSERT_EQ(1, (int)arr.size()); EXPECT_STREQ("xxx", conf.get_engine_output(arr.at(0)).c_str()); } @@ -3301,7 +3301,7 @@ VOID TEST(ConfigMainTest, CheckVhostConfig4) MockSrsConfig conf; HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "vhost ossrs.net{transcode xxx{engine{oformat flv;}}}")); vector arr = conf.get_transcode_engines(conf.get_transcode("ossrs.net", "xxx")); - ASSERT_EQ(1, arr.size()); + ASSERT_EQ(1, (int)arr.size()); EXPECT_STREQ("flv", conf.get_engine_oformat(arr.at(0)).c_str()); } @@ -3309,15 +3309,15 @@ VOID TEST(ConfigMainTest, CheckVhostConfig4) MockSrsConfig conf; HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "vhost ossrs.net{transcode xxx{engine{aparams {i;}}}}")); vector arr = conf.get_transcode_engines(conf.get_transcode("ossrs.net", "xxx")); - ASSERT_EQ(1, arr.size()); - EXPECT_EQ(1, conf.get_engine_aparams(arr.at(0)).size()); + ASSERT_EQ(1, (int)arr.size()); + EXPECT_EQ(1, (int)conf.get_engine_aparams(arr.at(0)).size()); } if (true) { MockSrsConfig conf; HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "vhost ossrs.net{transcode xxx{engine{achannels 1000;}}}")); vector arr = conf.get_transcode_engines(conf.get_transcode("ossrs.net", "xxx")); - ASSERT_EQ(1, arr.size()); + ASSERT_EQ(1, (int)arr.size()); EXPECT_EQ(1000, conf.get_engine_achannels(arr.at(0))); } @@ -3325,7 +3325,7 @@ VOID TEST(ConfigMainTest, CheckVhostConfig4) MockSrsConfig conf; HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "vhost ossrs.net{transcode xxx{engine{asample_rate 1000;}}}")); vector arr = conf.get_transcode_engines(conf.get_transcode("ossrs.net", "xxx")); - ASSERT_EQ(1, arr.size()); + ASSERT_EQ(1, (int)arr.size()); EXPECT_EQ(1000, conf.get_engine_asample_rate(arr.at(0))); } @@ -3333,7 +3333,7 @@ VOID TEST(ConfigMainTest, CheckVhostConfig4) MockSrsConfig conf; HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "vhost ossrs.net{transcode xxx{engine{abitrate 1000;}}}")); vector arr = conf.get_transcode_engines(conf.get_transcode("ossrs.net", "xxx")); - ASSERT_EQ(1, arr.size()); + ASSERT_EQ(1, (int)arr.size()); EXPECT_EQ(1000, conf.get_engine_abitrate(arr.at(0))); } @@ -3341,7 +3341,7 @@ VOID TEST(ConfigMainTest, CheckVhostConfig4) MockSrsConfig conf; HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "vhost ossrs.net{transcode xxx{engine{acodec aac;}}}")); vector arr = conf.get_transcode_engines(conf.get_transcode("ossrs.net", "xxx")); - ASSERT_EQ(1, arr.size()); + ASSERT_EQ(1, (int)arr.size()); EXPECT_STREQ("aac", conf.get_engine_acodec(arr.at(0)).c_str()); } @@ -3349,15 +3349,15 @@ VOID TEST(ConfigMainTest, CheckVhostConfig4) MockSrsConfig conf; HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "vhost ossrs.net{transcode xxx{engine{vparams {t;}}}}")); vector arr = conf.get_transcode_engines(conf.get_transcode("ossrs.net", "xxx")); - ASSERT_EQ(1, arr.size()); - EXPECT_EQ(1, conf.get_engine_vparams(arr.at(0)).size()); + ASSERT_EQ(1, (int)arr.size()); + EXPECT_EQ(1, (int)conf.get_engine_vparams(arr.at(0)).size()); } if (true) { MockSrsConfig conf; HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "vhost ossrs.net{transcode xxx{engine{vpreset main;}}}")); vector arr = conf.get_transcode_engines(conf.get_transcode("ossrs.net", "xxx")); - ASSERT_EQ(1, arr.size()); + ASSERT_EQ(1, (int)arr.size()); EXPECT_STREQ("main", conf.get_engine_vpreset(arr.at(0)).c_str()); } @@ -3365,7 +3365,7 @@ VOID TEST(ConfigMainTest, CheckVhostConfig4) MockSrsConfig conf; HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "vhost ossrs.net{transcode xxx{engine{vprofile main;}}}")); vector arr = conf.get_transcode_engines(conf.get_transcode("ossrs.net", "xxx")); - ASSERT_EQ(1, arr.size()); + ASSERT_EQ(1, (int)arr.size()); EXPECT_STREQ("main", conf.get_engine_vprofile(arr.at(0)).c_str()); } @@ -3373,7 +3373,7 @@ VOID TEST(ConfigMainTest, CheckVhostConfig4) MockSrsConfig conf; HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "vhost ossrs.net{transcode xxx{engine{vthreads 1000;}}}")); vector arr = conf.get_transcode_engines(conf.get_transcode("ossrs.net", "xxx")); - ASSERT_EQ(1, arr.size()); + ASSERT_EQ(1, (int)arr.size()); EXPECT_EQ(1000, conf.get_engine_vthreads(arr.at(0))); } @@ -3381,7 +3381,7 @@ VOID TEST(ConfigMainTest, CheckVhostConfig4) MockSrsConfig conf; HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "vhost ossrs.net{transcode xxx{engine{vheight 1000;}}}")); vector arr = conf.get_transcode_engines(conf.get_transcode("ossrs.net", "xxx")); - ASSERT_EQ(1, arr.size()); + ASSERT_EQ(1, (int)arr.size()); EXPECT_EQ(1000, conf.get_engine_vheight(arr.at(0))); } @@ -3389,7 +3389,7 @@ VOID TEST(ConfigMainTest, CheckVhostConfig4) MockSrsConfig conf; HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "vhost ossrs.net{transcode xxx{engine{vwidth 1000;}}}")); vector arr = conf.get_transcode_engines(conf.get_transcode("ossrs.net", "xxx")); - ASSERT_EQ(1, arr.size()); + ASSERT_EQ(1, (int)arr.size()); EXPECT_EQ(1000, conf.get_engine_vwidth(arr.at(0))); } @@ -3397,7 +3397,7 @@ VOID TEST(ConfigMainTest, CheckVhostConfig4) MockSrsConfig conf; HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "vhost ossrs.net{transcode xxx{engine{vfps 1000;}}}")); vector arr = conf.get_transcode_engines(conf.get_transcode("ossrs.net", "xxx")); - ASSERT_EQ(1, arr.size()); + ASSERT_EQ(1, (int)arr.size()); EXPECT_EQ(1000, conf.get_engine_vfps(arr.at(0))); } @@ -3405,7 +3405,7 @@ VOID TEST(ConfigMainTest, CheckVhostConfig4) MockSrsConfig conf; HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "vhost ossrs.net{transcode xxx{engine{vbitrate 1000;}}}")); vector arr = conf.get_transcode_engines(conf.get_transcode("ossrs.net", "xxx")); - ASSERT_EQ(1, arr.size()); + ASSERT_EQ(1, (int)arr.size()); EXPECT_EQ(1000, conf.get_engine_vbitrate(arr.at(0))); } @@ -3413,7 +3413,7 @@ VOID TEST(ConfigMainTest, CheckVhostConfig4) MockSrsConfig conf; HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "vhost ossrs.net{transcode xxx{engine{vcodec x264;}}}")); vector arr = conf.get_transcode_engines(conf.get_transcode("ossrs.net", "xxx")); - ASSERT_EQ(1, arr.size()); + ASSERT_EQ(1, (int)arr.size()); EXPECT_STREQ("x264", conf.get_engine_vcodec(arr.at(0)).c_str()); } @@ -3421,23 +3421,23 @@ VOID TEST(ConfigMainTest, CheckVhostConfig4) MockSrsConfig conf; HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "vhost ossrs.net{transcode xxx{engine{vfilter {i;}}}}")); vector arr = conf.get_transcode_engines(conf.get_transcode("ossrs.net", "xxx")); - ASSERT_EQ(1, arr.size()); - EXPECT_EQ(1, conf.get_engine_vfilter(arr.at(0)).size()); + ASSERT_EQ(1, (int)arr.size()); + EXPECT_EQ(1, (int)conf.get_engine_vfilter(arr.at(0)).size()); } if (true) { MockSrsConfig conf; HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "vhost ossrs.net{transcode xxx{engine{vfilter {i logo.png;}}}}")); vector arr = conf.get_transcode_engines(conf.get_transcode("ossrs.net", "xxx")); - ASSERT_EQ(1, arr.size()); - EXPECT_EQ(2, conf.get_engine_vfilter(arr.at(0)).size()); + ASSERT_EQ(1, (int)arr.size()); + EXPECT_EQ(2, (int)conf.get_engine_vfilter(arr.at(0)).size()); } if (true) { MockSrsConfig conf; HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "vhost ossrs.net{transcode xxx{engine{iformat mp4;}}}")); vector arr = conf.get_transcode_engines(conf.get_transcode("ossrs.net", "xxx")); - ASSERT_EQ(1, arr.size()); + ASSERT_EQ(1, (int)arr.size()); EXPECT_STREQ("mp4", conf.get_engine_iformat(arr.at(0)).c_str()); } @@ -3445,15 +3445,15 @@ VOID TEST(ConfigMainTest, CheckVhostConfig4) MockSrsConfig conf; HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "vhost ossrs.net{transcode xxx{engine{perfile {re;}}}}")); vector arr = conf.get_transcode_engines(conf.get_transcode("ossrs.net", "xxx")); - ASSERT_EQ(1, arr.size()); - EXPECT_EQ(1, conf.get_engine_perfile(arr.at(0)).size()); + ASSERT_EQ(1, (int)arr.size()); + EXPECT_EQ(1, (int)conf.get_engine_perfile(arr.at(0)).size()); } if (true) { MockSrsConfig conf; HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "vhost ossrs.net{transcode xxx{engine{enabled on;}}}")); vector arr = conf.get_transcode_engines(conf.get_transcode("ossrs.net", "xxx")); - ASSERT_EQ(1, arr.size()); + ASSERT_EQ(1, (int)arr.size()); EXPECT_TRUE(conf.get_engine_enabled(arr.at(0))); } @@ -3461,7 +3461,7 @@ VOID TEST(ConfigMainTest, CheckVhostConfig4) MockSrsConfig conf; HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "vhost ossrs.net{transcode xxx{engine;}}")); vector arr = conf.get_transcode_engines(conf.get_transcode("ossrs.net", "xxx")); - ASSERT_EQ(1, arr.size()); + ASSERT_EQ(1, (int)arr.size()); } if (true) { @@ -3492,13 +3492,13 @@ VOID TEST(ConfigMainTest, CheckVhostConfig5) HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "vhost ossrs.net{exec{enabled on;publish xxx;}}")); EXPECT_TRUE(conf.get_exec("ossrs.net") != NULL); EXPECT_TRUE(conf.get_exec_enabled("ossrs.net")); - EXPECT_EQ(1, conf.get_exec_publishs("ossrs.net").size()); + EXPECT_EQ(1, (int)conf.get_exec_publishs("ossrs.net").size()); } if (true) { MockSrsConfig conf; HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "vhost ossrs.net{ingest xxx{enabled on;ffmpeg xxx2;input{type xxx3;url xxx4;}}}")); - EXPECT_EQ(1, conf.get_ingesters("ossrs.net").size()); + EXPECT_EQ(1, (int)conf.get_ingesters("ossrs.net").size()); ASSERT_TRUE(conf.get_ingest_by_id("ossrs.net", "xxx") != NULL); EXPECT_TRUE(conf.get_ingest_enabled(conf.get_ingest_by_id("ossrs.net", "xxx"))); EXPECT_STREQ("xxx2", conf.get_ingest_ffmpeg(conf.get_ingest_by_id("ossrs.net", "xxx")).c_str()); @@ -3584,7 +3584,7 @@ VOID TEST(ConfigMainTest, CheckVhostConfig5) EXPECT_STREQ("xxx2", conf.get_dvr_plan("ossrs.net").c_str()); EXPECT_EQ(10*SRS_UTIME_SECONDS, conf.get_dvr_duration("ossrs.net")); EXPECT_TRUE(conf.get_dvr_wait_keyframe("ossrs.net")); - EXPECT_EQ(1, conf.get_dvr_time_jitter("ossrs.net")); + EXPECT_EQ(1, (int)conf.get_dvr_time_jitter("ossrs.net")); } if (true) { @@ -3637,7 +3637,7 @@ VOID TEST(ConfigMainTest, CheckVhostConfig5) if (true) { MockSrsConfig conf; HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "stats{network 0;disk xxx;}")); - EXPECT_EQ(0, conf.get_stats_network()); + EXPECT_EQ(0, (int)conf.get_stats_network()); EXPECT_TRUE(conf.get_stats_disk_device() != NULL); } } diff --git a/trunk/src/utest/srs_utest_kernel.cpp b/trunk/src/utest/srs_utest_kernel.cpp index 29638dceb..72395fc61 100644 --- a/trunk/src/utest/srs_utest_kernel.cpp +++ b/trunk/src/utest/srs_utest_kernel.cpp @@ -4869,20 +4869,20 @@ VOID TEST(KernelMP4Test, CoverMP4CodecSingleFrame) // Sequence header. HELPER_EXPECT_SUCCESS(dec.read_sample(&ht, &ft, &ct, &dts, &pts, &sample, &nb_sample)); - EXPECT_EQ(0, (int)dts); EXPECT_EQ(41, nb_sample); EXPECT_EQ(SrsMp4HandlerTypeVIDE, ht); EXPECT_EQ(SrsAudioAacFrameTraitSequenceHeader, ct); + EXPECT_EQ(0, (int)dts); EXPECT_EQ(41, (int)nb_sample); EXPECT_EQ(SrsMp4HandlerTypeVIDE, ht); EXPECT_EQ(SrsAudioAacFrameTraitSequenceHeader, ct); srs_freepa(sample); HELPER_EXPECT_SUCCESS(dec.read_sample(&ht, &ft, &ct, &dts, &pts, &sample, &nb_sample)); - EXPECT_EQ(0, (int)dts); EXPECT_EQ(2, nb_sample); EXPECT_EQ(SrsMp4HandlerTypeSOUN, ht); EXPECT_EQ(SrsAudioAacFrameTraitSequenceHeader, ct); + EXPECT_EQ(0, (int)dts); EXPECT_EQ(2, (int)nb_sample); EXPECT_EQ(SrsMp4HandlerTypeSOUN, ht); EXPECT_EQ(SrsAudioAacFrameTraitSequenceHeader, ct); srs_freepa(sample); // Frame group #0 HELPER_EXPECT_SUCCESS(dec.read_sample(&ht, &ft, &ct, &dts, &pts, &sample, &nb_sample)); - EXPECT_EQ(0, (int)dts); EXPECT_EQ(127, nb_sample); EXPECT_EQ(SrsMp4HandlerTypeVIDE, ht); EXPECT_NE(SrsAudioAacFrameTraitSequenceHeader, ct); + EXPECT_EQ(0, (int)dts); EXPECT_EQ(127, (int)nb_sample); EXPECT_EQ(SrsMp4HandlerTypeVIDE, ht); EXPECT_NE(SrsAudioAacFrameTraitSequenceHeader, ct); srs_freepa(sample); HELPER_EXPECT_SUCCESS(dec.read_sample(&ht, &ft, &ct, &dts, &pts, &sample, &nb_sample)); - EXPECT_EQ(0, (int)dts); EXPECT_EQ(87, nb_sample); EXPECT_EQ(SrsMp4HandlerTypeSOUN, ht); EXPECT_NE(SrsAudioAacFrameTraitSequenceHeader, ct); + EXPECT_EQ(0, (int)dts); EXPECT_EQ(87, (int)nb_sample); EXPECT_EQ(SrsMp4HandlerTypeSOUN, ht); EXPECT_NE(SrsAudioAacFrameTraitSequenceHeader, ct); srs_freepa(sample); } @@ -4984,20 +4984,20 @@ VOID TEST(KernelMP4Test, CoverMP4MultipleVideos) // Sequence header. HELPER_EXPECT_SUCCESS(dec.read_sample(&ht, &ft, &ct, &dts, &pts, &sample, &nb_sample)); - EXPECT_EQ(0, (int)dts); EXPECT_EQ(41, nb_sample); EXPECT_EQ(SrsMp4HandlerTypeVIDE, ht); EXPECT_EQ(SrsAudioAacFrameTraitSequenceHeader, ct); + EXPECT_EQ(0, (int)dts); EXPECT_EQ(41, (int)nb_sample); EXPECT_EQ(SrsMp4HandlerTypeVIDE, ht); EXPECT_EQ(SrsAudioAacFrameTraitSequenceHeader, ct); srs_freepa(sample); HELPER_EXPECT_SUCCESS(dec.read_sample(&ht, &ft, &ct, &dts, &pts, &sample, &nb_sample)); - EXPECT_EQ(0, (int)dts); EXPECT_EQ(2, nb_sample); EXPECT_EQ(SrsMp4HandlerTypeSOUN, ht); EXPECT_EQ(SrsAudioAacFrameTraitSequenceHeader, ct); + EXPECT_EQ(0, (int)dts); EXPECT_EQ(2, (int)nb_sample); EXPECT_EQ(SrsMp4HandlerTypeSOUN, ht); EXPECT_EQ(SrsAudioAacFrameTraitSequenceHeader, ct); srs_freepa(sample); // Frames order by dts asc. HELPER_EXPECT_SUCCESS(dec.read_sample(&ht, &ft, &ct, &dts, &pts, &sample, &nb_sample)); - EXPECT_EQ(0, (int)dts); EXPECT_EQ(127, nb_sample); EXPECT_EQ(SrsMp4HandlerTypeVIDE, ht); EXPECT_NE(SrsAudioAacFrameTraitSequenceHeader, ct); + EXPECT_EQ(0, (int)dts); EXPECT_EQ(127, (int)nb_sample); EXPECT_EQ(SrsMp4HandlerTypeVIDE, ht); EXPECT_NE(SrsAudioAacFrameTraitSequenceHeader, ct); srs_freepa(sample); HELPER_EXPECT_SUCCESS(dec.read_sample(&ht, &ft, &ct, &dts, &pts, &sample, &nb_sample)); - EXPECT_EQ(40, (int)dts); EXPECT_EQ(127, nb_sample); EXPECT_EQ(SrsMp4HandlerTypeVIDE, ht); EXPECT_NE(SrsAudioAacFrameTraitSequenceHeader, ct); + EXPECT_EQ(40, (int)dts); EXPECT_EQ(127, (int)nb_sample); EXPECT_EQ(SrsMp4HandlerTypeVIDE, ht); EXPECT_NE(SrsAudioAacFrameTraitSequenceHeader, ct); srs_freepa(sample); } } @@ -5082,24 +5082,24 @@ VOID TEST(KernelMP4Test, CoverMP4MultipleCTTs) // Sequence header. HELPER_EXPECT_SUCCESS(dec.read_sample(&ht, &ft, &ct, &dts, &pts, &sample, &nb_sample)); - EXPECT_EQ(0, (int)dts); EXPECT_EQ(41, nb_sample); EXPECT_EQ(SrsMp4HandlerTypeVIDE, ht); EXPECT_EQ(SrsAudioAacFrameTraitSequenceHeader, ct); + EXPECT_EQ(0, (int)dts); EXPECT_EQ(41, (int)nb_sample); EXPECT_EQ(SrsMp4HandlerTypeVIDE, ht); EXPECT_EQ(SrsAudioAacFrameTraitSequenceHeader, ct); srs_freepa(sample); HELPER_EXPECT_SUCCESS(dec.read_sample(&ht, &ft, &ct, &dts, &pts, &sample, &nb_sample)); - EXPECT_EQ(0, (int)dts); EXPECT_EQ(2, nb_sample); EXPECT_EQ(SrsMp4HandlerTypeSOUN, ht); EXPECT_EQ(SrsAudioAacFrameTraitSequenceHeader, ct); + EXPECT_EQ(0, (int)dts); EXPECT_EQ(2, (int)nb_sample); EXPECT_EQ(SrsMp4HandlerTypeSOUN, ht); EXPECT_EQ(SrsAudioAacFrameTraitSequenceHeader, ct); srs_freepa(sample); // Frames order by dts asc. HELPER_EXPECT_SUCCESS(dec.read_sample(&ht, &ft, &ct, &dts, &pts, &sample, &nb_sample)); - EXPECT_EQ(0, (int)dts); EXPECT_EQ(127, nb_sample); EXPECT_EQ(SrsMp4HandlerTypeVIDE, ht); EXPECT_NE(SrsAudioAacFrameTraitSequenceHeader, ct); + EXPECT_EQ(0, (int)dts); EXPECT_EQ(127, (int)nb_sample); EXPECT_EQ(SrsMp4HandlerTypeVIDE, ht); EXPECT_NE(SrsAudioAacFrameTraitSequenceHeader, ct); srs_freepa(sample); HELPER_EXPECT_SUCCESS(dec.read_sample(&ht, &ft, &ct, &dts, &pts, &sample, &nb_sample)); - EXPECT_EQ(40, (int)dts); EXPECT_EQ(80, (int)pts); EXPECT_EQ(127, nb_sample); EXPECT_EQ(SrsMp4HandlerTypeVIDE, ht); EXPECT_NE(SrsAudioAacFrameTraitSequenceHeader, ct); + EXPECT_EQ(40, (int)dts); EXPECT_EQ(80, (int)pts); EXPECT_EQ(127, (int)nb_sample); EXPECT_EQ(SrsMp4HandlerTypeVIDE, ht); EXPECT_NE(SrsAudioAacFrameTraitSequenceHeader, ct); srs_freepa(sample); HELPER_EXPECT_SUCCESS(dec.read_sample(&ht, &ft, &ct, &dts, &pts, &sample, &nb_sample)); - EXPECT_EQ(80, (int)dts); EXPECT_EQ(40, (int)pts); EXPECT_EQ(127, nb_sample); EXPECT_EQ(SrsMp4HandlerTypeVIDE, ht); EXPECT_NE(SrsAudioAacFrameTraitSequenceHeader, ct); + EXPECT_EQ(80, (int)dts); EXPECT_EQ(40, (int)pts); EXPECT_EQ(127, (int)nb_sample); EXPECT_EQ(SrsMp4HandlerTypeVIDE, ht); EXPECT_NE(SrsAudioAacFrameTraitSequenceHeader, ct); srs_freepa(sample); } } @@ -5194,28 +5194,28 @@ VOID TEST(KernelMP4Test, CoverMP4MultipleAVs) // Sequence header. HELPER_EXPECT_SUCCESS(dec.read_sample(&ht, &ft, &ct, &dts, &pts, &sample, &nb_sample)); - EXPECT_EQ(0, (int)dts); EXPECT_EQ(41, nb_sample); EXPECT_EQ(SrsMp4HandlerTypeVIDE, ht); EXPECT_EQ(SrsAudioAacFrameTraitSequenceHeader, ct); + EXPECT_EQ(0, (int)dts); EXPECT_EQ(41, (int)nb_sample); EXPECT_EQ(SrsMp4HandlerTypeVIDE, ht); EXPECT_EQ(SrsAudioAacFrameTraitSequenceHeader, ct); srs_freepa(sample); HELPER_EXPECT_SUCCESS(dec.read_sample(&ht, &ft, &ct, &dts, &pts, &sample, &nb_sample)); - EXPECT_EQ(0, (int)dts); EXPECT_EQ(2, nb_sample); EXPECT_EQ(SrsMp4HandlerTypeSOUN, ht); EXPECT_EQ(SrsAudioAacFrameTraitSequenceHeader, ct); + EXPECT_EQ(0, (int)dts); EXPECT_EQ(2, (int)nb_sample); EXPECT_EQ(SrsMp4HandlerTypeSOUN, ht); EXPECT_EQ(SrsAudioAacFrameTraitSequenceHeader, ct); srs_freepa(sample); // Frames order by dts asc. HELPER_EXPECT_SUCCESS(dec.read_sample(&ht, &ft, &ct, &dts, &pts, &sample, &nb_sample)); - EXPECT_EQ(0, (int)dts); EXPECT_EQ(127, nb_sample); EXPECT_EQ(SrsMp4HandlerTypeVIDE, ht); EXPECT_NE(SrsAudioAacFrameTraitSequenceHeader, ct); + EXPECT_EQ(0, (int)dts); EXPECT_EQ(127, (int)nb_sample); EXPECT_EQ(SrsMp4HandlerTypeVIDE, ht); EXPECT_NE(SrsAudioAacFrameTraitSequenceHeader, ct); srs_freepa(sample); HELPER_EXPECT_SUCCESS(dec.read_sample(&ht, &ft, &ct, &dts, &pts, &sample, &nb_sample)); - EXPECT_EQ(0, (int)dts); EXPECT_EQ(87, nb_sample); EXPECT_EQ(SrsMp4HandlerTypeSOUN, ht); EXPECT_NE(SrsAudioAacFrameTraitSequenceHeader, ct); + EXPECT_EQ(0, (int)dts); EXPECT_EQ(87, (int)nb_sample); EXPECT_EQ(SrsMp4HandlerTypeSOUN, ht); EXPECT_NE(SrsAudioAacFrameTraitSequenceHeader, ct); srs_freepa(sample); HELPER_EXPECT_SUCCESS(dec.read_sample(&ht, &ft, &ct, &dts, &pts, &sample, &nb_sample)); - EXPECT_EQ(20, (int)dts); EXPECT_EQ(87, nb_sample); EXPECT_EQ(SrsMp4HandlerTypeSOUN, ht); EXPECT_NE(SrsAudioAacFrameTraitSequenceHeader, ct); + EXPECT_EQ(20, (int)dts); EXPECT_EQ(87, (int)nb_sample); EXPECT_EQ(SrsMp4HandlerTypeSOUN, ht); EXPECT_NE(SrsAudioAacFrameTraitSequenceHeader, ct); srs_freepa(sample); HELPER_EXPECT_SUCCESS(dec.read_sample(&ht, &ft, &ct, &dts, &pts, &sample, &nb_sample)); - EXPECT_EQ(40, (int)dts); EXPECT_EQ(40, (int)pts); EXPECT_EQ(127, nb_sample); EXPECT_EQ(SrsMp4HandlerTypeVIDE, ht); EXPECT_NE(SrsAudioAacFrameTraitSequenceHeader, ct); + EXPECT_EQ(40, (int)dts); EXPECT_EQ(40, (int)pts); EXPECT_EQ(127, (int)nb_sample); EXPECT_EQ(SrsMp4HandlerTypeVIDE, ht); EXPECT_NE(SrsAudioAacFrameTraitSequenceHeader, ct); srs_freepa(sample); } } @@ -5338,3 +5338,16 @@ VOID TEST(KernelMP4Test, CoverMP4M2tsSegmentEncoder) HELPER_EXPECT_SUCCESS(enc.flush(dts)); } +VOID TEST(KernelUtilityTest, CoverStringAssign) +{ + string sps = "SRS"; + ASSERT_STREQ("SRS", sps.c_str()); + + sps.assign("Hello", 5); + ASSERT_STREQ("Hello", sps.c_str()); + + sps.assign("World", 0); + ASSERT_EQ(0, (int)sps.length()); + ASSERT_STREQ("", sps.c_str()); +} + diff --git a/trunk/src/utest/srs_utest_protocol.cpp b/trunk/src/utest/srs_utest_protocol.cpp index 2b13b2835..ebd260aa1 100644 --- a/trunk/src/utest/srs_utest_protocol.cpp +++ b/trunk/src/utest/srs_utest_protocol.cpp @@ -5855,7 +5855,7 @@ VOID TEST(ProtocolHTTPTest, HTTPParser) HELPER_EXPECT_SUCCESS(parser.parse("HelloGET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nWorld")); EXPECT_EQ(80, (int)parser.parsed); EXPECT_EQ(0, (int)parser.parser->nread); - EXPECT_EQ(0, parser.parser->content_length); + EXPECT_EQ(0, (int)parser.parser->content_length); } if (true) { @@ -5864,7 +5864,7 @@ VOID TEST(ProtocolHTTPTest, HTTPParser) HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nHel")); EXPECT_EQ(73, (int)parser.parsed); EXPECT_EQ(0, (int)parser.parser->nread); - EXPECT_EQ(2, parser.parser->content_length); + EXPECT_EQ(2, (int)parser.parser->content_length); } if (true) { @@ -5881,7 +5881,7 @@ VOID TEST(ProtocolHTTPTest, HTTPParser) // size = 34, nparsed = 34, nread = 34 HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHo")); EXPECT_EQ(34, (int)parser.parsed); - EXPECT_EQ(34, parser.parser->nread); + EXPECT_EQ(34, (int)parser.parser->nread); // size = 41, nparsed = 41, nread = 0 HELPER_EXPECT_SUCCESS(parser.parse("st: ossrs.net\r\nContent-Length: 5\r\n\r\nHello")); @@ -5894,7 +5894,7 @@ VOID TEST(ProtocolHTTPTest, HTTPParser) // size = 41, nparsed = 41, nread = 41 HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: oss")); EXPECT_EQ(41, (int)parser.parsed); - EXPECT_EQ(41, parser.parser->nread); + EXPECT_EQ(41, (int)parser.parser->nread); // size = 34, nparsed = 34, nread = 0 HELPER_EXPECT_SUCCESS(parser.parse("rs.net\r\nContent-Length: 5\r\n\r\nHello")); @@ -5907,7 +5907,7 @@ VOID TEST(ProtocolHTTPTest, HTTPParser) // size = 48, nparsed = 48, nread = 48 HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r")); EXPECT_EQ(48, (int)parser.parsed); - EXPECT_EQ(48, parser.parser->nread); + EXPECT_EQ(48, (int)parser.parser->nread); // size = 27, nparsed = 27, nread = 0 HELPER_EXPECT_SUCCESS(parser.parse("\nContent-Length: 5\r\n\r\nHello")); @@ -5920,7 +5920,7 @@ VOID TEST(ProtocolHTTPTest, HTTPParser) // size = 68, nparsed = 68, nread = 68 HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n")); EXPECT_EQ(68, (int)parser.parsed); - EXPECT_EQ(68, parser.parser->nread); + EXPECT_EQ(68, (int)parser.parser->nread); // size = 7, nparsed = 7, nread = 0 HELPER_EXPECT_SUCCESS(parser.parse("\r\nHello")); @@ -5933,7 +5933,7 @@ VOID TEST(ProtocolHTTPTest, HTTPParser) // size = 69, nparsed = 69, nread = 69 HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r")); EXPECT_EQ(69, (int)parser.parsed); - EXPECT_EQ(69, parser.parser->nread); + EXPECT_EQ(69, (int)parser.parser->nread); // size = 6, nparsed = 6, nread = 0 HELPER_EXPECT_SUCCESS(parser.parse("\nHello")); @@ -5954,12 +5954,12 @@ VOID TEST(ProtocolHTTPTest, HTTPParser) // nparsed = 2, size = 2, nread = 2 HELPER_EXPECT_SUCCESS(parser.parse("GE")); EXPECT_EQ(2, (int)parser.parsed); - EXPECT_EQ(2, parser.parser->nread); + EXPECT_EQ(2, (int)parser.parser->nread); // size = 0, nparsed = 1, nread=2 HELPER_EXPECT_FAILED(parser.parse("")); EXPECT_EQ(1, (int)parser.parsed); - EXPECT_EQ(2, parser.parser->nread); + EXPECT_EQ(2, (int)parser.parser->nread); } if (true) { @@ -5967,12 +5967,12 @@ VOID TEST(ProtocolHTTPTest, HTTPParser) // size = 2, nparsed = 2, nread = 2 HELPER_EXPECT_SUCCESS(parser.parse("GE")); EXPECT_EQ(2, (int)parser.parsed); - EXPECT_EQ(2, parser.parser->nread); + EXPECT_EQ(2, (int)parser.parser->nread); // size = 1, nparsed = 0, nread = 3 HELPER_EXPECT_FAILED(parser.parse("X")); EXPECT_EQ(0, (int)parser.parsed); - EXPECT_EQ(3, parser.parser->nread); + EXPECT_EQ(3, (int)parser.parser->nread); } if (true) { @@ -5980,12 +5980,12 @@ VOID TEST(ProtocolHTTPTest, HTTPParser) // size = 2, nparsed = 2, nread = 2 HELPER_EXPECT_SUCCESS(parser.parse("GE")); EXPECT_EQ(2, (int)parser.parsed); - EXPECT_EQ(2, parser.parser->nread); + EXPECT_EQ(2, (int)parser.parser->nread); // size = 1, nparsed = 1, nread = 3 HELPER_EXPECT_SUCCESS(parser.parse("T")); EXPECT_EQ(1, (int)parser.parsed); - EXPECT_EQ(3, parser.parser->nread); + EXPECT_EQ(3, (int)parser.parser->nread); } if (true) { @@ -5993,7 +5993,7 @@ VOID TEST(ProtocolHTTPTest, HTTPParser) // size = 3, nparsed = 3, nread = 3 HELPER_EXPECT_SUCCESS(parser.parse("GET")); EXPECT_EQ(3, (int)parser.parsed); - EXPECT_EQ(3, parser.parser->nread); + EXPECT_EQ(3, (int)parser.parser->nread); } }