From 1e34d2a5cd0cfd15ff510cc7d4831225a006871a Mon Sep 17 00:00:00 2001 From: zhengfl Date: Thu, 2 Oct 2014 19:45:04 +0800 Subject: [PATCH 001/800] client connection no disconnect --- trunk/src/app/srs_app_recv_thread.cpp | 23 +++++++++++++++++++++-- trunk/src/app/srs_app_recv_thread.hpp | 4 ++++ trunk/src/app/srs_app_rtmp_conn.cpp | 1 + trunk/src/app/srs_app_source.cpp | 8 ++++++++ trunk/src/app/srs_app_source.hpp | 4 ++++ 5 files changed, 38 insertions(+), 2 deletions(-) mode change 100644 => 100755 trunk/src/app/srs_app_recv_thread.cpp mode change 100644 => 100755 trunk/src/app/srs_app_recv_thread.hpp mode change 100644 => 100755 trunk/src/app/srs_app_rtmp_conn.cpp mode change 100644 => 100755 trunk/src/app/srs_app_source.cpp mode change 100644 => 100755 trunk/src/app/srs_app_source.hpp diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp old mode 100644 new mode 100755 index 310c5ebecf..3c32a80305 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -30,6 +30,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #include +#include using namespace std; @@ -138,11 +139,12 @@ SrsQueueRecvThread::SrsQueueRecvThread(SrsRtmpServer* rtmp_sdk, int timeout_ms) { rtmp = rtmp_sdk; recv_error_code = ERROR_SUCCESS; + _consumer = NULL; } SrsQueueRecvThread::~SrsQueueRecvThread() { - trd.stop(); + stop(); // clear all messages. std::vector::iterator it; @@ -160,6 +162,7 @@ int SrsQueueRecvThread::start() void SrsQueueRecvThread::stop() { + _consumer = NULL; trd.stop(); } @@ -195,7 +198,13 @@ bool SrsQueueRecvThread::can_handle() // for the message may cause the thread to stop, // when stop, the thread is freed, so the messages // are dropped. - return empty(); + bool e = empty(); +#ifdef SRS_PERF_QUEUE_COND_WAIT + if (_consumer && !e) { + _consumer->on_dispose(); + } +#endif + return e; } int SrsQueueRecvThread::handle(SrsCommonMessage* msg) @@ -209,6 +218,11 @@ int SrsQueueRecvThread::handle(SrsCommonMessage* msg) void SrsQueueRecvThread::on_recv_error(int ret) { +#ifdef SRS_PERF_QUEUE_COND_WAIT + if (_consumer) { + _consumer->on_dispose(); + } +#endif recv_error_code = ret; } @@ -226,6 +240,11 @@ void SrsQueueRecvThread::on_thread_stop() rtmp->set_auto_response(true); } +void SrsQueueRecvThread::set_consumer(SrsConsumer *consumer) +{ + _consumer = consumer; +} + SrsPublishRecvThread::SrsPublishRecvThread( SrsRtmpServer* rtmp_sdk, SrsRequest* _req, int mr_sock_fd, int timeout_ms, diff --git a/trunk/src/app/srs_app_recv_thread.hpp b/trunk/src/app/srs_app_recv_thread.hpp old mode 100644 new mode 100755 index e97639d1d4..cf5fc39ce4 --- a/trunk/src/app/srs_app_recv_thread.hpp +++ b/trunk/src/app/srs_app_recv_thread.hpp @@ -42,6 +42,7 @@ class SrsCommonMessage; class SrsRtmpConn; class SrsSource; class SrsRequest; +class SrsConsumer; /** * for the recv thread to handle the message. @@ -112,6 +113,7 @@ class SrsQueueRecvThread : public ISrsMessageHandler SrsRtmpServer* rtmp; // the recv thread error code. int recv_error_code; + SrsConsumer *_consumer; public: SrsQueueRecvThread(SrsRtmpServer* rtmp_sdk, int timeout_ms); virtual ~SrsQueueRecvThread(); @@ -130,6 +132,8 @@ class SrsQueueRecvThread : public ISrsMessageHandler public: virtual void on_thread_start(); virtual void on_thread_stop(); +public: + virtual void set_consumer(SrsConsumer *consumer); }; /** diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp old mode 100644 new mode 100755 index 230bb851c8..7a742eaa59 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -595,6 +595,7 @@ int SrsRtmpConn::do_playing(SrsSource* source, SrsQueueRecvThread* trd) // when mw_sleep changed, resize the socket send buffer. mw_enabled = true; change_mw_sleep(_srs_config->get_mw_sleep_ms(req->vhost)); + trd->set_consumer(consumer); while (true) { // to use isolate thread to recv, can improve about 33% performance. diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp old mode 100644 new mode 100755 index efece45160..ee75751cc8 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -515,6 +515,14 @@ void SrsConsumer::wait(int nb_msgs, int duration) // use cond block wait for high performance mode. st_cond_wait(mw_wait); } + +void SrsConsumer::on_dispose() +{ + if (mw_waiting) { + st_cond_signal(mw_wait); + mw_waiting = false; + } +} #endif int SrsConsumer::on_play_client_pause(bool is_pause) diff --git a/trunk/src/app/srs_app_source.hpp b/trunk/src/app/srs_app_source.hpp old mode 100644 new mode 100755 index ad7a060a49..eac2531d21 --- a/trunk/src/app/srs_app_source.hpp +++ b/trunk/src/app/srs_app_source.hpp @@ -246,6 +246,10 @@ class SrsConsumer * @param duration the messgae duration to wait. */ virtual void wait(int nb_msgs, int duration); + /** + * when waiting, a message incomming, we rouse it + */ + virtual void on_dispose(); #endif /** * when client send the pause message. From bafdd8312261a7eb9bd3f0dfde1752563e9c24b6 Mon Sep 17 00:00:00 2001 From: zhengfl Date: Thu, 2 Oct 2014 19:51:01 +0800 Subject: [PATCH 002/800] last --- trunk/src/app/srs_app_recv_thread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index 3c32a80305..afce8a81fc 100755 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -218,12 +218,12 @@ int SrsQueueRecvThread::handle(SrsCommonMessage* msg) void SrsQueueRecvThread::on_recv_error(int ret) { + recv_error_code = ret; #ifdef SRS_PERF_QUEUE_COND_WAIT if (_consumer) { _consumer->on_dispose(); } #endif - recv_error_code = ret; } void SrsQueueRecvThread::on_thread_start() From 2317f0e7672276b3f220162401f3d78026e53a38 Mon Sep 17 00:00:00 2001 From: zhengfl Date: Thu, 2 Oct 2014 20:25:36 +0800 Subject: [PATCH 003/800] refine --- trunk/src/app/srs_app_recv_thread.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index afce8a81fc..d73204de90 100755 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -198,13 +198,7 @@ bool SrsQueueRecvThread::can_handle() // for the message may cause the thread to stop, // when stop, the thread is freed, so the messages // are dropped. - bool e = empty(); -#ifdef SRS_PERF_QUEUE_COND_WAIT - if (_consumer && !e) { - _consumer->on_dispose(); - } -#endif - return e; + return empty(); } int SrsQueueRecvThread::handle(SrsCommonMessage* msg) @@ -212,7 +206,11 @@ int SrsQueueRecvThread::handle(SrsCommonMessage* msg) // put into queue, the send thread will get and process it, // @see SrsRtmpConn::process_play_control_msg queue.push_back(msg); - +#ifdef SRS_PERF_QUEUE_COND_WAIT + if (_consumer) { + _consumer->on_dispose(); + } +#endif return ERROR_SUCCESS; } From 5f299356233d3c46316170b270ed9a24c3a6fe7d Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 9 Oct 2014 17:40:43 +0800 Subject: [PATCH 004/800] update master to 2.0, because the 1.0release branch is created. 2.0.0 --- trunk/src/core/srs_core.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 96db793912..3807ef2cc1 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -29,7 +29,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // current release version -#define VERSION_MAJOR "1" +#define VERSION_MAJOR "2" #define VERSION_MINOR "0" #define VERSION_REVISION "0" #define RTMP_SIG_SRS_VERSION VERSION_MAJOR"."VERSION_MINOR"."VERSION_REVISION From d085a5cabc89f8376e2e79c263ac55d86b541088 Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 10 Oct 2014 09:50:18 +0800 Subject: [PATCH 005/800] update csdn mirror, add 1.0release --- trunk/scripts/csdn.mirror.sh | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/trunk/scripts/csdn.mirror.sh b/trunk/scripts/csdn.mirror.sh index 776e1e8c8a..6993e95773 100755 --- a/trunk/scripts/csdn.mirror.sh +++ b/trunk/scripts/csdn.mirror.sh @@ -39,30 +39,53 @@ ret=$?; if [[ 0 -ne $ret ]]; then 1. 在CSDN上创建项目,从https://github.com/winlinvip/simple-rtmp-server拷贝过来。 2. 在本地虚拟机上: git clone git@code.csdn.net:winlinvip/srs-csdn.git + git checkout master && git branch 1.0release && git push origin 1.0release 3. 创建同步的branch: git remote add upstream https://github.com/winlinvip/simple-rtmp-server.git git fetch upstream git checkout upstream/master -b srs.master + git checkout upstream/1.0release -b srs.1.0release 4. 执行本同步更新脚本,更新。 bash scripts/csdn.mirror.sh END exit 0; fi +############################################# +# branch master +############################################# for ((;;)); do git checkout srs.master && git pull ret=$?; if [[ 0 -ne $ret ]]; then - failed_msg "更新github分支失败,自动重试"; + failed_msg "(master)更新github分支失败,自动重试"; continue else - ok_msg "更新github分支成功" + ok_msg "(master)更新github分支成功" fi break done -git checkout master && git merge srs.master -ret=$?; if [[ 0 -ne $ret ]]; then failed_msg "合并github分支失败, ret=$ret"; exit $ret; fi -ok_msg "合并github分支成功" +############################################# +# branch 1.0release +############################################# +for ((;;)); do + git checkout srs.1.0release && git pull + ret=$?; if [[ 0 -ne $ret ]]; then + failed_msg "(1.0release)更新github分支失败,自动重试"; + continue + else + ok_msg "(1.0release)更新github分支成功" + fi + break +done + +git checkout 1.0release && git merge srs.1.0release +ret=$?; if [[ 0 -ne $ret ]]; then failed_msg "(1.0release)合并github分支失败, ret=$ret"; exit $ret; fi +ok_msg "(1.0release)合并github分支成功" + +############################################# +# push +############################################# for ((;;)); do git push From ffabcec191560301b1ff09b1428040c70caf0a45 Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 10 Oct 2014 09:51:25 +0800 Subject: [PATCH 006/800] update csdn mirror, add 1.0release --- trunk/scripts/csdn.mirror.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/trunk/scripts/csdn.mirror.sh b/trunk/scripts/csdn.mirror.sh index 6993e95773..e6e26a461b 100755 --- a/trunk/scripts/csdn.mirror.sh +++ b/trunk/scripts/csdn.mirror.sh @@ -65,6 +65,10 @@ for ((;;)); do break done +git checkout master && git merge srs.master +ret=$?; if [[ 0 -ne $ret ]]; then failed_msg "(master)合并github分支失败, ret=$ret"; exit $ret; fi +ok_msg "(master)合并github分支成功" + ############################################# # branch 1.0release ############################################# From 7f4c113e57594f4e8c0cea60b6a8ab82f50f62aa Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 12 Oct 2014 20:57:59 +0800 Subject: [PATCH 007/800] add st-1.9 to research --- trunk/research/st-1.9/Makefile | 469 +++ trunk/research/st-1.9/README | 394 +++ trunk/research/st-1.9/common.h | 466 +++ trunk/research/st-1.9/docs/fig.gif | Bin 0 -> 5374 bytes trunk/research/st-1.9/docs/notes.html | 434 +++ trunk/research/st-1.9/docs/reference.html | 3120 +++++++++++++++++ trunk/research/st-1.9/docs/st.html | 504 +++ trunk/research/st-1.9/docs/timeout_heap.txt | 60 + trunk/research/st-1.9/event.c | 1449 ++++++++ trunk/research/st-1.9/examples/Makefile | 115 + trunk/research/st-1.9/examples/README | 98 + trunk/research/st-1.9/examples/error.c | 168 + trunk/research/st-1.9/examples/lookupdns.c | 103 + trunk/research/st-1.9/examples/proxy.c | 541 +++ trunk/research/st-1.9/examples/res.c | 305 ++ trunk/research/st-1.9/examples/server.c | 1025 ++++++ trunk/research/st-1.9/extensions/Makefile | 91 + trunk/research/st-1.9/extensions/README | 42 + trunk/research/st-1.9/extensions/common.h | 77 + trunk/research/st-1.9/extensions/dnscache.c | 190 + trunk/research/st-1.9/extensions/dnsres.c | 305 ++ trunk/research/st-1.9/extensions/lrucache.c | 343 ++ .../st-1.9/extensions/print_stk.patch | 367 ++ trunk/research/st-1.9/extensions/stx.h | 91 + trunk/research/st-1.9/extensions/stx_fileio.c | 197 ++ trunk/research/st-1.9/extensions/stx_fileio.h | 52 + trunk/research/st-1.9/extensions/testdns.c | 112 + trunk/research/st-1.9/io.c | 778 ++++ trunk/research/st-1.9/key.c | 121 + trunk/research/st-1.9/libst.def | 51 + trunk/research/st-1.9/md.S | 431 +++ trunk/research/st-1.9/md.h | 627 ++++ trunk/research/st-1.9/osguess.sh | 45 + trunk/research/st-1.9/public.h | 184 + trunk/research/st-1.9/sched.c | 672 ++++ trunk/research/st-1.9/st.pc.in | 10 + trunk/research/st-1.9/st.spec | 79 + trunk/research/st-1.9/stk.c | 173 + trunk/research/st-1.9/sync.c | 369 ++ 39 files changed, 14658 insertions(+) create mode 100644 trunk/research/st-1.9/Makefile create mode 100644 trunk/research/st-1.9/README create mode 100644 trunk/research/st-1.9/common.h create mode 100644 trunk/research/st-1.9/docs/fig.gif create mode 100644 trunk/research/st-1.9/docs/notes.html create mode 100644 trunk/research/st-1.9/docs/reference.html create mode 100644 trunk/research/st-1.9/docs/st.html create mode 100644 trunk/research/st-1.9/docs/timeout_heap.txt create mode 100644 trunk/research/st-1.9/event.c create mode 100644 trunk/research/st-1.9/examples/Makefile create mode 100644 trunk/research/st-1.9/examples/README create mode 100644 trunk/research/st-1.9/examples/error.c create mode 100644 trunk/research/st-1.9/examples/lookupdns.c create mode 100644 trunk/research/st-1.9/examples/proxy.c create mode 100644 trunk/research/st-1.9/examples/res.c create mode 100644 trunk/research/st-1.9/examples/server.c create mode 100644 trunk/research/st-1.9/extensions/Makefile create mode 100644 trunk/research/st-1.9/extensions/README create mode 100644 trunk/research/st-1.9/extensions/common.h create mode 100644 trunk/research/st-1.9/extensions/dnscache.c create mode 100644 trunk/research/st-1.9/extensions/dnsres.c create mode 100644 trunk/research/st-1.9/extensions/lrucache.c create mode 100644 trunk/research/st-1.9/extensions/print_stk.patch create mode 100644 trunk/research/st-1.9/extensions/stx.h create mode 100644 trunk/research/st-1.9/extensions/stx_fileio.c create mode 100644 trunk/research/st-1.9/extensions/stx_fileio.h create mode 100644 trunk/research/st-1.9/extensions/testdns.c create mode 100644 trunk/research/st-1.9/io.c create mode 100644 trunk/research/st-1.9/key.c create mode 100644 trunk/research/st-1.9/libst.def create mode 100644 trunk/research/st-1.9/md.S create mode 100644 trunk/research/st-1.9/md.h create mode 100644 trunk/research/st-1.9/osguess.sh create mode 100644 trunk/research/st-1.9/public.h create mode 100644 trunk/research/st-1.9/sched.c create mode 100644 trunk/research/st-1.9/st.pc.in create mode 100644 trunk/research/st-1.9/st.spec create mode 100644 trunk/research/st-1.9/stk.c create mode 100644 trunk/research/st-1.9/sync.c diff --git a/trunk/research/st-1.9/Makefile b/trunk/research/st-1.9/Makefile new file mode 100644 index 0000000000..a38ac2b625 --- /dev/null +++ b/trunk/research/st-1.9/Makefile @@ -0,0 +1,469 @@ +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Netscape Portable Runtime library. +# +# The Initial Developer of the Original Code is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1994-2000 Netscape Communications Corporation. All +# Rights Reserved. +# +# Contributor(s): Silicon Graphics, Inc. +# +# Portions created by SGI are Copyright (C) 2000-2001 Silicon +# Graphics, Inc. All Rights Reserved. +# +# Alternatively, the contents of this file may be used under the +# terms of the GNU General Public License Version 2 or later (the +# "GPL"), in which case the provisions of the GPL are applicable +# instead of those above. If you wish to allow use of your +# version of this file only under the terms of the GPL and not to +# allow others to use your version of this file under the MPL, +# indicate your decision by deleting the provisions above and +# replace them with the notice and other provisions required by +# the GPL. If you do not delete the provisions above, a recipient +# may use your version of this file under either the MPL or the +# GPL. + +# This is the full version of the libst library - modify carefully +VERSION = 1.9 + +########################## +# Supported OSes: +# +#OS = AIX +#OS = CYGWIN +#OS = DARWIN +#OS = FREEBSD +#OS = HPUX +#OS = HPUX_64 +#OS = IRIX +#OS = IRIX_64 +#OS = LINUX +#OS = NETBSD +#OS = OPENBSD +#OS = OSF1 +#OS = SOLARIS +#OS = SOLARIS_64 + +# Please see the "Other possible defines" section below for +# possible compilation options. +########################## + +CC = cc +AR = ar +LD = ld +RANLIB = ranlib +LN = ln + +SHELL = /bin/sh +ECHO = /bin/echo + +BUILD = DBG +TARGETDIR = $(OS)_$(shell uname -r)_$(BUILD) + +DEFINES = -D$(OS) +CFLAGS = +SFLAGS = +ARFLAGS = -rv +LNFLAGS = -s +DSO_SUFFIX = so + +MAJOR = $(shell echo $(VERSION) | sed 's/^\([^\.]*\).*/\1/') +DESC = st.pc + +########################## +# Platform section. +# Possible targets: + +TARGETS = aix-debug aix-optimized \ + cygwin-debug cygwin-optimized \ + darwin-debug darwin-optimized \ + freebsd-debug freebsd-optimized \ + hpux-debug hpux-optimized \ + hpux-64-debug hpux-64-optimized \ + irix-n32-debug irix-n32-optimized \ + irix-64-debug irix-64-optimized \ + linux-debug linux-optimized \ + netbsd-debug netbsd-optimized \ + openbsd-debug openbsd-optimized \ + osf1-debug osf1-optimized \ + solaris-debug solaris-optimized \ + solaris-64-debug solaris-64-optimized + +# +# Platform specifics +# + +ifeq ($(OS), AIX) +AIX_VERSION = $(shell uname -v).$(shell uname -r) +TARGETDIR = $(OS)_$(AIX_VERSION)_$(BUILD) +CC = xlC +STATIC_ONLY = yes +ifeq ($(BUILD), OPT) +OTHER_FLAGS = -w +endif +ifneq ($(filter-out 4.1 4.2, $(AIX_VERSION)),) +DEFINES += -DMD_HAVE_SOCKLEN_T +endif +endif + +ifeq ($(OS), CYGWIN) +TARGETDIR = $(OS)_$(BUILD) +CC = gcc +LD = gcc +DSO_SUFFIX = dll +SLIBRARY = $(TARGETDIR)/libst.dll.a +DLIBRARY = $(TARGETDIR)/libst.dll +DEF_FILE = $(TARGETDIR)/libst.def +LDFLAGS = libst.def -shared --enable-auto-image-base -Wl,--output-def,$(DEF_FILE),--out-implib,$(SLIBRARY) +OTHER_FLAGS = -Wall +endif + +ifeq ($(OS), DARWIN) +LD = cc +SFLAGS = -fPIC -fno-common +DSO_SUFFIX = dylib +RELEASE = $(shell uname -r | cut -d. -f1) +PPC = $(shell test $(RELEASE) -le 9 && echo yes) +INTEL = $(shell test $(RELEASE) -ge 9 && echo yes) +ifeq ($(PPC), yes) +CFLAGS += -arch ppc +LDFLAGS += -arch ppc +endif +ifeq ($(INTEL), yes) +CFLAGS += -arch i386 -arch x86_64 +LDFLAGS += -arch i386 -arch x86_64 +endif +LDFLAGS += -dynamiclib -install_name /sw/lib/libst.$(MAJOR).$(DSO_SUFFIX) -compatibility_version $(MAJOR) -current_version $(VERSION) +OTHER_FLAGS = -Wall +endif + +ifeq ($(OS), FREEBSD) +SFLAGS = -fPIC +LDFLAGS = -shared -soname=$(SONAME) -lc +OTHER_FLAGS = -Wall +ifeq ($(shell test -f /usr/include/sys/event.h && echo yes), yes) +DEFINES += -DMD_HAVE_KQUEUE +endif +endif + +ifeq (HPUX, $(findstring HPUX, $(OS))) +ifeq ($(OS), HPUX_64) +DEFINES = -DHPUX +CFLAGS = -Ae +DD64 +Z +else +CFLAGS = -Ae +DAportable +Z +endif +RANLIB = true +LDFLAGS = -b +DSO_SUFFIX = sl +endif + +ifeq (IRIX, $(findstring IRIX, $(OS))) +ifeq ($(OS), IRIX_64) +DEFINES = -DIRIX +ABIFLAG = -64 +else +ABIFLAG = -n32 +endif +RANLIB = true +CFLAGS = $(ABIFLAG) -mips3 +LDFLAGS = $(ABIFLAG) -shared +OTHER_FLAGS = -fullwarn +endif + +ifeq ($(OS), LINUX) +EXTRA_OBJS = $(TARGETDIR)/md.o +SFLAGS = -fPIC +LDFLAGS = -shared -soname=$(SONAME) -lc +OTHER_FLAGS = -Wall +ifeq ($(shell test -f /usr/include/sys/epoll.h && echo yes), yes) +DEFINES += -DMD_HAVE_EPOLL +endif +endif + +ifeq ($(OS), NETBSD) +SFLAGS = -fPIC +LDFLAGS = -shared -soname=$(SONAME) -lc +OTHER_FLAGS = -Wall +endif + +ifeq ($(OS), OPENBSD) +SFLAGS = -fPIC +LDFLAGS = -shared -soname=$(SONAME) -lc +OTHER_FLAGS = -Wall +ifeq ($(shell test -f /usr/include/sys/event.h && echo yes), yes) +DEFINES += -DMD_HAVE_KQUEUE +endif +endif + +ifeq ($(OS), OSF1) +RANLIB = true +LDFLAGS = -shared -all -expect_unresolved "*" +endif + +ifeq (SOLARIS, $(findstring SOLARIS, $(OS))) +TARGETDIR = $(OS)_$(shell uname -r | sed 's/^5/2/')_$(BUILD) +CC = gcc +LD = gcc +RANLIB = true +LDFLAGS = -G +OTHER_FLAGS = -Wall +ifeq ($(OS), SOLARIS_64) +DEFINES = -DSOLARIS +CFLAGS += -m64 +LDFLAGS += -m64 +endif +endif + +# +# End of platform section. +########################## + + +ifeq ($(BUILD), OPT) +OTHER_FLAGS += -O +else +OTHER_FLAGS += -g +DEFINES += -DDEBUG +endif + +########################## +# Other possible defines: +# To use poll(2) instead of select(2) for events checking: +# DEFINES += -DUSE_POLL +# You may prefer to use select for applications that have many threads +# using one file descriptor, and poll for applications that have many +# different file descriptors. With USE_POLL poll() is called with at +# least one pollfd per I/O-blocked thread, so 1000 threads sharing one +# descriptor will poll 1000 identical pollfds and select would be more +# efficient. But if the threads all use different descriptors poll() +# may be better depending on your operating system's implementation of +# poll and select. Really, it's up to you. Oh, and on some platforms +# poll() fails with more than a few dozen descriptors. +# +# Some platforms allow to define FD_SETSIZE (if select() is used), e.g.: +# DEFINES += -DFD_SETSIZE=4096 +# +# To use malloc(3) instead of mmap(2) for stack allocation: +# DEFINES += -DMALLOC_STACK +# +# To provision more than the default 16 thread-specific-data keys +# (but not too many!): +# DEFINES += -DST_KEYS_MAX= +# +# To start with more than the default 64 initial pollfd slots +# (but the table grows dynamically anyway): +# DEFINES += -DST_MIN_POLLFDS_SIZE= +# +# Note that you can also add these defines by specifying them as +# make/gmake arguments (without editing this Makefile). For example: +# +# make EXTRA_CFLAGS=-DUSE_POLL +# +# (replace make with gmake if needed). +# +# You can also modify the default selection of an alternative event +# notification mechanism. E.g., to enable kqueue(2) support (if it's not +# enabled by default): +# +# gmake EXTRA_CFLAGS=-DMD_HAVE_KQUEUE +# +# or to disable default epoll(4) support: +# +# make EXTRA_CFLAGS=-UMD_HAVE_EPOLL +# +########################## + +CFLAGS += $(DEFINES) $(OTHER_FLAGS) $(EXTRA_CFLAGS) + +OBJS = $(TARGETDIR)/sched.o \ + $(TARGETDIR)/stk.o \ + $(TARGETDIR)/sync.o \ + $(TARGETDIR)/key.o \ + $(TARGETDIR)/io.o \ + $(TARGETDIR)/event.o +OBJS += $(EXTRA_OBJS) +HEADER = $(TARGETDIR)/st.h +SLIBRARY = $(TARGETDIR)/libst.a +DLIBRARY = $(TARGETDIR)/libst.$(DSO_SUFFIX).$(VERSION) +EXAMPLES = examples + +LINKNAME = libst.$(DSO_SUFFIX) +SONAME = libst.$(DSO_SUFFIX).$(MAJOR) +FULLNAME = libst.$(DSO_SUFFIX).$(VERSION) + +ifeq ($(OS), CYGWIN) +SONAME = cygst.$(DSO_SUFFIX) +SLIBRARY = $(TARGETDIR)/libst.dll.a +DLIBRARY = $(TARGETDIR)/$(SONAME) +LINKNAME = +# examples directory does not compile under cygwin +EXAMPLES = +endif + +ifeq ($(OS), DARWIN) +LINKNAME = libst.$(DSO_SUFFIX) +SONAME = libst.$(MAJOR).$(DSO_SUFFIX) +FULLNAME = libst.$(VERSION).$(DSO_SUFFIX) +endif + +ifeq ($(STATIC_ONLY), yes) +LIBRARIES = $(SLIBRARY) +else +LIBRARIES = $(SLIBRARY) $(DLIBRARY) +endif + +ifeq ($(OS),) +ST_ALL = unknown +else +ST_ALL = $(TARGETDIR) $(LIBRARIES) $(HEADER) $(EXAMPLES) $(DESC) +endif + +all: $(ST_ALL) + +unknown: + @echo + @echo "Please specify one of the following targets:" + @echo + @for target in $(TARGETS); do echo $$target; done + @echo + +st.pc: st.pc.in + sed "s/@VERSION@/${VERSION}/g" < $< > $@ + +$(TARGETDIR): + if [ ! -d $(TARGETDIR) ]; then mkdir $(TARGETDIR); fi + +$(SLIBRARY): $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + $(RANLIB) $@ + rm -f obj; $(LN) $(LNFLAGS) $(TARGETDIR) obj + +$(DLIBRARY): $(OBJS:%.o=%-pic.o) + $(LD) $(LDFLAGS) $^ -o $@ + if test "$(LINKNAME)"; then \ + cd $(TARGETDIR); \ + rm -f $(SONAME) $(LINKNAME); \ + $(LN) $(LNFLAGS) $(FULLNAME) $(SONAME); \ + $(LN) $(LNFLAGS) $(FULLNAME) $(LINKNAME); \ + fi + +$(HEADER): public.h + rm -f $@ + cp public.h $@ + +$(TARGETDIR)/md.o: md.S + $(CC) $(CFLAGS) -c $< -o $@ + +$(TARGETDIR)/%.o: %.c common.h md.h + $(CC) $(CFLAGS) -c $< -o $@ + +examples:: + @echo Making $@ + @cd $@; $(MAKE) CC="$(CC)" CFLAGS="$(CFLAGS)" OS="$(OS)" TARGETDIR="$(TARGETDIR)" + +clean: + rm -rf *_OPT *_DBG obj st.pc + +########################## +# Pattern rules: + +ifneq ($(SFLAGS),) +# Compile with shared library options if it's a C file +$(TARGETDIR)/%-pic.o: %.c common.h md.h + $(CC) $(CFLAGS) $(SFLAGS) -c $< -o $@ +endif + +# Compile assembly as normal or C as normal if no SFLAGS +%-pic.o: %.o + rm -f $@; $(LN) $(LNFLAGS) $(. Install them with: + # rpm -i libst*.rpm +Requires GNU automake and rpm 3.0.3 or later. + +Debian users: + If you run potato, please upgrade to woody. + If you run woody, "apt-get install libst-dev" will get you v1.3. + If you run testing/unstable, you will get the newest available version. + If you *must* have the newest libst in woody, you may follow these + not-recommended instructions: + 1. Add "deb-src unstable main" to your + /etc/apt/sources.list + 2. apt-get update + 3. apt-get source st + 4. cd st-1.4 (or whatever version you got) + 5. debuild + 6. dpkg -i ../*.deb + +If your application uses autoconf to search for dependencies and you +want to search for a given version of libst, you can simply add + PKG_CHECK_MODULES(MYAPP, st >= 1.3 mumble >= 0.2.23) +to your configure.ac/in. This will define @MYAPP_LIBS@ and +@MYAPP_CFLAGS@ which you may then use in your Makefile.am/in files to +link against mumble and st. + + +LICENSE + +The State Threads library is a derivative of the Netscape Portable +Runtime library (NSPR). All source code in this directory is +distributed under the terms of the Mozilla Public License (MPL) version +1.1 or the GNU General Public License (GPL) version 2 or later. For +more information about these licenses please see +http://www.mozilla.org/MPL/ and http://www.gnu.org/copyleft/. + +All source code in the "examples" directory is distributed under the BSD +style license. + + +PLATFORMS + +Please see the "docs/notes.html" file for the list of currently +supported platforms. + + +DEBUGGER SUPPORT + +It's almost impossible to print SP and PC in a portable way. The only +way to see thread's stack platform-independently is to actually jump to +the saved context. That's what the _st_iterate_threads() function does. +Do the following to iterate over all threads: + +- set the _st_iterate_threads_flag to 1 in debugger +- set breakpoint at the _st_show_thread_stack() function + (which does nothing) +- call the _st_iterate_threads() function which jumps to the + next thread +- at each break you can explore thread's stack +- continue +- when iteration is complete, you return to the original + point (you can see thread id and a message as arguments of + the _st_show_thread_stack() function). + +You can call _st_iterate_threads() in three ways: + +- Insert it into your source code at the point you want to + go over threads. +- Just run application and this function will be called at + the first context switch. +- Call it directly from the debugger at any point. + +This works with gdb and dbx. + +Example using gdb: + +(gdb) set _st_iterate_threads_flag = 1 +(gdb) b _st_show_thread_stack +... +(gdb) call _st_iterate_threads() +... +(gdb) bt +... +(gdb) c +... +(gdb) bt +... +(gdb) c +... +and so on... + +_st_iterate_threads_flag will be set to 0 automatically +after iteration is over or you can set it to 0 at any time +to stop iteration. + +Sometimes gdb complains about SIGSEGV when you call a function +directly at gdb command-line. It can be ignored -- just call the +same function right away again, it works just fine. For example: + +(gdb) set _st_iterate_threads_flag = 1 +(gdb) b _st_show_thread_stack +Breakpoint 1 at 0x809bbbb: file sched.c, line 856. +(gdb) call _st_iterate_threads() +Program received signal SIGSEGV, Segmentation fault. +.... +(gdb) # just call the function again: +(gdb) call _st_iterate_threads() +Breakpoint 1, _st_show_thread_stack (thread=0x4017aee4, messg=0x80ae7a2 +"Iteration started") at sched.c:856 +856 } +.... + +You can use simple gdb command-line scripting to display +all threads and their stack traces at once: + +(gdb) while _st_iterate_threads_flag + >bt + >c + >end +.... + +Another script to stop at the thread with the specific thread id +(e.g., 0x40252ee4): + +(gdb) # set the flag again: +(gdb) set _st_iterate_threads_flag = 1 +(gdb) call _st_iterate_threads() +Breakpoint 1, _st_show_thread_stack (thread=0x4017aee4, messg=0x80ae7a2 +"Iteration started") at sched.c:856 +856 } +.... +(gdb) while thread != 0x40252ee4 + >c + >end +.... +.... +Breakpoint 1, _st_show_thread_stack (thread=0x40252ee4, messg=0x0) at +sched.c:856 +856 } +(gdb) bt +.... +(gdb) # don't want to continue iteration, unset the flag: +(gdb) set _st_iterate_threads_flag = 0 +(gdb) c +Continuing. +Breakpoint 1, _st_show_thread_stack (thread=0x0, messg=0x80ae78e "Iteration +completed") + at sched.c:856 +856 } +(gdb) c +Continuing. +(gdb) return +Make selected stack frame return now? (y or n) y +#0 0x4011254e in __select () + from /lib/libc.so.6 +(gdb) detach + + +CHANGE LOG + +Changes from 1.8 to 1.9. +------------------------ +o Support 32-bit and 64-bit Intel Macs. + +o Added ST_VERSION string, and ST_VERSION_MAJOR and ST_VERSION_MINOR + [bug 1796801]. + +o Fixed some compiler warnings, based on a patch from Brian Wellington + [bug 1932741]. + + +Changes from 1.7 to 1.8. +-------------------------- +o Added support for kqueue and epoll on platforms that support them. + Added ability to choose the event notification system at program + startup. + +o Long-overdue public definitions of ST_UTIME_NO_TIMEOUT (-1ULL) and + ST_UTIME_NO_WAIT (0) [bug 1514436]. + +o Documentation patch for st_utime() [bug 1514484]. + +o Documentation patch for st_timecache_set() [bug 1514486]. + +o Documentation patch for st_netfd_serialize_accept() [bug 1514494]. + +o Added st_writev_resid() [rfe 1538344]. + +o Added st_readv_resid() [rfe 1538768] and, for symmetry, st_readv(). + + +Changes from 1.6 to 1.7. +------------------------ +o Support glibc 2.4, which breaks programs that manipulate jump buffers. + Replaced Linux IA64 special cases with new md.S that covers all + Linux. + + +Changes from 1.5.2 to 1.6. +-------------------------- +none + + +Changes from 1.5.1 to 1.5.2. +---------------------------- +o Alfred Perlstein's context switch callback feature. + +o Claus Assmann's st_recvmsg/st_sendmsg wrappers. + +o Extra stack padding for platforms that need it. + +o Ron Arts's timeout clarifications in the reference manual. + +o Raymond Bero and Anton Berezin's AMD64 FreeBSD port. + +o Claus Assmann's AMD64 SunOS 5.10 port. + +o Claus Assmann's AMD64 OpenBSD port. + +o Michael Abd-El-Malek's Mac OS X port. + +o Michael Abd-El-Malek's stack printing patch. + + +Changes from 1.5.0 to 1.5.1. +---------------------------- +o Andreas Gustafsson's USE_POLL fix. + +o Gene's st_set_utime_function() enhancement. + + +Changes from 1.4 to 1.5.0. +-------------------------- +o Andreas Gustafsson's performance patch. + +o New extensions: Improved DNS resolver, generic LRU cache, in-process + DNS cache, and a program to test the resolver and cache. + +o Support for AMD Opteron 64-bit CPUs under Linux. + +o Support for SPARC-64 under Solaris. + +o Andreas Gustafsson's support for VAX under NetBSD. + +o Changed unportable #warning directives in md.h to #error. + + +Changes from 1.3 to 1.4. +------------------------ +o Andreas Gustafsson's NetBSD port. + +o Wesley W. Terpstra's Darwin (MacOS X) port. + +o Support for many CPU architectures under Linux and *BSD. + +o Renamed private typedefs so they don't conflict with public ones any + more. + +o common.h now includes public.h for strict prototyping. + +o Joshua Levy's recommendation to make st_connect() and st_sendto() + accept const struct sockaddr pointers, as the originals do. + +o Clarified the documentation regarding blocking vs. non-blocking I/O. + +o Cygwin support. + +o Created the extensions directory. + +o Fixed warnings from ia64asm.S. + + +Changes from 1.2 to 1.3. +------------------------ +o Added st_read_resid() and st_write_resid() to allow the caller to know + how much data was transferred before an error occurred. Updated + documentation. + +o Updated project link, copyrights, and documentation regarding + timeouts. Added comment to st_connect(). + +o Optimized the _st_add_sleep_q() function in sched.c. Now we walk the + sleep queue *backward* when inserting a thread into it. When you + have lots (hundreds) of threads and several timeout values, it takes + a while to insert a thread at the appropriate point in the sleep + queue. The idea is that often this appropriate point is closer to + the end of the queue rather than the beginning. Measurements show + performance improves with this change. In any case this change + should do no harm. + +o Added a hint of when to define USE_POLL and when not to, to the + Makefile. + +o Added debugging support (files common.h and sched.c). See above. + +o Decreased the number of reallocations of _ST_POLLFDS in sched.c. + Inspired by Lev Walkin. + +o Fixed st_usleep(-1) and st_sleep(-1), and added a warning to the + documentation about too-large timeouts. + +o Linux/*BSD Alpha port. + +o Wesley W. Terpstra modernized the build process: + - properly build relocatable libraries under bsd and linux + - use library versioning + - added rpm spec file + - added debian/ files + See above for build instructions. + + +Changes from 1.1 to 1.2. +------------------------ +o Added st_randomize_stacks(). + +o Added a patch contributed by Sascha Schumann. + + +Changes from 1.0 to 1.1. +------------------------ +o Relicensed under dual MPL-GPL. + +o OpenBSD port. + +o Compile-time option to use poll() instead of select() for + event polling (see Makefile). + This is useful if you want to support a large number of open + file descriptors (larger than FD_SETSIZE) within a single + process. + +o Linux IA-64 port. + Two issues make IA-64 different from other platforms: + + - Besides the traditional call stack in memory, IA-64 uses the + general register stack. Thus each thread needs a backing store + for the register stack in addition to the memory stack. + + - Current implementation of setjmp()/longjmp() can not be used + for thread context-switching since it assumes that only one + register stack exists. Using special assembly functions for + context-switching is unavoidable. + +o Thread stack capping on IRIX. + This allows some profiling tools (such as SpeedShop) to know when + to stop unwinding the stack. Without this libexc, used by SpeedShop, + traces right off the stack and crashes. + +o Miscellaneous documentation additions. + + +COPYRIGHTS + +Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. +All Rights Reserved. diff --git a/trunk/research/st-1.9/common.h b/trunk/research/st-1.9/common.h new file mode 100644 index 0000000000..4df39e9e4a --- /dev/null +++ b/trunk/research/st-1.9/common.h @@ -0,0 +1,466 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Portable Runtime library. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Silicon Graphics, Inc. + * + * Portions created by SGI are Copyright (C) 2000-2001 Silicon + * Graphics, Inc. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +/* + * This file is derived directly from Netscape Communications Corporation, + * and consists of extensive modifications made during the year(s) 1999-2000. + */ + +#ifndef __ST_COMMON_H__ +#define __ST_COMMON_H__ + +#include +#include +#include +#include +#include + +/* Enable assertions only if DEBUG is defined */ +#ifndef DEBUG +#define NDEBUG +#endif +#include +#define ST_ASSERT(expr) assert(expr) + +#define ST_BEGIN_MACRO { +#define ST_END_MACRO } + +#ifdef DEBUG +#define ST_HIDDEN /*nothing*/ +#else +#define ST_HIDDEN static +#endif + +#include "public.h" +#include "md.h" + + +/***************************************** + * Circular linked list definitions + */ + +typedef struct _st_clist { + struct _st_clist *next; + struct _st_clist *prev; +} _st_clist_t; + +/* Insert element "_e" into the list, before "_l" */ +#define ST_INSERT_BEFORE(_e,_l) \ + ST_BEGIN_MACRO \ + (_e)->next = (_l); \ + (_e)->prev = (_l)->prev; \ + (_l)->prev->next = (_e); \ + (_l)->prev = (_e); \ + ST_END_MACRO + +/* Insert element "_e" into the list, after "_l" */ +#define ST_INSERT_AFTER(_e,_l) \ + ST_BEGIN_MACRO \ + (_e)->next = (_l)->next; \ + (_e)->prev = (_l); \ + (_l)->next->prev = (_e); \ + (_l)->next = (_e); \ + ST_END_MACRO + +/* Return the element following element "_e" */ +#define ST_NEXT_LINK(_e) ((_e)->next) + +/* Append an element "_e" to the end of the list "_l" */ +#define ST_APPEND_LINK(_e,_l) ST_INSERT_BEFORE(_e,_l) + +/* Insert an element "_e" at the head of the list "_l" */ +#define ST_INSERT_LINK(_e,_l) ST_INSERT_AFTER(_e,_l) + +/* Return the head/tail of the list */ +#define ST_LIST_HEAD(_l) (_l)->next +#define ST_LIST_TAIL(_l) (_l)->prev + +/* Remove the element "_e" from it's circular list */ +#define ST_REMOVE_LINK(_e) \ + ST_BEGIN_MACRO \ + (_e)->prev->next = (_e)->next; \ + (_e)->next->prev = (_e)->prev; \ + ST_END_MACRO + +/* Return non-zero if the given circular list "_l" is empty, */ +/* zero if the circular list is not empty */ +#define ST_CLIST_IS_EMPTY(_l) \ + ((_l)->next == (_l)) + +/* Initialize a circular list */ +#define ST_INIT_CLIST(_l) \ + ST_BEGIN_MACRO \ + (_l)->next = (_l); \ + (_l)->prev = (_l); \ + ST_END_MACRO + +#define ST_INIT_STATIC_CLIST(_l) \ + {(_l), (_l)} + + +/***************************************** + * Basic types definitions + */ + +typedef void (*_st_destructor_t)(void *); + + +typedef struct _st_stack { + _st_clist_t links; + char *vaddr; /* Base of stack's allocated memory */ + int vaddr_size; /* Size of stack's allocated memory */ + int stk_size; /* Size of usable portion of the stack */ + char *stk_bottom; /* Lowest address of stack's usable portion */ + char *stk_top; /* Highest address of stack's usable portion */ + void *sp; /* Stack pointer from C's point of view */ +#ifdef __ia64__ + void *bsp; /* Register stack backing store pointer */ +#endif +} _st_stack_t; + + +typedef struct _st_cond { + _st_clist_t wait_q; /* Condition variable wait queue */ +} _st_cond_t; + + +typedef struct _st_thread _st_thread_t; + +struct _st_thread { + int state; /* Thread's state */ + int flags; /* Thread's flags */ + + void *(*start)(void *arg); /* The start function of the thread */ + void *arg; /* Argument of the start function */ + void *retval; /* Return value of the start function */ + + _st_stack_t *stack; /* Info about thread's stack */ + + _st_clist_t links; /* For putting on run/sleep/zombie queue */ + _st_clist_t wait_links; /* For putting on mutex/condvar wait queue */ +#ifdef DEBUG + _st_clist_t tlink; /* For putting on thread queue */ +#endif + + st_utime_t due; /* Wakeup time when thread is sleeping */ + _st_thread_t *left; /* For putting in timeout heap */ + _st_thread_t *right; /* -- see docs/timeout_heap.txt for details */ + int heap_index; + + void **private_data; /* Per thread private data */ + + _st_cond_t *term; /* Termination condition variable for join */ + + jmp_buf context; /* Thread's context */ +}; + + +typedef struct _st_mutex { + _st_thread_t *owner; /* Current mutex owner */ + _st_clist_t wait_q; /* Mutex wait queue */ +} _st_mutex_t; + + +typedef struct _st_pollq { + _st_clist_t links; /* For putting on io queue */ + _st_thread_t *thread; /* Polling thread */ + struct pollfd *pds; /* Array of poll descriptors */ + int npds; /* Length of the array */ + int on_ioq; /* Is it on ioq? */ +} _st_pollq_t; + + +typedef struct _st_eventsys_ops { + const char *name; /* Name of this event system */ + int val; /* Type of this event system */ + int (*init)(void); /* Initialization */ + void (*dispatch)(void); /* Dispatch function */ + int (*pollset_add)(struct pollfd *, int); /* Add descriptor set */ + void (*pollset_del)(struct pollfd *, int); /* Delete descriptor set */ + int (*fd_new)(int); /* New descriptor allocated */ + int (*fd_close)(int); /* Descriptor closed */ + int (*fd_getlimit)(void); /* Descriptor hard limit */ +} _st_eventsys_t; + + +typedef struct _st_vp { + _st_thread_t *idle_thread; /* Idle thread for this vp */ + st_utime_t last_clock; /* The last time we went into vp_check_clock() */ + + _st_clist_t run_q; /* run queue for this vp */ + _st_clist_t io_q; /* io queue for this vp */ + _st_clist_t zombie_q; /* zombie queue for this vp */ +#ifdef DEBUG + _st_clist_t thread_q; /* all threads of this vp */ +#endif + int pagesize; + + _st_thread_t *sleep_q; /* sleep queue for this vp */ + int sleepq_size; /* number of threads on sleep queue */ + +#ifdef ST_SWITCH_CB + st_switch_cb_t switch_out_cb; /* called when a thread is switched out */ + st_switch_cb_t switch_in_cb; /* called when a thread is switched in */ +#endif +} _st_vp_t; + + +typedef struct _st_netfd { + int osfd; /* Underlying OS file descriptor */ + int inuse; /* In-use flag */ + void *private_data; /* Per descriptor private data */ + _st_destructor_t destructor; /* Private data destructor function */ + void *aux_data; /* Auxiliary data for internal use */ + struct _st_netfd *next; /* For putting on the free list */ +} _st_netfd_t; + + +/***************************************** + * Current vp, thread, and event system + */ + +extern _st_vp_t _st_this_vp; +extern _st_thread_t *_st_this_thread; +extern _st_eventsys_t *_st_eventsys; + +#define _ST_CURRENT_THREAD() (_st_this_thread) +#define _ST_SET_CURRENT_THREAD(_thread) (_st_this_thread = (_thread)) + +#define _ST_LAST_CLOCK (_st_this_vp.last_clock) + +#define _ST_RUNQ (_st_this_vp.run_q) +#define _ST_IOQ (_st_this_vp.io_q) +#define _ST_ZOMBIEQ (_st_this_vp.zombie_q) +#ifdef DEBUG +#define _ST_THREADQ (_st_this_vp.thread_q) +#endif + +#define _ST_PAGE_SIZE (_st_this_vp.pagesize) + +#define _ST_SLEEPQ (_st_this_vp.sleep_q) +#define _ST_SLEEPQ_SIZE (_st_this_vp.sleepq_size) + +#define _ST_VP_IDLE() (*_st_eventsys->dispatch)() + + +/***************************************** + * vp queues operations + */ + +#define _ST_ADD_IOQ(_pq) ST_APPEND_LINK(&_pq.links, &_ST_IOQ) +#define _ST_DEL_IOQ(_pq) ST_REMOVE_LINK(&_pq.links) + +#define _ST_ADD_RUNQ(_thr) ST_APPEND_LINK(&(_thr)->links, &_ST_RUNQ) +#define _ST_DEL_RUNQ(_thr) ST_REMOVE_LINK(&(_thr)->links) + +#define _ST_ADD_SLEEPQ(_thr, _timeout) _st_add_sleep_q(_thr, _timeout) +#define _ST_DEL_SLEEPQ(_thr) _st_del_sleep_q(_thr) + +#define _ST_ADD_ZOMBIEQ(_thr) ST_APPEND_LINK(&(_thr)->links, &_ST_ZOMBIEQ) +#define _ST_DEL_ZOMBIEQ(_thr) ST_REMOVE_LINK(&(_thr)->links) + +#ifdef DEBUG +#define _ST_ADD_THREADQ(_thr) ST_APPEND_LINK(&(_thr)->tlink, &_ST_THREADQ) +#define _ST_DEL_THREADQ(_thr) ST_REMOVE_LINK(&(_thr)->tlink) +#endif + + +/***************************************** + * Thread states and flags + */ + +#define _ST_ST_RUNNING 0 +#define _ST_ST_RUNNABLE 1 +#define _ST_ST_IO_WAIT 2 +#define _ST_ST_LOCK_WAIT 3 +#define _ST_ST_COND_WAIT 4 +#define _ST_ST_SLEEPING 5 +#define _ST_ST_ZOMBIE 6 +#define _ST_ST_SUSPENDED 7 + +#define _ST_FL_PRIMORDIAL 0x01 +#define _ST_FL_IDLE_THREAD 0x02 +#define _ST_FL_ON_SLEEPQ 0x04 +#define _ST_FL_INTERRUPT 0x08 +#define _ST_FL_TIMEDOUT 0x10 + + +/***************************************** + * Pointer conversion + */ + +#ifndef offsetof +#define offsetof(type, identifier) ((size_t)&(((type *)0)->identifier)) +#endif + +#define _ST_THREAD_PTR(_qp) \ + ((_st_thread_t *)((char *)(_qp) - offsetof(_st_thread_t, links))) + +#define _ST_THREAD_WAITQ_PTR(_qp) \ + ((_st_thread_t *)((char *)(_qp) - offsetof(_st_thread_t, wait_links))) + +#define _ST_THREAD_STACK_PTR(_qp) \ + ((_st_stack_t *)((char*)(_qp) - offsetof(_st_stack_t, links))) + +#define _ST_POLLQUEUE_PTR(_qp) \ + ((_st_pollq_t *)((char *)(_qp) - offsetof(_st_pollq_t, links))) + +#ifdef DEBUG +#define _ST_THREAD_THREADQ_PTR(_qp) \ + ((_st_thread_t *)((char *)(_qp) - offsetof(_st_thread_t, tlink))) +#endif + + +/***************************************** + * Constants + */ + +#ifndef ST_UTIME_NO_TIMEOUT +#define ST_UTIME_NO_TIMEOUT ((st_utime_t) -1LL) +#endif + +#ifndef __ia64__ +#define ST_DEFAULT_STACK_SIZE (64*1024) +#else +#define ST_DEFAULT_STACK_SIZE (128*1024) /* Includes register stack size */ +#endif + +#ifndef ST_KEYS_MAX +#define ST_KEYS_MAX 16 +#endif + +#ifndef ST_MIN_POLLFDS_SIZE +#define ST_MIN_POLLFDS_SIZE 64 +#endif + + +/***************************************** + * Threads context switching + */ + +#ifdef DEBUG +void _st_iterate_threads(void); +#define ST_DEBUG_ITERATE_THREADS() _st_iterate_threads() +#else +#define ST_DEBUG_ITERATE_THREADS() +#endif + +#ifdef ST_SWITCH_CB +#define ST_SWITCH_OUT_CB(_thread) \ + if (_st_this_vp.switch_out_cb != NULL && \ + _thread != _st_this_vp.idle_thread && \ + _thread->state != _ST_ST_ZOMBIE) { \ + _st_this_vp.switch_out_cb(); \ + } +#define ST_SWITCH_IN_CB(_thread) \ + if (_st_this_vp.switch_in_cb != NULL && \ + _thread != _st_this_vp.idle_thread && \ + _thread->state != _ST_ST_ZOMBIE) { \ + _st_this_vp.switch_in_cb(); \ + } +#else +#define ST_SWITCH_OUT_CB(_thread) +#define ST_SWITCH_IN_CB(_thread) +#endif + +/* + * Switch away from the current thread context by saving its state and + * calling the thread scheduler + */ +#define _ST_SWITCH_CONTEXT(_thread) \ + ST_BEGIN_MACRO \ + ST_SWITCH_OUT_CB(_thread); \ + if (!MD_SETJMP((_thread)->context)) { \ + _st_vp_schedule(); \ + } \ + ST_DEBUG_ITERATE_THREADS(); \ + ST_SWITCH_IN_CB(_thread); \ + ST_END_MACRO + +/* + * Restore a thread context that was saved by _ST_SWITCH_CONTEXT or + * initialized by _ST_INIT_CONTEXT + */ +#define _ST_RESTORE_CONTEXT(_thread) \ + ST_BEGIN_MACRO \ + _ST_SET_CURRENT_THREAD(_thread); \ + MD_LONGJMP((_thread)->context, 1); \ + ST_END_MACRO + +/* + * Initialize the thread context preparing it to execute _main + */ +#ifdef MD_INIT_CONTEXT +#define _ST_INIT_CONTEXT MD_INIT_CONTEXT +#else +#error Unknown OS +#endif + +/* + * Number of bytes reserved under the stack "bottom" + */ +#define _ST_STACK_PAD_SIZE MD_STACK_PAD_SIZE + + +/***************************************** + * Forward declarations + */ + +void _st_vp_schedule(void); +void _st_vp_check_clock(void); +void *_st_idle_thread_start(void *arg); +void _st_thread_main(void); +void _st_thread_cleanup(_st_thread_t *thread); +void _st_add_sleep_q(_st_thread_t *thread, st_utime_t timeout); +void _st_del_sleep_q(_st_thread_t *thread); +_st_stack_t *_st_stack_new(int stack_size); +void _st_stack_free(_st_stack_t *ts); +int _st_io_init(void); + +st_utime_t st_utime(void); +_st_cond_t *st_cond_new(void); +int st_cond_destroy(_st_cond_t *cvar); +int st_cond_timedwait(_st_cond_t *cvar, st_utime_t timeout); +int st_cond_signal(_st_cond_t *cvar); +ssize_t st_read(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout); +ssize_t st_write(_st_netfd_t *fd, const void *buf, size_t nbyte, + st_utime_t timeout); +int st_poll(struct pollfd *pds, int npds, st_utime_t timeout); +_st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg, + int joinable, int stk_size); + +#endif /* !__ST_COMMON_H__ */ + diff --git a/trunk/research/st-1.9/docs/fig.gif b/trunk/research/st-1.9/docs/fig.gif new file mode 100644 index 0000000000000000000000000000000000000000..7265a05db4f516b44fcf37c3949922ff4f62999d GIT binary patch literal 5374 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 literal 0 HcmV?d00001 diff --git a/trunk/research/st-1.9/docs/notes.html b/trunk/research/st-1.9/docs/notes.html new file mode 100644 index 0000000000..5a24369e22 --- /dev/null +++ b/trunk/research/st-1.9/docs/notes.html @@ -0,0 +1,434 @@ + + +State Threads Library Programming Notes + + +

Programming Notes

+

+ +

+ +

+


+

+ +

Porting

+The State Threads library uses OS concepts that are available in some +form on most UNIX platforms, making the library very portable across +many flavors of UNIX. However, there are several parts of the library +that rely on platform-specific features. Here is the list of such parts: +

+

    +
  • Thread context initialization: Two ingredients of the +jmp_buf +data structure (the program counter and the stack pointer) have to be +manually set in the thread creation routine. The jmp_buf data +structure is defined in the setjmp.h header file and differs from +platform to platform. Usually the program counter is a structure member +with PC in the name and the stack pointer is a structure member +with SP in the name. One can also look in the +Netscape's NSPR library source +which already has this code for many UNIX-like platforms +(mozilla/nsprpub/pr/include/md/*.h files). +

    +Note that on some BSD-derived platforms _setjmp(3)/_longjmp(3) +calls should be used instead of setjmp(3)/longjmp(3) (that is +the calls that manipulate only the stack and registers and do not +save and restore the process's signal mask).

  • +

    +Starting with glibc 2.4 on Linux the opacity of the jmp_buf data +structure is enforced by setjmp(3)/longjmp(3) so the +jmp_buf ingredients cannot be accessed directly anymore (unless +special environmental variable LD_POINTER_GUARD is set before application +execution). To avoid dependency on custom environment, the State Threads +library provides setjmp/longjmp replacement functions for +all Intel CPU architectures. Other CPU architectures can also be easily +supported (the setjmp/longjmp source code is widely available for +many CPU architectures). +

    +

  • High resolution time function: Some platforms (IRIX, Solaris) +provide a high resolution time function based on the free running hardware +counter. This function returns the time counted since some arbitrary +moment in the past (usually machine power up time). It is not correlated in +any way to the time of day, and thus is not subject to resetting, +drifting, etc. This type of time is ideal for tasks where cheap, accurate +interval timing is required. If such a function is not available on a +particular platform, the gettimeofday(3) function can be used +(though on some platforms it involves a system call). +

    +

  • The stack growth direction: The library needs to know whether the +stack grows toward lower (down) or higher (up) memory addresses. +One can write a simple test program that detects the stack growth direction +on a particular platform.
  • +

    +

  • Non-blocking attribute inheritance: On some platforms (e.g. IRIX) +the socket created as a result of the accept(2) call inherits the +non-blocking attribute of the listening socket. One needs to consult the manual +pages or write a simple test program to see if this applies to a specific +platform.
  • +

    +

  • Anonymous memory mapping: The library allocates memory segments +for thread stacks by doing anonymous memory mapping (mmap(2)). This +mapping is somewhat different on SVR4 and BSD4.3 derived platforms. +

    +The memory mapping can be avoided altogether by using malloc(3) for +stack allocation. In this case the MALLOC_STACK macro should be +defined.

  • +
+

+All machine-dependent feature test macros should be defined in the +md.h header file. The assembly code for setjmp/longjmp +replacement functions for all CPU architectures should be placed in +the md.S file. +

+The current version of the library is ported to: +

    +
  • IRIX 6.x (both 32 and 64 bit)
  • +
  • Linux (kernel 2.x and glibc 2.x) on x86, Alpha, MIPS and MIPSEL, + SPARC, ARM, PowerPC, 68k, HPPA, S390, IA-64, and Opteron (AMD-64)
  • +
  • Solaris 2.x (SunOS 5.x) on x86, AMD64, SPARC, and SPARC-64
  • +
  • AIX 4.x
  • +
  • HP-UX 11 (both 32 and 64 bit)
  • +
  • Tru64/OSF1
  • +
  • FreeBSD on x86, AMD64, and Alpha
  • +
  • OpenBSD on x86, AMD64, Alpha, and SPARC
  • +
  • NetBSD on x86, Alpha, SPARC, and VAX
  • +
  • MacOS X (Darwin) on PowerPC (32 bit) and Intel (both 32 and 64 bit) [universal]
  • +
  • Cygwin
  • +
+

+ + +

Signals

+Signal handling in an application using State Threads should be treated the +same way as in a classical UNIX process application. There is no such +thing as per-thread signal mask, all threads share the same signal handlers, +and only asynchronous-safe functions can be used in signal handlers. +However, there is a way to process signals synchronously by converting a +signal event to an I/O event: a signal catching function does a write to +a pipe which will be processed synchronously by a dedicated signal handling +thread. The following code demonstrates this technique (error handling is +omitted for clarity): +
+
+/* Per-process pipe which is used as a signal queue. */
+/* Up to PIPE_BUF/sizeof(int) signals can be queued up. */
+int sig_pipe[2];
+
+/* Signal catching function. */
+/* Converts signal event to I/O event. */
+void sig_catcher(int signo)
+{
+  int err;
+
+  /* Save errno to restore it after the write() */
+  err = errno;
+  /* write() is reentrant/async-safe */
+  write(sig_pipe[1], &signo, sizeof(int));
+  errno = err;
+}
+
+/* Signal processing function. */
+/* This is the "main" function of the signal processing thread. */
+void *sig_process(void *arg)
+{
+  st_netfd_t nfd;
+  int signo;
+
+  nfd = st_netfd_open(sig_pipe[0]);
+
+  for ( ; ; ) {
+    /* Read the next signal from the pipe */
+    st_read(nfd, &signo, sizeof(int), ST_UTIME_NO_TIMEOUT);
+
+    /* Process signal synchronously */
+    switch (signo) {
+    case SIGHUP:
+      /* do something here - reread config files, etc. */
+      break;
+    case SIGTERM:
+      /* do something here - cleanup, etc. */
+      break;
+      /*      .
+              .
+         Other signals
+              .
+              .
+      */
+    }
+  }
+
+  return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+  struct sigaction sa;
+        .
+        .
+        .
+
+  /* Create signal pipe */
+  pipe(sig_pipe);
+
+  /* Create signal processing thread */
+  st_thread_create(sig_process, NULL, 0, 0);
+
+  /* Install sig_catcher() as a signal handler */
+  sa.sa_handler = sig_catcher;
+  sigemptyset(&sa.sa_mask);
+  sa.sa_flags = 0;
+  sigaction(SIGHUP, &sa, NULL);
+
+  sa.sa_handler = sig_catcher;
+  sigemptyset(&sa.sa_mask);
+  sa.sa_flags = 0;
+  sigaction(SIGTERM, &sa, NULL);
+
+        .
+        .
+        .
+      
+}
+
+
+

+Note that if multiple processes are used (see below), the signal pipe should +be initialized after the fork(2) call so that each process has its +own private pipe. +

+ + +

Intra-Process Synchronization

+Due to the event-driven nature of the library scheduler, the thread context +switch (process state change) can only happen in a well-known set of +library functions. This set includes functions in which a thread may +"block": I/O functions (st_read(), st_write(), etc.), +sleep functions (st_sleep(), etc.), and thread synchronization +functions (st_thread_join(), st_cond_wait(), etc.). As a result, +process-specific global data need not to be protected by locks since a thread +cannot be rescheduled while in a critical section (and only one thread at a +time can access the same memory location). By the same token, +non thread-safe functions (in a traditional sense) can be safely used with +the State Threads. The library's mutex facilities are practically useless +for a correctly written application (no blocking functions in critical +section) and are provided mostly for completeness. This absence of locking +greatly simplifies an application design and provides a foundation for +scalability. +

+ + +

Inter-Process Synchronization

+The State Threads library makes it possible to multiplex a large number +of simultaneous connections onto a much smaller number of separate +processes, where each process uses a many-to-one user-level threading +implementation (N of M:1 mappings rather than one M:N +mapping used in native threading libraries on some platforms). This design +is key to the application's scalability. One can think about it as if a +set of all threads is partitioned into separate groups (processes) where +each group has a separate pool of resources (virtual address space, file +descriptors, etc.). An application designer has full control of how many +groups (processes) an application creates and what resources, if any, +are shared among different groups via standard UNIX inter-process +communication (IPC) facilities.

+There are several reasons for creating multiple processes: +

+

    +
  • To take advantage of multiple hardware entities (CPUs, disks, etc.) +available in the system (hardware parallelism).
  • +

    +

  • To reduce risk of losing a large number of user connections when one of +the processes crashes. For example, if C user connections (threads) +are multiplexed onto P processes and one of the processes crashes, +only a fraction (C/P) of all connections will be lost.
  • +

    +

  • To overcome per-process resource limitations imposed by the OS. For +example, if select(2) is used for event polling, the number of +simultaneous connections (threads) per process is +limited by the FD_SETSIZE parameter (see select(2)). +If FD_SETSIZE is equal to 1024 and each connection needs one file +descriptor, then an application should create 10 processes to support 10,000 +simultaneous connections.
  • +
+

+Ideally all user sessions are completely independent, so there is no need for +inter-process communication. It is always better to have several separate +smaller process-specific resources (e.g., data caches) than to have one large +resource shared (and modified) by all processes. Sometimes, however, there +is a need to share a common resource among different processes. In that case, +standard UNIX IPC facilities can be used. In addition to that, there is a way +to synchronize different processes so that only the thread accessing the +shared resource will be suspended (but not the entire process) if that resource +is unavailable. In the following code fragment a pipe is used as a counting +semaphore for inter-process synchronization: +

+#ifndef PIPE_BUF
+#define PIPE_BUF 512  /* POSIX */
+#endif
+
+/* Semaphore data structure */
+typedef struct ipc_sem {
+  st_netfd_t rdfd;  /* read descriptor */
+  st_netfd_t wrfd;  /* write descriptor */
+} ipc_sem_t;
+
+/* Create and initialize the semaphore. Should be called before fork(2). */
+/* 'value' must be less than PIPE_BUF. */
+/* If 'value' is 1, the semaphore works as mutex. */
+ipc_sem_t *ipc_sem_create(int value)
+{
+  ipc_sem_t *sem;
+  int p[2];
+  char b[PIPE_BUF];
+
+  /* Error checking is omitted for clarity */
+  sem = malloc(sizeof(ipc_sem_t));
+
+  /* Create the pipe */
+  pipe(p);
+  sem->rdfd = st_netfd_open(p[0]);
+  sem->wrfd = st_netfd_open(p[1]);
+
+  /* Initialize the semaphore: put 'value' bytes into the pipe */
+  write(p[1], b, value);
+
+  return sem;
+}
+
+/* Try to decrement the "value" of the semaphore. */
+/* If "value" is 0, the calling thread blocks on the semaphore. */
+int ipc_sem_wait(ipc_sem_t *sem)
+{
+  char c;
+
+  /* Read one byte from the pipe */
+  if (st_read(sem->rdfd, &c, 1, ST_UTIME_NO_TIMEOUT) != 1)
+    return -1;
+
+  return 0;
+}
+
+/* Increment the "value" of the semaphore. */
+int ipc_sem_post(ipc_sem_t *sem)
+{
+  char c;
+
+  if (st_write(sem->wrfd, &c, 1, ST_UTIME_NO_TIMEOUT) != 1)
+    return -1;
+
+  return 0;
+}
+
+
+

+ +Generally, the following steps should be followed when writing an application +using the State Threads library: +

+

    +
  1. Initialize the library (st_init()).
  2. +

    +

  3. Create resources that will be shared among different processes: + create and bind listening sockets, create shared memory segments, IPC + channels, synchronization primitives, etc.
  4. +

    +

  5. Create several processes (fork(2)). The parent process should + either exit or become a "watchdog" (e.g., it starts a new process when + an existing one crashes, does a cleanup upon application termination, + etc.).
  6. +

    +

  7. In each child process create a pool of threads + (st_thread_create()) to handle user connections.
  8. +
+

+ + +

Non-Network I/O

+ +The State Threads architecture uses non-blocking I/O on +st_netfd_t objects for concurrent processing of multiple user +connections. This architecture has a drawback: the entire process and +all its threads may block for the duration of a disk or other +non-network I/O operation, whether through State Threads I/O functions, +direct system calls, or standard I/O functions. (This is applicable +mostly to disk reads; disk writes are usually performed +asynchronously -- data goes to the buffer cache to be written to disk +later.) Fortunately, disk I/O (unlike network I/O) usually takes a +finite and predictable amount of time, but this may not be true for +special devices or user input devices (including stdin). Nevertheless, +such I/O reduces throughput of the system and increases response times. +There are several ways to design an application to overcome this +drawback: + +

+

+

+ + +

Timeouts

+ +The timeout parameter to st_cond_timedwait() and the +I/O functions, and the arguments to st_sleep() and +st_usleep() specify a maximum time to wait since the last +context switch not since the beginning of the function call. + +

The State Threads' time resolution is actually the time interval +between context switches. That time interval may be large in some +situations, for example, when a single thread does a lot of work +continuously. Note that a steady, uninterrupted stream of network I/O +qualifies for this description; a context switch occurs only when a +thread blocks. + +

If a specified I/O timeout is less than the time interval between +context switches the function may return with a timeout error before +that amount of time has elapsed since the beginning of the function +call. For example, if eight milliseconds have passed since the last +context switch and an I/O function with a timeout of 10 milliseconds +blocks, causing a switch, the call may return with a timeout error as +little as two milliseconds after it was called. (On Linux, +select()'s timeout is an upper bound on the amount of +time elapsed before select returns.) Similarly, if 12 ms have passed +already, the function may return immediately. + +

In almost all cases I/O timeouts should be used only for detecting a +broken network connection or for preventing a peer from holding an idle +connection for too long. Therefore for most applications realistic I/O +timeouts should be on the order of seconds. Furthermore, there's +probably no point in retrying operations that time out. Rather than +retrying simply use a larger timeout in the first place. + +

The largest valid timeout value is platform-dependent and may be +significantly less than INT_MAX seconds for select() +or INT_MAX milliseconds for poll(). Generally, you +should not use timeouts exceeding several hours. Use +ST_UTIME_NO_TIMEOUT (-1) as a special value to +indicate infinite timeout or indefinite sleep. Use +ST_UTIME_NO_WAIT (0) to indicate no waiting at all. + +

+


+

+ + + diff --git a/trunk/research/st-1.9/docs/reference.html b/trunk/research/st-1.9/docs/reference.html new file mode 100644 index 0000000000..3c9c7bd783 --- /dev/null +++ b/trunk/research/st-1.9/docs/reference.html @@ -0,0 +1,3120 @@ + + +State Threads Library Reference + + + +

State Threads Library Reference

+ +
+
Types
+
st_thread_t
+
st_cond_t
+
st_mutex_t
+
st_utime_t
+
st_netfd_t
+
st_switch_cb_t
+

+

Error Handling
+

+

Library Initialization
+

+

st_init()
+
st_getfdlimit()
+
st_set_eventsys()
+
st_get_eventsys()
+
st_get_eventsys_name()
+
st_set_utime_function()
+
st_timecache_set()
+
st_randomize_stacks()
+

+

st_switch_cb_t type
+
st_set_switch_in_cb()
+
st_set_switch_out_cb()
+

+

Thread Control and Identification
+

+

st_thread_t type
+
st_thread_create()
+
st_thread_exit()
+
st_thread_join()
+
st_thread_self()
+
st_thread_interrupt()
+
st_sleep()
+
st_usleep()
+
st_randomize_stacks()
+

+

Per-Thread Private Data
+

+

st_key_create()
+
st_key_getlimit()
+
st_thread_setspecific()
+
st_thread_getspecific()
+

+

Synchronization
+

+

st_cond_t type
+
st_cond_new()
+
st_cond_destroy()
+
st_cond_wait()
+
st_cond_timedwait()
+
st_cond_signal()
+
st_cond_broadcast()
+

+

st_mutex_t type
+
st_mutex_new()
+
st_mutex_destroy()
+
st_mutex_lock()
+
st_mutex_trylock()
+
st_mutex_unlock()
+

+

Timing
+

+

st_utime_t type
+
st_utime()
+
st_set_utime_function()
+
st_timecache_set()
+
st_time()
+

+

I/O Functions
+

+

st_netfd_t type
+
st_netfd_open()
+
st_netfd_open_socket()
+
st_netfd_free()
+
st_netfd_close()
+
st_netfd_fileno()
+
st_netfd_setspecific()
+
st_netfd_getspecific()
+
st_netfd_serialize_accept()
+
+
st_netfd_poll()
+

+

st_accept()
+
st_connect()
+
st_read()
+
st_read_fully()
+
st_read_resid()
+
st_readv()
+
st_readv_resid()
+
st_write()
+
st_write_resid()
+
st_writev()
+
st_writev_resid()
+
st_recvfrom()
+
st_sendto()
+
st_recvmsg()
+
st_sendmsg()
+

+

st_open()
+
st_poll()
+

+

Program Structure
+

+

List of Blocking Functions
+

+

+

+


+

+ + + +

Types

+ +The State Thread library defines the following types in the st.h +header file: +

+

+
st_thread_t
+
st_cond_t
+
st_mutex_t
+
st_utime_t
+
st_netfd_t
+
+

+


+

+ + +

st_thread_t

+ +Thread type. +

+

Syntax
+ +
+#include <st.h>
+
+typedef void *  st_thread_t;
+
+

+

Description
+ +A thread is represented and identified by a pointer to an opaque data +structure. This pointer is a required parameter for most of the functions +that operate on threads. +

+The thread identifier remains valid until the thread returns from its root +function and, if the thread was created joinable, is joined. +

+


+

+ + +

st_cond_t

+ +Condition variable type. +

+

Syntax
+ +
+#include <st.h>
+
+typedef void *  st_cond_t;
+
+

+

Description
+ +A condition variable is an opaque object identified by a pointer. +Condition variables provide synchronization primitives to wait for or wake +up threads waiting for certain conditions to be satisfied. +

+In the State Threads library there is no need to lock a mutex before +waiting on a condition variable. +

+


+

+ + +

st_mutex_t

+ +Mutex type. +

+

Syntax
+ +
+#include <st.h>
+
+typedef void *  st_mutex_t;
+
+

+

Description
+ +A mutex is an opaque object identified by a pointer. +Mutual exclusion locks (mutexes) are used to serialize the execution of +threads through critical sections of code. +

+If application using the State Threads library is written with no +I/O or control yielding in critical sections (that is no +blocking functions in critical sections), then there is +no need for mutexes.

+These mutexes can only be used for intra-process thread synchronization. +They cannot be used for inter-process synchronization. +

+


+

+ + +

st_utime_t

+ +High resolution time type ("u" stands for "micro"). +

+

Syntax
+ +
+#include <st.h>
+
+typedef unsigned long long  st_utime_t;
+
+

+

Description
+ +This datatype (unsigned 64-bit integer) represents high-resolution real time +expressed in microseconds since some arbitrary time in the past. It is not +correlated in any way to the time of day. +

+


+

+ + +

st_netfd_t

+ +File descriptor type. +

+

Syntax
+ +
+#include <st.h>
+
+typedef void *  st_netfd_t;
+
+

+

Description
+ +This datatype typically represents any open end point of network +communication (socket, end point of a pipe, FIFO, etc.) but can +encapsulate any open file descriptor. Objects of this type are +identified by a pointer to an opaque data structure. + +

+


+

+ + +

st_switch_cb_t

+ +Context switch callback function type. +

+

Syntax
+ +
+#include <st.h>
+
+typedef void (*st_switch_cb_t)(void);
+
+

+

Description
+ +This datatype is a convenience type for describing a pointer +to a function that will be called when a thread is set to stop +or set to run. +This feature is available only when ST_SWITCH_CB is defined +in <st.h>. + +

+


+

+ + +

Error Handling

+ + +All State Threads library non-void functions return on success either a +non-negative integer or a pointer to a newly created object (constructor-type +functions). On failure they return either -1 or a NULL +pointer respectively and set global errno to indicate the error. +It is safe to use errno because it is set right before the function +return and only one thread at a time can modify its value.

+The perror(3) function can be used to produce an error message on the +standard error output. +

+


+

+ + +

Library Initialization

+ +

+

+
st_init()
+
st_getfdlimit()
+
st_set_eventsys()
+
st_get_eventsys()
+
st_get_eventsys_name()
+

+These functions operate on a callback function of type +st_switch_cb_t: +

st_set_switch_in_cb()
+
st_set_switch_out_cb()
+
+

+


+

+ + +

st_init()

+ +Initializes the runtime. +

+

Syntax
+ +
+#include <st.h>
+
+int st_init(void);
+
+

+

Parameters
+None. +

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error. +

+

Description
+This function initializes the library runtime. It should be called near +the beginning of the application's main() function before any other +State Threads library function is called.

+Among other things, this function limits the number of open file descriptors +to the OS imposed per-process maximum number or, if select(2) is +used, to FD_SETSIZE, whichever is less (getrlimit(2)). +This limit can be +retrieved by st_getfdlimit(). It also sets the +disposition of the SIGPIPE signal to SIG_IGN (to be ignored) +(signal(5)). +

+Unlike POSIX threads, a new process created by the fork(2) system +call is an exact copy of the calling process and all state threads +which are running in the parent do exist in the child. That means that +st_init() may be called either before or after multiple processes +are created by fork(2). +

+If the library runtime is not properly initialized (e.g., st_init() +is accidentally omitted), then the process will receive either an arithmetic +exception (SIGFPE or SIGTRAP) or segmentation fault (SIGSEGV) signal upon +new thread creation or the first context switch, respectively. +

+


+

+ +

st_getfdlimit()

+ +Returns the maximum number of file descriptors that the calling process +can open. +

+

Syntax
+ +
+#include <st.h>
+
+int st_getfdlimit(void);
+
+

+

Parameters
+None. +

+

Returns
+The maximum number of file descriptors that the calling process can open. +If this function is called before the library is successfully initialized by +st_init(), a value of -1 is returned. +

+

Description
+This function returns the limit on the number of open file descriptors which +is set by the st_init() function. +

+


+

+ + +

st_set_eventsys()

+ +Sets event notification mechanism. +

+

Syntax
+ +
+#include <st.h>
+
+int st_set_eventsys(int eventsys);
+
+

+

Parameters
+st_set_eventsys() has the following parameter:

+eventsys

+An integer value identifying selected event notification mechanism. The +following values are defined in the st.h header file: +

+ + + + + + + + + + + + + + + +
ST_EVENTSYS_DEFAULTUse default event notification mechanism. Usually it's select(2) +but if the library was compiled with the USE_POLL macro defined +then the default is poll(2).
ST_EVENTSYS_SELECTUse select(2) as an event notification mechanism.
ST_EVENTSYS_POLLUse poll(2) as an event notification mechanism.
ST_EVENTSYS_ALTUse an alternative event notification mechanism. The actual +mechanism selected depends on OS support. For example, epoll(4) +will be used on Linux if supported and kqueue(2) will be used +on FreeBSD/OpenBSD. If the OS supports no alternative event +notification mechanism, setting ST_EVENTSYS_ALT has no effect +and the ST_EVENTSYS_DEFAULT mechanism will be used.
+

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error:

+ + + + + +
EINVAL +The supplied eventsys parameter has an invalid value. +
EBUSY +The event notification mechanism has already been set. +
+

+

Description
+This function sets the event notification mechanism that will be used by +the State Threads library. To have any effect, it must be called +before the st_init() function which performs +the actual initialization. If st_set_eventsys() is not called, +st_init() will set the ST_EVENTSYS_DEFAULT +mechanism. The mechanism cannot be changed once set. +

+There are no strict rules for selecting an event notification +mechanism. The "best" one depends on how your application behaves. +Try a few to see which one works best for you. As a rule of +thumb, you should use the ST_EVENTSYS_ALT mechanism if your +application deals with a very large number of network connections of +which only a few are active at once. +

+


+

+ +

st_get_eventsys()

+ +Returns the integer value identifying the event notification mechanism +being used by the State Threads library. +

+

Syntax
+ +
+#include <st.h>
+
+int st_get_eventsys(void);
+
+

+

Parameters
+None. +

+

Returns
+The integer value identifying the current event notification mechanism. +This value can be one of the following (see st_set_eventsys()): +ST_EVENTSYS_SELECT, ST_EVENTSYS_POLL, or +ST_EVENTSYS_ALT. Future versions of the library may return other +values. If a mechanism hasn't been set yet, a value of -1 is returned. +

+

Description
+This function returns the integer value identifying the event notification +mechanism which is actually being used by the State Threads library. +

+


+

+ +

st_get_eventsys_name()

+ +Returns the name of the event notification mechanism being used by the +State Threads library. +

+

Syntax
+ +
+#include <st.h>
+
+const char *st_get_eventsys_name(void);
+
+

+

Parameters
+None. +

+

Returns
+The string identifying the current event notification mechanism. If a +mechanism hasn't been set yet (see st_set_eventsys()), an empty string is +returned. Possible return values are "select", +"poll", "kqueue", or "epoll". Future versions +of the library may return other values. +

+

Description
+This function returns the string identifying the event notification +mechanism which is actually being used by the State Threads library. +

+


+

+ + +

st_set_switch_in_cb()

+ + +

st_set_switch_out_cb()

+
+Set the optional callback function for thread switches. +

+

Syntax
+ +
+#include <st.h>
+
+st_switch_cb_t st_set_switch_in_cb(st_switch_cb_t cb);
+st_switch_cb_t st_set_switch_out_cb(st_switch_cb_t cb);
+
+

+

Parameters
+st_set_switch_in_cb() and st_set_switch_out_cb() have the +following parameter:

+cb

+A function to be called when a thread is resumed and stopped respectively.

+

Returns
+The previous callback function pointer. +

+

Description
+These functions set the callback for when a thread is resumed and stopped +respectively. After being called any thread switch will call the callback. +Use a NULL pointer to disable the callback (this is the default). +Use st_thread_self() or thread +specific data to differentiate between threads.

+These functions can be called at any time.

+This feature is available only when ST_SWITCH_CB is defined +in <st.h>. +

+


+

+ + +

Thread Control and Identification

+ +

+These functions operate on a thread object of type +st_thread_t. +

+

+
st_thread_create()
+
st_thread_exit()
+
st_thread_join()
+
st_thread_self()
+
st_thread_interrupt()
+
st_sleep()
+
st_usleep()
+
st_randomize_stacks()
+
+

+


+

+ +

st_thread_create()

+ +Creates a new thread. +

+

Syntax
+ +
+#include <st.h>
+
+st_thread_t st_thread_create(void *(*start)(void *arg), void *arg,
+                             int joinable, int stack_size);
+
+
+

+

Parameters
+st_thread_create() has the following parameters:

+start

+A pointer to the thread's start function, which is called as the root of the +new thread. Return from this function terminates a thread.

+arg

+A pointer to the root function's only parameter.

+joinable

+Specifies whether the thread is joinable or unjoinable. If this parameter +is zero, the thread is unjoinable. Otherwise, it is joinable. +See also st_thread_join().

+stack_size

+Specifies your preference for the size of the stack, in bytes, associated +with the newly created thread. If you pass zero in this parameter, the +default stack size will be used. The default stack size is 128 KB on IA-64 +and 64 KB on all other platforms. On IA-64 only a half of stack_size +bytes is used for the memory stack. The other half is used for the register +stack backing store. +

+

Returns
+Upon successful completion, a new thread identifier is returned (this +identifier remains valid until the thread returns from its start function). +Otherwise, NULL is returned and errno is set +to indicate the error. +

+

Description
+This function creates a new thread. Note that the total number of threads +created by the application is limited by the amount of swap space available. +Upon thread creation, stack_size bytes are reserved on the swap +space. The stack pages are not actually used (valid) until touched by the +application. +

+


+

+ +

st_thread_exit()

+ +Terminates the calling thread. +

+

Syntax
+ +
+#include <st.h>
+
+void st_thread_exit(void *retval);
+
+

+

Parameters
+st_thread_exit() has the following parameters:

+retval

+If the thread is joinable, then the value retval may be retrieved +by st_thread_join(). If a thread returns from its +start function, it acts as if it had called st_thread_exit() with +retval as the value returned. +

+

Returns
+Nothing. +

+

Description
+This function terminates the calling thread. When a thread exits, per-thread +private data is destroyed by invoking the destructor function for any +non-NULL thread specific values associated with active keys (see +st_key_create()). This function is implicitly called +when a thread returns from its start function.

+When the last thread terminates the process exits with a zero status value. +

+


+

+ +

st_thread_join()

+ +Blocks the calling thread until a specified thread terminates. +

+

Syntax
+ +
+#include <st.h>
+
+int st_thread_join(st_thread_t thread, void **retvalp);
+
+

+

Parameters
+st_thread_join() has the following parameters:

+thread

+A valid identifier for the thread that is to be joined.

+retvalp

+If this parameter is not NULL, then the exit value of the +thread will be placed in the location referenced by this parameter +(see st_thread_exit()). +

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error:

+ + + + + +
EINVALTarget thread is unjoinable.
EINVALOther thread already waits on the same +joinable thread.
EDEADLKTarget thread is the same as the +calling thread.
EINTRCurrent thread was interrupted by +st_thread_interrupt().
+

+

Description
+This function is used to synchronize the termination of a thread and possibly +retrieve its exit value. Several threads cannot wait for the same thread +to complete - one of the calling threads operates successfully, and the others +terminate with the error. The calling thread is not blocked if the target +thread has already terminated. +

+


+

+ +

st_thread_self()

+ +Identifies the calling thread. +

+

Syntax
+ +
+#include <st.h>
+
+st_thread_t st_thread_self(void);
+
+

+

Parameters
+None. +

+

Returns
+Always returns a valid reference to the calling thread - a self-identity. +

+

Description
+This function identifies the calling thread. This is the same identifier +that the creating thread obtains from +st_thread_create(). +

+


+

+ +

st_thread_interrupt()

+ +Interrupts a target thread. +

+

Syntax
+ +
+#include <st.h>
+
+void st_thread_interrupt(st_thread_t thread);
+
+

+

Parameters
+st_thread_interrupt() has the following parameters:

+thread

+A valid identifier for the thread being interrupted. +

+

Returns
+Nothing. +

+

Description
+This function interrupts (unblocks) a target thread that is blocked in one +of the blocking functions. A function that was interrupted +returns an error and sets errno to EINTR. It is up to +the target thread to act upon an interrupt (e.g., it may exit or just +abort the current transaction).

+Note: State Threads library functions are never interrupted by a +caught signal. A blocking library function returns an error and sets +errno to EINTR only if the current thread was +interrupted via st_thread_interrupt(). +

+If a target thread is already runnable or running (e.g., it is a newly +created thread or calling thread itself), this function will prevent it +from subsequent blocking. In other words, the interrupt will be "delivered" +only when a target thread is about to block. +

+


+

+ +

st_sleep(), st_usleep()

+ +Suspends current thread for a specified amount of time. +

+

Syntax
+ +
+#include <st.h>
+
+int st_sleep(int secs);
+
+int st_usleep(st_utime_t usecs);
+
+

+

Parameters
+st_sleep() has the following parameters:

+secs

+The number of seconds you want the thread to sleep for. +

+st_usleep() has the following parameters:

+usecs

+The number of microseconds you want the thread to sleep for. This parameter +is a variable of type st_utime_t. +

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error:

+ + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
+

+

Description
+These functions suspend the calling thread from execution for a specified +number of seconds (st_sleep()) or microseconds (st_usleep()). +

+ +If zero is passed as a parameter to st_sleep(), or +ST_UTIME_NO_WAIT (0) is passed to +st_usleep(), the calling thread yields, thus potentially +allowing another thread to run. + +

+ +If -1 is passed as a parameter to st_sleep(), or +ST_UTIME_NO_TIMEOUT (-1) is passed to +st_usleep(), the calling thread will be suspended permanently. +It can be resumed again by interrupting it via st_thread_interrupt(). + +

+


+

+ +

st_randomize_stacks()

+ +Turns stack base address randomization on or off. +

+

Syntax
+ +
+#include <st.h>
+
+int st_randomize_stacks(int on);
+
+

+

Parameters
+st_randomize_stacks() has the following parameters:

+on

+If this parameter has a non-zero value, the State Threads library +randomizes the base addresses of stacks allocated for threads created +after this call. Otherwise new threads' stacks are typically page +aligned. +

+

Returns
+The previous state of stack randomization (a value of 0 if it +was off and a non-zero value otherwise). +

+

Description
+Randomizing state threads' stack bases may improve cache performance on +some systems when large numbers of state threads all perform roughly the +same work, as when they all start from the same root function. On many +modern systems the performance increase is negligible. You should +compare your application's performance with this feature on and off to +see if you really need it. +

+When randomization is enabled, new stacks are allocated one page larger +to accomodate the randomization. +

+This call affects only threads created afterward. It has no effect on +existing threads. +

+


+

+ + +

Per-Thread Private Data

+ +These functions allow to associate private data with each of the threads in +a process. +

+

+
st_key_create()
+
st_key_getlimit()
+
st_thread_setspecific()
+
st_thread_getspecific()
+
+

+


+

+ +

st_key_create()

+ +Creates a key (non-negative integer) that can be used by all +threads in the process to get and set thread-specific data. +

+

Syntax
+ +
+#include <st.h>
+
+int st_key_create(int *keyp, void (*destructor)(void *));
+
+

+

Parameters
+st_key_create() has the following parameters:

+keyp

+The newly created key is returned in the memory pointed to by this parameter. +The new key can be used with +st_thread_setspecific() and +st_thread_getspecific().

+destructor

+Specifies an optional destructor function for the private data associated +with the key. This function can be specified as NULL. +Upon thread exit (see st_thread_exit()), if a key +has a non-NULL destructor and has a non-NULL value +associated with that key, then the destructor function will be +called with the associated value. +

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error:

+ + +
EAGAINThe limit on the total number of keys per +process has been exceeded (see st_key_getlimit()). +
+

+

Description
+If this function is successful, every thread in the same process is capable +of associating private data with the new key. After a new key is created, all +active threads have the value NULL associated with that key. +After a new thread is created, the value NULL is associated with +all keys for that thread. If a non-NULL destructor function is +registered with a new key, it will be called at one of two times, as long as +the private data is not NULL: + +

+The key maintains independent data values for each binding thread. A thread +can get access only to its own thread-specific data. There is no way to +deallocate a private data key once it is allocated. +

+


+

+ +

st_key_getlimit()

+ +Returns the key limit. +

+

Syntax
+ +
+#include <st.h>
+
+int st_key_getlimit(void);
+
+

+

Parameters
+None. +

+

Returns
+The limit on the total number of keys per process. +

+

Description
+This function can be used to obtain the limit on the total number of keys +per process (see st_key_create()). +

+


+

+ +

st_thread_setspecific()

+ +Sets per-thread private data. +

+

Syntax
+ +
+#include <st.h>
+
+int st_thread_setspecific(int key, void *value);
+
+

+

Parameters
+st_thread_setspecific() has the following parameters:

+key

+This parameter represents a key with which thread-specific data is associated. +

+value

+The per-thread private data, or more likely, a pointer to the data which is +associated with key. +

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error:

+ + + +
EINVALThe specified key is invalid.
+

+

Description
+This function associates a thread-specific value with key. +Different threads may bind different values to the same key.

+If the thread already has non-NULL private data associated with +key, and if the destructor function for that key is not +NULL, this destructor function will be called before setting the +new data value. +

+


+

+ +

st_thread_getspecific()

+ +Retrieves the per-thread private data for the current thread. +

+

Syntax
+ +
+#include <st.h>
+
+void *st_thread_getspecific(int key);
+
+

+

Parameters
+st_thread_getspecific() has the following parameters:

+key

+This parameter represents a key with which thread-specific data is associated. +

+

Returns
+The thread-specific data associated with key. If no data is +associated with key, then NULL is returned. +

+

Description
+This function returns the calling thread's value that is bound to the +specified key (see +st_thread_setspecific()). +

+


+

+ + +

Synchronization

+ +

+These functions operate on condition variables +and mutual exclusion locks (mutexes).

+Functions are provided to wait on a condition variable and to wake up +(signal) threads that are waiting on the condition variable. +

+

+
st_cond_new()
+
st_cond_destroy()
+
st_cond_wait()
+
st_cond_timedwait()
+
st_cond_signal()
+
st_cond_broadcast()
+

+

st_mutex_new()
+
st_mutex_destroy()
+
st_mutex_lock()
+
st_mutex_trylock()
+
st_mutex_unlock()
+
+

+


+

+ +

st_cond_new()

+ +Creates a new condition variable. +

+

Syntax
+ +
+#include <st.h>
+
+st_cond_t st_cond_new(void);
+
+

+

Parameters
+None. +

+

Returns
+Upon successful completion, a new condition variable identifier is returned. +Otherwise, NULL is returned and errno is set +to indicate the error. +

+

Description
+This function creates a new condition variable. +

+


+

+ +

st_cond_destroy()

+ +Destroys a condition variable. +

+

Syntax
+ +
+#include <st.h>
+
+int st_cond_destroy(st_cond_t cvar);
+
+

+

Parameters
+st_cond_destroy() has the following parameters:

+cvar

+An identifier of the condition variable object to be destroyed. +

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error:

+ + + +
EBUSYThe condition variable is currently being +used by one or more threads.
+

+

Description
+This function destroys a condition variable. The caller is responsible for +ensuring that the condition variable is no longer in use. +

+


+

+ +

st_cond_wait()

+ +Waits on a condition. +

+

Syntax
+ +
+#include <st.h>
+
+int st_cond_wait(st_cond_t cvar);
+
+

+

Parameters
+st_cond_wait() has the following parameters:

+cvar

+The condition variable on which to wait. +

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error:

+ + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
+

+

Description
+This function is used to block on a condition variable. A return from this +function does not guarantee that the condition or event for which the caller +was waiting actually occurred. It is the responsibility of the caller +to recheck the condition wait predicate before proceeding.

+Note: The State Threads library scheduling guarantees that the +condition cannot change between the checking and blocking, therefore there +is no need for mutex protection. You must not call any +blocking functions between the condition checking and +the st_cond_wait() call. +

+


+

+ +

st_cond_timedwait()

+ +Waits on a condition. +

+

Syntax
+ +
+#include <st.h>
+
+int st_cond_timedwait(st_cond_t cvar, st_utime_t timeout);
+
+

+

Parameters
+st_cond_timedwait() has the following parameters:

+cvar

+The condition variable on which to wait.

+timeout

+If the number of microseconds specified by this parameter passes before the +waiting thread is signalled, an error is returned. This parameter is a +variable of type st_utime_t. Note that this +time value is a time delta; it is not an absolute time. +Also note that timeouts are measured since +the last context switch. +

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error:

+ + + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred before the thread was +awakened by st_cond_signal() or +st_cond_broadcast().
+

+

Description
+This function works the same way as st_cond_wait(), +except that an error is returned if the number of microseconds specified by +timeout passes before the waiting thread is signalled. +

+


+

+ +

st_cond_signal()

+ +Unblocks a thread waiting on a condition variable. +

+

Syntax
+ +
+#include <st.h>
+
+int st_cond_signal(st_cond_t cvar);
+
+

+

Parameters
+st_cond_signal() has the following parameters:

+cvar

+The condition variable to signal. +

+

Returns
+Always zero. +

+

Description
+This function unblocks (signals) one of the threads that are blocked on +cvar at the time of the call. If no thread is waiting on the +condition variable, the signal operation is a no-op. +

+


+

+ +

st_cond_broadcast()

+ +Unblocks all threads waiting on a condition variable. +

+

Syntax
+ +
+#include <st.h>
+
+int st_cond_broadcast(st_cond_t cvar);
+
+

+

Parameters
+st_cond_broadcast() has the following parameters:

+cvar

+The condition variable to broadcast. +

+

Returns
+Always zero. +

+

Description
+This function unblocks all threads blocked on the specified condition +variable at the time of the call. If no threads are waiting, this operation +is a no-op. +

+


+

+ + +

st_mutex_new()

+ +Creates a new mutual exclusion lock (mutex). +

+

Syntax
+ +
+#include <st.h>
+
+st_mutex_t st_mutex_new(void);
+
+

+

Parameters
+None. +

+

Returns
+Upon successful completion, a new mutex identifier is returned. +Otherwise, NULL is returned and errno is set to +indicate the error. +

+

Description
+This function creates a new opaque mutual exclusion lock (see +st_mutex_t). +

+


+

+ +

st_mutex_destroy()

+ +Destroys a specified mutex object. +

+

Syntax
+ +
+#include <st.h>
+
+int st_mutex_destroy(st_mutex_t lock);
+
+

+

Parameters
+st_mutex_destroy() has the following parameters:

+lock

+An identifier of the mutex object to be destroyed. +

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error:

+ + + +
EBUSYThe mutex is currently being used by +other threads.
+

+

Description
+This function destroys a mutex. The caller is responsible for ensuring +that the mutex is no longer in use. +

+


+

+ +

st_mutex_lock()

+ +Locks a specified mutex object. +

+

Syntax
+ +
+#include <st.h>
+
+int st_mutex_lock(st_mutex_t lock);
+
+

+

Parameters
+st_mutex_lock() has the following parameters:

+lock

+An identifier of the mutex object to be locked. +

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error:

+ + + +
EDEADLKThe current thread already owns the mutex. +
EINTRThe current thread was interrupted by +st_thread_interrupt().
+

+

Description
+A thread that calls this function will block until it can gain exclusive +ownership of a mutex, and retains ownership until it calls +st_mutex_unlock(). +

+


+

+ +

st_mutex_trylock()

+ +Attempts to acquire a mutex. +

+

Syntax
+ +
+#include <st.h>
+
+int st_mutex_trylock(st_mutex_t lock);
+
+

+

Parameters
+st_mutex_trylock() has the following parameters:

+lock

+An identifier of the mutex object to be locked. +

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error:

+ + +
EBUSYThe mutex is currently held by another +thread.
+

+

Description
+This function attempts to acquire a mutex. If the mutex object is locked +(by any thread, including the current thread), the call returns immediately +with an error. +

+


+

+ +

st_mutex_unlock()

+ +Releases a specified mutex object. +

+

Syntax
+ +
+#include <st.h>
+
+int st_mutex_unlock(st_mutex_t lock);
+
+

+

Parameters
+st_mutex_unlock() has the following parameters:

+lock

+An identifier of the mutex object to be unlocked. +

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error:

+ + +
EPERMThe current thread does not own the mutex. +
+

+

Description
+This function releases a specified mutex object previously acquired by +st_mutex_lock() or +st_mutex_trylock(). Only the thread that locked +a mutex should unlock it. +

+


+

+ + +

Timing

+ +

+

+
st_utime()
+
st_set_utime_function()
+
st_timecache_set()
+
st_time()
+
+

+


+

+ +

st_utime()

+ +Returns current high-resolution time. +

+

Syntax
+ +
+#include <st.h>
+
+st_utime_t st_utime(void);
+
+

+

Parameters
+None. +

+

Returns
+Current high-resolution time value of type +st_utime_t. +

+

Description
+This function returns the current high-resolution time. Time is +expressed as microseconds since some arbitrary time in the past. It is +not correlated in any way to the time of day. See also st_utime_t and st_time(). +

+


+

+ +

st_set_utime_function()

+ +Set high-resolution time function. +

+

Syntax
+ +
+#include <st.h>
+
+int st_set_utime_function(st_utime_t (*func)(void));
+
+

+

Parameters
+st_set_utime_function() has the following parameters:

+func

+This function will be called to get high-resolution time instead of the +default st_utime() function. It must return +number of microseconds since some arbitrary time in the past. +

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to EINVAL to indicate the error. +

+

Description
+This function may be called to replace the default implementation of the +st_utime() function. It must be called before the ST +library has been initialized (see st_init()). +The user-provided function func will be invoked whenever +st_utime() is called to obtain current high-resolution time. +Replacing default implementation may be useful, for example, for taking +advantage of high performance CPU cycle counters. +

+


+

+ +

st_timecache_set()

+ +Turns the time caching on or off. +

+

Syntax
+ +
+#include <st.h>
+
+int st_timecache_set(int on);
+
+

+

Parameters
+st_timecache_set() has the following parameters:

+on

+If this parameter has a non-zero value, the time caching is turned on +(enabled). Otherwise, the time caching is turned off (disabled). +By default time caching is disabled. +

+

Returns
+The previous state of time caching (a value of 0 if it was off and +a value of 1 otherwise). +

+

Description
+The State Threads library has the ability to "cache" the time value that is +reported by the time(2) system call. If the time caching is enabled +by calling this function with a non-zero argument, then the result value +of time(2) will be stored and updated at most once per second. The +cached time can be retrieved by st_time(). +By default time caching is disabled. +You may enable or disable time caching at any time but generally +you enable it once (if desired) during program initialization.

+Note: There are some pathological cases (e.g., very heavy loads during +application benchmarking) when a single thread runs for a long time without +giving up control and the cached time value is not updated properly. If you +always need "real-time" time values, don't enable the time caching. +

+


+

+ +

st_time()

+ +Returns the value of time in seconds since 00:00:00 UTC, January 1, 1970. +

+

Syntax
+ +
+#include <st.h>
+
+time_t st_time(void);
+
+

+

Parameters
+None. +

+

Returns
+The value of time in seconds since 00:00:00 UTC, January 1, 1970 as reported +by the time(2) system call. +

+

Description
+If the time caching was enabled by +st_timecache_set(), then this function returns +the cached result. Otherwise, it just calls time(2). +

+


+

+ + +

I/O Functions

+ +

+Most State Threads library I/O functions look like corresponding C library +functions with two exceptions: +

    +
  • They operate on file descriptor objects of type +st_netfd_t.
  • +
  • They take an additional argument of type +st_utime_t which represents an inactivity +timeout: if no I/O is possible during this amount of time, I/O functions +return an error code and set errno to ETIME. + +The boundary values ST_UTIME_NO_WAIT (0) and +ST_UTIME_NO_TIMEOUT (-1) for this argument indicate +that the thread should wait no time (function returns immediately) or +wait forever (never time out), respectively. + +Note that timeouts are measured since the +last context switch. +
  • +
+

+

+
st_netfd_open()
+
st_netfd_open_socket()
+
st_netfd_free()
+
st_netfd_close()
+
st_netfd_fileno()
+
st_netfd_setspecific()
+
st_netfd_getspecific()
+
st_netfd_serialize_accept()
+
st_netfd_poll()
+

+

st_accept()
+
st_connect()
+
st_read()
+
st_read_fully()
+
st_read_resid()
+
st_readv()
+
st_read_resid()
+
st_write()
+
st_write_resid()
+
st_writev()
+
st_writev_resid()
+
st_recvfrom()
+
st_sendto()
+
st_recvmsg()
+
st_sendmsg()
+
st_open()
+
st_poll()
+
+

+


+

+ +

st_netfd_open()

+ +Creates a new file descriptor object. +

+

Syntax
+ +
+#include <st.h>
+
+st_netfd_t st_netfd_open(int osfd);
+
+

+

Parameters
+st_netfd_open() has the following parameters:

+osfd

+ +Any open OS file descriptor; can be obtained from calls to +functions including, but not restricted to, pipe(2), socket(3), +socketpair(3), fcntl(2), dup(2), etc. + + +

+

Returns
+Upon successful completion, a new file descriptor object identifier is +returned. Otherwise, NULL is returned and errno is set +to indicate the error. +

+

Description
+This function creates a new file descriptor object of type +st_netfd_t.

+ +Note: Among other things, this function sets a non-blocking +flag on the underlying OS file descriptor. You should not modify this +flag directly. Also, once an st_netfd_t +has been created with a given file descriptor, you should avoid +passing that descriptor to normal I/O or stdio functions. Since the +O_NONBLOCK flag is shared across dup(2), this applies to +dup()'ed file descriptors as well - for instance, if you pass +standard output or standard input to st_netfd_open(), then +you should use st_write() instead of write +or fprintf when writing to standard error as well - since all +three descriptors could point to the same terminal. If necessary, you +can still use write directly if you remember to check +errno for EAGAIN, but fprintf and other +stdio functions should be avoided completely because, at least on +Linux, the stdio library cannot be made to work reliably with +non-blocking files. (This only applies to file descriptors which are +passed to st_netfd_open() or st_netfd_open_socket(), or which are +related to such descriptors through dup(); other file +descriptors are untouched by State Threads.) +

+


+

+ +

st_netfd_open_socket()

+ +Creates a new file descriptor object from a socket. +

+

Syntax
+ +
+#include <st.h>
+
+st_netfd_t st_netfd_open_socket(int osfd);
+
+

+

Parameters
+st_netfd_open_socket() has the following parameters:

+osfd

+An open OS file descriptor which is a socket initially obtained from a +socket(3) or socketpair(3) call. +

+

Returns
+Upon successful completion, a new file descriptor object identifier is +returned. Otherwise, NULL is returned and errno is set +to indicate the error. +

+

Description
+This function creates a new file descriptor object of type +st_netfd_t which represents an open end +point of network communication.

+Unlike the st_netfd_open() function which may be used +on OS file descriptors of any origin, st_netfd_open_socket() must +be used only on sockets. It is slightly more efficient than +st_netfd_open().

+Note: Among other things, this function sets a non-blocking flag +on the underlying OS socket. You should not modify this flag directly. +See st_netfd_open(). +

+


+

+ +

st_netfd_free()

+ +Frees a file descriptor object without closing the underlying OS file +descriptor. +

+

Syntax
+ +
+#include <st.h>
+
+void st_netfd_free(st_netfd_t fd);
+
+

+

Parameters
+st_netfd_free() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t). +

+

Returns
+Nothing. +

+

Description
+This function frees the memory and other resources identified by the +fd parameter without closing the underlying OS file descriptor. +Any non-NULL descriptor-specific data is destroyed by invoking +the specified destructor function (see st_netfd_setspecific()).

A thread should +not free file descriptor objects that are in use by other threads +because it may lead to unpredictable results (e.g., a freed file +descriptor may be reused without other threads knowing that). +

+


+

+ +

st_netfd_close()

+ +Closes a file descriptor. +

+

Syntax
+ +
+#include <st.h>
+
+int st_netfd_close(st_netfd_t fd);
+
+

+

Parameters
+st_netfd_close() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t). +

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error. +

+

Description
+This function closes the underlying OS file descriptor, frees the memory and +other resources identified by the fd parameter. Any non-NULL +descriptor-specific data is destroyed by invoking the specified destructor +function (see st_netfd_setspecific()).

+A thread should not close file descriptor objects that are in use by other +threads because it may lead to unpredictable results (e.g., a closed +file descriptor may be reused without other threads knowing that). +

+


+

+ +

st_netfd_fileno()

+ +Returns an underlying OS file descriptor. +

+

Syntax
+ +
+#include <st.h>
+
+int st_netfd_fileno(st_netfd_t fd);
+
+

+

Parameters
+st_netfd_fileno() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t). +

+

Returns
+An underlying OS file descriptor. +

+

Description
+This function returns the integer OS file descriptor associated with the named +file descriptor object. +

+


+

+ +

st_netfd_setspecific()

+ +Sets per-descriptor private data. +

+

Syntax
+ +
+#include <st.h>
+
+void st_netfd_setspecific(st_netfd_t fd, void *value,
+                          void (*destructor)(void *));
+
+

+

Parameters
+st_netfd_setspecific() has the following parameters:

+fd

+A valid file descriptor object identifier (see +st_netfd_t). +

+value

+The per-descriptor private data, or more likely, a pointer to the data which +is being associated with the named file descriptor object. +

+destructor

+Specifies an optional destructor function for the private data associated +with fd. This function can be specified as NULL. +If value is not NULL, then this destructor function will +be called with value as an argument upon freeing the file descriptor +object (see st_netfd_free() and +st_netfd_close()). +

+

Returns
+Nothing. +

+

Description
+This function allows to associate any data with the specified file +descriptor object (network connection). If a non-NULL destructor +function is registered, it will be called at one of two times, as long as +the associated data is not NULL: +
    +
  • when private data is replaced by calling +st_netfd_setspecific() again +
  • upon freeing the file descriptor object (see +st_netfd_free() and +st_netfd_close()) +
+

+


+

+ +

st_netfd_getspecific()

+ +Retrieves the per-descriptor private data. +

+

Syntax
+ +
+#include <st.h>
+
+void *st_netfd_getspecific(st_netfd_t fd);
+
+

+

Parameters
+st_netfd_getspecific() has the following parameters:

+fd

+A valid file descriptor object identifier (see +st_netfd_t). +

+

Returns
+The data associated with the named file descriptor object. If no data is +associated with fd, then NULL is returned. +

+

Description
+This function allows to retrieve the data that was associated with the +specified file descriptor object (see +st_netfd_setspecific()). +

+


+

+ +

st_netfd_serialize_accept()

+ +Serializes all subsequent accept(3) calls on a specified file +descriptor object. +

+

Syntax
+ +
+#include <st.h>
+
+int st_netfd_serialize_accept(st_netfd_t fd);
+
+

+

Parameters
+st_netfd_serialize_accept() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t) which has been successfully created +from a valid listening socket by st_netfd_open() or +st_netfd_open_socket(). +

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error. +

+

Description
+On some platforms (e.g., Solaris 2.5 and possibly other SVR4 implementations) +accept(3) calls from different processes on +the same listening socket (see bind(3), listen(3)) must be +serialized. This function causes all subsequent accept(3) calls +made by st_accept() on the specified file descriptor +object to be serialized. +

+st_netfd_serialize_accept() must be called before +creating multiple server processes via fork(2). If the application +does not create multiple processes to accept network connections on +the same listening socket, there is no need to call this function. +

+Deciding whether or not to serialize accepts is tricky. On some +platforms (IRIX, Linux) it's not needed at all and +st_netfd_serialize_accept() is a no-op. On other platforms +it depends on the version of the OS (Solaris 2.6 doesn't need it but +earlier versions do). Serializing accepts does incur a slight +performance penalty so you want to enable it only if necessary. Read +your system's manual pages for accept(2) and select(2) +to see if accept serialization is necessary on your system. +

+st_netfd_serialize_accept() allocates resources that are +freed upon freeing of the specified file descriptor object (see +st_netfd_free() and +st_netfd_close()). +

+


+

+ +

st_netfd_poll()

+ +Waits for I/O on a single file descriptor object. +

+

Syntax
+ +
+#include <st.h>
+
+int st_netfd_poll(st_netfd_t fd, int how, st_utime_t timeout);
+
+

+

Parameters
+st_netfd_poll() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t). +

+how

+Specifies I/O events of interest. This parameter can be constructed by +OR-ing any combination of the following event flags which are defined +in the poll.h header file:

+ + + + + +
POLLINfd is readable.
POLLOUTfd is is writable.
POLLPRIfd has an exception condition.
+

+timeout

+Amount of time in microseconds the call will block waiting for I/O +to become ready. This parameter is a variable of type +st_utime_t. If this time expires without any +I/O becoming ready, st_netfd_poll() returns an error and sets +errno to ETIME. +Note that timeouts are measured since the +last context switch. +

+

Returns
+If the named file descriptor object is ready for I/O within the specified +amount of time, a value of 0 is returned. Otherwise, a value +of -1 is returned and errno is set to indicate the error: +

+ + + + +
EBADFThe underlying OS file descriptor is invalid. +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred without any I/O +becoming ready.
+

+

Description
+This function returns as soon as I/O is ready on the named file +descriptor object or the specified amount of time expires. The +how parameter should be set to the I/O events (readable, +writable, exception, or some combination) that the caller is interested +in. If the value of timeout is ST_UTIME_NO_TIMEOUT +(-1), this function blocks until a requested I/O event occurs +or until the call is interrupted by st_thread_interrupt().

+Despite having an interface like poll(2), this function uses +the same event notification mechanism as the rest of the library. For +instance if an alternative event nofication mechanism was set using st_set_eventsys(), this function uses that +mechanism to check for events.

+Note: if kqueue(2) is used as an alternative event +notification mechanism (see st_set_eventsys()), the POLLPRI +event flag is not supported and st_netfd_poll() will return an error +if it's set (errno will be set to EINVAL). +

+


+

+ +

st_accept()

+ +Accepts a connection on a specified file descriptor object. +

+

Syntax
+ +
+#include <st.h>
+
+st_netfd_t st_accept(st_netfd_t fd, struct sockaddr *addr, int *addrlen,
+                     st_utime_t timeout);
+
+

+

Parameters
+st_accept() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t) representing the rendezvous socket +on which the caller is willing to accept new connections. This object has been +created from a valid listening socket by +st_netfd_open() or +st_netfd_open_socket().

+addr

+If this value is non-zero, it is a result parameter that is filled +in with the address of the connecting entity, as known to the communications +layer (see accept(3)).

+addrlen

+This parameter should initially contain the amount of space pointed to by +addr; on return it will contain the actual length (in bytes) of the +address returned (see accept(3)).

+timeout

+A value of type st_utime_t specifying the time +limit in microseconds for completion of the accept operation. +Note that timeouts are measured since the +last context switch. +

+

Returns
+Upon successful completion, a new file descriptor object identifier +representing the newly accepted connection is returned. Otherwise, +NULL is returned and errno is set to indicate the error. +Possible errno values are the same as set by the accept(3) +call with two exceptions:

+ + + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred and no pending +connection was accepted.
+

+

Description
+This function accepts the first connection from the queue of pending +connections and creates a new file descriptor object for the newly +accepted connection. The rendezvous socket can still be used to accept +more connections.

+st_accept() blocks the calling thread until either a new connection +is successfully accepted or an error occurs. If no pending connection can +be accepted before the time limit, this function returns NULL +and sets errno to ETIME. +

+


+

+ +

st_connect()

+ +Initiates a connection on a specified file descriptor object. +

+

Syntax
+ +
+#include <st.h>
+
+int st_connect(st_netfd_t fd, struct sockaddr *addr, int addrlen,
+               st_utime_t timeout);
+
+

+

Parameters
+st_connect() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t) representing a socket.

+addr

+A pointer to the address of the peer to which the socket is to be connected. +

+addrlen

+This parameter specifies the amount of space pointed to by addr. +

+timeout

+A value of type st_utime_t specifying the time +limit in microseconds for completion of the connect operation. +Note that timeouts are measured since the +last context switch. +

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error. Possible errno values are the same as set +by the connect(3) call with two exceptions:

+ + + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred and connection setup +was not completed.
+

+

Description
+This function is usually invoked on a file descriptor object representing +a TCP socket. Upon completion it establishes a TCP connection to the peer. +If the underlying OS socket is not bound, it will be bound to an arbitrary +local address (see connect(3)).

+st_connect() blocks the calling thread until either the connection +is successfully established or an error occurs. If the connection setup +cannot complete before the specified time limit, this function fails with +errno set to ETIME. +

+


+

+ +

st_read()

+ +Reads data from a specified file descriptor object. +

+

Syntax
+ +
+#include <st.h>
+
+ssize_t st_read(st_netfd_t fd, void *buf, size_t nbyte, st_utime_t timeout);
+
+

+

Parameters
+st_read() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t).

+buf

+A pointer to a buffer to hold the data read in. On output the buffer +contains the data.

+nbyte

+The size of buf in bytes.

+timeout

+A value of type st_utime_t specifying the time +limit in microseconds for completion of the read operation. +Note that timeouts are measured since the +last context switch. +

+

Returns
+On success a non-negative integer indicating the number of bytes actually +read is returned (a value of 0 means the network connection is +closed or end of file is reached). Otherwise, a value of -1 is +returned and errno is set to indicate the error. +Possible errno values are the same as set by the read(2) +call with two exceptions:

+ + + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred and no data was read. +
+

+

Description
+This function blocks the calling thread until it encounters an end-of-stream +indication, some positive number of bytes (but no more than nbyte +bytes) are read in, a timeout occurs, or an error occurs. +

+


+

+ +

st_read_fully()

+ +Reads the specified amount of data in full from a file descriptor object. +

+

Syntax
+ +
+#include <st.h>
+
+ssize_t st_read_fully(st_netfd_t fd, void *buf, size_t nbyte,
+                      st_utime_t timeout);
+
+

+

Parameters
+st_read_fully() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t).

+buf

+A pointer to a buffer to hold the data read in. On output the buffer +contains the data.

+nbyte

+The amount of data to be read in full (in bytes). It must not exceed the +size of buf.

+timeout

+A value of type st_utime_t specifying the +inactivity timeout (in microseconds). +Note that timeouts are measured since the +last context switch. +

+

Returns
+On success a non-negative integer indicating the number of bytes actually +read is returned (a value less than nbyte means the network +connection is closed or end of file is reached). Otherwise, a value of +-1 is returned and errno is set to indicate the error. +Possible errno values are the same as set by the read(2) +call with two exceptions:

+ + + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred. +
+

+

Description
+This function blocks the calling thread until the specified amount of data +is read in full, it encounters an end-of-stream indication, a timeout occurs, +or an error occurs. +

+


+

+ +

st_read_resid()

+ +Reads the specified amount of data in full from a file descriptor object. +

+

Syntax
+ +
+#include <st.h>
+
+int st_read_resid(st_netfd_t fd, void *buf, size_t *resid,
+		  st_utime_t timeout);
+
+

+

Parameters
+st_read_resid() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t).

+buf

+A pointer to a buffer to hold the data read in. On output the buffer +contains the data.

+resid

+A pointer to a number of bytes. +On entry, the amount of data to be read in full. +It must not exceed the size of buf. +On return, the amount of data remaining to be read. +(A non-zero returned value means some but not all of the data was read.)

+timeout

+A value of type st_utime_t specifying the +inactivity timeout (in microseconds). +Note that timeouts are measured since the +last context switch. +

+

Returns
+On success, zero is returned. *resid may be zero, indicating +a complete read, or non-zero, indicating the network +connection is closed or end of file is reached. +

+Otherwise, a value of -1 is returned, *resid is non-zero, +and errno is set to indicate the error. +Possible errno values are the same as set by the read(2) +call with two exceptions:

+ + + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred. +
+

+

Description
+This function blocks the calling thread until the specified amount of data +is read in full, it encounters an end-of-stream indication, a timeout occurs, +or an error occurs. It differs from st_read_fully() only in that +it allows the caller to know how many bytes were transferred before an error +occurred. +

+


+

+ +

st_readv()

+ +Reads data from a specified file descriptor object into multiple buffers. +

+

Syntax
+ +
+#include <st.h>
+
+ssize_t st_readv(st_netfd_t fd, const struct iovec *iov, int iov_size,
+		 st_utime_t timeout);
+
+

+

Parameters
+st_readv() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t).

+iov

+An array of iovec structures that identify the buffers for holding +the data read in. +On return the buffers contain the data.

+iov_size

+The number of iovec structures in the iov array.

+timeout

+A value of type st_utime_t specifying the time +limit in microseconds for completion of the read operation. +Note that timeouts are measured since the +last context switch. +

+

Returns
+On success a non-negative integer indicating the number of bytes actually +read is returned (a value of 0 means the network connection is +closed or end of file is reached). Otherwise, a value of -1 is +returned and errno is set to indicate the error. +Possible errno values are the same as set by the readv(2) +call with two exceptions:

+ + + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred and no data was read. +
+

+

Description
+This function blocks the calling thread until it encounters an end-of-stream +indication, some positive number of bytes (but no more than fit in the buffers) +are read in, a timeout occurs, or an error occurs. +

+


+

+ +

st_readv_resid()

+ +Reads the specified amount of data in full from a file descriptor object +into multiple buffers. +

+

Syntax
+ +
+#include <st.h>
+
+int st_readv_resid(st_netfd_t fd, struct iovec **iov, int *iov_size,
+		   st_utime_t timeout);
+
+

+

Parameters
+st_readv_resid() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t).

+iov

+A pointer to an array of iovec structures. +On entry, the iovecs identify the buffers for holding the data read in. +On return, the incomplete iovecs. +This function modifies both the pointer and the array to which it points.

+iov_size

+A pointer to a number of iovec structures. +On entry, the number of iovec structures pointed to by *iov. +On return, the number of incomplete or unused iovec structures. +(A non-zero returned value means some but not all of the data was read.)

+timeout

+A value of type st_utime_t specifying the +inactivity timeout (in microseconds). +Note that timeouts are measured since the +last context switch. +

+

Returns
+On success, zero is returned. *iov_size may be zero, indicating +a complete read, or non-zero, indicating the network connection is +closed or end of file is reached. *iov points to the first +iovec after the end of the original array on a complete read, or to the +first incomplete iovec on an incomplete read. +

+Otherwise, a value of -1 is returned, *iov_size is non-zero, +and errno is set to indicate the error. *iov points to the +first unused iovec. +Possible errno values are the same as set by the readv(2) +call with two exceptions:

+ + + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred. +
+

All of the iovecs before *iov are modified such that +iov_base points to the end of the original buffer and +iov_len is zero. +

+

Description
+This function blocks the calling thread until the specified amount of data +is read in full, it encounters an end-of-stream indication, a timeout occurs, +or an error occurs. Like st_read_resid() it blocks the thread until +all of the requested data is read or an error occurs. Use +st_readv() to read up to the requested amount of data. +

+


+

+ +

st_write()

+ +Writes a buffer of data to a specified file descriptor object. +

+

Syntax
+ +
+#include <st.h>
+
+ssize_t st_write(st_netfd_t fd, const void *buf, size_t nbyte,
+                 st_utime_t timeout);
+
+

+

Parameters
+st_write() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t).

+buf

+A pointer to the buffer holding the data to be written.

+nbyte

+The amount of data in bytes to be written from the buffer.

+timeout

+A value of type st_utime_t specifying the +inactivity timeout (in microseconds). +Note that timeouts are measured since the +last context switch. +

+

Returns
+On success a non-negative integer equal to nbyte is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error. Possible errno values are the same as set +by the write(2) call with two exceptions:

+ + + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred. +
+

+

Description
+This function blocks the calling thread until all the data is written, +a timeout occurs, or the write operation fails. The return value is equal to +either nbyte (on success) or -1 (on failure). Note that if +st_write() returns -1, some data (less than nbyte +bytes) may have been written before an error occurred. +

+


+

+ +

st_write_resid()

+ +Writes a buffer of data to a specified file descriptor object. +

+

Syntax
+ +
+#include <st.h>
+
+int st_write_resid(st_netfd_t fd, const void *buf, size_t *resid,
+                   st_utime_t timeout);
+
+

+

Parameters
+st_write_resid() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t).

+buf

+A pointer to the buffer holding the data to be written.

+resid

+A pointer to a number of bytes. +On entry, the amount of data to be written from the buffer. +On return, the amount of data remaining to be written. +(A non-zero returned value means some but not all of the data was written.)

+timeout

+A value of type st_utime_t specifying the +inactivity timeout (in microseconds). +Note that timeouts are measured since the +last context switch. +

+

Returns
+On success, zero is returned and *resid is zero. +Otherwise, a value of -1 is returned, *resid is non-zero, +and errno is set +to indicate the error. Possible errno values are the same as set +by the write(2) call with two exceptions:

+ + + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred. +
+

+

Description
+This function blocks the calling thread until all the data is written, +a timeout occurs, or the write operation fails. It differs from +st_write() only in that it allows the caller to know how many bytes +were transferred before an error occurred. +

+


+

+ +

st_writev()

+ +Writes data to a specified file descriptor object from multiple buffers. +

+

Syntax
+ +
+#include <st.h>
+
+ssize_t st_writev(st_netfd_t fd, const struct iovec *iov, int iov_size,
+                  st_utime_t timeout);
+
+

+

Parameters
+st_writev() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t).

+iov

+An array of iovec structures that describe the buffers to write +from (see writev(2)).

+iov_size

+Number of iovec structures in the iov array.

+timeout

+A value of type st_utime_t specifying the +inactivity timeout (in microseconds). +Note that timeouts are measured since the +last context switch. +

+

Returns
+On success a non-negative integer equal to the sum of all the buffer lengths +is returned. Otherwise, a value of -1 is returned and errno +is set to indicate the error. Possible errno values are the same as +set by the writev(2) call with two exceptions:

+ + + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred. +
+

+

Description
+This function blocks the calling thread until all the data is written, +a timeout occurs, or the write operation fails. The return value is equal to +either the sum of all the buffer lengths (on success) or -1 (on +failure). Note that if st_writev() returns -1, part of the +data may have been written before an error occurred. +

+


+

+ +

st_writev_resid()

+ +Writes multiple buffers of data to a specified file descriptor object. +

+

Syntax
+ +
+#include <st.h>
+
+int st_writev_resid(st_netfd_t fd, struct iovec **iov, int *iov_size,
+		    st_utime_t timeout);
+
+

+

Parameters
+st_writev_resid() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t).

+iov

+A pointer to an array of iovec structures. +On entry, the iovecs identify the buffers holding the data to write. +On return, the incomplete iovecs. +This function modifies both the pointer and the array to which it points.

+iov_size

+A pointer to a number of iovec structures. +On entry, the number of iovec structures pointed to by *iov. +On return, the number of incomplete or unused iovec structures. +(A non-zero returned value means some but not all of the data was written.)

+timeout

+A value of type st_utime_t specifying the +inactivity timeout (in microseconds). +Note that timeouts are measured since the +last context switch. +

+

Returns
+On success, zero is returned, *iov_size is zero, and *iov +points to the first iovec after the end of the original array. +Otherwise, a value of -1 is returned, *iov_size is non-zero, +*iov points to the first incomplete iovec, and errno is set +to indicate the error. Possible errno values are the same as set +by the writev(2) call with two exceptions:

+ + + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred. +
+

+All of the iovecs before *iov are modified such that +iov_base points to the end of the original buffer and +iov_len is zero. +

+

Description
+This function blocks the calling thread until all the data is written, +a timeout occurs, or the write operation fails. It differs from +st_writev() only in that it allows the caller to know how many bytes +were transferred before an error occurred. +

+


+

+ +

st_recvfrom()

+ +Receives bytes from a file descriptor object and stores the sending peer's +address. +

+

Syntax
+ +
+#include <st.h>
+
+int st_recvfrom(st_netfd_t fd, void *buf, int len, struct sockaddr *from,
+                int *fromlen, st_utime_t timeout);
+
+

+

Parameters
+st_recvfrom() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t) representing a UDP socket.

+buf

+A pointer to a buffer to hold the data received.

+len

+The size of buf in bytes.

+from

+If this parameter is not a NULL pointer, the source address of the +message is filled in (see recvfrom(3)).

+fromlen

+This is a value-result parameter, initialized to the size of the buffer +associated with from, and modified on return to indicate the actual +size of the address stored there.

+timeout

+A value of type st_utime_t specifying the time +limit in microseconds for completion of the receive operation. +Note that timeouts are measured since the +last context switch. +

+

Returns
+On success a non-negative integer indicating the length of the received +message in bytes is returned. Otherwise, a value of -1 is returned +and errno is set to indicate the error. Possible errno +values are the same as set by the recvfrom(3) call with two +exceptions:

+ + + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred and no data was received. +
+

+

Description
+This function receives up to a specified number of bytes from the specified +file descriptor object representing a UDP socket.

+st_recvfrom() blocks the calling thread until one or more bytes are +transferred, a timeout has occurred, or there is an error. No more than +len bytes will be transferred. +

+


+

+ +

st_sendto()

+ +Sends bytes to a specified destination. +

+

Syntax
+ +
+#include <st.h>
+
+int st_sendto(st_netfd_t fd, const void *msg, int len, struct sockaddr *to,
+              int tolen, st_utime_t timeout);
+
+

+

Parameters
+st_sendto() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t) representing a UDP socket.

+msg

+A pointer to a buffer containing the message to be sent.

+len

+The length of the message to be sent (in bytes).

+to

+A pointer to the address of the destination (see sendto(3)).

+tolen

+This parameter specifies the size of the destination address.

+timeout

+A value of type st_utime_t specifying the time +limit in microseconds for completion of the send operation. +Note that timeouts are measured since the +last context switch. +

+

Returns
+On success a non-negative integer indicating the number of bytes sent is +returned. Otherwise, a value of -1 is returned and errno is +set to indicate the error. Possible errno values are the same as +set by the sendto(3) call with two exceptions:

+ + + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred and no data was sent. +
+

+

Description
+This function sends a specified number of bytes from a file descriptor +object representing a UDP socket to the specified destination address. +If no buffer space is available at the underlying OS socket to hold the +message to be transmitted, then st_sendto() blocks the calling +thread until the space becomes available, a timeout occurs, or an error +occurs. +

+


+

+ +

st_recvmsg()

+ +Receives a message from a file descriptor object. +

+

Syntax
+ +
+#include <st.h>
+
+int st_recvmsg(st_netfd_t fd, struct msghdr *msg, int flags,
+               st_utime_t timeout);
+
+

+

Parameters
+st_recvmsg() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t) representing a UDP socket.

+msg

+A pointer to a msghdr structure to describe the data received.

+flags

+Control flags for recvmsg(3).

+timeout

+A value of type st_utime_t specifying the time +limit in microseconds for completion of the receive operation. +Note that timeouts are measured since the +last context switch. +

+

Returns
+On success a non-negative integer indicating the number of bytes received +is returned. Otherwise, a value of -1 is returned +and errno is set to indicate the error. Possible errno +values are the same as set by the recvmsg(3) call with two +exceptions:

+ + + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred and no data was received. +
+

+

Description
+This function receives bytes from the specified file descriptor object +representing a UDP socket. The operation is controlled by the in/out +msg parameter.

+st_recvmsg() blocks the calling thread until one or more bytes are +transferred, a timeout has occurred, or there is an error. +

+


+

+ +

st_sendmsg()

+ +Sends a message to a file descriptor object. +

+

Syntax
+ +
+#include <st.h>
+
+int st_sendmsg(st_netfd_t fd, const struct msghdr *msg, int flags,
+               st_utime_t timeout);
+
+

+

Parameters
+st_sendmsg() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t) representing a UDP socket.

+msg

+A pointer to a msghdr structure describing the message to be sent.

+flags

+Control flags for sendmsg(3).

+timeout

+A value of type st_utime_t specifying the time +limit in microseconds for completion of the send operation. +Note that timeouts are measured since the +last context switch. +

+

Returns
+On success a non-negative integer indicating the number of bytes sent is +returned. Otherwise, a value of -1 is returned and errno is +set to indicate the error. Possible errno values are the same as +set by the sendmsg(3) call with two exceptions:

+ + + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred and no data was sent. +
+

+

Description
+This function sends bytes to a file descriptor object representing a UDP +socket. The operation is controlled by the msg parameter. +If no buffer space is available at the underlying OS socket to hold the +message to be transmitted, then st_sendmsg() blocks the calling +thread until the space becomes available, a timeout occurs, or an error +occurs. +

+


+

+ +

st_open()

+ +Opens a file for reading, writing, or both. +

+

Syntax
+ +
+#include <st.h>
+
+st_netfd_t st_open(const char *path, int oflags, mode_t mode);
+
+

+

Parameters
+st_open() has the following parameters:

+path

+The pathname of the file to be opened.

+oflags

+File status flags. These are the same flags that are used by the +open(2) system call.

+mode

+Access permission bits of the file mode, if the file is created when +O_CREAT is set in oflags (see open(2)). +

+

Returns
+Upon successful completion, a new file descriptor object identifier is +returned. Otherwise, NULL is returned and errno is set +to indicate the error. +

+

Description
+This function creates a new file descriptor object of type +st_netfd_t for the file with the pathname +path. This object can be freed by +st_netfd_free() or +st_netfd_close().

+The primary purpose of this function is to open FIFOs (named pipes) or +other special files in order to create an end point of communication. +However, it can be used on regular files as well.

+Among other things, this function always sets a non-blocking flag on the +underlying OS file descriptor, so there is no need to include that flag in +oflags. +

+


+

+ +

st_poll()

+ +Detects when I/O is ready for a set of OS file descriptors. +

+

Syntax
+ +
+#include <st.h>
+
+int st_poll(struct pollfd *pds, int npds, st_utime_t timeout);
+
+

+

Parameters
+st_poll() has the following parameters:

+pds

+A pointer to an array of pollfd structures (see poll(2)). +

+npds

+The number of elements in the pds array.

+timeout

+A value of type st_utime_t specifying the +amount of time in microseconds the call will block waiting for I/O +to become ready. If this time expires without any I/O becoming ready, +st_poll() returns zero. +Note that timeouts are measured since the +last context switch. +

+

Returns
+Upon successful completion, a non-negative value is returned. A positive +value indicates the total number of OS file descriptors in pds +that have events. A value of 0 indicates that the call timed out. +Upon failure, a value of -1 is returned and errno is set +to indicate the error:

+ + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
+

+If an alternative event notification mechanism has been set by +st_set_eventsys(), other values of +errno could be set upon failure as well. The values +depend on the specific mechanism in use. +

+

Description
+This function returns as soon as I/O is ready on one or more of the specified +OS file descriptors. A count of the number of ready descriptors is returned +unless a timeout occurs, in which case zero is returned.

+The pollfd structure is defined in the poll.h header file +and contains the following members:

+

+    int fd;             /* OS file descriptor */
+    short events;       /* requested events   */
+    short revents;      /* returned events    */
+
+The events field should be set to the I/O events (readable, +writable, exception, or some combination) that the caller is interested in. +On return, the revents field is set to indicate what kind of I/O +is ready on the respective descriptor.

+The events and revents fields are constructed by OR-ing +any combination of the following event flags (defined in poll.h): +

+ + + + + + +
POLLINfd is readable.
POLLOUTfd is is writable.
POLLPRIfd has an exception condition.
POLLNVALfd is bad.
+

+The POLLNVAL flag is only valid in the revents field; +it is not used in the events field.

+Despite having an interface like poll(2), this function uses +the same event notification mechanism as the rest of the library. For +instance if an alternative event nofication mechanism was set using st_set_eventsys(), this function uses that +mechanism to check for events.

+Note that unlike the poll(2) call, this function has the +timeout parameter expressed in microseconds. If the value of +timeout is ST_UTIME_NO_TIMEOUT +(-1), this function blocks until a requested I/O +event occurs or until the call is interrupted by +st_thread_interrupt(). +

+Note: if kqueue(2) is used as an alternative event +notification mechanism (see st_set_eventsys()), the POLLPRI +event flag is not supported and st_poll() will return an error +if it's set (errno will be set to EINVAL). +

+


+

+ + +

Program Structure

+ +

+Generally, the following steps should be followed when writing an application +using the State Threads library: +

+

    +
  1. Configure the library by calling these pre-init functions, if desired. + +
  2. +

    +

  3. Initialize the library by calling st_init().
  4. +

    +

  5. Configure the library by calling these post-init functions, if desired. + +
  6. +

    +

  7. Create resources that will be shared among different processes: + create and bind listening sockets (see socket(3), + bind(3), listen(3), + st_netfd_open_socket(), and possibly + st_netfd_serialize_accept()), + create shared memory segments, inter-process communication (IPC) + channels and synchronization primitives (if any).
  8. +

    +

  9. Create several processes via fork(2). The parent process should + either exit or become a "watchdog" (e.g., it starts a new process when + an existing one crashes, does a cleanup upon application termination, + etc.).
  10. +

    +

  11. In each child process create a pool of threads (see + st_thread_create()) to handle user + connections. Each thread in the pool may accept client connections + (st_accept()), connect to other servers + (st_connect()), perform various network I/O + (st_read(), st_write(), etc.).
  12. +
+

+Note that only State Threads library I/O functions should +be used for a network I/O: any other I/O calls may block the calling process +indefinitely. For example, standard I/O functions (fgets(3), +fread(3), fwrite(3), fprintf(3), etc.) call +read(2) and write(2) directly and therefore should not be +used on sockets or pipes. +

+Also note that for short timeouts to work the program +should do context switches (for example by calling +st_usleep()) on a regular basis. +

+


+

+ + +

List of Blocking Functions

+ +

+The thread context switch (process state change) can only happen +in a well-known set of blocking functions. +Only the following functions can block the calling thread: +

+

+
st_thread_join()
+
st_sleep()
+
st_usleep()
+
st_cond_wait()
+
st_cond_timedwait()
+
st_mutex_lock()
+
st_netfd_poll()
+
st_accept()
+
st_connect()
+
st_read()
+
st_read_fully()
+
st_read_resid()
+
st_readv()
+
st_readv_resid()
+
st_write()
+
st_write_resid()
+
st_writev()
+
st_writev_resid()
+
st_recvfrom()
+
st_sendto()
+
st_recvmsg()
+
st_sendmsg()
+
st_poll()
+
+

+


+

+ + + + diff --git a/trunk/research/st-1.9/docs/st.html b/trunk/research/st-1.9/docs/st.html new file mode 100644 index 0000000000..a6b932a819 --- /dev/null +++ b/trunk/research/st-1.9/docs/st.html @@ -0,0 +1,504 @@ + + +State Threads for Internet Applications + + +

State Threads for Internet Applications

+

Introduction

+

+State Threads is an application library which provides a +foundation for writing fast and highly scalable Internet Applications +on UNIX-like platforms. It combines the simplicity of the multithreaded +programming paradigm, in which one thread supports each simultaneous +connection, with the performance and scalability of an event-driven +state machine architecture.

+ +

1. Definitions

+

+ +

1.1 Internet Applications

+ +

+An Internet Application (IA) is either a server or client network +application that accepts connections from clients and may or may not +connect to servers. In an IA the arrival or departure of network data +often controls processing (that is, IA is a data-driven application). +For each connection, an IA does some finite amount of work +involving data exchange with its peer, where its peer may be either +a client or a server. +The typical transaction steps of an IA are to accept a connection, +read a request, do some finite and predictable amount of work to +process the request, then write a response to the peer that sent the +request. One example of an IA is a Web server; +the most general example of an IA is a proxy server, because it both +accepts connections from clients and connects to other servers.

+

+We assume that the performance of an IA is constrained by available CPU +cycles rather than network bandwidth or disk I/O (that is, CPU +is a bottleneck resource). +

+ + +

1.2 Performance and Scalability

+ +

+The performance of an IA is usually evaluated as its +throughput measured in transactions per second or bytes per second (one +can be converted to the other, given the average transaction size). There are +several benchmarks that can be used to measure throughput of Web serving +applications for specific workloads (such as +SPECweb96, +WebStone, +WebBench). +Although there is no common definition for scalability, in general it +expresses the ability of an application to sustain its performance when some +external condition changes. For IAs this external condition is either the +number of clients (also known as "users," "simultaneous connections," or "load +generators") or the underlying hardware system size (number of CPUs, memory +size, and so on). Thus there are two types of scalability: load +scalability and system scalability, respectively. +

+The figure below shows how the throughput of an idealized IA changes with +the increasing number of clients (solid blue line). Initially the throughput +grows linearly (the slope represents the maximal throughput that one client +can provide). Within this initial range, the IA is underutilized and CPUs are +partially idle. Further increase in the number of clients leads to a system +saturation, and the throughput gradually stops growing as all CPUs become fully +utilized. After that point, the throughput stays flat because there are no +more CPU cycles available. +In the real world, however, each simultaneous connection +consumes some computational and memory resources, even when idle, and this +overhead grows with the number of clients. Therefore, the throughput of the +real world IA starts dropping after some point (dashed blue line in the figure +below). The rate at which the throughput drops depends, among other things, on +application design. +

+We say that an application has a good load scalability if it can +sustain its throughput over a wide range of loads. +Interestingly, the SPECweb99 +benchmark somewhat reflects the Web server's load scalability because it +measures the number of clients (load generators) given a mandatory minimal +throughput per client (that is, it measures the server's capacity). +This is unlike SPECweb96 and +other benchmarks that use the throughput as their main metric (see the figure +below). +

+

Figure: Throughput vs. Number of clients +
+

+System scalability is the ability of an application to sustain its +performance per hardware unit (such as a CPU) with the increasing number of +these units. In other words, good system scalability means that doubling the +number of processors will roughly double the application's throughput (dashed +green line). We assume here that the underlying operating system also scales +well. Good system scalability allows you to initially run an application on +the smallest system possible, while retaining the ability to move that +application to a larger system if necessary, without excessive effort or +expense. That is, an application need not be rewritten or even undergo a +major porting effort when changing system size. +

+Although scalability and performance are more important in the case of server +IAs, they should also be considered for some client applications (such as +benchmark load generators). +

+ + +

1.3 Concurrency

+ +

+Concurrency reflects the parallelism in a system. The two unrelated types +are virtual concurrency and real concurrency. +

    +
  • Virtual (or apparent) concurrency is the number of simultaneous +connections that a system supports. +

    +
  • Real concurrency is the number of hardware devices, including +CPUs, network cards, and disks, that actually allow a system to perform +tasks in parallel. +
+

+An IA must provide virtual concurrency in order to serve many users +simultaneously. +To achieve maximum performance and scalability in doing so, the number of +programming entities than an IA creates to be scheduled by the OS kernel +should be +kept close to (within an order of magnitude of) the real concurrency found on +the system. These programming entities scheduled by the kernel are known as +kernel execution vehicles. Examples of kernel execution vehicles +include Solaris lightweight processes and IRIX kernel threads. +In other words, the number of kernel execution vehicles should be dictated by +the system size and not by the number of simultaneous connections. +

+ +

2. Existing Architectures

+

+There are a few different architectures that are commonly used by IAs. +These include the Multi-Process, +Multi-Threaded, and Event-Driven State Machine +architectures. +

+ +

2.1 Multi-Process Architecture

+ +

+In the Multi-Process (MP) architecture, an individual process is +dedicated to each simultaneous connection. +A process performs all of a transaction's initialization steps +and services a connection completely before moving on to service +a new connection. +

+User sessions in IAs are relatively independent; therefore, no +synchronization between processes handling different connections is +necessary. Because each process has its own private address space, +this architecture is very robust. If a process serving one of the connections +crashes, the other sessions will not be affected. However, to serve many +concurrent connections, an equal number of processes must be employed. +Because processes are kernel entities (and are in fact the heaviest ones), +the number of kernel entities will be at least as large as the number of +concurrent sessions. On most systems, good performance will not be achieved +when more than a few hundred processes are created because of the high +context-switching overhead. In other words, MP applications have poor load +scalability. +

+On the other hand, MP applications have very good system scalability, because +no resources are shared among different processes and there is no +synchronization overhead. +

+The Apache Web Server 1.x ([Reference 1]) uses the MP +architecture on UNIX systems. +

+ +

2.2 Multi-Threaded Architecture

+ +

+In the Multi-Threaded (MT) architecture, multiple independent threads +of control are employed within a single shared address space. Like a +process in the MP architecture, each thread performs all of a +transaction's initialization steps and services a connection completely +before moving on to service a new connection. +

+Many modern UNIX operating systems implement a many-to-few model when +mapping user-level threads to kernel entities. In this model, an +arbitrarily large number of user-level threads is multiplexed onto a +lesser number of kernel execution vehicles. Kernel execution +vehicles are also known as virtual processors. Whenever a user-level +thread makes a blocking system call, the kernel execution vehicle it is using +will become blocked in the kernel. If there are no other non-blocked kernel +execution vehicles and there are other runnable user-level threads, a new +kernel execution vehicle will be created automatically. This prevents the +application from blocking when it can continue to make useful forward +progress. +

+Because IAs are by nature network I/O driven, all concurrent sessions block on +network I/O at various points. As a result, the number of virtual processors +created in the kernel grows close to the number of user-level threads +(or simultaneous connections). When this occurs, the many-to-few model +effectively degenerates to a one-to-one model. Again, like in +the MP architecture, the number of kernel execution vehicles is dictated by +the number of simultaneous connections rather than by number of CPUs. This +reduces an application's load scalability. However, because kernel threads +(lightweight processes) use fewer resources and are more light-weight than +traditional UNIX processes, an MT application should scale better with load +than an MP application. +

+Unexpectedly, the small number of virtual processors sharing the same address +space in the MT architecture destroys an application's system scalability +because of contention among the threads on various locks. Even if an +application itself is carefully +optimized to avoid lock contention around its own global data (a non-trivial +task), there are still standard library functions and system calls +that use common resources hidden from the application. For example, +on many platforms thread safety of memory allocation routines +(malloc(3), free(3), and so on) is achieved by using a single +global lock. Another example is a per-process file descriptor table. +This common resource table is shared by all kernel execution vehicles within +the same process and must be protected when one modifies it via +certain system calls (such as open(2), close(2), and so on). +In addition to that, maintaining the caches coherent +among CPUs on multiprocessor systems hurts performance when different threads +running on different CPUs modify data items on the same cache line. +

+In order to improve load scalability, some applications employ a different +type of MT architecture: they create one or more thread(s) per task +rather than one thread per connection. For example, one small group +of threads may be responsible for accepting client connections, another +for request processing, and yet another for serving responses. The main +advantage of this architecture is that it eliminates the tight coupling +between the number of threads and number of simultaneous connections. However, +in this architecture, different task-specific thread groups must share common +work queues that must be protected by mutual exclusion locks (a typical +producer-consumer problem). This adds synchronization overhead that causes an +application to perform badly on multiprocessor systems. In other words, in +this architecture, the application's system scalability is sacrificed for the +sake of load scalability. +

+Of course, the usual nightmares of threaded programming, including data +corruption, deadlocks, and race conditions, also make MT architecture (in any +form) non-simplistic to use. +

+ + +

2.3 Event-Driven State Machine Architecture

+ +

+In the Event-Driven State Machine (EDSM) architecture, a single process +is employed to concurrently process multiple connections. The basics of this +architecture are described in Comer and Stevens +[Reference 2]. +The EDSM architecture performs one basic data-driven step associated with +a particular connection at a time, thus multiplexing many concurrent +connections. The process operates as a state machine that receives an event +and then reacts to it. +

+In the idle state the EDSM calls select(2) or poll(2) to +wait for network I/O events. When a particular file descriptor is ready for +I/O, the EDSM completes the corresponding basic step (usually by invoking a +handler function) and starts the next one. This architecture uses +non-blocking system calls to perform asynchronous network I/O operations. +For more details on non-blocking I/O see Stevens +[Reference 3]. +

+To take advantage of hardware parallelism (real concurrency), multiple +identical processes may be created. This is called Symmetric Multi-Process +EDSM and is used, for example, in the Zeus Web Server +([Reference 4]). To more efficiently multiplex disk I/O, +special "helper" processes may be created. This is called Asymmetric +Multi-Process EDSM and was proposed for Web servers by Druschel +and others [Reference 5]. +

+EDSM is probably the most scalable architecture for IAs. +Because the number of simultaneous connections (virtual concurrency) is +completely decoupled from the number of kernel execution vehicles (processes), +this architecture has very good load scalability. It requires only minimal +user-level resources to create and maintain additional connection. +

+Like MP applications, Multi-Process EDSM has very good system scalability +because no resources are shared among different processes and there is no +synchronization overhead. +

+Unfortunately, the EDSM architecture is monolithic rather than based on the +concept of threads, so new applications generally need to be implemented from +the ground up. In effect, the EDSM architecture simulates threads and their +stacks the hard way. +

+ + +

3. State Threads Library

+ +

+The State Threads library combines the advantages of all of the above +architectures. The interface preserves the programming simplicity of thread +abstraction, allowing each simultaneous connection to be treated as a separate +thread of execution within a single process. The underlying implementation is +close to the EDSM architecture as the state of each particular concurrent +session is saved in a separate memory segment. +

+ +

3.1 State Changes and Scheduling

+

+The state of each concurrent session includes its stack environment +(stack pointer, program counter, CPU registers) and its stack. Conceptually, +a thread context switch can be viewed as a process changing its state. There +are no kernel entities involved other than processes. +Unlike other general-purpose threading libraries, the State Threads library +is fully deterministic. The thread context switch (process state change) can +only happen in a well-known set of functions (at I/O points or at explicit +synchronization points). As a result, process-specific global data does not +have to be protected by mutual exclusion locks in most cases. The entire +application is free to use all the static variables and non-reentrant library +functions it wants, greatly simplifying programming and debugging while +increasing performance. This is somewhat similar to a co-routine model +(co-operatively multitasked threads), except that no explicit yield is needed +-- +sooner or later, a thread performs a blocking I/O operation and thus surrenders +control. All threads of execution (simultaneous connections) have the +same priority, so scheduling is non-preemptive, like in the EDSM architecture. +Because IAs are data-driven (processing is limited by the size of network +buffers and data arrival rates), scheduling is non-time-slicing. +

+Only two types of external events are handled by the library's +scheduler, because only these events can be detected by +select(2) or poll(2): I/O events (a file descriptor is ready +for I/O) and time events +(some timeout has expired). However, other types of events (such as +a signal sent to a process) can also be handled by converting them to I/O +events. For example, a signal handling function can perform a write to a pipe +(write(2) is reentrant/asynchronous-safe), thus converting a signal +event to an I/O event. +

+To take advantage of hardware parallelism, as in the EDSM architecture, +multiple processes can be created in either a symmetric or asymmetric manner. +Process management is not in the library's scope but instead is left up to the +application. +

+There are several general-purpose threading libraries that implement a +many-to-one model (many user-level threads to one kernel execution +vehicle), using the same basic techniques as the State Threads library +(non-blocking I/O, event-driven scheduler, and so on). For an example, see GNU +Portable Threads ([Reference 6]). Because they are +general-purpose, these libraries have different objectives than the State +Threads library. The State Threads library is not a general-purpose +threading library, +but rather an application library that targets only certain types of +applications (IAs) in order to achieve the highest possible performance and +scalability for those applications. +

+ +

3.2 Scalability

+

+State threads are very lightweight user-level entities, and therefore creating +and maintaining user connections requires minimal resources. An application +using the State Threads library scales very well with the increasing number +of connections. +

+On multiprocessor systems an application should create multiple processes +to take advantage of hardware parallelism. Using multiple separate processes +is the only way to achieve the highest possible system scalability. +This is because duplicating per-process resources is the only way to avoid +significant synchronization overhead on multiprocessor systems. Creating +separate UNIX processes naturally offers resource duplication. Again, +as in the EDSM architecture, there is no connection between the number of +simultaneous connections (which may be very large and changes within a wide +range) and the number of kernel entities (which is usually small and constant). +In other words, the State Threads library makes it possible to multiplex a +large number of simultaneous connections onto a much smaller number of +separate processes, thus allowing an application to scale well with both +the load and system size. +

+ +

3.3 Performance

+

+Performance is one of the library's main objectives. The State Threads +library is implemented to minimize the number of system calls and +to make thread creation and context switching as fast as possible. +For example, per-thread signal mask does not exist (unlike +POSIX threads), so there is no need to save and restore a process's +signal mask on every thread context switch. This eliminates two system +calls per context switch. Signal events can be handled much more +efficiently by converting them to I/O events (see above). +

+ +

3.4 Portability

+

+The library uses the same general, underlying concepts as the EDSM +architecture, including non-blocking I/O, file descriptors, and +I/O multiplexing. These concepts are available in some form on most +UNIX platforms, making the library very portable across many +flavors of UNIX. There are only a few platform-dependent sections in the +source. +

+ +

3.5 State Threads and NSPR

+

+The State Threads library is a derivative of the Netscape Portable +Runtime library (NSPR) [Reference 7]. The primary goal of +NSPR is to provide a platform-independent layer for system facilities, +where system facilities include threads, thread synchronization, and I/O. +Performance and scalability are not the main concern of NSPR. The +State Threads library addresses performance and scalability while +remaining much smaller than NSPR. It is contained in 8 source files +as opposed to more than 400, but provides all the functionality that +is needed to write efficient IAs on UNIX-like platforms. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
NSPRState Threads
Lines of code~150,000~3000
Dynamic library size  
(debug version)
IRIX~700 KB~60 KB
Linux~900 KB~70 KB
+

+ +

Conclusion

+

+State Threads is an application library which provides a foundation for +writing Internet Applications. To summarize, it has the +following advantages: +

+

    +
  • It allows the design of fast and highly scalable applications. An +application will scale well with both load and number of CPUs. +

    +

  • It greatly simplifies application programming and debugging because, as a +rule, no mutual exclusion locking is necessary and the entire application is +free to use static variables and non-reentrant library functions. +
+

+The library's main limitation: +

+

    +
  • All I/O operations on sockets must use the State Thread library's I/O +functions because only those functions perform thread scheduling and prevent +the application's processes from blocking. +
+

+ +

References

+
    + +
  1. Apache Software Foundation, +http://www.apache.org. + +
  2. Douglas E. Comer, David L. Stevens, Internetworking With TCP/IP, +Vol. III: Client-Server Programming And Applications, Second Edition, +Ch. 8, 12. + +
  3. W. Richard Stevens, UNIX Network Programming, Second Edition, +Vol. 1, Ch. 15. + +
  4. Zeus Technology Limited, +http://www.zeus.co.uk. + +
  5. Peter Druschel, Vivek S. Pai, Willy Zwaenepoel, + +Flash: An Efficient and Portable Web Server. In Proceedings of the +USENIX 1999 Annual Technical Conference, Monterey, CA, June 1999. + +
  6. GNU Portable Threads, +http://www.gnu.org/software/pth/. + +
  7. Netscape Portable Runtime, +http://www.mozilla.org/docs/refList/refNSPR/. +
+ +

Other resources covering various architectural issues in IAs

+
    +
  1. Dan Kegel, The C10K problem, +http://www.kegel.com/c10k.html. +
  2. +
  3. James C. Hu, Douglas C. Schmidt, Irfan Pyarali, JAWS: Understanding +High Performance Web Systems, +http://www.cs.wustl.edu/~jxh/research/research.html.
  4. +
+

+


+

+ +

Portions created by SGI are Copyright © 2000 +Silicon Graphics, Inc. All rights reserved.
+

+ + + + diff --git a/trunk/research/st-1.9/docs/timeout_heap.txt b/trunk/research/st-1.9/docs/timeout_heap.txt new file mode 100644 index 0000000000..1582dc1293 --- /dev/null +++ b/trunk/research/st-1.9/docs/timeout_heap.txt @@ -0,0 +1,60 @@ +How the timeout heap works + +As of version 1.5, the State Threads Library represents the queue of +sleeping threads using a heap data structure rather than a sorted +linked list. This improves performance when there is a large number +of sleeping threads, since insertion into a heap takes O(log N) time +while insertion into a sorted list takes O(N) time. For example, in +one test 1000 threads were created, each thread called st_usleep() +with a random time interval, and then all the threads where +immediately interrupted and joined before the sleeps had a chance to +finish. The whole process was repeated 1000 times, for a total of a +million sleep queue insertions and removals. With the old list-based +sleep queue, this test took 100 seconds; now it takes only 12 seconds. + +Heap data structures are typically based on dynamically resized +arrays. However, since the existing ST code base was very nicely +structured around linking the thread objects into pointer-based lists +without the need for any auxiliary data structures, implementing the +heap using a similar nodes-and-pointers based approach seemed more +appropriate for ST than introducing a separate array. + +Thus, the new ST timeout heap works by organizing the existing +_st_thread_t objects in a balanced binary tree, just as they were +previously organized into a doubly-linked, sorted list. The global +_ST_SLEEPQ variable, formerly a linked list head, is now simply a +pointer to the root of this tree, and the root node of the tree is the +thread with the earliest timeout. Each thread object has two child +pointers, "left" and "right", pointing to threads with later timeouts. + +Each node in the tree is numbered with an integer index, corresponding +to the array index in an array-based heap, and the tree is kept fully +balanced and left-adjusted at all times. In other words, the tree +consists of any number of fully populated top levels, followed by a +single bottom level which may be partially populated, such that any +existing nodes form a contiguous block to the left and the spaces for +missing nodes form a contiguous block to the right. For example, if +there are nine threads waiting for a timeout, they are numbered and +arranged in a tree exactly as follows: + + 1 + / \ + 2 3 + / \ / \ + 4 5 6 7 + / \ + 8 9 + +Each node has either no children, only a left child, or both a left +and a right child. Children always time out later than their parents +(this is called the "heap invariant"), but when a node has two +children, their mutual order is unspecified - the left child may time +out before or after the right child. If a node is numbered N, its +left child is numbered 2N, and its right child is numbered 2N+1. + +There is no pointer from a child to its parent; all pointers point +downward. Additions and deletions both work by starting at the root +and traversing the tree towards the leaves, going left or right +according to the binary digits forming the index of the destination +node. As nodes are added or deleted, existing nodes are rearranged to +maintain the heap invariant. diff --git a/trunk/research/st-1.9/event.c b/trunk/research/st-1.9/event.c new file mode 100644 index 0000000000..cb14aed5a0 --- /dev/null +++ b/trunk/research/st-1.9/event.c @@ -0,0 +1,1449 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Portable Runtime library. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Silicon Graphics, Inc. + * Yahoo! Inc. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#include +#include +#include +#include +#include +#include +#include "common.h" + +#ifdef MD_HAVE_KQUEUE +#include +#endif +#ifdef MD_HAVE_EPOLL +#include +#endif + +#if defined(USE_POLL) && !defined(MD_HAVE_POLL) +/* Force poll usage if explicitly asked for it */ +#define MD_HAVE_POLL +#endif + + +static struct _st_seldata { + fd_set fd_read_set, fd_write_set, fd_exception_set; + int fd_ref_cnts[FD_SETSIZE][3]; + int maxfd; +} *_st_select_data; + +#define _ST_SELECT_MAX_OSFD (_st_select_data->maxfd) +#define _ST_SELECT_READ_SET (_st_select_data->fd_read_set) +#define _ST_SELECT_WRITE_SET (_st_select_data->fd_write_set) +#define _ST_SELECT_EXCEP_SET (_st_select_data->fd_exception_set) +#define _ST_SELECT_READ_CNT(fd) (_st_select_data->fd_ref_cnts[fd][0]) +#define _ST_SELECT_WRITE_CNT(fd) (_st_select_data->fd_ref_cnts[fd][1]) +#define _ST_SELECT_EXCEP_CNT(fd) (_st_select_data->fd_ref_cnts[fd][2]) + + +#ifdef MD_HAVE_POLL +static struct _st_polldata { + struct pollfd *pollfds; + int pollfds_size; + int fdcnt; +} *_st_poll_data; + +#define _ST_POLL_OSFD_CNT (_st_poll_data->fdcnt) +#define _ST_POLLFDS (_st_poll_data->pollfds) +#define _ST_POLLFDS_SIZE (_st_poll_data->pollfds_size) +#endif /* MD_HAVE_POLL */ + + +#ifdef MD_HAVE_KQUEUE +typedef struct _kq_fd_data { + int rd_ref_cnt; + int wr_ref_cnt; + int revents; +} _kq_fd_data_t; + +static struct _st_kqdata { + _kq_fd_data_t *fd_data; + struct kevent *evtlist; + struct kevent *addlist; + struct kevent *dellist; + int fd_data_size; + int evtlist_size; + int addlist_size; + int addlist_cnt; + int dellist_size; + int dellist_cnt; + int kq; + pid_t pid; +} *_st_kq_data; + +#ifndef ST_KQ_MIN_EVTLIST_SIZE +#define ST_KQ_MIN_EVTLIST_SIZE 64 +#endif + +#define _ST_KQ_READ_CNT(fd) (_st_kq_data->fd_data[fd].rd_ref_cnt) +#define _ST_KQ_WRITE_CNT(fd) (_st_kq_data->fd_data[fd].wr_ref_cnt) +#define _ST_KQ_REVENTS(fd) (_st_kq_data->fd_data[fd].revents) +#endif /* MD_HAVE_KQUEUE */ + + +#ifdef MD_HAVE_EPOLL +typedef struct _epoll_fd_data { + int rd_ref_cnt; + int wr_ref_cnt; + int ex_ref_cnt; + int revents; +} _epoll_fd_data_t; + +static struct _st_epolldata { + _epoll_fd_data_t *fd_data; + struct epoll_event *evtlist; + int fd_data_size; + int evtlist_size; + int evtlist_cnt; + int fd_hint; + int epfd; + pid_t pid; +} *_st_epoll_data; + +#ifndef ST_EPOLL_EVTLIST_SIZE +/* Not a limit, just a hint */ +#define ST_EPOLL_EVTLIST_SIZE 4096 +#endif + +#define _ST_EPOLL_READ_CNT(fd) (_st_epoll_data->fd_data[fd].rd_ref_cnt) +#define _ST_EPOLL_WRITE_CNT(fd) (_st_epoll_data->fd_data[fd].wr_ref_cnt) +#define _ST_EPOLL_EXCEP_CNT(fd) (_st_epoll_data->fd_data[fd].ex_ref_cnt) +#define _ST_EPOLL_REVENTS(fd) (_st_epoll_data->fd_data[fd].revents) + +#define _ST_EPOLL_READ_BIT(fd) (_ST_EPOLL_READ_CNT(fd) ? EPOLLIN : 0) +#define _ST_EPOLL_WRITE_BIT(fd) (_ST_EPOLL_WRITE_CNT(fd) ? EPOLLOUT : 0) +#define _ST_EPOLL_EXCEP_BIT(fd) (_ST_EPOLL_EXCEP_CNT(fd) ? EPOLLPRI : 0) +#define _ST_EPOLL_EVENTS(fd) \ + (_ST_EPOLL_READ_BIT(fd)|_ST_EPOLL_WRITE_BIT(fd)|_ST_EPOLL_EXCEP_BIT(fd)) + +#endif /* MD_HAVE_EPOLL */ + +_st_eventsys_t *_st_eventsys = NULL; + + +/***************************************** + * select event system + */ + +ST_HIDDEN int _st_select_init(void) +{ + _st_select_data = (struct _st_seldata *) malloc(sizeof(*_st_select_data)); + if (!_st_select_data) + return -1; + + memset(_st_select_data, 0, sizeof(*_st_select_data)); + _st_select_data->maxfd = -1; + + return 0; +} + +ST_HIDDEN int _st_select_pollset_add(struct pollfd *pds, int npds) +{ + struct pollfd *pd; + struct pollfd *epd = pds + npds; + + /* Do checks up front */ + for (pd = pds; pd < epd; pd++) { + if (pd->fd < 0 || pd->fd >= FD_SETSIZE || !pd->events || + (pd->events & ~(POLLIN | POLLOUT | POLLPRI))) { + errno = EINVAL; + return -1; + } + } + + for (pd = pds; pd < epd; pd++) { + if (pd->events & POLLIN) { + FD_SET(pd->fd, &_ST_SELECT_READ_SET); + _ST_SELECT_READ_CNT(pd->fd)++; + } + if (pd->events & POLLOUT) { + FD_SET(pd->fd, &_ST_SELECT_WRITE_SET); + _ST_SELECT_WRITE_CNT(pd->fd)++; + } + if (pd->events & POLLPRI) { + FD_SET(pd->fd, &_ST_SELECT_EXCEP_SET); + _ST_SELECT_EXCEP_CNT(pd->fd)++; + } + if (_ST_SELECT_MAX_OSFD < pd->fd) + _ST_SELECT_MAX_OSFD = pd->fd; + } + + return 0; +} + +ST_HIDDEN void _st_select_pollset_del(struct pollfd *pds, int npds) +{ + struct pollfd *pd; + struct pollfd *epd = pds + npds; + + for (pd = pds; pd < epd; pd++) { + if (pd->events & POLLIN) { + if (--_ST_SELECT_READ_CNT(pd->fd) == 0) + FD_CLR(pd->fd, &_ST_SELECT_READ_SET); + } + if (pd->events & POLLOUT) { + if (--_ST_SELECT_WRITE_CNT(pd->fd) == 0) + FD_CLR(pd->fd, &_ST_SELECT_WRITE_SET); + } + if (pd->events & POLLPRI) { + if (--_ST_SELECT_EXCEP_CNT(pd->fd) == 0) + FD_CLR(pd->fd, &_ST_SELECT_EXCEP_SET); + } + } +} + +ST_HIDDEN void _st_select_find_bad_fd(void) +{ + _st_clist_t *q; + _st_pollq_t *pq; + int notify; + struct pollfd *pds, *epds; + int pq_max_osfd, osfd; + short events; + + _ST_SELECT_MAX_OSFD = -1; + + for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) { + pq = _ST_POLLQUEUE_PTR(q); + notify = 0; + epds = pq->pds + pq->npds; + pq_max_osfd = -1; + + for (pds = pq->pds; pds < epds; pds++) { + osfd = pds->fd; + pds->revents = 0; + if (pds->events == 0) + continue; + if (fcntl(osfd, F_GETFL, 0) < 0) { + pds->revents = POLLNVAL; + notify = 1; + } + if (osfd > pq_max_osfd) { + pq_max_osfd = osfd; + } + } + + if (notify) { + ST_REMOVE_LINK(&pq->links); + pq->on_ioq = 0; + /* + * Decrement the count of descriptors for each descriptor/event + * because this I/O request is being removed from the ioq + */ + for (pds = pq->pds; pds < epds; pds++) { + osfd = pds->fd; + events = pds->events; + if (events & POLLIN) { + if (--_ST_SELECT_READ_CNT(osfd) == 0) { + FD_CLR(osfd, &_ST_SELECT_READ_SET); + } + } + if (events & POLLOUT) { + if (--_ST_SELECT_WRITE_CNT(osfd) == 0) { + FD_CLR(osfd, &_ST_SELECT_WRITE_SET); + } + } + if (events & POLLPRI) { + if (--_ST_SELECT_EXCEP_CNT(osfd) == 0) { + FD_CLR(osfd, &_ST_SELECT_EXCEP_SET); + } + } + } + + if (pq->thread->flags & _ST_FL_ON_SLEEPQ) + _ST_DEL_SLEEPQ(pq->thread); + pq->thread->state = _ST_ST_RUNNABLE; + _ST_ADD_RUNQ(pq->thread); + } else { + if (_ST_SELECT_MAX_OSFD < pq_max_osfd) + _ST_SELECT_MAX_OSFD = pq_max_osfd; + } + } +} + +ST_HIDDEN void _st_select_dispatch(void) +{ + struct timeval timeout, *tvp; + fd_set r, w, e; + fd_set *rp, *wp, *ep; + int nfd, pq_max_osfd, osfd; + _st_clist_t *q; + st_utime_t min_timeout; + _st_pollq_t *pq; + int notify; + struct pollfd *pds, *epds; + short events, revents; + + /* + * Assignment of fd_sets + */ + r = _ST_SELECT_READ_SET; + w = _ST_SELECT_WRITE_SET; + e = _ST_SELECT_EXCEP_SET; + + rp = &r; + wp = &w; + ep = &e; + + if (_ST_SLEEPQ == NULL) { + tvp = NULL; + } else { + min_timeout = (_ST_SLEEPQ->due <= _ST_LAST_CLOCK) ? 0 : + (_ST_SLEEPQ->due - _ST_LAST_CLOCK); + timeout.tv_sec = (int) (min_timeout / 1000000); + timeout.tv_usec = (int) (min_timeout % 1000000); + tvp = &timeout; + } + + /* Check for I/O operations */ + nfd = select(_ST_SELECT_MAX_OSFD + 1, rp, wp, ep, tvp); + + /* Notify threads that are associated with the selected descriptors */ + if (nfd > 0) { + _ST_SELECT_MAX_OSFD = -1; + for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) { + pq = _ST_POLLQUEUE_PTR(q); + notify = 0; + epds = pq->pds + pq->npds; + pq_max_osfd = -1; + + for (pds = pq->pds; pds < epds; pds++) { + osfd = pds->fd; + events = pds->events; + revents = 0; + if ((events & POLLIN) && FD_ISSET(osfd, rp)) { + revents |= POLLIN; + } + if ((events & POLLOUT) && FD_ISSET(osfd, wp)) { + revents |= POLLOUT; + } + if ((events & POLLPRI) && FD_ISSET(osfd, ep)) { + revents |= POLLPRI; + } + pds->revents = revents; + if (revents) { + notify = 1; + } + if (osfd > pq_max_osfd) { + pq_max_osfd = osfd; + } + } + if (notify) { + ST_REMOVE_LINK(&pq->links); + pq->on_ioq = 0; + /* + * Decrement the count of descriptors for each descriptor/event + * because this I/O request is being removed from the ioq + */ + for (pds = pq->pds; pds < epds; pds++) { + osfd = pds->fd; + events = pds->events; + if (events & POLLIN) { + if (--_ST_SELECT_READ_CNT(osfd) == 0) { + FD_CLR(osfd, &_ST_SELECT_READ_SET); + } + } + if (events & POLLOUT) { + if (--_ST_SELECT_WRITE_CNT(osfd) == 0) { + FD_CLR(osfd, &_ST_SELECT_WRITE_SET); + } + } + if (events & POLLPRI) { + if (--_ST_SELECT_EXCEP_CNT(osfd) == 0) { + FD_CLR(osfd, &_ST_SELECT_EXCEP_SET); + } + } + } + + if (pq->thread->flags & _ST_FL_ON_SLEEPQ) + _ST_DEL_SLEEPQ(pq->thread); + pq->thread->state = _ST_ST_RUNNABLE; + _ST_ADD_RUNQ(pq->thread); + } else { + if (_ST_SELECT_MAX_OSFD < pq_max_osfd) + _ST_SELECT_MAX_OSFD = pq_max_osfd; + } + } + } else if (nfd < 0) { + /* + * It can happen when a thread closes file descriptor + * that is being used by some other thread -- BAD! + */ + if (errno == EBADF) + _st_select_find_bad_fd(); + } +} + +ST_HIDDEN int _st_select_fd_new(int osfd) +{ + if (osfd >= FD_SETSIZE) { + errno = EMFILE; + return -1; + } + + return 0; +} + +ST_HIDDEN int _st_select_fd_close(int osfd) +{ + if (_ST_SELECT_READ_CNT(osfd) || _ST_SELECT_WRITE_CNT(osfd) || + _ST_SELECT_EXCEP_CNT(osfd)) { + errno = EBUSY; + return -1; + } + + return 0; +} + +ST_HIDDEN int _st_select_fd_getlimit(void) +{ + return FD_SETSIZE; +} + +static _st_eventsys_t _st_select_eventsys = { + "select", + ST_EVENTSYS_SELECT, + _st_select_init, + _st_select_dispatch, + _st_select_pollset_add, + _st_select_pollset_del, + _st_select_fd_new, + _st_select_fd_close, + _st_select_fd_getlimit +}; + + +#ifdef MD_HAVE_POLL +/***************************************** + * poll event system + */ + +ST_HIDDEN int _st_poll_init(void) +{ + _st_poll_data = (struct _st_polldata *) malloc(sizeof(*_st_poll_data)); + if (!_st_poll_data) + return -1; + + _ST_POLLFDS = (struct pollfd *) malloc(ST_MIN_POLLFDS_SIZE * + sizeof(struct pollfd)); + if (!_ST_POLLFDS) { + free(_st_poll_data); + _st_poll_data = NULL; + return -1; + } + _ST_POLLFDS_SIZE = ST_MIN_POLLFDS_SIZE; + _ST_POLL_OSFD_CNT = 0; + + return 0; +} + +ST_HIDDEN int _st_poll_pollset_add(struct pollfd *pds, int npds) +{ + struct pollfd *pd; + struct pollfd *epd = pds + npds; + + for (pd = pds; pd < epd; pd++) { + if (pd->fd < 0 || !pd->events) { + errno = EINVAL; + return -1; + } + } + + _ST_POLL_OSFD_CNT += npds; + + return 0; +} + +/* ARGSUSED */ +ST_HIDDEN void _st_poll_pollset_del(struct pollfd *pds, int npds) +{ + _ST_POLL_OSFD_CNT -= npds; + ST_ASSERT(_ST_POLL_OSFD_CNT >= 0); +} + +ST_HIDDEN void _st_poll_dispatch(void) +{ + int timeout, nfd; + _st_clist_t *q; + st_utime_t min_timeout; + _st_pollq_t *pq; + struct pollfd *pds, *epds, *pollfds; + + /* + * Build up the array of struct pollfd to wait on. + * If existing array is not big enough, release it and allocate a new one. + */ + ST_ASSERT(_ST_POLL_OSFD_CNT >= 0); + if (_ST_POLL_OSFD_CNT > _ST_POLLFDS_SIZE) { + free(_ST_POLLFDS); + _ST_POLLFDS = (struct pollfd *) malloc((_ST_POLL_OSFD_CNT + 10) * + sizeof(struct pollfd)); + ST_ASSERT(_ST_POLLFDS != NULL); + _ST_POLLFDS_SIZE = _ST_POLL_OSFD_CNT + 10; + } + pollfds = _ST_POLLFDS; + + /* Gather all descriptors into one array */ + for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) { + pq = _ST_POLLQUEUE_PTR(q); + memcpy(pollfds, pq->pds, sizeof(struct pollfd) * pq->npds); + pollfds += pq->npds; + } + ST_ASSERT(pollfds <= _ST_POLLFDS + _ST_POLLFDS_SIZE); + + if (_ST_SLEEPQ == NULL) { + timeout = -1; + } else { + min_timeout = (_ST_SLEEPQ->due <= _ST_LAST_CLOCK) ? 0 : + (_ST_SLEEPQ->due - _ST_LAST_CLOCK); + timeout = (int) (min_timeout / 1000); + } + + /* Check for I/O operations */ + nfd = poll(_ST_POLLFDS, _ST_POLL_OSFD_CNT, timeout); + + /* Notify threads that are associated with the selected descriptors */ + if (nfd > 0) { + pollfds = _ST_POLLFDS; + for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) { + pq = _ST_POLLQUEUE_PTR(q); + epds = pollfds + pq->npds; + for (pds = pollfds; pds < epds; pds++) { + if (pds->revents) + break; + } + if (pds < epds) { + memcpy(pq->pds, pollfds, sizeof(struct pollfd) * pq->npds); + ST_REMOVE_LINK(&pq->links); + pq->on_ioq = 0; + + if (pq->thread->flags & _ST_FL_ON_SLEEPQ) + _ST_DEL_SLEEPQ(pq->thread); + pq->thread->state = _ST_ST_RUNNABLE; + _ST_ADD_RUNQ(pq->thread); + + _ST_POLL_OSFD_CNT -= pq->npds; + ST_ASSERT(_ST_POLL_OSFD_CNT >= 0); + } + pollfds = epds; + } + } +} + +/* ARGSUSED */ +ST_HIDDEN int _st_poll_fd_new(int osfd) +{ + return 0; +} + +/* ARGSUSED */ +ST_HIDDEN int _st_poll_fd_close(int osfd) +{ + /* + * We don't maintain I/O counts for poll event system + * so nothing to check here. + */ + return 0; +} + +ST_HIDDEN int _st_poll_fd_getlimit(void) +{ + /* zero means no specific limit */ + return 0; +} + +static _st_eventsys_t _st_poll_eventsys = { + "poll", + ST_EVENTSYS_POLL, + _st_poll_init, + _st_poll_dispatch, + _st_poll_pollset_add, + _st_poll_pollset_del, + _st_poll_fd_new, + _st_poll_fd_close, + _st_poll_fd_getlimit +}; +#endif /* MD_HAVE_POLL */ + + +#ifdef MD_HAVE_KQUEUE +/***************************************** + * kqueue event system + */ + +ST_HIDDEN int _st_kq_init(void) +{ + int err = 0; + int rv = 0; + + _st_kq_data = (struct _st_kqdata *) calloc(1, sizeof(*_st_kq_data)); + if (!_st_kq_data) + return -1; + + if ((_st_kq_data->kq = kqueue()) < 0) { + err = errno; + rv = -1; + goto cleanup_kq; + } + fcntl(_st_kq_data->kq, F_SETFD, FD_CLOEXEC); + _st_kq_data->pid = getpid(); + + /* + * Allocate file descriptor data array. + * FD_SETSIZE looks like good initial size. + */ + _st_kq_data->fd_data_size = FD_SETSIZE; + _st_kq_data->fd_data = (_kq_fd_data_t *)calloc(_st_kq_data->fd_data_size, + sizeof(_kq_fd_data_t)); + if (!_st_kq_data->fd_data) { + err = errno; + rv = -1; + goto cleanup_kq; + } + + /* Allocate event lists */ + _st_kq_data->evtlist_size = ST_KQ_MIN_EVTLIST_SIZE; + _st_kq_data->evtlist = (struct kevent *)malloc(_st_kq_data->evtlist_size * + sizeof(struct kevent)); + _st_kq_data->addlist_size = ST_KQ_MIN_EVTLIST_SIZE; + _st_kq_data->addlist = (struct kevent *)malloc(_st_kq_data->addlist_size * + sizeof(struct kevent)); + _st_kq_data->dellist_size = ST_KQ_MIN_EVTLIST_SIZE; + _st_kq_data->dellist = (struct kevent *)malloc(_st_kq_data->dellist_size * + sizeof(struct kevent)); + if (!_st_kq_data->evtlist || !_st_kq_data->addlist || + !_st_kq_data->dellist) { + err = ENOMEM; + rv = -1; + } + + cleanup_kq: + if (rv < 0) { + if (_st_kq_data->kq >= 0) + close(_st_kq_data->kq); + free(_st_kq_data->fd_data); + free(_st_kq_data->evtlist); + free(_st_kq_data->addlist); + free(_st_kq_data->dellist); + free(_st_kq_data); + _st_kq_data = NULL; + errno = err; + } + + return rv; +} + +ST_HIDDEN int _st_kq_fd_data_expand(int maxfd) +{ + _kq_fd_data_t *ptr; + int n = _st_kq_data->fd_data_size; + + while (maxfd >= n) + n <<= 1; + + ptr = (_kq_fd_data_t *)realloc(_st_kq_data->fd_data, + n * sizeof(_kq_fd_data_t)); + if (!ptr) + return -1; + + memset(ptr + _st_kq_data->fd_data_size, 0, + (n - _st_kq_data->fd_data_size) * sizeof(_kq_fd_data_t)); + + _st_kq_data->fd_data = ptr; + _st_kq_data->fd_data_size = n; + + return 0; +} + +ST_HIDDEN int _st_kq_addlist_expand(int avail) +{ + struct kevent *ptr; + int n = _st_kq_data->addlist_size; + + while (avail > n - _st_kq_data->addlist_cnt) + n <<= 1; + + ptr = (struct kevent *)realloc(_st_kq_data->addlist, + n * sizeof(struct kevent)); + if (!ptr) + return -1; + + _st_kq_data->addlist = ptr; + _st_kq_data->addlist_size = n; + + /* + * Try to expand the result event list too + * (although we don't have to do it). + */ + ptr = (struct kevent *)realloc(_st_kq_data->evtlist, + n * sizeof(struct kevent)); + if (ptr) { + _st_kq_data->evtlist = ptr; + _st_kq_data->evtlist_size = n; + } + + return 0; +} + +ST_HIDDEN void _st_kq_addlist_add(const struct kevent *kev) +{ + ST_ASSERT(_st_kq_data->addlist_cnt < _st_kq_data->addlist_size); + memcpy(_st_kq_data->addlist + _st_kq_data->addlist_cnt, kev, + sizeof(struct kevent)); + _st_kq_data->addlist_cnt++; +} + +ST_HIDDEN void _st_kq_dellist_add(const struct kevent *kev) +{ + int n = _st_kq_data->dellist_size; + + if (_st_kq_data->dellist_cnt >= n) { + struct kevent *ptr; + + n <<= 1; + ptr = (struct kevent *)realloc(_st_kq_data->dellist, + n * sizeof(struct kevent)); + if (!ptr) { + /* See comment in _st_kq_pollset_del() */ + return; + } + + _st_kq_data->dellist = ptr; + _st_kq_data->dellist_size = n; + } + + memcpy(_st_kq_data->dellist + _st_kq_data->dellist_cnt, kev, + sizeof(struct kevent)); + _st_kq_data->dellist_cnt++; +} + +ST_HIDDEN int _st_kq_pollset_add(struct pollfd *pds, int npds) +{ + struct kevent kev; + struct pollfd *pd; + struct pollfd *epd = pds + npds; + + /* + * Pollset adding is "atomic". That is, either it succeeded for + * all descriptors in the set or it failed. It means that we + * need to do all the checks up front so we don't have to + * "unwind" if adding of one of the descriptors failed. + */ + for (pd = pds; pd < epd; pd++) { + /* POLLIN and/or POLLOUT must be set, but nothing else */ + if (pd->fd < 0 || !pd->events || (pd->events & ~(POLLIN | POLLOUT))) { + errno = EINVAL; + return -1; + } + if (pd->fd >= _st_kq_data->fd_data_size && + _st_kq_fd_data_expand(pd->fd) < 0) + return -1; + } + + /* + * Make sure we have enough room in the addlist for twice as many + * descriptors as in the pollset (for both READ and WRITE filters). + */ + npds <<= 1; + if (npds > _st_kq_data->addlist_size - _st_kq_data->addlist_cnt && + _st_kq_addlist_expand(npds) < 0) + return -1; + + for (pd = pds; pd < epd; pd++) { + if ((pd->events & POLLIN) && (_ST_KQ_READ_CNT(pd->fd)++ == 0)) { + memset(&kev, 0, sizeof(kev)); + kev.ident = pd->fd; + kev.filter = EVFILT_READ; +#ifdef NOTE_EOF + /* Make it behave like select() and poll() */ + kev.fflags = NOTE_EOF; +#endif + kev.flags = (EV_ADD | EV_ONESHOT); + _st_kq_addlist_add(&kev); + } + if ((pd->events & POLLOUT) && (_ST_KQ_WRITE_CNT(pd->fd)++ == 0)) { + memset(&kev, 0, sizeof(kev)); + kev.ident = pd->fd; + kev.filter = EVFILT_WRITE; + kev.flags = (EV_ADD | EV_ONESHOT); + _st_kq_addlist_add(&kev); + } + } + + return 0; +} + +ST_HIDDEN void _st_kq_pollset_del(struct pollfd *pds, int npds) +{ + struct kevent kev; + struct pollfd *pd; + struct pollfd *epd = pds + npds; + + /* + * It's OK if deleting fails because a descriptor will either be + * closed or fire only once (we set EV_ONESHOT flag). + */ + _st_kq_data->dellist_cnt = 0; + for (pd = pds; pd < epd; pd++) { + if ((pd->events & POLLIN) && (--_ST_KQ_READ_CNT(pd->fd) == 0)) { + memset(&kev, 0, sizeof(kev)); + kev.ident = pd->fd; + kev.filter = EVFILT_READ; + kev.flags = EV_DELETE; + _st_kq_dellist_add(&kev); + } + if ((pd->events & POLLOUT) && (--_ST_KQ_WRITE_CNT(pd->fd) == 0)) { + memset(&kev, 0, sizeof(kev)); + kev.ident = pd->fd; + kev.filter = EVFILT_WRITE; + kev.flags = EV_DELETE; + _st_kq_dellist_add(&kev); + } + } + + if (_st_kq_data->dellist_cnt > 0) { + /* + * We do "synchronous" kqueue deletes to avoid deleting + * closed descriptors and other possible problems. + */ + int rv; + do { + /* This kevent() won't block since result list size is 0 */ + rv = kevent(_st_kq_data->kq, _st_kq_data->dellist, + _st_kq_data->dellist_cnt, NULL, 0, NULL); + } while (rv < 0 && errno == EINTR); + } +} + +ST_HIDDEN void _st_kq_dispatch(void) +{ + struct timespec timeout, *tsp; + struct kevent kev; + st_utime_t min_timeout; + _st_clist_t *q; + _st_pollq_t *pq; + struct pollfd *pds, *epds; + int nfd, i, osfd, notify, filter; + short events, revents; + + if (_ST_SLEEPQ == NULL) { + tsp = NULL; + } else { + min_timeout = (_ST_SLEEPQ->due <= _ST_LAST_CLOCK) ? 0 : + (_ST_SLEEPQ->due - _ST_LAST_CLOCK); + timeout.tv_sec = (time_t) (min_timeout / 1000000); + timeout.tv_nsec = (long) ((min_timeout % 1000000) * 1000); + tsp = &timeout; + } + + retry_kevent: + /* Check for I/O operations */ + nfd = kevent(_st_kq_data->kq, + _st_kq_data->addlist, _st_kq_data->addlist_cnt, + _st_kq_data->evtlist, _st_kq_data->evtlist_size, tsp); + + _st_kq_data->addlist_cnt = 0; + + if (nfd > 0) { + for (i = 0; i < nfd; i++) { + osfd = _st_kq_data->evtlist[i].ident; + filter = _st_kq_data->evtlist[i].filter; + + if (filter == EVFILT_READ) { + _ST_KQ_REVENTS(osfd) |= POLLIN; + } else if (filter == EVFILT_WRITE) { + _ST_KQ_REVENTS(osfd) |= POLLOUT; + } + if (_st_kq_data->evtlist[i].flags & EV_ERROR) { + if (_st_kq_data->evtlist[i].data == EBADF) { + _ST_KQ_REVENTS(osfd) |= POLLNVAL; + } else { + _ST_KQ_REVENTS(osfd) |= POLLERR; + } + } + } + + _st_kq_data->dellist_cnt = 0; + + for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) { + pq = _ST_POLLQUEUE_PTR(q); + notify = 0; + epds = pq->pds + pq->npds; + + for (pds = pq->pds; pds < epds; pds++) { + osfd = pds->fd; + events = pds->events; + revents = (short)(_ST_KQ_REVENTS(osfd) & ~(POLLIN | POLLOUT)); + if ((events & POLLIN) && (_ST_KQ_REVENTS(osfd) & POLLIN)) { + revents |= POLLIN; + } + if ((events & POLLOUT) && (_ST_KQ_REVENTS(osfd) & POLLOUT)) { + revents |= POLLOUT; + } + pds->revents = revents; + if (revents) { + notify = 1; + } + } + if (notify) { + ST_REMOVE_LINK(&pq->links); + pq->on_ioq = 0; + for (pds = pq->pds; pds < epds; pds++) { + osfd = pds->fd; + events = pds->events; + /* + * We set EV_ONESHOT flag so we only need to delete + * descriptor if it didn't fire. + */ + if ((events & POLLIN) && (--_ST_KQ_READ_CNT(osfd) == 0) && + ((_ST_KQ_REVENTS(osfd) & POLLIN) == 0)) { + memset(&kev, 0, sizeof(kev)); + kev.ident = osfd; + kev.filter = EVFILT_READ; + kev.flags = EV_DELETE; + _st_kq_dellist_add(&kev); + } + if ((events & POLLOUT) && (--_ST_KQ_WRITE_CNT(osfd) == 0) + && ((_ST_KQ_REVENTS(osfd) & POLLOUT) == 0)) { + memset(&kev, 0, sizeof(kev)); + kev.ident = osfd; + kev.filter = EVFILT_WRITE; + kev.flags = EV_DELETE; + _st_kq_dellist_add(&kev); + } + } + + if (pq->thread->flags & _ST_FL_ON_SLEEPQ) + _ST_DEL_SLEEPQ(pq->thread); + pq->thread->state = _ST_ST_RUNNABLE; + _ST_ADD_RUNQ(pq->thread); + } + } + + if (_st_kq_data->dellist_cnt > 0) { + int rv; + do { + /* This kevent() won't block since result list size is 0 */ + rv = kevent(_st_kq_data->kq, _st_kq_data->dellist, + _st_kq_data->dellist_cnt, NULL, 0, NULL); + } while (rv < 0 && errno == EINTR); + } + + for (i = 0; i < nfd; i++) { + osfd = _st_kq_data->evtlist[i].ident; + _ST_KQ_REVENTS(osfd) = 0; + } + + } else if (nfd < 0) { + if (errno == EBADF && _st_kq_data->pid != getpid()) { + /* We probably forked, reinitialize kqueue */ + if ((_st_kq_data->kq = kqueue()) < 0) { + /* There is nothing we can do here, will retry later */ + return; + } + fcntl(_st_kq_data->kq, F_SETFD, FD_CLOEXEC); + _st_kq_data->pid = getpid(); + /* Re-register all descriptors on ioq with new kqueue */ + memset(_st_kq_data->fd_data, 0, + _st_kq_data->fd_data_size * sizeof(_kq_fd_data_t)); + for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) { + pq = _ST_POLLQUEUE_PTR(q); + _st_kq_pollset_add(pq->pds, pq->npds); + } + goto retry_kevent; + } + } +} + +ST_HIDDEN int _st_kq_fd_new(int osfd) +{ + if (osfd >= _st_kq_data->fd_data_size && _st_kq_fd_data_expand(osfd) < 0) + return -1; + + return 0; +} + +ST_HIDDEN int _st_kq_fd_close(int osfd) +{ + if (_ST_KQ_READ_CNT(osfd) || _ST_KQ_WRITE_CNT(osfd)) { + errno = EBUSY; + return -1; + } + + return 0; +} + +ST_HIDDEN int _st_kq_fd_getlimit(void) +{ + /* zero means no specific limit */ + return 0; +} + +static _st_eventsys_t _st_kq_eventsys = { + "kqueue", + ST_EVENTSYS_ALT, + _st_kq_init, + _st_kq_dispatch, + _st_kq_pollset_add, + _st_kq_pollset_del, + _st_kq_fd_new, + _st_kq_fd_close, + _st_kq_fd_getlimit +}; +#endif /* MD_HAVE_KQUEUE */ + + +#ifdef MD_HAVE_EPOLL +/***************************************** + * epoll event system + */ + +ST_HIDDEN int _st_epoll_init(void) +{ + int fdlim; + int err = 0; + int rv = 0; + + _st_epoll_data = + (struct _st_epolldata *) calloc(1, sizeof(*_st_epoll_data)); + if (!_st_epoll_data) + return -1; + + fdlim = st_getfdlimit(); + _st_epoll_data->fd_hint = (fdlim > 0 && fdlim < ST_EPOLL_EVTLIST_SIZE) ? + fdlim : ST_EPOLL_EVTLIST_SIZE; + + if ((_st_epoll_data->epfd = epoll_create(_st_epoll_data->fd_hint)) < 0) { + err = errno; + rv = -1; + goto cleanup_epoll; + } + fcntl(_st_epoll_data->epfd, F_SETFD, FD_CLOEXEC); + _st_epoll_data->pid = getpid(); + + /* Allocate file descriptor data array */ + _st_epoll_data->fd_data_size = _st_epoll_data->fd_hint; + _st_epoll_data->fd_data = + (_epoll_fd_data_t *)calloc(_st_epoll_data->fd_data_size, + sizeof(_epoll_fd_data_t)); + if (!_st_epoll_data->fd_data) { + err = errno; + rv = -1; + goto cleanup_epoll; + } + + /* Allocate event lists */ + _st_epoll_data->evtlist_size = _st_epoll_data->fd_hint; + _st_epoll_data->evtlist = + (struct epoll_event *)malloc(_st_epoll_data->evtlist_size * + sizeof(struct epoll_event)); + if (!_st_epoll_data->evtlist) { + err = errno; + rv = -1; + } + + cleanup_epoll: + if (rv < 0) { + if (_st_epoll_data->epfd >= 0) + close(_st_epoll_data->epfd); + free(_st_epoll_data->fd_data); + free(_st_epoll_data->evtlist); + free(_st_epoll_data); + _st_epoll_data = NULL; + errno = err; + } + + return rv; +} + +ST_HIDDEN int _st_epoll_fd_data_expand(int maxfd) +{ + _epoll_fd_data_t *ptr; + int n = _st_epoll_data->fd_data_size; + + while (maxfd >= n) + n <<= 1; + + ptr = (_epoll_fd_data_t *)realloc(_st_epoll_data->fd_data, + n * sizeof(_epoll_fd_data_t)); + if (!ptr) + return -1; + + memset(ptr + _st_epoll_data->fd_data_size, 0, + (n - _st_epoll_data->fd_data_size) * sizeof(_epoll_fd_data_t)); + + _st_epoll_data->fd_data = ptr; + _st_epoll_data->fd_data_size = n; + + return 0; +} + +ST_HIDDEN void _st_epoll_evtlist_expand(void) +{ + struct epoll_event *ptr; + int n = _st_epoll_data->evtlist_size; + + while (_st_epoll_data->evtlist_cnt > n) + n <<= 1; + + ptr = (struct epoll_event *)realloc(_st_epoll_data->evtlist, + n * sizeof(struct epoll_event)); + if (ptr) { + _st_epoll_data->evtlist = ptr; + _st_epoll_data->evtlist_size = n; + } +} + +ST_HIDDEN void _st_epoll_pollset_del(struct pollfd *pds, int npds) +{ + struct epoll_event ev; + struct pollfd *pd; + struct pollfd *epd = pds + npds; + int old_events, events, op; + + /* + * It's more or less OK if deleting fails because a descriptor + * will either be closed or deleted in dispatch function after + * it fires. + */ + for (pd = pds; pd < epd; pd++) { + old_events = _ST_EPOLL_EVENTS(pd->fd); + + if (pd->events & POLLIN) + _ST_EPOLL_READ_CNT(pd->fd)--; + if (pd->events & POLLOUT) + _ST_EPOLL_WRITE_CNT(pd->fd)--; + if (pd->events & POLLPRI) + _ST_EPOLL_EXCEP_CNT(pd->fd)--; + + events = _ST_EPOLL_EVENTS(pd->fd); + /* + * The _ST_EPOLL_REVENTS check below is needed so we can use + * this function inside dispatch(). Outside of dispatch() + * _ST_EPOLL_REVENTS is always zero for all descriptors. + */ + if (events != old_events && _ST_EPOLL_REVENTS(pd->fd) == 0) { + op = events ? EPOLL_CTL_MOD : EPOLL_CTL_DEL; + ev.events = events; + ev.data.fd = pd->fd; + if (epoll_ctl(_st_epoll_data->epfd, op, pd->fd, &ev) == 0 && + op == EPOLL_CTL_DEL) { + _st_epoll_data->evtlist_cnt--; + } + } + } +} + +ST_HIDDEN int _st_epoll_pollset_add(struct pollfd *pds, int npds) +{ + struct epoll_event ev; + int i, fd; + int old_events, events, op; + + /* Do as many checks as possible up front */ + for (i = 0; i < npds; i++) { + fd = pds[i].fd; + if (fd < 0 || !pds[i].events || + (pds[i].events & ~(POLLIN | POLLOUT | POLLPRI))) { + errno = EINVAL; + return -1; + } + if (fd >= _st_epoll_data->fd_data_size && + _st_epoll_fd_data_expand(fd) < 0) + return -1; + } + + for (i = 0; i < npds; i++) { + fd = pds[i].fd; + old_events = _ST_EPOLL_EVENTS(fd); + + if (pds[i].events & POLLIN) + _ST_EPOLL_READ_CNT(fd)++; + if (pds[i].events & POLLOUT) + _ST_EPOLL_WRITE_CNT(fd)++; + if (pds[i].events & POLLPRI) + _ST_EPOLL_EXCEP_CNT(fd)++; + + events = _ST_EPOLL_EVENTS(fd); + if (events != old_events) { + op = old_events ? EPOLL_CTL_MOD : EPOLL_CTL_ADD; + ev.events = events; + ev.data.fd = fd; + if (epoll_ctl(_st_epoll_data->epfd, op, fd, &ev) < 0 && + (op != EPOLL_CTL_ADD || errno != EEXIST)) + break; + if (op == EPOLL_CTL_ADD) { + _st_epoll_data->evtlist_cnt++; + if (_st_epoll_data->evtlist_cnt > _st_epoll_data->evtlist_size) + _st_epoll_evtlist_expand(); + } + } + } + + if (i < npds) { + /* Error */ + int err = errno; + /* Unroll the state */ + _st_epoll_pollset_del(pds, i + 1); + errno = err; + return -1; + } + + return 0; +} + +ST_HIDDEN void _st_epoll_dispatch(void) +{ + st_utime_t min_timeout; + _st_clist_t *q; + _st_pollq_t *pq; + struct pollfd *pds, *epds; + struct epoll_event ev; + int timeout, nfd, i, osfd, notify; + int events, op; + short revents; + + if (_ST_SLEEPQ == NULL) { + timeout = -1; + } else { + min_timeout = (_ST_SLEEPQ->due <= _ST_LAST_CLOCK) ? 0 : + (_ST_SLEEPQ->due - _ST_LAST_CLOCK); + timeout = (int) (min_timeout / 1000); + } + + if (_st_epoll_data->pid != getpid()) { + /* We probably forked, reinitialize epoll set */ + close(_st_epoll_data->epfd); + _st_epoll_data->epfd = epoll_create(_st_epoll_data->fd_hint); + if (_st_epoll_data->epfd < 0) { + /* There is nothing we can do here, will retry later */ + return; + } + fcntl(_st_epoll_data->epfd, F_SETFD, FD_CLOEXEC); + _st_epoll_data->pid = getpid(); + + /* Put all descriptors on ioq into new epoll set */ + memset(_st_epoll_data->fd_data, 0, + _st_epoll_data->fd_data_size * sizeof(_epoll_fd_data_t)); + _st_epoll_data->evtlist_cnt = 0; + for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) { + pq = _ST_POLLQUEUE_PTR(q); + _st_epoll_pollset_add(pq->pds, pq->npds); + } + } + + /* Check for I/O operations */ + nfd = epoll_wait(_st_epoll_data->epfd, _st_epoll_data->evtlist, + _st_epoll_data->evtlist_size, timeout); + + if (nfd > 0) { + for (i = 0; i < nfd; i++) { + osfd = _st_epoll_data->evtlist[i].data.fd; + _ST_EPOLL_REVENTS(osfd) = _st_epoll_data->evtlist[i].events; + if (_ST_EPOLL_REVENTS(osfd) & (EPOLLERR | EPOLLHUP)) { + /* Also set I/O bits on error */ + _ST_EPOLL_REVENTS(osfd) |= _ST_EPOLL_EVENTS(osfd); + } + } + + for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) { + pq = _ST_POLLQUEUE_PTR(q); + notify = 0; + epds = pq->pds + pq->npds; + + for (pds = pq->pds; pds < epds; pds++) { + if (_ST_EPOLL_REVENTS(pds->fd) == 0) { + pds->revents = 0; + continue; + } + osfd = pds->fd; + events = pds->events; + revents = 0; + if ((events & POLLIN) && (_ST_EPOLL_REVENTS(osfd) & EPOLLIN)) + revents |= POLLIN; + if ((events & POLLOUT) && (_ST_EPOLL_REVENTS(osfd) & EPOLLOUT)) + revents |= POLLOUT; + if ((events & POLLPRI) && (_ST_EPOLL_REVENTS(osfd) & EPOLLPRI)) + revents |= POLLPRI; + if (_ST_EPOLL_REVENTS(osfd) & EPOLLERR) + revents |= POLLERR; + if (_ST_EPOLL_REVENTS(osfd) & EPOLLHUP) + revents |= POLLHUP; + + pds->revents = revents; + if (revents) { + notify = 1; + } + } + if (notify) { + ST_REMOVE_LINK(&pq->links); + pq->on_ioq = 0; + /* + * Here we will only delete/modify descriptors that + * didn't fire (see comments in _st_epoll_pollset_del()). + */ + _st_epoll_pollset_del(pq->pds, pq->npds); + + if (pq->thread->flags & _ST_FL_ON_SLEEPQ) + _ST_DEL_SLEEPQ(pq->thread); + pq->thread->state = _ST_ST_RUNNABLE; + _ST_ADD_RUNQ(pq->thread); + } + } + + for (i = 0; i < nfd; i++) { + /* Delete/modify descriptors that fired */ + osfd = _st_epoll_data->evtlist[i].data.fd; + _ST_EPOLL_REVENTS(osfd) = 0; + events = _ST_EPOLL_EVENTS(osfd); + op = events ? EPOLL_CTL_MOD : EPOLL_CTL_DEL; + ev.events = events; + ev.data.fd = osfd; + if (epoll_ctl(_st_epoll_data->epfd, op, osfd, &ev) == 0 && + op == EPOLL_CTL_DEL) { + _st_epoll_data->evtlist_cnt--; + } + } + } +} + +ST_HIDDEN int _st_epoll_fd_new(int osfd) +{ + if (osfd >= _st_epoll_data->fd_data_size && + _st_epoll_fd_data_expand(osfd) < 0) + return -1; + + return 0; +} + +ST_HIDDEN int _st_epoll_fd_close(int osfd) +{ + if (_ST_EPOLL_READ_CNT(osfd) || _ST_EPOLL_WRITE_CNT(osfd) || + _ST_EPOLL_EXCEP_CNT(osfd)) { + errno = EBUSY; + return -1; + } + + return 0; +} + +ST_HIDDEN int _st_epoll_fd_getlimit(void) +{ + /* zero means no specific limit */ + return 0; +} + +/* + * Check if epoll functions are just stubs. + */ +ST_HIDDEN int _st_epoll_is_supported(void) +{ + struct epoll_event ev; + + ev.events = EPOLLIN; + ev.data.ptr = NULL; + /* Guaranteed to fail */ + epoll_ctl(-1, EPOLL_CTL_ADD, -1, &ev); + + return (errno != ENOSYS); +} + +static _st_eventsys_t _st_epoll_eventsys = { + "epoll", + ST_EVENTSYS_ALT, + _st_epoll_init, + _st_epoll_dispatch, + _st_epoll_pollset_add, + _st_epoll_pollset_del, + _st_epoll_fd_new, + _st_epoll_fd_close, + _st_epoll_fd_getlimit +}; +#endif /* MD_HAVE_EPOLL */ + + +/***************************************** + * Public functions + */ + +int st_set_eventsys(int eventsys) +{ + if (_st_eventsys) { + errno = EBUSY; + return -1; + } + + switch (eventsys) { + case ST_EVENTSYS_DEFAULT: +#ifdef USE_POLL + _st_eventsys = &_st_poll_eventsys; +#else + _st_eventsys = &_st_select_eventsys; +#endif + break; + case ST_EVENTSYS_SELECT: + _st_eventsys = &_st_select_eventsys; + break; +#ifdef MD_HAVE_POLL + case ST_EVENTSYS_POLL: + _st_eventsys = &_st_poll_eventsys; + break; +#endif + case ST_EVENTSYS_ALT: +#if defined (MD_HAVE_KQUEUE) + _st_eventsys = &_st_kq_eventsys; +#elif defined (MD_HAVE_EPOLL) + if (_st_epoll_is_supported()) + _st_eventsys = &_st_epoll_eventsys; +#endif + break; + default: + errno = EINVAL; + return -1; + } + + return 0; +} + +int st_get_eventsys(void) +{ + return _st_eventsys ? _st_eventsys->val : -1; +} + +const char *st_get_eventsys_name(void) +{ + return _st_eventsys ? _st_eventsys->name : ""; +} + diff --git a/trunk/research/st-1.9/examples/Makefile b/trunk/research/st-1.9/examples/Makefile new file mode 100644 index 0000000000..31c0a6e240 --- /dev/null +++ b/trunk/research/st-1.9/examples/Makefile @@ -0,0 +1,115 @@ +# +# Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. +# All Rights Reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of Silicon Graphics, Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +########################## +# Supported OSes: +# +# AIX +# FREEBSD +# HPUX +# HPUX_64 +# IRIX +# IRIX_64 +# LINUX +# LINUX_IA64 +# NETBSD +# OPENBSD +# OSF1 +# SOLARIS +# SOLARIS_64 + +########################## + +CC = cc + +SHELL = /bin/sh +ECHO = /bin/echo + +DEPTH = .. +BUILD = +TARGETDIR = + +DEFINES = +CFLAGS = +OTHER_FLAGS = + +OBJDIR = $(DEPTH)/$(TARGETDIR) +INCDIR = $(DEPTH)/$(TARGETDIR) +LIBST = $(OBJDIR)/libst.a +HEADER = $(INCDIR)/st.h + +LIBRESOLV = +EXTRALIBS = + +ifeq ($(OS),) +EXAMPLES = unknown +else +EXAMPLES = $(OBJDIR)/lookupdns $(OBJDIR)/proxy $(OBJDIR)/server +endif + + +########################## +# Platform section. +# + +ifeq (DARWIN, $(findstring DARWIN, $(OS))) +LIBRESOLV = -lresolv +endif + +ifeq (LINUX, $(findstring LINUX, $(OS))) +LIBRESOLV = -lresolv +endif + +ifeq (SOLARIS, $(findstring SOLARIS, $(OS))) +LIBRESOLV = -lresolv +EXTRALIBS = -lsocket -lnsl +endif + +# +# End of platform section. +########################## + + +all: $(EXAMPLES) + +$(OBJDIR)/lookupdns: lookupdns.c $(OBJDIR)/res.o $(LIBST) $(HEADER) + $(CC) $(CFLAGS) -I$(INCDIR) lookupdns.c $(OBJDIR)/res.o $(LIBST) $(LIBRESOLV) $(EXTRALIBS) -o $@ + +$(OBJDIR)/proxy: proxy.c $(LIBST) $(HEADER) + $(CC) $(CFLAGS) -I$(INCDIR) proxy.c $(LIBST) $(EXTRALIBS) -o $@ + +$(OBJDIR)/server: server.c $(OBJDIR)/error.o $(LIBST) $(HEADER) + $(CC) $(CFLAGS) -I$(INCDIR) server.c $(OBJDIR)/error.o $(LIBST) $(EXTRALIBS) -o $@ + +$(OBJDIR)/%.o: %.c + $(CC) $(CFLAGS) -I$(INCDIR) -c $< -o $@ + +.DEFAULT: + @cd $(DEPTH); $(MAKE) $@ + diff --git a/trunk/research/st-1.9/examples/README b/trunk/research/st-1.9/examples/README new file mode 100644 index 0000000000..646d4f6236 --- /dev/null +++ b/trunk/research/st-1.9/examples/README @@ -0,0 +1,98 @@ +Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. +All Rights Reserved. + + +This directory contains three example programs. + + +--------------------------------------------------------------------------- + +PROGRAM + + lookupdns + +FILES + + lookupdns.c + res.c + +USAGE + + lookupdns [] ... + +DESCRIPTION + + This program performs asynchronous DNS host name resolution and reports + IP address for each specified as a command line argument. + One ST thread is created for each host name. All threads do host name + resolution concurrently. + + +--------------------------------------------------------------------------- + +PROGRAM + + proxy + +FILES + + proxy.c + +USAGE + + proxy -l -r [-p ] [-S] + + -l bind to local address specified as []: + -r connect to remote address specified as : + -p create specified number of processes + -S serialize accept() calls from different processes + on the same listening socket (if needed). + +DESCRIPTION + + This program acts as a generic gateway. It listens for connections to a + local address. Upon accepting a client connection, it connects to the + specified remote address and then just pumps the data through without any + modification. + + +--------------------------------------------------------------------------- + +PROGRAM + + server + +FILES + + server.c + error.c + +USAGE + + server -l [] + + -l open all log files in specified directory. + + Possible options: + + -b : bind to specified address (multiple addresses + are permitted) + -p create specified number of processes + -t : specify thread limits per listening socket + across all processes + -u change server's user id to specified value + -q set max length of pending connections queue + -a enable access logging + -i run in interactive mode (useful for debugging) + -S serialize accept() calls from different processes + on the same listening socket (if needed). + +DESCRIPTION + + This program is a general server example. It accepts a client connection + and outputs a short HTML page. It can be easily adapted to provide + other services. + + +--------------------------------------------------------------------------- + diff --git a/trunk/research/st-1.9/examples/error.c b/trunk/research/st-1.9/examples/error.c new file mode 100644 index 0000000000..0b2e772874 --- /dev/null +++ b/trunk/research/st-1.9/examples/error.c @@ -0,0 +1,168 @@ +/* + * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Silicon Graphics, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include "st.h" + +/* + * Simple error reporting functions. + * Suggested in W. Richard Stevens' "Advanced Programming in UNIX + * Environment". + */ + +#define MAXLINE 4096 /* max line length */ + +static void err_doit(int, int, const char *, va_list); + + +/* + * Nonfatal error related to a system call. + * Print a message and return. + */ +void err_sys_report(int fd, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + err_doit(fd, 1, fmt, ap); + va_end(ap); +} + + +/* + * Fatal error related to a system call. + * Print a message and terminate. + */ +void err_sys_quit(int fd, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + err_doit(fd, 1, fmt, ap); + va_end(ap); + exit(1); +} + + +/* + * Fatal error related to a system call. + * Print a message, dump core, and terminate. + */ +void err_sys_dump(int fd, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + err_doit(fd, 1, fmt, ap); + va_end(ap); + abort(); /* dump core and terminate */ + exit(1); /* shouldn't get here */ +} + + +/* + * Nonfatal error unrelated to a system call. + * Print a message and return. + */ +void err_report(int fd, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + err_doit(fd, 0, fmt, ap); + va_end(ap); +} + + +/* + * Fatal error unrelated to a system call. + * Print a message and terminate. + */ +void err_quit(int fd, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + err_doit(fd, 0, fmt, ap); + va_end(ap); + exit(1); +} + + +/* + * Return a pointer to a string containing current time. + */ +char *err_tstamp(void) +{ + static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + static char str[32]; + static time_t lastt = 0; + struct tm *tmp; + time_t currt = st_time(); + + if (currt == lastt) + return str; + + tmp = localtime(&currt); + sprintf(str, "[%02d/%s/%d:%02d:%02d:%02d] ", tmp->tm_mday, + months[tmp->tm_mon], 1900 + tmp->tm_year, tmp->tm_hour, + tmp->tm_min, tmp->tm_sec); + lastt = currt; + + return str; +} + + +/* + * Print a message and return to caller. + * Caller specifies "errnoflag". + */ +static void err_doit(int fd, int errnoflag, const char *fmt, va_list ap) +{ + int errno_save; + char buf[MAXLINE]; + + errno_save = errno; /* value caller might want printed */ + strcpy(buf, err_tstamp()); /* prepend a message with time stamp */ + vsprintf(buf + strlen(buf), fmt, ap); + if (errnoflag) + sprintf(buf + strlen(buf), ": %s\n", strerror(errno_save)); + else + strcat(buf, "\n"); + write(fd, buf, strlen(buf)); + errno = errno_save; +} + diff --git a/trunk/research/st-1.9/examples/lookupdns.c b/trunk/research/st-1.9/examples/lookupdns.c new file mode 100644 index 0000000000..98f6ec5d82 --- /dev/null +++ b/trunk/research/st-1.9/examples/lookupdns.c @@ -0,0 +1,103 @@ +/* + * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Silicon Graphics, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "st.h" + +#if !defined(NETDB_INTERNAL) && defined(h_NETDB_INTERNAL) +#define NETDB_INTERNAL h_NETDB_INTERNAL +#endif + +/* Resolution timeout (in microseconds) */ +#define TIMEOUT (2*1000000LL) + +/* External function defined in the res.c file */ +int dns_getaddr(const char *host, struct in_addr *addr, st_utime_t timeout); + + +void *do_resolve(void *host) +{ + struct in_addr addr; + + /* Use dns_getaddr() instead of gethostbyname(3) to get IP address */ + if (dns_getaddr(host, &addr, TIMEOUT) < 0) { + fprintf(stderr, "dns_getaddr: can't resolve %s: ", (char *)host); + if (h_errno == NETDB_INTERNAL) + perror(""); + else + herror(""); + } else + printf("%-40s %s\n", (char *)host, inet_ntoa(addr)); + + return NULL; +} + + +/* + * Asynchronous DNS host name resolution. This program creates one + * ST thread for each host name (specified as command line arguments). + * All threads do host name resolution concurrently. + */ +int main(int argc, char *argv[]) +{ + int i; + + if (argc < 2) { + fprintf(stderr, "Usage: %s [] ...\n", argv[0]); + exit(1); + } + + if (st_init() < 0) { + perror("st_init"); + exit(1); + } + + for (i = 1; i < argc; i++) { + /* Create a separate thread for each host name */ + if (st_thread_create(do_resolve, argv[i], 0, 0) == NULL) { + perror("st_thread_create"); + exit(1); + } + } + + st_thread_exit(NULL); + + /* NOTREACHED */ + return 1; +} + diff --git a/trunk/research/st-1.9/examples/proxy.c b/trunk/research/st-1.9/examples/proxy.c new file mode 100644 index 0000000000..2f4636d6b8 --- /dev/null +++ b/trunk/research/st-1.9/examples/proxy.c @@ -0,0 +1,541 @@ +/* + * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Silicon Graphics, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "st.h" + +#define IOBUFSIZE (16*1024) + +#define IOV_LEN 256 +#define IOV_COUNT (IOBUFSIZE / IOV_LEN) + +#ifndef INADDR_NONE +#define INADDR_NONE 0xffffffff +#endif + +static char *prog; /* Program name */ +static struct sockaddr_in rmt_addr; /* Remote address */ + +static unsigned long testing; +#define TESTING_VERBOSE 0x1 +#define TESTING_READV 0x2 +#define TESTING_READ_RESID 0x4 +#define TESTING_WRITEV 0x8 +#define TESTING_WRITE_RESID 0x10 + +static void read_address(const char *str, struct sockaddr_in *sin); +static void start_daemon(void); +static int cpu_count(void); +static void set_concurrency(int nproc); +static void *handle_request(void *arg); +static void print_sys_error(const char *msg); + + +/* + * This program acts as a generic gateway. It listens for connections + * to a local address ('-l' option). Upon accepting a client connection, + * it connects to the specified remote address ('-r' option) and then + * just pumps the data through without any modification. + */ +int main(int argc, char *argv[]) +{ + extern char *optarg; + int opt, sock, n; + int laddr, raddr, num_procs, alt_ev, one_process; + int serialize_accept = 0; + struct sockaddr_in lcl_addr, cli_addr; + st_netfd_t cli_nfd, srv_nfd; + + prog = argv[0]; + num_procs = laddr = raddr = alt_ev = one_process = 0; + + /* Parse arguments */ + while((opt = getopt(argc, argv, "l:r:p:Saht:X")) != EOF) { + switch (opt) { + case 'a': + alt_ev = 1; + break; + case 'l': + read_address(optarg, &lcl_addr); + laddr = 1; + break; + case 'r': + read_address(optarg, &rmt_addr); + if (rmt_addr.sin_addr.s_addr == INADDR_ANY) { + fprintf(stderr, "%s: invalid remote address: %s\n", prog, optarg); + exit(1); + } + raddr = 1; + break; + case 'p': + num_procs = atoi(optarg); + if (num_procs < 1) { + fprintf(stderr, "%s: invalid number of processes: %s\n", prog, optarg); + exit(1); + } + break; + case 'S': + /* + * Serialization decision is tricky on some platforms. For example, + * Solaris 2.6 and above has kernel sockets implementation, so supposedly + * there is no need for serialization. The ST library may be compiled + * on one OS version, but used on another, so the need for serialization + * should be determined at run time by the application. Since it's just + * an example, the serialization decision is left up to user. + * Only on platforms where the serialization is never needed on any OS + * version st_netfd_serialize_accept() is a no-op. + */ + serialize_accept = 1; + break; + case 't': + testing = strtoul(optarg, NULL, 0); + break; + case 'X': + one_process = 1; + break; + case 'h': + case '?': + fprintf(stderr, "Usage: %s [options] -l <[host]:port> -r \n", + prog); + fprintf(stderr, "options are:\n"); + fprintf(stderr, " -p number of parallel processes\n"); + fprintf(stderr, " -S serialize accepts\n"); + fprintf(stderr, " -a use alternate event system\n"); +#ifdef DEBUG + fprintf(stderr, " -t mask testing/debugging mode\n"); + fprintf(stderr, " -X one process, don't daemonize\n"); +#endif + exit(1); + } + } + if (!laddr) { + fprintf(stderr, "%s: local address required\n", prog); + exit(1); + } + if (!raddr) { + fprintf(stderr, "%s: remote address required\n", prog); + exit(1); + } + if (num_procs == 0) + num_procs = cpu_count(); + + fprintf(stderr, "%s: starting proxy daemon on %s:%d\n", prog, + inet_ntoa(lcl_addr.sin_addr), ntohs(lcl_addr.sin_port)); + + /* Start the daemon */ + if (one_process) + num_procs = 1; + else + start_daemon(); + + if (alt_ev) + st_set_eventsys(ST_EVENTSYS_ALT); + + /* Initialize the ST library */ + if (st_init() < 0) { + print_sys_error("st_init"); + exit(1); + } + + /* Create and bind listening socket */ + if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) { + print_sys_error("socket"); + exit(1); + } + n = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&n, sizeof(n)) < 0) { + print_sys_error("setsockopt"); + exit(1); + } + if (bind(sock, (struct sockaddr *)&lcl_addr, sizeof(lcl_addr)) < 0) { + print_sys_error("bind"); + exit(1); + } + listen(sock, 128); + if ((srv_nfd = st_netfd_open_socket(sock)) == NULL) { + print_sys_error("st_netfd_open"); + exit(1); + } + /* See the comment regarding serialization decision above */ + if (num_procs > 1 && serialize_accept && st_netfd_serialize_accept(srv_nfd) + < 0) { + print_sys_error("st_netfd_serialize_accept"); + exit(1); + } + + /* Start server processes */ + if (!one_process) + set_concurrency(num_procs); + + for ( ; ; ) { + n = sizeof(cli_addr); + cli_nfd = st_accept(srv_nfd, (struct sockaddr *)&cli_addr, &n, + ST_UTIME_NO_TIMEOUT); + if (cli_nfd == NULL) { + print_sys_error("st_accept"); + exit(1); + } + if (st_thread_create(handle_request, cli_nfd, 0, 0) == NULL) { + print_sys_error("st_thread_create"); + exit(1); + } + } + + /* NOTREACHED */ + return 1; +} + + +static void read_address(const char *str, struct sockaddr_in *sin) +{ + char host[128], *p; + struct hostent *hp; + unsigned short port; + + strcpy(host, str); + if ((p = strchr(host, ':')) == NULL) { + fprintf(stderr, "%s: invalid address: %s\n", prog, host); + exit(1); + } + *p++ = '\0'; + port = (unsigned short) atoi(p); + if (port < 1) { + fprintf(stderr, "%s: invalid port: %s\n", prog, p); + exit(1); + } + + memset(sin, 0, sizeof(struct sockaddr_in)); + sin->sin_family = AF_INET; + sin->sin_port = htons(port); + if (host[0] == '\0') { + sin->sin_addr.s_addr = INADDR_ANY; + return; + } + sin->sin_addr.s_addr = inet_addr(host); + if (sin->sin_addr.s_addr == INADDR_NONE) { + /* not dotted-decimal */ + if ((hp = gethostbyname(host)) == NULL) { + fprintf(stderr, "%s: can't resolve address: %s\n", prog, host); + exit(1); + } + memcpy(&sin->sin_addr, hp->h_addr, hp->h_length); + } +} + +#ifdef DEBUG +static void show_iov(const struct iovec *iov, int niov) +{ + int i; + size_t total; + + printf("iov %p has %d entries:\n", iov, niov); + total = 0; + for (i = 0; i < niov; i++) { + printf("iov[%3d] iov_base=%p iov_len=0x%lx(%lu)\n", + i, iov[i].iov_base, (unsigned long) iov[i].iov_len, + (unsigned long) iov[i].iov_len); + total += iov[i].iov_len; + } + printf("total 0x%lx(%ld)\n", (unsigned long) total, (unsigned long) total); +} + +/* + * This version is tricked out to test all the + * st_(read|write)v?(_resid)? variants. Use the non-DEBUG version for + * anything serious. st_(read|write) are all this function really + * needs. + */ +static int pass(st_netfd_t in, st_netfd_t out) +{ + char buf[IOBUFSIZE]; + struct iovec iov[IOV_COUNT]; + int ioviter, nw, nr; + + if (testing & TESTING_READV) { + for (ioviter = 0; ioviter < IOV_COUNT; ioviter++) { + iov[ioviter].iov_base = &buf[ioviter * IOV_LEN]; + iov[ioviter].iov_len = IOV_LEN; + } + if (testing & TESTING_VERBOSE) { + printf("readv(%p)...\n", in); + show_iov(iov, IOV_COUNT); + } + if (testing & TESTING_READ_RESID) { + struct iovec *riov = iov; + int riov_cnt = IOV_COUNT; + if (st_readv_resid(in, &riov, &riov_cnt, ST_UTIME_NO_TIMEOUT) == 0) { + if (testing & TESTING_VERBOSE) { + printf("resid\n"); + show_iov(riov, riov_cnt); + printf("full\n"); + show_iov(iov, IOV_COUNT); + } + nr = 0; + for (ioviter = 0; ioviter < IOV_COUNT; ioviter++) + nr += iov[ioviter].iov_len; + nr = IOBUFSIZE - nr; + } else + nr = -1; + } else + nr = (int) st_readv(in, iov, IOV_COUNT, ST_UTIME_NO_TIMEOUT); + } else { + if (testing & TESTING_READ_RESID) { + size_t resid = IOBUFSIZE; + if (st_read_resid(in, buf, &resid, ST_UTIME_NO_TIMEOUT) == 0) + nr = IOBUFSIZE - resid; + else + nr = -1; + } else + nr = (int) st_read(in, buf, IOBUFSIZE, ST_UTIME_NO_TIMEOUT); + } + if (testing & TESTING_VERBOSE) + printf("got 0x%x(%d) E=%d\n", nr, nr, errno); + + if (nr <= 0) + return 0; + + if (testing & TESTING_WRITEV) { + for (nw = 0, ioviter = 0; nw < nr; + nw += iov[ioviter].iov_len, ioviter++) { + iov[ioviter].iov_base = &buf[nw]; + iov[ioviter].iov_len = nr - nw; + if (iov[ioviter].iov_len > IOV_LEN) + iov[ioviter].iov_len = IOV_LEN; + } + if (testing & TESTING_VERBOSE) { + printf("writev(%p)...\n", out); + show_iov(iov, ioviter); + } + if (testing & TESTING_WRITE_RESID) { + struct iovec *riov = iov; + int riov_cnt = ioviter; + if (st_writev_resid(out, &riov, &riov_cnt, ST_UTIME_NO_TIMEOUT) == 0) { + if (testing & TESTING_VERBOSE) { + printf("resid\n"); + show_iov(riov, riov_cnt); + printf("full\n"); + show_iov(iov, ioviter); + } + nw = 0; + while (--ioviter >= 0) + nw += iov[ioviter].iov_len; + nw = nr - nw; + } else + nw = -1; + } else + nw = st_writev(out, iov, ioviter, ST_UTIME_NO_TIMEOUT); + } else { + if (testing & TESTING_WRITE_RESID) { + size_t resid = nr; + if (st_write_resid(out, buf, &resid, ST_UTIME_NO_TIMEOUT) == 0) + nw = nr - resid; + else + nw = -1; + } else + nw = st_write(out, buf, nr, ST_UTIME_NO_TIMEOUT); + } + if (testing & TESTING_VERBOSE) + printf("put 0x%x(%d) E=%d\n", nw, nw, errno); + + if (nw != nr) + return 0; + + return 1; +} +#else /* DEBUG */ +/* + * This version is the simple one suitable for serious use. + */ +static int pass(st_netfd_t in, st_netfd_t out) +{ + char buf[IOBUFSIZE]; + int nw, nr; + + nr = (int) st_read(in, buf, IOBUFSIZE, ST_UTIME_NO_TIMEOUT); + if (nr <= 0) + return 0; + + nw = st_write(out, buf, nr, ST_UTIME_NO_TIMEOUT); + if (nw != nr) + return 0; + + return 1; +} +#endif + +static void *handle_request(void *arg) +{ + struct pollfd pds[2]; + st_netfd_t cli_nfd, rmt_nfd; + int sock; + + cli_nfd = (st_netfd_t) arg; + pds[0].fd = st_netfd_fileno(cli_nfd); + pds[0].events = POLLIN; + + /* Connect to remote host */ + if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) { + print_sys_error("socket"); + goto done; + } + if ((rmt_nfd = st_netfd_open_socket(sock)) == NULL) { + print_sys_error("st_netfd_open_socket"); + close(sock); + goto done; + } + if (st_connect(rmt_nfd, (struct sockaddr *)&rmt_addr, + sizeof(rmt_addr), ST_UTIME_NO_TIMEOUT) < 0) { + print_sys_error("st_connect"); + st_netfd_close(rmt_nfd); + goto done; + } + pds[1].fd = sock; + pds[1].events = POLLIN; + + /* + * Now just pump the data through. + * XXX This should use one thread for each direction for true full-duplex. + */ + for ( ; ; ) { + pds[0].revents = 0; + pds[1].revents = 0; + + if (st_poll(pds, 2, ST_UTIME_NO_TIMEOUT) <= 0) { + print_sys_error("st_poll"); + break; + } + + if (pds[0].revents & POLLIN) { + if (!pass(cli_nfd, rmt_nfd)) + break; + } + + if (pds[1].revents & POLLIN) { + if (!pass(rmt_nfd, cli_nfd)) + break; + } + } + st_netfd_close(rmt_nfd); + +done: + + st_netfd_close(cli_nfd); + + return NULL; +} + +static void start_daemon(void) +{ + pid_t pid; + + /* Start forking */ + if ((pid = fork()) < 0) { + print_sys_error("fork"); + exit(1); + } + if (pid > 0) + exit(0); /* parent */ + + /* First child process */ + setsid(); /* become session leader */ + + if ((pid = fork()) < 0) { + print_sys_error("fork"); + exit(1); + } + if (pid > 0) /* first child */ + exit(0); + + chdir("/"); + umask(022); +} + +/* + * Create separate processes ("virtual processors"). Since it's just an + * example, there is no watchdog - the parent just exits leaving children + * on their own. + */ +static void set_concurrency(int nproc) +{ + pid_t pid; + int i; + + if (nproc < 1) + nproc = 1; + + for (i = 0; i < nproc; i++) { + if ((pid = fork()) < 0) { + print_sys_error("fork"); + exit(1); + } + /* Child returns */ + if (pid == 0) + return; + } + + /* Parent just exits */ + exit(0); +} + +static int cpu_count(void) +{ + int n; + +#if defined (_SC_NPROCESSORS_ONLN) + n = (int) sysconf(_SC_NPROCESSORS_ONLN); +#elif defined (_SC_NPROC_ONLN) + n = (int) sysconf(_SC_NPROC_ONLN); +#elif defined (HPUX) +#include + n = mpctl(MPC_GETNUMSPUS, 0, 0); +#else + n = -1; + errno = ENOSYS; +#endif + + return n; +} + +static void print_sys_error(const char *msg) +{ + fprintf(stderr, "%s: %s: %s\n", prog, msg, strerror(errno)); +} + diff --git a/trunk/research/st-1.9/examples/res.c b/trunk/research/st-1.9/examples/res.c new file mode 100644 index 0000000000..14ecd8c927 --- /dev/null +++ b/trunk/research/st-1.9/examples/res.c @@ -0,0 +1,305 @@ +/* + * Copyright (c) 1985, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Silicon Graphics, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if defined (DARWIN) +#define BIND_8_COMPAT +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "st.h" + +#define MAXPACKET 1024 + +#if !defined(NETDB_INTERNAL) && defined(h_NETDB_INTERNAL) +#define NETDB_INTERNAL h_NETDB_INTERNAL +#endif + +/* New in Solaris 7 */ +#if !defined(_getshort) && defined(ns_get16) +#define _getshort(cp) ns_get16(cp) +#endif + +typedef union { + HEADER hdr; + u_char buf[MAXPACKET]; +} querybuf_t; + + +static int parse_answer(querybuf_t *ans, int len, struct in_addr *addr) +{ + char buf[MAXPACKET]; + HEADER *ahp; + u_char *cp, *eoa; + int type, n; + + ahp = &ans->hdr; + eoa = ans->buf + len; + cp = ans->buf + sizeof(HEADER); + + while (ahp->qdcount > 0) { + ahp->qdcount--; + cp += dn_skipname(cp, eoa) + QFIXEDSZ; + } + while (ahp->ancount > 0 && cp < eoa) { + ahp->ancount--; + if ((n = dn_expand(ans->buf, eoa, cp, buf, sizeof(buf))) < 0) + break; + cp += n; + type = _getshort(cp); + cp += 8; + n = _getshort(cp); + cp += 2; + if (type == T_CNAME) { + cp += n; + continue; + } + memcpy(addr, cp, n); + return 0; + } + + h_errno = TRY_AGAIN; + return -1; +} + + +static int query_domain(st_netfd_t nfd, const char *name, struct in_addr *addr, + st_utime_t timeout) +{ + querybuf_t qbuf; + u_char *buf = qbuf.buf; + HEADER *hp = &qbuf.hdr; + int blen = sizeof(qbuf); + int i, len, id; + + for (i = 0; i < _res.nscount; i++) { + len = res_mkquery(QUERY, name, C_IN, T_A, NULL, 0, NULL, buf, blen); + if (len <= 0) { + h_errno = NO_RECOVERY; + return -1; + } + id = hp->id; + + if (st_sendto(nfd, buf, len, (struct sockaddr *)&(_res.nsaddr_list[i]), + sizeof(struct sockaddr), timeout) != len) { + h_errno = NETDB_INTERNAL; + /* EINTR means interrupt by other thread, NOT by a caught signal */ + if (errno == EINTR) + return -1; + continue; + } + + /* Wait for reply */ + do { + len = st_recvfrom(nfd, buf, blen, NULL, NULL, timeout); + if (len <= 0) + break; + } while (id != hp->id); + + if (len < HFIXEDSZ) { + h_errno = NETDB_INTERNAL; + if (len >= 0) + errno = EMSGSIZE; + else if (errno == EINTR) /* see the comment above */ + return -1; + continue; + } + + hp->ancount = ntohs(hp->ancount); + hp->qdcount = ntohs(hp->qdcount); + if ((hp->rcode != NOERROR) || (hp->ancount == 0)) { + switch (hp->rcode) { + case NXDOMAIN: + h_errno = HOST_NOT_FOUND; + break; + case SERVFAIL: + h_errno = TRY_AGAIN; + break; + case NOERROR: + h_errno = NO_DATA; + break; + case FORMERR: + case NOTIMP: + case REFUSED: + default: + h_errno = NO_RECOVERY; + } + continue; + } + + if (parse_answer(&qbuf, len, addr) == 0) + return 0; + } + + return -1; +} + + +#define CLOSE_AND_RETURN(ret) \ + { \ + n = errno; \ + st_netfd_close(nfd); \ + errno = n; \ + return (ret); \ + } + + +int dns_getaddr(const char *host, struct in_addr *addr, st_utime_t timeout) +{ + char name[MAXDNAME], **domain; + const char *cp; + int s, n, maxlen, dots; + int trailing_dot, tried_as_is; + st_netfd_t nfd; + + if ((_res.options & RES_INIT) == 0 && res_init() == -1) { + h_errno = NETDB_INTERNAL; + return -1; + } + if (_res.options & RES_USEVC) { + h_errno = NETDB_INTERNAL; + errno = ENOSYS; + return -1; + } + if (!host || *host == '\0') { + h_errno = HOST_NOT_FOUND; + return -1; + } + + /* Create UDP socket */ + if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { + h_errno = NETDB_INTERNAL; + return -1; + } + if ((nfd = st_netfd_open_socket(s)) == NULL) { + h_errno = NETDB_INTERNAL; + n = errno; + close(s); + errno = n; + return -1; + } + + maxlen = sizeof(name) - 1; + n = 0; + dots = 0; + trailing_dot = 0; + tried_as_is = 0; + + for (cp = host; *cp && n < maxlen; cp++) { + dots += (*cp == '.'); + name[n++] = *cp; + } + if (name[n - 1] == '.') + trailing_dot = 1; + + /* + * If there are dots in the name already, let's just give it a try + * 'as is'. The threshold can be set with the "ndots" option. + */ + if (dots >= _res.ndots) { + if (query_domain(nfd, host, addr, timeout) == 0) + CLOSE_AND_RETURN(0); + if (h_errno == NETDB_INTERNAL && errno == EINTR) + CLOSE_AND_RETURN(-1); + tried_as_is = 1; + } + + /* + * We do at least one level of search if + * - there is no dot and RES_DEFNAME is set, or + * - there is at least one dot, there is no trailing dot, + * and RES_DNSRCH is set. + */ + if ((!dots && (_res.options & RES_DEFNAMES)) || + (dots && !trailing_dot && (_res.options & RES_DNSRCH))) { + name[n++] = '.'; + for (domain = _res.dnsrch; *domain; domain++) { + strncpy(name + n, *domain, maxlen - n); + if (query_domain(nfd, name, addr, timeout) == 0) + CLOSE_AND_RETURN(0); + if (h_errno == NETDB_INTERNAL && errno == EINTR) + CLOSE_AND_RETURN(-1); + if (!(_res.options & RES_DNSRCH)) + break; + } + } + + /* + * If we have not already tried the name "as is", do that now. + * note that we do this regardless of how many dots were in the + * name or whether it ends with a dot. + */ + if (!tried_as_is) { + if (query_domain(nfd, host, addr, timeout) == 0) + CLOSE_AND_RETURN(0); + } + + CLOSE_AND_RETURN(-1); +} + diff --git a/trunk/research/st-1.9/examples/server.c b/trunk/research/st-1.9/examples/server.c new file mode 100644 index 0000000000..5d5aa6d726 --- /dev/null +++ b/trunk/research/st-1.9/examples/server.c @@ -0,0 +1,1025 @@ +/* + * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Silicon Graphics, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "st.h" + + +/****************************************************************** + * Server configuration parameters + */ + +/* Log files */ +#define PID_FILE "pid" +#define ERRORS_FILE "errors" +#define ACCESS_FILE "access" + +/* Default server port */ +#define SERV_PORT_DEFAULT 8000 + +/* Socket listen queue size */ +#define LISTENQ_SIZE_DEFAULT 256 + +/* Max number of listening sockets ("hardware virtual servers") */ +#define MAX_BIND_ADDRS 16 + +/* Max number of "spare" threads per process per socket */ +#define MAX_WAIT_THREADS_DEFAULT 8 + +/* Number of file descriptors needed to handle one client session */ +#define FD_PER_THREAD 2 + +/* Access log buffer flushing interval (in seconds) */ +#define ACCLOG_FLUSH_INTERVAL 30 + +/* Request read timeout (in seconds) */ +#define REQUEST_TIMEOUT 30 + + +/****************************************************************** + * Global data + */ + +struct socket_info { + st_netfd_t nfd; /* Listening socket */ + char *addr; /* Bind address */ + unsigned int port; /* Port */ + int wait_threads; /* Number of threads waiting to accept */ + int busy_threads; /* Number of threads processing request */ + int rqst_count; /* Total number of processed requests */ +} srv_socket[MAX_BIND_ADDRS]; /* Array of listening sockets */ + +static int sk_count = 0; /* Number of listening sockets */ + +static int vp_count = 0; /* Number of server processes (VPs) */ +static pid_t *vp_pids; /* Array of VP pids */ + +static int my_index = -1; /* Current process index */ +static pid_t my_pid = -1; /* Current process pid */ + +static st_netfd_t sig_pipe[2]; /* Signal pipe */ + +/* + * Configuration flags/parameters + */ +static int interactive_mode = 0; +static int serialize_accept = 0; +static int log_access = 0; +static char *logdir = NULL; +static char *username = NULL; +static int listenq_size = LISTENQ_SIZE_DEFAULT; +static int errfd = STDERR_FILENO; + +/* + * Thread throttling parameters (all numbers are per listening socket). + * Zero values mean use default. + */ +static int max_threads = 0; /* Max number of threads */ +static int max_wait_threads = 0; /* Max number of "spare" threads */ +static int min_wait_threads = 2; /* Min number of "spare" threads */ + + +/****************************************************************** + * Useful macros + */ + +#ifndef INADDR_NONE +#define INADDR_NONE 0xffffffff +#endif + +#define SEC2USEC(s) ((s)*1000000LL) + +#define WAIT_THREADS(i) (srv_socket[i].wait_threads) +#define BUSY_THREADS(i) (srv_socket[i].busy_threads) +#define TOTAL_THREADS(i) (WAIT_THREADS(i) + BUSY_THREADS(i)) +#define RQST_COUNT(i) (srv_socket[i].rqst_count) + + +/****************************************************************** + * Forward declarations + */ + +static void usage(const char *progname); +static void parse_arguments(int argc, char *argv[]); +static void start_daemon(void); +static void set_thread_throttling(void); +static void create_listeners(void); +static void change_user(void); +static void open_log_files(void); +static void start_processes(void); +static void wdog_sighandler(int signo); +static void child_sighandler(int signo); +static void install_sighandlers(void); +static void start_threads(void); +static void *process_signals(void *arg); +static void *flush_acclog_buffer(void *arg); +static void *handle_connections(void *arg); +static void dump_server_info(void); + +static void Signal(int sig, void (*handler)(int)); +static int cpu_count(void); + +extern void handle_session(long srv_socket_index, st_netfd_t cli_nfd); +extern void load_configs(void); +extern void logbuf_open(void); +extern void logbuf_flush(void); +extern void logbuf_close(void); + +/* Error reporting functions defined in the error.c file */ +extern void err_sys_report(int fd, const char *fmt, ...); +extern void err_sys_quit(int fd, const char *fmt, ...); +extern void err_sys_dump(int fd, const char *fmt, ...); +extern void err_report(int fd, const char *fmt, ...); +extern void err_quit(int fd, const char *fmt, ...); + + +/* + * General server example: accept a client connection and do something. + * This program just outputs a short HTML page, but can be easily adapted + * to do other things. + * + * This server creates a constant number of processes ("virtual processors" + * or VPs) and replaces them when they die. Each virtual processor manages + * its own independent set of state threads (STs), the number of which varies + * with load against the server. Each state thread listens to exactly one + * listening socket. The initial process becomes the watchdog, waiting for + * children (VPs) to die or for a signal requesting termination or restart. + * Upon receiving a restart signal (SIGHUP), all VPs close and then reopen + * log files and reload configuration. All currently active connections remain + * active. It is assumed that new configuration affects only request + * processing and not the general server parameters such as number of VPs, + * thread limits, bind addresses, etc. Those are specified as command line + * arguments, so the server has to be stopped and then started again in order + * to change them. + * + * Each state thread loops processing connections from a single listening + * socket. Only one ST runs on a VP at a time, and VPs do not share memory, + * so no mutual exclusion locking is necessary on any data, and the entire + * server is free to use all the static variables and non-reentrant library + * functions it wants, greatly simplifying programming and debugging and + * increasing performance (for example, it is safe to ++ and -- all global + * counters or call inet_ntoa(3) without any mutexes). The current thread on + * each VP maintains equilibrium on that VP, starting a new thread or + * terminating itself if the number of spare threads exceeds the lower or + * upper limit. + * + * All I/O operations on sockets must use the State Thread library's I/O + * functions because only those functions prevent blocking of the entire VP + * process and perform state thread scheduling. + */ +int main(int argc, char *argv[]) +{ + /* Parse command-line options */ + parse_arguments(argc, argv); + + /* Allocate array of server pids */ + if ((vp_pids = calloc(vp_count, sizeof(pid_t))) == NULL) + err_sys_quit(errfd, "ERROR: calloc failed"); + + /* Start the daemon */ + if (!interactive_mode) + start_daemon(); + + /* Initialize the ST library */ + if (st_init() < 0) + err_sys_quit(errfd, "ERROR: initialization failed: st_init"); + + /* Set thread throttling parameters */ + set_thread_throttling(); + + /* Create listening sockets */ + create_listeners(); + + /* Change the user */ + if (username) + change_user(); + + /* Open log files */ + open_log_files(); + + /* Start server processes (VPs) */ + start_processes(); + + /* Turn time caching on */ + st_timecache_set(1); + + /* Install signal handlers */ + install_sighandlers(); + + /* Load configuration from config files */ + load_configs(); + + /* Start all threads */ + start_threads(); + + /* Become a signal processing thread */ + process_signals(NULL); + + /* NOTREACHED */ + return 1; +} + + +/******************************************************************/ + +static void usage(const char *progname) +{ + fprintf(stderr, "Usage: %s -l []\n\n" + "Possible options:\n\n" + "\t-b : Bind to specified address. Multiple" + " addresses\n" + "\t are permitted.\n" + "\t-p Create specified number of processes.\n" + "\t-t : Specify thread limits per listening" + " socket\n" + "\t across all processes.\n" + "\t-u Change server's user id to specified" + " value.\n" + "\t-q Set max length of pending connections" + " queue.\n" + "\t-a Enable access logging.\n" + "\t-i Run in interactive mode.\n" + "\t-S Serialize all accept() calls.\n" + "\t-h Print this message.\n", + progname); + exit(1); +} + + +/******************************************************************/ + +static void parse_arguments(int argc, char *argv[]) +{ + extern char *optarg; + int opt; + char *c; + + while ((opt = getopt(argc, argv, "b:p:l:t:u:q:aiSh")) != EOF) { + switch (opt) { + case 'b': + if (sk_count >= MAX_BIND_ADDRS) + err_quit(errfd, "ERROR: max number of bind addresses (%d) exceeded", + MAX_BIND_ADDRS); + if ((c = strdup(optarg)) == NULL) + err_sys_quit(errfd, "ERROR: strdup"); + srv_socket[sk_count++].addr = c; + break; + case 'p': + vp_count = atoi(optarg); + if (vp_count < 1) + err_quit(errfd, "ERROR: invalid number of processes: %s", optarg); + break; + case 'l': + logdir = optarg; + break; + case 't': + max_wait_threads = (int) strtol(optarg, &c, 10); + if (*c++ == ':') + max_threads = atoi(c); + if (max_wait_threads < 0 || max_threads < 0) + err_quit(errfd, "ERROR: invalid number of threads: %s", optarg); + break; + case 'u': + username = optarg; + break; + case 'q': + listenq_size = atoi(optarg); + if (listenq_size < 1) + err_quit(errfd, "ERROR: invalid listen queue size: %s", optarg); + break; + case 'a': + log_access = 1; + break; + case 'i': + interactive_mode = 1; + break; + case 'S': + /* + * Serialization decision is tricky on some platforms. For example, + * Solaris 2.6 and above has kernel sockets implementation, so supposedly + * there is no need for serialization. The ST library may be compiled + * on one OS version, but used on another, so the need for serialization + * should be determined at run time by the application. Since it's just + * an example, the serialization decision is left up to user. + * Only on platforms where the serialization is never needed on any OS + * version st_netfd_serialize_accept() is a no-op. + */ + serialize_accept = 1; + break; + case 'h': + case '?': + usage(argv[0]); + } + } + + if (logdir == NULL && !interactive_mode) { + err_report(errfd, "ERROR: logging directory is required\n"); + usage(argv[0]); + } + + if (getuid() == 0 && username == NULL) + err_report(errfd, "WARNING: running as super-user!"); + + if (vp_count == 0 && (vp_count = cpu_count()) < 1) + vp_count = 1; + + if (sk_count == 0) { + sk_count = 1; + srv_socket[0].addr = "0.0.0.0"; + } +} + + +/******************************************************************/ + +static void start_daemon(void) +{ + pid_t pid; + + /* Start forking */ + if ((pid = fork()) < 0) + err_sys_quit(errfd, "ERROR: fork"); + if (pid > 0) + exit(0); /* parent */ + + /* First child process */ + setsid(); /* become session leader */ + + if ((pid = fork()) < 0) + err_sys_quit(errfd, "ERROR: fork"); + if (pid > 0) /* first child */ + exit(0); + + umask(022); + + if (chdir(logdir) < 0) + err_sys_quit(errfd, "ERROR: can't change directory to %s: chdir", logdir); +} + + +/****************************************************************** + * For simplicity, the minimal size of thread pool is considered + * as a maximum number of spare threads (max_wait_threads) that + * will be created upon server startup. The pool size can grow up + * to the max_threads value. Note that this is a per listening + * socket limit. It is also possible to limit the total number of + * threads for all sockets rather than impose a per socket limit. + */ + +static void set_thread_throttling(void) +{ + /* + * Calculate total values across all processes. + * All numbers are per listening socket. + */ + if (max_wait_threads == 0) + max_wait_threads = MAX_WAIT_THREADS_DEFAULT * vp_count; + /* Assuming that each client session needs FD_PER_THREAD file descriptors */ + if (max_threads == 0) + max_threads = (st_getfdlimit() * vp_count) / FD_PER_THREAD / sk_count; + if (max_wait_threads > max_threads) + max_wait_threads = max_threads; + + /* + * Now calculate per-process values. + */ + if (max_wait_threads % vp_count) + max_wait_threads = max_wait_threads / vp_count + 1; + else + max_wait_threads = max_wait_threads / vp_count; + if (max_threads % vp_count) + max_threads = max_threads / vp_count + 1; + else + max_threads = max_threads / vp_count; + + if (min_wait_threads > max_wait_threads) + min_wait_threads = max_wait_threads; +} + + +/******************************************************************/ + +static void create_listeners(void) +{ + int i, n, sock; + char *c; + struct sockaddr_in serv_addr; + struct hostent *hp; + unsigned short port; + + for (i = 0; i < sk_count; i++) { + port = 0; + if ((c = strchr(srv_socket[i].addr, ':')) != NULL) { + *c++ = '\0'; + port = (unsigned short) atoi(c); + } + if (srv_socket[i].addr[0] == '\0') + srv_socket[i].addr = "0.0.0.0"; + if (port == 0) + port = SERV_PORT_DEFAULT; + + /* Create server socket */ + if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) + err_sys_quit(errfd, "ERROR: can't create socket: socket"); + n = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&n, sizeof(n)) < 0) + err_sys_quit(errfd, "ERROR: can't set SO_REUSEADDR: setsockopt"); + memset(&serv_addr, 0, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + serv_addr.sin_port = htons(port); + serv_addr.sin_addr.s_addr = inet_addr(srv_socket[i].addr); + if (serv_addr.sin_addr.s_addr == INADDR_NONE) { + /* not dotted-decimal */ + if ((hp = gethostbyname(srv_socket[i].addr)) == NULL) + err_quit(errfd, "ERROR: can't resolve address: %s", + srv_socket[i].addr); + memcpy(&serv_addr.sin_addr, hp->h_addr, hp->h_length); + } + srv_socket[i].port = port; + + /* Do bind and listen */ + if (bind(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) + err_sys_quit(errfd, "ERROR: can't bind to address %s, port %hu", + srv_socket[i].addr, port); + if (listen(sock, listenq_size) < 0) + err_sys_quit(errfd, "ERROR: listen"); + + /* Create file descriptor object from OS socket */ + if ((srv_socket[i].nfd = st_netfd_open_socket(sock)) == NULL) + err_sys_quit(errfd, "ERROR: st_netfd_open_socket"); + /* + * On some platforms (e.g. IRIX, Linux) accept() serialization is never + * needed for any OS version. In that case st_netfd_serialize_accept() + * is just a no-op. Also see the comment above. + */ + if (serialize_accept && st_netfd_serialize_accept(srv_socket[i].nfd) < 0) + err_sys_quit(errfd, "ERROR: st_netfd_serialize_accept"); + } +} + + +/******************************************************************/ + +static void change_user(void) +{ + struct passwd *pw; + + if ((pw = getpwnam(username)) == NULL) + err_quit(errfd, "ERROR: can't find user '%s': getpwnam failed", username); + + if (setgid(pw->pw_gid) < 0) + err_sys_quit(errfd, "ERROR: can't change group id: setgid"); + if (setuid(pw->pw_uid) < 0) + err_sys_quit(errfd, "ERROR: can't change user id: setuid"); + + err_report(errfd, "INFO: changed process user id to '%s'", username); +} + + +/******************************************************************/ + +static void open_log_files(void) +{ + int fd; + char str[32]; + + if (interactive_mode) + return; + + /* Open access log */ + if (log_access) + logbuf_open(); + + /* Open and write pid to pid file */ + if ((fd = open(PID_FILE, O_CREAT | O_WRONLY | O_TRUNC, 0644)) < 0) + err_sys_quit(errfd, "ERROR: can't open pid file: open"); + sprintf(str, "%d\n", (int)getpid()); + if (write(fd, str, strlen(str)) != strlen(str)) + err_sys_quit(errfd, "ERROR: can't write to pid file: write"); + close(fd); + + /* Open error log file */ + if ((fd = open(ERRORS_FILE, O_CREAT | O_WRONLY | O_APPEND, 0644)) < 0) + err_sys_quit(errfd, "ERROR: can't open error log file: open"); + errfd = fd; + + err_report(errfd, "INFO: starting the server..."); +} + + +/******************************************************************/ + +static void start_processes(void) +{ + int i, status; + pid_t pid; + sigset_t mask, omask; + + if (interactive_mode) { + my_index = 0; + my_pid = getpid(); + return; + } + + for (i = 0; i < vp_count; i++) { + if ((pid = fork()) < 0) { + err_sys_report(errfd, "ERROR: can't create process: fork"); + if (i == 0) + exit(1); + err_report(errfd, "WARN: started only %d processes out of %d", i, + vp_count); + vp_count = i; + break; + } + if (pid == 0) { + my_index = i; + my_pid = getpid(); + /* Child returns to continue in main() */ + return; + } + vp_pids[i] = pid; + } + + /* + * Parent process becomes a "watchdog" and never returns to main(). + */ + + /* Install signal handlers */ + Signal(SIGTERM, wdog_sighandler); /* terminate */ + Signal(SIGHUP, wdog_sighandler); /* restart */ + Signal(SIGUSR1, wdog_sighandler); /* dump info */ + + /* Now go to sleep waiting for a child termination or a signal */ + for ( ; ; ) { + if ((pid = wait(&status)) < 0) { + if (errno == EINTR) + continue; + err_sys_quit(errfd, "ERROR: watchdog: wait"); + } + /* Find index of the exited child */ + for (i = 0; i < vp_count; i++) { + if (vp_pids[i] == pid) + break; + } + + /* Block signals while printing and forking */ + sigemptyset(&mask); + sigaddset(&mask, SIGTERM); + sigaddset(&mask, SIGHUP); + sigaddset(&mask, SIGUSR1); + sigprocmask(SIG_BLOCK, &mask, &omask); + + if (WIFEXITED(status)) + err_report(errfd, "WARN: watchdog: process %d (pid %d) exited" + " with status %d", i, pid, WEXITSTATUS(status)); + else if (WIFSIGNALED(status)) + err_report(errfd, "WARN: watchdog: process %d (pid %d) terminated" + " by signal %d", i, pid, WTERMSIG(status)); + else if (WIFSTOPPED(status)) + err_report(errfd, "WARN: watchdog: process %d (pid %d) stopped" + " by signal %d", i, pid, WSTOPSIG(status)); + else + err_report(errfd, "WARN: watchdog: process %d (pid %d) terminated:" + " unknown termination reason", i, pid); + + /* Fork another VP */ + if ((pid = fork()) < 0) { + err_sys_report(errfd, "ERROR: watchdog: can't create process: fork"); + } else if (pid == 0) { + my_index = i; + my_pid = getpid(); + /* Child returns to continue in main() */ + return; + } + vp_pids[i] = pid; + + /* Restore the signal mask */ + sigprocmask(SIG_SETMASK, &omask, NULL); + } +} + + +/******************************************************************/ + +static void wdog_sighandler(int signo) +{ + int i, err; + + /* Save errno */ + err = errno; + /* Forward the signal to all children */ + for (i = 0; i < vp_count; i++) { + if (vp_pids[i] > 0) + kill(vp_pids[i], signo); + } + /* + * It is safe to do pretty much everything here because process is + * sleeping in wait() which is async-safe. + */ + switch (signo) { + case SIGHUP: + err_report(errfd, "INFO: watchdog: caught SIGHUP"); + /* Reopen log files - needed for log rotation */ + if (log_access) { + logbuf_close(); + logbuf_open(); + } + close(errfd); + if ((errfd = open(ERRORS_FILE, O_CREAT | O_WRONLY | O_APPEND, 0644)) < 0) + err_sys_quit(STDERR_FILENO, "ERROR: watchdog: open"); + break; + case SIGTERM: + /* Non-graceful termination */ + err_report(errfd, "INFO: watchdog: caught SIGTERM, terminating"); + unlink(PID_FILE); + exit(0); + case SIGUSR1: + err_report(errfd, "INFO: watchdog: caught SIGUSR1"); + break; + default: + err_report(errfd, "INFO: watchdog: caught signal %d", signo); + } + /* Restore errno */ + errno = err; +} + + +/******************************************************************/ + +static void install_sighandlers(void) +{ + sigset_t mask; + int p[2]; + + /* Create signal pipe */ + if (pipe(p) < 0) + err_sys_quit(errfd, "ERROR: process %d (pid %d): can't create" + " signal pipe: pipe", my_index, my_pid); + if ((sig_pipe[0] = st_netfd_open(p[0])) == NULL || + (sig_pipe[1] = st_netfd_open(p[1])) == NULL) + err_sys_quit(errfd, "ERROR: process %d (pid %d): can't create" + " signal pipe: st_netfd_open", my_index, my_pid); + + /* Install signal handlers */ + Signal(SIGTERM, child_sighandler); /* terminate */ + Signal(SIGHUP, child_sighandler); /* restart */ + Signal(SIGUSR1, child_sighandler); /* dump info */ + + /* Unblock signals */ + sigemptyset(&mask); + sigaddset(&mask, SIGTERM); + sigaddset(&mask, SIGHUP); + sigaddset(&mask, SIGUSR1); + sigprocmask(SIG_UNBLOCK, &mask, NULL); +} + + +/******************************************************************/ + +static void child_sighandler(int signo) +{ + int err, fd; + + err = errno; + fd = st_netfd_fileno(sig_pipe[1]); + + /* write() is async-safe */ + if (write(fd, &signo, sizeof(int)) != sizeof(int)) + err_sys_quit(errfd, "ERROR: process %d (pid %d): child's signal" + " handler: write", my_index, my_pid); + errno = err; +} + + +/****************************************************************** + * The "main" function of the signal processing thread. + */ + +/* ARGSUSED */ +static void *process_signals(void *arg) +{ + int signo; + + for ( ; ; ) { + /* Read the next signal from the signal pipe */ + if (st_read(sig_pipe[0], &signo, sizeof(int), + ST_UTIME_NO_TIMEOUT) != sizeof(int)) + err_sys_quit(errfd, "ERROR: process %d (pid %d): signal processor:" + " st_read", my_index, my_pid); + + switch (signo) { + case SIGHUP: + err_report(errfd, "INFO: process %d (pid %d): caught SIGHUP," + " reloading configuration", my_index, my_pid); + if (interactive_mode) { + load_configs(); + break; + } + /* Reopen log files - needed for log rotation */ + if (log_access) { + logbuf_flush(); + logbuf_close(); + logbuf_open(); + } + close(errfd); + if ((errfd = open(ERRORS_FILE, O_CREAT | O_WRONLY | O_APPEND, 0644)) < 0) + err_sys_quit(STDERR_FILENO, "ERROR: process %d (pid %d): signal" + " processor: open", my_index, my_pid); + /* Reload configuration */ + load_configs(); + break; + case SIGTERM: + /* + * Terminate ungracefully since it is generally not known how long + * it will take to gracefully complete all client sessions. + */ + err_report(errfd, "INFO: process %d (pid %d): caught SIGTERM," + " terminating", my_index, my_pid); + if (log_access) + logbuf_flush(); + exit(0); + case SIGUSR1: + err_report(errfd, "INFO: process %d (pid %d): caught SIGUSR1", + my_index, my_pid); + /* Print server info to stderr */ + dump_server_info(); + break; + default: + err_report(errfd, "INFO: process %d (pid %d): caught signal %d", + my_index, my_pid, signo); + } + } + + /* NOTREACHED */ + return NULL; +} + + +/****************************************************************** + * The "main" function of the access log flushing thread. + */ + +/* ARGSUSED */ +static void *flush_acclog_buffer(void *arg) +{ + for ( ; ; ) { + st_sleep(ACCLOG_FLUSH_INTERVAL); + logbuf_flush(); + } + + /* NOTREACHED */ + return NULL; +} + + +/******************************************************************/ + +static void start_threads(void) +{ + long i, n; + + /* Create access log flushing thread */ + if (log_access && st_thread_create(flush_acclog_buffer, NULL, 0, 0) == NULL) + err_sys_quit(errfd, "ERROR: process %d (pid %d): can't create" + " log flushing thread", my_index, my_pid); + + /* Create connections handling threads */ + for (i = 0; i < sk_count; i++) { + err_report(errfd, "INFO: process %d (pid %d): starting %d threads" + " on %s:%u", my_index, my_pid, max_wait_threads, + srv_socket[i].addr, srv_socket[i].port); + WAIT_THREADS(i) = 0; + BUSY_THREADS(i) = 0; + RQST_COUNT(i) = 0; + for (n = 0; n < max_wait_threads; n++) { + if (st_thread_create(handle_connections, (void *)i, 0, 0) != NULL) + WAIT_THREADS(i)++; + else + err_sys_report(errfd, "ERROR: process %d (pid %d): can't create" + " thread", my_index, my_pid); + } + if (WAIT_THREADS(i) == 0) + exit(1); + } +} + + +/******************************************************************/ + +static void *handle_connections(void *arg) +{ + st_netfd_t srv_nfd, cli_nfd; + struct sockaddr_in from; + int fromlen; + long i = (long) arg; + + srv_nfd = srv_socket[i].nfd; + fromlen = sizeof(from); + + while (WAIT_THREADS(i) <= max_wait_threads) { + cli_nfd = st_accept(srv_nfd, (struct sockaddr *)&from, &fromlen, + ST_UTIME_NO_TIMEOUT); + if (cli_nfd == NULL) { + err_sys_report(errfd, "ERROR: can't accept connection: st_accept"); + continue; + } + /* Save peer address, so we can retrieve it later */ + st_netfd_setspecific(cli_nfd, &from.sin_addr, NULL); + + WAIT_THREADS(i)--; + BUSY_THREADS(i)++; + if (WAIT_THREADS(i) < min_wait_threads && TOTAL_THREADS(i) < max_threads) { + /* Create another spare thread */ + if (st_thread_create(handle_connections, (void *)i, 0, 0) != NULL) + WAIT_THREADS(i)++; + else + err_sys_report(errfd, "ERROR: process %d (pid %d): can't create" + " thread", my_index, my_pid); + } + + handle_session(i, cli_nfd); + + st_netfd_close(cli_nfd); + WAIT_THREADS(i)++; + BUSY_THREADS(i)--; + } + + WAIT_THREADS(i)--; + return NULL; +} + + +/******************************************************************/ + +static void dump_server_info(void) +{ + char *buf; + int i, len; + + if ((buf = malloc(sk_count * 512)) == NULL) { + err_sys_report(errfd, "ERROR: malloc failed"); + return; + } + + len = sprintf(buf, "\n\nProcess #%d (pid %d):\n", my_index, (int)my_pid); + for (i = 0; i < sk_count; i++) { + len += sprintf(buf + len, "\nListening Socket #%d:\n" + "-------------------------\n" + "Address %s:%u\n" + "Thread limits (min/max) %d/%d\n" + "Waiting threads %d\n" + "Busy threads %d\n" + "Requests served %d\n", + i, srv_socket[i].addr, srv_socket[i].port, + max_wait_threads, max_threads, + WAIT_THREADS(i), BUSY_THREADS(i), RQST_COUNT(i)); + } + + write(STDERR_FILENO, buf, len); + free(buf); +} + + +/****************************************************************** + * Stubs + */ + +/* + * Session handling function stub. Just dumps small HTML page. + */ +void handle_session(long srv_socket_index, st_netfd_t cli_nfd) +{ + static char resp[] = "HTTP/1.0 200 OK\r\nContent-type: text/html\r\n" + "Connection: close\r\n\r\n

It worked!

\n"; + char buf[512]; + int n = sizeof(resp) - 1; + struct in_addr *from = st_netfd_getspecific(cli_nfd); + + if (st_read(cli_nfd, buf, sizeof(buf), SEC2USEC(REQUEST_TIMEOUT)) < 0) { + err_sys_report(errfd, "WARN: can't read request from %s: st_read", + inet_ntoa(*from)); + return; + } + if (st_write(cli_nfd, resp, n, ST_UTIME_NO_TIMEOUT) != n) { + err_sys_report(errfd, "WARN: can't write response to %s: st_write", + inet_ntoa(*from)); + return; + } + + RQST_COUNT(srv_socket_index)++; +} + + +/* + * Configuration loading function stub. + */ +void load_configs(void) +{ + err_report(errfd, "INFO: process %d (pid %d): configuration loaded", + my_index, my_pid); +} + + +/* + * Buffered access logging methods. + * Note that stdio functions (fopen(3), fprintf(3), fflush(3), etc.) cannot + * be used if multiple VPs are created since these functions can flush buffer + * at any point and thus write only partial log record to disk. + * Also, it is completely safe for all threads of the same VP to write to + * the same log buffer without any mutex protection (one buffer per VP, of + * course). + */ +void logbuf_open(void) +{ + +} + + +void logbuf_flush(void) +{ + +} + + +void logbuf_close(void) +{ + +} + + +/****************************************************************** + * Small utility functions + */ + +static void Signal(int sig, void (*handler)(int)) +{ + struct sigaction sa; + + sa.sa_handler = handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sigaction(sig, &sa, NULL); +} + +static int cpu_count(void) +{ + int n; + +#if defined (_SC_NPROCESSORS_ONLN) + n = (int) sysconf(_SC_NPROCESSORS_ONLN); +#elif defined (_SC_NPROC_ONLN) + n = (int) sysconf(_SC_NPROC_ONLN); +#elif defined (HPUX) +#include + n = mpctl(MPC_GETNUMSPUS, 0, 0); +#else + n = -1; + errno = ENOSYS; +#endif + + return n; +} + +/******************************************************************/ + diff --git a/trunk/research/st-1.9/extensions/Makefile b/trunk/research/st-1.9/extensions/Makefile new file mode 100644 index 0000000000..fc6634f93f --- /dev/null +++ b/trunk/research/st-1.9/extensions/Makefile @@ -0,0 +1,91 @@ +# +# Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. +# All Rights Reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of Silicon Graphics, Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +CC = cc + +SHELL = /bin/sh +ECHO = /bin/echo + +DEPTH = .. +BUILD = +TARGETDIR = obj + +DEFINES = +OTHER_FLAGS = +CFLAGS = + +OBJDIR = $(DEPTH)/$(TARGETDIR) +INCDIR = $(DEPTH)/$(TARGETDIR) + +LIBRESOLV = +EXTRALIBS = + +SLIBRARY = $(OBJDIR)/libstx.a +OBJS = $(OBJDIR)/dnscache.o $(OBJDIR)/dnsres.o $(OBJDIR)/lrucache.o + + +CFLAGS += -Wall -I$(INCDIR) +AR = ar +ARFLAGS = rv +RANLIB = ranlib + + +########################## +# Platform section. +# + +ifeq (LINUX, $(findstring LINUX, $(OS))) +LIBRESOLV = -lresolv +endif + +ifeq ($(OS), SOLARIS) +LIBRESOLV = -lresolv +EXTRALIBS = -lsocket -lnsl +endif + +# +# End of platform section. +########################## + + +all: $(SLIBRARY) + +$(SLIBRARY): $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + $(RANLIB) $@ + +$(OBJDIR)/%.o: %.c stx.h common.h + $(CC) $(CFLAGS) -c $< -o $@ + +clean: + rm -rf $(OBJS) $(SLIBRARY) + +#.DEFAULT: +# @cd $(DEPTH); $(MAKE) $@ + diff --git a/trunk/research/st-1.9/extensions/README b/trunk/research/st-1.9/extensions/README new file mode 100644 index 0000000000..f768aa7125 --- /dev/null +++ b/trunk/research/st-1.9/extensions/README @@ -0,0 +1,42 @@ +This directory contains extensions to the core State Threads Library +that were contributed by users. All files hereunder are not part of the +State Threads Library itself. They are provided as-is, without warranty +or support, and under whatever license terms their authors provided. To +contribute your own extensions, just mail them to the project +administrators or to one of the project's mailing lists; see +state-threads.sourceforge.net. Please indicate the license terms under +which the project may distribute your contribution. + +======================================================================== + +stx_fileio +---------- +Contributed by Jeff , 4 Nov 2002. + +Provides non-blocking random access file reading capability for +programs using the State Threads library. There is one public function: + +ssize_t stx_file_read(st_netfd_t fd, off_t offset, + void *buf, size_t nbytes, st_utime_t timeout); + +The implementation is not optimal in that the data is copied at least once +more than should be necessary. Its usefulness is limited to cases where +random access to a file is required and where starvation of other threads +is unacceptable. + +The particular application which motivated this implementation was a UDP +file transfer protocol. Because the OS does very little buffering of UDP +traffic it is important that UDP transmission threads are not starved for +periods of time which are long relative to the interval required to +maintain a steady send rate. + +Licensed under the same dual MPL/GPL as core State Threads. + +======================================================================== + +stx_dns +------- + +Documentation coming. + +======================================================================== diff --git a/trunk/research/st-1.9/extensions/common.h b/trunk/research/st-1.9/extensions/common.h new file mode 100644 index 0000000000..f6298ba09e --- /dev/null +++ b/trunk/research/st-1.9/extensions/common.h @@ -0,0 +1,77 @@ +#ifndef _STX_COMMON_H_ +#define _STX_COMMON_H_ + +#include +#include + + +#define STX_BEGIN_MACRO { +#define STX_END_MACRO } + + +/***************************************** + * Circular linked list definitions + */ + +typedef struct _stx_clist { + struct _stx_clist *next; + struct _stx_clist *prev; +} stx_clist_t; + +/* Insert element "_e" into the list, before "_l" */ +#define STX_CLIST_INSERT_BEFORE(_e,_l) \ + STX_BEGIN_MACRO \ + (_e)->next = (_l); \ + (_e)->prev = (_l)->prev; \ + (_l)->prev->next = (_e); \ + (_l)->prev = (_e); \ + STX_END_MACRO + +/* Insert element "_e" into the list, after "_l" */ +#define STX_CLIST_INSERT_AFTER(_e,_l) \ + STX_BEGIN_MACRO \ + (_e)->next = (_l)->next; \ + (_e)->prev = (_l); \ + (_l)->next->prev = (_e); \ + (_l)->next = (_e); \ + STX_END_MACRO + +/* Append an element "_e" to the end of the list "_l" */ +#define STX_CLIST_APPEND_LINK(_e,_l) STX_CLIST_INSERT_BEFORE(_e,_l) + +/* Remove the element "_e" from it's circular list */ +#define STX_CLIST_REMOVE_LINK(_e) \ + STX_BEGIN_MACRO \ + (_e)->prev->next = (_e)->next; \ + (_e)->next->prev = (_e)->prev; \ + STX_END_MACRO + +/* Return the head/tail of the list */ +#define STX_CLIST_HEAD(_l) (_l)->next +#define STX_CLIST_TAIL(_l) (_l)->prev + +/* Return non-zero if the given circular list "_l" is empty, */ +/* zero if the circular list is not empty */ +#define STX_CLIST_IS_EMPTY(_l) \ + ((_l)->next == (_l)) + +/* Initialize a circular list */ +#define STX_CLIST_INIT_CLIST(_l) \ + STX_BEGIN_MACRO \ + (_l)->next = (_l); \ + (_l)->prev = (_l); \ + STX_END_MACRO + + +/***************************************** + * Useful macros + */ + +#ifndef offsetof +#define offsetof(type, identifier) ((size_t)&(((type *)0)->identifier)) +#endif + +#define STX_MIN(a, b) (((a) < (b)) ? (a) : (b)) + +#endif /* !_STX_COMMON_H_ */ + diff --git a/trunk/research/st-1.9/extensions/dnscache.c b/trunk/research/st-1.9/extensions/dnscache.c new file mode 100644 index 0000000000..ac49166a14 --- /dev/null +++ b/trunk/research/st-1.9/extensions/dnscache.c @@ -0,0 +1,190 @@ +#include "stx.h" +#include "common.h" + + +/***************************************** + * Basic types definitions + */ + +typedef struct _stx_dns_data { + struct in_addr *addrs; + int num_addrs; + int cur; + time_t expires; +} stx_dns_data_t; + + +#define MAX_HOST_ADDRS 1024 + +static struct in_addr addr_list[MAX_HOST_ADDRS]; + +stx_cache_t *_stx_dns_cache = NULL; + +extern int _stx_dns_ttl; +extern int _stx_dns_getaddrlist(const char *hostname, struct in_addr *addrs, + int *num_addrs, st_utime_t timeout); + + +static unsigned long hash_hostname(const void *key) +{ + const char *name = (const char *)key; + unsigned long hash = 0; + + while (*name) + hash = (hash << 4) - hash + *name++; /* hash = hash * 15 + *name++ */ + + return hash; +} + +static void cleanup_entry(void *key, void *data) +{ + if (key) + free(key); + + if (data) { + if (((stx_dns_data_t *)data)->addrs) + free(((stx_dns_data_t *)data)->addrs); + free(data); + } +} + +static int lookup_entry(const char *host, struct in_addr *addrs, + int *num_addrs, int rotate) +{ + stx_cache_entry_t *entry; + stx_dns_data_t *data; + int n; + + entry = stx_cache_entry_lookup(_stx_dns_cache, host); + if (entry) { + data = (stx_dns_data_t *)stx_cache_entry_getdata(entry); + if (st_time() <= data->expires) { + if (*num_addrs == 1) { + if (rotate) { + *addrs = data->addrs[data->cur++]; + if (data->cur >= data->num_addrs) + data->cur = 0; + } else { + *addrs = data->addrs[0]; + } + } else { + n = STX_MIN(*num_addrs, data->num_addrs); + memcpy(addrs, data->addrs, n * sizeof(*addrs)); + *num_addrs = n; + } + + stx_cache_entry_release(_stx_dns_cache, entry); + return 1; + } + + /* + * Cache entry expired: decrement its refcount and purge it from cache. + */ + stx_cache_entry_release(_stx_dns_cache, entry); + stx_cache_entry_delete(_stx_dns_cache, entry); + } + + return 0; +} + +static void insert_entry(const char *host, struct in_addr *addrs, int count) +{ + stx_cache_entry_t *entry; + stx_dns_data_t *data; + char *key; + size_t n; + + if (_stx_dns_ttl > 0) { + key = strdup(host); + data = (stx_dns_data_t *)malloc(sizeof(stx_dns_data_t)); + n = count * sizeof(*addrs); + if (data) { + data->addrs = (struct in_addr *)malloc(n); + if (data->addrs) + memcpy(data->addrs, addrs, n); + data->num_addrs = count; + data->cur = 0; + data->expires = st_time() + _stx_dns_ttl; + } + entry = stx_cache_entry_create(key, data, strlen(host) + 1 + + sizeof(stx_dns_data_t) + n + + stx_cache_entry_sizeof()); + if (key && data && data->addrs && entry && + stx_cache_entry_insert(_stx_dns_cache, entry) == 0) { + stx_cache_entry_release(_stx_dns_cache, entry); + return; + } + + if (entry) + stx_cache_entry_delete(_stx_dns_cache, entry); + else + cleanup_entry(key, data); + } +} + + + +int _stx_dns_cache_getaddrlist(const char *hostname, struct in_addr *addrs, + int *num_addrs, st_utime_t timeout, + int rotate) +{ + char host[128]; + int n, count; + + if (!_stx_dns_cache) + return _stx_dns_getaddrlist(hostname, addrs, num_addrs, timeout); + + for (n = 0; n < sizeof(host) - 1 && hostname[n]; n++) { + host[n] = tolower(hostname[n]); + } + host[n] = '\0'; + + if (lookup_entry(host, addrs, num_addrs, rotate)) + return 0; + + count = MAX_HOST_ADDRS; + if (_stx_dns_getaddrlist(host, addr_list, &count, timeout) < 0) + return -1; + n = STX_MIN(*num_addrs, count); + memcpy(addrs, addr_list, n * sizeof(*addrs)); + *num_addrs = n; + + insert_entry(host, addr_list, count); + return 0; +} + + +int stx_dns_cache_init(size_t max_size, size_t max_bytes, size_t hash_size) +{ + _stx_dns_cache = stx_cache_create(max_size, max_bytes, hash_size, + hash_hostname, + (long (*)(const void *, const void *))strcmp, + cleanup_entry); + if (!_stx_dns_cache) + return -1; + + return 0; +} + +void stx_dns_cache_getinfo(stx_cache_info_t *info) +{ + if (_stx_dns_cache) + stx_cache_getinfo(_stx_dns_cache, info); + else + memset(info, 0, sizeof(stx_cache_info_t)); +} + +int stx_dns_getaddrlist(const char *hostname, struct in_addr *addrs, + int *num_addrs, st_utime_t timeout) +{ + return _stx_dns_cache_getaddrlist(hostname, addrs, num_addrs, timeout, 0); +} + +int stx_dns_getaddr(const char *hostname, struct in_addr *addr, + st_utime_t timeout) +{ + int n = 1; + + return _stx_dns_cache_getaddrlist(hostname, addr, &n, timeout, 1); +} + diff --git a/trunk/research/st-1.9/extensions/dnsres.c b/trunk/research/st-1.9/extensions/dnsres.c new file mode 100644 index 0000000000..04a91ccafa --- /dev/null +++ b/trunk/research/st-1.9/extensions/dnsres.c @@ -0,0 +1,305 @@ +/* + * Copyright (c) 1985, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Silicon Graphics, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "stx.h" + +#define MAXPACKET 1024 + +#if !defined(NETDB_INTERNAL) && defined(h_NETDB_INTERNAL) +#define NETDB_INTERNAL h_NETDB_INTERNAL +#endif + +/* New in Solaris 7 */ +#if !defined(_getshort) && defined(ns_get16) +#define _getshort(cp) ns_get16(cp) +#define _getlong(cp) ns_get32(cp) +#endif + +typedef union { + HEADER hdr; + u_char buf[MAXPACKET]; +} querybuf_t; + +int _stx_dns_ttl; + + +static int parse_answer(querybuf_t *ans, int len, struct in_addr *addrs, + int *num_addrs) +{ + char buf[MAXPACKET]; + HEADER *ahp; + u_char *cp, *eoa; + int type, n, i; + + ahp = &ans->hdr; + eoa = ans->buf + len; + cp = ans->buf + sizeof(HEADER); + h_errno = TRY_AGAIN; + _stx_dns_ttl = -1; + i = 0; + + while (ahp->qdcount > 0) { + ahp->qdcount--; + cp += dn_skipname(cp, eoa) + QFIXEDSZ; + } + while (ahp->ancount > 0 && cp < eoa && i < *num_addrs) { + ahp->ancount--; + if ((n = dn_expand(ans->buf, eoa, cp, buf, sizeof(buf))) < 0) + return -1; + cp += n; + if (cp + 4 + 4 + 2 >= eoa) + return -1; + type = _getshort(cp); + cp += 4; + if (type == T_A) + _stx_dns_ttl = _getlong(cp); + cp += 4; + n = _getshort(cp); + cp += 2; + if (type == T_A) { + if (n > sizeof(*addrs) || cp + n > eoa) + return -1; + memcpy(&addrs[i++], cp, n); + } + cp += n; + } + + *num_addrs = i; + return 0; +} + + +static int query_domain(st_netfd_t nfd, const char *name, + struct in_addr *addrs, int *num_addrs, + st_utime_t timeout) +{ + querybuf_t qbuf; + u_char *buf = qbuf.buf; + HEADER *hp = &qbuf.hdr; + int blen = sizeof(qbuf); + int i, len, id; + + for (i = 0; i < _res.nscount; i++) { + len = res_mkquery(QUERY, name, C_IN, T_A, NULL, 0, NULL, buf, blen); + if (len <= 0) { + h_errno = NO_RECOVERY; + return -1; + } + id = hp->id; + + if (st_sendto(nfd, buf, len, (struct sockaddr *)&(_res.nsaddr_list[i]), + sizeof(struct sockaddr), timeout) != len) { + h_errno = NETDB_INTERNAL; + /* EINTR means interrupt by other thread, NOT by a caught signal */ + if (errno == EINTR) + return -1; + continue; + } + + /* Wait for reply */ + do { + len = st_recvfrom(nfd, buf, blen, NULL, NULL, timeout); + if (len <= 0) + break; + } while (id != hp->id); + + if (len < HFIXEDSZ) { + h_errno = NETDB_INTERNAL; + if (len >= 0) + errno = EMSGSIZE; + else if (errno == EINTR) /* see the comment above */ + return -1; + continue; + } + + hp->ancount = ntohs(hp->ancount); + hp->qdcount = ntohs(hp->qdcount); + if ((hp->rcode != NOERROR) || (hp->ancount == 0)) { + switch (hp->rcode) { + case NXDOMAIN: + h_errno = HOST_NOT_FOUND; + break; + case SERVFAIL: + h_errno = TRY_AGAIN; + break; + case NOERROR: + h_errno = NO_DATA; + break; + case FORMERR: + case NOTIMP: + case REFUSED: + default: + h_errno = NO_RECOVERY; + } + continue; + } + + if (parse_answer(&qbuf, len, addrs, num_addrs) == 0) + return 0; + } + + return -1; +} + + +#define CLOSE_AND_RETURN(ret) \ + { \ + n = errno; \ + st_netfd_close(nfd); \ + errno = n; \ + return (ret); \ + } + + +int _stx_dns_getaddrlist(const char *host, struct in_addr *addrs, + int *num_addrs, st_utime_t timeout) +{ + char name[MAXDNAME], **domain; + const char *cp; + int s, n, maxlen, dots; + int trailing_dot, tried_as_is; + st_netfd_t nfd; + + if ((_res.options & RES_INIT) == 0 && res_init() == -1) { + h_errno = NETDB_INTERNAL; + return -1; + } + if (_res.options & RES_USEVC) { + h_errno = NETDB_INTERNAL; + errno = ENOSYS; + return -1; + } + if (!host || *host == '\0') { + h_errno = HOST_NOT_FOUND; + return -1; + } + + /* Create UDP socket */ + if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { + h_errno = NETDB_INTERNAL; + return -1; + } + if ((nfd = st_netfd_open_socket(s)) == NULL) { + h_errno = NETDB_INTERNAL; + n = errno; + close(s); + errno = n; + return -1; + } + + maxlen = sizeof(name) - 1; + n = 0; + dots = 0; + trailing_dot = 0; + tried_as_is = 0; + + for (cp = host; *cp && n < maxlen; cp++) { + dots += (*cp == '.'); + name[n++] = *cp; + } + if (name[n - 1] == '.') + trailing_dot = 1; + + /* + * If there are dots in the name already, let's just give it a try + * 'as is'. The threshold can be set with the "ndots" option. + */ + if (dots >= _res.ndots) { + if (query_domain(nfd, host, addrs, num_addrs, timeout) == 0) + CLOSE_AND_RETURN(0); + if (h_errno == NETDB_INTERNAL && errno == EINTR) + CLOSE_AND_RETURN(-1); + tried_as_is = 1; + } + + /* + * We do at least one level of search if + * - there is no dot and RES_DEFNAME is set, or + * - there is at least one dot, there is no trailing dot, + * and RES_DNSRCH is set. + */ + if ((!dots && (_res.options & RES_DEFNAMES)) || + (dots && !trailing_dot && (_res.options & RES_DNSRCH))) { + name[n++] = '.'; + for (domain = _res.dnsrch; *domain; domain++) { + strncpy(name + n, *domain, maxlen - n); + if (query_domain(nfd, name, addrs, num_addrs, timeout) == 0) + CLOSE_AND_RETURN(0); + if (h_errno == NETDB_INTERNAL && errno == EINTR) + CLOSE_AND_RETURN(-1); + if (!(_res.options & RES_DNSRCH)) + break; + } + } + + /* + * If we have not already tried the name "as is", do that now. + * note that we do this regardless of how many dots were in the + * name or whether it ends with a dot. + */ + if (!tried_as_is) { + if (query_domain(nfd, host, addrs, num_addrs, timeout) == 0) + CLOSE_AND_RETURN(0); + } + + CLOSE_AND_RETURN(-1); +} + diff --git a/trunk/research/st-1.9/extensions/lrucache.c b/trunk/research/st-1.9/extensions/lrucache.c new file mode 100644 index 0000000000..33494fee62 --- /dev/null +++ b/trunk/research/st-1.9/extensions/lrucache.c @@ -0,0 +1,343 @@ +#include "stx.h" +#include "common.h" + + +/***************************************** + * Basic types definitions + */ + +struct _stx_centry { + void *key; /* key for doing lookups */ + void *data; /* data in the cache */ + size_t weight; /* "weight" of this entry */ + struct _stx_centry *next; /* next entry */ + struct _stx_centry **pthis; + stx_clist_t lru_link; /* for putting this entry on LRU list */ + int ref_count; /* use count for this entry */ + int delete_pending; /* pending delete flag */ +}; + +struct _stx_cache { + size_t max_size; /* max size of cache */ + size_t cur_size; /* current size of cache */ + + size_t max_weight; /* cache capacity */ + size_t cur_weight; /* current total "weight" of all entries */ + + size_t hash_size; /* size of hash table */ + stx_cache_entry_t **table; /* hash table for this cache */ + + stx_clist_t lru_list; /* least-recently-used list */ + + /* Cache stats */ + unsigned long hits; /* num cache hits */ + unsigned long lookups; /* num cache lookups */ + unsigned long inserts; /* num inserts */ + unsigned long deletes; /* num deletes */ + + /* Functions */ + unsigned long (*key_hash_fn)(const void *); + long (*key_cmp_fn)(const void *, const void *); + void (*cleanup_fn)(void *, void *); +}; + + +#define STX_CACHE_ENTRY_PTR(_qp) \ + ((stx_cache_entry_t *)((char *)(_qp) - offsetof(stx_cache_entry_t, lru_link))) + + +/***************************************** + * Cache methods + */ + +stx_cache_t *stx_cache_create(size_t max_size, size_t max_weight, + size_t hash_size, + unsigned long (*key_hash_fn)(const void *key), + long (*key_cmp_fn)(const void *key1, + const void *key2), + void (*cleanup_fn)(void *key, void *data)) +{ + stx_cache_t *newcache; + + newcache = (stx_cache_t *)calloc(1, sizeof(stx_cache_t)); + if (newcache == NULL) + return NULL; + newcache->table = (stx_cache_entry_t **)calloc(hash_size, + sizeof(stx_cache_entry_t *)); + if (newcache->table == NULL) { + free(newcache); + return NULL; + } + + newcache->max_size = max_size; + newcache->max_weight = max_weight; + newcache->hash_size = hash_size; + STX_CLIST_INIT_CLIST(&(newcache->lru_list)); + newcache->key_hash_fn = key_hash_fn; + newcache->key_cmp_fn = key_cmp_fn; + newcache->cleanup_fn = cleanup_fn; + + return newcache; +} + + +void stx_cache_empty(stx_cache_t *cache) +{ + size_t i; + stx_cache_entry_t *entry, *next_entry; + + for (i = 0; i < cache->hash_size; i++) { + entry = cache->table[i]; + while (entry) { + next_entry = entry->next; + stx_cache_entry_delete(cache, entry); + entry = next_entry; + } + } +} + + +void stx_cache_traverse(stx_cache_t *cache, + void (*callback)(void *key, void *data)) +{ + size_t i; + stx_cache_entry_t *entry; + + for (i = 0; i < cache->hash_size; i++) { + for (entry = cache->table[i]; entry; entry = entry->next) { + if (!entry->delete_pending) + (*callback)(entry->key, entry->data); + } + } +} + + +void stx_cache_traverse_lru(stx_cache_t *cache, + void (*callback)(void *key, void *data), + unsigned int n) +{ + stx_clist_t *q; + stx_cache_entry_t *entry; + + for (q = STX_CLIST_HEAD(&cache->lru_list); q != &cache->lru_list && n; + q = q->next, n--) { + entry = STX_CACHE_ENTRY_PTR(q); + (*callback)(entry->key, entry->data); + } +} + + +void stx_cache_traverse_mru(stx_cache_t *cache, + void (*callback)(void *key, void *data), + unsigned int n) +{ + stx_clist_t *q; + stx_cache_entry_t *entry; + + for (q = STX_CLIST_TAIL(&cache->lru_list); q != &cache->lru_list && n; + q = q->prev, n--) { + entry = STX_CACHE_ENTRY_PTR(q); + (*callback)(entry->key, entry->data); + } +} + + +size_t stx_cache_getsize(stx_cache_t *cache) +{ + return cache->cur_size; +} + + +size_t stx_cache_getweight(stx_cache_t *cache) +{ + return cache->cur_weight; +} + + +void stx_cache_getinfo(stx_cache_t *cache, stx_cache_info_t *info) +{ + info->max_size = cache->max_size; + info->max_weight = cache->max_weight; + info->hash_size = cache->hash_size; + info->cur_size = cache->cur_size; + info->cur_weight = cache->cur_weight; + info->hits = cache->hits; + info->lookups = cache->lookups; + info->inserts = cache->inserts; + info->deletes = cache->deletes; +} + + +/***************************************** + * Cache entry methods + */ + +stx_cache_entry_t *stx_cache_entry_create(void *key, void *data, + size_t weight) +{ + stx_cache_entry_t *newentry; + + newentry = (stx_cache_entry_t *)calloc(1, sizeof(stx_cache_entry_t)); + if (newentry == NULL) + return NULL; + + newentry->key = key; + newentry->data = data; + newentry->weight = weight; + + return newentry; +} + + +void stx_cache_entry_delete(stx_cache_t *cache, stx_cache_entry_t *entry) +{ + entry->delete_pending = 1; + + if (entry->ref_count > 0) + return; + + if (entry->pthis) { + *entry->pthis = entry->next; + if (entry->next) + entry->next->pthis = entry->pthis; + + cache->cur_size--; + cache->cur_weight -= entry->weight; + cache->deletes++; + STX_CLIST_REMOVE_LINK(&(entry->lru_link)); + } + + if (cache->cleanup_fn) + cache->cleanup_fn(entry->key, entry->data); + + entry->pthis = NULL; + entry->key = NULL; + entry->data = NULL; + free(entry); +} + + +stx_cache_entry_t *stx_cache_entry_lookup(stx_cache_t *cache, const void *key) +{ + unsigned long bucket; + stx_cache_entry_t *entry; + + cache->lookups++; + bucket = cache->key_hash_fn(key) % cache->hash_size; + for (entry = cache->table[bucket]; entry; entry = entry->next) { + if (!entry->delete_pending && cache->key_cmp_fn(key, entry->key) == 0) + break; + } + if (entry) { + cache->hits++; + if (entry->ref_count == 0) + STX_CLIST_REMOVE_LINK(&(entry->lru_link)); + entry->ref_count++; + } + + return entry; +} + + +void stx_cache_entry_release(stx_cache_t *cache, stx_cache_entry_t *entry) +{ + if (entry->ref_count == 0) + return; + + entry->ref_count--; + + if (entry->ref_count == 0) { + STX_CLIST_APPEND_LINK(&(entry->lru_link), &(cache->lru_list)); + if (entry->delete_pending) + stx_cache_entry_delete(cache, entry); + } +} + + +int stx_cache_entry_insert(stx_cache_t *cache, stx_cache_entry_t *entry) +{ + stx_cache_entry_t *old_entry; + unsigned long bucket; + + /* + * If cache capacity is exceeded, try to remove LRU entries till there is + * enough room or LRU list is empty. + */ + while (cache->cur_weight + entry->weight > cache->max_weight) { + old_entry = stx_cache_entry_getlru(cache); + if (!old_entry) { + /* cache capacity is exceeded and all entries are in use */ + return -1; + } + stx_cache_entry_delete(cache, old_entry); + } + + /* If cache size is exceeded, remove LRU entry */ + if (cache->cur_size >= cache->max_size) { + old_entry = stx_cache_entry_getlru(cache); + if (!old_entry) { + /* cache size is exceeded and all entries are in use */ + return -1; + } + stx_cache_entry_delete(cache, old_entry); + } + + /* Don't add duplicate entries in the cache */ + bucket = cache->key_hash_fn(entry->key) % cache->hash_size; + for (old_entry = cache->table[bucket]; old_entry; + old_entry = old_entry->next) { + if (!old_entry->delete_pending && + cache->key_cmp_fn(entry->key, old_entry->key) == 0) + break; + } + if (old_entry) + stx_cache_entry_delete(cache, old_entry); + + /* Insert in the hash table */ + entry->next = cache->table[bucket]; + cache->table[bucket] = entry; + entry->pthis = &cache->table[bucket]; + if (entry->next) + entry->next->pthis = &entry->next; + entry->ref_count++; + + cache->inserts++; + cache->cur_size++; + cache->cur_weight += entry->weight; + + return 0; +} + + +stx_cache_entry_t *stx_cache_entry_getlru(stx_cache_t *cache) +{ + if (STX_CLIST_IS_EMPTY(&(cache->lru_list))) + return NULL; + + return STX_CACHE_ENTRY_PTR(STX_CLIST_HEAD(&(cache->lru_list))); +} + + +int stx_cache_entry_sizeof(void) +{ + return (int)sizeof(stx_cache_entry_t); +} + + +void *stx_cache_entry_getdata(stx_cache_entry_t *entry) +{ + return entry->data; +} + + +void *stx_cache_entry_getkey(stx_cache_entry_t *entry) +{ + return entry->key; +} + + +size_t stx_cache_entry_getweight(stx_cache_entry_t *entry) +{ + return entry->weight; +} + diff --git a/trunk/research/st-1.9/extensions/print_stk.patch b/trunk/research/st-1.9/extensions/print_stk.patch new file mode 100644 index 0000000000..f7451c7b07 --- /dev/null +++ b/trunk/research/st-1.9/extensions/print_stk.patch @@ -0,0 +1,367 @@ +Michael Abd-El-Malek contributed this patch. He wrote: +---------------------------------------- +Hello, + +This is a patch that enables programmatically dumping the stack of +every thread. This has been useful in debugging deadlocks, etc... +Our usage model is that the SIGUSR2 handler calls the new +_st_print_thread_stacks function, which dumps the stack for all +threads. A convenient feature is that for thread stacks that are the +same (which is common for application with a lot of worker threads +waiting for work), only one stack trace is printed, along with a +count of how many threads have that same stack. + +I use the glibc backtrace function to get the backtrace, and then use +popen to execute addr2line and convert memory addresses to file +names, function names, and line numbers. If glibc isn't available, +_st_print_thread_stacks just prints a warning. And this feature is +only available if DEBUG is turned on. + +We've found this feature extremely helpful when debugging. + +The patch can be a bit more robust (it assumes addr2line exists). +But I didn't want to go through the hassle of doing this, if the +StateThreads community doesn't want to use this patch. (In our +environment, addr2line will always be there.) + +Cheers, +Mike +---------------------------------------- +Invoking complex functions from a signal handler is not recommended, +plus this patch changes the behavior of existing API hooks. It will +not become part of State Threads proper but you may find it useful +nonetheless. This patch applies to st-1.5.2. + +diff -Nur Makefile.1.5.2 Makefile +--- Makefile.1.5.2 Wed Sep 7 14:19:50 2005 ++++ Makefile Wed Sep 7 14:33:08 2005 +@@ -255,7 +255,8 @@ + $(TARGETDIR)/stk.o \ + $(TARGETDIR)/sync.o \ + $(TARGETDIR)/key.o \ +- $(TARGETDIR)/io.o ++ $(TARGETDIR)/io.o \ ++ $(TARGETDIR)/backtrace.o + OBJS += $(EXTRA_OBJS) + HEADER = $(TARGETDIR)/st.h + SLIBRARY = $(TARGETDIR)/libst.a +diff -Nur backtrace.c.1.5.2 backtrace.c +--- backtrace.c.1.5.2 Wed Dec 31 16:00:00 1969 ++++ backtrace.c Wed Sep 7 13:40:21 2005 +@@ -0,0 +1,211 @@ ++/* ++ * The contents of this file are subject to the Mozilla Public ++ * License Version 1.1 (the "License"); you may not use this file ++ * except in compliance with the License. You may obtain a copy of ++ * the License at http://www.mozilla.org/MPL/ ++ * ++ * Software distributed under the License is distributed on an "AS ++ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or ++ * implied. See the License for the specific language governing ++ * rights and limitations under the License. ++ * ++ * Contributor(s): Michael Abd-El-Malek (mabdelmalek@cmu.edu) ++ * Carnegie Mellon University ++ * ++ * Alternatively, the contents of this file may be used under the ++ * terms of the GNU General Public License Version 2 or later (the ++ * "GPL"), in which case the provisions of the GPL are applicable ++ * instead of those above. If you wish to allow use of your ++ * version of this file only under the terms of the GPL and not to ++ * allow others to use your version of this file under the MPL, ++ * indicate your decision by deleting the provisions above and ++ * replace them with the notice and other provisions required by ++ * the GPL. If you do not delete the provisions above, a recipient ++ * may use your version of this file under either the MPL or the ++ * GPL. ++ */ ++ ++ ++ ++/* ++ * This file contains routines for printing a stack trace of all threads. ++ * Only works when DEBUG is defined and where glibc is available, since it ++ * provides the backtrace() function. ++ */ ++ ++#define _GNU_SOURCE /* to get program_invocation_name */ ++ ++#include ++#include ++ ++ ++#if defined(DEBUG) && defined(__GLIBC__) ++ ++#include ++#include "common.h" ++#include ++#include ++#include ++ ++ ++/* The maximum number of frames to get a stack trace for. If a thread has more ++ * frames than this, then we only show the latest X frames. */ ++#define MAX_NUM_FRAMES 64 ++ ++ ++typedef struct thread_stack_s { ++ uint32_t num_frames; ++ void* addresses[MAX_NUM_FRAMES]; /* frame pointers */ ++ char* locations[MAX_NUM_FRAMES]; /* file/function/line numbers */ ++ uint32_t num_matches; ++ ++ struct thread_stack_s* next; ++} thread_stack_t; ++ ++static thread_stack_t* stacks = NULL; ++ ++ ++/* Converts the function's memory addresses to function names, file names, and ++ * line numbers. Calls binutil's addr2line program. */ ++static void get_symbol_names(thread_stack_t *stack) ++{ ++ char program_to_run[1024], function[256], filename_lineno[256], temp[19]; ++ FILE* output; ++ int num_bytes_left; ++ uint32_t i; ++ ++ /* Construct the arguments to addr2line */ ++ num_bytes_left = sizeof(program_to_run); ++ num_bytes_left -= snprintf(program_to_run, sizeof(program_to_run), ++ "addr2line -fCe %s", program_invocation_name); ++ for (i = 0; i < stack->num_frames && num_bytes_left > 0; ++i) { ++ num_bytes_left -= snprintf(temp, sizeof(temp), " %p", stack->addresses[i]); ++ strncat(program_to_run, temp, num_bytes_left); ++ } ++ ++ /* Use popen to execute addr2line and read its ouput */ ++ output = popen(program_to_run, "r"); ++ for (i = 0; i < stack->num_frames; ++i) { ++ char* function_listing = (char*) malloc(512); ++ fscanf(output, "%255s\n", function); ++ fscanf(output, "%255s\n", filename_lineno); ++ snprintf(function_listing, 512, "%s at %s", function, filename_lineno); ++ stack->locations[i] = function_listing; ++ } ++ pclose(output); ++} ++ ++ ++static void print_stack(thread_stack_t* stack) ++{ ++ int skip_offset = 0, cmp_len; ++ uint32_t i; ++ ++ /* Get the function names/filenames/line numbers */ ++ get_symbol_names(stack); ++ ++ cmp_len = strlen("_st_iterate_threads_helper"); ++ ++ /* Print the backtrace */ ++ for (i = 0; i < stack->num_frames; ++i) { ++ /* Skip frames we don't have location info for */ ++ if (!strncmp(stack->locations[i], "??", 2)) { ++ continue; ++ } ++ ++ /* Skip the frames that are used for printing the stack trace */ ++ if (skip_offset) { ++ printf("\t#%2d %s %p\n", i - skip_offset, stack->locations[i], ++ stack->addresses[i]); ++ } else if (!strncmp(stack->locations[i], "_st_iterate_threads_helper", ++ cmp_len)) { ++ skip_offset = i + 1; ++ } ++ } ++} ++ ++ ++static void add_current_thread_stack(void) ++{ ++ thread_stack_t *new_stack = malloc(sizeof(thread_stack_t)); ++ thread_stack_t *search; ++ ++ /* Call glibc function to get the backtrace */ ++ new_stack->num_frames = backtrace(new_stack->addresses, MAX_NUM_FRAMES); ++ ++ /* Check if we have another stacks that is equivalent. If so, then coaelsce ++ * two stacks into one, to minimize output to user. */ ++ search = stacks; ++ while (search) { ++ if (search->num_frames == new_stack->num_frames && ++ !memcmp(search->addresses, new_stack->addresses, ++ search->num_frames * sizeof(void*))) { ++ /* Found an existing stack that is the same as this thread's stack */ ++ ++search->num_matches; ++ free(new_stack); ++ return; ++ } else { ++ search = search->next; ++ } ++ } ++ ++ /* This is a new stack. Add it to the list of stacks. */ ++ new_stack->num_matches = 1; ++ new_stack->next = stacks; ++ stacks = new_stack; ++} ++ ++static void print_stack_frames(void) ++{ ++ while (stacks) { ++ printf("\n%u thread(s) with this backtrace:\n", stacks->num_matches); ++ print_stack(stacks); ++ stacks = stacks->next; ++ } ++ printf("\n"); ++} ++ ++static void free_stacks(void) ++{ ++ uint32_t i; ++ while (stacks) { ++ thread_stack_t *next = stacks->next; ++ for (i = 0; i < stacks->num_frames; ++i) { ++ free(stacks->locations[i]); ++ } ++ free(stacks); ++ stacks = next; ++ } ++ stacks = NULL; ++} ++ ++ ++static void st_print_thread_stack(_st_thread_t *thread, int start_flag, ++ int end_flag) ++{ ++ if (end_flag == 0) { ++ add_current_thread_stack(); ++ } else { ++ print_stack_frames(); ++ } ++} ++ ++ ++void _st_print_thread_stacks(int ignore) ++{ ++ _st_iterate_threads_flag = 1; ++ _st_iterate_threads_helper(st_print_thread_stack); ++ _st_iterate_threads_flag = 0; ++ ++ /* Deallocate memory */ ++ free_stacks(); ++} ++ ++#else /* defined(DEBUG) && defined(__GLIBC__) */ ++ ++void _st_print_thread_stacks(int ignore) ++{ ++ printf("%s: need DEBUG mode and glibc-specific functions to read stack.\n", ++ __FUNCTION__); ++} ++#endif /* defined(DEBUG) && defined(__GLIBC__) */ +diff -Nur common.h.1.5.2 common.h +--- common.h.1.5.2 Wed Sep 7 14:18:37 2005 ++++ common.h Wed Sep 7 14:35:36 2005 +@@ -371,8 +371,18 @@ + */ + + #ifdef DEBUG +-void _st_iterate_threads(void); +-#define ST_DEBUG_ITERATE_THREADS() _st_iterate_threads() ++typedef void(*_st_func_ptr_t)(_st_thread_t *thread, ++ int start_flag, ++ int end_flag); ++/* Pointer to function that will be called on thread switch */ ++extern _st_func_ptr_t _st_iterate_func_ptr; ++extern int _st_iterate_threads_flag; ++/* Thread iteration function that will call an arbitrary function */ ++extern void _st_iterate_threads_helper(_st_func_ptr_t func); ++#define ST_DEBUG_ITERATE_THREADS() \ ++ if (_st_iterate_func_ptr) { \ ++ _st_iterate_threads_helper(_st_iterate_func_ptr); \ ++ } + #else + #define ST_DEBUG_ITERATE_THREADS() + #endif +diff -Nur public.h.1.5.2 public.h +--- public.h.1.5.2 Wed Sep 7 11:46:58 2005 ++++ public.h Wed Sep 7 13:38:46 2005 +@@ -171,8 +171,10 @@ + extern st_netfd_t st_open(const char *path, int oflags, mode_t mode); + + #ifdef DEBUG +-extern void _st_show_thread_stack(st_thread_t thread, const char *messg); ++extern void _st_show_thread_stack(st_thread_t thread, int start_flag, ++ int end_flag); + extern void _st_iterate_threads(void); ++extern void _st_print_thread_stacks(int ignore); + #endif + + #ifdef __cplusplus +diff -Nur sched.c.1.5.2 sched.c +--- sched.c.1.5.2 Wed Sep 7 10:48:05 2005 ++++ sched.c Wed Sep 7 13:38:46 2005 +@@ -919,16 +919,13 @@ + + + #ifdef DEBUG +-/* ARGSUSED */ +-void _st_show_thread_stack(_st_thread_t *thread, const char *messg) +-{ +- +-} +- + /* To be set from debugger */ + int _st_iterate_threads_flag = 0; ++/* Thread iteration function that will call an arbitrary function */ ++_st_func_ptr_t _st_iterate_func_ptr = NULL; + +-void _st_iterate_threads(void) ++/* This function iterates over all threads, calling "func" for each thread. */ ++void _st_iterate_threads_helper(_st_func_ptr_t func) + { + static _st_thread_t *thread = NULL; + static jmp_buf orig_jb, save_jb; +@@ -944,16 +941,20 @@ + + if (thread) { + memcpy(thread->context, save_jb, sizeof(jmp_buf)); +- _st_show_thread_stack(thread, NULL); ++ func(thread, 0, 0); + } else { + if (MD_SETJMP(orig_jb)) { + _st_iterate_threads_flag = 0; ++ _st_iterate_func_ptr = NULL; + thread = NULL; +- _st_show_thread_stack(thread, "Iteration completed"); ++ /* Last thread to iterate through */ ++ func(thread, 0, 1); + return; + } ++ /* First thread to iterate through */ + thread = _ST_CURRENT_THREAD(); +- _st_show_thread_stack(thread, "Iteration started"); ++ _st_iterate_func_ptr = func; ++ func(thread, 1, 0); + } + + q = thread->tlink.next; +@@ -966,5 +967,17 @@ + memcpy(save_jb, thread->context, sizeof(jmp_buf)); + MD_LONGJMP(thread->context, 1); + } ++ ++/* ARGSUSED */ ++void _st_show_thread_stack(_st_thread_t *thread, int start_flag, int end_flag) ++{ ++} ++ ++/* Iterate over threads inside debugger; see st/README */ ++void _st_iterate_threads(void) ++{ ++ _st_iterate_threads_helper(_st_show_thread_stack); ++} ++ + #endif /* DEBUG */ + diff --git a/trunk/research/st-1.9/extensions/stx.h b/trunk/research/st-1.9/extensions/stx.h new file mode 100644 index 0000000000..8371e0d93c --- /dev/null +++ b/trunk/research/st-1.9/extensions/stx.h @@ -0,0 +1,91 @@ +#ifndef _STX_H_ +#define _STX_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "st.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +/***************************************** + * Basic types definitions + */ + +typedef struct _stx_centry stx_cache_entry_t; +typedef struct _stx_cache stx_cache_t; + +/* This is public type */ +typedef struct _stx_cache_info { + size_t max_size; + size_t max_weight; + size_t hash_size; + size_t cur_size; + size_t cur_weight; + unsigned long hits; + unsigned long lookups; + unsigned long inserts; + unsigned long deletes; +} stx_cache_info_t; + + +/***************************************** + * Cache and cache entry methods + */ + +stx_cache_t *stx_cache_create(size_t max_size, size_t max_weight, + size_t hash_size, + unsigned long (*key_hash_fn)(const void *key), + long (*key_cmp_fn)(const void *key1, + const void *key2), + void (*cleanup_fn)(void *key, void *data)); +void stx_cache_empty(stx_cache_t *cache); +void stx_cache_traverse(stx_cache_t *cache, + void (*callback)(void *key, void *data)); +void stx_cache_traverse_lru(stx_cache_t *, void (*)(void *, void *), + unsigned int); +void stx_cache_traverse_mru(stx_cache_t *, void (*)(void *, void *), + unsigned int); +void stx_cache_getinfo(stx_cache_t *cache, stx_cache_info_t *info); +size_t stx_cache_getsize(stx_cache_t *cache); +size_t stx_cache_getweight(stx_cache_t *cache); + + +stx_cache_entry_t *stx_cache_entry_create(void *key, void *data, + size_t weight); +void stx_cache_entry_delete(stx_cache_t *cache, stx_cache_entry_t *entry); +stx_cache_entry_t *stx_cache_entry_lookup(stx_cache_t *cache, const void *key); +void stx_cache_entry_release(stx_cache_t *, stx_cache_entry_t *); +int stx_cache_entry_insert(stx_cache_t *cache, stx_cache_entry_t *entry); +stx_cache_entry_t *stx_cache_entry_getlru(stx_cache_t *cache); +int stx_cache_entry_sizeof(void); +void *stx_cache_entry_getdata(stx_cache_entry_t *entry); +void *stx_cache_entry_getkey(stx_cache_entry_t *entry); +size_t stx_cache_entry_getweight(stx_cache_entry_t *entry); + + +int stx_dns_cache_init(size_t max_size, size_t max_bytes, size_t hash_size); +void stx_dns_cache_getinfo(stx_cache_info_t *info); +int stx_dns_getaddrlist(const char *hostname, struct in_addr *addrs, + int *num_addrs, st_utime_t timeout); +int stx_dns_getaddr(const char *hostname, struct in_addr *addr, + st_utime_t timeout); + +#ifdef __cplusplus +} +#endif + +#endif /* !_STX_H_ */ + diff --git a/trunk/research/st-1.9/extensions/stx_fileio.c b/trunk/research/st-1.9/extensions/stx_fileio.c new file mode 100644 index 0000000000..cb24346e8a --- /dev/null +++ b/trunk/research/st-1.9/extensions/stx_fileio.c @@ -0,0 +1,197 @@ +/* + * File I/O extension to the State Threads Library. + */ + +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the file I/O extension to the State Threads Library. + * + * The Initial Developer of the Original Code is Jeff + * . Portions created by the Initial + * Developer are Copyright (C) 2002 the Initial Developer. All Rights + * Reserved. + * + * Contributor(s): (none) + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#include + +#include "stx_fileio.h" + +#define STX_FILEIO_SIGNUM SIGUSR2 + +typedef struct { + st_netfd_t data_fd; + st_netfd_t control_fd; + pid_t pid; +} fileio_data_t; + +#define FILEREADER_MAX_READ 1024 + +typedef struct { + off_t offset; + ssize_t nbytes; +} file_reader_cb_t; + +/** + * Fork a process to read a file and return its pid. Receives + * offset/length commands from control stream and sends corresponding data + * to out stream. A zero length on the control stream signals an end. + * + * @param fd stream from which to read + * @param control_out receives the file descriptor to which control commands can be sent + * @param fd_out receives the file descriptor from which the output of the command can be read. + * @return PID of the process created to execute the command + */ +pid_t +file_reader(int fd, int *fd_control, int *fd_out) +{ + pid_t pid; + int control_pipe[2], out_pipe[2]; + + if (pipe(control_pipe) < 0 || pipe(out_pipe) < 0) + return (pid_t)-1; + + pid = fork(); + if (pid == (pid_t) -1) + { + close(control_pipe[0]); + close(control_pipe[1]); + close(out_pipe[0]); + close(out_pipe[1]); + return pid; + } + else if (pid == (pid_t) 0) + { + // child + off_t pos = 0; + file_reader_cb_t cb; + char buf[FILEREADER_MAX_READ]; + if (fd == -1) + _exit(EXIT_FAILURE); + + while (sizeof(cb) == read(control_pipe[0], &cb, sizeof(cb))) { + ssize_t nb; + if (0 >= cb.nbytes) + goto clean_exit; + if (pos != cb.offset) { + pos = lseek(fd, cb.offset, SEEK_SET); + if (pos == (off_t)-1) + break; + } + nb = read(fd, buf, cb.nbytes); + if (nb == (ssize_t)-1) + break; + pos += nb; + write(out_pipe[1], (char *)&nb, sizeof(nb)); + write(out_pipe[1], buf, nb); + } + perror("ERROR: file_reader: "); + clean_exit: + close(control_pipe[0]); + close(control_pipe[1]); + close(out_pipe[0]); + close(out_pipe[1]); + _exit(EXIT_SUCCESS); + } + + // parent + close(out_pipe[1]); + close(control_pipe[0]); + *fd_out = out_pipe[0]; + *fd_control = control_pipe[1]; + return pid; +} + +/** + * fileio_data_t destructor callback + */ +static void +fileio_data_destructor(void *dat_in) +{ + if (dat_in) { + fileio_data_t *dat = (fileio_data_t *)dat_in; + file_reader_cb_t cb; + cb.offset = 0; + cb.nbytes = 0; + st_write(dat->control_fd, (char *)&cb, sizeof(cb), + ST_UTIME_NO_TIMEOUT); + waitpid(dat->pid, NULL, 0); + st_netfd_close(dat->control_fd); + st_netfd_close(dat->data_fd); + free(dat_in); + } +} + +/** + * Retrieve fileio_data_t struct from an st descriptor. Create and store + * a new one if needed. + */ +static fileio_data_t *get_fileio_data(st_netfd_t fd) +{ + fileio_data_t *dat = (fileio_data_t *)st_netfd_getspecific(fd); + if (!dat) { + int fd_control, fd_out; + pid_t pid = file_reader(st_netfd_fileno(fd), &fd_control, &fd_out); + if (pid != (pid_t)-1) { + dat = (fileio_data_t *)calloc(1, sizeof(fileio_data_t)); + dat->control_fd = st_netfd_open(fd_control); + dat->data_fd = st_netfd_open(fd_out); + dat->pid = pid; + st_netfd_setspecific(fd, dat, fileio_data_destructor); + } + } + return dat; +} + +/** + * Read data from the specified section of a file. Uses a forked + * file_reader process to do the actual reading so as to avoid causing all + * State Threads to block. + * + * @param fd must refer to a seekable file. + * @param offset absolute offset within the file + * @param buf output buffer + * @param nbytes size of the output buffer + * @param timeout + */ +ssize_t +stx_file_read(st_netfd_t fd, off_t offset, void *buf, size_t nbytes, st_utime_t timeout) +{ + fileio_data_t *dat = get_fileio_data(fd); + if (dat) { + file_reader_cb_t cb; + ssize_t ret = (ssize_t)-1; + cb.offset = offset; + cb.nbytes = nbytes; + st_write(dat->control_fd, (char *)&cb, sizeof(cb), timeout); + if (sizeof(ret) == st_read(dat->data_fd, (char *)&ret, sizeof(ret), timeout) && 0 < ret && ret <= nbytes) { + return st_read(dat->data_fd, buf, ret, timeout); + } else { + return ret; + } + } + + return (ssize_t)-1; +} diff --git a/trunk/research/st-1.9/extensions/stx_fileio.h b/trunk/research/st-1.9/extensions/stx_fileio.h new file mode 100644 index 0000000000..b6bec190b7 --- /dev/null +++ b/trunk/research/st-1.9/extensions/stx_fileio.h @@ -0,0 +1,52 @@ +/* + * File I/O extension to the State Threads Library. + */ + +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the file I/O extension to the State Threads Library. + * + * The Initial Developer of the Original Code is Jeff + * . Portions created by the Initial + * Developer are Copyright (C) 2002 the Initial Developer. All Rights + * Reserved. + * + * Contributor(s): (none) + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#ifndef __STX_FILEIO_H__ +#define __STX_FILEIO_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern ssize_t stx_file_read(st_netfd_t fd, off_t offset, void *buf, size_t nbytes, st_utime_t timeout); + +#ifdef __cplusplus +} +#endif +#endif /* !__STX_FILEIO_H__ */ diff --git a/trunk/research/st-1.9/extensions/testdns.c b/trunk/research/st-1.9/extensions/testdns.c new file mode 100644 index 0000000000..aa896b25e5 --- /dev/null +++ b/trunk/research/st-1.9/extensions/testdns.c @@ -0,0 +1,112 @@ +#include "stx.h" +#include +#include + + +#define MAX_ADDRS 128 +#define TIMEOUT (4*1000000LL) + +static void do_resolve(const char *host) +{ + struct in_addr addrs[MAX_ADDRS]; + int i, n = MAX_ADDRS; + + if (stx_dns_getaddrlist(host, addrs, &n, TIMEOUT) < 0) { + fprintf(stderr, "stx_dns_getaddrlist: can't resolve %s: ", host); + if (h_errno == NETDB_INTERNAL) + perror(""); + else + herror(""); + } else { + if (n > 0) + printf("%-40s %s\n", (char *)host, inet_ntoa(addrs[0])); + for (i = 1; i < n; i++) + printf("%-40s %s\n", "", inet_ntoa(addrs[i])); + } +} + +static void show_info(void) +{ + stx_cache_info_t info; + + stx_dns_cache_getinfo(&info); + printf("DNS cache info:\n\n"); + printf("max_size: %8d\n", (int)info.max_size); + printf("capacity: %8d bytes\n", (int)info.max_weight); + printf("hash_size: %8d\n", (int)info.hash_size); + printf("cur_size: %8d\n" + "cur_mem: %8d bytes\n" + "hits: %8d\n" + "lookups: %8d\n" + "inserts: %8d\n" + "deletes: %8d\n", + (int)info.cur_size, (int)info.cur_weight, (int)info.hits, + (int)info.lookups, (int)info.inserts, (int)info.deletes); +} + +extern stx_cache_t *_stx_dns_cache; + +static void printhost(void *host, void *data) +{ + printf("%s\n", (char *)host); +} + +static void show_lru(void) +{ + printf("LRU hosts:\n\n"); + stx_cache_traverse_lru(_stx_dns_cache, printhost, 10); +} + +static void show_mru(void) +{ + printf("MRU hosts:\n\n"); + stx_cache_traverse_mru(_stx_dns_cache, printhost, 10); +} + +static void flush_cache(void) +{ + stx_cache_empty(_stx_dns_cache); + printf("DNS cache is empty\n"); +} + + +int main() +{ + char line[256]; + char str[sizeof(line)]; + + st_init(); + stx_dns_cache_init(100, 10000, 101); + + for ( ; ; ) { + fputs("> ", stdout); + fflush(stdout); + if (!fgets(line, sizeof(line), stdin)) + break; + if (sscanf(line, "%s", str) != 1) + continue; + if (strcmp(str, "exit") == 0 || strcmp(str, "quit") == 0) + break; + if (strcmp(str, "info") == 0) { + show_info(); + continue; + } + if (strcmp(str, "lru") == 0) { + show_lru(); + continue; + } + if (strcmp(str, "mru") == 0) { + show_mru(); + continue; + } + if (strcmp(str, "flush") == 0) { + flush_cache(); + continue; + } + + do_resolve(str); + } + + return 0; +} + diff --git a/trunk/research/st-1.9/io.c b/trunk/research/st-1.9/io.c new file mode 100644 index 0000000000..f95ff8c6fa --- /dev/null +++ b/trunk/research/st-1.9/io.c @@ -0,0 +1,778 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Portable Runtime library. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Silicon Graphics, Inc. + * + * Portions created by SGI are Copyright (C) 2000-2001 Silicon + * Graphics, Inc. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +/* + * This file is derived directly from Netscape Communications Corporation, + * and consists of extensive modifications made during the year(s) 1999-2000. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "common.h" + + +#if EAGAIN != EWOULDBLOCK +#define _IO_NOT_READY_ERROR ((errno == EAGAIN) || (errno == EWOULDBLOCK)) +#else +#define _IO_NOT_READY_ERROR (errno == EAGAIN) +#endif + +#define _LOCAL_MAXIOV 16 + +/* File descriptor object free list */ +static _st_netfd_t *_st_netfd_freelist = NULL; +/* Maximum number of file descriptors that the process can open */ +static int _st_osfd_limit = -1; + +static void _st_netfd_free_aux_data(_st_netfd_t *fd); + +int _st_io_init(void) +{ + struct sigaction sigact; + struct rlimit rlim; + int fdlim; + + /* Ignore SIGPIPE */ + sigact.sa_handler = SIG_IGN; + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + if (sigaction(SIGPIPE, &sigact, NULL) < 0) + return -1; + + /* Set maximum number of open file descriptors */ + if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) + return -1; + + fdlim = (*_st_eventsys->fd_getlimit)(); + if (fdlim > 0 && rlim.rlim_max > (rlim_t) fdlim) { + rlim.rlim_max = fdlim; + } + rlim.rlim_cur = rlim.rlim_max; + if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) + return -1; + _st_osfd_limit = (int) rlim.rlim_max; + + return 0; +} + + +int st_getfdlimit(void) +{ + return _st_osfd_limit; +} + + +void st_netfd_free(_st_netfd_t *fd) +{ + if (!fd->inuse) + return; + + fd->inuse = 0; + if (fd->aux_data) + _st_netfd_free_aux_data(fd); + if (fd->private_data && fd->destructor) + (*(fd->destructor))(fd->private_data); + fd->private_data = NULL; + fd->destructor = NULL; + fd->next = _st_netfd_freelist; + _st_netfd_freelist = fd; +} + + +static _st_netfd_t *_st_netfd_new(int osfd, int nonblock, int is_socket) +{ + _st_netfd_t *fd; + int flags = 1; + + if ((*_st_eventsys->fd_new)(osfd) < 0) + return NULL; + + if (_st_netfd_freelist) { + fd = _st_netfd_freelist; + _st_netfd_freelist = _st_netfd_freelist->next; + } else { + fd = calloc(1, sizeof(_st_netfd_t)); + if (!fd) + return NULL; + } + + fd->osfd = osfd; + fd->inuse = 1; + fd->next = NULL; + + if (nonblock) { + /* Use just one system call */ + if (is_socket && ioctl(osfd, FIONBIO, &flags) != -1) + return fd; + /* Do it the Posix way */ + if ((flags = fcntl(osfd, F_GETFL, 0)) < 0 || + fcntl(osfd, F_SETFL, flags | O_NONBLOCK) < 0) { + st_netfd_free(fd); + return NULL; + } + } + + return fd; +} + + +_st_netfd_t *st_netfd_open(int osfd) +{ + return _st_netfd_new(osfd, 1, 0); +} + + +_st_netfd_t *st_netfd_open_socket(int osfd) +{ + return _st_netfd_new(osfd, 1, 1); +} + + +int st_netfd_close(_st_netfd_t *fd) +{ + if ((*_st_eventsys->fd_close)(fd->osfd) < 0) + return -1; + + st_netfd_free(fd); + return close(fd->osfd); +} + + +int st_netfd_fileno(_st_netfd_t *fd) +{ + return (fd->osfd); +} + + +void st_netfd_setspecific(_st_netfd_t *fd, void *value, + _st_destructor_t destructor) +{ + if (value != fd->private_data) { + /* Free up previously set non-NULL data value */ + if (fd->private_data && fd->destructor) + (*(fd->destructor))(fd->private_data); + } + fd->private_data = value; + fd->destructor = destructor; +} + + +void *st_netfd_getspecific(_st_netfd_t *fd) +{ + return (fd->private_data); +} + + +/* + * Wait for I/O on a single descriptor. + */ +int st_netfd_poll(_st_netfd_t *fd, int how, st_utime_t timeout) +{ + struct pollfd pd; + int n; + + pd.fd = fd->osfd; + pd.events = (short) how; + pd.revents = 0; + + if ((n = st_poll(&pd, 1, timeout)) < 0) + return -1; + if (n == 0) { + /* Timed out */ + errno = ETIME; + return -1; + } + if (pd.revents & POLLNVAL) { + errno = EBADF; + return -1; + } + + return 0; +} + + +#ifdef MD_ALWAYS_UNSERIALIZED_ACCEPT +/* No-op */ +int st_netfd_serialize_accept(_st_netfd_t *fd) +{ + fd->aux_data = NULL; + return 0; +} + +/* No-op */ +static void _st_netfd_free_aux_data(_st_netfd_t *fd) +{ + fd->aux_data = NULL; +} + +_st_netfd_t *st_accept(_st_netfd_t *fd, struct sockaddr *addr, int *addrlen, + st_utime_t timeout) +{ + int osfd, err; + _st_netfd_t *newfd; + + while ((osfd = accept(fd->osfd, addr, (socklen_t *)addrlen)) < 0) { + if (errno == EINTR) + continue; + if (!_IO_NOT_READY_ERROR) + return NULL; + /* Wait until the socket becomes readable */ + if (st_netfd_poll(fd, POLLIN, timeout) < 0) + return NULL; + } + + /* On some platforms the new socket created by accept() inherits */ + /* the nonblocking attribute of the listening socket */ +#if defined (MD_ACCEPT_NB_INHERITED) + newfd = _st_netfd_new(osfd, 0, 1); +#elif defined (MD_ACCEPT_NB_NOT_INHERITED) + newfd = _st_netfd_new(osfd, 1, 1); +#else +#error Unknown OS +#endif + + if (!newfd) { + err = errno; + close(osfd); + errno = err; + } + + return newfd; +} + +#else /* MD_ALWAYS_UNSERIALIZED_ACCEPT */ +/* + * On some platforms accept() calls from different processes + * on the same listen socket must be serialized. + * The following code serializes accept()'s without process blocking. + * A pipe is used as an inter-process semaphore. + */ +int st_netfd_serialize_accept(_st_netfd_t *fd) +{ + _st_netfd_t **p; + int osfd[2], err; + + if (fd->aux_data) { + errno = EINVAL; + return -1; + } + if ((p = (_st_netfd_t **)calloc(2, sizeof(_st_netfd_t *))) == NULL) + return -1; + if (pipe(osfd) < 0) { + free(p); + return -1; + } + if ((p[0] = st_netfd_open(osfd[0])) != NULL && + (p[1] = st_netfd_open(osfd[1])) != NULL && + write(osfd[1], " ", 1) == 1) { + fd->aux_data = p; + return 0; + } + /* Error */ + err = errno; + if (p[0]) + st_netfd_free(p[0]); + if (p[1]) + st_netfd_free(p[1]); + close(osfd[0]); + close(osfd[1]); + free(p); + errno = err; + + return -1; +} + +static void _st_netfd_free_aux_data(_st_netfd_t *fd) +{ + _st_netfd_t **p = (_st_netfd_t **) fd->aux_data; + + st_netfd_close(p[0]); + st_netfd_close(p[1]); + free(p); + fd->aux_data = NULL; +} + +_st_netfd_t *st_accept(_st_netfd_t *fd, struct sockaddr *addr, int *addrlen, + st_utime_t timeout) +{ + int osfd, err; + _st_netfd_t *newfd; + _st_netfd_t **p = (_st_netfd_t **) fd->aux_data; + ssize_t n; + char c; + + for ( ; ; ) { + if (p == NULL) { + osfd = accept(fd->osfd, addr, (socklen_t *)addrlen); + } else { + /* Get the lock */ + n = st_read(p[0], &c, 1, timeout); + if (n < 0) + return NULL; + ST_ASSERT(n == 1); + /* Got the lock */ + osfd = accept(fd->osfd, addr, (socklen_t *)addrlen); + /* Unlock */ + err = errno; + n = st_write(p[1], &c, 1, timeout); + ST_ASSERT(n == 1); + errno = err; + } + if (osfd >= 0) + break; + if (errno == EINTR) + continue; + if (!_IO_NOT_READY_ERROR) + return NULL; + /* Wait until the socket becomes readable */ + if (st_netfd_poll(fd, POLLIN, timeout) < 0) + return NULL; + } + + /* On some platforms the new socket created by accept() inherits */ + /* the nonblocking attribute of the listening socket */ +#if defined (MD_ACCEPT_NB_INHERITED) + newfd = _st_netfd_new(osfd, 0, 1); +#elif defined (MD_ACCEPT_NB_NOT_INHERITED) + newfd = _st_netfd_new(osfd, 1, 1); +#else +#error Unknown OS +#endif + + if (!newfd) { + err = errno; + close(osfd); + errno = err; + } + + return newfd; +} +#endif /* MD_ALWAYS_UNSERIALIZED_ACCEPT */ + + +int st_connect(_st_netfd_t *fd, const struct sockaddr *addr, int addrlen, + st_utime_t timeout) +{ + int n, err = 0; + + while (connect(fd->osfd, addr, addrlen) < 0) { + if (errno != EINTR) { + /* + * On some platforms, if connect() is interrupted (errno == EINTR) + * after the kernel binds the socket, a subsequent connect() + * attempt will fail with errno == EADDRINUSE. Ignore EADDRINUSE + * iff connect() was previously interrupted. See Rich Stevens' + * "UNIX Network Programming," Vol. 1, 2nd edition, p. 413 + * ("Interrupted connect"). + */ + if (errno != EINPROGRESS && (errno != EADDRINUSE || err == 0)) + return -1; + /* Wait until the socket becomes writable */ + if (st_netfd_poll(fd, POLLOUT, timeout) < 0) + return -1; + /* Try to find out whether the connection setup succeeded or failed */ + n = sizeof(int); + if (getsockopt(fd->osfd, SOL_SOCKET, SO_ERROR, (char *)&err, + (socklen_t *)&n) < 0) + return -1; + if (err) { + errno = err; + return -1; + } + break; + } + err = 1; + } + + return 0; +} + + +ssize_t st_read(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout) +{ + ssize_t n; + + while ((n = read(fd->osfd, buf, nbyte)) < 0) { + if (errno == EINTR) + continue; + if (!_IO_NOT_READY_ERROR) + return -1; + /* Wait until the socket becomes readable */ + if (st_netfd_poll(fd, POLLIN, timeout) < 0) + return -1; + } + + return n; +} + + +int st_read_resid(_st_netfd_t *fd, void *buf, size_t *resid, + st_utime_t timeout) +{ + struct iovec iov, *riov; + int riov_size, rv; + + iov.iov_base = buf; + iov.iov_len = *resid; + riov = &iov; + riov_size = 1; + rv = st_readv_resid(fd, &riov, &riov_size, timeout); + *resid = iov.iov_len; + return rv; +} + + +ssize_t st_readv(_st_netfd_t *fd, const struct iovec *iov, int iov_size, + st_utime_t timeout) +{ + ssize_t n; + + while ((n = readv(fd->osfd, iov, iov_size)) < 0) { + if (errno == EINTR) + continue; + if (!_IO_NOT_READY_ERROR) + return -1; + /* Wait until the socket becomes readable */ + if (st_netfd_poll(fd, POLLIN, timeout) < 0) + return -1; + } + + return n; +} + +int st_readv_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, + st_utime_t timeout) +{ + ssize_t n; + + while (*iov_size > 0) { + if (*iov_size == 1) + n = read(fd->osfd, (*iov)->iov_base, (*iov)->iov_len); + else + n = readv(fd->osfd, *iov, *iov_size); + if (n < 0) { + if (errno == EINTR) + continue; + if (!_IO_NOT_READY_ERROR) + return -1; + } else if (n == 0) + break; + else { + while ((size_t) n >= (*iov)->iov_len) { + n -= (*iov)->iov_len; + (*iov)->iov_base = (char *) (*iov)->iov_base + (*iov)->iov_len; + (*iov)->iov_len = 0; + (*iov)++; + (*iov_size)--; + if (n == 0) + break; + } + if (*iov_size == 0) + break; + (*iov)->iov_base = (char *) (*iov)->iov_base + n; + (*iov)->iov_len -= n; + } + /* Wait until the socket becomes readable */ + if (st_netfd_poll(fd, POLLIN, timeout) < 0) + return -1; + } + + return 0; +} + + +ssize_t st_read_fully(_st_netfd_t *fd, void *buf, size_t nbyte, + st_utime_t timeout) +{ + size_t resid = nbyte; + return st_read_resid(fd, buf, &resid, timeout) == 0 ? + (ssize_t) (nbyte - resid) : -1; +} + + +int st_write_resid(_st_netfd_t *fd, const void *buf, size_t *resid, + st_utime_t timeout) +{ + struct iovec iov, *riov; + int riov_size, rv; + + iov.iov_base = (void *) buf; /* we promise not to modify buf */ + iov.iov_len = *resid; + riov = &iov; + riov_size = 1; + rv = st_writev_resid(fd, &riov, &riov_size, timeout); + *resid = iov.iov_len; + return rv; +} + + +ssize_t st_write(_st_netfd_t *fd, const void *buf, size_t nbyte, + st_utime_t timeout) +{ + size_t resid = nbyte; + return st_write_resid(fd, buf, &resid, timeout) == 0 ? + (ssize_t) (nbyte - resid) : -1; +} + + +ssize_t st_writev(_st_netfd_t *fd, const struct iovec *iov, int iov_size, + st_utime_t timeout) +{ + ssize_t n, rv; + size_t nleft, nbyte; + int index, iov_cnt; + struct iovec *tmp_iov; + struct iovec local_iov[_LOCAL_MAXIOV]; + + /* Calculate the total number of bytes to be sent */ + nbyte = 0; + for (index = 0; index < iov_size; index++) + nbyte += iov[index].iov_len; + + rv = (ssize_t)nbyte; + nleft = nbyte; + tmp_iov = (struct iovec *) iov; /* we promise not to modify iov */ + iov_cnt = iov_size; + + while (nleft > 0) { + if (iov_cnt == 1) { + if (st_write(fd, tmp_iov[0].iov_base, nleft, timeout) != (ssize_t) nleft) + rv = -1; + break; + } + if ((n = writev(fd->osfd, tmp_iov, iov_cnt)) < 0) { + if (errno == EINTR) + continue; + if (!_IO_NOT_READY_ERROR) { + rv = -1; + break; + } + } else { + if ((size_t) n == nleft) + break; + nleft -= n; + /* Find the next unwritten vector */ + n = (ssize_t)(nbyte - nleft); + for (index = 0; (size_t) n >= iov[index].iov_len; index++) + n -= iov[index].iov_len; + + if (tmp_iov == iov) { + /* Must copy iov's around */ + if (iov_size - index <= _LOCAL_MAXIOV) { + tmp_iov = local_iov; + } else { + tmp_iov = calloc(1, (iov_size - index) * sizeof(struct iovec)); + if (tmp_iov == NULL) + return -1; + } + } + + /* Fill in the first partial read */ + tmp_iov[0].iov_base = &(((char *)iov[index].iov_base)[n]); + tmp_iov[0].iov_len = iov[index].iov_len - n; + index++; + /* Copy the remaining vectors */ + for (iov_cnt = 1; index < iov_size; iov_cnt++, index++) { + tmp_iov[iov_cnt].iov_base = iov[index].iov_base; + tmp_iov[iov_cnt].iov_len = iov[index].iov_len; + } + } + /* Wait until the socket becomes writable */ + if (st_netfd_poll(fd, POLLOUT, timeout) < 0) { + rv = -1; + break; + } + } + + if (tmp_iov != iov && tmp_iov != local_iov) + free(tmp_iov); + + return rv; +} + + +int st_writev_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, + st_utime_t timeout) +{ + ssize_t n; + + while (*iov_size > 0) { + if (*iov_size == 1) + n = write(fd->osfd, (*iov)->iov_base, (*iov)->iov_len); + else + n = writev(fd->osfd, *iov, *iov_size); + if (n < 0) { + if (errno == EINTR) + continue; + if (!_IO_NOT_READY_ERROR) + return -1; + } else { + while ((size_t) n >= (*iov)->iov_len) { + n -= (*iov)->iov_len; + (*iov)->iov_base = (char *) (*iov)->iov_base + (*iov)->iov_len; + (*iov)->iov_len = 0; + (*iov)++; + (*iov_size)--; + if (n == 0) + break; + } + if (*iov_size == 0) + break; + (*iov)->iov_base = (char *) (*iov)->iov_base + n; + (*iov)->iov_len -= n; + } + /* Wait until the socket becomes writable */ + if (st_netfd_poll(fd, POLLOUT, timeout) < 0) + return -1; + } + + return 0; +} + + +/* + * Simple I/O functions for UDP. + */ +int st_recvfrom(_st_netfd_t *fd, void *buf, int len, struct sockaddr *from, + int *fromlen, st_utime_t timeout) +{ + int n; + + while ((n = recvfrom(fd->osfd, buf, len, 0, from, (socklen_t *)fromlen)) + < 0) { + if (errno == EINTR) + continue; + if (!_IO_NOT_READY_ERROR) + return -1; + /* Wait until the socket becomes readable */ + if (st_netfd_poll(fd, POLLIN, timeout) < 0) + return -1; + } + + return n; +} + + +int st_sendto(_st_netfd_t *fd, const void *msg, int len, + const struct sockaddr *to, int tolen, st_utime_t timeout) +{ + int n; + + while ((n = sendto(fd->osfd, msg, len, 0, to, tolen)) < 0) { + if (errno == EINTR) + continue; + if (!_IO_NOT_READY_ERROR) + return -1; + /* Wait until the socket becomes writable */ + if (st_netfd_poll(fd, POLLOUT, timeout) < 0) + return -1; + } + + return n; +} + + +int st_recvmsg(_st_netfd_t *fd, struct msghdr *msg, int flags, + st_utime_t timeout) +{ + int n; + + while ((n = recvmsg(fd->osfd, msg, flags)) < 0) { + if (errno == EINTR) + continue; + if (!_IO_NOT_READY_ERROR) + return -1; + /* Wait until the socket becomes readable */ + if (st_netfd_poll(fd, POLLIN, timeout) < 0) + return -1; + } + + return n; +} + + +int st_sendmsg(_st_netfd_t *fd, const struct msghdr *msg, int flags, + st_utime_t timeout) +{ + int n; + + while ((n = sendmsg(fd->osfd, msg, flags)) < 0) { + if (errno == EINTR) + continue; + if (!_IO_NOT_READY_ERROR) + return -1; + /* Wait until the socket becomes writable */ + if (st_netfd_poll(fd, POLLOUT, timeout) < 0) + return -1; + } + + return n; +} + + + +/* + * To open FIFOs or other special files. + */ +_st_netfd_t *st_open(const char *path, int oflags, mode_t mode) +{ + int osfd, err; + _st_netfd_t *newfd; + + while ((osfd = open(path, oflags | O_NONBLOCK, mode)) < 0) { + if (errno != EINTR) + return NULL; + } + + newfd = _st_netfd_new(osfd, 0, 0); + if (!newfd) { + err = errno; + close(osfd); + errno = err; + } + + return newfd; +} + diff --git a/trunk/research/st-1.9/key.c b/trunk/research/st-1.9/key.c new file mode 100644 index 0000000000..9708c355d8 --- /dev/null +++ b/trunk/research/st-1.9/key.c @@ -0,0 +1,121 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Portable Runtime library. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Silicon Graphics, Inc. + * + * Portions created by SGI are Copyright (C) 2000-2001 Silicon + * Graphics, Inc. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +/* + * This file is derived directly from Netscape Communications Corporation, + * and consists of extensive modifications made during the year(s) 1999-2000. + */ + +#include +#include +#include "common.h" + + +/* + * Destructor table for per-thread private data + */ +static _st_destructor_t _st_destructors[ST_KEYS_MAX]; +static int key_max = 0; + + +/* + * Return a key to be used for thread specific data + */ +int st_key_create(int *keyp, _st_destructor_t destructor) +{ + if (key_max >= ST_KEYS_MAX) { + errno = EAGAIN; + return -1; + } + + *keyp = key_max++; + _st_destructors[*keyp] = destructor; + + return 0; +} + + +int st_key_getlimit(void) +{ + return ST_KEYS_MAX; +} + + +int st_thread_setspecific(int key, void *value) +{ + _st_thread_t *me = _ST_CURRENT_THREAD(); + + if (key < 0 || key >= key_max) { + errno = EINVAL; + return -1; + } + + if (value != me->private_data[key]) { + /* free up previously set non-NULL data value */ + if (me->private_data[key] && _st_destructors[key]) { + (*_st_destructors[key])(me->private_data[key]); + } + me->private_data[key] = value; + } + + return 0; +} + + +void *st_thread_getspecific(int key) +{ + if (key < 0 || key >= key_max) + return NULL; + + return ((_ST_CURRENT_THREAD())->private_data[key]); +} + + +/* + * Free up all per-thread private data + */ +void _st_thread_cleanup(_st_thread_t *thread) +{ + int key; + + for (key = 0; key < key_max; key++) { + if (thread->private_data[key] && _st_destructors[key]) { + (*_st_destructors[key])(thread->private_data[key]); + thread->private_data[key] = NULL; + } + } +} + diff --git a/trunk/research/st-1.9/libst.def b/trunk/research/st-1.9/libst.def new file mode 100644 index 0000000000..6eaf149a97 --- /dev/null +++ b/trunk/research/st-1.9/libst.def @@ -0,0 +1,51 @@ +EXPORTS + st_accept @62 + st_cond_broadcast @63 + st_cond_destroy @64 + st_cond_new @65 + st_cond_signal @66 + st_cond_timedwait @67 + st_cond_wait @68 + st_connect @69 + st_getfdlimit @70 + st_init @71 + st_key_create @72 + st_key_getlimit @73 + st_mutex_destroy @74 + st_mutex_lock @75 + st_mutex_new @76 + st_mutex_trylock @77 + st_mutex_unlock @78 + st_netfd_close @79 + st_netfd_fileno @80 + st_netfd_free @81 + st_netfd_getspecific @82 + st_netfd_open @83 + st_netfd_open_socket @84 + st_netfd_poll @85 + st_netfd_serialize_accept @86 + st_netfd_setspecific @87 + st_open @88 + st_poll @89 + st_randomize_stacks @90 + st_read @91 + st_read_fully @92 + st_read_resid @93 + st_recvfrom @94 + st_sendto @95 + st_sleep @96 + st_thread_create @97 + st_thread_exit @98 + st_thread_getspecific @99 + st_thread_interrupt @100 + st_thread_join @101 + st_thread_self @102 + st_thread_setspecific @103 + st_time @104 + st_timecache_set @105 + st_usleep @106 + st_utime @107 + st_utime_last_clock @108 + st_write @109 + st_write_resid @110 + st_writev @111 diff --git a/trunk/research/st-1.9/md.S b/trunk/research/st-1.9/md.S new file mode 100644 index 0000000000..ab4f7b5577 --- /dev/null +++ b/trunk/research/st-1.9/md.S @@ -0,0 +1,431 @@ +/* + * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. + * All Rights Reserved. + */ + +#if defined(__ia64__) + +/* + * The internal __jmp_buf layout is different from one used + * by setjmp()/longjmp(). + * + * Offset Description + * ------ ----------- + * 0x000 stack pointer (r12) + * 0x008 gp (r1) + * 0x010 caller's unat + * 0x018 fpsr + * 0x020 r4 + * 0x028 r5 + * 0x030 r6 + * 0x038 r7 + * 0x040 rp (b0) + * 0x048 b1 + * 0x050 b2 + * 0x058 b3 + * 0x060 b4 + * 0x068 b5 + * 0x070 ar.pfs + * 0x078 ar.lc + * 0x080 pr + * 0x088 ar.bsp + * 0x090 ar.unat + * 0x098 &__jmp_buf + * 0x0a0 ar.rsc + * 0x0a8 ar.rnat + * 0x0b0 f2 + * 0x0c0 f3 + * 0x0d0 f4 + * 0x0e0 f5 + * 0x0f0 f16 + * 0x100 f17 + * 0x110 f18 + * 0x120 f19 + * 0x130 f20 + * 0x130 f21 + * 0x140 f22 + * 0x150 f23 + * 0x160 f24 + * 0x170 f25 + * 0x180 f26 + * 0x190 f27 + * 0x1a0 f28 + * 0x1b0 f29 + * 0x1c0 f30 + * 0x1d0 f31 + * + * Note that the address of __jmp_buf is saved but not used: we assume + * that the jmp_buf data structure is never moved around in memory. + */ + +/* + * Implemented according to "IA-64 Software Conventions and Runtime + * Architecture Guide", Chapter 10: "Context Management". + */ + + .text + .psr abi64 + .psr lsb + .lsb + + /* _st_md_cxt_save(__jmp_buf env) */ + .align 32 + .global _st_md_cxt_save + .proc _st_md_cxt_save +_st_md_cxt_save: + alloc r14 = ar.pfs,1,0,0,0 + mov r16 = ar.unat + ;; + mov r17 = ar.fpsr + mov r2 = in0 + add r3 = 8,in0 + ;; + st8.spill.nta [r2] = sp,16 // r12 (sp) + ;; + st8.spill.nta [r3] = gp,16 // r1 (gp) + ;; + st8.nta [r2] = r16,16 // save caller's unat + st8.nta [r3] = r17,16 // save fpsr + add r8 = 0xb0,in0 + ;; + st8.spill.nta [r2] = r4,16 // r4 + ;; + st8.spill.nta [r3] = r5,16 // r5 + add r9 = 0xc0,in0 + ;; + stf.spill.nta [r8] = f2,32 + stf.spill.nta [r9] = f3,32 + mov r15 = rp + ;; + stf.spill.nta [r8] = f4,32 + stf.spill.nta [r9] = f5,32 + mov r17 = b1 + ;; + stf.spill.nta [r8] = f16,32 + stf.spill.nta [r9] = f17,32 + mov r18 = b2 + ;; + stf.spill.nta [r8] = f18,32 + stf.spill.nta [r9] = f19,32 + mov r19 = b3 + ;; + stf.spill.nta [r8] = f20,32 + stf.spill.nta [r9] = f21,32 + mov r20 = b4 + ;; + stf.spill.nta [r8] = f22,32 + stf.spill.nta [r9] = f23,32 + mov r21 = b5 + ;; + stf.spill.nta [r8] = f24,32 + stf.spill.nta [r9] = f25,32 + mov r22 = ar.lc + ;; + stf.spill.nta [r8] = f26,32 + stf.spill.nta [r9] = f27,32 + mov r24 = pr + ;; + stf.spill.nta [r8] = f28,32 + stf.spill.nta [r9] = f29,32 + ;; + stf.spill.nta [r8] = f30 + stf.spill.nta [r9] = f31 + + st8.spill.nta [r2] = r6,16 // r6 + ;; + st8.spill.nta [r3] = r7,16 // r7 + ;; + mov r23 = ar.bsp + mov r25 = ar.unat + + st8.nta [r2] = r15,16 // b0 + st8.nta [r3] = r17,16 // b1 + ;; + st8.nta [r2] = r18,16 // b2 + st8.nta [r3] = r19,16 // b3 + mov r26 = ar.rsc + ;; + st8.nta [r2] = r20,16 // b4 + st8.nta [r3] = r21,16 // b5 + ;; + st8.nta [r2] = r14,16 // ar.pfs + st8.nta [r3] = r22,16 // ar.lc + ;; + st8.nta [r2] = r24,16 // pr + st8.nta [r3] = r23,16 // ar.bsp + ;; + st8.nta [r2] = r25,16 // ar.unat + st8.nta [r3] = in0,16 // &__jmp_buf (just in case) + ;; + st8.nta [r2] = r26 // ar.rsc + ;; + flushrs // flush dirty regs to backing store + ;; + and r27 = ~0x3,r26 // clear ar.rsc.mode + ;; + mov ar.rsc = r27 // put RSE in enforced lazy mode + ;; + mov r28 = ar.rnat + ;; + st8.nta [r3] = r28 // ar.rnat + mov ar.rsc = r26 // restore ar.rsc + ;; + mov r8 = 0 + br.ret.sptk.few b0 + .endp _st_md_cxt_save + + +/****************************************************************/ + + /* _st_md_cxt_restore(__jmp_buf env, int val) */ + .global _st_md_cxt_restore + .proc _st_md_cxt_restore +_st_md_cxt_restore: + alloc r8 = ar.pfs,2,0,0,0 + add r2 = 0x88,in0 // r2 <- &jmpbuf.ar_bsp + mov r16 = ar.rsc + ;; + flushrs // flush dirty regs to backing store + ;; + and r17 = ~0x3,r16 // clear ar.rsc.mode + ;; + mov ar.rsc = r17 // put RSE in enforced lazy mode + ;; + invala // invalidate the ALAT + ;; + ld8 r23 = [r2],8 // r23 <- jmpbuf.ar_bsp + ;; + mov ar.bspstore = r23 // write BSPSTORE + ld8 r25 = [r2],24 // r25 <- jmpbuf.ar_unat + ;; + ld8 r26 = [r2],-8 // r26 <- jmpbuf.ar_rnat + ;; + mov ar.rnat = r26 // write RNAT + ld8 r27 = [r2] // r27 <- jmpbuf.ar_rsc + ;; + mov ar.rsc = r27 // write RSE control + mov r2 = in0 + ;; + mov ar.unat = r25 // write ar.unat + add r3 = 8,in0 + ;; + ld8.fill.nta sp = [r2],16 // r12 (sp) + ld8.fill.nta gp = [r3],16 // r1 (gp) + ;; + ld8.nta r16 = [r2],16 // caller's unat + ld8.nta r17 = [r3],16 // fpsr + ;; + ld8.fill.nta r4 = [r2],16 // r4 + ld8.fill.nta r5 = [r3],16 // r5 + ;; + ld8.fill.nta r6 = [r2],16 // r6 + ld8.fill.nta r7 = [r3],16 // r7 + ;; + mov ar.unat = r16 // restore caller's unat + mov ar.fpsr = r17 // restore fpsr + ;; + ld8.nta r16 = [r2],16 // b0 + ld8.nta r17 = [r3],16 // b1 + ;; + ld8.nta r18 = [r2],16 // b2 + ld8.nta r19 = [r3],16 // b3 + ;; + ld8.nta r20 = [r2],16 // b4 + ld8.nta r21 = [r3],16 // b5 + ;; + ld8.nta r11 = [r2],16 // ar.pfs + ld8.nta r22 = [r3],72 // ar.lc + ;; + ld8.nta r24 = [r2],48 // pr + mov b0 = r16 + ;; + ldf.fill.nta f2 = [r2],32 + ldf.fill.nta f3 = [r3],32 + mov b1 = r17 + ;; + ldf.fill.nta f4 = [r2],32 + ldf.fill.nta f5 = [r3],32 + mov b2 = r18 + ;; + ldf.fill.nta f16 = [r2],32 + ldf.fill.nta f17 = [r3],32 + mov b3 = r19 + ;; + ldf.fill.nta f18 = [r2],32 + ldf.fill.nta f19 = [r3],32 + mov b4 = r20 + ;; + ldf.fill.nta f20 = [r2],32 + ldf.fill.nta f21 = [r3],32 + mov b5 = r21 + ;; + ldf.fill.nta f22 = [r2],32 + ldf.fill.nta f23 = [r3],32 + mov ar.lc = r22 + ;; + ldf.fill.nta f24 = [r2],32 + ldf.fill.nta f25 = [r3],32 + cmp.eq p6,p7 = 0,in1 + ;; + ldf.fill.nta f26 = [r2],32 + ldf.fill.nta f27 = [r3],32 + mov ar.pfs = r11 + ;; + ldf.fill.nta f28 = [r2],32 + ldf.fill.nta f29 = [r3],32 + ;; + ldf.fill.nta f30 = [r2] + ldf.fill.nta f31 = [r3] +(p6) mov r8 = 1 +(p7) mov r8 = in1 + + mov pr = r24,-1 + br.ret.sptk.few b0 + .endp _st_md_cxt_restore + +/****************************************************************/ + +#elif defined(__i386__) + +/* + * Internal __jmp_buf layout + */ +#define JB_BX 0 +#define JB_SI 1 +#define JB_DI 2 +#define JB_BP 3 +#define JB_SP 4 +#define JB_PC 5 + + .file "md.S" + .text + + /* _st_md_cxt_save(__jmp_buf env) */ +.globl _st_md_cxt_save + .type _st_md_cxt_save, @function + .align 16 +_st_md_cxt_save: + movl 4(%esp), %eax + + /* + * Save registers. + */ + movl %ebx, (JB_BX*4)(%eax) + movl %esi, (JB_SI*4)(%eax) + movl %edi, (JB_DI*4)(%eax) + /* Save SP */ + leal 4(%esp), %ecx + movl %ecx, (JB_SP*4)(%eax) + /* Save PC we are returning to */ + movl 0(%esp), %ecx + movl %ecx, (JB_PC*4)(%eax) + /* Save caller frame pointer */ + movl %ebp, (JB_BP*4)(%eax) + xorl %eax, %eax + ret + .size _st_md_cxt_save, .-_st_md_cxt_save + + +/****************************************************************/ + + /* _st_md_cxt_restore(__jmp_buf env, int val) */ +.globl _st_md_cxt_restore + .type _st_md_cxt_restore, @function + .align 16 +_st_md_cxt_restore: + /* First argument is jmp_buf */ + movl 4(%esp), %ecx + /* Second argument is return value */ + movl 8(%esp), %eax + /* Set the return address */ + movl (JB_PC*4)(%ecx), %edx + /* + * Restore registers. + */ + movl (JB_BX*4)(%ecx), %ebx + movl (JB_SI*4)(%ecx), %esi + movl (JB_DI*4)(%ecx), %edi + movl (JB_BP*4)(%ecx), %ebp + movl (JB_SP*4)(%ecx), %esp + testl %eax, %eax + jnz 1f + incl %eax + /* Jump to saved PC */ +1: jmp *%edx + .size _st_md_cxt_restore, .-_st_md_cxt_restore + +/****************************************************************/ + +#elif defined(__amd64__) || defined(__x86_64__) + +/* + * Internal __jmp_buf layout + */ +#define JB_RBX 0 +#define JB_RBP 1 +#define JB_R12 2 +#define JB_R13 3 +#define JB_R14 4 +#define JB_R15 5 +#define JB_RSP 6 +#define JB_PC 7 + + .file "md.S" + .text + + /* _st_md_cxt_save(__jmp_buf env) */ +.globl _st_md_cxt_save + .type _st_md_cxt_save, @function + .align 16 +_st_md_cxt_save: + /* + * Save registers. + */ + movq %rbx, (JB_RBX*8)(%rdi) + movq %rbp, (JB_RBP*8)(%rdi) + movq %r12, (JB_R12*8)(%rdi) + movq %r13, (JB_R13*8)(%rdi) + movq %r14, (JB_R14*8)(%rdi) + movq %r15, (JB_R15*8)(%rdi) + /* Save SP */ + leaq 8(%rsp), %rdx + movq %rdx, (JB_RSP*8)(%rdi) + /* Save PC we are returning to */ + movq (%rsp), %rax + movq %rax, (JB_PC*8)(%rdi) + xorq %rax, %rax + ret + .size _st_md_cxt_save, .-_st_md_cxt_save + + +/****************************************************************/ + + /* _st_md_cxt_restore(__jmp_buf env, int val) */ +.globl _st_md_cxt_restore + .type _st_md_cxt_restore, @function + .align 16 +_st_md_cxt_restore: + /* + * Restore registers. + */ + movq (JB_RBX*8)(%rdi), %rbx + movq (JB_RBP*8)(%rdi), %rbp + movq (JB_R12*8)(%rdi), %r12 + movq (JB_R13*8)(%rdi), %r13 + movq (JB_R14*8)(%rdi), %r14 + movq (JB_R15*8)(%rdi), %r15 + /* Set return value */ + test %esi, %esi + mov $01, %eax + cmove %eax, %esi + mov %esi, %eax + movq (JB_PC*8)(%rdi), %rdx + movq (JB_RSP*8)(%rdi), %rsp + /* Jump to saved PC */ + jmpq *%rdx + .size _st_md_cxt_restore, .-_st_md_cxt_restore + +/****************************************************************/ + +#endif + diff --git a/trunk/research/st-1.9/md.h b/trunk/research/st-1.9/md.h new file mode 100644 index 0000000000..5bf795f242 --- /dev/null +++ b/trunk/research/st-1.9/md.h @@ -0,0 +1,627 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Portable Runtime library. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Silicon Graphics, Inc. + * + * Portions created by SGI are Copyright (C) 2000-2001 Silicon + * Graphics, Inc. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +/* + * This file is derived directly from Netscape Communications Corporation, + * and consists of extensive modifications made during the year(s) 1999-2000. + */ + +#ifndef __ST_MD_H__ +#define __ST_MD_H__ + +#if defined(ETIMEDOUT) && !defined(ETIME) +#define ETIME ETIMEDOUT +#endif + +#if defined(MAP_ANONYMOUS) && !defined(MAP_ANON) +#define MAP_ANON MAP_ANONYMOUS +#endif + +#ifndef MAP_FAILED +#define MAP_FAILED -1 +#endif + +/***************************************** + * Platform specifics + */ + +#if defined (AIX) + +#define MD_STACK_GROWS_DOWN +#define MD_USE_SYSV_ANON_MMAP +#define MD_ACCEPT_NB_INHERITED +#define MD_ALWAYS_UNSERIALIZED_ACCEPT + +#ifndef MD_HAVE_SOCKLEN_T +#define MD_HAVE_SOCKLEN_T +#define socklen_t unsigned long +#endif + +#define MD_SETJMP(env) _setjmp(env) +#define MD_LONGJMP(env, val) _longjmp(env, val) + +#define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + (_thread)->context[3] = (long) (_sp); \ + ST_END_MACRO + +#define MD_GET_UTIME() \ + timebasestruct_t rt; \ + (void) read_real_time(&rt, TIMEBASE_SZ); \ + (void) time_base_to_time(&rt, TIMEBASE_SZ); \ + return (rt.tb_high * 1000000LL + rt.tb_low / 1000) + +#elif defined (CYGWIN) + +#define MD_STACK_GROWS_DOWN +#define MD_USE_BSD_ANON_MMAP +#define MD_ACCEPT_NB_NOT_INHERITED +#define MD_ALWAYS_UNSERIALIZED_ACCEPT + +#define MD_SETJMP(env) setjmp(env) +#define MD_LONGJMP(env, val) longjmp(env, val) + +#define MD_JB_SP 7 + +#define MD_GET_SP(_t) (_t)->context[MD_JB_SP] + +#define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + MD_GET_SP(_thread) = (long) (_sp); \ + ST_END_MACRO + +#define MD_GET_UTIME() \ + struct timeval tv; \ + (void) gettimeofday(&tv, NULL); \ + return (tv.tv_sec * 1000000LL + tv.tv_usec) + +#elif defined (DARWIN) + +#define MD_STACK_GROWS_DOWN +#define MD_USE_BSD_ANON_MMAP +#define MD_ACCEPT_NB_INHERITED +#define MD_ALWAYS_UNSERIALIZED_ACCEPT +#define MD_HAVE_SOCKLEN_T + +#define MD_SETJMP(env) _setjmp(env) +#define MD_LONGJMP(env, val) _longjmp(env, val) + +#if defined(__ppc__) +#define MD_JB_SP 0 +#elif defined(__i386__) +#define MD_JB_SP 9 +#elif defined(__x86_64__) +#define MD_JB_SP 4 +#else +#error Unknown CPU architecture +#endif + +#define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + *((long *)&((_thread)->context[MD_JB_SP])) = (long) (_sp); \ + ST_END_MACRO + +#define MD_GET_UTIME() \ + struct timeval tv; \ + (void) gettimeofday(&tv, NULL); \ + return (tv.tv_sec * 1000000LL + tv.tv_usec) + +#elif defined (FREEBSD) + +#define MD_STACK_GROWS_DOWN +#define MD_USE_BSD_ANON_MMAP +#define MD_ACCEPT_NB_INHERITED +#define MD_ALWAYS_UNSERIALIZED_ACCEPT + +#define MD_SETJMP(env) _setjmp(env) +#define MD_LONGJMP(env, val) _longjmp(env, val) + +#if defined(__i386__) +#define MD_JB_SP 2 +#elif defined(__alpha__) +#define MD_JB_SP 34 +#elif defined(__amd64__) +#define MD_JB_SP 2 +#else +#error Unknown CPU architecture +#endif + +#define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + (_thread)->context[0]._jb[MD_JB_SP] = (long) (_sp); \ + ST_END_MACRO + +#define MD_GET_UTIME() \ + struct timeval tv; \ + (void) gettimeofday(&tv, NULL); \ + return (tv.tv_sec * 1000000LL + tv.tv_usec) + +#elif defined (HPUX) + +#define MD_STACK_GROWS_UP +#define MD_USE_BSD_ANON_MMAP +#define MD_ACCEPT_NB_INHERITED +#define MD_ALWAYS_UNSERIALIZED_ACCEPT + +#define MD_SETJMP(env) _setjmp(env) +#define MD_LONGJMP(env, val) _longjmp(env, val) + +#ifndef __LP64__ +/* 32-bit mode (ILP32 data model) */ +#define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + ((long *)((_thread)->context))[1] = (long) (_sp); \ + ST_END_MACRO +#else +/* 64-bit mode (LP64 data model) */ +#define MD_STACK_PAD_SIZE 256 +/* Last stack frame must be preserved */ +#define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + memcpy((char *)(_sp) - MD_STACK_PAD_SIZE, \ + ((char **)((_thread)->context))[1] - MD_STACK_PAD_SIZE, \ + MD_STACK_PAD_SIZE); \ + ((long *)((_thread)->context))[1] = (long) (_sp); \ + ST_END_MACRO +#endif /* !__LP64__ */ + +#define MD_GET_UTIME() \ + struct timeval tv; \ + (void) gettimeofday(&tv, NULL); \ + return (tv.tv_sec * 1000000LL + tv.tv_usec) + +#elif defined (IRIX) + +#include + +#define MD_STACK_GROWS_DOWN +#define MD_USE_SYSV_ANON_MMAP +#define MD_ACCEPT_NB_INHERITED +#define MD_ALWAYS_UNSERIALIZED_ACCEPT + +#define MD_SETJMP(env) setjmp(env) +#define MD_LONGJMP(env, val) longjmp(env, val) + +#define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + (void) MD_SETJMP((_thread)->context); \ + (_thread)->context[JB_SP] = (long) (_sp); \ + (_thread)->context[JB_PC] = (long) _main; \ + ST_END_MACRO + +#define MD_GET_UTIME() \ + static int inited = 0; \ + static clockid_t clock_id = CLOCK_SGI_CYCLE; \ + struct timespec ts; \ + if (!inited) { \ + if (syssgi(SGI_CYCLECNTR_SIZE) < 64) \ + clock_id = CLOCK_REALTIME; \ + inited = 1; \ + } \ + (void) clock_gettime(clock_id, &ts); \ + return (ts.tv_sec * 1000000LL + ts.tv_nsec / 1000) + +/* + * Cap the stack by zeroing out the saved return address register + * value. This allows libexc, used by SpeedShop, to know when to stop + * backtracing since it won't find main, start, or any other known + * stack root function in a state thread's stack. Without this libexc + * traces right off the stack and crashes. + * The function preamble stores ra at 8(sp), this stores zero there. + * N.B. This macro is compiler/ABI dependent. It must change if ANY more + * automatic variables are added to the _st_thread_main() routine, because + * the address where ra is stored will change. + */ +#if !defined(__GNUC__) && defined(_MIPS_SIM) && _MIPS_SIM != _ABIO32 +#define MD_CAP_STACK(var_addr) \ + (((volatile __uint64_t *)(var_addr))[1] = 0) +#endif + +#elif defined (LINUX) + +/* + * These are properties of the linux kernel and are the same on every + * flavor and architecture. + */ +#define MD_USE_BSD_ANON_MMAP +#define MD_ACCEPT_NB_NOT_INHERITED +#define MD_ALWAYS_UNSERIALIZED_ACCEPT +/* + * Modern GNU/Linux is Posix.1g compliant. + */ +#define MD_HAVE_SOCKLEN_T + +/* + * All architectures and flavors of linux have the gettimeofday + * function but if you know of a faster way, use it. + */ +#define MD_GET_UTIME() \ + struct timeval tv; \ + (void) gettimeofday(&tv, NULL); \ + return (tv.tv_sec * 1000000LL + tv.tv_usec) + +#if defined(__ia64__) +#define MD_STACK_GROWS_DOWN + +/* + * IA-64 architecture. Besides traditional memory call stack, IA-64 + * uses general register stack. Thus each thread needs a backing store + * for register stack in addition to memory stack. Standard + * setjmp()/longjmp() cannot be used for thread context switching + * because their implementation implicitly assumes that only one + * register stack exists. + */ +#ifdef USE_LIBC_SETJMP +#undef USE_LIBC_SETJMP +#endif +#define MD_USE_BUILTIN_SETJMP + +#define MD_STACK_PAD_SIZE 128 +/* Last register stack frame must be preserved */ +#define MD_INIT_CONTEXT(_thread, _sp, _bsp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + memcpy((char *)(_bsp) - MD_STACK_PAD_SIZE, \ + (char *)(_thread)->context[0].__jmpbuf[17] - MD_STACK_PAD_SIZE, \ + MD_STACK_PAD_SIZE); \ + (_thread)->context[0].__jmpbuf[0] = (long) (_sp); \ + (_thread)->context[0].__jmpbuf[17] = (long) (_bsp); \ + ST_END_MACRO + +#elif defined(__mips__) +#define MD_STACK_GROWS_DOWN + +#define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + MD_SETJMP((_thread)->context); \ + _thread->context[0].__jmpbuf[0].__pc = (__ptr_t) _main; \ + _thread->context[0].__jmpbuf[0].__sp = _sp; \ + ST_END_MACRO + +#else /* Not IA-64 or mips */ + +/* + * On linux, there are a few styles of jmpbuf format. These vary based + * on architecture/glibc combination. + * + * Most of the glibc based toggles were lifted from: + * mozilla/nsprpub/pr/include/md/_linux.h + */ + +/* + * Starting with glibc 2.4, JB_SP definitions are not public anymore. + * They, however, can still be found in glibc source tree in + * architecture-specific "jmpbuf-offsets.h" files. + * Most importantly, the content of jmp_buf is mangled by setjmp to make + * it completely opaque (the mangling can be disabled by setting the + * LD_POINTER_GUARD environment variable before application execution). + * Therefore we will use built-in _st_md_cxt_save/_st_md_cxt_restore + * functions as a setjmp/longjmp replacement wherever they are available + * unless USE_LIBC_SETJMP is defined. + */ + +#if defined(__powerpc__) +#define MD_STACK_GROWS_DOWN + +#if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1) +#ifndef JB_GPR1 +#define JB_GPR1 0 +#endif +#define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_GPR1] +#else +/* not an error but certainly cause for caution */ +#error "Untested use of old glibc on powerpc" +#define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__misc[0] +#endif /* glibc 2.1 or later */ + +#elif defined(__alpha) +#define MD_STACK_GROWS_DOWN + +#if defined(__GLIBC__) && __GLIBC__ >= 2 +#ifndef JB_SP +#define JB_SP 8 +#endif +#define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_SP] +#else +/* not an error but certainly cause for caution */ +#error "Untested use of old glibc on alpha" +#define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__sp +#endif + +#elif defined(__mc68000__) +#define MD_STACK_GROWS_DOWN + +/* m68k still uses old style sigjmp_buf */ +#define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__sp + +#elif defined(__sparc__) +#define MD_STACK_GROWS_DOWN + +#if defined(__GLIBC__) && __GLIBC__ >= 2 +#ifndef JB_SP +#define JB_SP 0 +#endif +#define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_SP] +#else +/* not an error but certainly cause for caution */ +#error "Untested use of old glic on sparc -- also using odd mozilla derived __fp" +#define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__fp +#endif + +#elif defined(__i386__) +#define MD_STACK_GROWS_DOWN +#define MD_USE_BUILTIN_SETJMP + +#if defined(__GLIBC__) && __GLIBC__ >= 2 +#ifndef JB_SP +#define JB_SP 4 +#endif +#define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_SP] +#else +/* not an error but certainly cause for caution */ +#error "Untested use of old glibc on i386" +#define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__sp +#endif + +#elif defined(__amd64__) || defined(__x86_64__) +#define MD_STACK_GROWS_DOWN +#define MD_USE_BUILTIN_SETJMP + +#ifndef JB_RSP +#define JB_RSP 6 +#endif +#define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_RSP] + +#elif defined(__arm__) +#define MD_STACK_GROWS_DOWN + +#if defined(__GLIBC__) && __GLIBC__ >= 2 +#define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[20] +#else +#error "ARM/Linux pre-glibc2 not supported yet" +#endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */ + +#elif defined(__s390__) +#define MD_STACK_GROWS_DOWN + +/* There is no JB_SP in glibc at this time. (glibc 2.2.5) + */ +#define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__gregs[9] + +#elif defined(__hppa__) +#define MD_STACK_GROWS_UP + +/* yes, this is gross, unfortunately at the moment (2002/08/01) there is + * a bug in hppa's glibc header definition for JB_SP, so we can't + * use that... + */ +#define MD_GET_SP(_t) (*(long *)(((char *)&(_t)->context[0].__jmpbuf[0]) + 76)) + +#else +#error "Unknown CPU architecture" +#endif /* Cases with common MD_INIT_CONTEXT and different SP locations */ + +#define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + MD_GET_SP(_thread) = (long) (_sp); \ + ST_END_MACRO + +#endif /* Cases with different MD_INIT_CONTEXT */ + +#if defined(MD_USE_BUILTIN_SETJMP) && !defined(USE_LIBC_SETJMP) +#define MD_SETJMP(env) _st_md_cxt_save(env) +#define MD_LONGJMP(env, val) _st_md_cxt_restore(env, val) + +extern int _st_md_cxt_save(jmp_buf env); +extern void _st_md_cxt_restore(jmp_buf env, int val); +#else +#define MD_SETJMP(env) setjmp(env) +#define MD_LONGJMP(env, val) longjmp(env, val) +#endif + +#elif defined (NETBSD) + +#define MD_STACK_GROWS_DOWN +#define MD_USE_BSD_ANON_MMAP +#define MD_ACCEPT_NB_INHERITED +#define MD_ALWAYS_UNSERIALIZED_ACCEPT +#define MD_HAVE_SOCKLEN_T + +#define MD_SETJMP(env) _setjmp(env) +#define MD_LONGJMP(env, val) _longjmp(env, val) + +#if defined(__i386__) +#define MD_JB_SP 2 +#elif defined(__alpha__) +#define MD_JB_SP 34 +#elif defined(__sparc__) +#define MD_JB_SP 0 +#elif defined(__vax__) +#define MD_JB_SP 2 +#else +#error Unknown CPU architecture +#endif + +#define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + (_thread)->context[MD_JB_SP] = (long) (_sp); \ + ST_END_MACRO + +#define MD_GET_UTIME() \ + struct timeval tv; \ + (void) gettimeofday(&tv, NULL); \ + return (tv.tv_sec * 1000000LL + tv.tv_usec) + +#elif defined (OPENBSD) + +#define MD_STACK_GROWS_DOWN +#define MD_USE_BSD_ANON_MMAP +#define MD_ACCEPT_NB_INHERITED +#define MD_ALWAYS_UNSERIALIZED_ACCEPT + +#define MD_SETJMP(env) _setjmp(env) +#define MD_LONGJMP(env, val) _longjmp(env, val) + +#if defined(__i386__) +#define MD_JB_SP 2 +#elif defined(__alpha__) +#define MD_JB_SP 34 +#elif defined(__sparc__) +#define MD_JB_SP 0 +#elif defined(__amd64__) +#define MD_JB_SP 6 +#else +#error Unknown CPU architecture +#endif + +#define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + (_thread)->context[MD_JB_SP] = (long) (_sp); \ + ST_END_MACRO + +#define MD_GET_UTIME() \ + struct timeval tv; \ + (void) gettimeofday(&tv, NULL); \ + return (tv.tv_sec * 1000000LL + tv.tv_usec) + +#elif defined (OSF1) + +#include + +#define MD_STACK_GROWS_DOWN +#define MD_USE_SYSV_ANON_MMAP +#define MD_ACCEPT_NB_NOT_INHERITED +#define MD_ALWAYS_UNSERIALIZED_ACCEPT + +#define MD_SETJMP(env) _setjmp(env) +#define MD_LONGJMP(env, val) _longjmp(env, val) + +#define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + ((struct sigcontext *)((_thread)->context))->sc_sp = (long) (_sp); \ + ST_END_MACRO + +#define MD_GET_UTIME() \ + struct timeval tv; \ + (void) gettimeofday(&tv, NULL); \ + return (tv.tv_sec * 1000000LL + tv.tv_usec) + +#elif defined (SOLARIS) + +#include +extern int getpagesize(void); + +#define MD_STACK_GROWS_DOWN +#define MD_USE_SYSV_ANON_MMAP +#define MD_ACCEPT_NB_NOT_INHERITED + +#define MD_SETJMP(env) setjmp(env) +#define MD_LONGJMP(env, val) longjmp(env, val) + +#if defined(sparc) || defined(__sparc) +#ifdef _LP64 +#define MD_STACK_PAD_SIZE 4095 +#endif +#define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + (void) MD_SETJMP((_thread)->context); \ + (_thread)->context[1] = (long) (_sp); \ + (_thread)->context[2] = (long) _main; \ + ST_END_MACRO +#elif defined(i386) || defined(__i386) +#define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + (void) MD_SETJMP((_thread)->context); \ + (_thread)->context[4] = (long) (_sp); \ + (_thread)->context[5] = (long) _main; \ + ST_END_MACRO +#elif defined(__amd64__) +#define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + (_thread)->context[6] = (long) (_sp); \ + ST_END_MACRO +#else +#error Unknown CPU architecture +#endif + +#define MD_GET_UTIME() \ + return (gethrtime() / 1000) + +#else +#error Unknown OS +#endif /* OS */ + +#if !defined(MD_HAVE_POLL) && !defined(MD_DONT_HAVE_POLL) +#define MD_HAVE_POLL +#endif + +#ifndef MD_STACK_PAD_SIZE +#define MD_STACK_PAD_SIZE 128 +#endif + +#if !defined(MD_HAVE_SOCKLEN_T) && !defined(socklen_t) +#define socklen_t int +#endif + +#ifndef MD_CAP_STACK +#define MD_CAP_STACK(var_addr) +#endif + +#endif /* !__ST_MD_H__ */ + diff --git a/trunk/research/st-1.9/osguess.sh b/trunk/research/st-1.9/osguess.sh new file mode 100644 index 0000000000..531681efe6 --- /dev/null +++ b/trunk/research/st-1.9/osguess.sh @@ -0,0 +1,45 @@ +# +# This script can be used to automatically guess target OS. +# It requires the config.guess utility which is a part of GNU Autoconf. +# GNU Autoconf can be downloaded from ftp://ftp.gnu.org/gnu/autoconf/ +# +# Use "default" as a make target for automatic builds. +# + + +# Specify path to the config.guess utility (unless set via environment) +#CONFIG_GUESS_PATH= + + +if [ x"$CONFIG_GUESS_PATH" = x ]; then + echo "Error: CONFIG_GUESS_PATH variable is not set" + exit 1 +fi + +if [ ! -f "$CONFIG_GUESS_PATH/config.guess" ]; then + echo "Can't find $CONFIG_GUESS_PATH/config.guess utility. Wrong path?" + exit 1 +fi + +sys_info=`/bin/sh $CONFIG_GUESS_PATH/config.guess` + +echo "Building for $sys_info" + +case "$sys_info" in + *-ibm-aix4* ) OS=AIX ;; + *-freebsd* ) OS=FREEBSD ;; + hppa*-hp-hpux11*) OS=HPUX ;; + *-sgi-irix6* ) OS=IRIX ;; + *-linux* ) OS=LINUX ;; + *-netbsd* ) OS=NETBSD ;; + *-openbsd* ) OS=OPENBSD ;; + *-dec-osf* ) OS=OSF1 ;; + *-solaris2* ) OS=SOLARIS ;; + *-darwin* ) OS=DARWIN ;; + * ) OS= + echo "Sorry, unsupported OS" + exit 1 ;; +esac + +echo "Making with OS=$OS" + diff --git a/trunk/research/st-1.9/public.h b/trunk/research/st-1.9/public.h new file mode 100644 index 0000000000..e0cc58dc41 --- /dev/null +++ b/trunk/research/st-1.9/public.h @@ -0,0 +1,184 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Portable Runtime library. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Silicon Graphics, Inc. + * + * Portions created by SGI are Copyright (C) 2000-2001 Silicon + * Graphics, Inc. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#ifndef __ST_THREAD_H__ +#define __ST_THREAD_H__ + +#include +#include +#include +#include +#include +#include +#include + +#define ST_VERSION "1.9" +#define ST_VERSION_MAJOR 1 +#define ST_VERSION_MINOR 9 + +/* Undefine this to remove the context switch callback feature. */ +#define ST_SWITCH_CB + +#ifndef ETIME +#define ETIME ETIMEDOUT +#endif + +#ifndef ST_UTIME_NO_TIMEOUT +#define ST_UTIME_NO_TIMEOUT ((st_utime_t) -1LL) +#endif + +#ifndef ST_UTIME_NO_WAIT +#define ST_UTIME_NO_WAIT 0 +#endif + +#define ST_EVENTSYS_DEFAULT 0 +#define ST_EVENTSYS_SELECT 1 +#define ST_EVENTSYS_POLL 2 +#define ST_EVENTSYS_ALT 3 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned long long st_utime_t; +typedef struct _st_thread * st_thread_t; +typedef struct _st_cond * st_cond_t; +typedef struct _st_mutex * st_mutex_t; +typedef struct _st_netfd * st_netfd_t; +#ifdef ST_SWITCH_CB +typedef void (*st_switch_cb_t)(void); +#endif + +extern int st_init(void); +extern int st_getfdlimit(void); + +extern int st_set_eventsys(int eventsys); +extern int st_get_eventsys(void); +extern const char *st_get_eventsys_name(void); + +#ifdef ST_SWITCH_CB +extern st_switch_cb_t st_set_switch_in_cb(st_switch_cb_t cb); +extern st_switch_cb_t st_set_switch_out_cb(st_switch_cb_t cb); +#endif + +extern st_thread_t st_thread_self(void); +extern void st_thread_exit(void *retval); +extern int st_thread_join(st_thread_t thread, void **retvalp); +extern void st_thread_interrupt(st_thread_t thread); +extern st_thread_t st_thread_create(void *(*start)(void *arg), void *arg, + int joinable, int stack_size); +extern int st_randomize_stacks(int on); +extern int st_set_utime_function(st_utime_t (*func)(void)); + +extern st_utime_t st_utime(void); +extern st_utime_t st_utime_last_clock(void); +extern int st_timecache_set(int on); +extern time_t st_time(void); +extern int st_usleep(st_utime_t usecs); +extern int st_sleep(int secs); +extern st_cond_t st_cond_new(void); +extern int st_cond_destroy(st_cond_t cvar); +extern int st_cond_timedwait(st_cond_t cvar, st_utime_t timeout); +extern int st_cond_wait(st_cond_t cvar); +extern int st_cond_signal(st_cond_t cvar); +extern int st_cond_broadcast(st_cond_t cvar); +extern st_mutex_t st_mutex_new(void); +extern int st_mutex_destroy(st_mutex_t lock); +extern int st_mutex_lock(st_mutex_t lock); +extern int st_mutex_unlock(st_mutex_t lock); +extern int st_mutex_trylock(st_mutex_t lock); + +extern int st_key_create(int *keyp, void (*destructor)(void *)); +extern int st_key_getlimit(void); +extern int st_thread_setspecific(int key, void *value); +extern void *st_thread_getspecific(int key); + +extern st_netfd_t st_netfd_open(int osfd); +extern st_netfd_t st_netfd_open_socket(int osfd); +extern void st_netfd_free(st_netfd_t fd); +extern int st_netfd_close(st_netfd_t fd); +extern int st_netfd_fileno(st_netfd_t fd); +extern void st_netfd_setspecific(st_netfd_t fd, void *value, + void (*destructor)(void *)); +extern void *st_netfd_getspecific(st_netfd_t fd); +extern int st_netfd_serialize_accept(st_netfd_t fd); +extern int st_netfd_poll(st_netfd_t fd, int how, st_utime_t timeout); + +extern int st_poll(struct pollfd *pds, int npds, st_utime_t timeout); +extern st_netfd_t st_accept(st_netfd_t fd, struct sockaddr *addr, int *addrlen, + st_utime_t timeout); +extern int st_connect(st_netfd_t fd, const struct sockaddr *addr, int addrlen, + st_utime_t timeout); +extern ssize_t st_read(st_netfd_t fd, void *buf, size_t nbyte, + st_utime_t timeout); +extern ssize_t st_read_fully(st_netfd_t fd, void *buf, size_t nbyte, + st_utime_t timeout); +extern int st_read_resid(st_netfd_t fd, void *buf, size_t *resid, + st_utime_t timeout); +extern ssize_t st_readv(st_netfd_t fd, const struct iovec *iov, int iov_size, + st_utime_t timeout); +extern int st_readv_resid(st_netfd_t fd, struct iovec **iov, int *iov_size, + st_utime_t timeout); +extern ssize_t st_write(st_netfd_t fd, const void *buf, size_t nbyte, + st_utime_t timeout); +extern int st_write_resid(st_netfd_t fd, const void *buf, size_t *resid, + st_utime_t timeout); +extern ssize_t st_writev(st_netfd_t fd, const struct iovec *iov, int iov_size, + st_utime_t timeout); +extern int st_writev_resid(st_netfd_t fd, struct iovec **iov, int *iov_size, + st_utime_t timeout); +extern int st_recvfrom(st_netfd_t fd, void *buf, int len, + struct sockaddr *from, int *fromlen, + st_utime_t timeout); +extern int st_sendto(st_netfd_t fd, const void *msg, int len, + const struct sockaddr *to, int tolen, st_utime_t timeout); +extern int st_recvmsg(st_netfd_t fd, struct msghdr *msg, int flags, + st_utime_t timeout); +extern int st_sendmsg(st_netfd_t fd, const struct msghdr *msg, int flags, + st_utime_t timeout); +extern st_netfd_t st_open(const char *path, int oflags, mode_t mode); + +#ifdef DEBUG +extern void _st_show_thread_stack(st_thread_t thread, const char *messg); +extern void _st_iterate_threads(void); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* !__ST_THREAD_H__ */ + diff --git a/trunk/research/st-1.9/sched.c b/trunk/research/st-1.9/sched.c new file mode 100644 index 0000000000..d9c393ca1e --- /dev/null +++ b/trunk/research/st-1.9/sched.c @@ -0,0 +1,672 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Portable Runtime library. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Silicon Graphics, Inc. + * + * Portions created by SGI are Copyright (C) 2000-2001 Silicon + * Graphics, Inc. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +/* + * This file is derived directly from Netscape Communications Corporation, + * and consists of extensive modifications made during the year(s) 1999-2000. + */ + +#include +#include +#include +#include +#include +#include +#include "common.h" + + +/* Global data */ +_st_vp_t _st_this_vp; /* This VP */ +_st_thread_t *_st_this_thread; /* Current thread */ +int _st_active_count = 0; /* Active thread count */ + +time_t _st_curr_time = 0; /* Current time as returned by time(2) */ +st_utime_t _st_last_tset; /* Last time it was fetched */ + + +int st_poll(struct pollfd *pds, int npds, st_utime_t timeout) +{ + struct pollfd *pd; + struct pollfd *epd = pds + npds; + _st_pollq_t pq; + _st_thread_t *me = _ST_CURRENT_THREAD(); + int n; + + if (me->flags & _ST_FL_INTERRUPT) { + me->flags &= ~_ST_FL_INTERRUPT; + errno = EINTR; + return -1; + } + + if ((*_st_eventsys->pollset_add)(pds, npds) < 0) + return -1; + + pq.pds = pds; + pq.npds = npds; + pq.thread = me; + pq.on_ioq = 1; + _ST_ADD_IOQ(pq); + if (timeout != ST_UTIME_NO_TIMEOUT) + _ST_ADD_SLEEPQ(me, timeout); + me->state = _ST_ST_IO_WAIT; + + _ST_SWITCH_CONTEXT(me); + + n = 0; + if (pq.on_ioq) { + /* If we timed out, the pollq might still be on the ioq. Remove it */ + _ST_DEL_IOQ(pq); + (*_st_eventsys->pollset_del)(pds, npds); + } else { + /* Count the number of ready descriptors */ + for (pd = pds; pd < epd; pd++) { + if (pd->revents) + n++; + } + } + + if (me->flags & _ST_FL_INTERRUPT) { + me->flags &= ~_ST_FL_INTERRUPT; + errno = EINTR; + return -1; + } + + return n; +} + + +void _st_vp_schedule(void) +{ + _st_thread_t *thread; + + if (_ST_RUNQ.next != &_ST_RUNQ) { + /* Pull thread off of the run queue */ + thread = _ST_THREAD_PTR(_ST_RUNQ.next); + _ST_DEL_RUNQ(thread); + } else { + /* If there are no threads to run, switch to the idle thread */ + thread = _st_this_vp.idle_thread; + } + ST_ASSERT(thread->state == _ST_ST_RUNNABLE); + + /* Resume the thread */ + thread->state = _ST_ST_RUNNING; + _ST_RESTORE_CONTEXT(thread); +} + + +/* + * Initialize this Virtual Processor + */ +int st_init(void) +{ + _st_thread_t *thread; + + if (_st_active_count) { + /* Already initialized */ + return 0; + } + + /* We can ignore return value here */ + st_set_eventsys(ST_EVENTSYS_DEFAULT); + + if (_st_io_init() < 0) + return -1; + + memset(&_st_this_vp, 0, sizeof(_st_vp_t)); + + ST_INIT_CLIST(&_ST_RUNQ); + ST_INIT_CLIST(&_ST_IOQ); + ST_INIT_CLIST(&_ST_ZOMBIEQ); +#ifdef DEBUG + ST_INIT_CLIST(&_ST_THREADQ); +#endif + + if ((*_st_eventsys->init)() < 0) + return -1; + + _st_this_vp.pagesize = getpagesize(); + _st_this_vp.last_clock = st_utime(); + + /* + * Create idle thread + */ + _st_this_vp.idle_thread = st_thread_create(_st_idle_thread_start, + NULL, 0, 0); + if (!_st_this_vp.idle_thread) + return -1; + _st_this_vp.idle_thread->flags = _ST_FL_IDLE_THREAD; + _st_active_count--; + _ST_DEL_RUNQ(_st_this_vp.idle_thread); + + /* + * Initialize primordial thread + */ + thread = (_st_thread_t *) calloc(1, sizeof(_st_thread_t) + + (ST_KEYS_MAX * sizeof(void *))); + if (!thread) + return -1; + thread->private_data = (void **) (thread + 1); + thread->state = _ST_ST_RUNNING; + thread->flags = _ST_FL_PRIMORDIAL; + _ST_SET_CURRENT_THREAD(thread); + _st_active_count++; +#ifdef DEBUG + _ST_ADD_THREADQ(thread); +#endif + + return 0; +} + + +#ifdef ST_SWITCH_CB +st_switch_cb_t st_set_switch_in_cb(st_switch_cb_t cb) +{ + st_switch_cb_t ocb = _st_this_vp.switch_in_cb; + _st_this_vp.switch_in_cb = cb; + return ocb; +} + +st_switch_cb_t st_set_switch_out_cb(st_switch_cb_t cb) +{ + st_switch_cb_t ocb = _st_this_vp.switch_out_cb; + _st_this_vp.switch_out_cb = cb; + return ocb; +} +#endif + + +/* + * Start function for the idle thread + */ +/* ARGSUSED */ +void *_st_idle_thread_start(void *arg) +{ + _st_thread_t *me = _ST_CURRENT_THREAD(); + + while (_st_active_count > 0) { + /* Idle vp till I/O is ready or the smallest timeout expired */ + _ST_VP_IDLE(); + + /* Check sleep queue for expired threads */ + _st_vp_check_clock(); + + me->state = _ST_ST_RUNNABLE; + _ST_SWITCH_CONTEXT(me); + } + + /* No more threads */ + exit(0); + + /* NOTREACHED */ + return NULL; +} + + +void st_thread_exit(void *retval) +{ + _st_thread_t *thread = _ST_CURRENT_THREAD(); + + thread->retval = retval; + _st_thread_cleanup(thread); + _st_active_count--; + if (thread->term) { + /* Put thread on the zombie queue */ + thread->state = _ST_ST_ZOMBIE; + _ST_ADD_ZOMBIEQ(thread); + + /* Notify on our termination condition variable */ + st_cond_signal(thread->term); + + /* Switch context and come back later */ + _ST_SWITCH_CONTEXT(thread); + + /* Continue the cleanup */ + st_cond_destroy(thread->term); + thread->term = NULL; + } + +#ifdef DEBUG + _ST_DEL_THREADQ(thread); +#endif + + if (!(thread->flags & _ST_FL_PRIMORDIAL)) + _st_stack_free(thread->stack); + + /* Find another thread to run */ + _ST_SWITCH_CONTEXT(thread); + /* Not going to land here */ +} + + +int st_thread_join(_st_thread_t *thread, void **retvalp) +{ + _st_cond_t *term = thread->term; + + /* Can't join a non-joinable thread */ + if (term == NULL) { + errno = EINVAL; + return -1; + } + if (_ST_CURRENT_THREAD() == thread) { + errno = EDEADLK; + return -1; + } + + /* Multiple threads can't wait on the same joinable thread */ + if (term->wait_q.next != &term->wait_q) { + errno = EINVAL; + return -1; + } + + while (thread->state != _ST_ST_ZOMBIE) { + if (st_cond_timedwait(term, ST_UTIME_NO_TIMEOUT) != 0) + return -1; + } + + if (retvalp) + *retvalp = thread->retval; + + /* + * Remove target thread from the zombie queue and make it runnable. + * When it gets scheduled later, it will do the clean up. + */ + thread->state = _ST_ST_RUNNABLE; + _ST_DEL_ZOMBIEQ(thread); + _ST_ADD_RUNQ(thread); + + return 0; +} + + +void _st_thread_main(void) +{ + _st_thread_t *thread = _ST_CURRENT_THREAD(); + + /* + * Cap the stack by zeroing out the saved return address register + * value. This allows some debugging/profiling tools to know when + * to stop unwinding the stack. It's a no-op on most platforms. + */ + MD_CAP_STACK(&thread); + + /* Run thread main */ + thread->retval = (*thread->start)(thread->arg); + + /* All done, time to go away */ + st_thread_exit(thread->retval); +} + + +/* + * Insert "thread" into the timeout heap, in the position + * specified by thread->heap_index. See docs/timeout_heap.txt + * for details about the timeout heap. + */ +static _st_thread_t **heap_insert(_st_thread_t *thread) { + int target = thread->heap_index; + int s = target; + _st_thread_t **p = &_ST_SLEEPQ; + int bits = 0; + int bit; + int index = 1; + + while (s) { + s >>= 1; + bits++; + } + for (bit = bits - 2; bit >= 0; bit--) { + if (thread->due < (*p)->due) { + _st_thread_t *t = *p; + thread->left = t->left; + thread->right = t->right; + *p = thread; + thread->heap_index = index; + thread = t; + } + index <<= 1; + if (target & (1 << bit)) { + p = &((*p)->right); + index |= 1; + } else { + p = &((*p)->left); + } + } + thread->heap_index = index; + *p = thread; + thread->left = thread->right = NULL; + return p; +} + + +/* + * Delete "thread" from the timeout heap. + */ +static void heap_delete(_st_thread_t *thread) { + _st_thread_t *t, **p; + int bits = 0; + int s, bit; + + /* First find and unlink the last heap element */ + p = &_ST_SLEEPQ; + s = _ST_SLEEPQ_SIZE; + while (s) { + s >>= 1; + bits++; + } + for (bit = bits - 2; bit >= 0; bit--) { + if (_ST_SLEEPQ_SIZE & (1 << bit)) { + p = &((*p)->right); + } else { + p = &((*p)->left); + } + } + t = *p; + *p = NULL; + --_ST_SLEEPQ_SIZE; + if (t != thread) { + /* + * Insert the unlinked last element in place of the element we are deleting + */ + t->heap_index = thread->heap_index; + p = heap_insert(t); + t = *p; + t->left = thread->left; + t->right = thread->right; + + /* + * Reestablish the heap invariant. + */ + for (;;) { + _st_thread_t *y; /* The younger child */ + int index_tmp; + if (t->left == NULL) + break; + else if (t->right == NULL) + y = t->left; + else if (t->left->due < t->right->due) + y = t->left; + else + y = t->right; + if (t->due > y->due) { + _st_thread_t *tl = y->left; + _st_thread_t *tr = y->right; + *p = y; + if (y == t->left) { + y->left = t; + y->right = t->right; + p = &y->left; + } else { + y->left = t->left; + y->right = t; + p = &y->right; + } + t->left = tl; + t->right = tr; + index_tmp = t->heap_index; + t->heap_index = y->heap_index; + y->heap_index = index_tmp; + } else { + break; + } + } + } + thread->left = thread->right = NULL; +} + + +void _st_add_sleep_q(_st_thread_t *thread, st_utime_t timeout) +{ + thread->due = _ST_LAST_CLOCK + timeout; + thread->flags |= _ST_FL_ON_SLEEPQ; + thread->heap_index = ++_ST_SLEEPQ_SIZE; + heap_insert(thread); +} + + +void _st_del_sleep_q(_st_thread_t *thread) +{ + heap_delete(thread); + thread->flags &= ~_ST_FL_ON_SLEEPQ; +} + + +void _st_vp_check_clock(void) +{ + _st_thread_t *thread; + st_utime_t elapsed, now; + + now = st_utime(); + elapsed = now - _ST_LAST_CLOCK; + _ST_LAST_CLOCK = now; + + if (_st_curr_time && now - _st_last_tset > 999000) { + _st_curr_time = time(NULL); + _st_last_tset = now; + } + + while (_ST_SLEEPQ != NULL) { + thread = _ST_SLEEPQ; + ST_ASSERT(thread->flags & _ST_FL_ON_SLEEPQ); + if (thread->due > now) + break; + _ST_DEL_SLEEPQ(thread); + + /* If thread is waiting on condition variable, set the time out flag */ + if (thread->state == _ST_ST_COND_WAIT) + thread->flags |= _ST_FL_TIMEDOUT; + + /* Make thread runnable */ + ST_ASSERT(!(thread->flags & _ST_FL_IDLE_THREAD)); + thread->state = _ST_ST_RUNNABLE; + _ST_ADD_RUNQ(thread); + } +} + + +void st_thread_interrupt(_st_thread_t *thread) +{ + /* If thread is already dead */ + if (thread->state == _ST_ST_ZOMBIE) + return; + + thread->flags |= _ST_FL_INTERRUPT; + + if (thread->state == _ST_ST_RUNNING || thread->state == _ST_ST_RUNNABLE) + return; + + if (thread->flags & _ST_FL_ON_SLEEPQ) + _ST_DEL_SLEEPQ(thread); + + /* Make thread runnable */ + thread->state = _ST_ST_RUNNABLE; + _ST_ADD_RUNQ(thread); +} + + +_st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg, + int joinable, int stk_size) +{ + _st_thread_t *thread; + _st_stack_t *stack; + void **ptds; + char *sp; +#ifdef __ia64__ + char *bsp; +#endif + + /* Adjust stack size */ + if (stk_size == 0) + stk_size = ST_DEFAULT_STACK_SIZE; + stk_size = ((stk_size + _ST_PAGE_SIZE - 1) / _ST_PAGE_SIZE) * _ST_PAGE_SIZE; + stack = _st_stack_new(stk_size); + if (!stack) + return NULL; + + /* Allocate thread object and per-thread data off the stack */ +#if defined (MD_STACK_GROWS_DOWN) + sp = stack->stk_top; +#ifdef __ia64__ + /* + * The stack segment is split in the middle. The upper half is used + * as backing store for the register stack which grows upward. + * The lower half is used for the traditional memory stack which + * grows downward. Both stacks start in the middle and grow outward + * from each other. + */ + sp -= (stk_size >> 1); + bsp = sp; + /* Make register stack 64-byte aligned */ + if ((unsigned long)bsp & 0x3f) + bsp = bsp + (0x40 - ((unsigned long)bsp & 0x3f)); + stack->bsp = bsp + _ST_STACK_PAD_SIZE; +#endif + sp = sp - (ST_KEYS_MAX * sizeof(void *)); + ptds = (void **) sp; + sp = sp - sizeof(_st_thread_t); + thread = (_st_thread_t *) sp; + + /* Make stack 64-byte aligned */ + if ((unsigned long)sp & 0x3f) + sp = sp - ((unsigned long)sp & 0x3f); + stack->sp = sp - _ST_STACK_PAD_SIZE; +#elif defined (MD_STACK_GROWS_UP) + sp = stack->stk_bottom; + thread = (_st_thread_t *) sp; + sp = sp + sizeof(_st_thread_t); + ptds = (void **) sp; + sp = sp + (ST_KEYS_MAX * sizeof(void *)); + + /* Make stack 64-byte aligned */ + if ((unsigned long)sp & 0x3f) + sp = sp + (0x40 - ((unsigned long)sp & 0x3f)); + stack->sp = sp + _ST_STACK_PAD_SIZE; +#else +#error Unknown OS +#endif + + memset(thread, 0, sizeof(_st_thread_t)); + memset(ptds, 0, ST_KEYS_MAX * sizeof(void *)); + + /* Initialize thread */ + thread->private_data = ptds; + thread->stack = stack; + thread->start = start; + thread->arg = arg; + +#ifndef __ia64__ + _ST_INIT_CONTEXT(thread, stack->sp, _st_thread_main); +#else + _ST_INIT_CONTEXT(thread, stack->sp, stack->bsp, _st_thread_main); +#endif + + /* If thread is joinable, allocate a termination condition variable */ + if (joinable) { + thread->term = st_cond_new(); + if (thread->term == NULL) { + _st_stack_free(thread->stack); + return NULL; + } + } + + /* Make thread runnable */ + thread->state = _ST_ST_RUNNABLE; + _st_active_count++; + _ST_ADD_RUNQ(thread); +#ifdef DEBUG + _ST_ADD_THREADQ(thread); +#endif + + return thread; +} + + +_st_thread_t *st_thread_self(void) +{ + return _ST_CURRENT_THREAD(); +} + + +#ifdef DEBUG +/* ARGSUSED */ +void _st_show_thread_stack(_st_thread_t *thread, const char *messg) +{ + +} + +/* To be set from debugger */ +int _st_iterate_threads_flag = 0; + +void _st_iterate_threads(void) +{ + static _st_thread_t *thread = NULL; + static jmp_buf orig_jb, save_jb; + _st_clist_t *q; + + if (!_st_iterate_threads_flag) { + if (thread) { + memcpy(thread->context, save_jb, sizeof(jmp_buf)); + MD_LONGJMP(orig_jb, 1); + } + return; + } + + if (thread) { + memcpy(thread->context, save_jb, sizeof(jmp_buf)); + _st_show_thread_stack(thread, NULL); + } else { + if (MD_SETJMP(orig_jb)) { + _st_iterate_threads_flag = 0; + thread = NULL; + _st_show_thread_stack(thread, "Iteration completed"); + return; + } + thread = _ST_CURRENT_THREAD(); + _st_show_thread_stack(thread, "Iteration started"); + } + + q = thread->tlink.next; + if (q == &_ST_THREADQ) + q = q->next; + ST_ASSERT(q != &_ST_THREADQ); + thread = _ST_THREAD_THREADQ_PTR(q); + if (thread == _ST_CURRENT_THREAD()) + MD_LONGJMP(orig_jb, 1); + memcpy(save_jb, thread->context, sizeof(jmp_buf)); + MD_LONGJMP(thread->context, 1); +} +#endif /* DEBUG */ + diff --git a/trunk/research/st-1.9/st.pc.in b/trunk/research/st-1.9/st.pc.in new file mode 100644 index 0000000000..46c39ec522 --- /dev/null +++ b/trunk/research/st-1.9/st.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: libst +Description: State Thread Library +Version: @VERSION@ +Libs: -L${libdir} -lst +Cflags: -I${includedir} diff --git a/trunk/research/st-1.9/st.spec b/trunk/research/st-1.9/st.spec new file mode 100644 index 0000000000..4914aa1961 --- /dev/null +++ b/trunk/research/st-1.9/st.spec @@ -0,0 +1,79 @@ +Summary: State Threads Library +Name: st +Version: 1.9 +Release: 1 +Copyright: MPL 1.2 or GPL 2+ +Packager: Wesley W. Terpstra +Source: http://prdownloads.sourceforge.net/state-threads/st-%{version}.tar.gz +Prefix: /usr +BuildRoot: /tmp/%{name}-%{version}-build +Group: Development/Libraries + +%description +The State Threads library has an interface similar to POSIX threads. + +However, the threads are actually all run in-process. This type of +threading allows for controlled schedualing points. It is highly useful +for designing robust and extremely scalable internet applications since +there is no resource contention and locking is generally unnecessary. + +It can be combined with traditional threading or multiple process +parallelism to take advantage of multiple processors. + +See: for further +information about how state threads improve performance. + +%package -n libst-devel +Summary: State Threads Library - Development Files +Group: Development/Libraries +Requires: libst1 + +%description -n libst-devel +Development headers and documentation for libst + +%package -n libst1 +Summary: State Threads Library - Shared Libs Major 1 +Group: System/Libraries + +%description -n libst1 +Shared libraries for running applications linked against api version 1. + +%prep +%setup -q + +%build +make CONFIG_GUESS_PATH=/usr/share/automake default-optimized + +%install +if [ -d ${RPM_BUILD_ROOT} ]; then rm -rf ${RPM_BUILD_ROOT}; fi + +mkdir -m 0755 -p ${RPM_BUILD_ROOT}/%{prefix}/lib/pkgconfig +mkdir -m 0755 -p ${RPM_BUILD_ROOT}/%{prefix}/include +mkdir -m 0755 -p ${RPM_BUILD_ROOT}/%{prefix}/share/doc/libst-devel +cp -a obj/libst.* ${RPM_BUILD_ROOT}/%{prefix}/lib +cp -a obj/st.h ${RPM_BUILD_ROOT}/%{prefix}/include +sed "s*@prefix@*%{prefix}*g" ${RPM_BUILD_ROOT}/%{prefix}/lib/pkgconfig/st.pc +cp -a docs/* ${RPM_BUILD_ROOT}/%{prefix}/share/doc/libst-devel/ +cp -a examples ${RPM_BUILD_ROOT}/%{prefix}/share/doc/libst-devel/ + +%post -n libst1 +/sbin/ldconfig %{prefix}/lib + +%files -n libst1 +%defattr(-,root,root) +%{prefix}/lib/lib*.so.* + +%files -n libst-devel +%defattr(-,root,root) +%{prefix}/include/* +%{prefix}/lib/lib*.a +%{prefix}/lib/lib*.so +%{prefix}/lib/pkgconfig/st.pc +%{prefix}/share/doc/libst-devel/* + +%clean +if [ -d ${RPM_BUILD_ROOT} ]; then rm -rf ${RPM_BUILD_ROOT}; fi + +%changelog +* Wed Dec 26 2001 Wesley W. Terpstra +- first rpms for libst-1.3.tar.gz diff --git a/trunk/research/st-1.9/stk.c b/trunk/research/st-1.9/stk.c new file mode 100644 index 0000000000..344552eb65 --- /dev/null +++ b/trunk/research/st-1.9/stk.c @@ -0,0 +1,173 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Portable Runtime library. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Silicon Graphics, Inc. + * + * Portions created by SGI are Copyright (C) 2000-2001 Silicon + * Graphics, Inc. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +/* + * This file is derived directly from Netscape Communications Corporation, + * and consists of extensive modifications made during the year(s) 1999-2000. + */ + +#include +#include +#include +#include +#include +#include "common.h" + + +/* How much space to leave between the stacks, at each end */ +#define REDZONE _ST_PAGE_SIZE + +_st_clist_t _st_free_stacks = ST_INIT_STATIC_CLIST(&_st_free_stacks); +int _st_num_free_stacks = 0; +int _st_randomize_stacks = 0; + +static char *_st_new_stk_segment(int size); + +_st_stack_t *_st_stack_new(int stack_size) +{ + _st_clist_t *qp; + _st_stack_t *ts; + int extra; + + for (qp = _st_free_stacks.next; qp != &_st_free_stacks; qp = qp->next) { + ts = _ST_THREAD_STACK_PTR(qp); + if (ts->stk_size >= stack_size) { + /* Found a stack that is big enough */ + ST_REMOVE_LINK(&ts->links); + _st_num_free_stacks--; + ts->links.next = NULL; + ts->links.prev = NULL; + return ts; + } + } + + /* Make a new thread stack object. */ + if ((ts = (_st_stack_t *)calloc(1, sizeof(_st_stack_t))) == NULL) + return NULL; + extra = _st_randomize_stacks ? _ST_PAGE_SIZE : 0; + ts->vaddr_size = stack_size + 2*REDZONE + extra; + ts->vaddr = _st_new_stk_segment(ts->vaddr_size); + if (!ts->vaddr) { + free(ts); + return NULL; + } + ts->stk_size = stack_size; + ts->stk_bottom = ts->vaddr + REDZONE; + ts->stk_top = ts->stk_bottom + stack_size; + +#ifdef DEBUG + mprotect(ts->vaddr, REDZONE, PROT_NONE); + mprotect(ts->stk_top + extra, REDZONE, PROT_NONE); +#endif + + if (extra) { + long offset = (random() % extra) & ~0xf; + + ts->stk_bottom += offset; + ts->stk_top += offset; + } + + return ts; +} + + +/* + * Free the stack for the current thread + */ +void _st_stack_free(_st_stack_t *ts) +{ + if (!ts) + return; + + /* Put the stack on the free list */ + ST_APPEND_LINK(&ts->links, _st_free_stacks.prev); + _st_num_free_stacks++; +} + + +static char *_st_new_stk_segment(int size) +{ +#ifdef MALLOC_STACK + void *vaddr = malloc(size); +#else + static int zero_fd = -1; + int mmap_flags = MAP_PRIVATE; + void *vaddr; + +#if defined (MD_USE_SYSV_ANON_MMAP) + if (zero_fd < 0) { + if ((zero_fd = open("/dev/zero", O_RDWR, 0)) < 0) + return NULL; + fcntl(zero_fd, F_SETFD, FD_CLOEXEC); + } +#elif defined (MD_USE_BSD_ANON_MMAP) + mmap_flags |= MAP_ANON; +#else +#error Unknown OS +#endif + + vaddr = mmap(NULL, size, PROT_READ | PROT_WRITE, mmap_flags, zero_fd, 0); + if (vaddr == (void *)MAP_FAILED) + return NULL; + +#endif /* MALLOC_STACK */ + + return (char *)vaddr; +} + + +/* Not used */ +#if 0 +void _st_delete_stk_segment(char *vaddr, int size) +{ +#ifdef MALLOC_STACK + free(vaddr); +#else + (void) munmap(vaddr, size); +#endif +} +#endif + +int st_randomize_stacks(int on) +{ + int wason = _st_randomize_stacks; + + _st_randomize_stacks = on; + if (on) + srandom((unsigned int) st_utime()); + + return wason; +} diff --git a/trunk/research/st-1.9/sync.c b/trunk/research/st-1.9/sync.c new file mode 100644 index 0000000000..a71876c5fb --- /dev/null +++ b/trunk/research/st-1.9/sync.c @@ -0,0 +1,369 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Portable Runtime library. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Silicon Graphics, Inc. + * + * Portions created by SGI are Copyright (C) 2000-2001 Silicon + * Graphics, Inc. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +/* + * This file is derived directly from Netscape Communications Corporation, + * and consists of extensive modifications made during the year(s) 1999-2000. + */ + +#include +#include +#include +#include "common.h" + + +extern time_t _st_curr_time; +extern st_utime_t _st_last_tset; +extern int _st_active_count; + +static st_utime_t (*_st_utime)(void) = NULL; + + +/***************************************** + * Time functions + */ + +st_utime_t st_utime(void) +{ + if (_st_utime == NULL) { +#ifdef MD_GET_UTIME + MD_GET_UTIME(); +#else +#error Unknown OS +#endif + } + + return (*_st_utime)(); +} + + +int st_set_utime_function(st_utime_t (*func)(void)) +{ + if (_st_active_count) { + errno = EINVAL; + return -1; + } + + _st_utime = func; + + return 0; +} + + +st_utime_t st_utime_last_clock(void) +{ + return _ST_LAST_CLOCK; +} + + +int st_timecache_set(int on) +{ + int wason = (_st_curr_time) ? 1 : 0; + + if (on) { + _st_curr_time = time(NULL); + _st_last_tset = st_utime(); + } else + _st_curr_time = 0; + + return wason; +} + + +time_t st_time(void) +{ + if (_st_curr_time) + return _st_curr_time; + + return time(NULL); +} + + +int st_usleep(st_utime_t usecs) +{ + _st_thread_t *me = _ST_CURRENT_THREAD(); + + if (me->flags & _ST_FL_INTERRUPT) { + me->flags &= ~_ST_FL_INTERRUPT; + errno = EINTR; + return -1; + } + + if (usecs != ST_UTIME_NO_TIMEOUT) { + me->state = _ST_ST_SLEEPING; + _ST_ADD_SLEEPQ(me, usecs); + } else + me->state = _ST_ST_SUSPENDED; + + _ST_SWITCH_CONTEXT(me); + + if (me->flags & _ST_FL_INTERRUPT) { + me->flags &= ~_ST_FL_INTERRUPT; + errno = EINTR; + return -1; + } + + return 0; +} + + +int st_sleep(int secs) +{ + return st_usleep((secs >= 0) ? secs * (st_utime_t) 1000000LL : + ST_UTIME_NO_TIMEOUT); +} + + +/***************************************** + * Condition variable functions + */ + +_st_cond_t *st_cond_new(void) +{ + _st_cond_t *cvar; + + cvar = (_st_cond_t *) calloc(1, sizeof(_st_cond_t)); + if (cvar) { + ST_INIT_CLIST(&cvar->wait_q); + } + + return cvar; +} + + +int st_cond_destroy(_st_cond_t *cvar) +{ + if (cvar->wait_q.next != &cvar->wait_q) { + errno = EBUSY; + return -1; + } + + free(cvar); + + return 0; +} + + +int st_cond_timedwait(_st_cond_t *cvar, st_utime_t timeout) +{ + _st_thread_t *me = _ST_CURRENT_THREAD(); + int rv; + + if (me->flags & _ST_FL_INTERRUPT) { + me->flags &= ~_ST_FL_INTERRUPT; + errno = EINTR; + return -1; + } + + /* Put caller thread on the condition variable's wait queue */ + me->state = _ST_ST_COND_WAIT; + ST_APPEND_LINK(&me->wait_links, &cvar->wait_q); + + if (timeout != ST_UTIME_NO_TIMEOUT) + _ST_ADD_SLEEPQ(me, timeout); + + _ST_SWITCH_CONTEXT(me); + + ST_REMOVE_LINK(&me->wait_links); + rv = 0; + + if (me->flags & _ST_FL_TIMEDOUT) { + me->flags &= ~_ST_FL_TIMEDOUT; + errno = ETIME; + rv = -1; + } + if (me->flags & _ST_FL_INTERRUPT) { + me->flags &= ~_ST_FL_INTERRUPT; + errno = EINTR; + rv = -1; + } + + return rv; +} + + +int st_cond_wait(_st_cond_t *cvar) +{ + return st_cond_timedwait(cvar, ST_UTIME_NO_TIMEOUT); +} + + +static int _st_cond_signal(_st_cond_t *cvar, int broadcast) +{ + _st_thread_t *thread; + _st_clist_t *q; + + for (q = cvar->wait_q.next; q != &cvar->wait_q; q = q->next) { + thread = _ST_THREAD_WAITQ_PTR(q); + if (thread->state == _ST_ST_COND_WAIT) { + if (thread->flags & _ST_FL_ON_SLEEPQ) + _ST_DEL_SLEEPQ(thread); + + /* Make thread runnable */ + thread->state = _ST_ST_RUNNABLE; + _ST_ADD_RUNQ(thread); + if (!broadcast) + break; + } + } + + return 0; +} + + +int st_cond_signal(_st_cond_t *cvar) +{ + return _st_cond_signal(cvar, 0); +} + + +int st_cond_broadcast(_st_cond_t *cvar) +{ + return _st_cond_signal(cvar, 1); +} + + +/***************************************** + * Mutex functions + */ + +_st_mutex_t *st_mutex_new(void) +{ + _st_mutex_t *lock; + + lock = (_st_mutex_t *) calloc(1, sizeof(_st_mutex_t)); + if (lock) { + ST_INIT_CLIST(&lock->wait_q); + lock->owner = NULL; + } + + return lock; +} + + +int st_mutex_destroy(_st_mutex_t *lock) +{ + if (lock->owner != NULL || lock->wait_q.next != &lock->wait_q) { + errno = EBUSY; + return -1; + } + + free(lock); + + return 0; +} + + +int st_mutex_lock(_st_mutex_t *lock) +{ + _st_thread_t *me = _ST_CURRENT_THREAD(); + + if (me->flags & _ST_FL_INTERRUPT) { + me->flags &= ~_ST_FL_INTERRUPT; + errno = EINTR; + return -1; + } + + if (lock->owner == NULL) { + /* Got the mutex */ + lock->owner = me; + return 0; + } + + if (lock->owner == me) { + errno = EDEADLK; + return -1; + } + + /* Put caller thread on the mutex's wait queue */ + me->state = _ST_ST_LOCK_WAIT; + ST_APPEND_LINK(&me->wait_links, &lock->wait_q); + + _ST_SWITCH_CONTEXT(me); + + ST_REMOVE_LINK(&me->wait_links); + + if ((me->flags & _ST_FL_INTERRUPT) && lock->owner != me) { + me->flags &= ~_ST_FL_INTERRUPT; + errno = EINTR; + return -1; + } + + return 0; +} + + +int st_mutex_unlock(_st_mutex_t *lock) +{ + _st_thread_t *thread; + _st_clist_t *q; + + if (lock->owner != _ST_CURRENT_THREAD()) { + errno = EPERM; + return -1; + } + + for (q = lock->wait_q.next; q != &lock->wait_q; q = q->next) { + thread = _ST_THREAD_WAITQ_PTR(q); + if (thread->state == _ST_ST_LOCK_WAIT) { + lock->owner = thread; + /* Make thread runnable */ + thread->state = _ST_ST_RUNNABLE; + _ST_ADD_RUNQ(thread); + return 0; + } + } + + /* No threads waiting on this mutex */ + lock->owner = NULL; + + return 0; +} + + +int st_mutex_trylock(_st_mutex_t *lock) +{ + if (lock->owner != NULL) { + errno = EBUSY; + return -1; + } + + /* Got the mutex */ + lock->owner = _ST_CURRENT_THREAD(); + + return 0; +} + From 7692373e0260ab155078a719ab0f8a7edd7e5662 Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 12 Oct 2014 21:29:48 +0800 Subject: [PATCH 008/800] remove the unused files. add upp project file --- trunk/research/st-1.9/README | 394 --- trunk/research/st-1.9/docs/fig.gif | Bin 5374 -> 0 bytes trunk/research/st-1.9/docs/notes.html | 434 --- trunk/research/st-1.9/docs/reference.html | 3120 ----------------- trunk/research/st-1.9/docs/st.html | 504 --- trunk/research/st-1.9/docs/timeout_heap.txt | 60 - trunk/research/st-1.9/examples/Makefile | 115 - trunk/research/st-1.9/examples/README | 98 - trunk/research/st-1.9/examples/error.c | 168 - trunk/research/st-1.9/examples/lookupdns.c | 103 - trunk/research/st-1.9/examples/proxy.c | 541 --- trunk/research/st-1.9/examples/res.c | 305 -- trunk/research/st-1.9/examples/server.c | 1025 ------ trunk/research/st-1.9/extensions/Makefile | 91 - trunk/research/st-1.9/extensions/README | 42 - trunk/research/st-1.9/extensions/common.h | 77 - trunk/research/st-1.9/extensions/dnscache.c | 190 - trunk/research/st-1.9/extensions/dnsres.c | 305 -- trunk/research/st-1.9/extensions/lrucache.c | 343 -- .../st-1.9/extensions/print_stk.patch | 367 -- trunk/research/st-1.9/extensions/stx.h | 91 - trunk/research/st-1.9/extensions/stx_fileio.c | 197 -- trunk/research/st-1.9/extensions/stx_fileio.h | 52 - trunk/research/st-1.9/extensions/testdns.c | 112 - trunk/research/st-1.9/libst.def | 51 - trunk/research/st-1.9/st.pc.in | 10 - trunk/research/st-1.9/st.spec | 79 - trunk/research/st-1.9/st/init | 3 + trunk/research/st-1.9/st/st.upp | 16 + 29 files changed, 19 insertions(+), 8874 deletions(-) delete mode 100644 trunk/research/st-1.9/README delete mode 100644 trunk/research/st-1.9/docs/fig.gif delete mode 100644 trunk/research/st-1.9/docs/notes.html delete mode 100644 trunk/research/st-1.9/docs/reference.html delete mode 100644 trunk/research/st-1.9/docs/st.html delete mode 100644 trunk/research/st-1.9/docs/timeout_heap.txt delete mode 100644 trunk/research/st-1.9/examples/Makefile delete mode 100644 trunk/research/st-1.9/examples/README delete mode 100644 trunk/research/st-1.9/examples/error.c delete mode 100644 trunk/research/st-1.9/examples/lookupdns.c delete mode 100644 trunk/research/st-1.9/examples/proxy.c delete mode 100644 trunk/research/st-1.9/examples/res.c delete mode 100644 trunk/research/st-1.9/examples/server.c delete mode 100644 trunk/research/st-1.9/extensions/Makefile delete mode 100644 trunk/research/st-1.9/extensions/README delete mode 100644 trunk/research/st-1.9/extensions/common.h delete mode 100644 trunk/research/st-1.9/extensions/dnscache.c delete mode 100644 trunk/research/st-1.9/extensions/dnsres.c delete mode 100644 trunk/research/st-1.9/extensions/lrucache.c delete mode 100644 trunk/research/st-1.9/extensions/print_stk.patch delete mode 100644 trunk/research/st-1.9/extensions/stx.h delete mode 100644 trunk/research/st-1.9/extensions/stx_fileio.c delete mode 100644 trunk/research/st-1.9/extensions/stx_fileio.h delete mode 100644 trunk/research/st-1.9/extensions/testdns.c delete mode 100644 trunk/research/st-1.9/libst.def delete mode 100644 trunk/research/st-1.9/st.pc.in delete mode 100644 trunk/research/st-1.9/st.spec create mode 100644 trunk/research/st-1.9/st/init create mode 100644 trunk/research/st-1.9/st/st.upp diff --git a/trunk/research/st-1.9/README b/trunk/research/st-1.9/README deleted file mode 100644 index 8ec33df19c..0000000000 --- a/trunk/research/st-1.9/README +++ /dev/null @@ -1,394 +0,0 @@ -WELCOME! - -The State Threads Library is a small application library which provides -a foundation for writing fast and highly scalable Internet applications -(such as web servers, proxy servers, mail transfer agents, and so on, -really any network-data-driven application) on UNIX-like platforms. It -combines the simplicity of the multithreaded programming paradigm, in -which one thread supports each simultaneous connection, with the -performance and scalability of an event-driven state machine -architecture. In other words, this library offers a threading API for -structuring an Internet application as a state machine. For more -details, please see the library documentation in the "docs" directory or -on-line at - - http://state-threads.sourceforge.net/docs/ - -The State Threads Project is an open source project for maintaining and -enhancing the State Threads Library. For more information about this -project, please see - - http://state-threads.sourceforge.net/ - - -BUILDING - -To build the library by hand, use the GNU make utility. Run the make -command (e.g., `gmake') with no arguments to display all supported -targets. - -To build more or less automatically, first set the CONFIG_GUESS_PATH -variable in either osguess.sh or your environment then run "make -default" which guesses your OS and builds. Requires the "config.guess" -utility from GNU autoconf (not included with ST). You can use one from -a larger "main" software project or just use any config.guess available -on your system. You can also get it directly from GNU: -ftp://ftp.gnu.org/gnu/autoconf/ - -To build rpms (RedHat Linux 6.2 or later, Linux/Mandrake, Solaris with -gnome, etc.): - download the latest st-x.y.tar.gz - # rpm -ta st-x.y.tar.gz -The .rpms will land in /usr/src/RPMS/. Install them with: - # rpm -i libst*.rpm -Requires GNU automake and rpm 3.0.3 or later. - -Debian users: - If you run potato, please upgrade to woody. - If you run woody, "apt-get install libst-dev" will get you v1.3. - If you run testing/unstable, you will get the newest available version. - If you *must* have the newest libst in woody, you may follow these - not-recommended instructions: - 1. Add "deb-src unstable main" to your - /etc/apt/sources.list - 2. apt-get update - 3. apt-get source st - 4. cd st-1.4 (or whatever version you got) - 5. debuild - 6. dpkg -i ../*.deb - -If your application uses autoconf to search for dependencies and you -want to search for a given version of libst, you can simply add - PKG_CHECK_MODULES(MYAPP, st >= 1.3 mumble >= 0.2.23) -to your configure.ac/in. This will define @MYAPP_LIBS@ and -@MYAPP_CFLAGS@ which you may then use in your Makefile.am/in files to -link against mumble and st. - - -LICENSE - -The State Threads library is a derivative of the Netscape Portable -Runtime library (NSPR). All source code in this directory is -distributed under the terms of the Mozilla Public License (MPL) version -1.1 or the GNU General Public License (GPL) version 2 or later. For -more information about these licenses please see -http://www.mozilla.org/MPL/ and http://www.gnu.org/copyleft/. - -All source code in the "examples" directory is distributed under the BSD -style license. - - -PLATFORMS - -Please see the "docs/notes.html" file for the list of currently -supported platforms. - - -DEBUGGER SUPPORT - -It's almost impossible to print SP and PC in a portable way. The only -way to see thread's stack platform-independently is to actually jump to -the saved context. That's what the _st_iterate_threads() function does. -Do the following to iterate over all threads: - -- set the _st_iterate_threads_flag to 1 in debugger -- set breakpoint at the _st_show_thread_stack() function - (which does nothing) -- call the _st_iterate_threads() function which jumps to the - next thread -- at each break you can explore thread's stack -- continue -- when iteration is complete, you return to the original - point (you can see thread id and a message as arguments of - the _st_show_thread_stack() function). - -You can call _st_iterate_threads() in three ways: - -- Insert it into your source code at the point you want to - go over threads. -- Just run application and this function will be called at - the first context switch. -- Call it directly from the debugger at any point. - -This works with gdb and dbx. - -Example using gdb: - -(gdb) set _st_iterate_threads_flag = 1 -(gdb) b _st_show_thread_stack -... -(gdb) call _st_iterate_threads() -... -(gdb) bt -... -(gdb) c -... -(gdb) bt -... -(gdb) c -... -and so on... - -_st_iterate_threads_flag will be set to 0 automatically -after iteration is over or you can set it to 0 at any time -to stop iteration. - -Sometimes gdb complains about SIGSEGV when you call a function -directly at gdb command-line. It can be ignored -- just call the -same function right away again, it works just fine. For example: - -(gdb) set _st_iterate_threads_flag = 1 -(gdb) b _st_show_thread_stack -Breakpoint 1 at 0x809bbbb: file sched.c, line 856. -(gdb) call _st_iterate_threads() -Program received signal SIGSEGV, Segmentation fault. -.... -(gdb) # just call the function again: -(gdb) call _st_iterate_threads() -Breakpoint 1, _st_show_thread_stack (thread=0x4017aee4, messg=0x80ae7a2 -"Iteration started") at sched.c:856 -856 } -.... - -You can use simple gdb command-line scripting to display -all threads and their stack traces at once: - -(gdb) while _st_iterate_threads_flag - >bt - >c - >end -.... - -Another script to stop at the thread with the specific thread id -(e.g., 0x40252ee4): - -(gdb) # set the flag again: -(gdb) set _st_iterate_threads_flag = 1 -(gdb) call _st_iterate_threads() -Breakpoint 1, _st_show_thread_stack (thread=0x4017aee4, messg=0x80ae7a2 -"Iteration started") at sched.c:856 -856 } -.... -(gdb) while thread != 0x40252ee4 - >c - >end -.... -.... -Breakpoint 1, _st_show_thread_stack (thread=0x40252ee4, messg=0x0) at -sched.c:856 -856 } -(gdb) bt -.... -(gdb) # don't want to continue iteration, unset the flag: -(gdb) set _st_iterate_threads_flag = 0 -(gdb) c -Continuing. -Breakpoint 1, _st_show_thread_stack (thread=0x0, messg=0x80ae78e "Iteration -completed") - at sched.c:856 -856 } -(gdb) c -Continuing. -(gdb) return -Make selected stack frame return now? (y or n) y -#0 0x4011254e in __select () - from /lib/libc.so.6 -(gdb) detach - - -CHANGE LOG - -Changes from 1.8 to 1.9. ------------------------- -o Support 32-bit and 64-bit Intel Macs. - -o Added ST_VERSION string, and ST_VERSION_MAJOR and ST_VERSION_MINOR - [bug 1796801]. - -o Fixed some compiler warnings, based on a patch from Brian Wellington - [bug 1932741]. - - -Changes from 1.7 to 1.8. --------------------------- -o Added support for kqueue and epoll on platforms that support them. - Added ability to choose the event notification system at program - startup. - -o Long-overdue public definitions of ST_UTIME_NO_TIMEOUT (-1ULL) and - ST_UTIME_NO_WAIT (0) [bug 1514436]. - -o Documentation patch for st_utime() [bug 1514484]. - -o Documentation patch for st_timecache_set() [bug 1514486]. - -o Documentation patch for st_netfd_serialize_accept() [bug 1514494]. - -o Added st_writev_resid() [rfe 1538344]. - -o Added st_readv_resid() [rfe 1538768] and, for symmetry, st_readv(). - - -Changes from 1.6 to 1.7. ------------------------- -o Support glibc 2.4, which breaks programs that manipulate jump buffers. - Replaced Linux IA64 special cases with new md.S that covers all - Linux. - - -Changes from 1.5.2 to 1.6. --------------------------- -none - - -Changes from 1.5.1 to 1.5.2. ----------------------------- -o Alfred Perlstein's context switch callback feature. - -o Claus Assmann's st_recvmsg/st_sendmsg wrappers. - -o Extra stack padding for platforms that need it. - -o Ron Arts's timeout clarifications in the reference manual. - -o Raymond Bero and Anton Berezin's AMD64 FreeBSD port. - -o Claus Assmann's AMD64 SunOS 5.10 port. - -o Claus Assmann's AMD64 OpenBSD port. - -o Michael Abd-El-Malek's Mac OS X port. - -o Michael Abd-El-Malek's stack printing patch. - - -Changes from 1.5.0 to 1.5.1. ----------------------------- -o Andreas Gustafsson's USE_POLL fix. - -o Gene's st_set_utime_function() enhancement. - - -Changes from 1.4 to 1.5.0. --------------------------- -o Andreas Gustafsson's performance patch. - -o New extensions: Improved DNS resolver, generic LRU cache, in-process - DNS cache, and a program to test the resolver and cache. - -o Support for AMD Opteron 64-bit CPUs under Linux. - -o Support for SPARC-64 under Solaris. - -o Andreas Gustafsson's support for VAX under NetBSD. - -o Changed unportable #warning directives in md.h to #error. - - -Changes from 1.3 to 1.4. ------------------------- -o Andreas Gustafsson's NetBSD port. - -o Wesley W. Terpstra's Darwin (MacOS X) port. - -o Support for many CPU architectures under Linux and *BSD. - -o Renamed private typedefs so they don't conflict with public ones any - more. - -o common.h now includes public.h for strict prototyping. - -o Joshua Levy's recommendation to make st_connect() and st_sendto() - accept const struct sockaddr pointers, as the originals do. - -o Clarified the documentation regarding blocking vs. non-blocking I/O. - -o Cygwin support. - -o Created the extensions directory. - -o Fixed warnings from ia64asm.S. - - -Changes from 1.2 to 1.3. ------------------------- -o Added st_read_resid() and st_write_resid() to allow the caller to know - how much data was transferred before an error occurred. Updated - documentation. - -o Updated project link, copyrights, and documentation regarding - timeouts. Added comment to st_connect(). - -o Optimized the _st_add_sleep_q() function in sched.c. Now we walk the - sleep queue *backward* when inserting a thread into it. When you - have lots (hundreds) of threads and several timeout values, it takes - a while to insert a thread at the appropriate point in the sleep - queue. The idea is that often this appropriate point is closer to - the end of the queue rather than the beginning. Measurements show - performance improves with this change. In any case this change - should do no harm. - -o Added a hint of when to define USE_POLL and when not to, to the - Makefile. - -o Added debugging support (files common.h and sched.c). See above. - -o Decreased the number of reallocations of _ST_POLLFDS in sched.c. - Inspired by Lev Walkin. - -o Fixed st_usleep(-1) and st_sleep(-1), and added a warning to the - documentation about too-large timeouts. - -o Linux/*BSD Alpha port. - -o Wesley W. Terpstra modernized the build process: - - properly build relocatable libraries under bsd and linux - - use library versioning - - added rpm spec file - - added debian/ files - See above for build instructions. - - -Changes from 1.1 to 1.2. ------------------------- -o Added st_randomize_stacks(). - -o Added a patch contributed by Sascha Schumann. - - -Changes from 1.0 to 1.1. ------------------------- -o Relicensed under dual MPL-GPL. - -o OpenBSD port. - -o Compile-time option to use poll() instead of select() for - event polling (see Makefile). - This is useful if you want to support a large number of open - file descriptors (larger than FD_SETSIZE) within a single - process. - -o Linux IA-64 port. - Two issues make IA-64 different from other platforms: - - - Besides the traditional call stack in memory, IA-64 uses the - general register stack. Thus each thread needs a backing store - for the register stack in addition to the memory stack. - - - Current implementation of setjmp()/longjmp() can not be used - for thread context-switching since it assumes that only one - register stack exists. Using special assembly functions for - context-switching is unavoidable. - -o Thread stack capping on IRIX. - This allows some profiling tools (such as SpeedShop) to know when - to stop unwinding the stack. Without this libexc, used by SpeedShop, - traces right off the stack and crashes. - -o Miscellaneous documentation additions. - - -COPYRIGHTS - -Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. -All Rights Reserved. diff --git a/trunk/research/st-1.9/docs/fig.gif b/trunk/research/st-1.9/docs/fig.gif deleted file mode 100644 index 7265a05db4f516b44fcf37c3949922ff4f62999d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5374 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 diff --git a/trunk/research/st-1.9/docs/notes.html b/trunk/research/st-1.9/docs/notes.html deleted file mode 100644 index 5a24369e22..0000000000 --- a/trunk/research/st-1.9/docs/notes.html +++ /dev/null @@ -1,434 +0,0 @@ - - -State Threads Library Programming Notes - - -

Programming Notes

-

- -

- -

-


-

- -

Porting

-The State Threads library uses OS concepts that are available in some -form on most UNIX platforms, making the library very portable across -many flavors of UNIX. However, there are several parts of the library -that rely on platform-specific features. Here is the list of such parts: -

-

    -
  • Thread context initialization: Two ingredients of the -jmp_buf -data structure (the program counter and the stack pointer) have to be -manually set in the thread creation routine. The jmp_buf data -structure is defined in the setjmp.h header file and differs from -platform to platform. Usually the program counter is a structure member -with PC in the name and the stack pointer is a structure member -with SP in the name. One can also look in the -Netscape's NSPR library source -which already has this code for many UNIX-like platforms -(mozilla/nsprpub/pr/include/md/*.h files). -

    -Note that on some BSD-derived platforms _setjmp(3)/_longjmp(3) -calls should be used instead of setjmp(3)/longjmp(3) (that is -the calls that manipulate only the stack and registers and do not -save and restore the process's signal mask).

  • -

    -Starting with glibc 2.4 on Linux the opacity of the jmp_buf data -structure is enforced by setjmp(3)/longjmp(3) so the -jmp_buf ingredients cannot be accessed directly anymore (unless -special environmental variable LD_POINTER_GUARD is set before application -execution). To avoid dependency on custom environment, the State Threads -library provides setjmp/longjmp replacement functions for -all Intel CPU architectures. Other CPU architectures can also be easily -supported (the setjmp/longjmp source code is widely available for -many CPU architectures). -

    -

  • High resolution time function: Some platforms (IRIX, Solaris) -provide a high resolution time function based on the free running hardware -counter. This function returns the time counted since some arbitrary -moment in the past (usually machine power up time). It is not correlated in -any way to the time of day, and thus is not subject to resetting, -drifting, etc. This type of time is ideal for tasks where cheap, accurate -interval timing is required. If such a function is not available on a -particular platform, the gettimeofday(3) function can be used -(though on some platforms it involves a system call). -

    -

  • The stack growth direction: The library needs to know whether the -stack grows toward lower (down) or higher (up) memory addresses. -One can write a simple test program that detects the stack growth direction -on a particular platform.
  • -

    -

  • Non-blocking attribute inheritance: On some platforms (e.g. IRIX) -the socket created as a result of the accept(2) call inherits the -non-blocking attribute of the listening socket. One needs to consult the manual -pages or write a simple test program to see if this applies to a specific -platform.
  • -

    -

  • Anonymous memory mapping: The library allocates memory segments -for thread stacks by doing anonymous memory mapping (mmap(2)). This -mapping is somewhat different on SVR4 and BSD4.3 derived platforms. -

    -The memory mapping can be avoided altogether by using malloc(3) for -stack allocation. In this case the MALLOC_STACK macro should be -defined.

  • -
-

-All machine-dependent feature test macros should be defined in the -md.h header file. The assembly code for setjmp/longjmp -replacement functions for all CPU architectures should be placed in -the md.S file. -

-The current version of the library is ported to: -

    -
  • IRIX 6.x (both 32 and 64 bit)
  • -
  • Linux (kernel 2.x and glibc 2.x) on x86, Alpha, MIPS and MIPSEL, - SPARC, ARM, PowerPC, 68k, HPPA, S390, IA-64, and Opteron (AMD-64)
  • -
  • Solaris 2.x (SunOS 5.x) on x86, AMD64, SPARC, and SPARC-64
  • -
  • AIX 4.x
  • -
  • HP-UX 11 (both 32 and 64 bit)
  • -
  • Tru64/OSF1
  • -
  • FreeBSD on x86, AMD64, and Alpha
  • -
  • OpenBSD on x86, AMD64, Alpha, and SPARC
  • -
  • NetBSD on x86, Alpha, SPARC, and VAX
  • -
  • MacOS X (Darwin) on PowerPC (32 bit) and Intel (both 32 and 64 bit) [universal]
  • -
  • Cygwin
  • -
-

- - -

Signals

-Signal handling in an application using State Threads should be treated the -same way as in a classical UNIX process application. There is no such -thing as per-thread signal mask, all threads share the same signal handlers, -and only asynchronous-safe functions can be used in signal handlers. -However, there is a way to process signals synchronously by converting a -signal event to an I/O event: a signal catching function does a write to -a pipe which will be processed synchronously by a dedicated signal handling -thread. The following code demonstrates this technique (error handling is -omitted for clarity): -
-
-/* Per-process pipe which is used as a signal queue. */
-/* Up to PIPE_BUF/sizeof(int) signals can be queued up. */
-int sig_pipe[2];
-
-/* Signal catching function. */
-/* Converts signal event to I/O event. */
-void sig_catcher(int signo)
-{
-  int err;
-
-  /* Save errno to restore it after the write() */
-  err = errno;
-  /* write() is reentrant/async-safe */
-  write(sig_pipe[1], &signo, sizeof(int));
-  errno = err;
-}
-
-/* Signal processing function. */
-/* This is the "main" function of the signal processing thread. */
-void *sig_process(void *arg)
-{
-  st_netfd_t nfd;
-  int signo;
-
-  nfd = st_netfd_open(sig_pipe[0]);
-
-  for ( ; ; ) {
-    /* Read the next signal from the pipe */
-    st_read(nfd, &signo, sizeof(int), ST_UTIME_NO_TIMEOUT);
-
-    /* Process signal synchronously */
-    switch (signo) {
-    case SIGHUP:
-      /* do something here - reread config files, etc. */
-      break;
-    case SIGTERM:
-      /* do something here - cleanup, etc. */
-      break;
-      /*      .
-              .
-         Other signals
-              .
-              .
-      */
-    }
-  }
-
-  return NULL;
-}
-
-int main(int argc, char *argv[])
-{
-  struct sigaction sa;
-        .
-        .
-        .
-
-  /* Create signal pipe */
-  pipe(sig_pipe);
-
-  /* Create signal processing thread */
-  st_thread_create(sig_process, NULL, 0, 0);
-
-  /* Install sig_catcher() as a signal handler */
-  sa.sa_handler = sig_catcher;
-  sigemptyset(&sa.sa_mask);
-  sa.sa_flags = 0;
-  sigaction(SIGHUP, &sa, NULL);
-
-  sa.sa_handler = sig_catcher;
-  sigemptyset(&sa.sa_mask);
-  sa.sa_flags = 0;
-  sigaction(SIGTERM, &sa, NULL);
-
-        .
-        .
-        .
-      
-}
-
-
-

-Note that if multiple processes are used (see below), the signal pipe should -be initialized after the fork(2) call so that each process has its -own private pipe. -

- - -

Intra-Process Synchronization

-Due to the event-driven nature of the library scheduler, the thread context -switch (process state change) can only happen in a well-known set of -library functions. This set includes functions in which a thread may -"block": I/O functions (st_read(), st_write(), etc.), -sleep functions (st_sleep(), etc.), and thread synchronization -functions (st_thread_join(), st_cond_wait(), etc.). As a result, -process-specific global data need not to be protected by locks since a thread -cannot be rescheduled while in a critical section (and only one thread at a -time can access the same memory location). By the same token, -non thread-safe functions (in a traditional sense) can be safely used with -the State Threads. The library's mutex facilities are practically useless -for a correctly written application (no blocking functions in critical -section) and are provided mostly for completeness. This absence of locking -greatly simplifies an application design and provides a foundation for -scalability. -

- - -

Inter-Process Synchronization

-The State Threads library makes it possible to multiplex a large number -of simultaneous connections onto a much smaller number of separate -processes, where each process uses a many-to-one user-level threading -implementation (N of M:1 mappings rather than one M:N -mapping used in native threading libraries on some platforms). This design -is key to the application's scalability. One can think about it as if a -set of all threads is partitioned into separate groups (processes) where -each group has a separate pool of resources (virtual address space, file -descriptors, etc.). An application designer has full control of how many -groups (processes) an application creates and what resources, if any, -are shared among different groups via standard UNIX inter-process -communication (IPC) facilities.

-There are several reasons for creating multiple processes: -

-

    -
  • To take advantage of multiple hardware entities (CPUs, disks, etc.) -available in the system (hardware parallelism).
  • -

    -

  • To reduce risk of losing a large number of user connections when one of -the processes crashes. For example, if C user connections (threads) -are multiplexed onto P processes and one of the processes crashes, -only a fraction (C/P) of all connections will be lost.
  • -

    -

  • To overcome per-process resource limitations imposed by the OS. For -example, if select(2) is used for event polling, the number of -simultaneous connections (threads) per process is -limited by the FD_SETSIZE parameter (see select(2)). -If FD_SETSIZE is equal to 1024 and each connection needs one file -descriptor, then an application should create 10 processes to support 10,000 -simultaneous connections.
  • -
-

-Ideally all user sessions are completely independent, so there is no need for -inter-process communication. It is always better to have several separate -smaller process-specific resources (e.g., data caches) than to have one large -resource shared (and modified) by all processes. Sometimes, however, there -is a need to share a common resource among different processes. In that case, -standard UNIX IPC facilities can be used. In addition to that, there is a way -to synchronize different processes so that only the thread accessing the -shared resource will be suspended (but not the entire process) if that resource -is unavailable. In the following code fragment a pipe is used as a counting -semaphore for inter-process synchronization: -

-#ifndef PIPE_BUF
-#define PIPE_BUF 512  /* POSIX */
-#endif
-
-/* Semaphore data structure */
-typedef struct ipc_sem {
-  st_netfd_t rdfd;  /* read descriptor */
-  st_netfd_t wrfd;  /* write descriptor */
-} ipc_sem_t;
-
-/* Create and initialize the semaphore. Should be called before fork(2). */
-/* 'value' must be less than PIPE_BUF. */
-/* If 'value' is 1, the semaphore works as mutex. */
-ipc_sem_t *ipc_sem_create(int value)
-{
-  ipc_sem_t *sem;
-  int p[2];
-  char b[PIPE_BUF];
-
-  /* Error checking is omitted for clarity */
-  sem = malloc(sizeof(ipc_sem_t));
-
-  /* Create the pipe */
-  pipe(p);
-  sem->rdfd = st_netfd_open(p[0]);
-  sem->wrfd = st_netfd_open(p[1]);
-
-  /* Initialize the semaphore: put 'value' bytes into the pipe */
-  write(p[1], b, value);
-
-  return sem;
-}
-
-/* Try to decrement the "value" of the semaphore. */
-/* If "value" is 0, the calling thread blocks on the semaphore. */
-int ipc_sem_wait(ipc_sem_t *sem)
-{
-  char c;
-
-  /* Read one byte from the pipe */
-  if (st_read(sem->rdfd, &c, 1, ST_UTIME_NO_TIMEOUT) != 1)
-    return -1;
-
-  return 0;
-}
-
-/* Increment the "value" of the semaphore. */
-int ipc_sem_post(ipc_sem_t *sem)
-{
-  char c;
-
-  if (st_write(sem->wrfd, &c, 1, ST_UTIME_NO_TIMEOUT) != 1)
-    return -1;
-
-  return 0;
-}
-
-
-

- -Generally, the following steps should be followed when writing an application -using the State Threads library: -

-

    -
  1. Initialize the library (st_init()).
  2. -

    -

  3. Create resources that will be shared among different processes: - create and bind listening sockets, create shared memory segments, IPC - channels, synchronization primitives, etc.
  4. -

    -

  5. Create several processes (fork(2)). The parent process should - either exit or become a "watchdog" (e.g., it starts a new process when - an existing one crashes, does a cleanup upon application termination, - etc.).
  6. -

    -

  7. In each child process create a pool of threads - (st_thread_create()) to handle user connections.
  8. -
-

- - -

Non-Network I/O

- -The State Threads architecture uses non-blocking I/O on -st_netfd_t objects for concurrent processing of multiple user -connections. This architecture has a drawback: the entire process and -all its threads may block for the duration of a disk or other -non-network I/O operation, whether through State Threads I/O functions, -direct system calls, or standard I/O functions. (This is applicable -mostly to disk reads; disk writes are usually performed -asynchronously -- data goes to the buffer cache to be written to disk -later.) Fortunately, disk I/O (unlike network I/O) usually takes a -finite and predictable amount of time, but this may not be true for -special devices or user input devices (including stdin). Nevertheless, -such I/O reduces throughput of the system and increases response times. -There are several ways to design an application to overcome this -drawback: - -

-

-

- - -

Timeouts

- -The timeout parameter to st_cond_timedwait() and the -I/O functions, and the arguments to st_sleep() and -st_usleep() specify a maximum time to wait since the last -context switch not since the beginning of the function call. - -

The State Threads' time resolution is actually the time interval -between context switches. That time interval may be large in some -situations, for example, when a single thread does a lot of work -continuously. Note that a steady, uninterrupted stream of network I/O -qualifies for this description; a context switch occurs only when a -thread blocks. - -

If a specified I/O timeout is less than the time interval between -context switches the function may return with a timeout error before -that amount of time has elapsed since the beginning of the function -call. For example, if eight milliseconds have passed since the last -context switch and an I/O function with a timeout of 10 milliseconds -blocks, causing a switch, the call may return with a timeout error as -little as two milliseconds after it was called. (On Linux, -select()'s timeout is an upper bound on the amount of -time elapsed before select returns.) Similarly, if 12 ms have passed -already, the function may return immediately. - -

In almost all cases I/O timeouts should be used only for detecting a -broken network connection or for preventing a peer from holding an idle -connection for too long. Therefore for most applications realistic I/O -timeouts should be on the order of seconds. Furthermore, there's -probably no point in retrying operations that time out. Rather than -retrying simply use a larger timeout in the first place. - -

The largest valid timeout value is platform-dependent and may be -significantly less than INT_MAX seconds for select() -or INT_MAX milliseconds for poll(). Generally, you -should not use timeouts exceeding several hours. Use -ST_UTIME_NO_TIMEOUT (-1) as a special value to -indicate infinite timeout or indefinite sleep. Use -ST_UTIME_NO_WAIT (0) to indicate no waiting at all. - -

-


-

- - - diff --git a/trunk/research/st-1.9/docs/reference.html b/trunk/research/st-1.9/docs/reference.html deleted file mode 100644 index 3c9c7bd783..0000000000 --- a/trunk/research/st-1.9/docs/reference.html +++ /dev/null @@ -1,3120 +0,0 @@ - - -State Threads Library Reference - - - -

State Threads Library Reference

- -
-
Types
-
st_thread_t
-
st_cond_t
-
st_mutex_t
-
st_utime_t
-
st_netfd_t
-
st_switch_cb_t
-

-

Error Handling
-

-

Library Initialization
-

-

st_init()
-
st_getfdlimit()
-
st_set_eventsys()
-
st_get_eventsys()
-
st_get_eventsys_name()
-
st_set_utime_function()
-
st_timecache_set()
-
st_randomize_stacks()
-

-

st_switch_cb_t type
-
st_set_switch_in_cb()
-
st_set_switch_out_cb()
-

-

Thread Control and Identification
-

-

st_thread_t type
-
st_thread_create()
-
st_thread_exit()
-
st_thread_join()
-
st_thread_self()
-
st_thread_interrupt()
-
st_sleep()
-
st_usleep()
-
st_randomize_stacks()
-

-

Per-Thread Private Data
-

-

st_key_create()
-
st_key_getlimit()
-
st_thread_setspecific()
-
st_thread_getspecific()
-

-

Synchronization
-

-

st_cond_t type
-
st_cond_new()
-
st_cond_destroy()
-
st_cond_wait()
-
st_cond_timedwait()
-
st_cond_signal()
-
st_cond_broadcast()
-

-

st_mutex_t type
-
st_mutex_new()
-
st_mutex_destroy()
-
st_mutex_lock()
-
st_mutex_trylock()
-
st_mutex_unlock()
-

-

Timing
-

-

st_utime_t type
-
st_utime()
-
st_set_utime_function()
-
st_timecache_set()
-
st_time()
-

-

I/O Functions
-

-

st_netfd_t type
-
st_netfd_open()
-
st_netfd_open_socket()
-
st_netfd_free()
-
st_netfd_close()
-
st_netfd_fileno()
-
st_netfd_setspecific()
-
st_netfd_getspecific()
-
st_netfd_serialize_accept()
-
-
st_netfd_poll()
-

-

st_accept()
-
st_connect()
-
st_read()
-
st_read_fully()
-
st_read_resid()
-
st_readv()
-
st_readv_resid()
-
st_write()
-
st_write_resid()
-
st_writev()
-
st_writev_resid()
-
st_recvfrom()
-
st_sendto()
-
st_recvmsg()
-
st_sendmsg()
-

-

st_open()
-
st_poll()
-

-

Program Structure
-

-

List of Blocking Functions
-

-

-

-


-

- - - -

Types

- -The State Thread library defines the following types in the st.h -header file: -

-

-
st_thread_t
-
st_cond_t
-
st_mutex_t
-
st_utime_t
-
st_netfd_t
-
-

-


-

- - -

st_thread_t

- -Thread type. -

-

Syntax
- -
-#include <st.h>
-
-typedef void *  st_thread_t;
-
-

-

Description
- -A thread is represented and identified by a pointer to an opaque data -structure. This pointer is a required parameter for most of the functions -that operate on threads. -

-The thread identifier remains valid until the thread returns from its root -function and, if the thread was created joinable, is joined. -

-


-

- - -

st_cond_t

- -Condition variable type. -

-

Syntax
- -
-#include <st.h>
-
-typedef void *  st_cond_t;
-
-

-

Description
- -A condition variable is an opaque object identified by a pointer. -Condition variables provide synchronization primitives to wait for or wake -up threads waiting for certain conditions to be satisfied. -

-In the State Threads library there is no need to lock a mutex before -waiting on a condition variable. -

-


-

- - -

st_mutex_t

- -Mutex type. -

-

Syntax
- -
-#include <st.h>
-
-typedef void *  st_mutex_t;
-
-

-

Description
- -A mutex is an opaque object identified by a pointer. -Mutual exclusion locks (mutexes) are used to serialize the execution of -threads through critical sections of code. -

-If application using the State Threads library is written with no -I/O or control yielding in critical sections (that is no -blocking functions in critical sections), then there is -no need for mutexes.

-These mutexes can only be used for intra-process thread synchronization. -They cannot be used for inter-process synchronization. -

-


-

- - -

st_utime_t

- -High resolution time type ("u" stands for "micro"). -

-

Syntax
- -
-#include <st.h>
-
-typedef unsigned long long  st_utime_t;
-
-

-

Description
- -This datatype (unsigned 64-bit integer) represents high-resolution real time -expressed in microseconds since some arbitrary time in the past. It is not -correlated in any way to the time of day. -

-


-

- - -

st_netfd_t

- -File descriptor type. -

-

Syntax
- -
-#include <st.h>
-
-typedef void *  st_netfd_t;
-
-

-

Description
- -This datatype typically represents any open end point of network -communication (socket, end point of a pipe, FIFO, etc.) but can -encapsulate any open file descriptor. Objects of this type are -identified by a pointer to an opaque data structure. - -

-


-

- - -

st_switch_cb_t

- -Context switch callback function type. -

-

Syntax
- -
-#include <st.h>
-
-typedef void (*st_switch_cb_t)(void);
-
-

-

Description
- -This datatype is a convenience type for describing a pointer -to a function that will be called when a thread is set to stop -or set to run. -This feature is available only when ST_SWITCH_CB is defined -in <st.h>. - -

-


-

- - -

Error Handling

- - -All State Threads library non-void functions return on success either a -non-negative integer or a pointer to a newly created object (constructor-type -functions). On failure they return either -1 or a NULL -pointer respectively and set global errno to indicate the error. -It is safe to use errno because it is set right before the function -return and only one thread at a time can modify its value.

-The perror(3) function can be used to produce an error message on the -standard error output. -

-


-

- - -

Library Initialization

- -

-

-
st_init()
-
st_getfdlimit()
-
st_set_eventsys()
-
st_get_eventsys()
-
st_get_eventsys_name()
-

-These functions operate on a callback function of type -st_switch_cb_t: -

st_set_switch_in_cb()
-
st_set_switch_out_cb()
-
-

-


-

- - -

st_init()

- -Initializes the runtime. -

-

Syntax
- -
-#include <st.h>
-
-int st_init(void);
-
-

-

Parameters
-None. -

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error. -

-

Description
-This function initializes the library runtime. It should be called near -the beginning of the application's main() function before any other -State Threads library function is called.

-Among other things, this function limits the number of open file descriptors -to the OS imposed per-process maximum number or, if select(2) is -used, to FD_SETSIZE, whichever is less (getrlimit(2)). -This limit can be -retrieved by st_getfdlimit(). It also sets the -disposition of the SIGPIPE signal to SIG_IGN (to be ignored) -(signal(5)). -

-Unlike POSIX threads, a new process created by the fork(2) system -call is an exact copy of the calling process and all state threads -which are running in the parent do exist in the child. That means that -st_init() may be called either before or after multiple processes -are created by fork(2). -

-If the library runtime is not properly initialized (e.g., st_init() -is accidentally omitted), then the process will receive either an arithmetic -exception (SIGFPE or SIGTRAP) or segmentation fault (SIGSEGV) signal upon -new thread creation or the first context switch, respectively. -

-


-

- -

st_getfdlimit()

- -Returns the maximum number of file descriptors that the calling process -can open. -

-

Syntax
- -
-#include <st.h>
-
-int st_getfdlimit(void);
-
-

-

Parameters
-None. -

-

Returns
-The maximum number of file descriptors that the calling process can open. -If this function is called before the library is successfully initialized by -st_init(), a value of -1 is returned. -

-

Description
-This function returns the limit on the number of open file descriptors which -is set by the st_init() function. -

-


-

- - -

st_set_eventsys()

- -Sets event notification mechanism. -

-

Syntax
- -
-#include <st.h>
-
-int st_set_eventsys(int eventsys);
-
-

-

Parameters
-st_set_eventsys() has the following parameter:

-eventsys

-An integer value identifying selected event notification mechanism. The -following values are defined in the st.h header file: -

- - - - - - - - - - - - - - - -
ST_EVENTSYS_DEFAULTUse default event notification mechanism. Usually it's select(2) -but if the library was compiled with the USE_POLL macro defined -then the default is poll(2).
ST_EVENTSYS_SELECTUse select(2) as an event notification mechanism.
ST_EVENTSYS_POLLUse poll(2) as an event notification mechanism.
ST_EVENTSYS_ALTUse an alternative event notification mechanism. The actual -mechanism selected depends on OS support. For example, epoll(4) -will be used on Linux if supported and kqueue(2) will be used -on FreeBSD/OpenBSD. If the OS supports no alternative event -notification mechanism, setting ST_EVENTSYS_ALT has no effect -and the ST_EVENTSYS_DEFAULT mechanism will be used.
-

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error:

- - - - - -
EINVAL -The supplied eventsys parameter has an invalid value. -
EBUSY -The event notification mechanism has already been set. -
-

-

Description
-This function sets the event notification mechanism that will be used by -the State Threads library. To have any effect, it must be called -before the st_init() function which performs -the actual initialization. If st_set_eventsys() is not called, -st_init() will set the ST_EVENTSYS_DEFAULT -mechanism. The mechanism cannot be changed once set. -

-There are no strict rules for selecting an event notification -mechanism. The "best" one depends on how your application behaves. -Try a few to see which one works best for you. As a rule of -thumb, you should use the ST_EVENTSYS_ALT mechanism if your -application deals with a very large number of network connections of -which only a few are active at once. -

-


-

- -

st_get_eventsys()

- -Returns the integer value identifying the event notification mechanism -being used by the State Threads library. -

-

Syntax
- -
-#include <st.h>
-
-int st_get_eventsys(void);
-
-

-

Parameters
-None. -

-

Returns
-The integer value identifying the current event notification mechanism. -This value can be one of the following (see st_set_eventsys()): -ST_EVENTSYS_SELECT, ST_EVENTSYS_POLL, or -ST_EVENTSYS_ALT. Future versions of the library may return other -values. If a mechanism hasn't been set yet, a value of -1 is returned. -

-

Description
-This function returns the integer value identifying the event notification -mechanism which is actually being used by the State Threads library. -

-


-

- -

st_get_eventsys_name()

- -Returns the name of the event notification mechanism being used by the -State Threads library. -

-

Syntax
- -
-#include <st.h>
-
-const char *st_get_eventsys_name(void);
-
-

-

Parameters
-None. -

-

Returns
-The string identifying the current event notification mechanism. If a -mechanism hasn't been set yet (see st_set_eventsys()), an empty string is -returned. Possible return values are "select", -"poll", "kqueue", or "epoll". Future versions -of the library may return other values. -

-

Description
-This function returns the string identifying the event notification -mechanism which is actually being used by the State Threads library. -

-


-

- - -

st_set_switch_in_cb()

- - -

st_set_switch_out_cb()

-
-Set the optional callback function for thread switches. -

-

Syntax
- -
-#include <st.h>
-
-st_switch_cb_t st_set_switch_in_cb(st_switch_cb_t cb);
-st_switch_cb_t st_set_switch_out_cb(st_switch_cb_t cb);
-
-

-

Parameters
-st_set_switch_in_cb() and st_set_switch_out_cb() have the -following parameter:

-cb

-A function to be called when a thread is resumed and stopped respectively.

-

Returns
-The previous callback function pointer. -

-

Description
-These functions set the callback for when a thread is resumed and stopped -respectively. After being called any thread switch will call the callback. -Use a NULL pointer to disable the callback (this is the default). -Use st_thread_self() or thread -specific data to differentiate between threads.

-These functions can be called at any time.

-This feature is available only when ST_SWITCH_CB is defined -in <st.h>. -

-


-

- - -

Thread Control and Identification

- -

-These functions operate on a thread object of type -st_thread_t. -

-

-
st_thread_create()
-
st_thread_exit()
-
st_thread_join()
-
st_thread_self()
-
st_thread_interrupt()
-
st_sleep()
-
st_usleep()
-
st_randomize_stacks()
-
-

-


-

- -

st_thread_create()

- -Creates a new thread. -

-

Syntax
- -
-#include <st.h>
-
-st_thread_t st_thread_create(void *(*start)(void *arg), void *arg,
-                             int joinable, int stack_size);
-
-
-

-

Parameters
-st_thread_create() has the following parameters:

-start

-A pointer to the thread's start function, which is called as the root of the -new thread. Return from this function terminates a thread.

-arg

-A pointer to the root function's only parameter.

-joinable

-Specifies whether the thread is joinable or unjoinable. If this parameter -is zero, the thread is unjoinable. Otherwise, it is joinable. -See also st_thread_join().

-stack_size

-Specifies your preference for the size of the stack, in bytes, associated -with the newly created thread. If you pass zero in this parameter, the -default stack size will be used. The default stack size is 128 KB on IA-64 -and 64 KB on all other platforms. On IA-64 only a half of stack_size -bytes is used for the memory stack. The other half is used for the register -stack backing store. -

-

Returns
-Upon successful completion, a new thread identifier is returned (this -identifier remains valid until the thread returns from its start function). -Otherwise, NULL is returned and errno is set -to indicate the error. -

-

Description
-This function creates a new thread. Note that the total number of threads -created by the application is limited by the amount of swap space available. -Upon thread creation, stack_size bytes are reserved on the swap -space. The stack pages are not actually used (valid) until touched by the -application. -

-


-

- -

st_thread_exit()

- -Terminates the calling thread. -

-

Syntax
- -
-#include <st.h>
-
-void st_thread_exit(void *retval);
-
-

-

Parameters
-st_thread_exit() has the following parameters:

-retval

-If the thread is joinable, then the value retval may be retrieved -by st_thread_join(). If a thread returns from its -start function, it acts as if it had called st_thread_exit() with -retval as the value returned. -

-

Returns
-Nothing. -

-

Description
-This function terminates the calling thread. When a thread exits, per-thread -private data is destroyed by invoking the destructor function for any -non-NULL thread specific values associated with active keys (see -st_key_create()). This function is implicitly called -when a thread returns from its start function.

-When the last thread terminates the process exits with a zero status value. -

-


-

- -

st_thread_join()

- -Blocks the calling thread until a specified thread terminates. -

-

Syntax
- -
-#include <st.h>
-
-int st_thread_join(st_thread_t thread, void **retvalp);
-
-

-

Parameters
-st_thread_join() has the following parameters:

-thread

-A valid identifier for the thread that is to be joined.

-retvalp

-If this parameter is not NULL, then the exit value of the -thread will be placed in the location referenced by this parameter -(see st_thread_exit()). -

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error:

- - - - - -
EINVALTarget thread is unjoinable.
EINVALOther thread already waits on the same -joinable thread.
EDEADLKTarget thread is the same as the -calling thread.
EINTRCurrent thread was interrupted by -st_thread_interrupt().
-

-

Description
-This function is used to synchronize the termination of a thread and possibly -retrieve its exit value. Several threads cannot wait for the same thread -to complete - one of the calling threads operates successfully, and the others -terminate with the error. The calling thread is not blocked if the target -thread has already terminated. -

-


-

- -

st_thread_self()

- -Identifies the calling thread. -

-

Syntax
- -
-#include <st.h>
-
-st_thread_t st_thread_self(void);
-
-

-

Parameters
-None. -

-

Returns
-Always returns a valid reference to the calling thread - a self-identity. -

-

Description
-This function identifies the calling thread. This is the same identifier -that the creating thread obtains from -st_thread_create(). -

-


-

- -

st_thread_interrupt()

- -Interrupts a target thread. -

-

Syntax
- -
-#include <st.h>
-
-void st_thread_interrupt(st_thread_t thread);
-
-

-

Parameters
-st_thread_interrupt() has the following parameters:

-thread

-A valid identifier for the thread being interrupted. -

-

Returns
-Nothing. -

-

Description
-This function interrupts (unblocks) a target thread that is blocked in one -of the blocking functions. A function that was interrupted -returns an error and sets errno to EINTR. It is up to -the target thread to act upon an interrupt (e.g., it may exit or just -abort the current transaction).

-Note: State Threads library functions are never interrupted by a -caught signal. A blocking library function returns an error and sets -errno to EINTR only if the current thread was -interrupted via st_thread_interrupt(). -

-If a target thread is already runnable or running (e.g., it is a newly -created thread or calling thread itself), this function will prevent it -from subsequent blocking. In other words, the interrupt will be "delivered" -only when a target thread is about to block. -

-


-

- -

st_sleep(), st_usleep()

- -Suspends current thread for a specified amount of time. -

-

Syntax
- -
-#include <st.h>
-
-int st_sleep(int secs);
-
-int st_usleep(st_utime_t usecs);
-
-

-

Parameters
-st_sleep() has the following parameters:

-secs

-The number of seconds you want the thread to sleep for. -

-st_usleep() has the following parameters:

-usecs

-The number of microseconds you want the thread to sleep for. This parameter -is a variable of type st_utime_t. -

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error:

- - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
-

-

Description
-These functions suspend the calling thread from execution for a specified -number of seconds (st_sleep()) or microseconds (st_usleep()). -

- -If zero is passed as a parameter to st_sleep(), or -ST_UTIME_NO_WAIT (0) is passed to -st_usleep(), the calling thread yields, thus potentially -allowing another thread to run. - -

- -If -1 is passed as a parameter to st_sleep(), or -ST_UTIME_NO_TIMEOUT (-1) is passed to -st_usleep(), the calling thread will be suspended permanently. -It can be resumed again by interrupting it via st_thread_interrupt(). - -

-


-

- -

st_randomize_stacks()

- -Turns stack base address randomization on or off. -

-

Syntax
- -
-#include <st.h>
-
-int st_randomize_stacks(int on);
-
-

-

Parameters
-st_randomize_stacks() has the following parameters:

-on

-If this parameter has a non-zero value, the State Threads library -randomizes the base addresses of stacks allocated for threads created -after this call. Otherwise new threads' stacks are typically page -aligned. -

-

Returns
-The previous state of stack randomization (a value of 0 if it -was off and a non-zero value otherwise). -

-

Description
-Randomizing state threads' stack bases may improve cache performance on -some systems when large numbers of state threads all perform roughly the -same work, as when they all start from the same root function. On many -modern systems the performance increase is negligible. You should -compare your application's performance with this feature on and off to -see if you really need it. -

-When randomization is enabled, new stacks are allocated one page larger -to accomodate the randomization. -

-This call affects only threads created afterward. It has no effect on -existing threads. -

-


-

- - -

Per-Thread Private Data

- -These functions allow to associate private data with each of the threads in -a process. -

-

-
st_key_create()
-
st_key_getlimit()
-
st_thread_setspecific()
-
st_thread_getspecific()
-
-

-


-

- -

st_key_create()

- -Creates a key (non-negative integer) that can be used by all -threads in the process to get and set thread-specific data. -

-

Syntax
- -
-#include <st.h>
-
-int st_key_create(int *keyp, void (*destructor)(void *));
-
-

-

Parameters
-st_key_create() has the following parameters:

-keyp

-The newly created key is returned in the memory pointed to by this parameter. -The new key can be used with -st_thread_setspecific() and -st_thread_getspecific().

-destructor

-Specifies an optional destructor function for the private data associated -with the key. This function can be specified as NULL. -Upon thread exit (see st_thread_exit()), if a key -has a non-NULL destructor and has a non-NULL value -associated with that key, then the destructor function will be -called with the associated value. -

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error:

- - -
EAGAINThe limit on the total number of keys per -process has been exceeded (see st_key_getlimit()). -
-

-

Description
-If this function is successful, every thread in the same process is capable -of associating private data with the new key. After a new key is created, all -active threads have the value NULL associated with that key. -After a new thread is created, the value NULL is associated with -all keys for that thread. If a non-NULL destructor function is -registered with a new key, it will be called at one of two times, as long as -the private data is not NULL: - -

-The key maintains independent data values for each binding thread. A thread -can get access only to its own thread-specific data. There is no way to -deallocate a private data key once it is allocated. -

-


-

- -

st_key_getlimit()

- -Returns the key limit. -

-

Syntax
- -
-#include <st.h>
-
-int st_key_getlimit(void);
-
-

-

Parameters
-None. -

-

Returns
-The limit on the total number of keys per process. -

-

Description
-This function can be used to obtain the limit on the total number of keys -per process (see st_key_create()). -

-


-

- -

st_thread_setspecific()

- -Sets per-thread private data. -

-

Syntax
- -
-#include <st.h>
-
-int st_thread_setspecific(int key, void *value);
-
-

-

Parameters
-st_thread_setspecific() has the following parameters:

-key

-This parameter represents a key with which thread-specific data is associated. -

-value

-The per-thread private data, or more likely, a pointer to the data which is -associated with key. -

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error:

- - - -
EINVALThe specified key is invalid.
-

-

Description
-This function associates a thread-specific value with key. -Different threads may bind different values to the same key.

-If the thread already has non-NULL private data associated with -key, and if the destructor function for that key is not -NULL, this destructor function will be called before setting the -new data value. -

-


-

- -

st_thread_getspecific()

- -Retrieves the per-thread private data for the current thread. -

-

Syntax
- -
-#include <st.h>
-
-void *st_thread_getspecific(int key);
-
-

-

Parameters
-st_thread_getspecific() has the following parameters:

-key

-This parameter represents a key with which thread-specific data is associated. -

-

Returns
-The thread-specific data associated with key. If no data is -associated with key, then NULL is returned. -

-

Description
-This function returns the calling thread's value that is bound to the -specified key (see -st_thread_setspecific()). -

-


-

- - -

Synchronization

- -

-These functions operate on condition variables -and mutual exclusion locks (mutexes).

-Functions are provided to wait on a condition variable and to wake up -(signal) threads that are waiting on the condition variable. -

-

-
st_cond_new()
-
st_cond_destroy()
-
st_cond_wait()
-
st_cond_timedwait()
-
st_cond_signal()
-
st_cond_broadcast()
-

-

st_mutex_new()
-
st_mutex_destroy()
-
st_mutex_lock()
-
st_mutex_trylock()
-
st_mutex_unlock()
-
-

-


-

- -

st_cond_new()

- -Creates a new condition variable. -

-

Syntax
- -
-#include <st.h>
-
-st_cond_t st_cond_new(void);
-
-

-

Parameters
-None. -

-

Returns
-Upon successful completion, a new condition variable identifier is returned. -Otherwise, NULL is returned and errno is set -to indicate the error. -

-

Description
-This function creates a new condition variable. -

-


-

- -

st_cond_destroy()

- -Destroys a condition variable. -

-

Syntax
- -
-#include <st.h>
-
-int st_cond_destroy(st_cond_t cvar);
-
-

-

Parameters
-st_cond_destroy() has the following parameters:

-cvar

-An identifier of the condition variable object to be destroyed. -

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error:

- - - -
EBUSYThe condition variable is currently being -used by one or more threads.
-

-

Description
-This function destroys a condition variable. The caller is responsible for -ensuring that the condition variable is no longer in use. -

-


-

- -

st_cond_wait()

- -Waits on a condition. -

-

Syntax
- -
-#include <st.h>
-
-int st_cond_wait(st_cond_t cvar);
-
-

-

Parameters
-st_cond_wait() has the following parameters:

-cvar

-The condition variable on which to wait. -

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error:

- - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
-

-

Description
-This function is used to block on a condition variable. A return from this -function does not guarantee that the condition or event for which the caller -was waiting actually occurred. It is the responsibility of the caller -to recheck the condition wait predicate before proceeding.

-Note: The State Threads library scheduling guarantees that the -condition cannot change between the checking and blocking, therefore there -is no need for mutex protection. You must not call any -blocking functions between the condition checking and -the st_cond_wait() call. -

-


-

- -

st_cond_timedwait()

- -Waits on a condition. -

-

Syntax
- -
-#include <st.h>
-
-int st_cond_timedwait(st_cond_t cvar, st_utime_t timeout);
-
-

-

Parameters
-st_cond_timedwait() has the following parameters:

-cvar

-The condition variable on which to wait.

-timeout

-If the number of microseconds specified by this parameter passes before the -waiting thread is signalled, an error is returned. This parameter is a -variable of type st_utime_t. Note that this -time value is a time delta; it is not an absolute time. -Also note that timeouts are measured since -the last context switch. -

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error:

- - - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred before the thread was -awakened by st_cond_signal() or -st_cond_broadcast().
-

-

Description
-This function works the same way as st_cond_wait(), -except that an error is returned if the number of microseconds specified by -timeout passes before the waiting thread is signalled. -

-


-

- -

st_cond_signal()

- -Unblocks a thread waiting on a condition variable. -

-

Syntax
- -
-#include <st.h>
-
-int st_cond_signal(st_cond_t cvar);
-
-

-

Parameters
-st_cond_signal() has the following parameters:

-cvar

-The condition variable to signal. -

-

Returns
-Always zero. -

-

Description
-This function unblocks (signals) one of the threads that are blocked on -cvar at the time of the call. If no thread is waiting on the -condition variable, the signal operation is a no-op. -

-


-

- -

st_cond_broadcast()

- -Unblocks all threads waiting on a condition variable. -

-

Syntax
- -
-#include <st.h>
-
-int st_cond_broadcast(st_cond_t cvar);
-
-

-

Parameters
-st_cond_broadcast() has the following parameters:

-cvar

-The condition variable to broadcast. -

-

Returns
-Always zero. -

-

Description
-This function unblocks all threads blocked on the specified condition -variable at the time of the call. If no threads are waiting, this operation -is a no-op. -

-


-

- - -

st_mutex_new()

- -Creates a new mutual exclusion lock (mutex). -

-

Syntax
- -
-#include <st.h>
-
-st_mutex_t st_mutex_new(void);
-
-

-

Parameters
-None. -

-

Returns
-Upon successful completion, a new mutex identifier is returned. -Otherwise, NULL is returned and errno is set to -indicate the error. -

-

Description
-This function creates a new opaque mutual exclusion lock (see -st_mutex_t). -

-


-

- -

st_mutex_destroy()

- -Destroys a specified mutex object. -

-

Syntax
- -
-#include <st.h>
-
-int st_mutex_destroy(st_mutex_t lock);
-
-

-

Parameters
-st_mutex_destroy() has the following parameters:

-lock

-An identifier of the mutex object to be destroyed. -

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error:

- - - -
EBUSYThe mutex is currently being used by -other threads.
-

-

Description
-This function destroys a mutex. The caller is responsible for ensuring -that the mutex is no longer in use. -

-


-

- -

st_mutex_lock()

- -Locks a specified mutex object. -

-

Syntax
- -
-#include <st.h>
-
-int st_mutex_lock(st_mutex_t lock);
-
-

-

Parameters
-st_mutex_lock() has the following parameters:

-lock

-An identifier of the mutex object to be locked. -

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error:

- - - -
EDEADLKThe current thread already owns the mutex. -
EINTRThe current thread was interrupted by -st_thread_interrupt().
-

-

Description
-A thread that calls this function will block until it can gain exclusive -ownership of a mutex, and retains ownership until it calls -st_mutex_unlock(). -

-


-

- -

st_mutex_trylock()

- -Attempts to acquire a mutex. -

-

Syntax
- -
-#include <st.h>
-
-int st_mutex_trylock(st_mutex_t lock);
-
-

-

Parameters
-st_mutex_trylock() has the following parameters:

-lock

-An identifier of the mutex object to be locked. -

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error:

- - -
EBUSYThe mutex is currently held by another -thread.
-

-

Description
-This function attempts to acquire a mutex. If the mutex object is locked -(by any thread, including the current thread), the call returns immediately -with an error. -

-


-

- -

st_mutex_unlock()

- -Releases a specified mutex object. -

-

Syntax
- -
-#include <st.h>
-
-int st_mutex_unlock(st_mutex_t lock);
-
-

-

Parameters
-st_mutex_unlock() has the following parameters:

-lock

-An identifier of the mutex object to be unlocked. -

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error:

- - -
EPERMThe current thread does not own the mutex. -
-

-

Description
-This function releases a specified mutex object previously acquired by -st_mutex_lock() or -st_mutex_trylock(). Only the thread that locked -a mutex should unlock it. -

-


-

- - -

Timing

- -

-

-
st_utime()
-
st_set_utime_function()
-
st_timecache_set()
-
st_time()
-
-

-


-

- -

st_utime()

- -Returns current high-resolution time. -

-

Syntax
- -
-#include <st.h>
-
-st_utime_t st_utime(void);
-
-

-

Parameters
-None. -

-

Returns
-Current high-resolution time value of type -st_utime_t. -

-

Description
-This function returns the current high-resolution time. Time is -expressed as microseconds since some arbitrary time in the past. It is -not correlated in any way to the time of day. See also st_utime_t and st_time(). -

-


-

- -

st_set_utime_function()

- -Set high-resolution time function. -

-

Syntax
- -
-#include <st.h>
-
-int st_set_utime_function(st_utime_t (*func)(void));
-
-

-

Parameters
-st_set_utime_function() has the following parameters:

-func

-This function will be called to get high-resolution time instead of the -default st_utime() function. It must return -number of microseconds since some arbitrary time in the past. -

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to EINVAL to indicate the error. -

-

Description
-This function may be called to replace the default implementation of the -st_utime() function. It must be called before the ST -library has been initialized (see st_init()). -The user-provided function func will be invoked whenever -st_utime() is called to obtain current high-resolution time. -Replacing default implementation may be useful, for example, for taking -advantage of high performance CPU cycle counters. -

-


-

- -

st_timecache_set()

- -Turns the time caching on or off. -

-

Syntax
- -
-#include <st.h>
-
-int st_timecache_set(int on);
-
-

-

Parameters
-st_timecache_set() has the following parameters:

-on

-If this parameter has a non-zero value, the time caching is turned on -(enabled). Otherwise, the time caching is turned off (disabled). -By default time caching is disabled. -

-

Returns
-The previous state of time caching (a value of 0 if it was off and -a value of 1 otherwise). -

-

Description
-The State Threads library has the ability to "cache" the time value that is -reported by the time(2) system call. If the time caching is enabled -by calling this function with a non-zero argument, then the result value -of time(2) will be stored and updated at most once per second. The -cached time can be retrieved by st_time(). -By default time caching is disabled. -You may enable or disable time caching at any time but generally -you enable it once (if desired) during program initialization.

-Note: There are some pathological cases (e.g., very heavy loads during -application benchmarking) when a single thread runs for a long time without -giving up control and the cached time value is not updated properly. If you -always need "real-time" time values, don't enable the time caching. -

-


-

- -

st_time()

- -Returns the value of time in seconds since 00:00:00 UTC, January 1, 1970. -

-

Syntax
- -
-#include <st.h>
-
-time_t st_time(void);
-
-

-

Parameters
-None. -

-

Returns
-The value of time in seconds since 00:00:00 UTC, January 1, 1970 as reported -by the time(2) system call. -

-

Description
-If the time caching was enabled by -st_timecache_set(), then this function returns -the cached result. Otherwise, it just calls time(2). -

-


-

- - -

I/O Functions

- -

-Most State Threads library I/O functions look like corresponding C library -functions with two exceptions: -

    -
  • They operate on file descriptor objects of type -st_netfd_t.
  • -
  • They take an additional argument of type -st_utime_t which represents an inactivity -timeout: if no I/O is possible during this amount of time, I/O functions -return an error code and set errno to ETIME. - -The boundary values ST_UTIME_NO_WAIT (0) and -ST_UTIME_NO_TIMEOUT (-1) for this argument indicate -that the thread should wait no time (function returns immediately) or -wait forever (never time out), respectively. - -Note that timeouts are measured since the -last context switch. -
  • -
-

-

-
st_netfd_open()
-
st_netfd_open_socket()
-
st_netfd_free()
-
st_netfd_close()
-
st_netfd_fileno()
-
st_netfd_setspecific()
-
st_netfd_getspecific()
-
st_netfd_serialize_accept()
-
st_netfd_poll()
-

-

st_accept()
-
st_connect()
-
st_read()
-
st_read_fully()
-
st_read_resid()
-
st_readv()
-
st_read_resid()
-
st_write()
-
st_write_resid()
-
st_writev()
-
st_writev_resid()
-
st_recvfrom()
-
st_sendto()
-
st_recvmsg()
-
st_sendmsg()
-
st_open()
-
st_poll()
-
-

-


-

- -

st_netfd_open()

- -Creates a new file descriptor object. -

-

Syntax
- -
-#include <st.h>
-
-st_netfd_t st_netfd_open(int osfd);
-
-

-

Parameters
-st_netfd_open() has the following parameters:

-osfd

- -Any open OS file descriptor; can be obtained from calls to -functions including, but not restricted to, pipe(2), socket(3), -socketpair(3), fcntl(2), dup(2), etc. - - -

-

Returns
-Upon successful completion, a new file descriptor object identifier is -returned. Otherwise, NULL is returned and errno is set -to indicate the error. -

-

Description
-This function creates a new file descriptor object of type -st_netfd_t.

- -Note: Among other things, this function sets a non-blocking -flag on the underlying OS file descriptor. You should not modify this -flag directly. Also, once an st_netfd_t -has been created with a given file descriptor, you should avoid -passing that descriptor to normal I/O or stdio functions. Since the -O_NONBLOCK flag is shared across dup(2), this applies to -dup()'ed file descriptors as well - for instance, if you pass -standard output or standard input to st_netfd_open(), then -you should use st_write() instead of write -or fprintf when writing to standard error as well - since all -three descriptors could point to the same terminal. If necessary, you -can still use write directly if you remember to check -errno for EAGAIN, but fprintf and other -stdio functions should be avoided completely because, at least on -Linux, the stdio library cannot be made to work reliably with -non-blocking files. (This only applies to file descriptors which are -passed to st_netfd_open() or st_netfd_open_socket(), or which are -related to such descriptors through dup(); other file -descriptors are untouched by State Threads.) -

-


-

- -

st_netfd_open_socket()

- -Creates a new file descriptor object from a socket. -

-

Syntax
- -
-#include <st.h>
-
-st_netfd_t st_netfd_open_socket(int osfd);
-
-

-

Parameters
-st_netfd_open_socket() has the following parameters:

-osfd

-An open OS file descriptor which is a socket initially obtained from a -socket(3) or socketpair(3) call. -

-

Returns
-Upon successful completion, a new file descriptor object identifier is -returned. Otherwise, NULL is returned and errno is set -to indicate the error. -

-

Description
-This function creates a new file descriptor object of type -st_netfd_t which represents an open end -point of network communication.

-Unlike the st_netfd_open() function which may be used -on OS file descriptors of any origin, st_netfd_open_socket() must -be used only on sockets. It is slightly more efficient than -st_netfd_open().

-Note: Among other things, this function sets a non-blocking flag -on the underlying OS socket. You should not modify this flag directly. -See st_netfd_open(). -

-


-

- -

st_netfd_free()

- -Frees a file descriptor object without closing the underlying OS file -descriptor. -

-

Syntax
- -
-#include <st.h>
-
-void st_netfd_free(st_netfd_t fd);
-
-

-

Parameters
-st_netfd_free() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t). -

-

Returns
-Nothing. -

-

Description
-This function frees the memory and other resources identified by the -fd parameter without closing the underlying OS file descriptor. -Any non-NULL descriptor-specific data is destroyed by invoking -the specified destructor function (see st_netfd_setspecific()).

A thread should -not free file descriptor objects that are in use by other threads -because it may lead to unpredictable results (e.g., a freed file -descriptor may be reused without other threads knowing that). -

-


-

- -

st_netfd_close()

- -Closes a file descriptor. -

-

Syntax
- -
-#include <st.h>
-
-int st_netfd_close(st_netfd_t fd);
-
-

-

Parameters
-st_netfd_close() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t). -

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error. -

-

Description
-This function closes the underlying OS file descriptor, frees the memory and -other resources identified by the fd parameter. Any non-NULL -descriptor-specific data is destroyed by invoking the specified destructor -function (see st_netfd_setspecific()).

-A thread should not close file descriptor objects that are in use by other -threads because it may lead to unpredictable results (e.g., a closed -file descriptor may be reused without other threads knowing that). -

-


-

- -

st_netfd_fileno()

- -Returns an underlying OS file descriptor. -

-

Syntax
- -
-#include <st.h>
-
-int st_netfd_fileno(st_netfd_t fd);
-
-

-

Parameters
-st_netfd_fileno() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t). -

-

Returns
-An underlying OS file descriptor. -

-

Description
-This function returns the integer OS file descriptor associated with the named -file descriptor object. -

-


-

- -

st_netfd_setspecific()

- -Sets per-descriptor private data. -

-

Syntax
- -
-#include <st.h>
-
-void st_netfd_setspecific(st_netfd_t fd, void *value,
-                          void (*destructor)(void *));
-
-

-

Parameters
-st_netfd_setspecific() has the following parameters:

-fd

-A valid file descriptor object identifier (see -st_netfd_t). -

-value

-The per-descriptor private data, or more likely, a pointer to the data which -is being associated with the named file descriptor object. -

-destructor

-Specifies an optional destructor function for the private data associated -with fd. This function can be specified as NULL. -If value is not NULL, then this destructor function will -be called with value as an argument upon freeing the file descriptor -object (see st_netfd_free() and -st_netfd_close()). -

-

Returns
-Nothing. -

-

Description
-This function allows to associate any data with the specified file -descriptor object (network connection). If a non-NULL destructor -function is registered, it will be called at one of two times, as long as -the associated data is not NULL: -
    -
  • when private data is replaced by calling -st_netfd_setspecific() again -
  • upon freeing the file descriptor object (see -st_netfd_free() and -st_netfd_close()) -
-

-


-

- -

st_netfd_getspecific()

- -Retrieves the per-descriptor private data. -

-

Syntax
- -
-#include <st.h>
-
-void *st_netfd_getspecific(st_netfd_t fd);
-
-

-

Parameters
-st_netfd_getspecific() has the following parameters:

-fd

-A valid file descriptor object identifier (see -st_netfd_t). -

-

Returns
-The data associated with the named file descriptor object. If no data is -associated with fd, then NULL is returned. -

-

Description
-This function allows to retrieve the data that was associated with the -specified file descriptor object (see -st_netfd_setspecific()). -

-


-

- -

st_netfd_serialize_accept()

- -Serializes all subsequent accept(3) calls on a specified file -descriptor object. -

-

Syntax
- -
-#include <st.h>
-
-int st_netfd_serialize_accept(st_netfd_t fd);
-
-

-

Parameters
-st_netfd_serialize_accept() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t) which has been successfully created -from a valid listening socket by st_netfd_open() or -st_netfd_open_socket(). -

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error. -

-

Description
-On some platforms (e.g., Solaris 2.5 and possibly other SVR4 implementations) -accept(3) calls from different processes on -the same listening socket (see bind(3), listen(3)) must be -serialized. This function causes all subsequent accept(3) calls -made by st_accept() on the specified file descriptor -object to be serialized. -

-st_netfd_serialize_accept() must be called before -creating multiple server processes via fork(2). If the application -does not create multiple processes to accept network connections on -the same listening socket, there is no need to call this function. -

-Deciding whether or not to serialize accepts is tricky. On some -platforms (IRIX, Linux) it's not needed at all and -st_netfd_serialize_accept() is a no-op. On other platforms -it depends on the version of the OS (Solaris 2.6 doesn't need it but -earlier versions do). Serializing accepts does incur a slight -performance penalty so you want to enable it only if necessary. Read -your system's manual pages for accept(2) and select(2) -to see if accept serialization is necessary on your system. -

-st_netfd_serialize_accept() allocates resources that are -freed upon freeing of the specified file descriptor object (see -st_netfd_free() and -st_netfd_close()). -

-


-

- -

st_netfd_poll()

- -Waits for I/O on a single file descriptor object. -

-

Syntax
- -
-#include <st.h>
-
-int st_netfd_poll(st_netfd_t fd, int how, st_utime_t timeout);
-
-

-

Parameters
-st_netfd_poll() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t). -

-how

-Specifies I/O events of interest. This parameter can be constructed by -OR-ing any combination of the following event flags which are defined -in the poll.h header file:

- - - - - -
POLLINfd is readable.
POLLOUTfd is is writable.
POLLPRIfd has an exception condition.
-

-timeout

-Amount of time in microseconds the call will block waiting for I/O -to become ready. This parameter is a variable of type -st_utime_t. If this time expires without any -I/O becoming ready, st_netfd_poll() returns an error and sets -errno to ETIME. -Note that timeouts are measured since the -last context switch. -

-

Returns
-If the named file descriptor object is ready for I/O within the specified -amount of time, a value of 0 is returned. Otherwise, a value -of -1 is returned and errno is set to indicate the error: -

- - - - -
EBADFThe underlying OS file descriptor is invalid. -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred without any I/O -becoming ready.
-

-

Description
-This function returns as soon as I/O is ready on the named file -descriptor object or the specified amount of time expires. The -how parameter should be set to the I/O events (readable, -writable, exception, or some combination) that the caller is interested -in. If the value of timeout is ST_UTIME_NO_TIMEOUT -(-1), this function blocks until a requested I/O event occurs -or until the call is interrupted by st_thread_interrupt().

-Despite having an interface like poll(2), this function uses -the same event notification mechanism as the rest of the library. For -instance if an alternative event nofication mechanism was set using st_set_eventsys(), this function uses that -mechanism to check for events.

-Note: if kqueue(2) is used as an alternative event -notification mechanism (see st_set_eventsys()), the POLLPRI -event flag is not supported and st_netfd_poll() will return an error -if it's set (errno will be set to EINVAL). -

-


-

- -

st_accept()

- -Accepts a connection on a specified file descriptor object. -

-

Syntax
- -
-#include <st.h>
-
-st_netfd_t st_accept(st_netfd_t fd, struct sockaddr *addr, int *addrlen,
-                     st_utime_t timeout);
-
-

-

Parameters
-st_accept() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t) representing the rendezvous socket -on which the caller is willing to accept new connections. This object has been -created from a valid listening socket by -st_netfd_open() or -st_netfd_open_socket().

-addr

-If this value is non-zero, it is a result parameter that is filled -in with the address of the connecting entity, as known to the communications -layer (see accept(3)).

-addrlen

-This parameter should initially contain the amount of space pointed to by -addr; on return it will contain the actual length (in bytes) of the -address returned (see accept(3)).

-timeout

-A value of type st_utime_t specifying the time -limit in microseconds for completion of the accept operation. -Note that timeouts are measured since the -last context switch. -

-

Returns
-Upon successful completion, a new file descriptor object identifier -representing the newly accepted connection is returned. Otherwise, -NULL is returned and errno is set to indicate the error. -Possible errno values are the same as set by the accept(3) -call with two exceptions:

- - - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred and no pending -connection was accepted.
-

-

Description
-This function accepts the first connection from the queue of pending -connections and creates a new file descriptor object for the newly -accepted connection. The rendezvous socket can still be used to accept -more connections.

-st_accept() blocks the calling thread until either a new connection -is successfully accepted or an error occurs. If no pending connection can -be accepted before the time limit, this function returns NULL -and sets errno to ETIME. -

-


-

- -

st_connect()

- -Initiates a connection on a specified file descriptor object. -

-

Syntax
- -
-#include <st.h>
-
-int st_connect(st_netfd_t fd, struct sockaddr *addr, int addrlen,
-               st_utime_t timeout);
-
-

-

Parameters
-st_connect() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t) representing a socket.

-addr

-A pointer to the address of the peer to which the socket is to be connected. -

-addrlen

-This parameter specifies the amount of space pointed to by addr. -

-timeout

-A value of type st_utime_t specifying the time -limit in microseconds for completion of the connect operation. -Note that timeouts are measured since the -last context switch. -

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error. Possible errno values are the same as set -by the connect(3) call with two exceptions:

- - - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred and connection setup -was not completed.
-

-

Description
-This function is usually invoked on a file descriptor object representing -a TCP socket. Upon completion it establishes a TCP connection to the peer. -If the underlying OS socket is not bound, it will be bound to an arbitrary -local address (see connect(3)).

-st_connect() blocks the calling thread until either the connection -is successfully established or an error occurs. If the connection setup -cannot complete before the specified time limit, this function fails with -errno set to ETIME. -

-


-

- -

st_read()

- -Reads data from a specified file descriptor object. -

-

Syntax
- -
-#include <st.h>
-
-ssize_t st_read(st_netfd_t fd, void *buf, size_t nbyte, st_utime_t timeout);
-
-

-

Parameters
-st_read() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t).

-buf

-A pointer to a buffer to hold the data read in. On output the buffer -contains the data.

-nbyte

-The size of buf in bytes.

-timeout

-A value of type st_utime_t specifying the time -limit in microseconds for completion of the read operation. -Note that timeouts are measured since the -last context switch. -

-

Returns
-On success a non-negative integer indicating the number of bytes actually -read is returned (a value of 0 means the network connection is -closed or end of file is reached). Otherwise, a value of -1 is -returned and errno is set to indicate the error. -Possible errno values are the same as set by the read(2) -call with two exceptions:

- - - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred and no data was read. -
-

-

Description
-This function blocks the calling thread until it encounters an end-of-stream -indication, some positive number of bytes (but no more than nbyte -bytes) are read in, a timeout occurs, or an error occurs. -

-


-

- -

st_read_fully()

- -Reads the specified amount of data in full from a file descriptor object. -

-

Syntax
- -
-#include <st.h>
-
-ssize_t st_read_fully(st_netfd_t fd, void *buf, size_t nbyte,
-                      st_utime_t timeout);
-
-

-

Parameters
-st_read_fully() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t).

-buf

-A pointer to a buffer to hold the data read in. On output the buffer -contains the data.

-nbyte

-The amount of data to be read in full (in bytes). It must not exceed the -size of buf.

-timeout

-A value of type st_utime_t specifying the -inactivity timeout (in microseconds). -Note that timeouts are measured since the -last context switch. -

-

Returns
-On success a non-negative integer indicating the number of bytes actually -read is returned (a value less than nbyte means the network -connection is closed or end of file is reached). Otherwise, a value of --1 is returned and errno is set to indicate the error. -Possible errno values are the same as set by the read(2) -call with two exceptions:

- - - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred. -
-

-

Description
-This function blocks the calling thread until the specified amount of data -is read in full, it encounters an end-of-stream indication, a timeout occurs, -or an error occurs. -

-


-

- -

st_read_resid()

- -Reads the specified amount of data in full from a file descriptor object. -

-

Syntax
- -
-#include <st.h>
-
-int st_read_resid(st_netfd_t fd, void *buf, size_t *resid,
-		  st_utime_t timeout);
-
-

-

Parameters
-st_read_resid() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t).

-buf

-A pointer to a buffer to hold the data read in. On output the buffer -contains the data.

-resid

-A pointer to a number of bytes. -On entry, the amount of data to be read in full. -It must not exceed the size of buf. -On return, the amount of data remaining to be read. -(A non-zero returned value means some but not all of the data was read.)

-timeout

-A value of type st_utime_t specifying the -inactivity timeout (in microseconds). -Note that timeouts are measured since the -last context switch. -

-

Returns
-On success, zero is returned. *resid may be zero, indicating -a complete read, or non-zero, indicating the network -connection is closed or end of file is reached. -

-Otherwise, a value of -1 is returned, *resid is non-zero, -and errno is set to indicate the error. -Possible errno values are the same as set by the read(2) -call with two exceptions:

- - - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred. -
-

-

Description
-This function blocks the calling thread until the specified amount of data -is read in full, it encounters an end-of-stream indication, a timeout occurs, -or an error occurs. It differs from st_read_fully() only in that -it allows the caller to know how many bytes were transferred before an error -occurred. -

-


-

- -

st_readv()

- -Reads data from a specified file descriptor object into multiple buffers. -

-

Syntax
- -
-#include <st.h>
-
-ssize_t st_readv(st_netfd_t fd, const struct iovec *iov, int iov_size,
-		 st_utime_t timeout);
-
-

-

Parameters
-st_readv() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t).

-iov

-An array of iovec structures that identify the buffers for holding -the data read in. -On return the buffers contain the data.

-iov_size

-The number of iovec structures in the iov array.

-timeout

-A value of type st_utime_t specifying the time -limit in microseconds for completion of the read operation. -Note that timeouts are measured since the -last context switch. -

-

Returns
-On success a non-negative integer indicating the number of bytes actually -read is returned (a value of 0 means the network connection is -closed or end of file is reached). Otherwise, a value of -1 is -returned and errno is set to indicate the error. -Possible errno values are the same as set by the readv(2) -call with two exceptions:

- - - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred and no data was read. -
-

-

Description
-This function blocks the calling thread until it encounters an end-of-stream -indication, some positive number of bytes (but no more than fit in the buffers) -are read in, a timeout occurs, or an error occurs. -

-


-

- -

st_readv_resid()

- -Reads the specified amount of data in full from a file descriptor object -into multiple buffers. -

-

Syntax
- -
-#include <st.h>
-
-int st_readv_resid(st_netfd_t fd, struct iovec **iov, int *iov_size,
-		   st_utime_t timeout);
-
-

-

Parameters
-st_readv_resid() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t).

-iov

-A pointer to an array of iovec structures. -On entry, the iovecs identify the buffers for holding the data read in. -On return, the incomplete iovecs. -This function modifies both the pointer and the array to which it points.

-iov_size

-A pointer to a number of iovec structures. -On entry, the number of iovec structures pointed to by *iov. -On return, the number of incomplete or unused iovec structures. -(A non-zero returned value means some but not all of the data was read.)

-timeout

-A value of type st_utime_t specifying the -inactivity timeout (in microseconds). -Note that timeouts are measured since the -last context switch. -

-

Returns
-On success, zero is returned. *iov_size may be zero, indicating -a complete read, or non-zero, indicating the network connection is -closed or end of file is reached. *iov points to the first -iovec after the end of the original array on a complete read, or to the -first incomplete iovec on an incomplete read. -

-Otherwise, a value of -1 is returned, *iov_size is non-zero, -and errno is set to indicate the error. *iov points to the -first unused iovec. -Possible errno values are the same as set by the readv(2) -call with two exceptions:

- - - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred. -
-

All of the iovecs before *iov are modified such that -iov_base points to the end of the original buffer and -iov_len is zero. -

-

Description
-This function blocks the calling thread until the specified amount of data -is read in full, it encounters an end-of-stream indication, a timeout occurs, -or an error occurs. Like st_read_resid() it blocks the thread until -all of the requested data is read or an error occurs. Use -st_readv() to read up to the requested amount of data. -

-


-

- -

st_write()

- -Writes a buffer of data to a specified file descriptor object. -

-

Syntax
- -
-#include <st.h>
-
-ssize_t st_write(st_netfd_t fd, const void *buf, size_t nbyte,
-                 st_utime_t timeout);
-
-

-

Parameters
-st_write() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t).

-buf

-A pointer to the buffer holding the data to be written.

-nbyte

-The amount of data in bytes to be written from the buffer.

-timeout

-A value of type st_utime_t specifying the -inactivity timeout (in microseconds). -Note that timeouts are measured since the -last context switch. -

-

Returns
-On success a non-negative integer equal to nbyte is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error. Possible errno values are the same as set -by the write(2) call with two exceptions:

- - - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred. -
-

-

Description
-This function blocks the calling thread until all the data is written, -a timeout occurs, or the write operation fails. The return value is equal to -either nbyte (on success) or -1 (on failure). Note that if -st_write() returns -1, some data (less than nbyte -bytes) may have been written before an error occurred. -

-


-

- -

st_write_resid()

- -Writes a buffer of data to a specified file descriptor object. -

-

Syntax
- -
-#include <st.h>
-
-int st_write_resid(st_netfd_t fd, const void *buf, size_t *resid,
-                   st_utime_t timeout);
-
-

-

Parameters
-st_write_resid() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t).

-buf

-A pointer to the buffer holding the data to be written.

-resid

-A pointer to a number of bytes. -On entry, the amount of data to be written from the buffer. -On return, the amount of data remaining to be written. -(A non-zero returned value means some but not all of the data was written.)

-timeout

-A value of type st_utime_t specifying the -inactivity timeout (in microseconds). -Note that timeouts are measured since the -last context switch. -

-

Returns
-On success, zero is returned and *resid is zero. -Otherwise, a value of -1 is returned, *resid is non-zero, -and errno is set -to indicate the error. Possible errno values are the same as set -by the write(2) call with two exceptions:

- - - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred. -
-

-

Description
-This function blocks the calling thread until all the data is written, -a timeout occurs, or the write operation fails. It differs from -st_write() only in that it allows the caller to know how many bytes -were transferred before an error occurred. -

-


-

- -

st_writev()

- -Writes data to a specified file descriptor object from multiple buffers. -

-

Syntax
- -
-#include <st.h>
-
-ssize_t st_writev(st_netfd_t fd, const struct iovec *iov, int iov_size,
-                  st_utime_t timeout);
-
-

-

Parameters
-st_writev() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t).

-iov

-An array of iovec structures that describe the buffers to write -from (see writev(2)).

-iov_size

-Number of iovec structures in the iov array.

-timeout

-A value of type st_utime_t specifying the -inactivity timeout (in microseconds). -Note that timeouts are measured since the -last context switch. -

-

Returns
-On success a non-negative integer equal to the sum of all the buffer lengths -is returned. Otherwise, a value of -1 is returned and errno -is set to indicate the error. Possible errno values are the same as -set by the writev(2) call with two exceptions:

- - - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred. -
-

-

Description
-This function blocks the calling thread until all the data is written, -a timeout occurs, or the write operation fails. The return value is equal to -either the sum of all the buffer lengths (on success) or -1 (on -failure). Note that if st_writev() returns -1, part of the -data may have been written before an error occurred. -

-


-

- -

st_writev_resid()

- -Writes multiple buffers of data to a specified file descriptor object. -

-

Syntax
- -
-#include <st.h>
-
-int st_writev_resid(st_netfd_t fd, struct iovec **iov, int *iov_size,
-		    st_utime_t timeout);
-
-

-

Parameters
-st_writev_resid() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t).

-iov

-A pointer to an array of iovec structures. -On entry, the iovecs identify the buffers holding the data to write. -On return, the incomplete iovecs. -This function modifies both the pointer and the array to which it points.

-iov_size

-A pointer to a number of iovec structures. -On entry, the number of iovec structures pointed to by *iov. -On return, the number of incomplete or unused iovec structures. -(A non-zero returned value means some but not all of the data was written.)

-timeout

-A value of type st_utime_t specifying the -inactivity timeout (in microseconds). -Note that timeouts are measured since the -last context switch. -

-

Returns
-On success, zero is returned, *iov_size is zero, and *iov -points to the first iovec after the end of the original array. -Otherwise, a value of -1 is returned, *iov_size is non-zero, -*iov points to the first incomplete iovec, and errno is set -to indicate the error. Possible errno values are the same as set -by the writev(2) call with two exceptions:

- - - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred. -
-

-All of the iovecs before *iov are modified such that -iov_base points to the end of the original buffer and -iov_len is zero. -

-

Description
-This function blocks the calling thread until all the data is written, -a timeout occurs, or the write operation fails. It differs from -st_writev() only in that it allows the caller to know how many bytes -were transferred before an error occurred. -

-


-

- -

st_recvfrom()

- -Receives bytes from a file descriptor object and stores the sending peer's -address. -

-

Syntax
- -
-#include <st.h>
-
-int st_recvfrom(st_netfd_t fd, void *buf, int len, struct sockaddr *from,
-                int *fromlen, st_utime_t timeout);
-
-

-

Parameters
-st_recvfrom() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t) representing a UDP socket.

-buf

-A pointer to a buffer to hold the data received.

-len

-The size of buf in bytes.

-from

-If this parameter is not a NULL pointer, the source address of the -message is filled in (see recvfrom(3)).

-fromlen

-This is a value-result parameter, initialized to the size of the buffer -associated with from, and modified on return to indicate the actual -size of the address stored there.

-timeout

-A value of type st_utime_t specifying the time -limit in microseconds for completion of the receive operation. -Note that timeouts are measured since the -last context switch. -

-

Returns
-On success a non-negative integer indicating the length of the received -message in bytes is returned. Otherwise, a value of -1 is returned -and errno is set to indicate the error. Possible errno -values are the same as set by the recvfrom(3) call with two -exceptions:

- - - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred and no data was received. -
-

-

Description
-This function receives up to a specified number of bytes from the specified -file descriptor object representing a UDP socket.

-st_recvfrom() blocks the calling thread until one or more bytes are -transferred, a timeout has occurred, or there is an error. No more than -len bytes will be transferred. -

-


-

- -

st_sendto()

- -Sends bytes to a specified destination. -

-

Syntax
- -
-#include <st.h>
-
-int st_sendto(st_netfd_t fd, const void *msg, int len, struct sockaddr *to,
-              int tolen, st_utime_t timeout);
-
-

-

Parameters
-st_sendto() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t) representing a UDP socket.

-msg

-A pointer to a buffer containing the message to be sent.

-len

-The length of the message to be sent (in bytes).

-to

-A pointer to the address of the destination (see sendto(3)).

-tolen

-This parameter specifies the size of the destination address.

-timeout

-A value of type st_utime_t specifying the time -limit in microseconds for completion of the send operation. -Note that timeouts are measured since the -last context switch. -

-

Returns
-On success a non-negative integer indicating the number of bytes sent is -returned. Otherwise, a value of -1 is returned and errno is -set to indicate the error. Possible errno values are the same as -set by the sendto(3) call with two exceptions:

- - - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred and no data was sent. -
-

-

Description
-This function sends a specified number of bytes from a file descriptor -object representing a UDP socket to the specified destination address. -If no buffer space is available at the underlying OS socket to hold the -message to be transmitted, then st_sendto() blocks the calling -thread until the space becomes available, a timeout occurs, or an error -occurs. -

-


-

- -

st_recvmsg()

- -Receives a message from a file descriptor object. -

-

Syntax
- -
-#include <st.h>
-
-int st_recvmsg(st_netfd_t fd, struct msghdr *msg, int flags,
-               st_utime_t timeout);
-
-

-

Parameters
-st_recvmsg() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t) representing a UDP socket.

-msg

-A pointer to a msghdr structure to describe the data received.

-flags

-Control flags for recvmsg(3).

-timeout

-A value of type st_utime_t specifying the time -limit in microseconds for completion of the receive operation. -Note that timeouts are measured since the -last context switch. -

-

Returns
-On success a non-negative integer indicating the number of bytes received -is returned. Otherwise, a value of -1 is returned -and errno is set to indicate the error. Possible errno -values are the same as set by the recvmsg(3) call with two -exceptions:

- - - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred and no data was received. -
-

-

Description
-This function receives bytes from the specified file descriptor object -representing a UDP socket. The operation is controlled by the in/out -msg parameter.

-st_recvmsg() blocks the calling thread until one or more bytes are -transferred, a timeout has occurred, or there is an error. -

-


-

- -

st_sendmsg()

- -Sends a message to a file descriptor object. -

-

Syntax
- -
-#include <st.h>
-
-int st_sendmsg(st_netfd_t fd, const struct msghdr *msg, int flags,
-               st_utime_t timeout);
-
-

-

Parameters
-st_sendmsg() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t) representing a UDP socket.

-msg

-A pointer to a msghdr structure describing the message to be sent.

-flags

-Control flags for sendmsg(3).

-timeout

-A value of type st_utime_t specifying the time -limit in microseconds for completion of the send operation. -Note that timeouts are measured since the -last context switch. -

-

Returns
-On success a non-negative integer indicating the number of bytes sent is -returned. Otherwise, a value of -1 is returned and errno is -set to indicate the error. Possible errno values are the same as -set by the sendmsg(3) call with two exceptions:

- - - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred and no data was sent. -
-

-

Description
-This function sends bytes to a file descriptor object representing a UDP -socket. The operation is controlled by the msg parameter. -If no buffer space is available at the underlying OS socket to hold the -message to be transmitted, then st_sendmsg() blocks the calling -thread until the space becomes available, a timeout occurs, or an error -occurs. -

-


-

- -

st_open()

- -Opens a file for reading, writing, or both. -

-

Syntax
- -
-#include <st.h>
-
-st_netfd_t st_open(const char *path, int oflags, mode_t mode);
-
-

-

Parameters
-st_open() has the following parameters:

-path

-The pathname of the file to be opened.

-oflags

-File status flags. These are the same flags that are used by the -open(2) system call.

-mode

-Access permission bits of the file mode, if the file is created when -O_CREAT is set in oflags (see open(2)). -

-

Returns
-Upon successful completion, a new file descriptor object identifier is -returned. Otherwise, NULL is returned and errno is set -to indicate the error. -

-

Description
-This function creates a new file descriptor object of type -st_netfd_t for the file with the pathname -path. This object can be freed by -st_netfd_free() or -st_netfd_close().

-The primary purpose of this function is to open FIFOs (named pipes) or -other special files in order to create an end point of communication. -However, it can be used on regular files as well.

-Among other things, this function always sets a non-blocking flag on the -underlying OS file descriptor, so there is no need to include that flag in -oflags. -

-


-

- -

st_poll()

- -Detects when I/O is ready for a set of OS file descriptors. -

-

Syntax
- -
-#include <st.h>
-
-int st_poll(struct pollfd *pds, int npds, st_utime_t timeout);
-
-

-

Parameters
-st_poll() has the following parameters:

-pds

-A pointer to an array of pollfd structures (see poll(2)). -

-npds

-The number of elements in the pds array.

-timeout

-A value of type st_utime_t specifying the -amount of time in microseconds the call will block waiting for I/O -to become ready. If this time expires without any I/O becoming ready, -st_poll() returns zero. -Note that timeouts are measured since the -last context switch. -

-

Returns
-Upon successful completion, a non-negative value is returned. A positive -value indicates the total number of OS file descriptors in pds -that have events. A value of 0 indicates that the call timed out. -Upon failure, a value of -1 is returned and errno is set -to indicate the error:

- - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
-

-If an alternative event notification mechanism has been set by -st_set_eventsys(), other values of -errno could be set upon failure as well. The values -depend on the specific mechanism in use. -

-

Description
-This function returns as soon as I/O is ready on one or more of the specified -OS file descriptors. A count of the number of ready descriptors is returned -unless a timeout occurs, in which case zero is returned.

-The pollfd structure is defined in the poll.h header file -and contains the following members:

-

-    int fd;             /* OS file descriptor */
-    short events;       /* requested events   */
-    short revents;      /* returned events    */
-
-The events field should be set to the I/O events (readable, -writable, exception, or some combination) that the caller is interested in. -On return, the revents field is set to indicate what kind of I/O -is ready on the respective descriptor.

-The events and revents fields are constructed by OR-ing -any combination of the following event flags (defined in poll.h): -

- - - - - - -
POLLINfd is readable.
POLLOUTfd is is writable.
POLLPRIfd has an exception condition.
POLLNVALfd is bad.
-

-The POLLNVAL flag is only valid in the revents field; -it is not used in the events field.

-Despite having an interface like poll(2), this function uses -the same event notification mechanism as the rest of the library. For -instance if an alternative event nofication mechanism was set using st_set_eventsys(), this function uses that -mechanism to check for events.

-Note that unlike the poll(2) call, this function has the -timeout parameter expressed in microseconds. If the value of -timeout is ST_UTIME_NO_TIMEOUT -(-1), this function blocks until a requested I/O -event occurs or until the call is interrupted by -st_thread_interrupt(). -

-Note: if kqueue(2) is used as an alternative event -notification mechanism (see st_set_eventsys()), the POLLPRI -event flag is not supported and st_poll() will return an error -if it's set (errno will be set to EINVAL). -

-


-

- - -

Program Structure

- -

-Generally, the following steps should be followed when writing an application -using the State Threads library: -

-

    -
  1. Configure the library by calling these pre-init functions, if desired. - -
  2. -

    -

  3. Initialize the library by calling st_init().
  4. -

    -

  5. Configure the library by calling these post-init functions, if desired. - -
  6. -

    -

  7. Create resources that will be shared among different processes: - create and bind listening sockets (see socket(3), - bind(3), listen(3), - st_netfd_open_socket(), and possibly - st_netfd_serialize_accept()), - create shared memory segments, inter-process communication (IPC) - channels and synchronization primitives (if any).
  8. -

    -

  9. Create several processes via fork(2). The parent process should - either exit or become a "watchdog" (e.g., it starts a new process when - an existing one crashes, does a cleanup upon application termination, - etc.).
  10. -

    -

  11. In each child process create a pool of threads (see - st_thread_create()) to handle user - connections. Each thread in the pool may accept client connections - (st_accept()), connect to other servers - (st_connect()), perform various network I/O - (st_read(), st_write(), etc.).
  12. -
-

-Note that only State Threads library I/O functions should -be used for a network I/O: any other I/O calls may block the calling process -indefinitely. For example, standard I/O functions (fgets(3), -fread(3), fwrite(3), fprintf(3), etc.) call -read(2) and write(2) directly and therefore should not be -used on sockets or pipes. -

-Also note that for short timeouts to work the program -should do context switches (for example by calling -st_usleep()) on a regular basis. -

-


-

- - -

List of Blocking Functions

- -

-The thread context switch (process state change) can only happen -in a well-known set of blocking functions. -Only the following functions can block the calling thread: -

-

-
st_thread_join()
-
st_sleep()
-
st_usleep()
-
st_cond_wait()
-
st_cond_timedwait()
-
st_mutex_lock()
-
st_netfd_poll()
-
st_accept()
-
st_connect()
-
st_read()
-
st_read_fully()
-
st_read_resid()
-
st_readv()
-
st_readv_resid()
-
st_write()
-
st_write_resid()
-
st_writev()
-
st_writev_resid()
-
st_recvfrom()
-
st_sendto()
-
st_recvmsg()
-
st_sendmsg()
-
st_poll()
-
-

-


-

- - - - diff --git a/trunk/research/st-1.9/docs/st.html b/trunk/research/st-1.9/docs/st.html deleted file mode 100644 index a6b932a819..0000000000 --- a/trunk/research/st-1.9/docs/st.html +++ /dev/null @@ -1,504 +0,0 @@ - - -State Threads for Internet Applications - - -

State Threads for Internet Applications

-

Introduction

-

-State Threads is an application library which provides a -foundation for writing fast and highly scalable Internet Applications -on UNIX-like platforms. It combines the simplicity of the multithreaded -programming paradigm, in which one thread supports each simultaneous -connection, with the performance and scalability of an event-driven -state machine architecture.

- -

1. Definitions

-

- -

1.1 Internet Applications

- -

-An Internet Application (IA) is either a server or client network -application that accepts connections from clients and may or may not -connect to servers. In an IA the arrival or departure of network data -often controls processing (that is, IA is a data-driven application). -For each connection, an IA does some finite amount of work -involving data exchange with its peer, where its peer may be either -a client or a server. -The typical transaction steps of an IA are to accept a connection, -read a request, do some finite and predictable amount of work to -process the request, then write a response to the peer that sent the -request. One example of an IA is a Web server; -the most general example of an IA is a proxy server, because it both -accepts connections from clients and connects to other servers.

-

-We assume that the performance of an IA is constrained by available CPU -cycles rather than network bandwidth or disk I/O (that is, CPU -is a bottleneck resource). -

- - -

1.2 Performance and Scalability

- -

-The performance of an IA is usually evaluated as its -throughput measured in transactions per second or bytes per second (one -can be converted to the other, given the average transaction size). There are -several benchmarks that can be used to measure throughput of Web serving -applications for specific workloads (such as -SPECweb96, -WebStone, -WebBench). -Although there is no common definition for scalability, in general it -expresses the ability of an application to sustain its performance when some -external condition changes. For IAs this external condition is either the -number of clients (also known as "users," "simultaneous connections," or "load -generators") or the underlying hardware system size (number of CPUs, memory -size, and so on). Thus there are two types of scalability: load -scalability and system scalability, respectively. -

-The figure below shows how the throughput of an idealized IA changes with -the increasing number of clients (solid blue line). Initially the throughput -grows linearly (the slope represents the maximal throughput that one client -can provide). Within this initial range, the IA is underutilized and CPUs are -partially idle. Further increase in the number of clients leads to a system -saturation, and the throughput gradually stops growing as all CPUs become fully -utilized. After that point, the throughput stays flat because there are no -more CPU cycles available. -In the real world, however, each simultaneous connection -consumes some computational and memory resources, even when idle, and this -overhead grows with the number of clients. Therefore, the throughput of the -real world IA starts dropping after some point (dashed blue line in the figure -below). The rate at which the throughput drops depends, among other things, on -application design. -

-We say that an application has a good load scalability if it can -sustain its throughput over a wide range of loads. -Interestingly, the SPECweb99 -benchmark somewhat reflects the Web server's load scalability because it -measures the number of clients (load generators) given a mandatory minimal -throughput per client (that is, it measures the server's capacity). -This is unlike SPECweb96 and -other benchmarks that use the throughput as their main metric (see the figure -below). -

-

Figure: Throughput vs. Number of clients -
-

-System scalability is the ability of an application to sustain its -performance per hardware unit (such as a CPU) with the increasing number of -these units. In other words, good system scalability means that doubling the -number of processors will roughly double the application's throughput (dashed -green line). We assume here that the underlying operating system also scales -well. Good system scalability allows you to initially run an application on -the smallest system possible, while retaining the ability to move that -application to a larger system if necessary, without excessive effort or -expense. That is, an application need not be rewritten or even undergo a -major porting effort when changing system size. -

-Although scalability and performance are more important in the case of server -IAs, they should also be considered for some client applications (such as -benchmark load generators). -

- - -

1.3 Concurrency

- -

-Concurrency reflects the parallelism in a system. The two unrelated types -are virtual concurrency and real concurrency. -

    -
  • Virtual (or apparent) concurrency is the number of simultaneous -connections that a system supports. -

    -
  • Real concurrency is the number of hardware devices, including -CPUs, network cards, and disks, that actually allow a system to perform -tasks in parallel. -
-

-An IA must provide virtual concurrency in order to serve many users -simultaneously. -To achieve maximum performance and scalability in doing so, the number of -programming entities than an IA creates to be scheduled by the OS kernel -should be -kept close to (within an order of magnitude of) the real concurrency found on -the system. These programming entities scheduled by the kernel are known as -kernel execution vehicles. Examples of kernel execution vehicles -include Solaris lightweight processes and IRIX kernel threads. -In other words, the number of kernel execution vehicles should be dictated by -the system size and not by the number of simultaneous connections. -

- -

2. Existing Architectures

-

-There are a few different architectures that are commonly used by IAs. -These include the Multi-Process, -Multi-Threaded, and Event-Driven State Machine -architectures. -

- -

2.1 Multi-Process Architecture

- -

-In the Multi-Process (MP) architecture, an individual process is -dedicated to each simultaneous connection. -A process performs all of a transaction's initialization steps -and services a connection completely before moving on to service -a new connection. -

-User sessions in IAs are relatively independent; therefore, no -synchronization between processes handling different connections is -necessary. Because each process has its own private address space, -this architecture is very robust. If a process serving one of the connections -crashes, the other sessions will not be affected. However, to serve many -concurrent connections, an equal number of processes must be employed. -Because processes are kernel entities (and are in fact the heaviest ones), -the number of kernel entities will be at least as large as the number of -concurrent sessions. On most systems, good performance will not be achieved -when more than a few hundred processes are created because of the high -context-switching overhead. In other words, MP applications have poor load -scalability. -

-On the other hand, MP applications have very good system scalability, because -no resources are shared among different processes and there is no -synchronization overhead. -

-The Apache Web Server 1.x ([Reference 1]) uses the MP -architecture on UNIX systems. -

- -

2.2 Multi-Threaded Architecture

- -

-In the Multi-Threaded (MT) architecture, multiple independent threads -of control are employed within a single shared address space. Like a -process in the MP architecture, each thread performs all of a -transaction's initialization steps and services a connection completely -before moving on to service a new connection. -

-Many modern UNIX operating systems implement a many-to-few model when -mapping user-level threads to kernel entities. In this model, an -arbitrarily large number of user-level threads is multiplexed onto a -lesser number of kernel execution vehicles. Kernel execution -vehicles are also known as virtual processors. Whenever a user-level -thread makes a blocking system call, the kernel execution vehicle it is using -will become blocked in the kernel. If there are no other non-blocked kernel -execution vehicles and there are other runnable user-level threads, a new -kernel execution vehicle will be created automatically. This prevents the -application from blocking when it can continue to make useful forward -progress. -

-Because IAs are by nature network I/O driven, all concurrent sessions block on -network I/O at various points. As a result, the number of virtual processors -created in the kernel grows close to the number of user-level threads -(or simultaneous connections). When this occurs, the many-to-few model -effectively degenerates to a one-to-one model. Again, like in -the MP architecture, the number of kernel execution vehicles is dictated by -the number of simultaneous connections rather than by number of CPUs. This -reduces an application's load scalability. However, because kernel threads -(lightweight processes) use fewer resources and are more light-weight than -traditional UNIX processes, an MT application should scale better with load -than an MP application. -

-Unexpectedly, the small number of virtual processors sharing the same address -space in the MT architecture destroys an application's system scalability -because of contention among the threads on various locks. Even if an -application itself is carefully -optimized to avoid lock contention around its own global data (a non-trivial -task), there are still standard library functions and system calls -that use common resources hidden from the application. For example, -on many platforms thread safety of memory allocation routines -(malloc(3), free(3), and so on) is achieved by using a single -global lock. Another example is a per-process file descriptor table. -This common resource table is shared by all kernel execution vehicles within -the same process and must be protected when one modifies it via -certain system calls (such as open(2), close(2), and so on). -In addition to that, maintaining the caches coherent -among CPUs on multiprocessor systems hurts performance when different threads -running on different CPUs modify data items on the same cache line. -

-In order to improve load scalability, some applications employ a different -type of MT architecture: they create one or more thread(s) per task -rather than one thread per connection. For example, one small group -of threads may be responsible for accepting client connections, another -for request processing, and yet another for serving responses. The main -advantage of this architecture is that it eliminates the tight coupling -between the number of threads and number of simultaneous connections. However, -in this architecture, different task-specific thread groups must share common -work queues that must be protected by mutual exclusion locks (a typical -producer-consumer problem). This adds synchronization overhead that causes an -application to perform badly on multiprocessor systems. In other words, in -this architecture, the application's system scalability is sacrificed for the -sake of load scalability. -

-Of course, the usual nightmares of threaded programming, including data -corruption, deadlocks, and race conditions, also make MT architecture (in any -form) non-simplistic to use. -

- - -

2.3 Event-Driven State Machine Architecture

- -

-In the Event-Driven State Machine (EDSM) architecture, a single process -is employed to concurrently process multiple connections. The basics of this -architecture are described in Comer and Stevens -[Reference 2]. -The EDSM architecture performs one basic data-driven step associated with -a particular connection at a time, thus multiplexing many concurrent -connections. The process operates as a state machine that receives an event -and then reacts to it. -

-In the idle state the EDSM calls select(2) or poll(2) to -wait for network I/O events. When a particular file descriptor is ready for -I/O, the EDSM completes the corresponding basic step (usually by invoking a -handler function) and starts the next one. This architecture uses -non-blocking system calls to perform asynchronous network I/O operations. -For more details on non-blocking I/O see Stevens -[Reference 3]. -

-To take advantage of hardware parallelism (real concurrency), multiple -identical processes may be created. This is called Symmetric Multi-Process -EDSM and is used, for example, in the Zeus Web Server -([Reference 4]). To more efficiently multiplex disk I/O, -special "helper" processes may be created. This is called Asymmetric -Multi-Process EDSM and was proposed for Web servers by Druschel -and others [Reference 5]. -

-EDSM is probably the most scalable architecture for IAs. -Because the number of simultaneous connections (virtual concurrency) is -completely decoupled from the number of kernel execution vehicles (processes), -this architecture has very good load scalability. It requires only minimal -user-level resources to create and maintain additional connection. -

-Like MP applications, Multi-Process EDSM has very good system scalability -because no resources are shared among different processes and there is no -synchronization overhead. -

-Unfortunately, the EDSM architecture is monolithic rather than based on the -concept of threads, so new applications generally need to be implemented from -the ground up. In effect, the EDSM architecture simulates threads and their -stacks the hard way. -

- - -

3. State Threads Library

- -

-The State Threads library combines the advantages of all of the above -architectures. The interface preserves the programming simplicity of thread -abstraction, allowing each simultaneous connection to be treated as a separate -thread of execution within a single process. The underlying implementation is -close to the EDSM architecture as the state of each particular concurrent -session is saved in a separate memory segment. -

- -

3.1 State Changes and Scheduling

-

-The state of each concurrent session includes its stack environment -(stack pointer, program counter, CPU registers) and its stack. Conceptually, -a thread context switch can be viewed as a process changing its state. There -are no kernel entities involved other than processes. -Unlike other general-purpose threading libraries, the State Threads library -is fully deterministic. The thread context switch (process state change) can -only happen in a well-known set of functions (at I/O points or at explicit -synchronization points). As a result, process-specific global data does not -have to be protected by mutual exclusion locks in most cases. The entire -application is free to use all the static variables and non-reentrant library -functions it wants, greatly simplifying programming and debugging while -increasing performance. This is somewhat similar to a co-routine model -(co-operatively multitasked threads), except that no explicit yield is needed --- -sooner or later, a thread performs a blocking I/O operation and thus surrenders -control. All threads of execution (simultaneous connections) have the -same priority, so scheduling is non-preemptive, like in the EDSM architecture. -Because IAs are data-driven (processing is limited by the size of network -buffers and data arrival rates), scheduling is non-time-slicing. -

-Only two types of external events are handled by the library's -scheduler, because only these events can be detected by -select(2) or poll(2): I/O events (a file descriptor is ready -for I/O) and time events -(some timeout has expired). However, other types of events (such as -a signal sent to a process) can also be handled by converting them to I/O -events. For example, a signal handling function can perform a write to a pipe -(write(2) is reentrant/asynchronous-safe), thus converting a signal -event to an I/O event. -

-To take advantage of hardware parallelism, as in the EDSM architecture, -multiple processes can be created in either a symmetric or asymmetric manner. -Process management is not in the library's scope but instead is left up to the -application. -

-There are several general-purpose threading libraries that implement a -many-to-one model (many user-level threads to one kernel execution -vehicle), using the same basic techniques as the State Threads library -(non-blocking I/O, event-driven scheduler, and so on). For an example, see GNU -Portable Threads ([Reference 6]). Because they are -general-purpose, these libraries have different objectives than the State -Threads library. The State Threads library is not a general-purpose -threading library, -but rather an application library that targets only certain types of -applications (IAs) in order to achieve the highest possible performance and -scalability for those applications. -

- -

3.2 Scalability

-

-State threads are very lightweight user-level entities, and therefore creating -and maintaining user connections requires minimal resources. An application -using the State Threads library scales very well with the increasing number -of connections. -

-On multiprocessor systems an application should create multiple processes -to take advantage of hardware parallelism. Using multiple separate processes -is the only way to achieve the highest possible system scalability. -This is because duplicating per-process resources is the only way to avoid -significant synchronization overhead on multiprocessor systems. Creating -separate UNIX processes naturally offers resource duplication. Again, -as in the EDSM architecture, there is no connection between the number of -simultaneous connections (which may be very large and changes within a wide -range) and the number of kernel entities (which is usually small and constant). -In other words, the State Threads library makes it possible to multiplex a -large number of simultaneous connections onto a much smaller number of -separate processes, thus allowing an application to scale well with both -the load and system size. -

- -

3.3 Performance

-

-Performance is one of the library's main objectives. The State Threads -library is implemented to minimize the number of system calls and -to make thread creation and context switching as fast as possible. -For example, per-thread signal mask does not exist (unlike -POSIX threads), so there is no need to save and restore a process's -signal mask on every thread context switch. This eliminates two system -calls per context switch. Signal events can be handled much more -efficiently by converting them to I/O events (see above). -

- -

3.4 Portability

-

-The library uses the same general, underlying concepts as the EDSM -architecture, including non-blocking I/O, file descriptors, and -I/O multiplexing. These concepts are available in some form on most -UNIX platforms, making the library very portable across many -flavors of UNIX. There are only a few platform-dependent sections in the -source. -

- -

3.5 State Threads and NSPR

-

-The State Threads library is a derivative of the Netscape Portable -Runtime library (NSPR) [Reference 7]. The primary goal of -NSPR is to provide a platform-independent layer for system facilities, -where system facilities include threads, thread synchronization, and I/O. -Performance and scalability are not the main concern of NSPR. The -State Threads library addresses performance and scalability while -remaining much smaller than NSPR. It is contained in 8 source files -as opposed to more than 400, but provides all the functionality that -is needed to write efficient IAs on UNIX-like platforms. -

- - - - - - - - - - - - - - - - - - - - - - - - - - - -
NSPRState Threads
Lines of code~150,000~3000
Dynamic library size  
(debug version)
IRIX~700 KB~60 KB
Linux~900 KB~70 KB
-

- -

Conclusion

-

-State Threads is an application library which provides a foundation for -writing Internet Applications. To summarize, it has the -following advantages: -

-

    -
  • It allows the design of fast and highly scalable applications. An -application will scale well with both load and number of CPUs. -

    -

  • It greatly simplifies application programming and debugging because, as a -rule, no mutual exclusion locking is necessary and the entire application is -free to use static variables and non-reentrant library functions. -
-

-The library's main limitation: -

-

    -
  • All I/O operations on sockets must use the State Thread library's I/O -functions because only those functions perform thread scheduling and prevent -the application's processes from blocking. -
-

- -

References

-
    - -
  1. Apache Software Foundation, -http://www.apache.org. - -
  2. Douglas E. Comer, David L. Stevens, Internetworking With TCP/IP, -Vol. III: Client-Server Programming And Applications, Second Edition, -Ch. 8, 12. - -
  3. W. Richard Stevens, UNIX Network Programming, Second Edition, -Vol. 1, Ch. 15. - -
  4. Zeus Technology Limited, -http://www.zeus.co.uk. - -
  5. Peter Druschel, Vivek S. Pai, Willy Zwaenepoel, - -Flash: An Efficient and Portable Web Server. In Proceedings of the -USENIX 1999 Annual Technical Conference, Monterey, CA, June 1999. - -
  6. GNU Portable Threads, -http://www.gnu.org/software/pth/. - -
  7. Netscape Portable Runtime, -http://www.mozilla.org/docs/refList/refNSPR/. -
- -

Other resources covering various architectural issues in IAs

-
    -
  1. Dan Kegel, The C10K problem, -http://www.kegel.com/c10k.html. -
  2. -
  3. James C. Hu, Douglas C. Schmidt, Irfan Pyarali, JAWS: Understanding -High Performance Web Systems, -http://www.cs.wustl.edu/~jxh/research/research.html.
  4. -
-

-


-

- -

Portions created by SGI are Copyright © 2000 -Silicon Graphics, Inc. All rights reserved.
-

- - - - diff --git a/trunk/research/st-1.9/docs/timeout_heap.txt b/trunk/research/st-1.9/docs/timeout_heap.txt deleted file mode 100644 index 1582dc1293..0000000000 --- a/trunk/research/st-1.9/docs/timeout_heap.txt +++ /dev/null @@ -1,60 +0,0 @@ -How the timeout heap works - -As of version 1.5, the State Threads Library represents the queue of -sleeping threads using a heap data structure rather than a sorted -linked list. This improves performance when there is a large number -of sleeping threads, since insertion into a heap takes O(log N) time -while insertion into a sorted list takes O(N) time. For example, in -one test 1000 threads were created, each thread called st_usleep() -with a random time interval, and then all the threads where -immediately interrupted and joined before the sleeps had a chance to -finish. The whole process was repeated 1000 times, for a total of a -million sleep queue insertions and removals. With the old list-based -sleep queue, this test took 100 seconds; now it takes only 12 seconds. - -Heap data structures are typically based on dynamically resized -arrays. However, since the existing ST code base was very nicely -structured around linking the thread objects into pointer-based lists -without the need for any auxiliary data structures, implementing the -heap using a similar nodes-and-pointers based approach seemed more -appropriate for ST than introducing a separate array. - -Thus, the new ST timeout heap works by organizing the existing -_st_thread_t objects in a balanced binary tree, just as they were -previously organized into a doubly-linked, sorted list. The global -_ST_SLEEPQ variable, formerly a linked list head, is now simply a -pointer to the root of this tree, and the root node of the tree is the -thread with the earliest timeout. Each thread object has two child -pointers, "left" and "right", pointing to threads with later timeouts. - -Each node in the tree is numbered with an integer index, corresponding -to the array index in an array-based heap, and the tree is kept fully -balanced and left-adjusted at all times. In other words, the tree -consists of any number of fully populated top levels, followed by a -single bottom level which may be partially populated, such that any -existing nodes form a contiguous block to the left and the spaces for -missing nodes form a contiguous block to the right. For example, if -there are nine threads waiting for a timeout, they are numbered and -arranged in a tree exactly as follows: - - 1 - / \ - 2 3 - / \ / \ - 4 5 6 7 - / \ - 8 9 - -Each node has either no children, only a left child, or both a left -and a right child. Children always time out later than their parents -(this is called the "heap invariant"), but when a node has two -children, their mutual order is unspecified - the left child may time -out before or after the right child. If a node is numbered N, its -left child is numbered 2N, and its right child is numbered 2N+1. - -There is no pointer from a child to its parent; all pointers point -downward. Additions and deletions both work by starting at the root -and traversing the tree towards the leaves, going left or right -according to the binary digits forming the index of the destination -node. As nodes are added or deleted, existing nodes are rearranged to -maintain the heap invariant. diff --git a/trunk/research/st-1.9/examples/Makefile b/trunk/research/st-1.9/examples/Makefile deleted file mode 100644 index 31c0a6e240..0000000000 --- a/trunk/research/st-1.9/examples/Makefile +++ /dev/null @@ -1,115 +0,0 @@ -# -# Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. -# All Rights Reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of Silicon Graphics, Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -########################## -# Supported OSes: -# -# AIX -# FREEBSD -# HPUX -# HPUX_64 -# IRIX -# IRIX_64 -# LINUX -# LINUX_IA64 -# NETBSD -# OPENBSD -# OSF1 -# SOLARIS -# SOLARIS_64 - -########################## - -CC = cc - -SHELL = /bin/sh -ECHO = /bin/echo - -DEPTH = .. -BUILD = -TARGETDIR = - -DEFINES = -CFLAGS = -OTHER_FLAGS = - -OBJDIR = $(DEPTH)/$(TARGETDIR) -INCDIR = $(DEPTH)/$(TARGETDIR) -LIBST = $(OBJDIR)/libst.a -HEADER = $(INCDIR)/st.h - -LIBRESOLV = -EXTRALIBS = - -ifeq ($(OS),) -EXAMPLES = unknown -else -EXAMPLES = $(OBJDIR)/lookupdns $(OBJDIR)/proxy $(OBJDIR)/server -endif - - -########################## -# Platform section. -# - -ifeq (DARWIN, $(findstring DARWIN, $(OS))) -LIBRESOLV = -lresolv -endif - -ifeq (LINUX, $(findstring LINUX, $(OS))) -LIBRESOLV = -lresolv -endif - -ifeq (SOLARIS, $(findstring SOLARIS, $(OS))) -LIBRESOLV = -lresolv -EXTRALIBS = -lsocket -lnsl -endif - -# -# End of platform section. -########################## - - -all: $(EXAMPLES) - -$(OBJDIR)/lookupdns: lookupdns.c $(OBJDIR)/res.o $(LIBST) $(HEADER) - $(CC) $(CFLAGS) -I$(INCDIR) lookupdns.c $(OBJDIR)/res.o $(LIBST) $(LIBRESOLV) $(EXTRALIBS) -o $@ - -$(OBJDIR)/proxy: proxy.c $(LIBST) $(HEADER) - $(CC) $(CFLAGS) -I$(INCDIR) proxy.c $(LIBST) $(EXTRALIBS) -o $@ - -$(OBJDIR)/server: server.c $(OBJDIR)/error.o $(LIBST) $(HEADER) - $(CC) $(CFLAGS) -I$(INCDIR) server.c $(OBJDIR)/error.o $(LIBST) $(EXTRALIBS) -o $@ - -$(OBJDIR)/%.o: %.c - $(CC) $(CFLAGS) -I$(INCDIR) -c $< -o $@ - -.DEFAULT: - @cd $(DEPTH); $(MAKE) $@ - diff --git a/trunk/research/st-1.9/examples/README b/trunk/research/st-1.9/examples/README deleted file mode 100644 index 646d4f6236..0000000000 --- a/trunk/research/st-1.9/examples/README +++ /dev/null @@ -1,98 +0,0 @@ -Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. -All Rights Reserved. - - -This directory contains three example programs. - - ---------------------------------------------------------------------------- - -PROGRAM - - lookupdns - -FILES - - lookupdns.c - res.c - -USAGE - - lookupdns [] ... - -DESCRIPTION - - This program performs asynchronous DNS host name resolution and reports - IP address for each specified as a command line argument. - One ST thread is created for each host name. All threads do host name - resolution concurrently. - - ---------------------------------------------------------------------------- - -PROGRAM - - proxy - -FILES - - proxy.c - -USAGE - - proxy -l -r [-p ] [-S] - - -l bind to local address specified as []: - -r connect to remote address specified as : - -p create specified number of processes - -S serialize accept() calls from different processes - on the same listening socket (if needed). - -DESCRIPTION - - This program acts as a generic gateway. It listens for connections to a - local address. Upon accepting a client connection, it connects to the - specified remote address and then just pumps the data through without any - modification. - - ---------------------------------------------------------------------------- - -PROGRAM - - server - -FILES - - server.c - error.c - -USAGE - - server -l [] - - -l open all log files in specified directory. - - Possible options: - - -b : bind to specified address (multiple addresses - are permitted) - -p create specified number of processes - -t : specify thread limits per listening socket - across all processes - -u change server's user id to specified value - -q set max length of pending connections queue - -a enable access logging - -i run in interactive mode (useful for debugging) - -S serialize accept() calls from different processes - on the same listening socket (if needed). - -DESCRIPTION - - This program is a general server example. It accepts a client connection - and outputs a short HTML page. It can be easily adapted to provide - other services. - - ---------------------------------------------------------------------------- - diff --git a/trunk/research/st-1.9/examples/error.c b/trunk/research/st-1.9/examples/error.c deleted file mode 100644 index 0b2e772874..0000000000 --- a/trunk/research/st-1.9/examples/error.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. - * All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Silicon Graphics, Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include -#include -#include -#include -#include "st.h" - -/* - * Simple error reporting functions. - * Suggested in W. Richard Stevens' "Advanced Programming in UNIX - * Environment". - */ - -#define MAXLINE 4096 /* max line length */ - -static void err_doit(int, int, const char *, va_list); - - -/* - * Nonfatal error related to a system call. - * Print a message and return. - */ -void err_sys_report(int fd, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - err_doit(fd, 1, fmt, ap); - va_end(ap); -} - - -/* - * Fatal error related to a system call. - * Print a message and terminate. - */ -void err_sys_quit(int fd, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - err_doit(fd, 1, fmt, ap); - va_end(ap); - exit(1); -} - - -/* - * Fatal error related to a system call. - * Print a message, dump core, and terminate. - */ -void err_sys_dump(int fd, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - err_doit(fd, 1, fmt, ap); - va_end(ap); - abort(); /* dump core and terminate */ - exit(1); /* shouldn't get here */ -} - - -/* - * Nonfatal error unrelated to a system call. - * Print a message and return. - */ -void err_report(int fd, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - err_doit(fd, 0, fmt, ap); - va_end(ap); -} - - -/* - * Fatal error unrelated to a system call. - * Print a message and terminate. - */ -void err_quit(int fd, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - err_doit(fd, 0, fmt, ap); - va_end(ap); - exit(1); -} - - -/* - * Return a pointer to a string containing current time. - */ -char *err_tstamp(void) -{ - static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; - static char str[32]; - static time_t lastt = 0; - struct tm *tmp; - time_t currt = st_time(); - - if (currt == lastt) - return str; - - tmp = localtime(&currt); - sprintf(str, "[%02d/%s/%d:%02d:%02d:%02d] ", tmp->tm_mday, - months[tmp->tm_mon], 1900 + tmp->tm_year, tmp->tm_hour, - tmp->tm_min, tmp->tm_sec); - lastt = currt; - - return str; -} - - -/* - * Print a message and return to caller. - * Caller specifies "errnoflag". - */ -static void err_doit(int fd, int errnoflag, const char *fmt, va_list ap) -{ - int errno_save; - char buf[MAXLINE]; - - errno_save = errno; /* value caller might want printed */ - strcpy(buf, err_tstamp()); /* prepend a message with time stamp */ - vsprintf(buf + strlen(buf), fmt, ap); - if (errnoflag) - sprintf(buf + strlen(buf), ": %s\n", strerror(errno_save)); - else - strcat(buf, "\n"); - write(fd, buf, strlen(buf)); - errno = errno_save; -} - diff --git a/trunk/research/st-1.9/examples/lookupdns.c b/trunk/research/st-1.9/examples/lookupdns.c deleted file mode 100644 index 98f6ec5d82..0000000000 --- a/trunk/research/st-1.9/examples/lookupdns.c +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. - * All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Silicon Graphics, Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "st.h" - -#if !defined(NETDB_INTERNAL) && defined(h_NETDB_INTERNAL) -#define NETDB_INTERNAL h_NETDB_INTERNAL -#endif - -/* Resolution timeout (in microseconds) */ -#define TIMEOUT (2*1000000LL) - -/* External function defined in the res.c file */ -int dns_getaddr(const char *host, struct in_addr *addr, st_utime_t timeout); - - -void *do_resolve(void *host) -{ - struct in_addr addr; - - /* Use dns_getaddr() instead of gethostbyname(3) to get IP address */ - if (dns_getaddr(host, &addr, TIMEOUT) < 0) { - fprintf(stderr, "dns_getaddr: can't resolve %s: ", (char *)host); - if (h_errno == NETDB_INTERNAL) - perror(""); - else - herror(""); - } else - printf("%-40s %s\n", (char *)host, inet_ntoa(addr)); - - return NULL; -} - - -/* - * Asynchronous DNS host name resolution. This program creates one - * ST thread for each host name (specified as command line arguments). - * All threads do host name resolution concurrently. - */ -int main(int argc, char *argv[]) -{ - int i; - - if (argc < 2) { - fprintf(stderr, "Usage: %s [] ...\n", argv[0]); - exit(1); - } - - if (st_init() < 0) { - perror("st_init"); - exit(1); - } - - for (i = 1; i < argc; i++) { - /* Create a separate thread for each host name */ - if (st_thread_create(do_resolve, argv[i], 0, 0) == NULL) { - perror("st_thread_create"); - exit(1); - } - } - - st_thread_exit(NULL); - - /* NOTREACHED */ - return 1; -} - diff --git a/trunk/research/st-1.9/examples/proxy.c b/trunk/research/st-1.9/examples/proxy.c deleted file mode 100644 index 2f4636d6b8..0000000000 --- a/trunk/research/st-1.9/examples/proxy.c +++ /dev/null @@ -1,541 +0,0 @@ -/* - * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. - * All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Silicon Graphics, Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "st.h" - -#define IOBUFSIZE (16*1024) - -#define IOV_LEN 256 -#define IOV_COUNT (IOBUFSIZE / IOV_LEN) - -#ifndef INADDR_NONE -#define INADDR_NONE 0xffffffff -#endif - -static char *prog; /* Program name */ -static struct sockaddr_in rmt_addr; /* Remote address */ - -static unsigned long testing; -#define TESTING_VERBOSE 0x1 -#define TESTING_READV 0x2 -#define TESTING_READ_RESID 0x4 -#define TESTING_WRITEV 0x8 -#define TESTING_WRITE_RESID 0x10 - -static void read_address(const char *str, struct sockaddr_in *sin); -static void start_daemon(void); -static int cpu_count(void); -static void set_concurrency(int nproc); -static void *handle_request(void *arg); -static void print_sys_error(const char *msg); - - -/* - * This program acts as a generic gateway. It listens for connections - * to a local address ('-l' option). Upon accepting a client connection, - * it connects to the specified remote address ('-r' option) and then - * just pumps the data through without any modification. - */ -int main(int argc, char *argv[]) -{ - extern char *optarg; - int opt, sock, n; - int laddr, raddr, num_procs, alt_ev, one_process; - int serialize_accept = 0; - struct sockaddr_in lcl_addr, cli_addr; - st_netfd_t cli_nfd, srv_nfd; - - prog = argv[0]; - num_procs = laddr = raddr = alt_ev = one_process = 0; - - /* Parse arguments */ - while((opt = getopt(argc, argv, "l:r:p:Saht:X")) != EOF) { - switch (opt) { - case 'a': - alt_ev = 1; - break; - case 'l': - read_address(optarg, &lcl_addr); - laddr = 1; - break; - case 'r': - read_address(optarg, &rmt_addr); - if (rmt_addr.sin_addr.s_addr == INADDR_ANY) { - fprintf(stderr, "%s: invalid remote address: %s\n", prog, optarg); - exit(1); - } - raddr = 1; - break; - case 'p': - num_procs = atoi(optarg); - if (num_procs < 1) { - fprintf(stderr, "%s: invalid number of processes: %s\n", prog, optarg); - exit(1); - } - break; - case 'S': - /* - * Serialization decision is tricky on some platforms. For example, - * Solaris 2.6 and above has kernel sockets implementation, so supposedly - * there is no need for serialization. The ST library may be compiled - * on one OS version, but used on another, so the need for serialization - * should be determined at run time by the application. Since it's just - * an example, the serialization decision is left up to user. - * Only on platforms where the serialization is never needed on any OS - * version st_netfd_serialize_accept() is a no-op. - */ - serialize_accept = 1; - break; - case 't': - testing = strtoul(optarg, NULL, 0); - break; - case 'X': - one_process = 1; - break; - case 'h': - case '?': - fprintf(stderr, "Usage: %s [options] -l <[host]:port> -r \n", - prog); - fprintf(stderr, "options are:\n"); - fprintf(stderr, " -p number of parallel processes\n"); - fprintf(stderr, " -S serialize accepts\n"); - fprintf(stderr, " -a use alternate event system\n"); -#ifdef DEBUG - fprintf(stderr, " -t mask testing/debugging mode\n"); - fprintf(stderr, " -X one process, don't daemonize\n"); -#endif - exit(1); - } - } - if (!laddr) { - fprintf(stderr, "%s: local address required\n", prog); - exit(1); - } - if (!raddr) { - fprintf(stderr, "%s: remote address required\n", prog); - exit(1); - } - if (num_procs == 0) - num_procs = cpu_count(); - - fprintf(stderr, "%s: starting proxy daemon on %s:%d\n", prog, - inet_ntoa(lcl_addr.sin_addr), ntohs(lcl_addr.sin_port)); - - /* Start the daemon */ - if (one_process) - num_procs = 1; - else - start_daemon(); - - if (alt_ev) - st_set_eventsys(ST_EVENTSYS_ALT); - - /* Initialize the ST library */ - if (st_init() < 0) { - print_sys_error("st_init"); - exit(1); - } - - /* Create and bind listening socket */ - if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) { - print_sys_error("socket"); - exit(1); - } - n = 1; - if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&n, sizeof(n)) < 0) { - print_sys_error("setsockopt"); - exit(1); - } - if (bind(sock, (struct sockaddr *)&lcl_addr, sizeof(lcl_addr)) < 0) { - print_sys_error("bind"); - exit(1); - } - listen(sock, 128); - if ((srv_nfd = st_netfd_open_socket(sock)) == NULL) { - print_sys_error("st_netfd_open"); - exit(1); - } - /* See the comment regarding serialization decision above */ - if (num_procs > 1 && serialize_accept && st_netfd_serialize_accept(srv_nfd) - < 0) { - print_sys_error("st_netfd_serialize_accept"); - exit(1); - } - - /* Start server processes */ - if (!one_process) - set_concurrency(num_procs); - - for ( ; ; ) { - n = sizeof(cli_addr); - cli_nfd = st_accept(srv_nfd, (struct sockaddr *)&cli_addr, &n, - ST_UTIME_NO_TIMEOUT); - if (cli_nfd == NULL) { - print_sys_error("st_accept"); - exit(1); - } - if (st_thread_create(handle_request, cli_nfd, 0, 0) == NULL) { - print_sys_error("st_thread_create"); - exit(1); - } - } - - /* NOTREACHED */ - return 1; -} - - -static void read_address(const char *str, struct sockaddr_in *sin) -{ - char host[128], *p; - struct hostent *hp; - unsigned short port; - - strcpy(host, str); - if ((p = strchr(host, ':')) == NULL) { - fprintf(stderr, "%s: invalid address: %s\n", prog, host); - exit(1); - } - *p++ = '\0'; - port = (unsigned short) atoi(p); - if (port < 1) { - fprintf(stderr, "%s: invalid port: %s\n", prog, p); - exit(1); - } - - memset(sin, 0, sizeof(struct sockaddr_in)); - sin->sin_family = AF_INET; - sin->sin_port = htons(port); - if (host[0] == '\0') { - sin->sin_addr.s_addr = INADDR_ANY; - return; - } - sin->sin_addr.s_addr = inet_addr(host); - if (sin->sin_addr.s_addr == INADDR_NONE) { - /* not dotted-decimal */ - if ((hp = gethostbyname(host)) == NULL) { - fprintf(stderr, "%s: can't resolve address: %s\n", prog, host); - exit(1); - } - memcpy(&sin->sin_addr, hp->h_addr, hp->h_length); - } -} - -#ifdef DEBUG -static void show_iov(const struct iovec *iov, int niov) -{ - int i; - size_t total; - - printf("iov %p has %d entries:\n", iov, niov); - total = 0; - for (i = 0; i < niov; i++) { - printf("iov[%3d] iov_base=%p iov_len=0x%lx(%lu)\n", - i, iov[i].iov_base, (unsigned long) iov[i].iov_len, - (unsigned long) iov[i].iov_len); - total += iov[i].iov_len; - } - printf("total 0x%lx(%ld)\n", (unsigned long) total, (unsigned long) total); -} - -/* - * This version is tricked out to test all the - * st_(read|write)v?(_resid)? variants. Use the non-DEBUG version for - * anything serious. st_(read|write) are all this function really - * needs. - */ -static int pass(st_netfd_t in, st_netfd_t out) -{ - char buf[IOBUFSIZE]; - struct iovec iov[IOV_COUNT]; - int ioviter, nw, nr; - - if (testing & TESTING_READV) { - for (ioviter = 0; ioviter < IOV_COUNT; ioviter++) { - iov[ioviter].iov_base = &buf[ioviter * IOV_LEN]; - iov[ioviter].iov_len = IOV_LEN; - } - if (testing & TESTING_VERBOSE) { - printf("readv(%p)...\n", in); - show_iov(iov, IOV_COUNT); - } - if (testing & TESTING_READ_RESID) { - struct iovec *riov = iov; - int riov_cnt = IOV_COUNT; - if (st_readv_resid(in, &riov, &riov_cnt, ST_UTIME_NO_TIMEOUT) == 0) { - if (testing & TESTING_VERBOSE) { - printf("resid\n"); - show_iov(riov, riov_cnt); - printf("full\n"); - show_iov(iov, IOV_COUNT); - } - nr = 0; - for (ioviter = 0; ioviter < IOV_COUNT; ioviter++) - nr += iov[ioviter].iov_len; - nr = IOBUFSIZE - nr; - } else - nr = -1; - } else - nr = (int) st_readv(in, iov, IOV_COUNT, ST_UTIME_NO_TIMEOUT); - } else { - if (testing & TESTING_READ_RESID) { - size_t resid = IOBUFSIZE; - if (st_read_resid(in, buf, &resid, ST_UTIME_NO_TIMEOUT) == 0) - nr = IOBUFSIZE - resid; - else - nr = -1; - } else - nr = (int) st_read(in, buf, IOBUFSIZE, ST_UTIME_NO_TIMEOUT); - } - if (testing & TESTING_VERBOSE) - printf("got 0x%x(%d) E=%d\n", nr, nr, errno); - - if (nr <= 0) - return 0; - - if (testing & TESTING_WRITEV) { - for (nw = 0, ioviter = 0; nw < nr; - nw += iov[ioviter].iov_len, ioviter++) { - iov[ioviter].iov_base = &buf[nw]; - iov[ioviter].iov_len = nr - nw; - if (iov[ioviter].iov_len > IOV_LEN) - iov[ioviter].iov_len = IOV_LEN; - } - if (testing & TESTING_VERBOSE) { - printf("writev(%p)...\n", out); - show_iov(iov, ioviter); - } - if (testing & TESTING_WRITE_RESID) { - struct iovec *riov = iov; - int riov_cnt = ioviter; - if (st_writev_resid(out, &riov, &riov_cnt, ST_UTIME_NO_TIMEOUT) == 0) { - if (testing & TESTING_VERBOSE) { - printf("resid\n"); - show_iov(riov, riov_cnt); - printf("full\n"); - show_iov(iov, ioviter); - } - nw = 0; - while (--ioviter >= 0) - nw += iov[ioviter].iov_len; - nw = nr - nw; - } else - nw = -1; - } else - nw = st_writev(out, iov, ioviter, ST_UTIME_NO_TIMEOUT); - } else { - if (testing & TESTING_WRITE_RESID) { - size_t resid = nr; - if (st_write_resid(out, buf, &resid, ST_UTIME_NO_TIMEOUT) == 0) - nw = nr - resid; - else - nw = -1; - } else - nw = st_write(out, buf, nr, ST_UTIME_NO_TIMEOUT); - } - if (testing & TESTING_VERBOSE) - printf("put 0x%x(%d) E=%d\n", nw, nw, errno); - - if (nw != nr) - return 0; - - return 1; -} -#else /* DEBUG */ -/* - * This version is the simple one suitable for serious use. - */ -static int pass(st_netfd_t in, st_netfd_t out) -{ - char buf[IOBUFSIZE]; - int nw, nr; - - nr = (int) st_read(in, buf, IOBUFSIZE, ST_UTIME_NO_TIMEOUT); - if (nr <= 0) - return 0; - - nw = st_write(out, buf, nr, ST_UTIME_NO_TIMEOUT); - if (nw != nr) - return 0; - - return 1; -} -#endif - -static void *handle_request(void *arg) -{ - struct pollfd pds[2]; - st_netfd_t cli_nfd, rmt_nfd; - int sock; - - cli_nfd = (st_netfd_t) arg; - pds[0].fd = st_netfd_fileno(cli_nfd); - pds[0].events = POLLIN; - - /* Connect to remote host */ - if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) { - print_sys_error("socket"); - goto done; - } - if ((rmt_nfd = st_netfd_open_socket(sock)) == NULL) { - print_sys_error("st_netfd_open_socket"); - close(sock); - goto done; - } - if (st_connect(rmt_nfd, (struct sockaddr *)&rmt_addr, - sizeof(rmt_addr), ST_UTIME_NO_TIMEOUT) < 0) { - print_sys_error("st_connect"); - st_netfd_close(rmt_nfd); - goto done; - } - pds[1].fd = sock; - pds[1].events = POLLIN; - - /* - * Now just pump the data through. - * XXX This should use one thread for each direction for true full-duplex. - */ - for ( ; ; ) { - pds[0].revents = 0; - pds[1].revents = 0; - - if (st_poll(pds, 2, ST_UTIME_NO_TIMEOUT) <= 0) { - print_sys_error("st_poll"); - break; - } - - if (pds[0].revents & POLLIN) { - if (!pass(cli_nfd, rmt_nfd)) - break; - } - - if (pds[1].revents & POLLIN) { - if (!pass(rmt_nfd, cli_nfd)) - break; - } - } - st_netfd_close(rmt_nfd); - -done: - - st_netfd_close(cli_nfd); - - return NULL; -} - -static void start_daemon(void) -{ - pid_t pid; - - /* Start forking */ - if ((pid = fork()) < 0) { - print_sys_error("fork"); - exit(1); - } - if (pid > 0) - exit(0); /* parent */ - - /* First child process */ - setsid(); /* become session leader */ - - if ((pid = fork()) < 0) { - print_sys_error("fork"); - exit(1); - } - if (pid > 0) /* first child */ - exit(0); - - chdir("/"); - umask(022); -} - -/* - * Create separate processes ("virtual processors"). Since it's just an - * example, there is no watchdog - the parent just exits leaving children - * on their own. - */ -static void set_concurrency(int nproc) -{ - pid_t pid; - int i; - - if (nproc < 1) - nproc = 1; - - for (i = 0; i < nproc; i++) { - if ((pid = fork()) < 0) { - print_sys_error("fork"); - exit(1); - } - /* Child returns */ - if (pid == 0) - return; - } - - /* Parent just exits */ - exit(0); -} - -static int cpu_count(void) -{ - int n; - -#if defined (_SC_NPROCESSORS_ONLN) - n = (int) sysconf(_SC_NPROCESSORS_ONLN); -#elif defined (_SC_NPROC_ONLN) - n = (int) sysconf(_SC_NPROC_ONLN); -#elif defined (HPUX) -#include - n = mpctl(MPC_GETNUMSPUS, 0, 0); -#else - n = -1; - errno = ENOSYS; -#endif - - return n; -} - -static void print_sys_error(const char *msg) -{ - fprintf(stderr, "%s: %s: %s\n", prog, msg, strerror(errno)); -} - diff --git a/trunk/research/st-1.9/examples/res.c b/trunk/research/st-1.9/examples/res.c deleted file mode 100644 index 14ecd8c927..0000000000 --- a/trunk/research/st-1.9/examples/res.c +++ /dev/null @@ -1,305 +0,0 @@ -/* - * Copyright (c) 1985, 1988, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. - * All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Silicon Graphics, Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#if defined (DARWIN) -#define BIND_8_COMPAT -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "st.h" - -#define MAXPACKET 1024 - -#if !defined(NETDB_INTERNAL) && defined(h_NETDB_INTERNAL) -#define NETDB_INTERNAL h_NETDB_INTERNAL -#endif - -/* New in Solaris 7 */ -#if !defined(_getshort) && defined(ns_get16) -#define _getshort(cp) ns_get16(cp) -#endif - -typedef union { - HEADER hdr; - u_char buf[MAXPACKET]; -} querybuf_t; - - -static int parse_answer(querybuf_t *ans, int len, struct in_addr *addr) -{ - char buf[MAXPACKET]; - HEADER *ahp; - u_char *cp, *eoa; - int type, n; - - ahp = &ans->hdr; - eoa = ans->buf + len; - cp = ans->buf + sizeof(HEADER); - - while (ahp->qdcount > 0) { - ahp->qdcount--; - cp += dn_skipname(cp, eoa) + QFIXEDSZ; - } - while (ahp->ancount > 0 && cp < eoa) { - ahp->ancount--; - if ((n = dn_expand(ans->buf, eoa, cp, buf, sizeof(buf))) < 0) - break; - cp += n; - type = _getshort(cp); - cp += 8; - n = _getshort(cp); - cp += 2; - if (type == T_CNAME) { - cp += n; - continue; - } - memcpy(addr, cp, n); - return 0; - } - - h_errno = TRY_AGAIN; - return -1; -} - - -static int query_domain(st_netfd_t nfd, const char *name, struct in_addr *addr, - st_utime_t timeout) -{ - querybuf_t qbuf; - u_char *buf = qbuf.buf; - HEADER *hp = &qbuf.hdr; - int blen = sizeof(qbuf); - int i, len, id; - - for (i = 0; i < _res.nscount; i++) { - len = res_mkquery(QUERY, name, C_IN, T_A, NULL, 0, NULL, buf, blen); - if (len <= 0) { - h_errno = NO_RECOVERY; - return -1; - } - id = hp->id; - - if (st_sendto(nfd, buf, len, (struct sockaddr *)&(_res.nsaddr_list[i]), - sizeof(struct sockaddr), timeout) != len) { - h_errno = NETDB_INTERNAL; - /* EINTR means interrupt by other thread, NOT by a caught signal */ - if (errno == EINTR) - return -1; - continue; - } - - /* Wait for reply */ - do { - len = st_recvfrom(nfd, buf, blen, NULL, NULL, timeout); - if (len <= 0) - break; - } while (id != hp->id); - - if (len < HFIXEDSZ) { - h_errno = NETDB_INTERNAL; - if (len >= 0) - errno = EMSGSIZE; - else if (errno == EINTR) /* see the comment above */ - return -1; - continue; - } - - hp->ancount = ntohs(hp->ancount); - hp->qdcount = ntohs(hp->qdcount); - if ((hp->rcode != NOERROR) || (hp->ancount == 0)) { - switch (hp->rcode) { - case NXDOMAIN: - h_errno = HOST_NOT_FOUND; - break; - case SERVFAIL: - h_errno = TRY_AGAIN; - break; - case NOERROR: - h_errno = NO_DATA; - break; - case FORMERR: - case NOTIMP: - case REFUSED: - default: - h_errno = NO_RECOVERY; - } - continue; - } - - if (parse_answer(&qbuf, len, addr) == 0) - return 0; - } - - return -1; -} - - -#define CLOSE_AND_RETURN(ret) \ - { \ - n = errno; \ - st_netfd_close(nfd); \ - errno = n; \ - return (ret); \ - } - - -int dns_getaddr(const char *host, struct in_addr *addr, st_utime_t timeout) -{ - char name[MAXDNAME], **domain; - const char *cp; - int s, n, maxlen, dots; - int trailing_dot, tried_as_is; - st_netfd_t nfd; - - if ((_res.options & RES_INIT) == 0 && res_init() == -1) { - h_errno = NETDB_INTERNAL; - return -1; - } - if (_res.options & RES_USEVC) { - h_errno = NETDB_INTERNAL; - errno = ENOSYS; - return -1; - } - if (!host || *host == '\0') { - h_errno = HOST_NOT_FOUND; - return -1; - } - - /* Create UDP socket */ - if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { - h_errno = NETDB_INTERNAL; - return -1; - } - if ((nfd = st_netfd_open_socket(s)) == NULL) { - h_errno = NETDB_INTERNAL; - n = errno; - close(s); - errno = n; - return -1; - } - - maxlen = sizeof(name) - 1; - n = 0; - dots = 0; - trailing_dot = 0; - tried_as_is = 0; - - for (cp = host; *cp && n < maxlen; cp++) { - dots += (*cp == '.'); - name[n++] = *cp; - } - if (name[n - 1] == '.') - trailing_dot = 1; - - /* - * If there are dots in the name already, let's just give it a try - * 'as is'. The threshold can be set with the "ndots" option. - */ - if (dots >= _res.ndots) { - if (query_domain(nfd, host, addr, timeout) == 0) - CLOSE_AND_RETURN(0); - if (h_errno == NETDB_INTERNAL && errno == EINTR) - CLOSE_AND_RETURN(-1); - tried_as_is = 1; - } - - /* - * We do at least one level of search if - * - there is no dot and RES_DEFNAME is set, or - * - there is at least one dot, there is no trailing dot, - * and RES_DNSRCH is set. - */ - if ((!dots && (_res.options & RES_DEFNAMES)) || - (dots && !trailing_dot && (_res.options & RES_DNSRCH))) { - name[n++] = '.'; - for (domain = _res.dnsrch; *domain; domain++) { - strncpy(name + n, *domain, maxlen - n); - if (query_domain(nfd, name, addr, timeout) == 0) - CLOSE_AND_RETURN(0); - if (h_errno == NETDB_INTERNAL && errno == EINTR) - CLOSE_AND_RETURN(-1); - if (!(_res.options & RES_DNSRCH)) - break; - } - } - - /* - * If we have not already tried the name "as is", do that now. - * note that we do this regardless of how many dots were in the - * name or whether it ends with a dot. - */ - if (!tried_as_is) { - if (query_domain(nfd, host, addr, timeout) == 0) - CLOSE_AND_RETURN(0); - } - - CLOSE_AND_RETURN(-1); -} - diff --git a/trunk/research/st-1.9/examples/server.c b/trunk/research/st-1.9/examples/server.c deleted file mode 100644 index 5d5aa6d726..0000000000 --- a/trunk/research/st-1.9/examples/server.c +++ /dev/null @@ -1,1025 +0,0 @@ -/* - * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. - * All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Silicon Graphics, Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "st.h" - - -/****************************************************************** - * Server configuration parameters - */ - -/* Log files */ -#define PID_FILE "pid" -#define ERRORS_FILE "errors" -#define ACCESS_FILE "access" - -/* Default server port */ -#define SERV_PORT_DEFAULT 8000 - -/* Socket listen queue size */ -#define LISTENQ_SIZE_DEFAULT 256 - -/* Max number of listening sockets ("hardware virtual servers") */ -#define MAX_BIND_ADDRS 16 - -/* Max number of "spare" threads per process per socket */ -#define MAX_WAIT_THREADS_DEFAULT 8 - -/* Number of file descriptors needed to handle one client session */ -#define FD_PER_THREAD 2 - -/* Access log buffer flushing interval (in seconds) */ -#define ACCLOG_FLUSH_INTERVAL 30 - -/* Request read timeout (in seconds) */ -#define REQUEST_TIMEOUT 30 - - -/****************************************************************** - * Global data - */ - -struct socket_info { - st_netfd_t nfd; /* Listening socket */ - char *addr; /* Bind address */ - unsigned int port; /* Port */ - int wait_threads; /* Number of threads waiting to accept */ - int busy_threads; /* Number of threads processing request */ - int rqst_count; /* Total number of processed requests */ -} srv_socket[MAX_BIND_ADDRS]; /* Array of listening sockets */ - -static int sk_count = 0; /* Number of listening sockets */ - -static int vp_count = 0; /* Number of server processes (VPs) */ -static pid_t *vp_pids; /* Array of VP pids */ - -static int my_index = -1; /* Current process index */ -static pid_t my_pid = -1; /* Current process pid */ - -static st_netfd_t sig_pipe[2]; /* Signal pipe */ - -/* - * Configuration flags/parameters - */ -static int interactive_mode = 0; -static int serialize_accept = 0; -static int log_access = 0; -static char *logdir = NULL; -static char *username = NULL; -static int listenq_size = LISTENQ_SIZE_DEFAULT; -static int errfd = STDERR_FILENO; - -/* - * Thread throttling parameters (all numbers are per listening socket). - * Zero values mean use default. - */ -static int max_threads = 0; /* Max number of threads */ -static int max_wait_threads = 0; /* Max number of "spare" threads */ -static int min_wait_threads = 2; /* Min number of "spare" threads */ - - -/****************************************************************** - * Useful macros - */ - -#ifndef INADDR_NONE -#define INADDR_NONE 0xffffffff -#endif - -#define SEC2USEC(s) ((s)*1000000LL) - -#define WAIT_THREADS(i) (srv_socket[i].wait_threads) -#define BUSY_THREADS(i) (srv_socket[i].busy_threads) -#define TOTAL_THREADS(i) (WAIT_THREADS(i) + BUSY_THREADS(i)) -#define RQST_COUNT(i) (srv_socket[i].rqst_count) - - -/****************************************************************** - * Forward declarations - */ - -static void usage(const char *progname); -static void parse_arguments(int argc, char *argv[]); -static void start_daemon(void); -static void set_thread_throttling(void); -static void create_listeners(void); -static void change_user(void); -static void open_log_files(void); -static void start_processes(void); -static void wdog_sighandler(int signo); -static void child_sighandler(int signo); -static void install_sighandlers(void); -static void start_threads(void); -static void *process_signals(void *arg); -static void *flush_acclog_buffer(void *arg); -static void *handle_connections(void *arg); -static void dump_server_info(void); - -static void Signal(int sig, void (*handler)(int)); -static int cpu_count(void); - -extern void handle_session(long srv_socket_index, st_netfd_t cli_nfd); -extern void load_configs(void); -extern void logbuf_open(void); -extern void logbuf_flush(void); -extern void logbuf_close(void); - -/* Error reporting functions defined in the error.c file */ -extern void err_sys_report(int fd, const char *fmt, ...); -extern void err_sys_quit(int fd, const char *fmt, ...); -extern void err_sys_dump(int fd, const char *fmt, ...); -extern void err_report(int fd, const char *fmt, ...); -extern void err_quit(int fd, const char *fmt, ...); - - -/* - * General server example: accept a client connection and do something. - * This program just outputs a short HTML page, but can be easily adapted - * to do other things. - * - * This server creates a constant number of processes ("virtual processors" - * or VPs) and replaces them when they die. Each virtual processor manages - * its own independent set of state threads (STs), the number of which varies - * with load against the server. Each state thread listens to exactly one - * listening socket. The initial process becomes the watchdog, waiting for - * children (VPs) to die or for a signal requesting termination or restart. - * Upon receiving a restart signal (SIGHUP), all VPs close and then reopen - * log files and reload configuration. All currently active connections remain - * active. It is assumed that new configuration affects only request - * processing and not the general server parameters such as number of VPs, - * thread limits, bind addresses, etc. Those are specified as command line - * arguments, so the server has to be stopped and then started again in order - * to change them. - * - * Each state thread loops processing connections from a single listening - * socket. Only one ST runs on a VP at a time, and VPs do not share memory, - * so no mutual exclusion locking is necessary on any data, and the entire - * server is free to use all the static variables and non-reentrant library - * functions it wants, greatly simplifying programming and debugging and - * increasing performance (for example, it is safe to ++ and -- all global - * counters or call inet_ntoa(3) without any mutexes). The current thread on - * each VP maintains equilibrium on that VP, starting a new thread or - * terminating itself if the number of spare threads exceeds the lower or - * upper limit. - * - * All I/O operations on sockets must use the State Thread library's I/O - * functions because only those functions prevent blocking of the entire VP - * process and perform state thread scheduling. - */ -int main(int argc, char *argv[]) -{ - /* Parse command-line options */ - parse_arguments(argc, argv); - - /* Allocate array of server pids */ - if ((vp_pids = calloc(vp_count, sizeof(pid_t))) == NULL) - err_sys_quit(errfd, "ERROR: calloc failed"); - - /* Start the daemon */ - if (!interactive_mode) - start_daemon(); - - /* Initialize the ST library */ - if (st_init() < 0) - err_sys_quit(errfd, "ERROR: initialization failed: st_init"); - - /* Set thread throttling parameters */ - set_thread_throttling(); - - /* Create listening sockets */ - create_listeners(); - - /* Change the user */ - if (username) - change_user(); - - /* Open log files */ - open_log_files(); - - /* Start server processes (VPs) */ - start_processes(); - - /* Turn time caching on */ - st_timecache_set(1); - - /* Install signal handlers */ - install_sighandlers(); - - /* Load configuration from config files */ - load_configs(); - - /* Start all threads */ - start_threads(); - - /* Become a signal processing thread */ - process_signals(NULL); - - /* NOTREACHED */ - return 1; -} - - -/******************************************************************/ - -static void usage(const char *progname) -{ - fprintf(stderr, "Usage: %s -l []\n\n" - "Possible options:\n\n" - "\t-b : Bind to specified address. Multiple" - " addresses\n" - "\t are permitted.\n" - "\t-p Create specified number of processes.\n" - "\t-t : Specify thread limits per listening" - " socket\n" - "\t across all processes.\n" - "\t-u Change server's user id to specified" - " value.\n" - "\t-q Set max length of pending connections" - " queue.\n" - "\t-a Enable access logging.\n" - "\t-i Run in interactive mode.\n" - "\t-S Serialize all accept() calls.\n" - "\t-h Print this message.\n", - progname); - exit(1); -} - - -/******************************************************************/ - -static void parse_arguments(int argc, char *argv[]) -{ - extern char *optarg; - int opt; - char *c; - - while ((opt = getopt(argc, argv, "b:p:l:t:u:q:aiSh")) != EOF) { - switch (opt) { - case 'b': - if (sk_count >= MAX_BIND_ADDRS) - err_quit(errfd, "ERROR: max number of bind addresses (%d) exceeded", - MAX_BIND_ADDRS); - if ((c = strdup(optarg)) == NULL) - err_sys_quit(errfd, "ERROR: strdup"); - srv_socket[sk_count++].addr = c; - break; - case 'p': - vp_count = atoi(optarg); - if (vp_count < 1) - err_quit(errfd, "ERROR: invalid number of processes: %s", optarg); - break; - case 'l': - logdir = optarg; - break; - case 't': - max_wait_threads = (int) strtol(optarg, &c, 10); - if (*c++ == ':') - max_threads = atoi(c); - if (max_wait_threads < 0 || max_threads < 0) - err_quit(errfd, "ERROR: invalid number of threads: %s", optarg); - break; - case 'u': - username = optarg; - break; - case 'q': - listenq_size = atoi(optarg); - if (listenq_size < 1) - err_quit(errfd, "ERROR: invalid listen queue size: %s", optarg); - break; - case 'a': - log_access = 1; - break; - case 'i': - interactive_mode = 1; - break; - case 'S': - /* - * Serialization decision is tricky on some platforms. For example, - * Solaris 2.6 and above has kernel sockets implementation, so supposedly - * there is no need for serialization. The ST library may be compiled - * on one OS version, but used on another, so the need for serialization - * should be determined at run time by the application. Since it's just - * an example, the serialization decision is left up to user. - * Only on platforms where the serialization is never needed on any OS - * version st_netfd_serialize_accept() is a no-op. - */ - serialize_accept = 1; - break; - case 'h': - case '?': - usage(argv[0]); - } - } - - if (logdir == NULL && !interactive_mode) { - err_report(errfd, "ERROR: logging directory is required\n"); - usage(argv[0]); - } - - if (getuid() == 0 && username == NULL) - err_report(errfd, "WARNING: running as super-user!"); - - if (vp_count == 0 && (vp_count = cpu_count()) < 1) - vp_count = 1; - - if (sk_count == 0) { - sk_count = 1; - srv_socket[0].addr = "0.0.0.0"; - } -} - - -/******************************************************************/ - -static void start_daemon(void) -{ - pid_t pid; - - /* Start forking */ - if ((pid = fork()) < 0) - err_sys_quit(errfd, "ERROR: fork"); - if (pid > 0) - exit(0); /* parent */ - - /* First child process */ - setsid(); /* become session leader */ - - if ((pid = fork()) < 0) - err_sys_quit(errfd, "ERROR: fork"); - if (pid > 0) /* first child */ - exit(0); - - umask(022); - - if (chdir(logdir) < 0) - err_sys_quit(errfd, "ERROR: can't change directory to %s: chdir", logdir); -} - - -/****************************************************************** - * For simplicity, the minimal size of thread pool is considered - * as a maximum number of spare threads (max_wait_threads) that - * will be created upon server startup. The pool size can grow up - * to the max_threads value. Note that this is a per listening - * socket limit. It is also possible to limit the total number of - * threads for all sockets rather than impose a per socket limit. - */ - -static void set_thread_throttling(void) -{ - /* - * Calculate total values across all processes. - * All numbers are per listening socket. - */ - if (max_wait_threads == 0) - max_wait_threads = MAX_WAIT_THREADS_DEFAULT * vp_count; - /* Assuming that each client session needs FD_PER_THREAD file descriptors */ - if (max_threads == 0) - max_threads = (st_getfdlimit() * vp_count) / FD_PER_THREAD / sk_count; - if (max_wait_threads > max_threads) - max_wait_threads = max_threads; - - /* - * Now calculate per-process values. - */ - if (max_wait_threads % vp_count) - max_wait_threads = max_wait_threads / vp_count + 1; - else - max_wait_threads = max_wait_threads / vp_count; - if (max_threads % vp_count) - max_threads = max_threads / vp_count + 1; - else - max_threads = max_threads / vp_count; - - if (min_wait_threads > max_wait_threads) - min_wait_threads = max_wait_threads; -} - - -/******************************************************************/ - -static void create_listeners(void) -{ - int i, n, sock; - char *c; - struct sockaddr_in serv_addr; - struct hostent *hp; - unsigned short port; - - for (i = 0; i < sk_count; i++) { - port = 0; - if ((c = strchr(srv_socket[i].addr, ':')) != NULL) { - *c++ = '\0'; - port = (unsigned short) atoi(c); - } - if (srv_socket[i].addr[0] == '\0') - srv_socket[i].addr = "0.0.0.0"; - if (port == 0) - port = SERV_PORT_DEFAULT; - - /* Create server socket */ - if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) - err_sys_quit(errfd, "ERROR: can't create socket: socket"); - n = 1; - if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&n, sizeof(n)) < 0) - err_sys_quit(errfd, "ERROR: can't set SO_REUSEADDR: setsockopt"); - memset(&serv_addr, 0, sizeof(serv_addr)); - serv_addr.sin_family = AF_INET; - serv_addr.sin_port = htons(port); - serv_addr.sin_addr.s_addr = inet_addr(srv_socket[i].addr); - if (serv_addr.sin_addr.s_addr == INADDR_NONE) { - /* not dotted-decimal */ - if ((hp = gethostbyname(srv_socket[i].addr)) == NULL) - err_quit(errfd, "ERROR: can't resolve address: %s", - srv_socket[i].addr); - memcpy(&serv_addr.sin_addr, hp->h_addr, hp->h_length); - } - srv_socket[i].port = port; - - /* Do bind and listen */ - if (bind(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) - err_sys_quit(errfd, "ERROR: can't bind to address %s, port %hu", - srv_socket[i].addr, port); - if (listen(sock, listenq_size) < 0) - err_sys_quit(errfd, "ERROR: listen"); - - /* Create file descriptor object from OS socket */ - if ((srv_socket[i].nfd = st_netfd_open_socket(sock)) == NULL) - err_sys_quit(errfd, "ERROR: st_netfd_open_socket"); - /* - * On some platforms (e.g. IRIX, Linux) accept() serialization is never - * needed for any OS version. In that case st_netfd_serialize_accept() - * is just a no-op. Also see the comment above. - */ - if (serialize_accept && st_netfd_serialize_accept(srv_socket[i].nfd) < 0) - err_sys_quit(errfd, "ERROR: st_netfd_serialize_accept"); - } -} - - -/******************************************************************/ - -static void change_user(void) -{ - struct passwd *pw; - - if ((pw = getpwnam(username)) == NULL) - err_quit(errfd, "ERROR: can't find user '%s': getpwnam failed", username); - - if (setgid(pw->pw_gid) < 0) - err_sys_quit(errfd, "ERROR: can't change group id: setgid"); - if (setuid(pw->pw_uid) < 0) - err_sys_quit(errfd, "ERROR: can't change user id: setuid"); - - err_report(errfd, "INFO: changed process user id to '%s'", username); -} - - -/******************************************************************/ - -static void open_log_files(void) -{ - int fd; - char str[32]; - - if (interactive_mode) - return; - - /* Open access log */ - if (log_access) - logbuf_open(); - - /* Open and write pid to pid file */ - if ((fd = open(PID_FILE, O_CREAT | O_WRONLY | O_TRUNC, 0644)) < 0) - err_sys_quit(errfd, "ERROR: can't open pid file: open"); - sprintf(str, "%d\n", (int)getpid()); - if (write(fd, str, strlen(str)) != strlen(str)) - err_sys_quit(errfd, "ERROR: can't write to pid file: write"); - close(fd); - - /* Open error log file */ - if ((fd = open(ERRORS_FILE, O_CREAT | O_WRONLY | O_APPEND, 0644)) < 0) - err_sys_quit(errfd, "ERROR: can't open error log file: open"); - errfd = fd; - - err_report(errfd, "INFO: starting the server..."); -} - - -/******************************************************************/ - -static void start_processes(void) -{ - int i, status; - pid_t pid; - sigset_t mask, omask; - - if (interactive_mode) { - my_index = 0; - my_pid = getpid(); - return; - } - - for (i = 0; i < vp_count; i++) { - if ((pid = fork()) < 0) { - err_sys_report(errfd, "ERROR: can't create process: fork"); - if (i == 0) - exit(1); - err_report(errfd, "WARN: started only %d processes out of %d", i, - vp_count); - vp_count = i; - break; - } - if (pid == 0) { - my_index = i; - my_pid = getpid(); - /* Child returns to continue in main() */ - return; - } - vp_pids[i] = pid; - } - - /* - * Parent process becomes a "watchdog" and never returns to main(). - */ - - /* Install signal handlers */ - Signal(SIGTERM, wdog_sighandler); /* terminate */ - Signal(SIGHUP, wdog_sighandler); /* restart */ - Signal(SIGUSR1, wdog_sighandler); /* dump info */ - - /* Now go to sleep waiting for a child termination or a signal */ - for ( ; ; ) { - if ((pid = wait(&status)) < 0) { - if (errno == EINTR) - continue; - err_sys_quit(errfd, "ERROR: watchdog: wait"); - } - /* Find index of the exited child */ - for (i = 0; i < vp_count; i++) { - if (vp_pids[i] == pid) - break; - } - - /* Block signals while printing and forking */ - sigemptyset(&mask); - sigaddset(&mask, SIGTERM); - sigaddset(&mask, SIGHUP); - sigaddset(&mask, SIGUSR1); - sigprocmask(SIG_BLOCK, &mask, &omask); - - if (WIFEXITED(status)) - err_report(errfd, "WARN: watchdog: process %d (pid %d) exited" - " with status %d", i, pid, WEXITSTATUS(status)); - else if (WIFSIGNALED(status)) - err_report(errfd, "WARN: watchdog: process %d (pid %d) terminated" - " by signal %d", i, pid, WTERMSIG(status)); - else if (WIFSTOPPED(status)) - err_report(errfd, "WARN: watchdog: process %d (pid %d) stopped" - " by signal %d", i, pid, WSTOPSIG(status)); - else - err_report(errfd, "WARN: watchdog: process %d (pid %d) terminated:" - " unknown termination reason", i, pid); - - /* Fork another VP */ - if ((pid = fork()) < 0) { - err_sys_report(errfd, "ERROR: watchdog: can't create process: fork"); - } else if (pid == 0) { - my_index = i; - my_pid = getpid(); - /* Child returns to continue in main() */ - return; - } - vp_pids[i] = pid; - - /* Restore the signal mask */ - sigprocmask(SIG_SETMASK, &omask, NULL); - } -} - - -/******************************************************************/ - -static void wdog_sighandler(int signo) -{ - int i, err; - - /* Save errno */ - err = errno; - /* Forward the signal to all children */ - for (i = 0; i < vp_count; i++) { - if (vp_pids[i] > 0) - kill(vp_pids[i], signo); - } - /* - * It is safe to do pretty much everything here because process is - * sleeping in wait() which is async-safe. - */ - switch (signo) { - case SIGHUP: - err_report(errfd, "INFO: watchdog: caught SIGHUP"); - /* Reopen log files - needed for log rotation */ - if (log_access) { - logbuf_close(); - logbuf_open(); - } - close(errfd); - if ((errfd = open(ERRORS_FILE, O_CREAT | O_WRONLY | O_APPEND, 0644)) < 0) - err_sys_quit(STDERR_FILENO, "ERROR: watchdog: open"); - break; - case SIGTERM: - /* Non-graceful termination */ - err_report(errfd, "INFO: watchdog: caught SIGTERM, terminating"); - unlink(PID_FILE); - exit(0); - case SIGUSR1: - err_report(errfd, "INFO: watchdog: caught SIGUSR1"); - break; - default: - err_report(errfd, "INFO: watchdog: caught signal %d", signo); - } - /* Restore errno */ - errno = err; -} - - -/******************************************************************/ - -static void install_sighandlers(void) -{ - sigset_t mask; - int p[2]; - - /* Create signal pipe */ - if (pipe(p) < 0) - err_sys_quit(errfd, "ERROR: process %d (pid %d): can't create" - " signal pipe: pipe", my_index, my_pid); - if ((sig_pipe[0] = st_netfd_open(p[0])) == NULL || - (sig_pipe[1] = st_netfd_open(p[1])) == NULL) - err_sys_quit(errfd, "ERROR: process %d (pid %d): can't create" - " signal pipe: st_netfd_open", my_index, my_pid); - - /* Install signal handlers */ - Signal(SIGTERM, child_sighandler); /* terminate */ - Signal(SIGHUP, child_sighandler); /* restart */ - Signal(SIGUSR1, child_sighandler); /* dump info */ - - /* Unblock signals */ - sigemptyset(&mask); - sigaddset(&mask, SIGTERM); - sigaddset(&mask, SIGHUP); - sigaddset(&mask, SIGUSR1); - sigprocmask(SIG_UNBLOCK, &mask, NULL); -} - - -/******************************************************************/ - -static void child_sighandler(int signo) -{ - int err, fd; - - err = errno; - fd = st_netfd_fileno(sig_pipe[1]); - - /* write() is async-safe */ - if (write(fd, &signo, sizeof(int)) != sizeof(int)) - err_sys_quit(errfd, "ERROR: process %d (pid %d): child's signal" - " handler: write", my_index, my_pid); - errno = err; -} - - -/****************************************************************** - * The "main" function of the signal processing thread. - */ - -/* ARGSUSED */ -static void *process_signals(void *arg) -{ - int signo; - - for ( ; ; ) { - /* Read the next signal from the signal pipe */ - if (st_read(sig_pipe[0], &signo, sizeof(int), - ST_UTIME_NO_TIMEOUT) != sizeof(int)) - err_sys_quit(errfd, "ERROR: process %d (pid %d): signal processor:" - " st_read", my_index, my_pid); - - switch (signo) { - case SIGHUP: - err_report(errfd, "INFO: process %d (pid %d): caught SIGHUP," - " reloading configuration", my_index, my_pid); - if (interactive_mode) { - load_configs(); - break; - } - /* Reopen log files - needed for log rotation */ - if (log_access) { - logbuf_flush(); - logbuf_close(); - logbuf_open(); - } - close(errfd); - if ((errfd = open(ERRORS_FILE, O_CREAT | O_WRONLY | O_APPEND, 0644)) < 0) - err_sys_quit(STDERR_FILENO, "ERROR: process %d (pid %d): signal" - " processor: open", my_index, my_pid); - /* Reload configuration */ - load_configs(); - break; - case SIGTERM: - /* - * Terminate ungracefully since it is generally not known how long - * it will take to gracefully complete all client sessions. - */ - err_report(errfd, "INFO: process %d (pid %d): caught SIGTERM," - " terminating", my_index, my_pid); - if (log_access) - logbuf_flush(); - exit(0); - case SIGUSR1: - err_report(errfd, "INFO: process %d (pid %d): caught SIGUSR1", - my_index, my_pid); - /* Print server info to stderr */ - dump_server_info(); - break; - default: - err_report(errfd, "INFO: process %d (pid %d): caught signal %d", - my_index, my_pid, signo); - } - } - - /* NOTREACHED */ - return NULL; -} - - -/****************************************************************** - * The "main" function of the access log flushing thread. - */ - -/* ARGSUSED */ -static void *flush_acclog_buffer(void *arg) -{ - for ( ; ; ) { - st_sleep(ACCLOG_FLUSH_INTERVAL); - logbuf_flush(); - } - - /* NOTREACHED */ - return NULL; -} - - -/******************************************************************/ - -static void start_threads(void) -{ - long i, n; - - /* Create access log flushing thread */ - if (log_access && st_thread_create(flush_acclog_buffer, NULL, 0, 0) == NULL) - err_sys_quit(errfd, "ERROR: process %d (pid %d): can't create" - " log flushing thread", my_index, my_pid); - - /* Create connections handling threads */ - for (i = 0; i < sk_count; i++) { - err_report(errfd, "INFO: process %d (pid %d): starting %d threads" - " on %s:%u", my_index, my_pid, max_wait_threads, - srv_socket[i].addr, srv_socket[i].port); - WAIT_THREADS(i) = 0; - BUSY_THREADS(i) = 0; - RQST_COUNT(i) = 0; - for (n = 0; n < max_wait_threads; n++) { - if (st_thread_create(handle_connections, (void *)i, 0, 0) != NULL) - WAIT_THREADS(i)++; - else - err_sys_report(errfd, "ERROR: process %d (pid %d): can't create" - " thread", my_index, my_pid); - } - if (WAIT_THREADS(i) == 0) - exit(1); - } -} - - -/******************************************************************/ - -static void *handle_connections(void *arg) -{ - st_netfd_t srv_nfd, cli_nfd; - struct sockaddr_in from; - int fromlen; - long i = (long) arg; - - srv_nfd = srv_socket[i].nfd; - fromlen = sizeof(from); - - while (WAIT_THREADS(i) <= max_wait_threads) { - cli_nfd = st_accept(srv_nfd, (struct sockaddr *)&from, &fromlen, - ST_UTIME_NO_TIMEOUT); - if (cli_nfd == NULL) { - err_sys_report(errfd, "ERROR: can't accept connection: st_accept"); - continue; - } - /* Save peer address, so we can retrieve it later */ - st_netfd_setspecific(cli_nfd, &from.sin_addr, NULL); - - WAIT_THREADS(i)--; - BUSY_THREADS(i)++; - if (WAIT_THREADS(i) < min_wait_threads && TOTAL_THREADS(i) < max_threads) { - /* Create another spare thread */ - if (st_thread_create(handle_connections, (void *)i, 0, 0) != NULL) - WAIT_THREADS(i)++; - else - err_sys_report(errfd, "ERROR: process %d (pid %d): can't create" - " thread", my_index, my_pid); - } - - handle_session(i, cli_nfd); - - st_netfd_close(cli_nfd); - WAIT_THREADS(i)++; - BUSY_THREADS(i)--; - } - - WAIT_THREADS(i)--; - return NULL; -} - - -/******************************************************************/ - -static void dump_server_info(void) -{ - char *buf; - int i, len; - - if ((buf = malloc(sk_count * 512)) == NULL) { - err_sys_report(errfd, "ERROR: malloc failed"); - return; - } - - len = sprintf(buf, "\n\nProcess #%d (pid %d):\n", my_index, (int)my_pid); - for (i = 0; i < sk_count; i++) { - len += sprintf(buf + len, "\nListening Socket #%d:\n" - "-------------------------\n" - "Address %s:%u\n" - "Thread limits (min/max) %d/%d\n" - "Waiting threads %d\n" - "Busy threads %d\n" - "Requests served %d\n", - i, srv_socket[i].addr, srv_socket[i].port, - max_wait_threads, max_threads, - WAIT_THREADS(i), BUSY_THREADS(i), RQST_COUNT(i)); - } - - write(STDERR_FILENO, buf, len); - free(buf); -} - - -/****************************************************************** - * Stubs - */ - -/* - * Session handling function stub. Just dumps small HTML page. - */ -void handle_session(long srv_socket_index, st_netfd_t cli_nfd) -{ - static char resp[] = "HTTP/1.0 200 OK\r\nContent-type: text/html\r\n" - "Connection: close\r\n\r\n

It worked!

\n"; - char buf[512]; - int n = sizeof(resp) - 1; - struct in_addr *from = st_netfd_getspecific(cli_nfd); - - if (st_read(cli_nfd, buf, sizeof(buf), SEC2USEC(REQUEST_TIMEOUT)) < 0) { - err_sys_report(errfd, "WARN: can't read request from %s: st_read", - inet_ntoa(*from)); - return; - } - if (st_write(cli_nfd, resp, n, ST_UTIME_NO_TIMEOUT) != n) { - err_sys_report(errfd, "WARN: can't write response to %s: st_write", - inet_ntoa(*from)); - return; - } - - RQST_COUNT(srv_socket_index)++; -} - - -/* - * Configuration loading function stub. - */ -void load_configs(void) -{ - err_report(errfd, "INFO: process %d (pid %d): configuration loaded", - my_index, my_pid); -} - - -/* - * Buffered access logging methods. - * Note that stdio functions (fopen(3), fprintf(3), fflush(3), etc.) cannot - * be used if multiple VPs are created since these functions can flush buffer - * at any point and thus write only partial log record to disk. - * Also, it is completely safe for all threads of the same VP to write to - * the same log buffer without any mutex protection (one buffer per VP, of - * course). - */ -void logbuf_open(void) -{ - -} - - -void logbuf_flush(void) -{ - -} - - -void logbuf_close(void) -{ - -} - - -/****************************************************************** - * Small utility functions - */ - -static void Signal(int sig, void (*handler)(int)) -{ - struct sigaction sa; - - sa.sa_handler = handler; - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - sigaction(sig, &sa, NULL); -} - -static int cpu_count(void) -{ - int n; - -#if defined (_SC_NPROCESSORS_ONLN) - n = (int) sysconf(_SC_NPROCESSORS_ONLN); -#elif defined (_SC_NPROC_ONLN) - n = (int) sysconf(_SC_NPROC_ONLN); -#elif defined (HPUX) -#include - n = mpctl(MPC_GETNUMSPUS, 0, 0); -#else - n = -1; - errno = ENOSYS; -#endif - - return n; -} - -/******************************************************************/ - diff --git a/trunk/research/st-1.9/extensions/Makefile b/trunk/research/st-1.9/extensions/Makefile deleted file mode 100644 index fc6634f93f..0000000000 --- a/trunk/research/st-1.9/extensions/Makefile +++ /dev/null @@ -1,91 +0,0 @@ -# -# Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. -# All Rights Reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of Silicon Graphics, Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -CC = cc - -SHELL = /bin/sh -ECHO = /bin/echo - -DEPTH = .. -BUILD = -TARGETDIR = obj - -DEFINES = -OTHER_FLAGS = -CFLAGS = - -OBJDIR = $(DEPTH)/$(TARGETDIR) -INCDIR = $(DEPTH)/$(TARGETDIR) - -LIBRESOLV = -EXTRALIBS = - -SLIBRARY = $(OBJDIR)/libstx.a -OBJS = $(OBJDIR)/dnscache.o $(OBJDIR)/dnsres.o $(OBJDIR)/lrucache.o - - -CFLAGS += -Wall -I$(INCDIR) -AR = ar -ARFLAGS = rv -RANLIB = ranlib - - -########################## -# Platform section. -# - -ifeq (LINUX, $(findstring LINUX, $(OS))) -LIBRESOLV = -lresolv -endif - -ifeq ($(OS), SOLARIS) -LIBRESOLV = -lresolv -EXTRALIBS = -lsocket -lnsl -endif - -# -# End of platform section. -########################## - - -all: $(SLIBRARY) - -$(SLIBRARY): $(OBJS) - $(AR) $(ARFLAGS) $@ $(OBJS) - $(RANLIB) $@ - -$(OBJDIR)/%.o: %.c stx.h common.h - $(CC) $(CFLAGS) -c $< -o $@ - -clean: - rm -rf $(OBJS) $(SLIBRARY) - -#.DEFAULT: -# @cd $(DEPTH); $(MAKE) $@ - diff --git a/trunk/research/st-1.9/extensions/README b/trunk/research/st-1.9/extensions/README deleted file mode 100644 index f768aa7125..0000000000 --- a/trunk/research/st-1.9/extensions/README +++ /dev/null @@ -1,42 +0,0 @@ -This directory contains extensions to the core State Threads Library -that were contributed by users. All files hereunder are not part of the -State Threads Library itself. They are provided as-is, without warranty -or support, and under whatever license terms their authors provided. To -contribute your own extensions, just mail them to the project -administrators or to one of the project's mailing lists; see -state-threads.sourceforge.net. Please indicate the license terms under -which the project may distribute your contribution. - -======================================================================== - -stx_fileio ----------- -Contributed by Jeff , 4 Nov 2002. - -Provides non-blocking random access file reading capability for -programs using the State Threads library. There is one public function: - -ssize_t stx_file_read(st_netfd_t fd, off_t offset, - void *buf, size_t nbytes, st_utime_t timeout); - -The implementation is not optimal in that the data is copied at least once -more than should be necessary. Its usefulness is limited to cases where -random access to a file is required and where starvation of other threads -is unacceptable. - -The particular application which motivated this implementation was a UDP -file transfer protocol. Because the OS does very little buffering of UDP -traffic it is important that UDP transmission threads are not starved for -periods of time which are long relative to the interval required to -maintain a steady send rate. - -Licensed under the same dual MPL/GPL as core State Threads. - -======================================================================== - -stx_dns -------- - -Documentation coming. - -======================================================================== diff --git a/trunk/research/st-1.9/extensions/common.h b/trunk/research/st-1.9/extensions/common.h deleted file mode 100644 index f6298ba09e..0000000000 --- a/trunk/research/st-1.9/extensions/common.h +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef _STX_COMMON_H_ -#define _STX_COMMON_H_ - -#include -#include - - -#define STX_BEGIN_MACRO { -#define STX_END_MACRO } - - -/***************************************** - * Circular linked list definitions - */ - -typedef struct _stx_clist { - struct _stx_clist *next; - struct _stx_clist *prev; -} stx_clist_t; - -/* Insert element "_e" into the list, before "_l" */ -#define STX_CLIST_INSERT_BEFORE(_e,_l) \ - STX_BEGIN_MACRO \ - (_e)->next = (_l); \ - (_e)->prev = (_l)->prev; \ - (_l)->prev->next = (_e); \ - (_l)->prev = (_e); \ - STX_END_MACRO - -/* Insert element "_e" into the list, after "_l" */ -#define STX_CLIST_INSERT_AFTER(_e,_l) \ - STX_BEGIN_MACRO \ - (_e)->next = (_l)->next; \ - (_e)->prev = (_l); \ - (_l)->next->prev = (_e); \ - (_l)->next = (_e); \ - STX_END_MACRO - -/* Append an element "_e" to the end of the list "_l" */ -#define STX_CLIST_APPEND_LINK(_e,_l) STX_CLIST_INSERT_BEFORE(_e,_l) - -/* Remove the element "_e" from it's circular list */ -#define STX_CLIST_REMOVE_LINK(_e) \ - STX_BEGIN_MACRO \ - (_e)->prev->next = (_e)->next; \ - (_e)->next->prev = (_e)->prev; \ - STX_END_MACRO - -/* Return the head/tail of the list */ -#define STX_CLIST_HEAD(_l) (_l)->next -#define STX_CLIST_TAIL(_l) (_l)->prev - -/* Return non-zero if the given circular list "_l" is empty, */ -/* zero if the circular list is not empty */ -#define STX_CLIST_IS_EMPTY(_l) \ - ((_l)->next == (_l)) - -/* Initialize a circular list */ -#define STX_CLIST_INIT_CLIST(_l) \ - STX_BEGIN_MACRO \ - (_l)->next = (_l); \ - (_l)->prev = (_l); \ - STX_END_MACRO - - -/***************************************** - * Useful macros - */ - -#ifndef offsetof -#define offsetof(type, identifier) ((size_t)&(((type *)0)->identifier)) -#endif - -#define STX_MIN(a, b) (((a) < (b)) ? (a) : (b)) - -#endif /* !_STX_COMMON_H_ */ - diff --git a/trunk/research/st-1.9/extensions/dnscache.c b/trunk/research/st-1.9/extensions/dnscache.c deleted file mode 100644 index ac49166a14..0000000000 --- a/trunk/research/st-1.9/extensions/dnscache.c +++ /dev/null @@ -1,190 +0,0 @@ -#include "stx.h" -#include "common.h" - - -/***************************************** - * Basic types definitions - */ - -typedef struct _stx_dns_data { - struct in_addr *addrs; - int num_addrs; - int cur; - time_t expires; -} stx_dns_data_t; - - -#define MAX_HOST_ADDRS 1024 - -static struct in_addr addr_list[MAX_HOST_ADDRS]; - -stx_cache_t *_stx_dns_cache = NULL; - -extern int _stx_dns_ttl; -extern int _stx_dns_getaddrlist(const char *hostname, struct in_addr *addrs, - int *num_addrs, st_utime_t timeout); - - -static unsigned long hash_hostname(const void *key) -{ - const char *name = (const char *)key; - unsigned long hash = 0; - - while (*name) - hash = (hash << 4) - hash + *name++; /* hash = hash * 15 + *name++ */ - - return hash; -} - -static void cleanup_entry(void *key, void *data) -{ - if (key) - free(key); - - if (data) { - if (((stx_dns_data_t *)data)->addrs) - free(((stx_dns_data_t *)data)->addrs); - free(data); - } -} - -static int lookup_entry(const char *host, struct in_addr *addrs, - int *num_addrs, int rotate) -{ - stx_cache_entry_t *entry; - stx_dns_data_t *data; - int n; - - entry = stx_cache_entry_lookup(_stx_dns_cache, host); - if (entry) { - data = (stx_dns_data_t *)stx_cache_entry_getdata(entry); - if (st_time() <= data->expires) { - if (*num_addrs == 1) { - if (rotate) { - *addrs = data->addrs[data->cur++]; - if (data->cur >= data->num_addrs) - data->cur = 0; - } else { - *addrs = data->addrs[0]; - } - } else { - n = STX_MIN(*num_addrs, data->num_addrs); - memcpy(addrs, data->addrs, n * sizeof(*addrs)); - *num_addrs = n; - } - - stx_cache_entry_release(_stx_dns_cache, entry); - return 1; - } - - /* - * Cache entry expired: decrement its refcount and purge it from cache. - */ - stx_cache_entry_release(_stx_dns_cache, entry); - stx_cache_entry_delete(_stx_dns_cache, entry); - } - - return 0; -} - -static void insert_entry(const char *host, struct in_addr *addrs, int count) -{ - stx_cache_entry_t *entry; - stx_dns_data_t *data; - char *key; - size_t n; - - if (_stx_dns_ttl > 0) { - key = strdup(host); - data = (stx_dns_data_t *)malloc(sizeof(stx_dns_data_t)); - n = count * sizeof(*addrs); - if (data) { - data->addrs = (struct in_addr *)malloc(n); - if (data->addrs) - memcpy(data->addrs, addrs, n); - data->num_addrs = count; - data->cur = 0; - data->expires = st_time() + _stx_dns_ttl; - } - entry = stx_cache_entry_create(key, data, strlen(host) + 1 + - sizeof(stx_dns_data_t) + n + - stx_cache_entry_sizeof()); - if (key && data && data->addrs && entry && - stx_cache_entry_insert(_stx_dns_cache, entry) == 0) { - stx_cache_entry_release(_stx_dns_cache, entry); - return; - } - - if (entry) - stx_cache_entry_delete(_stx_dns_cache, entry); - else - cleanup_entry(key, data); - } -} - - - -int _stx_dns_cache_getaddrlist(const char *hostname, struct in_addr *addrs, - int *num_addrs, st_utime_t timeout, - int rotate) -{ - char host[128]; - int n, count; - - if (!_stx_dns_cache) - return _stx_dns_getaddrlist(hostname, addrs, num_addrs, timeout); - - for (n = 0; n < sizeof(host) - 1 && hostname[n]; n++) { - host[n] = tolower(hostname[n]); - } - host[n] = '\0'; - - if (lookup_entry(host, addrs, num_addrs, rotate)) - return 0; - - count = MAX_HOST_ADDRS; - if (_stx_dns_getaddrlist(host, addr_list, &count, timeout) < 0) - return -1; - n = STX_MIN(*num_addrs, count); - memcpy(addrs, addr_list, n * sizeof(*addrs)); - *num_addrs = n; - - insert_entry(host, addr_list, count); - return 0; -} - - -int stx_dns_cache_init(size_t max_size, size_t max_bytes, size_t hash_size) -{ - _stx_dns_cache = stx_cache_create(max_size, max_bytes, hash_size, - hash_hostname, - (long (*)(const void *, const void *))strcmp, - cleanup_entry); - if (!_stx_dns_cache) - return -1; - - return 0; -} - -void stx_dns_cache_getinfo(stx_cache_info_t *info) -{ - if (_stx_dns_cache) - stx_cache_getinfo(_stx_dns_cache, info); - else - memset(info, 0, sizeof(stx_cache_info_t)); -} - -int stx_dns_getaddrlist(const char *hostname, struct in_addr *addrs, - int *num_addrs, st_utime_t timeout) -{ - return _stx_dns_cache_getaddrlist(hostname, addrs, num_addrs, timeout, 0); -} - -int stx_dns_getaddr(const char *hostname, struct in_addr *addr, - st_utime_t timeout) -{ - int n = 1; - - return _stx_dns_cache_getaddrlist(hostname, addr, &n, timeout, 1); -} - diff --git a/trunk/research/st-1.9/extensions/dnsres.c b/trunk/research/st-1.9/extensions/dnsres.c deleted file mode 100644 index 04a91ccafa..0000000000 --- a/trunk/research/st-1.9/extensions/dnsres.c +++ /dev/null @@ -1,305 +0,0 @@ -/* - * Copyright (c) 1985, 1988, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. - * All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Silicon Graphics, Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "stx.h" - -#define MAXPACKET 1024 - -#if !defined(NETDB_INTERNAL) && defined(h_NETDB_INTERNAL) -#define NETDB_INTERNAL h_NETDB_INTERNAL -#endif - -/* New in Solaris 7 */ -#if !defined(_getshort) && defined(ns_get16) -#define _getshort(cp) ns_get16(cp) -#define _getlong(cp) ns_get32(cp) -#endif - -typedef union { - HEADER hdr; - u_char buf[MAXPACKET]; -} querybuf_t; - -int _stx_dns_ttl; - - -static int parse_answer(querybuf_t *ans, int len, struct in_addr *addrs, - int *num_addrs) -{ - char buf[MAXPACKET]; - HEADER *ahp; - u_char *cp, *eoa; - int type, n, i; - - ahp = &ans->hdr; - eoa = ans->buf + len; - cp = ans->buf + sizeof(HEADER); - h_errno = TRY_AGAIN; - _stx_dns_ttl = -1; - i = 0; - - while (ahp->qdcount > 0) { - ahp->qdcount--; - cp += dn_skipname(cp, eoa) + QFIXEDSZ; - } - while (ahp->ancount > 0 && cp < eoa && i < *num_addrs) { - ahp->ancount--; - if ((n = dn_expand(ans->buf, eoa, cp, buf, sizeof(buf))) < 0) - return -1; - cp += n; - if (cp + 4 + 4 + 2 >= eoa) - return -1; - type = _getshort(cp); - cp += 4; - if (type == T_A) - _stx_dns_ttl = _getlong(cp); - cp += 4; - n = _getshort(cp); - cp += 2; - if (type == T_A) { - if (n > sizeof(*addrs) || cp + n > eoa) - return -1; - memcpy(&addrs[i++], cp, n); - } - cp += n; - } - - *num_addrs = i; - return 0; -} - - -static int query_domain(st_netfd_t nfd, const char *name, - struct in_addr *addrs, int *num_addrs, - st_utime_t timeout) -{ - querybuf_t qbuf; - u_char *buf = qbuf.buf; - HEADER *hp = &qbuf.hdr; - int blen = sizeof(qbuf); - int i, len, id; - - for (i = 0; i < _res.nscount; i++) { - len = res_mkquery(QUERY, name, C_IN, T_A, NULL, 0, NULL, buf, blen); - if (len <= 0) { - h_errno = NO_RECOVERY; - return -1; - } - id = hp->id; - - if (st_sendto(nfd, buf, len, (struct sockaddr *)&(_res.nsaddr_list[i]), - sizeof(struct sockaddr), timeout) != len) { - h_errno = NETDB_INTERNAL; - /* EINTR means interrupt by other thread, NOT by a caught signal */ - if (errno == EINTR) - return -1; - continue; - } - - /* Wait for reply */ - do { - len = st_recvfrom(nfd, buf, blen, NULL, NULL, timeout); - if (len <= 0) - break; - } while (id != hp->id); - - if (len < HFIXEDSZ) { - h_errno = NETDB_INTERNAL; - if (len >= 0) - errno = EMSGSIZE; - else if (errno == EINTR) /* see the comment above */ - return -1; - continue; - } - - hp->ancount = ntohs(hp->ancount); - hp->qdcount = ntohs(hp->qdcount); - if ((hp->rcode != NOERROR) || (hp->ancount == 0)) { - switch (hp->rcode) { - case NXDOMAIN: - h_errno = HOST_NOT_FOUND; - break; - case SERVFAIL: - h_errno = TRY_AGAIN; - break; - case NOERROR: - h_errno = NO_DATA; - break; - case FORMERR: - case NOTIMP: - case REFUSED: - default: - h_errno = NO_RECOVERY; - } - continue; - } - - if (parse_answer(&qbuf, len, addrs, num_addrs) == 0) - return 0; - } - - return -1; -} - - -#define CLOSE_AND_RETURN(ret) \ - { \ - n = errno; \ - st_netfd_close(nfd); \ - errno = n; \ - return (ret); \ - } - - -int _stx_dns_getaddrlist(const char *host, struct in_addr *addrs, - int *num_addrs, st_utime_t timeout) -{ - char name[MAXDNAME], **domain; - const char *cp; - int s, n, maxlen, dots; - int trailing_dot, tried_as_is; - st_netfd_t nfd; - - if ((_res.options & RES_INIT) == 0 && res_init() == -1) { - h_errno = NETDB_INTERNAL; - return -1; - } - if (_res.options & RES_USEVC) { - h_errno = NETDB_INTERNAL; - errno = ENOSYS; - return -1; - } - if (!host || *host == '\0') { - h_errno = HOST_NOT_FOUND; - return -1; - } - - /* Create UDP socket */ - if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { - h_errno = NETDB_INTERNAL; - return -1; - } - if ((nfd = st_netfd_open_socket(s)) == NULL) { - h_errno = NETDB_INTERNAL; - n = errno; - close(s); - errno = n; - return -1; - } - - maxlen = sizeof(name) - 1; - n = 0; - dots = 0; - trailing_dot = 0; - tried_as_is = 0; - - for (cp = host; *cp && n < maxlen; cp++) { - dots += (*cp == '.'); - name[n++] = *cp; - } - if (name[n - 1] == '.') - trailing_dot = 1; - - /* - * If there are dots in the name already, let's just give it a try - * 'as is'. The threshold can be set with the "ndots" option. - */ - if (dots >= _res.ndots) { - if (query_domain(nfd, host, addrs, num_addrs, timeout) == 0) - CLOSE_AND_RETURN(0); - if (h_errno == NETDB_INTERNAL && errno == EINTR) - CLOSE_AND_RETURN(-1); - tried_as_is = 1; - } - - /* - * We do at least one level of search if - * - there is no dot and RES_DEFNAME is set, or - * - there is at least one dot, there is no trailing dot, - * and RES_DNSRCH is set. - */ - if ((!dots && (_res.options & RES_DEFNAMES)) || - (dots && !trailing_dot && (_res.options & RES_DNSRCH))) { - name[n++] = '.'; - for (domain = _res.dnsrch; *domain; domain++) { - strncpy(name + n, *domain, maxlen - n); - if (query_domain(nfd, name, addrs, num_addrs, timeout) == 0) - CLOSE_AND_RETURN(0); - if (h_errno == NETDB_INTERNAL && errno == EINTR) - CLOSE_AND_RETURN(-1); - if (!(_res.options & RES_DNSRCH)) - break; - } - } - - /* - * If we have not already tried the name "as is", do that now. - * note that we do this regardless of how many dots were in the - * name or whether it ends with a dot. - */ - if (!tried_as_is) { - if (query_domain(nfd, host, addrs, num_addrs, timeout) == 0) - CLOSE_AND_RETURN(0); - } - - CLOSE_AND_RETURN(-1); -} - diff --git a/trunk/research/st-1.9/extensions/lrucache.c b/trunk/research/st-1.9/extensions/lrucache.c deleted file mode 100644 index 33494fee62..0000000000 --- a/trunk/research/st-1.9/extensions/lrucache.c +++ /dev/null @@ -1,343 +0,0 @@ -#include "stx.h" -#include "common.h" - - -/***************************************** - * Basic types definitions - */ - -struct _stx_centry { - void *key; /* key for doing lookups */ - void *data; /* data in the cache */ - size_t weight; /* "weight" of this entry */ - struct _stx_centry *next; /* next entry */ - struct _stx_centry **pthis; - stx_clist_t lru_link; /* for putting this entry on LRU list */ - int ref_count; /* use count for this entry */ - int delete_pending; /* pending delete flag */ -}; - -struct _stx_cache { - size_t max_size; /* max size of cache */ - size_t cur_size; /* current size of cache */ - - size_t max_weight; /* cache capacity */ - size_t cur_weight; /* current total "weight" of all entries */ - - size_t hash_size; /* size of hash table */ - stx_cache_entry_t **table; /* hash table for this cache */ - - stx_clist_t lru_list; /* least-recently-used list */ - - /* Cache stats */ - unsigned long hits; /* num cache hits */ - unsigned long lookups; /* num cache lookups */ - unsigned long inserts; /* num inserts */ - unsigned long deletes; /* num deletes */ - - /* Functions */ - unsigned long (*key_hash_fn)(const void *); - long (*key_cmp_fn)(const void *, const void *); - void (*cleanup_fn)(void *, void *); -}; - - -#define STX_CACHE_ENTRY_PTR(_qp) \ - ((stx_cache_entry_t *)((char *)(_qp) - offsetof(stx_cache_entry_t, lru_link))) - - -/***************************************** - * Cache methods - */ - -stx_cache_t *stx_cache_create(size_t max_size, size_t max_weight, - size_t hash_size, - unsigned long (*key_hash_fn)(const void *key), - long (*key_cmp_fn)(const void *key1, - const void *key2), - void (*cleanup_fn)(void *key, void *data)) -{ - stx_cache_t *newcache; - - newcache = (stx_cache_t *)calloc(1, sizeof(stx_cache_t)); - if (newcache == NULL) - return NULL; - newcache->table = (stx_cache_entry_t **)calloc(hash_size, - sizeof(stx_cache_entry_t *)); - if (newcache->table == NULL) { - free(newcache); - return NULL; - } - - newcache->max_size = max_size; - newcache->max_weight = max_weight; - newcache->hash_size = hash_size; - STX_CLIST_INIT_CLIST(&(newcache->lru_list)); - newcache->key_hash_fn = key_hash_fn; - newcache->key_cmp_fn = key_cmp_fn; - newcache->cleanup_fn = cleanup_fn; - - return newcache; -} - - -void stx_cache_empty(stx_cache_t *cache) -{ - size_t i; - stx_cache_entry_t *entry, *next_entry; - - for (i = 0; i < cache->hash_size; i++) { - entry = cache->table[i]; - while (entry) { - next_entry = entry->next; - stx_cache_entry_delete(cache, entry); - entry = next_entry; - } - } -} - - -void stx_cache_traverse(stx_cache_t *cache, - void (*callback)(void *key, void *data)) -{ - size_t i; - stx_cache_entry_t *entry; - - for (i = 0; i < cache->hash_size; i++) { - for (entry = cache->table[i]; entry; entry = entry->next) { - if (!entry->delete_pending) - (*callback)(entry->key, entry->data); - } - } -} - - -void stx_cache_traverse_lru(stx_cache_t *cache, - void (*callback)(void *key, void *data), - unsigned int n) -{ - stx_clist_t *q; - stx_cache_entry_t *entry; - - for (q = STX_CLIST_HEAD(&cache->lru_list); q != &cache->lru_list && n; - q = q->next, n--) { - entry = STX_CACHE_ENTRY_PTR(q); - (*callback)(entry->key, entry->data); - } -} - - -void stx_cache_traverse_mru(stx_cache_t *cache, - void (*callback)(void *key, void *data), - unsigned int n) -{ - stx_clist_t *q; - stx_cache_entry_t *entry; - - for (q = STX_CLIST_TAIL(&cache->lru_list); q != &cache->lru_list && n; - q = q->prev, n--) { - entry = STX_CACHE_ENTRY_PTR(q); - (*callback)(entry->key, entry->data); - } -} - - -size_t stx_cache_getsize(stx_cache_t *cache) -{ - return cache->cur_size; -} - - -size_t stx_cache_getweight(stx_cache_t *cache) -{ - return cache->cur_weight; -} - - -void stx_cache_getinfo(stx_cache_t *cache, stx_cache_info_t *info) -{ - info->max_size = cache->max_size; - info->max_weight = cache->max_weight; - info->hash_size = cache->hash_size; - info->cur_size = cache->cur_size; - info->cur_weight = cache->cur_weight; - info->hits = cache->hits; - info->lookups = cache->lookups; - info->inserts = cache->inserts; - info->deletes = cache->deletes; -} - - -/***************************************** - * Cache entry methods - */ - -stx_cache_entry_t *stx_cache_entry_create(void *key, void *data, - size_t weight) -{ - stx_cache_entry_t *newentry; - - newentry = (stx_cache_entry_t *)calloc(1, sizeof(stx_cache_entry_t)); - if (newentry == NULL) - return NULL; - - newentry->key = key; - newentry->data = data; - newentry->weight = weight; - - return newentry; -} - - -void stx_cache_entry_delete(stx_cache_t *cache, stx_cache_entry_t *entry) -{ - entry->delete_pending = 1; - - if (entry->ref_count > 0) - return; - - if (entry->pthis) { - *entry->pthis = entry->next; - if (entry->next) - entry->next->pthis = entry->pthis; - - cache->cur_size--; - cache->cur_weight -= entry->weight; - cache->deletes++; - STX_CLIST_REMOVE_LINK(&(entry->lru_link)); - } - - if (cache->cleanup_fn) - cache->cleanup_fn(entry->key, entry->data); - - entry->pthis = NULL; - entry->key = NULL; - entry->data = NULL; - free(entry); -} - - -stx_cache_entry_t *stx_cache_entry_lookup(stx_cache_t *cache, const void *key) -{ - unsigned long bucket; - stx_cache_entry_t *entry; - - cache->lookups++; - bucket = cache->key_hash_fn(key) % cache->hash_size; - for (entry = cache->table[bucket]; entry; entry = entry->next) { - if (!entry->delete_pending && cache->key_cmp_fn(key, entry->key) == 0) - break; - } - if (entry) { - cache->hits++; - if (entry->ref_count == 0) - STX_CLIST_REMOVE_LINK(&(entry->lru_link)); - entry->ref_count++; - } - - return entry; -} - - -void stx_cache_entry_release(stx_cache_t *cache, stx_cache_entry_t *entry) -{ - if (entry->ref_count == 0) - return; - - entry->ref_count--; - - if (entry->ref_count == 0) { - STX_CLIST_APPEND_LINK(&(entry->lru_link), &(cache->lru_list)); - if (entry->delete_pending) - stx_cache_entry_delete(cache, entry); - } -} - - -int stx_cache_entry_insert(stx_cache_t *cache, stx_cache_entry_t *entry) -{ - stx_cache_entry_t *old_entry; - unsigned long bucket; - - /* - * If cache capacity is exceeded, try to remove LRU entries till there is - * enough room or LRU list is empty. - */ - while (cache->cur_weight + entry->weight > cache->max_weight) { - old_entry = stx_cache_entry_getlru(cache); - if (!old_entry) { - /* cache capacity is exceeded and all entries are in use */ - return -1; - } - stx_cache_entry_delete(cache, old_entry); - } - - /* If cache size is exceeded, remove LRU entry */ - if (cache->cur_size >= cache->max_size) { - old_entry = stx_cache_entry_getlru(cache); - if (!old_entry) { - /* cache size is exceeded and all entries are in use */ - return -1; - } - stx_cache_entry_delete(cache, old_entry); - } - - /* Don't add duplicate entries in the cache */ - bucket = cache->key_hash_fn(entry->key) % cache->hash_size; - for (old_entry = cache->table[bucket]; old_entry; - old_entry = old_entry->next) { - if (!old_entry->delete_pending && - cache->key_cmp_fn(entry->key, old_entry->key) == 0) - break; - } - if (old_entry) - stx_cache_entry_delete(cache, old_entry); - - /* Insert in the hash table */ - entry->next = cache->table[bucket]; - cache->table[bucket] = entry; - entry->pthis = &cache->table[bucket]; - if (entry->next) - entry->next->pthis = &entry->next; - entry->ref_count++; - - cache->inserts++; - cache->cur_size++; - cache->cur_weight += entry->weight; - - return 0; -} - - -stx_cache_entry_t *stx_cache_entry_getlru(stx_cache_t *cache) -{ - if (STX_CLIST_IS_EMPTY(&(cache->lru_list))) - return NULL; - - return STX_CACHE_ENTRY_PTR(STX_CLIST_HEAD(&(cache->lru_list))); -} - - -int stx_cache_entry_sizeof(void) -{ - return (int)sizeof(stx_cache_entry_t); -} - - -void *stx_cache_entry_getdata(stx_cache_entry_t *entry) -{ - return entry->data; -} - - -void *stx_cache_entry_getkey(stx_cache_entry_t *entry) -{ - return entry->key; -} - - -size_t stx_cache_entry_getweight(stx_cache_entry_t *entry) -{ - return entry->weight; -} - diff --git a/trunk/research/st-1.9/extensions/print_stk.patch b/trunk/research/st-1.9/extensions/print_stk.patch deleted file mode 100644 index f7451c7b07..0000000000 --- a/trunk/research/st-1.9/extensions/print_stk.patch +++ /dev/null @@ -1,367 +0,0 @@ -Michael Abd-El-Malek contributed this patch. He wrote: ----------------------------------------- -Hello, - -This is a patch that enables programmatically dumping the stack of -every thread. This has been useful in debugging deadlocks, etc... -Our usage model is that the SIGUSR2 handler calls the new -_st_print_thread_stacks function, which dumps the stack for all -threads. A convenient feature is that for thread stacks that are the -same (which is common for application with a lot of worker threads -waiting for work), only one stack trace is printed, along with a -count of how many threads have that same stack. - -I use the glibc backtrace function to get the backtrace, and then use -popen to execute addr2line and convert memory addresses to file -names, function names, and line numbers. If glibc isn't available, -_st_print_thread_stacks just prints a warning. And this feature is -only available if DEBUG is turned on. - -We've found this feature extremely helpful when debugging. - -The patch can be a bit more robust (it assumes addr2line exists). -But I didn't want to go through the hassle of doing this, if the -StateThreads community doesn't want to use this patch. (In our -environment, addr2line will always be there.) - -Cheers, -Mike ----------------------------------------- -Invoking complex functions from a signal handler is not recommended, -plus this patch changes the behavior of existing API hooks. It will -not become part of State Threads proper but you may find it useful -nonetheless. This patch applies to st-1.5.2. - -diff -Nur Makefile.1.5.2 Makefile ---- Makefile.1.5.2 Wed Sep 7 14:19:50 2005 -+++ Makefile Wed Sep 7 14:33:08 2005 -@@ -255,7 +255,8 @@ - $(TARGETDIR)/stk.o \ - $(TARGETDIR)/sync.o \ - $(TARGETDIR)/key.o \ -- $(TARGETDIR)/io.o -+ $(TARGETDIR)/io.o \ -+ $(TARGETDIR)/backtrace.o - OBJS += $(EXTRA_OBJS) - HEADER = $(TARGETDIR)/st.h - SLIBRARY = $(TARGETDIR)/libst.a -diff -Nur backtrace.c.1.5.2 backtrace.c ---- backtrace.c.1.5.2 Wed Dec 31 16:00:00 1969 -+++ backtrace.c Wed Sep 7 13:40:21 2005 -@@ -0,0 +1,211 @@ -+/* -+ * The contents of this file are subject to the Mozilla Public -+ * License Version 1.1 (the "License"); you may not use this file -+ * except in compliance with the License. You may obtain a copy of -+ * the License at http://www.mozilla.org/MPL/ -+ * -+ * Software distributed under the License is distributed on an "AS -+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or -+ * implied. See the License for the specific language governing -+ * rights and limitations under the License. -+ * -+ * Contributor(s): Michael Abd-El-Malek (mabdelmalek@cmu.edu) -+ * Carnegie Mellon University -+ * -+ * Alternatively, the contents of this file may be used under the -+ * terms of the GNU General Public License Version 2 or later (the -+ * "GPL"), in which case the provisions of the GPL are applicable -+ * instead of those above. If you wish to allow use of your -+ * version of this file only under the terms of the GPL and not to -+ * allow others to use your version of this file under the MPL, -+ * indicate your decision by deleting the provisions above and -+ * replace them with the notice and other provisions required by -+ * the GPL. If you do not delete the provisions above, a recipient -+ * may use your version of this file under either the MPL or the -+ * GPL. -+ */ -+ -+ -+ -+/* -+ * This file contains routines for printing a stack trace of all threads. -+ * Only works when DEBUG is defined and where glibc is available, since it -+ * provides the backtrace() function. -+ */ -+ -+#define _GNU_SOURCE /* to get program_invocation_name */ -+ -+#include -+#include -+ -+ -+#if defined(DEBUG) && defined(__GLIBC__) -+ -+#include -+#include "common.h" -+#include -+#include -+#include -+ -+ -+/* The maximum number of frames to get a stack trace for. If a thread has more -+ * frames than this, then we only show the latest X frames. */ -+#define MAX_NUM_FRAMES 64 -+ -+ -+typedef struct thread_stack_s { -+ uint32_t num_frames; -+ void* addresses[MAX_NUM_FRAMES]; /* frame pointers */ -+ char* locations[MAX_NUM_FRAMES]; /* file/function/line numbers */ -+ uint32_t num_matches; -+ -+ struct thread_stack_s* next; -+} thread_stack_t; -+ -+static thread_stack_t* stacks = NULL; -+ -+ -+/* Converts the function's memory addresses to function names, file names, and -+ * line numbers. Calls binutil's addr2line program. */ -+static void get_symbol_names(thread_stack_t *stack) -+{ -+ char program_to_run[1024], function[256], filename_lineno[256], temp[19]; -+ FILE* output; -+ int num_bytes_left; -+ uint32_t i; -+ -+ /* Construct the arguments to addr2line */ -+ num_bytes_left = sizeof(program_to_run); -+ num_bytes_left -= snprintf(program_to_run, sizeof(program_to_run), -+ "addr2line -fCe %s", program_invocation_name); -+ for (i = 0; i < stack->num_frames && num_bytes_left > 0; ++i) { -+ num_bytes_left -= snprintf(temp, sizeof(temp), " %p", stack->addresses[i]); -+ strncat(program_to_run, temp, num_bytes_left); -+ } -+ -+ /* Use popen to execute addr2line and read its ouput */ -+ output = popen(program_to_run, "r"); -+ for (i = 0; i < stack->num_frames; ++i) { -+ char* function_listing = (char*) malloc(512); -+ fscanf(output, "%255s\n", function); -+ fscanf(output, "%255s\n", filename_lineno); -+ snprintf(function_listing, 512, "%s at %s", function, filename_lineno); -+ stack->locations[i] = function_listing; -+ } -+ pclose(output); -+} -+ -+ -+static void print_stack(thread_stack_t* stack) -+{ -+ int skip_offset = 0, cmp_len; -+ uint32_t i; -+ -+ /* Get the function names/filenames/line numbers */ -+ get_symbol_names(stack); -+ -+ cmp_len = strlen("_st_iterate_threads_helper"); -+ -+ /* Print the backtrace */ -+ for (i = 0; i < stack->num_frames; ++i) { -+ /* Skip frames we don't have location info for */ -+ if (!strncmp(stack->locations[i], "??", 2)) { -+ continue; -+ } -+ -+ /* Skip the frames that are used for printing the stack trace */ -+ if (skip_offset) { -+ printf("\t#%2d %s %p\n", i - skip_offset, stack->locations[i], -+ stack->addresses[i]); -+ } else if (!strncmp(stack->locations[i], "_st_iterate_threads_helper", -+ cmp_len)) { -+ skip_offset = i + 1; -+ } -+ } -+} -+ -+ -+static void add_current_thread_stack(void) -+{ -+ thread_stack_t *new_stack = malloc(sizeof(thread_stack_t)); -+ thread_stack_t *search; -+ -+ /* Call glibc function to get the backtrace */ -+ new_stack->num_frames = backtrace(new_stack->addresses, MAX_NUM_FRAMES); -+ -+ /* Check if we have another stacks that is equivalent. If so, then coaelsce -+ * two stacks into one, to minimize output to user. */ -+ search = stacks; -+ while (search) { -+ if (search->num_frames == new_stack->num_frames && -+ !memcmp(search->addresses, new_stack->addresses, -+ search->num_frames * sizeof(void*))) { -+ /* Found an existing stack that is the same as this thread's stack */ -+ ++search->num_matches; -+ free(new_stack); -+ return; -+ } else { -+ search = search->next; -+ } -+ } -+ -+ /* This is a new stack. Add it to the list of stacks. */ -+ new_stack->num_matches = 1; -+ new_stack->next = stacks; -+ stacks = new_stack; -+} -+ -+static void print_stack_frames(void) -+{ -+ while (stacks) { -+ printf("\n%u thread(s) with this backtrace:\n", stacks->num_matches); -+ print_stack(stacks); -+ stacks = stacks->next; -+ } -+ printf("\n"); -+} -+ -+static void free_stacks(void) -+{ -+ uint32_t i; -+ while (stacks) { -+ thread_stack_t *next = stacks->next; -+ for (i = 0; i < stacks->num_frames; ++i) { -+ free(stacks->locations[i]); -+ } -+ free(stacks); -+ stacks = next; -+ } -+ stacks = NULL; -+} -+ -+ -+static void st_print_thread_stack(_st_thread_t *thread, int start_flag, -+ int end_flag) -+{ -+ if (end_flag == 0) { -+ add_current_thread_stack(); -+ } else { -+ print_stack_frames(); -+ } -+} -+ -+ -+void _st_print_thread_stacks(int ignore) -+{ -+ _st_iterate_threads_flag = 1; -+ _st_iterate_threads_helper(st_print_thread_stack); -+ _st_iterate_threads_flag = 0; -+ -+ /* Deallocate memory */ -+ free_stacks(); -+} -+ -+#else /* defined(DEBUG) && defined(__GLIBC__) */ -+ -+void _st_print_thread_stacks(int ignore) -+{ -+ printf("%s: need DEBUG mode and glibc-specific functions to read stack.\n", -+ __FUNCTION__); -+} -+#endif /* defined(DEBUG) && defined(__GLIBC__) */ -diff -Nur common.h.1.5.2 common.h ---- common.h.1.5.2 Wed Sep 7 14:18:37 2005 -+++ common.h Wed Sep 7 14:35:36 2005 -@@ -371,8 +371,18 @@ - */ - - #ifdef DEBUG --void _st_iterate_threads(void); --#define ST_DEBUG_ITERATE_THREADS() _st_iterate_threads() -+typedef void(*_st_func_ptr_t)(_st_thread_t *thread, -+ int start_flag, -+ int end_flag); -+/* Pointer to function that will be called on thread switch */ -+extern _st_func_ptr_t _st_iterate_func_ptr; -+extern int _st_iterate_threads_flag; -+/* Thread iteration function that will call an arbitrary function */ -+extern void _st_iterate_threads_helper(_st_func_ptr_t func); -+#define ST_DEBUG_ITERATE_THREADS() \ -+ if (_st_iterate_func_ptr) { \ -+ _st_iterate_threads_helper(_st_iterate_func_ptr); \ -+ } - #else - #define ST_DEBUG_ITERATE_THREADS() - #endif -diff -Nur public.h.1.5.2 public.h ---- public.h.1.5.2 Wed Sep 7 11:46:58 2005 -+++ public.h Wed Sep 7 13:38:46 2005 -@@ -171,8 +171,10 @@ - extern st_netfd_t st_open(const char *path, int oflags, mode_t mode); - - #ifdef DEBUG --extern void _st_show_thread_stack(st_thread_t thread, const char *messg); -+extern void _st_show_thread_stack(st_thread_t thread, int start_flag, -+ int end_flag); - extern void _st_iterate_threads(void); -+extern void _st_print_thread_stacks(int ignore); - #endif - - #ifdef __cplusplus -diff -Nur sched.c.1.5.2 sched.c ---- sched.c.1.5.2 Wed Sep 7 10:48:05 2005 -+++ sched.c Wed Sep 7 13:38:46 2005 -@@ -919,16 +919,13 @@ - - - #ifdef DEBUG --/* ARGSUSED */ --void _st_show_thread_stack(_st_thread_t *thread, const char *messg) --{ -- --} -- - /* To be set from debugger */ - int _st_iterate_threads_flag = 0; -+/* Thread iteration function that will call an arbitrary function */ -+_st_func_ptr_t _st_iterate_func_ptr = NULL; - --void _st_iterate_threads(void) -+/* This function iterates over all threads, calling "func" for each thread. */ -+void _st_iterate_threads_helper(_st_func_ptr_t func) - { - static _st_thread_t *thread = NULL; - static jmp_buf orig_jb, save_jb; -@@ -944,16 +941,20 @@ - - if (thread) { - memcpy(thread->context, save_jb, sizeof(jmp_buf)); -- _st_show_thread_stack(thread, NULL); -+ func(thread, 0, 0); - } else { - if (MD_SETJMP(orig_jb)) { - _st_iterate_threads_flag = 0; -+ _st_iterate_func_ptr = NULL; - thread = NULL; -- _st_show_thread_stack(thread, "Iteration completed"); -+ /* Last thread to iterate through */ -+ func(thread, 0, 1); - return; - } -+ /* First thread to iterate through */ - thread = _ST_CURRENT_THREAD(); -- _st_show_thread_stack(thread, "Iteration started"); -+ _st_iterate_func_ptr = func; -+ func(thread, 1, 0); - } - - q = thread->tlink.next; -@@ -966,5 +967,17 @@ - memcpy(save_jb, thread->context, sizeof(jmp_buf)); - MD_LONGJMP(thread->context, 1); - } -+ -+/* ARGSUSED */ -+void _st_show_thread_stack(_st_thread_t *thread, int start_flag, int end_flag) -+{ -+} -+ -+/* Iterate over threads inside debugger; see st/README */ -+void _st_iterate_threads(void) -+{ -+ _st_iterate_threads_helper(_st_show_thread_stack); -+} -+ - #endif /* DEBUG */ - diff --git a/trunk/research/st-1.9/extensions/stx.h b/trunk/research/st-1.9/extensions/stx.h deleted file mode 100644 index 8371e0d93c..0000000000 --- a/trunk/research/st-1.9/extensions/stx.h +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef _STX_H_ -#define _STX_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "st.h" - - -#ifdef __cplusplus -extern "C" { -#endif - - -/***************************************** - * Basic types definitions - */ - -typedef struct _stx_centry stx_cache_entry_t; -typedef struct _stx_cache stx_cache_t; - -/* This is public type */ -typedef struct _stx_cache_info { - size_t max_size; - size_t max_weight; - size_t hash_size; - size_t cur_size; - size_t cur_weight; - unsigned long hits; - unsigned long lookups; - unsigned long inserts; - unsigned long deletes; -} stx_cache_info_t; - - -/***************************************** - * Cache and cache entry methods - */ - -stx_cache_t *stx_cache_create(size_t max_size, size_t max_weight, - size_t hash_size, - unsigned long (*key_hash_fn)(const void *key), - long (*key_cmp_fn)(const void *key1, - const void *key2), - void (*cleanup_fn)(void *key, void *data)); -void stx_cache_empty(stx_cache_t *cache); -void stx_cache_traverse(stx_cache_t *cache, - void (*callback)(void *key, void *data)); -void stx_cache_traverse_lru(stx_cache_t *, void (*)(void *, void *), - unsigned int); -void stx_cache_traverse_mru(stx_cache_t *, void (*)(void *, void *), - unsigned int); -void stx_cache_getinfo(stx_cache_t *cache, stx_cache_info_t *info); -size_t stx_cache_getsize(stx_cache_t *cache); -size_t stx_cache_getweight(stx_cache_t *cache); - - -stx_cache_entry_t *stx_cache_entry_create(void *key, void *data, - size_t weight); -void stx_cache_entry_delete(stx_cache_t *cache, stx_cache_entry_t *entry); -stx_cache_entry_t *stx_cache_entry_lookup(stx_cache_t *cache, const void *key); -void stx_cache_entry_release(stx_cache_t *, stx_cache_entry_t *); -int stx_cache_entry_insert(stx_cache_t *cache, stx_cache_entry_t *entry); -stx_cache_entry_t *stx_cache_entry_getlru(stx_cache_t *cache); -int stx_cache_entry_sizeof(void); -void *stx_cache_entry_getdata(stx_cache_entry_t *entry); -void *stx_cache_entry_getkey(stx_cache_entry_t *entry); -size_t stx_cache_entry_getweight(stx_cache_entry_t *entry); - - -int stx_dns_cache_init(size_t max_size, size_t max_bytes, size_t hash_size); -void stx_dns_cache_getinfo(stx_cache_info_t *info); -int stx_dns_getaddrlist(const char *hostname, struct in_addr *addrs, - int *num_addrs, st_utime_t timeout); -int stx_dns_getaddr(const char *hostname, struct in_addr *addr, - st_utime_t timeout); - -#ifdef __cplusplus -} -#endif - -#endif /* !_STX_H_ */ - diff --git a/trunk/research/st-1.9/extensions/stx_fileio.c b/trunk/research/st-1.9/extensions/stx_fileio.c deleted file mode 100644 index cb24346e8a..0000000000 --- a/trunk/research/st-1.9/extensions/stx_fileio.c +++ /dev/null @@ -1,197 +0,0 @@ -/* - * File I/O extension to the State Threads Library. - */ - -/* - * The contents of this file are subject to the Mozilla Public - * License Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code is the file I/O extension to the State Threads Library. - * - * The Initial Developer of the Original Code is Jeff - * . Portions created by the Initial - * Developer are Copyright (C) 2002 the Initial Developer. All Rights - * Reserved. - * - * Contributor(s): (none) - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License Version 2 or later (the - * "GPL"), in which case the provisions of the GPL are applicable - * instead of those above. If you wish to allow use of your - * version of this file only under the terms of the GPL and not to - * allow others to use your version of this file under the MPL, - * indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by - * the GPL. If you do not delete the provisions above, a recipient - * may use your version of this file under either the MPL or the - * GPL. - */ - -#include - -#include "stx_fileio.h" - -#define STX_FILEIO_SIGNUM SIGUSR2 - -typedef struct { - st_netfd_t data_fd; - st_netfd_t control_fd; - pid_t pid; -} fileio_data_t; - -#define FILEREADER_MAX_READ 1024 - -typedef struct { - off_t offset; - ssize_t nbytes; -} file_reader_cb_t; - -/** - * Fork a process to read a file and return its pid. Receives - * offset/length commands from control stream and sends corresponding data - * to out stream. A zero length on the control stream signals an end. - * - * @param fd stream from which to read - * @param control_out receives the file descriptor to which control commands can be sent - * @param fd_out receives the file descriptor from which the output of the command can be read. - * @return PID of the process created to execute the command - */ -pid_t -file_reader(int fd, int *fd_control, int *fd_out) -{ - pid_t pid; - int control_pipe[2], out_pipe[2]; - - if (pipe(control_pipe) < 0 || pipe(out_pipe) < 0) - return (pid_t)-1; - - pid = fork(); - if (pid == (pid_t) -1) - { - close(control_pipe[0]); - close(control_pipe[1]); - close(out_pipe[0]); - close(out_pipe[1]); - return pid; - } - else if (pid == (pid_t) 0) - { - // child - off_t pos = 0; - file_reader_cb_t cb; - char buf[FILEREADER_MAX_READ]; - if (fd == -1) - _exit(EXIT_FAILURE); - - while (sizeof(cb) == read(control_pipe[0], &cb, sizeof(cb))) { - ssize_t nb; - if (0 >= cb.nbytes) - goto clean_exit; - if (pos != cb.offset) { - pos = lseek(fd, cb.offset, SEEK_SET); - if (pos == (off_t)-1) - break; - } - nb = read(fd, buf, cb.nbytes); - if (nb == (ssize_t)-1) - break; - pos += nb; - write(out_pipe[1], (char *)&nb, sizeof(nb)); - write(out_pipe[1], buf, nb); - } - perror("ERROR: file_reader: "); - clean_exit: - close(control_pipe[0]); - close(control_pipe[1]); - close(out_pipe[0]); - close(out_pipe[1]); - _exit(EXIT_SUCCESS); - } - - // parent - close(out_pipe[1]); - close(control_pipe[0]); - *fd_out = out_pipe[0]; - *fd_control = control_pipe[1]; - return pid; -} - -/** - * fileio_data_t destructor callback - */ -static void -fileio_data_destructor(void *dat_in) -{ - if (dat_in) { - fileio_data_t *dat = (fileio_data_t *)dat_in; - file_reader_cb_t cb; - cb.offset = 0; - cb.nbytes = 0; - st_write(dat->control_fd, (char *)&cb, sizeof(cb), - ST_UTIME_NO_TIMEOUT); - waitpid(dat->pid, NULL, 0); - st_netfd_close(dat->control_fd); - st_netfd_close(dat->data_fd); - free(dat_in); - } -} - -/** - * Retrieve fileio_data_t struct from an st descriptor. Create and store - * a new one if needed. - */ -static fileio_data_t *get_fileio_data(st_netfd_t fd) -{ - fileio_data_t *dat = (fileio_data_t *)st_netfd_getspecific(fd); - if (!dat) { - int fd_control, fd_out; - pid_t pid = file_reader(st_netfd_fileno(fd), &fd_control, &fd_out); - if (pid != (pid_t)-1) { - dat = (fileio_data_t *)calloc(1, sizeof(fileio_data_t)); - dat->control_fd = st_netfd_open(fd_control); - dat->data_fd = st_netfd_open(fd_out); - dat->pid = pid; - st_netfd_setspecific(fd, dat, fileio_data_destructor); - } - } - return dat; -} - -/** - * Read data from the specified section of a file. Uses a forked - * file_reader process to do the actual reading so as to avoid causing all - * State Threads to block. - * - * @param fd must refer to a seekable file. - * @param offset absolute offset within the file - * @param buf output buffer - * @param nbytes size of the output buffer - * @param timeout - */ -ssize_t -stx_file_read(st_netfd_t fd, off_t offset, void *buf, size_t nbytes, st_utime_t timeout) -{ - fileio_data_t *dat = get_fileio_data(fd); - if (dat) { - file_reader_cb_t cb; - ssize_t ret = (ssize_t)-1; - cb.offset = offset; - cb.nbytes = nbytes; - st_write(dat->control_fd, (char *)&cb, sizeof(cb), timeout); - if (sizeof(ret) == st_read(dat->data_fd, (char *)&ret, sizeof(ret), timeout) && 0 < ret && ret <= nbytes) { - return st_read(dat->data_fd, buf, ret, timeout); - } else { - return ret; - } - } - - return (ssize_t)-1; -} diff --git a/trunk/research/st-1.9/extensions/stx_fileio.h b/trunk/research/st-1.9/extensions/stx_fileio.h deleted file mode 100644 index b6bec190b7..0000000000 --- a/trunk/research/st-1.9/extensions/stx_fileio.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * File I/O extension to the State Threads Library. - */ - -/* - * The contents of this file are subject to the Mozilla Public - * License Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code is the file I/O extension to the State Threads Library. - * - * The Initial Developer of the Original Code is Jeff - * . Portions created by the Initial - * Developer are Copyright (C) 2002 the Initial Developer. All Rights - * Reserved. - * - * Contributor(s): (none) - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License Version 2 or later (the - * "GPL"), in which case the provisions of the GPL are applicable - * instead of those above. If you wish to allow use of your - * version of this file only under the terms of the GPL and not to - * allow others to use your version of this file under the MPL, - * indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by - * the GPL. If you do not delete the provisions above, a recipient - * may use your version of this file under either the MPL or the - * GPL. - */ - -#ifndef __STX_FILEIO_H__ -#define __STX_FILEIO_H__ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -extern ssize_t stx_file_read(st_netfd_t fd, off_t offset, void *buf, size_t nbytes, st_utime_t timeout); - -#ifdef __cplusplus -} -#endif -#endif /* !__STX_FILEIO_H__ */ diff --git a/trunk/research/st-1.9/extensions/testdns.c b/trunk/research/st-1.9/extensions/testdns.c deleted file mode 100644 index aa896b25e5..0000000000 --- a/trunk/research/st-1.9/extensions/testdns.c +++ /dev/null @@ -1,112 +0,0 @@ -#include "stx.h" -#include -#include - - -#define MAX_ADDRS 128 -#define TIMEOUT (4*1000000LL) - -static void do_resolve(const char *host) -{ - struct in_addr addrs[MAX_ADDRS]; - int i, n = MAX_ADDRS; - - if (stx_dns_getaddrlist(host, addrs, &n, TIMEOUT) < 0) { - fprintf(stderr, "stx_dns_getaddrlist: can't resolve %s: ", host); - if (h_errno == NETDB_INTERNAL) - perror(""); - else - herror(""); - } else { - if (n > 0) - printf("%-40s %s\n", (char *)host, inet_ntoa(addrs[0])); - for (i = 1; i < n; i++) - printf("%-40s %s\n", "", inet_ntoa(addrs[i])); - } -} - -static void show_info(void) -{ - stx_cache_info_t info; - - stx_dns_cache_getinfo(&info); - printf("DNS cache info:\n\n"); - printf("max_size: %8d\n", (int)info.max_size); - printf("capacity: %8d bytes\n", (int)info.max_weight); - printf("hash_size: %8d\n", (int)info.hash_size); - printf("cur_size: %8d\n" - "cur_mem: %8d bytes\n" - "hits: %8d\n" - "lookups: %8d\n" - "inserts: %8d\n" - "deletes: %8d\n", - (int)info.cur_size, (int)info.cur_weight, (int)info.hits, - (int)info.lookups, (int)info.inserts, (int)info.deletes); -} - -extern stx_cache_t *_stx_dns_cache; - -static void printhost(void *host, void *data) -{ - printf("%s\n", (char *)host); -} - -static void show_lru(void) -{ - printf("LRU hosts:\n\n"); - stx_cache_traverse_lru(_stx_dns_cache, printhost, 10); -} - -static void show_mru(void) -{ - printf("MRU hosts:\n\n"); - stx_cache_traverse_mru(_stx_dns_cache, printhost, 10); -} - -static void flush_cache(void) -{ - stx_cache_empty(_stx_dns_cache); - printf("DNS cache is empty\n"); -} - - -int main() -{ - char line[256]; - char str[sizeof(line)]; - - st_init(); - stx_dns_cache_init(100, 10000, 101); - - for ( ; ; ) { - fputs("> ", stdout); - fflush(stdout); - if (!fgets(line, sizeof(line), stdin)) - break; - if (sscanf(line, "%s", str) != 1) - continue; - if (strcmp(str, "exit") == 0 || strcmp(str, "quit") == 0) - break; - if (strcmp(str, "info") == 0) { - show_info(); - continue; - } - if (strcmp(str, "lru") == 0) { - show_lru(); - continue; - } - if (strcmp(str, "mru") == 0) { - show_mru(); - continue; - } - if (strcmp(str, "flush") == 0) { - flush_cache(); - continue; - } - - do_resolve(str); - } - - return 0; -} - diff --git a/trunk/research/st-1.9/libst.def b/trunk/research/st-1.9/libst.def deleted file mode 100644 index 6eaf149a97..0000000000 --- a/trunk/research/st-1.9/libst.def +++ /dev/null @@ -1,51 +0,0 @@ -EXPORTS - st_accept @62 - st_cond_broadcast @63 - st_cond_destroy @64 - st_cond_new @65 - st_cond_signal @66 - st_cond_timedwait @67 - st_cond_wait @68 - st_connect @69 - st_getfdlimit @70 - st_init @71 - st_key_create @72 - st_key_getlimit @73 - st_mutex_destroy @74 - st_mutex_lock @75 - st_mutex_new @76 - st_mutex_trylock @77 - st_mutex_unlock @78 - st_netfd_close @79 - st_netfd_fileno @80 - st_netfd_free @81 - st_netfd_getspecific @82 - st_netfd_open @83 - st_netfd_open_socket @84 - st_netfd_poll @85 - st_netfd_serialize_accept @86 - st_netfd_setspecific @87 - st_open @88 - st_poll @89 - st_randomize_stacks @90 - st_read @91 - st_read_fully @92 - st_read_resid @93 - st_recvfrom @94 - st_sendto @95 - st_sleep @96 - st_thread_create @97 - st_thread_exit @98 - st_thread_getspecific @99 - st_thread_interrupt @100 - st_thread_join @101 - st_thread_self @102 - st_thread_setspecific @103 - st_time @104 - st_timecache_set @105 - st_usleep @106 - st_utime @107 - st_utime_last_clock @108 - st_write @109 - st_write_resid @110 - st_writev @111 diff --git a/trunk/research/st-1.9/st.pc.in b/trunk/research/st-1.9/st.pc.in deleted file mode 100644 index 46c39ec522..0000000000 --- a/trunk/research/st-1.9/st.pc.in +++ /dev/null @@ -1,10 +0,0 @@ -prefix=@prefix@ -exec_prefix=${prefix} -libdir=${exec_prefix}/lib -includedir=${prefix}/include - -Name: libst -Description: State Thread Library -Version: @VERSION@ -Libs: -L${libdir} -lst -Cflags: -I${includedir} diff --git a/trunk/research/st-1.9/st.spec b/trunk/research/st-1.9/st.spec deleted file mode 100644 index 4914aa1961..0000000000 --- a/trunk/research/st-1.9/st.spec +++ /dev/null @@ -1,79 +0,0 @@ -Summary: State Threads Library -Name: st -Version: 1.9 -Release: 1 -Copyright: MPL 1.2 or GPL 2+ -Packager: Wesley W. Terpstra -Source: http://prdownloads.sourceforge.net/state-threads/st-%{version}.tar.gz -Prefix: /usr -BuildRoot: /tmp/%{name}-%{version}-build -Group: Development/Libraries - -%description -The State Threads library has an interface similar to POSIX threads. - -However, the threads are actually all run in-process. This type of -threading allows for controlled schedualing points. It is highly useful -for designing robust and extremely scalable internet applications since -there is no resource contention and locking is generally unnecessary. - -It can be combined with traditional threading or multiple process -parallelism to take advantage of multiple processors. - -See: for further -information about how state threads improve performance. - -%package -n libst-devel -Summary: State Threads Library - Development Files -Group: Development/Libraries -Requires: libst1 - -%description -n libst-devel -Development headers and documentation for libst - -%package -n libst1 -Summary: State Threads Library - Shared Libs Major 1 -Group: System/Libraries - -%description -n libst1 -Shared libraries for running applications linked against api version 1. - -%prep -%setup -q - -%build -make CONFIG_GUESS_PATH=/usr/share/automake default-optimized - -%install -if [ -d ${RPM_BUILD_ROOT} ]; then rm -rf ${RPM_BUILD_ROOT}; fi - -mkdir -m 0755 -p ${RPM_BUILD_ROOT}/%{prefix}/lib/pkgconfig -mkdir -m 0755 -p ${RPM_BUILD_ROOT}/%{prefix}/include -mkdir -m 0755 -p ${RPM_BUILD_ROOT}/%{prefix}/share/doc/libst-devel -cp -a obj/libst.* ${RPM_BUILD_ROOT}/%{prefix}/lib -cp -a obj/st.h ${RPM_BUILD_ROOT}/%{prefix}/include -sed "s*@prefix@*%{prefix}*g" ${RPM_BUILD_ROOT}/%{prefix}/lib/pkgconfig/st.pc -cp -a docs/* ${RPM_BUILD_ROOT}/%{prefix}/share/doc/libst-devel/ -cp -a examples ${RPM_BUILD_ROOT}/%{prefix}/share/doc/libst-devel/ - -%post -n libst1 -/sbin/ldconfig %{prefix}/lib - -%files -n libst1 -%defattr(-,root,root) -%{prefix}/lib/lib*.so.* - -%files -n libst-devel -%defattr(-,root,root) -%{prefix}/include/* -%{prefix}/lib/lib*.a -%{prefix}/lib/lib*.so -%{prefix}/lib/pkgconfig/st.pc -%{prefix}/share/doc/libst-devel/* - -%clean -if [ -d ${RPM_BUILD_ROOT} ]; then rm -rf ${RPM_BUILD_ROOT}; fi - -%changelog -* Wed Dec 26 2001 Wesley W. Terpstra -- first rpms for libst-1.3.tar.gz diff --git a/trunk/research/st-1.9/st/init b/trunk/research/st-1.9/st/init new file mode 100644 index 0000000000..61604b75fb --- /dev/null +++ b/trunk/research/st-1.9/st/init @@ -0,0 +1,3 @@ +#ifndef _st_icpp_init_stub +#define _st_icpp_init_stub +#endif diff --git a/trunk/research/st-1.9/st/st.upp b/trunk/research/st-1.9/st/st.upp new file mode 100644 index 0000000000..d71da8addf --- /dev/null +++ b/trunk/research/st-1.9/st/st.upp @@ -0,0 +1,16 @@ +file + main readonly separator, + ..\common.h, + ..\event.c, + ..\io.c, + ..\key.c, + ..\md.h, + ..\md.S, + ..\public.h, + ..\sched.c, + ..\stk.c, + ..\sync.c; + +mainconfig + "" = "MAIN"; + From 2f388d614f9bf012ed9884ed53b803176f56487c Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 12 Oct 2014 21:31:50 +0800 Subject: [PATCH 009/800] rename st-1.9 to st --- trunk/research/{st-1.9 => st}/Makefile | 0 trunk/research/{st-1.9 => st}/common.h | 0 trunk/research/{st-1.9 => st}/event.c | 0 trunk/research/{st-1.9 => st}/io.c | 0 trunk/research/{st-1.9 => st}/key.c | 0 trunk/research/{st-1.9 => st}/md.S | 0 trunk/research/{st-1.9 => st}/md.h | 0 trunk/research/{st-1.9 => st}/osguess.sh | 0 trunk/research/{st-1.9 => st}/public.h | 0 trunk/research/{st-1.9 => st}/sched.c | 0 trunk/research/{st-1.9 => st}/st/init | 0 trunk/research/{st-1.9 => st}/st/st.upp | 0 trunk/research/{st-1.9 => st}/stk.c | 0 trunk/research/{st-1.9 => st}/sync.c | 0 14 files changed, 0 insertions(+), 0 deletions(-) rename trunk/research/{st-1.9 => st}/Makefile (100%) rename trunk/research/{st-1.9 => st}/common.h (100%) rename trunk/research/{st-1.9 => st}/event.c (100%) rename trunk/research/{st-1.9 => st}/io.c (100%) rename trunk/research/{st-1.9 => st}/key.c (100%) rename trunk/research/{st-1.9 => st}/md.S (100%) rename trunk/research/{st-1.9 => st}/md.h (100%) rename trunk/research/{st-1.9 => st}/osguess.sh (100%) rename trunk/research/{st-1.9 => st}/public.h (100%) rename trunk/research/{st-1.9 => st}/sched.c (100%) rename trunk/research/{st-1.9 => st}/st/init (100%) rename trunk/research/{st-1.9 => st}/st/st.upp (100%) rename trunk/research/{st-1.9 => st}/stk.c (100%) rename trunk/research/{st-1.9 => st}/sync.c (100%) diff --git a/trunk/research/st-1.9/Makefile b/trunk/research/st/Makefile similarity index 100% rename from trunk/research/st-1.9/Makefile rename to trunk/research/st/Makefile diff --git a/trunk/research/st-1.9/common.h b/trunk/research/st/common.h similarity index 100% rename from trunk/research/st-1.9/common.h rename to trunk/research/st/common.h diff --git a/trunk/research/st-1.9/event.c b/trunk/research/st/event.c similarity index 100% rename from trunk/research/st-1.9/event.c rename to trunk/research/st/event.c diff --git a/trunk/research/st-1.9/io.c b/trunk/research/st/io.c similarity index 100% rename from trunk/research/st-1.9/io.c rename to trunk/research/st/io.c diff --git a/trunk/research/st-1.9/key.c b/trunk/research/st/key.c similarity index 100% rename from trunk/research/st-1.9/key.c rename to trunk/research/st/key.c diff --git a/trunk/research/st-1.9/md.S b/trunk/research/st/md.S similarity index 100% rename from trunk/research/st-1.9/md.S rename to trunk/research/st/md.S diff --git a/trunk/research/st-1.9/md.h b/trunk/research/st/md.h similarity index 100% rename from trunk/research/st-1.9/md.h rename to trunk/research/st/md.h diff --git a/trunk/research/st-1.9/osguess.sh b/trunk/research/st/osguess.sh similarity index 100% rename from trunk/research/st-1.9/osguess.sh rename to trunk/research/st/osguess.sh diff --git a/trunk/research/st-1.9/public.h b/trunk/research/st/public.h similarity index 100% rename from trunk/research/st-1.9/public.h rename to trunk/research/st/public.h diff --git a/trunk/research/st-1.9/sched.c b/trunk/research/st/sched.c similarity index 100% rename from trunk/research/st-1.9/sched.c rename to trunk/research/st/sched.c diff --git a/trunk/research/st-1.9/st/init b/trunk/research/st/st/init similarity index 100% rename from trunk/research/st-1.9/st/init rename to trunk/research/st/st/init diff --git a/trunk/research/st-1.9/st/st.upp b/trunk/research/st/st/st.upp similarity index 100% rename from trunk/research/st-1.9/st/st.upp rename to trunk/research/st/st/st.upp diff --git a/trunk/research/st-1.9/stk.c b/trunk/research/st/stk.c similarity index 100% rename from trunk/research/st-1.9/stk.c rename to trunk/research/st/stk.c diff --git a/trunk/research/st-1.9/sync.c b/trunk/research/st/sync.c similarity index 100% rename from trunk/research/st-1.9/sync.c rename to trunk/research/st/sync.c From b65df251c31784b512a9a03cd9013f026190d779 Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 12 Oct 2014 21:50:28 +0800 Subject: [PATCH 010/800] chmod +x *.sh --- trunk/research/st/osguess.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 trunk/research/st/osguess.sh diff --git a/trunk/research/st/osguess.sh b/trunk/research/st/osguess.sh old mode 100644 new mode 100755 From 48e187a75d7ea51c49c7538c5c518cfaa2a011b4 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 15 Oct 2014 15:07:19 +0800 Subject: [PATCH 011/800] I donot think QQ is neccessary. --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index bc9b6d387a..a4136d48f0 100755 --- a/README.md +++ b/README.md @@ -8,7 +8,6 @@ SRS定位是运营级的互联网直播服务器集群,追求更好的概念 下载发布版(国内阿里云镜像): [Centos6-x86_64](http://www.ossrs.net/srs.release/releases/files/SRS-CentOS6-x86_64-1.0.0.zip) 其他[more...](http://www.ossrs.net/srs.release/releases/)
-加入QQ群: [http://url.cn/WAHICw](http://url.cn/WAHICw) (Group: 212189142)
同类产品:[BLS](https://github.com/wenjiegit/Bull-Live-Server)/[BLE](https://github.com/wenjiegit/Bull-Live-Encoder), [NGINX-RTMP](https://github.com/arut/nginx-rtmp-module), [CRTMPD](http://www.rtmpd.com/), [RED5](http://www.red5.org/), [WOWZA](http://www.wowza.com/), [FMS/AMS](http://www.adobe.com/products/adobe-media-server-standard.html) 获得源码(github): [https://github.com/winlinvip/simple-rtmp-server](https://github.com/winlinvip/simple-rtmp-server) [GIT使用方法](https://github.com/winlinvip/simple-rtmp-server/wiki/Git) From 75f3df5d3012a82e68f774883651b247d652a70b Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 16 Oct 2014 08:57:18 +0800 Subject: [PATCH 012/800] remove qq url. --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index a4136d48f0..35e0c6f885 100755 --- a/README.md +++ b/README.md @@ -53,7 +53,6 @@ CSDN mirror: [https://code.csdn.net/winlinvip/srs-csdn](https://code.csdn.net/wi See also: [https://github.com/winlinvip/simple-rtmp-server](https://github.com/winlinvip/simple-rtmp-server)
Github DEMO: [demo with your SRS](http://winlinvip.github.io/srs.release/trunk/research/players/srs_player.html?server=192.168.1.170&vhost=192.168.1.170)
Wiki: [https://github.com/winlinvip/simple-rtmp-server/wiki](https://github.com/winlinvip/simple-rtmp-server/wiki)
-TencentQQ: [http://url.cn/WAHICw](http://url.cn/WAHICw) (Group: 212189142)
StreamServers:[BLS](https://github.com/wenjiegit/Bull-Live-Server)/[BLE](https://github.com/wenjiegit/Bull-Live-Encoder), [NGINX-RTMP](https://github.com/arut/nginx-rtmp-module), [CRTMPD](http://www.rtmpd.com/), [RED5](http://www.red5.org/), [WOWZA](http://www.wowza.com/), [FMS/AMS](http://www.adobe.com/products/adobe-media-server-standard.html) ## AUTHORS From 9b90a7444fde91b627139f12d9ca71ab7e7f3176 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 16 Oct 2014 09:19:09 +0800 Subject: [PATCH 013/800] remove all chinese in readme. --- README.md | 151 +++++++++++++++++++++++------------------------------- 1 file changed, 63 insertions(+), 88 deletions(-) diff --git a/README.md b/README.md index 35e0c6f885..734b149dce 100755 --- a/README.md +++ b/README.md @@ -1,34 +1,6 @@ #Simple-RTMP-Server -SRS定位是运营级的互联网直播服务器集群,追求更好的概念完整性和最简单实现的代码。 - -下载发布版(github): -[Centos6-x86_64](http://winlinvip.github.io/srs.release/releases/files/SRS-CentOS6-x86_64-1.0.0.zip) -其他[more...](http://winlinvip.github.io/srs.release/releases/)
-下载发布版(国内阿里云镜像): -[Centos6-x86_64](http://www.ossrs.net/srs.release/releases/files/SRS-CentOS6-x86_64-1.0.0.zip) -其他[more...](http://www.ossrs.net/srs.release/releases/)
-同类产品:[BLS](https://github.com/wenjiegit/Bull-Live-Server)/[BLE](https://github.com/wenjiegit/Bull-Live-Encoder), [NGINX-RTMP](https://github.com/arut/nginx-rtmp-module), [CRTMPD](http://www.rtmpd.com/), [RED5](http://www.red5.org/), [WOWZA](http://www.wowza.com/), [FMS/AMS](http://www.adobe.com/products/adobe-media-server-standard.html) - -获得源码(github): [https://github.com/winlinvip/simple-rtmp-server](https://github.com/winlinvip/simple-rtmp-server) [GIT使用方法](https://github.com/winlinvip/simple-rtmp-server/wiki/Git) - -```bash -git clone https://github.com/winlinvip/simple-rtmp-server.git -``` - -获得源码(国内CSDN镜像): [https://code.csdn.net/winlinvip/srs-csdn](https://code.csdn.net/winlinvip/srs-csdn) [GIT使用方法](https://github.com/winlinvip/simple-rtmp-server/wiki/Git) - -```bash -git clone https://code.csdn.net/winlinvip/srs-csdn.git -``` - -报告问题(BugReport): [https://github.com/winlinvip/simple-rtmp-server/issues/new](https://github.com/winlinvip/simple-rtmp-server/issues/new)
-中文资料(Wiki): [https://github.com/winlinvip/simple-rtmp-server/wiki](https://github.com/winlinvip/simple-rtmp-server/wiki)
-使用步骤(Usage): [https://github.com/winlinvip/simple-rtmp-server#usage](#usage)
-公用机器(LiveShow): [https://github.com/winlinvip/simple-rtmp-server/wiki/LiveShow](https://github.com/winlinvip/simple-rtmp-server/wiki/LiveShow)
-捐款(Donation): [GitHub](http://winlinvip.github.io/srs.release/donation/index.html) -或 [阿里云镜像](http://www.ossrs.net/srs.release/donation/index.html) ,查看 -[捐献墙(Donations)](https://github.com/winlinvip/simple-rtmp-server/blob/master/DONATIONS.txt)
+SRS is industrial-strength live streaming cluster, for the best conceptual integrity and the simplest implementation. ## About @@ -47,6 +19,7 @@ rtmp([encoder push](https://github.com/winlinvip/simple-rtmp-server/wiki/Deliver [forward](https://github.com/winlinvip/simple-rtmp-server/wiki/FFMPEG), [http hooks](https://github.com/winlinvip/simple-rtmp-server/wiki/HTTPCallback), [http api](https://github.com/winlinvip/simple-rtmp-server/wiki/HTTPApi), [http server](https://github.com/winlinvip/simple-rtmp-server/wiki/HTTPServer), [dvr](https://github.com/winlinvip/simple-rtmp-server/wiki/DVR). +WebSite: [http://ossrs.net](http://ossrs.net)
Release: [http://winlinvip.github.io/srs.release](http://winlinvip.github.io/srs.release)
Blog: [http://blog.csdn.net/win_lin](http://blog.csdn.net/win_lin)
CSDN mirror: [https://code.csdn.net/winlinvip/srs-csdn](https://code.csdn.net/winlinvip/srs-csdn)
@@ -129,47 +102,73 @@ OR [aliyun mirror](http://www.ossrs.net/srs.release/donation/index.html) Donations:
[https://github.com/winlinvip/simple-rtmp-server/blob/master/DONATIONS.txt](https://github.com/winlinvip/simple-rtmp-server/blob/master/DONATIONS.txt) +## Download + +From github.io:
+[Centos6-x86_64](http://winlinvip.github.io/srs.release/releases/files/SRS-CentOS6-x86_64-1.0.0.zip) +[more...](http://winlinvip.github.io/srs.release/releases/)
+ +From ossrs.net:
+[Centos6-x86_64](http://www.ossrs.net/srs.release/releases/files/SRS-CentOS6-x86_64-1.0.0.zip) +[more...](http://www.ossrs.net/srs.release/releases/) + +## Mirrors + +Github: [https://github.com/winlinvip/simple-rtmp-server](https://github.com/winlinvip/simple-rtmp-server) +[Git Usage](https://github.com/winlinvip/simple-rtmp-server/wiki/Git) + +```bash +git clone https://github.com/winlinvip/simple-rtmp-server.git +``` + +CSDN: [https://code.csdn.net/winlinvip/srs-csdn](https://code.csdn.net/winlinvip/srs-csdn) +[Git Usage](https://github.com/winlinvip/simple-rtmp-server/wiki/Git) + +```bash +git clone https://code.csdn.net/winlinvip/srs-csdn.git +``` + ## System Requirements Supported operating systems and hardware: * All Linux , both 32 and 64 bits * All hardware. ## Summary -1. 简洁稳定:Simple, also stable enough. -1. 高性能:[High-performance](https://github.com/winlinvip/simple-rtmp-server/wiki/Performance): single-thread, async socket, event/st-thread driven. -1. 高并发:[High-concurrency](https://github.com/winlinvip/simple-rtmp-server/wiki/Performance), 1800 connections(500kbps), 900Mbps, CPU 90.2%, 41MB -1. RTMP源站:Support [RTMP Origin Server](https://github.com/winlinvip/simple-rtmp-server/wiki/DeliveryRTMP). -1. CDN边缘(上下行加速):Support [RTMP Edge Server](https://github.com/winlinvip/simple-rtmp-server/wiki/Edge) for CDN, push/pull stream from any RTMP server -1. 单进程(无多进程):Support single process; no multiple processes. -1. 支持Vhost:Support [Vhost](https://github.com/winlinvip/simple-rtmp-server/wiki/RtmpUrlVhost), support \_\_defaultVhost\_\_. -1. 直播(无点播):Support [RTMP](https://github.com/winlinvip/simple-rtmp-server/wiki/DeliveryRTMP) live streaming; no vod streaming. -1. 苹果HLS:Support Apple [HLS(m3u8)](https://github.com/winlinvip/simple-rtmp-server/wiki/DeliveryHLS) live streaming. -1. 支持纯音频HLS:Support [HLS audio-only](https://github.com/winlinvip/simple-rtmp-server/wiki/DeliveryHLS#hlsaudioonly) live streaming. -1. 支持Reload:Support [Reload](https://github.com/winlinvip/simple-rtmp-server/wiki/Reload) config to enable changes. -1. 支持GopCache:Support [cache last gop](https://github.com/winlinvip/simple-rtmp-server/wiki/LowLatency#gop-cache) for flash player to fast startup. -1. 侦听多端口:Support listen at multiple ports. -1. 长时间推流:Support long time(>4.6hours) publish/play. -1. 转发流:Support [Forward](https://github.com/winlinvip/simple-rtmp-server/wiki/Forward) in master-slave mode. -1. 流转码:Support live stream [Transcoding](https://github.com/winlinvip/simple-rtmp-server/wiki/FFMPEG) by ffmpeg. -1. 支持FFMPEG滤镜:Support [ffmpeg](https://github.com/winlinvip/simple-rtmp-server/wiki/FFMPEG) filters(logo/overlay/crop), x264 params, copy/vn/an. -1. 只转码音频:Support audio [transcode](https://github.com/winlinvip/simple-rtmp-server/wiki/FFMPEG) only, speex/mp3 to aac -1. 支持HTTP回调:Support [http callback api hooks](https://github.com/winlinvip/simple-rtmp-server/wiki/HTTPCallback)(for authentication and injection). -1. 带宽测速:Support [bandwidth test](https://github.com/winlinvip/simple-rtmp-server/wiki/BandwidthTestTool) api and flash client. -1. 演示页面:Player, publisher(encoder), and [demo pages(jquery+bootstrap)](https://github.com/winlinvip/simple-rtmp-server/wiki/SampleDemo). -1. 视频会议演示:[Demo](https://github.com/winlinvip/simple-rtmp-server/wiki/SampleDemo) video meeting or chat(SRS+cherrypy+jquery+bootstrap). -1. 中文Wiki:Full documents in [wiki](https://github.com/winlinvip/simple-rtmp-server/wiki/CNHome), in Chineses. -1. 客户端库:Support RTMP(play-publish) library: [srs-librtmp](https://github.com/winlinvip/simple-rtmp-server/wiki/SrsLibrtmp) -1. 支持ARM平台:Support ARM([debian armhf, v7cpu](https://github.com/winlinvip/simple-rtmp-server/wiki/SrsLinuxArm)) with rtmp/ssl/hls/librtmp. -1. 支持Init.d脚本:Support [init.d](https://github.com/winlinvip/simple-rtmp-server/wiki/LinuxService) and packge script, log to file. -1. 支持ATC:Support [RTMP ATC](https://github.com/winlinvip/simple-rtmp-server/wiki/RTMP-ATC) for HLS/HDS to support backup(failover) -1. 支持HTTP-RESTful-API:Support [HTTP RESTful management api](https://github.com/winlinvip/simple-rtmp-server/wiki/HTTPApi). -1. 采集流:Support [Ingest](https://github.com/winlinvip/simple-rtmp-server/wiki/Ingest) FILE/HTTP/RTMP/RTSP(RTP, SDP) to RTMP using external tools(e.g ffmepg). -1. 支持录制:Support [DVR](https://github.com/winlinvip/simple-rtmp-server/wiki/DVR), record live to flv file for vod. -1. 可追溯日志:Support [tracable log, session based log](https://github.com/winlinvip/simple-rtmp-server/wiki/SrsLog). -1. 支持FMS-Token穿越:Support DRM [token traverse](https://github.com/winlinvip/simple-rtmp-server/wiki/DRM#tokentraverse) for fms origin authenticate. -1. 全面的Utest:Support system full utest on gtest. -1. (不稳定)内嵌HTTP服务器:[experiment] Support embeded [HTTP server](https://github.com/winlinvip/simple-rtmp-server/wiki/SampleHTTP) for hls(live/vod) -1. (不稳定)FLV点播流:[experiment] Support [vod stream(http flv/hls vod stream)](https://github.com/winlinvip/simple-rtmp-server/wiki/FlvVodStream). +1. Simple, also stable enough. +1. [High-performance](https://github.com/winlinvip/simple-rtmp-server/wiki/Performance): single-thread, async socket, event/st-thread driven. +1. [High-concurrency](https://github.com/winlinvip/simple-rtmp-server/wiki/Performance), 1800 connections(500kbps), 900Mbps, CPU 90.2%, 41MB +1. Support [RTMP Origin Server](https://github.com/winlinvip/simple-rtmp-server/wiki/DeliveryRTMP). +1. Support [RTMP Edge Server](https://github.com/winlinvip/simple-rtmp-server/wiki/Edge) for CDN, push/pull stream from any RTMP server +1. Support single process; no multiple processes. +1. Support [Vhost](https://github.com/winlinvip/simple-rtmp-server/wiki/RtmpUrlVhost), support \_\_defaultVhost\_\_. +1. Support [RTMP](https://github.com/winlinvip/simple-rtmp-server/wiki/DeliveryRTMP) live streaming; no vod streaming. +1. Support Apple [HLS(m3u8)](https://github.com/winlinvip/simple-rtmp-server/wiki/DeliveryHLS) live streaming. +1. Support [HLS audio-only](https://github.com/winlinvip/simple-rtmp-server/wiki/DeliveryHLS#hlsaudioonly) live streaming. +1. Support [Reload](https://github.com/winlinvip/simple-rtmp-server/wiki/Reload) config to enable changes. +1. Support [cache last gop](https://github.com/winlinvip/simple-rtmp-server/wiki/LowLatency#gop-cache) for flash player to fast startup. +1. Support listen at multiple ports. +1. Support long time(>4.6hours) publish/play. +1. Support [Forward](https://github.com/winlinvip/simple-rtmp-server/wiki/Forward) in master-slave mode. +1. Support live stream [Transcoding](https://github.com/winlinvip/simple-rtmp-server/wiki/FFMPEG) by ffmpeg. +1. Support [ffmpeg](https://github.com/winlinvip/simple-rtmp-server/wiki/FFMPEG) filters(logo/overlay/crop), x264 params, copy/vn/an. +1. Support audio [transcode](https://github.com/winlinvip/simple-rtmp-server/wiki/FFMPEG) only, speex/mp3 to aac +1. Support [http callback api hooks](https://github.com/winlinvip/simple-rtmp-server/wiki/HTTPCallback)(for authentication and injection). +1. Support [bandwidth test](https://github.com/winlinvip/simple-rtmp-server/wiki/BandwidthTestTool) api and flash client. +1. Player, publisher(encoder), and [demo pages(jquery+bootstrap)](https://github.com/winlinvip/simple-rtmp-server/wiki/SampleDemo). +1. [Demo](https://github.com/winlinvip/simple-rtmp-server/wiki/SampleDemo) video meeting or chat(SRS+cherrypy+jquery+bootstrap). +1. Full documents in [wiki](https://github.com/winlinvip/simple-rtmp-server/wiki/CNHome), in Chineses. +1. Support RTMP(play-publish) library: [srs-librtmp](https://github.com/winlinvip/simple-rtmp-server/wiki/SrsLibrtmp) +1. Support ARM([debian armhf, v7cpu](https://github.com/winlinvip/simple-rtmp-server/wiki/SrsLinuxArm)) with rtmp/ssl/hls/librtmp. +1. Support [init.d](https://github.com/winlinvip/simple-rtmp-server/wiki/LinuxService) and packge script, log to file. +1. Support [RTMP ATC](https://github.com/winlinvip/simple-rtmp-server/wiki/RTMP-ATC) for HLS/HDS to support backup(failover) +1. Support [HTTP RESTful management api](https://github.com/winlinvip/simple-rtmp-server/wiki/HTTPApi). +1. Support [Ingest](https://github.com/winlinvip/simple-rtmp-server/wiki/Ingest) FILE/HTTP/RTMP/RTSP(RTP, SDP) to RTMP using external tools(e.g ffmepg). +1. Support [DVR](https://github.com/winlinvip/simple-rtmp-server/wiki/DVR), record live to flv file for vod. +1. Support [tracable log, session based log](https://github.com/winlinvip/simple-rtmp-server/wiki/SrsLog). +1. Support DRM [token traverse](https://github.com/winlinvip/simple-rtmp-server/wiki/DRM#tokentraverse) for fms origin authenticate. +1. Support system full utest on gtest. +1. [experiment] Support embeded [HTTP server](https://github.com/winlinvip/simple-rtmp-server/wiki/SampleHTTP) for hls(live/vod) +1. [experiment] Support [vod stream(http flv/hls vod stream)](https://github.com/winlinvip/simple-rtmp-server/wiki/FlvVodStream). 1. [no-plan] Support <500ms latency, FRSC(Fast RTMP-compatible Stream Channel tech). 1. [no-plan] Support RTMP 302 redirect [#92](https://github.com/winlinvip/simple-rtmp-server/issues/92). 1. [no-plan] Support multiple processes, for both origin and edge @@ -573,30 +572,6 @@ Remark: only send signals to child processes. -### (plan) CLI Architecture - -
-                       +---------+
-                    +--+ stream1 +---------+
-                    |  +---------+         |
- +--------+         |  +---------+         |   +-------+
- | master +--fork->-+--+ streamN +---amf0--+>--+  cli  +
- +--------+         |  +---------+         |   +-------+
-                    |  +-------------+     |
-                    +--+ back source +-----+
-                       +-------------+
-Remark:
-(1) master listen the global api port, for example, 33330
-(2) back source and stream processes listen at private api port, 
-    for example, 33331, 33332, 33333
-(3) work processes(stream and back-source), report private api
-    port to master global api port.
-(4) cli connect to master global api port, get all other private
-    api ports
-(5) cli connect to each stream/back-source process to get api data,
-    cli analysis and summary the data, return to user.
-
- ### Bandwidth Test Workflow

From 8053f0ba9d5f39c2bde38068efaf745fb6b54915 Mon Sep 17 00:00:00 2001
From: winlin 
Date: Thu, 16 Oct 2014 09:22:05 +0800
Subject: [PATCH 014/800] refine readme.

---
 README.md | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 734b149dce..942ef30f2c 100755
--- a/README.md
+++ b/README.md
@@ -1,6 +1,10 @@
 #Simple-RTMP-Server
 
-SRS is industrial-strength live streaming cluster, for the best conceptual integrity and the simplest implementation.
+SRS is industrial-strength live streaming cluster, 
+for the best conceptual integrity and the simplest implementation, 
+which delivering rtmp/hls/http live/vod on x86/x64/arm/mips linux, 
+supports origin/edge/vhost and transcode/ingest/dvr/forward/http-api/http-callback/reload 
+with wiki and the most simple arch.
 
 ## About
 

From cba9e518456b227b36c74e46cc7239754e1e3136 Mon Sep 17 00:00:00 2001
From: winlin 
Date: Thu, 16 Oct 2014 10:21:12 +0800
Subject: [PATCH 015/800] refine readme.

---
 README.md | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/README.md b/README.md
index 942ef30f2c..d3bf5cfaff 100755
--- a/README.md
+++ b/README.md
@@ -3,8 +3,9 @@
 SRS is industrial-strength live streaming cluster, 
 for the best conceptual integrity and the simplest implementation, 
 which delivering rtmp/hls/http live/vod on x86/x64/arm/mips linux, 
-supports origin/edge/vhost and transcode/ingest/dvr/forward/http-api/http-callback/reload 
-with wiki and the most simple arch.
+supports origin/edge/vhost and transcode/ingest and dvr/forward 
+and http-api/http-callback/reload, with wiki and the most 
+simple arch.
 
 ## About
 
@@ -100,19 +101,20 @@ Please select your language:
 
 ## Donation
 
-[http://winlinvip.github.io/srs.release/donation/index.html](http://winlinvip.github.io/srs.release/donation/index.html) 
-OR [aliyun mirror](http://www.ossrs.net/srs.release/donation/index.html)
+Donation:
+[http://winlinvip.github.io/srs.release/donation/index.html](http://winlinvip.github.io/srs.release/donation/index.html) OR
+[http://www.ossrs.net/srs.release/donation/index.html](http://www.ossrs.net/srs.release/donation/index.html) Donations:
[https://github.com/winlinvip/simple-rtmp-server/blob/master/DONATIONS.txt](https://github.com/winlinvip/simple-rtmp-server/blob/master/DONATIONS.txt) ## Download -From github.io:
+From github.io: [Centos6-x86_64](http://winlinvip.github.io/srs.release/releases/files/SRS-CentOS6-x86_64-1.0.0.zip) [more...](http://winlinvip.github.io/srs.release/releases/)
-From ossrs.net:
+From ossrs.net: [Centos6-x86_64](http://www.ossrs.net/srs.release/releases/files/SRS-CentOS6-x86_64-1.0.0.zip) [more...](http://www.ossrs.net/srs.release/releases/) From 4ab4ce29bb8338c5f960e3bfa0e7d5bcce3ff396 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 16 Oct 2014 10:29:26 +0800 Subject: [PATCH 016/800] refine readme. --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index d3bf5cfaff..3943d0533a 100755 --- a/README.md +++ b/README.md @@ -188,12 +188,6 @@ Supported operating systems and hardware: ## Releases * 2014-10-09, [Release v1.0-beta](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.beta), all bug fixed, 1.0.0, 59316 lines.
-* 2014-08-03, [Release v1.0-mainline7](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline7), config utest, all bug fixed. 57432 lines.
-* 2014-07-13, [Release v1.0-mainline6](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline6), core/kernel/rtmp utest, refine bandwidth(as/js/srslibrtmp library). 50029 lines.
-* 2014-06-27, [Release v1.0-mainline5](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline5), refine perf 3k+ clients, edge token traverse, [srs monitor](http://ossrs.net:1977), 30days online. 41573 lines.
-* 2014-05-28, [Release v1.0-mainline4](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline4), support heartbeat, tracable log, fix mem leak and bugs. 39200 lines.
-* 2014-05-18, [Release v1.0-mainline3](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline3), support mips, fms origin, json(http-api). 37594 lines.
-* 2014-04-28, [Release v1.0-mainline2](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline2), support [dvr](https://github.com/winlinvip/simple-rtmp-server/wiki/DVR), android, [edge](https://github.com/winlinvip/simple-rtmp-server/wiki/Edge). 35255 lines.
* 2014-04-07, [Release v1.0-mainline](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline), support [arm](https://github.com/winlinvip/simple-rtmp-server/wiki/SrsLinuxArm), [init.d](https://github.com/winlinvip/simple-rtmp-server/wiki/LinuxService), http [server](https://github.com/winlinvip/simple-rtmp-server/wiki/HTTPServer)/[api](https://github.com/winlinvip/simple-rtmp-server/wiki/HTTPApi), [ingest](https://github.com/winlinvip/simple-rtmp-server/wiki/SampleIngest). 30000 lines.
* 2013-12-25, [Release v0.9](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.9), support bandwidth test, player/encoder/chat [demos](https://github.com/winlinvip/simple-rtmp-server/wiki/SampleDemo). 20926 lines.
* 2013-12-08, [Release v0.8](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.8), support [http hooks callback](https://github.com/winlinvip/simple-rtmp-server/wiki/HTTPCallback), update [st_load](https://github.com/winlinvip/st-load). 19186 lines.
From ba868da7c4e2cbcee7dc75047d8f32d84dd13e80 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 16 Oct 2014 10:37:33 +0800 Subject: [PATCH 017/800] refine readme. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3943d0533a..39ed929473 100755 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ for the best conceptual integrity and the simplest implementation, which delivering rtmp/hls/http live/vod on x86/x64/arm/mips linux, supports origin/edge/vhost and transcode/ingest and dvr/forward and http-api/http-callback/reload, with wiki and the most -simple arch. +simple architecture. ## About From e707006a68f22f1f5e38f65fe0de8b64c091f8f6 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 16 Oct 2014 10:58:20 +0800 Subject: [PATCH 018/800] revert github srs README to English. 2.0.0. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 39ed929473..c32c85445d 100755 --- a/README.md +++ b/README.md @@ -201,6 +201,7 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History +* v1.0, 2014-10-16, revert github srs README to English. 2.0.0. * v1.0, 2014-10-09, [1.0 beta(1.0.0)](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.beta) released. 59316 lines. * v1.0, 2014-10-08, fix [#151](https://github.com/winlinvip/simple-rtmp-server/issues/151), always reap ts whatever audio or video packet. 0.9.223. * v1.0, 2014-10-08, fix [#162](https://github.com/winlinvip/simple-rtmp-server/issues/162), failed if no epoll. 0.9.222. From b1b70cdf09d366a411c1d5329721ec5eea51d765 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 18 Oct 2014 10:55:26 +0800 Subject: [PATCH 019/800] update readme. --- README.md | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index c32c85445d..25ddf67294 100755 --- a/README.md +++ b/README.md @@ -7,6 +7,14 @@ supports origin/edge/vhost and transcode/ingest and dvr/forward and http-api/http-callback/reload, with wiki and the most simple architecture. +Download from github.io: +[Centos6-x86_64](http://winlinvip.github.io/srs.release/releases/files/SRS-CentOS6-x86_64-1.0.0.zip) +[more...](http://winlinvip.github.io/srs.release/releases/) + +Download from ossrs.net: +[Centos6-x86_64](http://www.ossrs.net/srs.release/releases/files/SRS-CentOS6-x86_64-1.0.0.zip) +[more...](http://www.ossrs.net/srs.release/releases/) + ## About SRS(SIMPLE RTMP Server) over state-threads created in 2013.10. @@ -108,16 +116,6 @@ Donation:
Donations:
[https://github.com/winlinvip/simple-rtmp-server/blob/master/DONATIONS.txt](https://github.com/winlinvip/simple-rtmp-server/blob/master/DONATIONS.txt) -## Download - -From github.io: -[Centos6-x86_64](http://winlinvip.github.io/srs.release/releases/files/SRS-CentOS6-x86_64-1.0.0.zip) -[more...](http://winlinvip.github.io/srs.release/releases/)
- -From ossrs.net: -[Centos6-x86_64](http://www.ossrs.net/srs.release/releases/files/SRS-CentOS6-x86_64-1.0.0.zip) -[more...](http://www.ossrs.net/srs.release/releases/) - ## Mirrors Github: [https://github.com/winlinvip/simple-rtmp-server](https://github.com/winlinvip/simple-rtmp-server) From a4d1b136d849721714417b92a9707077e286e8a1 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 18 Oct 2014 11:01:35 +0800 Subject: [PATCH 020/800] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 25ddf67294..0008849075 100755 --- a/README.md +++ b/README.md @@ -173,6 +173,7 @@ Supported operating systems and hardware: 1. Support system full utest on gtest. 1. [experiment] Support embeded [HTTP server](https://github.com/winlinvip/simple-rtmp-server/wiki/SampleHTTP) for hls(live/vod) 1. [experiment] Support [vod stream(http flv/hls vod stream)](https://github.com/winlinvip/simple-rtmp-server/wiki/FlvVodStream). +1. [dev] Suppport [English wiki](https://github.com/winlinvip/simple-rtmp-server/wiki/ENHome_v1). 1. [no-plan] Support <500ms latency, FRSC(Fast RTMP-compatible Stream Channel tech). 1. [no-plan] Support RTMP 302 redirect [#92](https://github.com/winlinvip/simple-rtmp-server/issues/92). 1. [no-plan] Support multiple processes, for both origin and edge From 4fe5a9bdb39197d06aebda445c2625efea750177 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 18 Oct 2014 11:03:15 +0800 Subject: [PATCH 021/800] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0008849075..882df57718 100755 --- a/README.md +++ b/README.md @@ -174,6 +174,7 @@ Supported operating systems and hardware: 1. [experiment] Support embeded [HTTP server](https://github.com/winlinvip/simple-rtmp-server/wiki/SampleHTTP) for hls(live/vod) 1. [experiment] Support [vod stream(http flv/hls vod stream)](https://github.com/winlinvip/simple-rtmp-server/wiki/FlvVodStream). 1. [dev] Suppport [English wiki](https://github.com/winlinvip/simple-rtmp-server/wiki/ENHome_v1). +1. [dev] Research and simplify st, [bug #182](https://github.com/winlinvip/simple-rtmp-server/issues/182). 1. [no-plan] Support <500ms latency, FRSC(Fast RTMP-compatible Stream Channel tech). 1. [no-plan] Support RTMP 302 redirect [#92](https://github.com/winlinvip/simple-rtmp-server/issues/92). 1. [no-plan] Support multiple processes, for both origin and edge From d9b12b151c6b39cdd25599760d7e4400ab9044f7 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 18 Oct 2014 13:16:17 +0800 Subject: [PATCH 022/800] refine the makefile. --- trunk/research/st/Makefile | 385 +++--------------------------------- trunk/research/st/srs.c | 7 + trunk/research/st/st/st.upp | 2 + 3 files changed, 32 insertions(+), 362 deletions(-) mode change 100644 => 100755 trunk/research/st/Makefile create mode 100644 trunk/research/st/srs.c mode change 100644 => 100755 trunk/research/st/st/st.upp diff --git a/trunk/research/st/Makefile b/trunk/research/st/Makefile old mode 100644 new mode 100755 index a38ac2b625..6344631ac6 --- a/trunk/research/st/Makefile +++ b/trunk/research/st/Makefile @@ -32,209 +32,26 @@ # may use your version of this file under either the MPL or the # GPL. -# This is the full version of the libst library - modify carefully -VERSION = 1.9 - -########################## -# Supported OSes: -# -#OS = AIX -#OS = CYGWIN -#OS = DARWIN -#OS = FREEBSD -#OS = HPUX -#OS = HPUX_64 -#OS = IRIX -#OS = IRIX_64 -#OS = LINUX -#OS = NETBSD -#OS = OPENBSD -#OS = OSF1 -#OS = SOLARIS -#OS = SOLARIS_64 - -# Please see the "Other possible defines" section below for -# possible compilation options. ########################## - +# Target dir and cc: CC = cc -AR = ar -LD = ld -RANLIB = ranlib -LN = ln - -SHELL = /bin/sh -ECHO = /bin/echo - -BUILD = DBG -TARGETDIR = $(OS)_$(shell uname -r)_$(BUILD) - -DEFINES = -D$(OS) -CFLAGS = -SFLAGS = -ARFLAGS = -rv -LNFLAGS = -s -DSO_SUFFIX = so - -MAJOR = $(shell echo $(VERSION) | sed 's/^\([^\.]*\).*/\1/') -DESC = st.pc +TARGETDIR = objs ########################## -# Platform section. -# Possible targets: - -TARGETS = aix-debug aix-optimized \ - cygwin-debug cygwin-optimized \ - darwin-debug darwin-optimized \ - freebsd-debug freebsd-optimized \ - hpux-debug hpux-optimized \ - hpux-64-debug hpux-64-optimized \ - irix-n32-debug irix-n32-optimized \ - irix-64-debug irix-64-optimized \ - linux-debug linux-optimized \ - netbsd-debug netbsd-optimized \ - openbsd-debug openbsd-optimized \ - osf1-debug osf1-optimized \ - solaris-debug solaris-optimized \ - solaris-64-debug solaris-64-optimized - -# -# Platform specifics -# - -ifeq ($(OS), AIX) -AIX_VERSION = $(shell uname -v).$(shell uname -r) -TARGETDIR = $(OS)_$(AIX_VERSION)_$(BUILD) -CC = xlC -STATIC_ONLY = yes -ifeq ($(BUILD), OPT) -OTHER_FLAGS = -w -endif -ifneq ($(filter-out 4.1 4.2, $(AIX_VERSION)),) -DEFINES += -DMD_HAVE_SOCKLEN_T -endif -endif - -ifeq ($(OS), CYGWIN) -TARGETDIR = $(OS)_$(BUILD) -CC = gcc -LD = gcc -DSO_SUFFIX = dll -SLIBRARY = $(TARGETDIR)/libst.dll.a -DLIBRARY = $(TARGETDIR)/libst.dll -DEF_FILE = $(TARGETDIR)/libst.def -LDFLAGS = libst.def -shared --enable-auto-image-base -Wl,--output-def,$(DEF_FILE),--out-implib,$(SLIBRARY) -OTHER_FLAGS = -Wall -endif - -ifeq ($(OS), DARWIN) -LD = cc -SFLAGS = -fPIC -fno-common -DSO_SUFFIX = dylib -RELEASE = $(shell uname -r | cut -d. -f1) -PPC = $(shell test $(RELEASE) -le 9 && echo yes) -INTEL = $(shell test $(RELEASE) -ge 9 && echo yes) -ifeq ($(PPC), yes) -CFLAGS += -arch ppc -LDFLAGS += -arch ppc -endif -ifeq ($(INTEL), yes) -CFLAGS += -arch i386 -arch x86_64 -LDFLAGS += -arch i386 -arch x86_64 -endif -LDFLAGS += -dynamiclib -install_name /sw/lib/libst.$(MAJOR).$(DSO_SUFFIX) -compatibility_version $(MAJOR) -current_version $(VERSION) -OTHER_FLAGS = -Wall -endif - -ifeq ($(OS), FREEBSD) -SFLAGS = -fPIC -LDFLAGS = -shared -soname=$(SONAME) -lc -OTHER_FLAGS = -Wall -ifeq ($(shell test -f /usr/include/sys/event.h && echo yes), yes) -DEFINES += -DMD_HAVE_KQUEUE -endif -endif - -ifeq (HPUX, $(findstring HPUX, $(OS))) -ifeq ($(OS), HPUX_64) -DEFINES = -DHPUX -CFLAGS = -Ae +DD64 +Z -else -CFLAGS = -Ae +DAportable +Z -endif -RANLIB = true -LDFLAGS = -b -DSO_SUFFIX = sl -endif +# Supported OSes: +OS = LINUX -ifeq (IRIX, $(findstring IRIX, $(OS))) -ifeq ($(OS), IRIX_64) -DEFINES = -DIRIX -ABIFLAG = -64 -else -ABIFLAG = -n32 -endif -RANLIB = true -CFLAGS = $(ABIFLAG) -mips3 -LDFLAGS = $(ABIFLAG) -shared -OTHER_FLAGS = -fullwarn +ifneq ($(shell test -f /usr/include/sys/epoll.h && echo yes), yes) +default: + @echo "epoll not found" + @exit 1 endif -ifeq ($(OS), LINUX) EXTRA_OBJS = $(TARGETDIR)/md.o -SFLAGS = -fPIC -LDFLAGS = -shared -soname=$(SONAME) -lc -OTHER_FLAGS = -Wall -ifeq ($(shell test -f /usr/include/sys/epoll.h && echo yes), yes) -DEFINES += -DMD_HAVE_EPOLL -endif -endif - -ifeq ($(OS), NETBSD) -SFLAGS = -fPIC -LDFLAGS = -shared -soname=$(SONAME) -lc -OTHER_FLAGS = -Wall -endif -ifeq ($(OS), OPENBSD) -SFLAGS = -fPIC -LDFLAGS = -shared -soname=$(SONAME) -lc -OTHER_FLAGS = -Wall -ifeq ($(shell test -f /usr/include/sys/event.h && echo yes), yes) -DEFINES += -DMD_HAVE_KQUEUE -endif -endif - -ifeq ($(OS), OSF1) -RANLIB = true -LDFLAGS = -shared -all -expect_unresolved "*" -endif - -ifeq (SOLARIS, $(findstring SOLARIS, $(OS))) -TARGETDIR = $(OS)_$(shell uname -r | sed 's/^5/2/')_$(BUILD) -CC = gcc -LD = gcc -RANLIB = true -LDFLAGS = -G -OTHER_FLAGS = -Wall -ifeq ($(OS), SOLARIS_64) -DEFINES = -DSOLARIS -CFLAGS += -m64 -LDFLAGS += -m64 -endif -endif - -# -# End of platform section. -########################## - - -ifeq ($(BUILD), OPT) -OTHER_FLAGS += -O -else -OTHER_FLAGS += -g -DEFINES += -DDEBUG -endif +CFLAGS = +OTHER_FLAGS += -Wall -g -O0 +DEFINES = -D$(OS) -DDEBUG -DMD_HAVE_EPOLL ########################## # Other possible defines: @@ -285,81 +102,23 @@ endif CFLAGS += $(DEFINES) $(OTHER_FLAGS) $(EXTRA_CFLAGS) -OBJS = $(TARGETDIR)/sched.o \ - $(TARGETDIR)/stk.o \ - $(TARGETDIR)/sync.o \ - $(TARGETDIR)/key.o \ - $(TARGETDIR)/io.o \ - $(TARGETDIR)/event.o +OBJS = $(TARGETDIR)/sched.o \ + $(TARGETDIR)/stk.o \ + $(TARGETDIR)/sync.o \ + $(TARGETDIR)/key.o \ + $(TARGETDIR)/io.o \ + $(TARGETDIR)/event.o \ + $(TARGETDIR)/srs.o OBJS += $(EXTRA_OBJS) -HEADER = $(TARGETDIR)/st.h -SLIBRARY = $(TARGETDIR)/libst.a -DLIBRARY = $(TARGETDIR)/libst.$(DSO_SUFFIX).$(VERSION) -EXAMPLES = examples - -LINKNAME = libst.$(DSO_SUFFIX) -SONAME = libst.$(DSO_SUFFIX).$(MAJOR) -FULLNAME = libst.$(DSO_SUFFIX).$(VERSION) - -ifeq ($(OS), CYGWIN) -SONAME = cygst.$(DSO_SUFFIX) -SLIBRARY = $(TARGETDIR)/libst.dll.a -DLIBRARY = $(TARGETDIR)/$(SONAME) -LINKNAME = -# examples directory does not compile under cygwin -EXAMPLES = -endif - -ifeq ($(OS), DARWIN) -LINKNAME = libst.$(DSO_SUFFIX) -SONAME = libst.$(MAJOR).$(DSO_SUFFIX) -FULLNAME = libst.$(VERSION).$(DSO_SUFFIX) -endif - -ifeq ($(STATIC_ONLY), yes) -LIBRARIES = $(SLIBRARY) -else -LIBRARIES = $(SLIBRARY) $(DLIBRARY) -endif - -ifeq ($(OS),) -ST_ALL = unknown -else -ST_ALL = $(TARGETDIR) $(LIBRARIES) $(HEADER) $(EXAMPLES) $(DESC) -endif - -all: $(ST_ALL) +SRS = $(TARGETDIR)/srs -unknown: - @echo - @echo "Please specify one of the following targets:" - @echo - @for target in $(TARGETS); do echo $$target; done - @echo - -st.pc: st.pc.in - sed "s/@VERSION@/${VERSION}/g" < $< > $@ +all: $(TARGETDIR) $(SRS) $(TARGETDIR): if [ ! -d $(TARGETDIR) ]; then mkdir $(TARGETDIR); fi -$(SLIBRARY): $(OBJS) - $(AR) $(ARFLAGS) $@ $(OBJS) - $(RANLIB) $@ - rm -f obj; $(LN) $(LNFLAGS) $(TARGETDIR) obj - -$(DLIBRARY): $(OBJS:%.o=%-pic.o) - $(LD) $(LDFLAGS) $^ -o $@ - if test "$(LINKNAME)"; then \ - cd $(TARGETDIR); \ - rm -f $(SONAME) $(LINKNAME); \ - $(LN) $(LNFLAGS) $(FULLNAME) $(SONAME); \ - $(LN) $(LNFLAGS) $(FULLNAME) $(LINKNAME); \ - fi - -$(HEADER): public.h - rm -f $@ - cp public.h $@ +$(SRS): $(OBJS) + $(CC) $(CFLAGS) -o $@ $(OBJS) $(TARGETDIR)/md.o: md.S $(CC) $(CFLAGS) -c $< -o $@ @@ -367,103 +126,5 @@ $(TARGETDIR)/md.o: md.S $(TARGETDIR)/%.o: %.c common.h md.h $(CC) $(CFLAGS) -c $< -o $@ -examples:: - @echo Making $@ - @cd $@; $(MAKE) CC="$(CC)" CFLAGS="$(CFLAGS)" OS="$(OS)" TARGETDIR="$(TARGETDIR)" - clean: - rm -rf *_OPT *_DBG obj st.pc - -########################## -# Pattern rules: - -ifneq ($(SFLAGS),) -# Compile with shared library options if it's a C file -$(TARGETDIR)/%-pic.o: %.c common.h md.h - $(CC) $(CFLAGS) $(SFLAGS) -c $< -o $@ -endif - -# Compile assembly as normal or C as normal if no SFLAGS -%-pic.o: %.o - rm -f $@; $(LN) $(LNFLAGS) $( Date: Sat, 18 Oct 2014 13:58:23 +0800 Subject: [PATCH 023/800] remove supports for OSX. 2.0.1. --- README.md | 1 + trunk/auto/depends.sh | 115 +------------------------------ trunk/auto/options.sh | 55 +-------------- trunk/research/st/Makefile | 1 + trunk/src/app/srs_app_config.cpp | 2 +- trunk/src/core/srs_core.hpp | 2 +- 6 files changed, 7 insertions(+), 169 deletions(-) mode change 100644 => 100755 trunk/auto/options.sh diff --git a/README.md b/README.md index 882df57718..14fb59b6d1 100755 --- a/README.md +++ b/README.md @@ -201,6 +201,7 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History +* v1.0, 2014-10-18, remove supports for OSX. 2.0.1. * v1.0, 2014-10-16, revert github srs README to English. 2.0.0. * v1.0, 2014-10-09, [1.0 beta(1.0.0)](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.beta) released. 59316 lines. * v1.0, 2014-10-08, fix [#151](https://github.com/winlinvip/simple-rtmp-server/issues/151), always reap ts whatever audio or video packet. 0.9.223. diff --git a/trunk/auto/depends.sh b/trunk/auto/depends.sh index c145462a2b..d7f3f521ce 100755 --- a/trunk/auto/depends.sh +++ b/trunk/auto/depends.sh @@ -214,91 +214,6 @@ function Centos_prepare() return 0 } Centos_prepare; ret=$?; if [[ 0 -ne $ret ]]; then echo "CentOS prepare failed, ret=$ret"; exit $ret; fi -##################################################################################### -# for OSX, auto install tools by brew -##################################################################################### -OS_IS_OSX=NO -function OSX_prepare() -{ - SYS_NAME=`uname -s` - if [ $SYS_NAME != Darwin ]; then - echo "This is not Darwin OSX" - return 0; - fi - - OS_IS_OSX=YES - echo "OSX detected, install tools if needed" - - gcc --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then - echo "install gcc" - require_sudoer "sudo brew install gcc" - sudo brew install gcc; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi - echo "install gcc success" - fi - - g++ --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then - echo "install gcc-c++" - require_sudoer "sudo brew install gcc-c++" - sudo brew install gcc-c++; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi - echo "install gcc-c++ success" - fi - - make --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then - echo "install make" - require_sudoer "sudo brew install make" - sudo brew install make; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi - echo "install make success" - fi - - patch --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then - echo "install patch" - require_sudoer "sudo brew install patch" - sudo brew install patch; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi - echo "install patch success" - fi - - if [ $SRS_FFMPEG_TOOL = YES ]; then - automake --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then - echo "install automake" - require_sudoer "sudo brew install automake" - sudo brew install automake; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi - echo "install automake success" - fi - - autoconf --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then - echo "install autoconf" - require_sudoer "sudo brew install autoconf" - sudo brew install autoconf; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi - echo "install autoconf success" - fi - - libtool --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then - echo "install libtool" - require_sudoer "sudo brew install libtool" - sudo brew install libtool; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi - echo "install libtool success" - fi - - if [[ ! -f /usr/include/pcre.h ]]; then - echo "install pcre-devel" - require_sudoer "sudo brew install pcre-devel" - sudo brew install pcre-devel; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi - echo "install pcre-devel success" - fi - - if [[ ! -f /usr/include/zlib.h ]]; then - echo "install zlib-devel" - require_sudoer "sudo brew install zlib-devel" - sudo brew install zlib-devel; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi - echo "install zlib-devel success" - fi - fi - - echo "OSX install tools success" - return 0 -} -OSX_prepare; ret=$?; if [[ 0 -ne $ret ]]; then echo "OSX prepare failed, ret=$ret"; exit $ret; fi - ##################################################################################### # st-1.9 @@ -323,9 +238,6 @@ if [ $SRS_EMBEDED_CPU = YES ]; then ) fi else - if [ $SRS_OSX = YES ]; then - _ST_MAKE=darwin-debug - fi if [[ ! -f ${SRS_OBJS}/_flag.st.arm.tmp && -f ${SRS_OBJS}/st/libst.a ]]; then echo "st-1.9t is ok."; else @@ -351,21 +263,8 @@ if [ ! -f ${SRS_OBJS}/st/libst.a ]; then echo "build st-1.9 static lib failed."; ##################################################################################### # check the arm flag file, if flag changed, need to rebuild the st. if [ $SRS_HTTP_PARSER = YES ]; then - # for osx(darwin), donot use sed. - if [ $SRS_OSX = YES ]; then - if [[ -f ${SRS_OBJS}/hp/http_parser.h && -f ${SRS_OBJS}/hp/libhttp_parser.a ]]; then - echo "http-parser-2.1 is ok."; - else - echo "build http-parser-2.1 for osx(darwin)"; - ( - rm -rf ${SRS_OBJS}/http-parser-2.1 && cd ${SRS_OBJS} && unzip -q ../3rdparty/http-parser-2.1.zip && - cd http-parser-2.1 && - make package && - cd .. && rm -rf hp && ln -sf http-parser-2.1 hp - ) - fi # ok, arm specified, if the flag filed does not exists, need to rebuild. - elif [ $SRS_EMBEDED_CPU = YES ]; then + if [ $SRS_EMBEDED_CPU = YES ]; then if [[ -f ${SRS_OBJS}/_flag.st.hp.tmp && -f ${SRS_OBJS}/hp/http_parser.h && -f ${SRS_OBJS}/hp/libhttp_parser.a ]]; then echo "http-parser-2.1 for arm is ok."; else @@ -570,18 +469,6 @@ fi # extra configure options CONFIGURE_TOOL="./config" EXTRA_CONFIGURE="" -if [ $SRS_OSX = YES ]; then - CONFIGURE_TOOL="./Configure" - arch=`uname -m` && echo "OSX $arch"; - if [ $arch = x86_64 ]; then - echo "configure 64bit openssl"; - EXTRA_CONFIGURE=darwin64-x86_64-cc - else - echo "configure 32bit openssl"; - EXTRA_CONFIGURE=darwin-i386-cc - fi - echo "openssl extra config: $CONFIGURE_TOOL $EXTRA_CONFIGURE" -fi if [ $SRS_EMBEDED_CPU = YES ]; then CONFIGURE_TOOL="./Configure" fi diff --git a/trunk/auto/options.sh b/trunk/auto/options.sh old mode 100644 new mode 100755 index 6b0f5f04dc..82e0cb51ba --- a/trunk/auto/options.sh +++ b/trunk/auto/options.sh @@ -63,8 +63,6 @@ SRS_USE_SYS_SSL=NO # presets # for x86/x64 pc/servers SRS_X86_X64=NO -# for ios(darwin) -SRS_OSX=NO # armhf(v7cpu) built on ubuntu12 SRS_ARM_UBUNTU12=NO # mips built on ubuntu12 @@ -161,7 +159,6 @@ Options: Presets: --x86-x64 [default] for x86/x64 cpu, common pc and servers. - --osx for IOS(darwin) to build SRS. --pi for raspberry-pi(directly build), open features hls/ssl/static. --cubie for cubieboard(directly build), open features except ffmpeg/nginx. --arm alias for --with-arm-ubuntu12, for ubuntu12, arm crossbuild @@ -252,7 +249,6 @@ function parse_user_option() { --log-trace) SRS_LOG_TRACE=YES ;; --x86-x64) SRS_X86_X64=YES ;; - --osx) SRS_OSX=YES ;; --arm) SRS_ARM_UBUNTU12=YES ;; --mips) SRS_MIPS_UBUNTU12=YES ;; --pi) SRS_PI=YES ;; @@ -322,9 +318,7 @@ function apply_user_presets() { if [ $SRS_PI = NO ]; then if [ $SRS_CUBIE = NO ]; then if [ $SRS_X86_X64 = NO ]; then - if [ $SRS_OSX = NO ]; then - SRS_X86_X64=YES; opt="--x86-x64 $opt"; - fi + SRS_X86_X64=YES; opt="--x86-x64 $opt"; fi fi fi @@ -597,34 +591,7 @@ function apply_user_presets() { SRS_GPROF=NO SRS_STATIC=NO fi - - # if osx dev specified, open main server features. - if [ $SRS_OSX = YES ]; then - SRS_HLS=YES - SRS_DVR=YES - SRS_NGINX=NO - SRS_SSL=YES - SRS_FFMPEG_TOOL=NO - SRS_TRANSCODE=YES - SRS_INGEST=YES - SRS_STAT=NO - SRS_HTTP_PARSER=YES - SRS_HTTP_CALLBACK=YES - SRS_HTTP_SERVER=YES - SRS_HTTP_API=YES - SRS_LIBRTMP=NO - SRS_RESEARCH=NO - SRS_UTEST=NO - SRS_GPERF=NO - SRS_GPERF_MC=NO - SRS_GPERF_MP=NO - SRS_GPERF_CP=NO - SRS_GPROF=NO - SRS_STATIC=NO - fi - - - + # for srs demo if [ $SRS_DEMO = YES ]; then SRS_HLS=YES @@ -812,24 +779,6 @@ function check_option_conflicts() { echo "x86/x64 should never use static, see: ./configure --help"; __check_ok=NO; fi fi - - # for darwin, not support stat yet. - if [ $SRS_OSX = YES ]; then - if [ $SRS_STAT = YES ]; then - echo "osx should never use stat, see: ./configure --help"; __check_ok=NO; - fi - fi - - # for darwin, must use --osx, vice versa - if [ $SRS_OSX = YES ]; then - if [ `uname -s` != Darwin ]; then - echo "--osx is for darwin(your os is not), see: ./configure --help"; __check_ok=NO; - fi - else - if [ `uname -s` = Darwin ]; then - echo "use --osx for darwin, see: ./configure --help"; __check_ok=NO; - fi - fi # TODO: FIXME: check more os. diff --git a/trunk/research/st/Makefile b/trunk/research/st/Makefile index 6344631ac6..b51e2369c6 100755 --- a/trunk/research/st/Makefile +++ b/trunk/research/st/Makefile @@ -112,6 +112,7 @@ OBJS = $(TARGETDIR)/sched.o \ OBJS += $(EXTRA_OBJS) SRS = $(TARGETDIR)/srs +linux-debug: all all: $(TARGETDIR) $(SRS) $(TARGETDIR): diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index fbb97b62a7..bd7172fa83 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -1166,7 +1166,7 @@ void SrsConfig::print_help(char** argv) { printf( RTMP_SIG_SRS_NAME" "RTMP_SIG_SRS_VERSION" "RTMP_SIG_SRS_COPYRIGHT"\n" - "license: "RTMP_SIG_SRS_LICENSE"\n" + "License: "RTMP_SIG_SRS_LICENSE"\n" "Primary Authors: "RTMP_SIG_SRS_PRIMARY_AUTHROS"\n" "Build: "SRS_AUTO_BUILD_DATE" Configuration:"SRS_AUTO_USER_CONFIGURE"\n" "Features:"SRS_AUTO_CONFIGURE"\n""\n" diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 3807ef2cc1..47f56b1032 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR "2" #define VERSION_MINOR "0" -#define VERSION_REVISION "0" +#define VERSION_REVISION "1" #define RTMP_SIG_SRS_VERSION VERSION_MAJOR"."VERSION_MINOR"."VERSION_REVISION // server info. #define RTMP_SIG_SRS_KEY "SRS" From 9ab6ff15fe4a5a00bd91eaa2cb57529df96818c9 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 18 Oct 2014 13:58:47 +0800 Subject: [PATCH 024/800] remove supports for OSX. 2.0.1. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 14fb59b6d1..f100aa026c 100755 --- a/README.md +++ b/README.md @@ -201,7 +201,7 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History -* v1.0, 2014-10-18, remove supports for OSX. 2.0.1. +* v1.0, 2014-10-18, remove supports for OSX(darwin). 2.0.1. * v1.0, 2014-10-16, revert github srs README to English. 2.0.0. * v1.0, 2014-10-09, [1.0 beta(1.0.0)](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.beta) released. 59316 lines. * v1.0, 2014-10-08, fix [#151](https://github.com/winlinvip/simple-rtmp-server/issues/151), always reap ts whatever audio or video packet. 0.9.223. From 1f0f98ff04532be942e1a048fa106a0c970705b0 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 18 Oct 2014 14:04:52 +0800 Subject: [PATCH 025/800] update readme. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f100aa026c..9db3cc4b0a 100755 --- a/README.md +++ b/README.md @@ -572,6 +572,7 @@ Remark: (b) The CLI architecture is similar to this, instead, cli process will collect informations from all stream process, master process only send signals to child processes. +(c) Maybe multiple thread is ok? By winlin.
### Bandwidth Test Workflow From 57e83562210430ef94fabdc4ea71cd05499924d5 Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 19 Oct 2014 19:42:27 +0800 Subject: [PATCH 026/800] fix #184, support AnnexB in RTMP body for HLS. 2.0.2 --- README.md | 5 +- trunk/src/app/srs_app_avc_aac.cpp | 333 +++++++++++++++++--------- trunk/src/app/srs_app_avc_aac.hpp | 16 ++ trunk/src/app/srs_app_utility.cpp | 34 ++- trunk/src/app/srs_app_utility.hpp | 10 + trunk/src/core/srs_core.hpp | 2 +- trunk/src/kernel/srs_kernel_error.hpp | 1 + 7 files changed, 286 insertions(+), 115 deletions(-) diff --git a/README.md b/README.md index 9db3cc4b0a..eefee9a43c 100755 --- a/README.md +++ b/README.md @@ -201,8 +201,9 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History -* v1.0, 2014-10-18, remove supports for OSX(darwin). 2.0.1. -* v1.0, 2014-10-16, revert github srs README to English. 2.0.0. +* v2.0, 2014-10-19, fix [#184](https://github.com/winlinvip/simple-rtmp-server/issues/184), support AnnexB in RTMP body for HLS. 2.0.2 +* v2.0, 2014-10-18, remove supports for OSX(darwin). 2.0.1. +* v2.0, 2014-10-16, revert github srs README to English. 2.0.0. * v1.0, 2014-10-09, [1.0 beta(1.0.0)](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.beta) released. 59316 lines. * v1.0, 2014-10-08, fix [#151](https://github.com/winlinvip/simple-rtmp-server/issues/151), always reap ts whatever audio or video packet. 0.9.223. * v1.0, 2014-10-08, fix [#162](https://github.com/winlinvip/simple-rtmp-server/issues/162), failed if no epoll. 0.9.222. diff --git a/trunk/src/app/srs_app_avc_aac.cpp b/trunk/src/app/srs_app_avc_aac.cpp index 752a5d9a5c..4c0f2843e7 100644 --- a/trunk/src/app/srs_app_avc_aac.cpp +++ b/trunk/src/app/srs_app_avc_aac.cpp @@ -27,6 +27,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #include +#include SrsCodecSampleUnit::SrsCodecSampleUnit() { @@ -358,143 +359,255 @@ int SrsAvcAacCodec::video_avc_demux(char* data, int size, SrsCodecSample* sample sample->avc_packet_type = (SrsCodecVideoAVCType)avc_packet_type; if (avc_packet_type == SrsCodecVideoAVCTypeSequenceHeader) { - // AVCDecoderConfigurationRecord - // 5.2.4.1.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 - avc_extra_size = stream->size() - stream->pos(); - if (avc_extra_size > 0) { - srs_freep(avc_extra_data); - avc_extra_data = new char[avc_extra_size]; - memcpy(avc_extra_data, stream->data() + stream->pos(), avc_extra_size); + if ((ret = avc_demux_sps_pps(stream)) != ERROR_SUCCESS) { + return ret; } - - if (!stream->require(6)) { + } else if (avc_packet_type == SrsCodecVideoAVCTypeNALU){ + // ensure the sequence header demuxed + if (avc_extra_size <= 0 || !avc_extra_data) { ret = ERROR_HLS_DECODE_ERROR; - srs_error("hls decode video avc sequenc header failed. ret=%d", ret); + srs_error("hls decode video avc failed, sequence header not found. ret=%d", ret); return ret; } - //int8_t configurationVersion = stream->read_1bytes(); - stream->read_1bytes(); - //int8_t AVCProfileIndication = stream->read_1bytes(); - avc_profile = stream->read_1bytes(); - //int8_t profile_compatibility = stream->read_1bytes(); - stream->read_1bytes(); - //int8_t AVCLevelIndication = stream->read_1bytes(); - avc_level = stream->read_1bytes(); - // parse the NALU size. - int8_t lengthSizeMinusOne = stream->read_1bytes(); - lengthSizeMinusOne &= 0x03; - NAL_unit_length = lengthSizeMinusOne; - - // 1 sps - if (!stream->require(1)) { - ret = ERROR_HLS_DECODE_ERROR; - srs_error("hls decode video avc sequenc header sps failed. ret=%d", ret); - return ret; + // One or more NALUs (Full frames are required) + // try "AnnexB" from H.264-AVC-ISO_IEC_14496-10.pdf, page 211. + if ((ret = avc_demux_annexb_format(stream, sample)) != ERROR_SUCCESS) { + // stop try when system error. + if (ret != ERROR_HLS_AVC_TRY_OTHERS) { + srs_error("avc demux for annexb failed. ret=%d", ret); + return ret; + } + + // try "ISO Base Media File Format" from H.264-AVC-ISO_IEC_14496-15.pdf, page 20 + if ((ret = avc_demux_ibmf_format(stream, sample)) != ERROR_SUCCESS) { + return ret; + } } - int8_t numOfSequenceParameterSets = stream->read_1bytes(); - numOfSequenceParameterSets &= 0x1f; - if (numOfSequenceParameterSets != 1) { - ret = ERROR_HLS_DECODE_ERROR; - srs_error("hls decode video avc sequenc header sps failed. ret=%d", ret); + } else { + // ignored. + } + + srs_info("video decoded, type=%d, codec=%d, avc=%d, time=%d, size=%d", + frame_type, video_codec_id, avc_packet_type, composition_time, size); + + return ret; +} + +int SrsAvcAacCodec::avc_demux_sps_pps(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + // AVCDecoderConfigurationRecord + // 5.2.4.1.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 + avc_extra_size = stream->size() - stream->pos(); + if (avc_extra_size > 0) { + srs_freep(avc_extra_data); + avc_extra_data = new char[avc_extra_size]; + memcpy(avc_extra_data, stream->data() + stream->pos(), avc_extra_size); + } + + if (!stream->require(6)) { + ret = ERROR_HLS_DECODE_ERROR; + srs_error("hls decode video avc sequenc header failed. ret=%d", ret); + return ret; + } + //int8_t configurationVersion = stream->read_1bytes(); + stream->read_1bytes(); + //int8_t AVCProfileIndication = stream->read_1bytes(); + avc_profile = stream->read_1bytes(); + //int8_t profile_compatibility = stream->read_1bytes(); + stream->read_1bytes(); + //int8_t AVCLevelIndication = stream->read_1bytes(); + avc_level = stream->read_1bytes(); + + // parse the NALU size. + int8_t lengthSizeMinusOne = stream->read_1bytes(); + lengthSizeMinusOne &= 0x03; + NAL_unit_length = lengthSizeMinusOne; + + // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 + // 5.2.4.1 AVC decoder configuration record + // 5.2.4.1.2 Semantics + // The value of this field shall be one of 0, 1, or 3 corresponding to a + // length encoded with 1, 2, or 4 bytes, respectively. + if (NAL_unit_length == 2) { + ret = ERROR_HLS_DECODE_ERROR; + srs_error("sps lengthSizeMinusOne should never be 2. ret=%d", ret); + return ret; + } + + // 1 sps + if (!stream->require(1)) { + ret = ERROR_HLS_DECODE_ERROR; + srs_error("hls decode video avc sequenc header sps failed. ret=%d", ret); + return ret; + } + int8_t numOfSequenceParameterSets = stream->read_1bytes(); + numOfSequenceParameterSets &= 0x1f; + if (numOfSequenceParameterSets != 1) { + ret = ERROR_HLS_DECODE_ERROR; + srs_error("hls decode video avc sequenc header sps failed. ret=%d", ret); + return ret; + } + if (!stream->require(2)) { + ret = ERROR_HLS_DECODE_ERROR; + srs_error("hls decode video avc sequenc header sps size failed. ret=%d", ret); + return ret; + } + sequenceParameterSetLength = stream->read_2bytes(); + if (!stream->require(sequenceParameterSetLength)) { + ret = ERROR_HLS_DECODE_ERROR; + srs_error("hls decode video avc sequenc header sps data failed. ret=%d", ret); + return ret; + } + if (sequenceParameterSetLength > 0) { + srs_freep(sequenceParameterSetNALUnit); + sequenceParameterSetNALUnit = new char[sequenceParameterSetLength]; + memcpy(sequenceParameterSetNALUnit, stream->data() + stream->pos(), sequenceParameterSetLength); + stream->skip(sequenceParameterSetLength); + } + // 1 pps + if (!stream->require(1)) { + ret = ERROR_HLS_DECODE_ERROR; + srs_error("hls decode video avc sequenc header pps failed. ret=%d", ret); + return ret; + } + int8_t numOfPictureParameterSets = stream->read_1bytes(); + numOfPictureParameterSets &= 0x1f; + if (numOfPictureParameterSets != 1) { + ret = ERROR_HLS_DECODE_ERROR; + srs_error("hls decode video avc sequenc header pps failed. ret=%d", ret); + return ret; + } + if (!stream->require(2)) { + ret = ERROR_HLS_DECODE_ERROR; + srs_error("hls decode video avc sequenc header pps size failed. ret=%d", ret); + return ret; + } + pictureParameterSetLength = stream->read_2bytes(); + if (!stream->require(pictureParameterSetLength)) { + ret = ERROR_HLS_DECODE_ERROR; + srs_error("hls decode video avc sequenc header pps data failed. ret=%d", ret); + return ret; + } + if (pictureParameterSetLength > 0) { + srs_freep(pictureParameterSetNALUnit); + pictureParameterSetNALUnit = new char[pictureParameterSetLength]; + memcpy(pictureParameterSetNALUnit, stream->data() + stream->pos(), pictureParameterSetLength); + stream->skip(pictureParameterSetLength); + } + + return ret; +} + +int SrsAvcAacCodec::avc_demux_annexb_format(SrsStream* stream, SrsCodecSample* sample) +{ + int ret = ERROR_SUCCESS; + + // not annexb, try others + if (!srs_avc_startswith_annexb(stream, NULL)) { + return ERROR_HLS_AVC_TRY_OTHERS; + } + + // AnnexB + // B.1.1 Byte stream NAL unit syntax, + // H.264-AVC-ISO_IEC_14496-10.pdf, page 211. + while (!stream->empty()) { + // find start code + int nb_start_code = 0; + if (!srs_avc_startswith_annexb(stream, &nb_start_code)) { return ret; } - if (!stream->require(2)) { - ret = ERROR_HLS_DECODE_ERROR; - srs_error("hls decode video avc sequenc header sps size failed. ret=%d", ret); - return ret; + + // skip the start code. + if (nb_start_code > 0) { + stream->skip(nb_start_code); } - sequenceParameterSetLength = stream->read_2bytes(); - if (!stream->require(sequenceParameterSetLength)) { - ret = ERROR_HLS_DECODE_ERROR; - srs_error("hls decode video avc sequenc header sps data failed. ret=%d", ret); - return ret; + + // the NALU start bytes. + char* p = stream->data() + stream->pos(); + + // get the last matched NALU + while (!stream->empty()) { + if (srs_avc_startswith_annexb(stream, &nb_start_code)) { + break; + } + + stream->skip(1); } - if (sequenceParameterSetLength > 0) { - srs_freep(sequenceParameterSetNALUnit); - sequenceParameterSetNALUnit = new char[sequenceParameterSetLength]; - memcpy(sequenceParameterSetNALUnit, stream->data() + stream->pos(), sequenceParameterSetLength); - stream->skip(sequenceParameterSetLength); + + char* pp = stream->data() + stream->pos(); + + // skip the empty. + if (pp - p <= 0) { + continue; } - // 1 pps - if (!stream->require(1)) { - ret = ERROR_HLS_DECODE_ERROR; - srs_error("hls decode video avc sequenc header pps failed. ret=%d", ret); + + // got the NALU. + if ((ret = sample->add_sample_unit(p, pp - p)) != ERROR_SUCCESS) { + srs_error("annexb add video sample failed. ret=%d", ret); return ret; } - int8_t numOfPictureParameterSets = stream->read_1bytes(); - numOfPictureParameterSets &= 0x1f; - if (numOfPictureParameterSets != 1) { + } + + return ret; +} + +int SrsAvcAacCodec::avc_demux_ibmf_format(SrsStream* stream, SrsCodecSample* sample) +{ + int ret = ERROR_SUCCESS; + + int PictureLength = stream->size() - stream->pos(); + + // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 + // 5.2.4.1 AVC decoder configuration record + // 5.2.4.1.2 Semantics + // The value of this field shall be one of 0, 1, or 3 corresponding to a + // length encoded with 1, 2, or 4 bytes, respectively. + srs_assert(NAL_unit_length != 2); + + // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 20 + for (int i = 0; i < PictureLength;) { + // unsigned int((NAL_unit_length+1)*8) NALUnitLength; + if (!stream->require(NAL_unit_length + 1)) { ret = ERROR_HLS_DECODE_ERROR; - srs_error("hls decode video avc sequenc header pps failed. ret=%d", ret); + srs_error("hls decode video avc NALU size failed. ret=%d", ret); return ret; } - if (!stream->require(2)) { + int32_t NALUnitLength = 0; + if (NAL_unit_length == 3) { + NALUnitLength = stream->read_4bytes(); + } else if (NAL_unit_length == 1) { + NALUnitLength = stream->read_2bytes(); + } else { + NALUnitLength = stream->read_1bytes(); + } + + // maybe stream is invalid format. + // see: https://github.com/winlinvip/simple-rtmp-server/issues/183 + if (NALUnitLength < 0) { ret = ERROR_HLS_DECODE_ERROR; - srs_error("hls decode video avc sequenc header pps size failed. ret=%d", ret); + srs_error("maybe stream is AnnexB format. ret=%d", ret); return ret; } - pictureParameterSetLength = stream->read_2bytes(); - if (!stream->require(pictureParameterSetLength)) { + + // NALUnit + if (!stream->require(NALUnitLength)) { ret = ERROR_HLS_DECODE_ERROR; - srs_error("hls decode video avc sequenc header pps data failed. ret=%d", ret); + srs_error("hls decode video avc NALU data failed. ret=%d", ret); return ret; } - if (pictureParameterSetLength > 0) { - srs_freep(pictureParameterSetNALUnit); - pictureParameterSetNALUnit = new char[pictureParameterSetLength]; - memcpy(pictureParameterSetNALUnit, stream->data() + stream->pos(), pictureParameterSetLength); - stream->skip(pictureParameterSetLength); - } - } else if (avc_packet_type == SrsCodecVideoAVCTypeNALU){ - // ensure the sequence header demuxed - if (avc_extra_size <= 0 || !avc_extra_data) { - ret = ERROR_HLS_DECODE_ERROR; - srs_error("hls decode video avc failed, sequence header not found. ret=%d", ret); + // 7.3.1 NAL unit syntax, H.264-AVC-ISO_IEC_14496-10.pdf, page 44. + if ((ret = sample->add_sample_unit(stream->data() + stream->pos(), NALUnitLength)) != ERROR_SUCCESS) { + srs_error("hls add video sample failed. ret=%d", ret); return ret; } + stream->skip(NALUnitLength); - // One or more NALUs (Full frames are required) - // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 20 - int PictureLength = stream->size() - stream->pos(); - for (int i = 0; i < PictureLength;) { - if (!stream->require(NAL_unit_length + 1)) { - ret = ERROR_HLS_DECODE_ERROR; - srs_error("hls decode video avc NALU size failed. ret=%d", ret); - return ret; - } - int32_t NALUnitLength = 0; - if (NAL_unit_length == 3) { - NALUnitLength = stream->read_4bytes(); - } else if (NAL_unit_length == 2) { - NALUnitLength = stream->read_3bytes(); - } else if (NAL_unit_length == 1) { - NALUnitLength = stream->read_2bytes(); - } else { - NALUnitLength = stream->read_1bytes(); - } - // NALUnit - if (!stream->require(NALUnitLength)) { - ret = ERROR_HLS_DECODE_ERROR; - srs_error("hls decode video avc NALU data failed. ret=%d", ret); - return ret; - } - // 7.3.1 NAL unit syntax, H.264-AVC-ISO_IEC_14496-10.pdf, page 44. - if ((ret = sample->add_sample_unit(stream->data() + stream->pos(), NALUnitLength)) != ERROR_SUCCESS) { - srs_error("hls add video sample failed. ret=%d", ret); - return ret; - } - stream->skip(NALUnitLength); - - i += NAL_unit_length + 1 + NALUnitLength; - } - } else { - // ignored. + i += NAL_unit_length + 1 + NALUnitLength; } - srs_info("video decoded, type=%d, codec=%d, avc=%d, time=%d, size=%d", - frame_type, video_codec_id, avc_packet_type, composition_time, size); - return ret; } diff --git a/trunk/src/app/srs_app_avc_aac.hpp b/trunk/src/app/srs_app_avc_aac.hpp index 1e0e42fe1a..96b13e6f43 100644 --- a/trunk/src/app/srs_app_avc_aac.hpp +++ b/trunk/src/app/srs_app_avc_aac.hpp @@ -278,6 +278,22 @@ class SrsAvcAacCodec * demux the h.264 NALUs to sampe units. */ virtual int video_avc_demux(char* data, int size, SrsCodecSample* sample); +private: + /** + * when avc packet type is SrsCodecVideoAVCTypeSequenceHeader, + * decode the sps and pps. + */ + virtual int avc_demux_sps_pps(SrsStream* stream); + /** + * demux the avc NALU in "AnnexB" + * from H.264-AVC-ISO_IEC_14496-10.pdf, page 211. + */ + virtual int avc_demux_annexb_format(SrsStream* stream, SrsCodecSample* sample); + /** + * demux the avc NALU in "ISO Base Media File Format" + * from H.264-AVC-ISO_IEC_14496-15.pdf, page 20 + */ + virtual int avc_demux_ibmf_format(SrsStream* stream, SrsCodecSample* sample); }; #endif diff --git a/trunk/src/app/srs_app_utility.cpp b/trunk/src/app/srs_app_utility.cpp index b4d910f9ef..c11716e750 100644 --- a/trunk/src/app/srs_app_utility.cpp +++ b/trunk/src/app/srs_app_utility.cpp @@ -36,8 +36,9 @@ using namespace std; #include #include #include +#include -int srs_socket_connect(std::string server, int port, int64_t timeout, st_netfd_t* pstfd) +int srs_socket_connect(string server, int port, int64_t timeout, st_netfd_t* pstfd) { int ret = ERROR_SUCCESS; @@ -89,7 +90,7 @@ int srs_socket_connect(std::string server, int port, int64_t timeout, st_netfd_t return ret; } -int srs_get_log_level(std::string level) +int srs_get_log_level(string level) { if ("verbose" == level) { return SrsLogLevel::Verbose; @@ -106,6 +107,35 @@ int srs_get_log_level(std::string level) } } +bool srs_avc_startswith_annexb(SrsStream* stream, int* pnb_start_code) +{ + char* bytes = stream->data() + stream->pos(); + char* p = bytes; + + for (;;) { + if (!stream->require(p - bytes + 3)) { + return false; + } + + // not match + if (p[0] != 0x00 || p[1] != 0x00) { + return false; + } + + // match N[00] 00 00 01, where N>=0 + if (p[2] == 0x01) { + if (pnb_start_code) { + *pnb_start_code = (int)(p - bytes) + 3; + } + return true; + } + + p++; + } + + return false; +} + static SrsRusage _srs_system_rusage; SrsRusage::SrsRusage() diff --git a/trunk/src/app/srs_app_utility.hpp b/trunk/src/app/srs_app_utility.hpp index cd05b5959b..a310483b6c 100644 --- a/trunk/src/app/srs_app_utility.hpp +++ b/trunk/src/app/srs_app_utility.hpp @@ -39,6 +39,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include class SrsKbps; +class SrsStream; // client open socket and connect to server. extern int srs_socket_connect(std::string server, int port, int64_t timeout, st_netfd_t* pstfd); @@ -49,6 +50,15 @@ extern int srs_socket_connect(std::string server, int port, int64_t timeout, st_ */ extern int srs_get_log_level(std::string level); +/** +* whether stream starts with the avc NALU in "AnnexB" +* from H.264-AVC-ISO_IEC_14496-10.pdf, page 211. +* start code must be "N[00] 00 00 01" where N>=0 +* @param pnb_start_code output the size of start code, must >=3. +* NULL to ignore. +*/ +extern bool srs_avc_startswith_annexb(SrsStream* stream, int* pnb_start_code = NULL); + // current process resouce usage. // @see: man getrusage class SrsRusage diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 47f56b1032..0a50dc91a8 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR "2" #define VERSION_MINOR "0" -#define VERSION_REVISION "1" +#define VERSION_REVISION "2" #define RTMP_SIG_SRS_VERSION VERSION_MAJOR"."VERSION_MINOR"."VERSION_REVISION // server info. #define RTMP_SIG_SRS_KEY "SRS" diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index 27218bcac5..4eb2f3426f 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -183,6 +183,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define ERROR_KERNEL_FLV_STREAM_CLOSED 3037 #define ERROR_KERNEL_STREAM_INIT 3038 #define ERROR_EDGE_VHOST_REMOVED 3039 +#define ERROR_HLS_AVC_TRY_OTHERS 3040 /** * whether the error code is an system control error. From 616d1cb2b3df0a1da0811d263979e8fd6693658b Mon Sep 17 00:00:00 2001 From: winlin Date: Mon, 20 Oct 2014 09:52:19 +0800 Subject: [PATCH 027/800] update readme. --- README.md | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index eefee9a43c..e6c7f03d27 100755 --- a/README.md +++ b/README.md @@ -19,18 +19,33 @@ Download from ossrs.net: SRS(SIMPLE RTMP Server) over state-threads created in 2013.10. -SRS is a simple, [RTMP](https://github.com/winlinvip/simple-rtmp-server/wiki/DeliveryRTMP)/[HLS](https://github.com/winlinvip/simple-rtmp-server/wiki/DeliveryHLS), -[high-performance](https://github.com/winlinvip/simple-rtmp-server/wiki/Performance), single/multiple(plan) processes, edge/origin live server, +SRS focus on small problem domain, which is the most complex for all software(see OOAD). +Because of lack of deveoper resource, SRS only provides features which is the most popular +for internet. SRS is simple for and only for problem domain is simplified. + +SRS is a simple, [RTMP](https://github.com/winlinvip/simple-rtmp-server/wiki/DeliveryRTMP)/ +[HLS](https://github.com/winlinvip/simple-rtmp-server/wiki/DeliveryHLS), +[high-performance](https://github.com/winlinvip/simple-rtmp-server/wiki/Performance), +single/multiple(plan) processes, edge/origin live server, [x86/x64/arm](https://github.com/winlinvip/simple-rtmp-server/wiki/SrsLinuxArm), -compile depends on [st](http://sourceforge.net/projects/state-threads)(required), [ssl](http://www.openssl.org/) and [http-parser](https://github.com/joyent/http-parser), -use [nginx](http://nginx.org/), [ffmpeg](http://ffmpeg.org/) and [cherrypy](http://www.cherrypy.org/) as external tools. that is, only need st to run srs for +compile depends on [st](http://sourceforge.net/projects/state-threads)(required), +[ssl](http://www.openssl.org/) and [http-parser](https://github.com/joyent/http-parser), +use [nginx](http://nginx.org/), [ffmpeg](http://ffmpeg.org/) and +[cherrypy](http://www.cherrypy.org/) as external tools. that is, only need st to run srs for minimum run. see [Build](https://github.com/winlinvip/simple-rtmp-server/wiki/Build). SRS supports [vhost](https://github.com/winlinvip/simple-rtmp-server/wiki/RtmpUrlVhost), -rtmp([encoder push](https://github.com/winlinvip/simple-rtmp-server/wiki/DeliveryRTMP), client/[edge](https://github.com/winlinvip/simple-rtmp-server/wiki/Edge) pull), [ingester(srs pull)](https://github.com/winlinvip/simple-rtmp-server/wiki/Ingest), -[HLS](https://github.com/winlinvip/simple-rtmp-server/wiki/DeliveryHLS), [HLS audio only](https://github.com/winlinvip/simple-rtmp-server/wiki/DeliveryHLS#hlsaudioonly), [transcoding](https://github.com/winlinvip/simple-rtmp-server/wiki/FFMPEG), -[forward](https://github.com/winlinvip/simple-rtmp-server/wiki/FFMPEG), [http hooks](https://github.com/winlinvip/simple-rtmp-server/wiki/HTTPCallback), [http api](https://github.com/winlinvip/simple-rtmp-server/wiki/HTTPApi), -[http server](https://github.com/winlinvip/simple-rtmp-server/wiki/HTTPServer), [dvr](https://github.com/winlinvip/simple-rtmp-server/wiki/DVR). +rtmp([encoder push](https://github.com/winlinvip/simple-rtmp-server/wiki/DeliveryRTMP), +client/[edge](https://github.com/winlinvip/simple-rtmp-server/wiki/Edge) pull), +[ingester(srs pull)](https://github.com/winlinvip/simple-rtmp-server/wiki/Ingest), +[HLS](https://github.com/winlinvip/simple-rtmp-server/wiki/DeliveryHLS), +[HLS audio only](https://github.com/winlinvip/simple-rtmp-server/wiki/DeliveryHLS#hlsaudioonly), +[transcoding](https://github.com/winlinvip/simple-rtmp-server/wiki/FFMPEG), +[forward](https://github.com/winlinvip/simple-rtmp-server/wiki/FFMPEG), +[http hooks](https://github.com/winlinvip/simple-rtmp-server/wiki/HTTPCallback), +[http api](https://github.com/winlinvip/simple-rtmp-server/wiki/HTTPApi), +[http server](https://github.com/winlinvip/simple-rtmp-server/wiki/HTTPServer), +[dvr](https://github.com/winlinvip/simple-rtmp-server/wiki/DVR). WebSite: [http://ossrs.net](http://ossrs.net)
Release: [http://winlinvip.github.io/srs.release](http://winlinvip.github.io/srs.release)
@@ -39,7 +54,10 @@ CSDN mirror: [https://code.csdn.net/winlinvip/srs-csdn](https://code.csdn.net/wi See also: [https://github.com/winlinvip/simple-rtmp-server](https://github.com/winlinvip/simple-rtmp-server)
Github DEMO: [demo with your SRS](http://winlinvip.github.io/srs.release/trunk/research/players/srs_player.html?server=192.168.1.170&vhost=192.168.1.170)
Wiki: [https://github.com/winlinvip/simple-rtmp-server/wiki](https://github.com/winlinvip/simple-rtmp-server/wiki)
-StreamServers:[BLS](https://github.com/wenjiegit/Bull-Live-Server)/[BLE](https://github.com/wenjiegit/Bull-Live-Encoder), [NGINX-RTMP](https://github.com/arut/nginx-rtmp-module), [CRTMPD](http://www.rtmpd.com/), [RED5](http://www.red5.org/), [WOWZA](http://www.wowza.com/), [FMS/AMS](http://www.adobe.com/products/adobe-media-server-standard.html) +StreamServers:[BLS](https://github.com/wenjiegit/Bull-Live-Server)/[BLE](https://github.com/wenjiegit/Bull-Live-Encoder), +[NGINX-RTMP](https://github.com/arut/nginx-rtmp-module), [CRTMPD](http://www.rtmpd.com/), +[RED5](http://www.red5.org/), [WOWZA](http://www.wowza.com/), +[FMS/AMS](http://www.adobe.com/products/adobe-media-server-standard.html) ## AUTHORS The PRIMARY AUTHORS are (and/or have been)(Authors ordered by first contribution): @@ -53,7 +71,8 @@ About the primary AUTHORS: And here is an inevitably incomplete list of MUCH-APPRECIATED CONTRIBUTORS -- people who have submitted patches, reported bugs, added translations, helped -answer newbie questions, and generally made SRS that much better: [AUTHORS.txt](https://github.com/winlinvip/simple-rtmp-server/blob/master/AUTHORS.txt) +answer newbie questions, and generally made SRS that much better: +[AUTHORS.txt](https://github.com/winlinvip/simple-rtmp-server/blob/master/AUTHORS.txt) A big THANK YOU goes to: * [chnvideo](chnvideo.com) co-founders([wiseyoung](mailto:wiseyoung@chnvideo.com), [trueice](mailto:trueice@chnvideo.com), [leijian](mailto:leijian@chnvideo.com)) for [big supports](https://github.com/winlinvip/simple-rtmp-server/wiki/Product#bigthanks). From abe2779a37fe8cf3037f361a09a6058262fe9381 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 21 Oct 2014 09:46:40 +0800 Subject: [PATCH 028/800] update readme. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e6c7f03d27..d5ba06d0e2 100755 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ SRS is industrial-strength live streaming cluster, for the best conceptual integrity and the simplest implementation, -which delivering rtmp/hls/http live/vod on x86/x64/arm/mips linux, +which delivering rtmp/hls/http live on x86/x64/arm/mips linux, supports origin/edge/vhost and transcode/ingest and dvr/forward and http-api/http-callback/reload, with wiki and the most simple architecture. From b9b345ec37629d48c980b0ee3d7445737011fe69 Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 24 Oct 2014 09:44:05 +0800 Subject: [PATCH 029/800] update readme, ENHome_v1 to v1_ENHome --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d5ba06d0e2..c5cb622ad1 100755 --- a/README.md +++ b/README.md @@ -192,7 +192,7 @@ Supported operating systems and hardware: 1. Support system full utest on gtest. 1. [experiment] Support embeded [HTTP server](https://github.com/winlinvip/simple-rtmp-server/wiki/SampleHTTP) for hls(live/vod) 1. [experiment] Support [vod stream(http flv/hls vod stream)](https://github.com/winlinvip/simple-rtmp-server/wiki/FlvVodStream). -1. [dev] Suppport [English wiki](https://github.com/winlinvip/simple-rtmp-server/wiki/ENHome_v1). +1. [dev] Suppport [English wiki](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_ENHome). 1. [dev] Research and simplify st, [bug #182](https://github.com/winlinvip/simple-rtmp-server/issues/182). 1. [no-plan] Support <500ms latency, FRSC(Fast RTMP-compatible Stream Channel tech). 1. [no-plan] Support RTMP 302 redirect [#92](https://github.com/winlinvip/simple-rtmp-server/issues/92). From e46e7fc596db7c92c4c00aae8ab9093ac8e36dee Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 24 Oct 2014 09:58:53 +0800 Subject: [PATCH 030/800] update readme, rename wiki/xxx to wiki/v1_xxx --- README.md | 192 +++++++++++------------ trunk/CMakeLists.txt | 52 +++--- trunk/auto/depends.sh | 2 +- trunk/auto/options.sh | 4 +- trunk/conf/demo.19350.conf | 2 +- trunk/conf/demo.conf | 2 +- trunk/conf/dvr.segment.conf | 2 +- trunk/conf/dvr.session.conf | 2 +- trunk/conf/edge.conf | 2 +- trunk/conf/edge.token.traverse.conf | 2 +- trunk/conf/ffmpeg.transcode.conf | 2 +- trunk/conf/forward.master.conf | 2 +- trunk/conf/forward.slave.conf | 2 +- trunk/conf/full.conf | 2 +- trunk/conf/hls.conf | 2 +- trunk/conf/http.hls.conf | 2 +- trunk/conf/ingest.conf | 2 +- trunk/conf/origin.conf | 2 +- trunk/conf/realtime.conf | 2 +- trunk/conf/rtmp.conf | 2 +- trunk/conf/transcode2hls.audio.only.conf | 2 +- trunk/configure | 32 ++-- trunk/research/arm/jmp.cpp | 2 +- trunk/research/arm/jmp_sp.cpp | 2 +- trunk/research/arm/test.cpp | 2 +- trunk/research/librtmp/Makefile | 2 +- trunk/scripts/install.sh | 2 +- trunk/scripts/package.sh | 2 +- trunk/scripts/run.sh | 2 +- trunk/src/app/srs_app_config.cpp | 2 +- trunk/src/app/srs_app_reload.hpp | 2 +- trunk/src/main/srs_main_server.cpp | 2 +- trunk/src/utest/srs_utest_config.cpp | 2 +- 33 files changed, 169 insertions(+), 169 deletions(-) diff --git a/README.md b/README.md index c5cb622ad1..b53d5622f2 100755 --- a/README.md +++ b/README.md @@ -23,29 +23,29 @@ SRS focus on small problem domain, which is the most complex for all software(se Because of lack of deveoper resource, SRS only provides features which is the most popular for internet. SRS is simple for and only for problem domain is simplified. -SRS is a simple, [RTMP](https://github.com/winlinvip/simple-rtmp-server/wiki/DeliveryRTMP)/ -[HLS](https://github.com/winlinvip/simple-rtmp-server/wiki/DeliveryHLS), -[high-performance](https://github.com/winlinvip/simple-rtmp-server/wiki/Performance), +SRS is a simple, [RTMP](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DeliveryRTMP)/ +[HLS](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DeliveryHLS), +[high-performance](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Performance), single/multiple(plan) processes, edge/origin live server, -[x86/x64/arm](https://github.com/winlinvip/simple-rtmp-server/wiki/SrsLinuxArm), +[x86/x64/arm](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLinuxArm), compile depends on [st](http://sourceforge.net/projects/state-threads)(required), [ssl](http://www.openssl.org/) and [http-parser](https://github.com/joyent/http-parser), use [nginx](http://nginx.org/), [ffmpeg](http://ffmpeg.org/) and [cherrypy](http://www.cherrypy.org/) as external tools. that is, only need st to run srs for -minimum run. see [Build](https://github.com/winlinvip/simple-rtmp-server/wiki/Build). - -SRS supports [vhost](https://github.com/winlinvip/simple-rtmp-server/wiki/RtmpUrlVhost), -rtmp([encoder push](https://github.com/winlinvip/simple-rtmp-server/wiki/DeliveryRTMP), -client/[edge](https://github.com/winlinvip/simple-rtmp-server/wiki/Edge) pull), -[ingester(srs pull)](https://github.com/winlinvip/simple-rtmp-server/wiki/Ingest), -[HLS](https://github.com/winlinvip/simple-rtmp-server/wiki/DeliveryHLS), -[HLS audio only](https://github.com/winlinvip/simple-rtmp-server/wiki/DeliveryHLS#hlsaudioonly), -[transcoding](https://github.com/winlinvip/simple-rtmp-server/wiki/FFMPEG), -[forward](https://github.com/winlinvip/simple-rtmp-server/wiki/FFMPEG), -[http hooks](https://github.com/winlinvip/simple-rtmp-server/wiki/HTTPCallback), -[http api](https://github.com/winlinvip/simple-rtmp-server/wiki/HTTPApi), -[http server](https://github.com/winlinvip/simple-rtmp-server/wiki/HTTPServer), -[dvr](https://github.com/winlinvip/simple-rtmp-server/wiki/DVR). +minimum run. see [Build](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Build). + +SRS supports [vhost](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_RtmpUrlVhost), +rtmp([encoder push](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DeliveryRTMP), +client/[edge](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Edge) pull), +[ingester(srs pull)](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Ingest), +[HLS](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DeliveryHLS), +[HLS audio only](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DeliveryHLS#hlsaudioonly), +[transcoding](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_FFMPEG), +[forward](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_FFMPEG), +[http hooks](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HTTPCallback), +[http api](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HTTPApi), +[http server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HTTPServer), +[dvr](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DVR). WebSite: [http://ossrs.net](http://ossrs.net)
Release: [http://winlinvip.github.io/srs.release](http://winlinvip.github.io/srs.release)
@@ -75,7 +75,7 @@ answer newbie questions, and generally made SRS that much better: [AUTHORS.txt](https://github.com/winlinvip/simple-rtmp-server/blob/master/AUTHORS.txt) A big THANK YOU goes to: -* [chnvideo](chnvideo.com) co-founders([wiseyoung](mailto:wiseyoung@chnvideo.com), [trueice](mailto:trueice@chnvideo.com), [leijian](mailto:leijian@chnvideo.com)) for [big supports](https://github.com/winlinvip/simple-rtmp-server/wiki/Product#bigthanks). +* [chnvideo](chnvideo.com) co-founders([wiseyoung](mailto:wiseyoung@chnvideo.com), [trueice](mailto:trueice@chnvideo.com), [leijian](mailto:leijian@chnvideo.com)) for [big supports](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Product#bigthanks). * Genes amd Mabbott for creating [st](https://github.com/winlinvip/state-threads)([state-threads](http://sourceforge.net/projects/state-threads/)). * Michael Talyanksy for introducing us to use st. * Roman Arutyunyan for creating [nginx-rtmp](https://github.com/arut/nginx-rtmp-module) for SRS to refer to. @@ -94,7 +94,7 @@ cd simple-rtmp-server/trunk Step 2: build SRS, -Requires Centos6.x/Ubuntu12 32/64bits, others see [Build](https://github.com/winlinvip/simple-rtmp-server/wiki/Build) +Requires Centos6.x/Ubuntu12 32/64bits, others see [Build](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Build)
 ./configure && make
@@ -107,24 +107,24 @@ cd simple-rtmp-server/trunk
 
See also: -* [Usage: How to delivery RTMP?](https://github.com/winlinvip/simple-rtmp-server/wiki/SampleRTMP) -* [Usage: How to delivery HLS?](https://github.com/winlinvip/simple-rtmp-server/wiki/SampleHLS) -* [Usage: How to delivery HLS for other codec?](https://github.com/winlinvip/simple-rtmp-server/wiki/SampleTranscode2HLS) -* [Usage: How to transode RTMP stream by SRS?](https://github.com/winlinvip/simple-rtmp-server/wiki/SampleFFMPEG) -* [Usage: How to forward stream to other server?](https://github.com/winlinvip/simple-rtmp-server/wiki/SampleForward) -* [Usage: How to deploy low lantency application?](https://github.com/winlinvip/simple-rtmp-server/wiki/SampleRealtime) -* [Usage: How to deploy SRS on ARM?](https://github.com/winlinvip/simple-rtmp-server/wiki/SampleARM) -* [Usage: How to ingest file/stream/device to SRS?](https://github.com/winlinvip/simple-rtmp-server/wiki/SampleIngest) -* [Usage: How to use SRS-HTTP-server to delivery HTTP/HLS stream?](https://github.com/winlinvip/simple-rtmp-server/wiki/SampleHTTP) -* [Usage: How to show the demo of SRS?](https://github.com/winlinvip/simple-rtmp-server/wiki/SampleDemo) -* [Usage: Solution using SRS?](https://github.com/winlinvip/simple-rtmp-server/wiki/Sample) -* [Usage: Why SRS?](https://github.com/winlinvip/simple-rtmp-server/wiki/Product) +* [Usage: How to delivery RTMP?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleRTMP) +* [Usage: How to delivery HLS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleHLS) +* [Usage: How to delivery HLS for other codec?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleTranscode2HLS) +* [Usage: How to transode RTMP stream by SRS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleFFMPEG) +* [Usage: How to forward stream to other server?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleForward) +* [Usage: How to deploy low lantency application?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleRealtime) +* [Usage: How to deploy SRS on ARM?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleARM) +* [Usage: How to ingest file/stream/device to SRS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleIngest) +* [Usage: How to use SRS-HTTP-server to delivery HTTP/HLS stream?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleHTTP) +* [Usage: How to show the demo of SRS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleDemo) +* [Usage: Solution using SRS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Sample) +* [Usage: Why SRS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Product) ## Wiki Please select your language: -* [English](https://github.com/winlinvip/simple-rtmp-server/wiki/ENHome) -* [Chinese](https://github.com/winlinvip/simple-rtmp-server/wiki/CNHome) +* [English](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_ENHome) +* [Chinese](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CNHome) ## Donation @@ -138,14 +138,14 @@ Donations:
## Mirrors Github: [https://github.com/winlinvip/simple-rtmp-server](https://github.com/winlinvip/simple-rtmp-server) -[Git Usage](https://github.com/winlinvip/simple-rtmp-server/wiki/Git) +[Git Usage](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Git) ```bash git clone https://github.com/winlinvip/simple-rtmp-server.git ``` CSDN: [https://code.csdn.net/winlinvip/srs-csdn](https://code.csdn.net/winlinvip/srs-csdn) -[Git Usage](https://github.com/winlinvip/simple-rtmp-server/wiki/Git) +[Git Usage](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Git) ```bash git clone https://code.csdn.net/winlinvip/srs-csdn.git @@ -158,41 +158,41 @@ Supported operating systems and hardware: ## Summary 1. Simple, also stable enough. -1. [High-performance](https://github.com/winlinvip/simple-rtmp-server/wiki/Performance): single-thread, async socket, event/st-thread driven. -1. [High-concurrency](https://github.com/winlinvip/simple-rtmp-server/wiki/Performance), 1800 connections(500kbps), 900Mbps, CPU 90.2%, 41MB -1. Support [RTMP Origin Server](https://github.com/winlinvip/simple-rtmp-server/wiki/DeliveryRTMP). -1. Support [RTMP Edge Server](https://github.com/winlinvip/simple-rtmp-server/wiki/Edge) for CDN, push/pull stream from any RTMP server +1. [High-performance](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Performance): single-thread, async socket, event/st-thread driven. +1. [High-concurrency](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Performance), 1800 connections(500kbps), 900Mbps, CPU 90.2%, 41MB +1. Support [RTMP Origin Server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DeliveryRTMP). +1. Support [RTMP Edge Server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Edge) for CDN, push/pull stream from any RTMP server 1. Support single process; no multiple processes. -1. Support [Vhost](https://github.com/winlinvip/simple-rtmp-server/wiki/RtmpUrlVhost), support \_\_defaultVhost\_\_. -1. Support [RTMP](https://github.com/winlinvip/simple-rtmp-server/wiki/DeliveryRTMP) live streaming; no vod streaming. -1. Support Apple [HLS(m3u8)](https://github.com/winlinvip/simple-rtmp-server/wiki/DeliveryHLS) live streaming. -1. Support [HLS audio-only](https://github.com/winlinvip/simple-rtmp-server/wiki/DeliveryHLS#hlsaudioonly) live streaming. -1. Support [Reload](https://github.com/winlinvip/simple-rtmp-server/wiki/Reload) config to enable changes. -1. Support [cache last gop](https://github.com/winlinvip/simple-rtmp-server/wiki/LowLatency#gop-cache) for flash player to fast startup. +1. Support [Vhost](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_RtmpUrlVhost), support \_\_defaultVhost\_\_. +1. Support [RTMP](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DeliveryRTMP) live streaming; no vod streaming. +1. Support Apple [HLS(m3u8)](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DeliveryHLS) live streaming. +1. Support [HLS audio-only](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DeliveryHLS#hlsaudioonly) live streaming. +1. Support [Reload](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Reload) config to enable changes. +1. Support [cache last gop](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_LowLatency#gop-cache) for flash player to fast startup. 1. Support listen at multiple ports. 1. Support long time(>4.6hours) publish/play. -1. Support [Forward](https://github.com/winlinvip/simple-rtmp-server/wiki/Forward) in master-slave mode. -1. Support live stream [Transcoding](https://github.com/winlinvip/simple-rtmp-server/wiki/FFMPEG) by ffmpeg. -1. Support [ffmpeg](https://github.com/winlinvip/simple-rtmp-server/wiki/FFMPEG) filters(logo/overlay/crop), x264 params, copy/vn/an. -1. Support audio [transcode](https://github.com/winlinvip/simple-rtmp-server/wiki/FFMPEG) only, speex/mp3 to aac -1. Support [http callback api hooks](https://github.com/winlinvip/simple-rtmp-server/wiki/HTTPCallback)(for authentication and injection). -1. Support [bandwidth test](https://github.com/winlinvip/simple-rtmp-server/wiki/BandwidthTestTool) api and flash client. -1. Player, publisher(encoder), and [demo pages(jquery+bootstrap)](https://github.com/winlinvip/simple-rtmp-server/wiki/SampleDemo). -1. [Demo](https://github.com/winlinvip/simple-rtmp-server/wiki/SampleDemo) video meeting or chat(SRS+cherrypy+jquery+bootstrap). -1. Full documents in [wiki](https://github.com/winlinvip/simple-rtmp-server/wiki/CNHome), in Chineses. -1. Support RTMP(play-publish) library: [srs-librtmp](https://github.com/winlinvip/simple-rtmp-server/wiki/SrsLibrtmp) -1. Support ARM([debian armhf, v7cpu](https://github.com/winlinvip/simple-rtmp-server/wiki/SrsLinuxArm)) with rtmp/ssl/hls/librtmp. -1. Support [init.d](https://github.com/winlinvip/simple-rtmp-server/wiki/LinuxService) and packge script, log to file. -1. Support [RTMP ATC](https://github.com/winlinvip/simple-rtmp-server/wiki/RTMP-ATC) for HLS/HDS to support backup(failover) -1. Support [HTTP RESTful management api](https://github.com/winlinvip/simple-rtmp-server/wiki/HTTPApi). -1. Support [Ingest](https://github.com/winlinvip/simple-rtmp-server/wiki/Ingest) FILE/HTTP/RTMP/RTSP(RTP, SDP) to RTMP using external tools(e.g ffmepg). -1. Support [DVR](https://github.com/winlinvip/simple-rtmp-server/wiki/DVR), record live to flv file for vod. -1. Support [tracable log, session based log](https://github.com/winlinvip/simple-rtmp-server/wiki/SrsLog). -1. Support DRM [token traverse](https://github.com/winlinvip/simple-rtmp-server/wiki/DRM#tokentraverse) for fms origin authenticate. +1. Support [Forward](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Forward) in master-slave mode. +1. Support live stream [Transcoding](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_FFMPEG) by ffmpeg. +1. Support [ffmpeg](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_FFMPEG) filters(logo/overlay/crop), x264 params, copy/vn/an. +1. Support audio [transcode](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_FFMPEG) only, speex/mp3 to aac +1. Support [http callback api hooks](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HTTPCallback)(for authentication and injection). +1. Support [bandwidth test](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_BandwidthTestTool) api and flash client. +1. Player, publisher(encoder), and [demo pages(jquery+bootstrap)](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleDemo). +1. [Demo](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleDemo) video meeting or chat(SRS+cherrypy+jquery+bootstrap). +1. Full documents in [wiki](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CNHome), in Chineses. +1. Support RTMP(play-publish) library: [srs-librtmp](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLibrtmp) +1. Support ARM([debian armhf, v7cpu](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLinuxArm)) with rtmp/ssl/hls/librtmp. +1. Support [init.d](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_LinuxService) and packge script, log to file. +1. Support [RTMP ATC](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_RTMP-ATC) for HLS/HDS to support backup(failover) +1. Support [HTTP RESTful management api](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HTTPApi). +1. Support [Ingest](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Ingest) FILE/HTTP/RTMP/RTSP(RTP, SDP) to RTMP using external tools(e.g ffmepg). +1. Support [DVR](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DVR), record live to flv file for vod. +1. Support [tracable log, session based log](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLog). +1. Support DRM [token traverse](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DRM#tokentraverse) for fms origin authenticate. 1. Support system full utest on gtest. -1. [experiment] Support embeded [HTTP server](https://github.com/winlinvip/simple-rtmp-server/wiki/SampleHTTP) for hls(live/vod) -1. [experiment] Support [vod stream(http flv/hls vod stream)](https://github.com/winlinvip/simple-rtmp-server/wiki/FlvVodStream). -1. [dev] Suppport [English wiki](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_ENHome). +1. [experiment] Support embeded [HTTP server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleHTTP) for hls(live/vod) +1. [experiment] Support [vod stream(http flv/hls vod stream)](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_FlvVodStream). +1. [dev] Suppport [English wiki](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_v1_ENHome). 1. [dev] Research and simplify st, [bug #182](https://github.com/winlinvip/simple-rtmp-server/issues/182). 1. [no-plan] Support <500ms latency, FRSC(Fast RTMP-compatible Stream Channel tech). 1. [no-plan] Support RTMP 302 redirect [#92](https://github.com/winlinvip/simple-rtmp-server/issues/92). @@ -207,16 +207,16 @@ Supported operating systems and hardware: ## Releases * 2014-10-09, [Release v1.0-beta](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.beta), all bug fixed, 1.0.0, 59316 lines.
-* 2014-04-07, [Release v1.0-mainline](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline), support [arm](https://github.com/winlinvip/simple-rtmp-server/wiki/SrsLinuxArm), [init.d](https://github.com/winlinvip/simple-rtmp-server/wiki/LinuxService), http [server](https://github.com/winlinvip/simple-rtmp-server/wiki/HTTPServer)/[api](https://github.com/winlinvip/simple-rtmp-server/wiki/HTTPApi), [ingest](https://github.com/winlinvip/simple-rtmp-server/wiki/SampleIngest). 30000 lines.
-* 2013-12-25, [Release v0.9](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.9), support bandwidth test, player/encoder/chat [demos](https://github.com/winlinvip/simple-rtmp-server/wiki/SampleDemo). 20926 lines.
-* 2013-12-08, [Release v0.8](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.8), support [http hooks callback](https://github.com/winlinvip/simple-rtmp-server/wiki/HTTPCallback), update [st_load](https://github.com/winlinvip/st-load). 19186 lines.
-* 2013-12-03, [Release v0.7](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.7), support [live stream transcoding](https://github.com/winlinvip/simple-rtmp-server/wiki/FFMPEG). 17605 lines.
-* 2013-11-29, [Release v0.6](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.6), support [forward](https://github.com/winlinvip/simple-rtmp-server/wiki/Forward) stream to origin/edge. 16094 lines.
-* 2013-11-26, [Release v0.5](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.5), support [HLS(m3u8)](https://github.com/winlinvip/simple-rtmp-server/wiki/DeliveryHLS), fragment and window. 14449 lines.
-* 2013-11-10, [Release v0.4](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.4), support [reload](https://github.com/winlinvip/simple-rtmp-server/wiki/Reload) config, pause, longtime publish/play. 12500 lines.
-* 2013-11-04, [Release v0.3](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.3), support [vhost](https://github.com/winlinvip/simple-rtmp-server/wiki/RtmpUrlVhost), refer, gop cache, listen multiple ports. 11773 lines.
-* 2013-10-25, [Release v0.2](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.2), support [rtmp](https://github.com/winlinvip/simple-rtmp-server/wiki/RTMPHandshake) flash publish, h264, time jitter correct. 10125 lines.
-* 2013-10-23, [Release v0.1](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.1), support [rtmp FMLE/FFMPEG publish](https://github.com/winlinvip/simple-rtmp-server/wiki/DeliveryRTMP), vp6. 8287 lines.
+* 2014-04-07, [Release v1.0-mainline](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline), support [arm](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLinuxArm), [init.d](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_LinuxService), http [server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HTTPServer)/[api](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HTTPApi), [ingest](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleIngest). 30000 lines.
+* 2013-12-25, [Release v0.9](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.9), support bandwidth test, player/encoder/chat [demos](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleDemo). 20926 lines.
+* 2013-12-08, [Release v0.8](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.8), support [http hooks callback](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HTTPCallback), update [st_load](https://github.com/winlinvip/st-load). 19186 lines.
+* 2013-12-03, [Release v0.7](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.7), support [live stream transcoding](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_FFMPEG). 17605 lines.
+* 2013-11-29, [Release v0.6](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.6), support [forward](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Forward) stream to origin/edge. 16094 lines.
+* 2013-11-26, [Release v0.5](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.5), support [HLS(m3u8)](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DeliveryHLS), fragment and window. 14449 lines.
+* 2013-11-10, [Release v0.4](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.4), support [reload](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Reload) config, pause, longtime publish/play. 12500 lines.
+* 2013-11-04, [Release v0.3](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.3), support [vhost](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_RtmpUrlVhost), refer, gop cache, listen multiple ports. 11773 lines.
+* 2013-10-25, [Release v0.2](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.2), support [rtmp](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_RTMPHandshake) flash publish, h264, time jitter correct. 10125 lines.
+* 2013-10-23, [Release v0.1](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.1), support [rtmp FMLE/FFMPEG publish](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DeliveryRTMP), vp6. 8287 lines.
* 2013-10-17, Created.
## History @@ -266,8 +266,8 @@ Supported operating systems and hardware: * v1.0, 2014-06-27, SRS online 30days with RTMP/HLS. * v1.0, 2014-06-25, fix [#108](https://github.com/winlinvip/simple-rtmp-server/issues/108), support config time jitter for encoder non-monotonical stream. 0.9.133 * v1.0, 2014-06-23, support report summaries in heartbeat. 0.9.132 -* v1.0, 2014-06-22, performance refine, support [3k+](https://github.com/winlinvip/simple-rtmp-server/wiki/Performance#%E6%80%A7%E8%83%BD%E4%BE%8B%E8%A1%8C%E6%8A%A5%E5%91%8A4k) connections(270kbps). 0.9.130 -* v1.0, 2014-06-21, support edge [token traverse](https://github.com/winlinvip/simple-rtmp-server/wiki/DRM#tokentraverse), fix [#104](https://github.com/winlinvip/simple-rtmp-server/issues/104). 0.9.129 +* v1.0, 2014-06-22, performance refine, support [3k+](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Performance#%E6%80%A7%E8%83%BD%E4%BE%8B%E8%A1%8C%E6%8A%A5%E5%91%8A4k) connections(270kbps). 0.9.130 +* v1.0, 2014-06-21, support edge [token traverse](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DRM#tokentraverse), fix [#104](https://github.com/winlinvip/simple-rtmp-server/issues/104). 0.9.129 * v1.0, 2014-06-19, add connections count to api summaries. 0.9.127 * v1.0, 2014-06-19, add srs bytes and kbps to api summaries. 0.9.126 * v1.0, 2014-06-18, add network bytes to api summaries. 0.9.125 @@ -309,11 +309,11 @@ Supported operating systems and hardware: * v1.0, 2014-04-11, add speex1.2 to transcode flash encoder stream. 0.9.58 * v1.0, 2014-04-10, support reload ingesters(add/remov/update). 0.9.57 * v1.0, 2014-04-07, [1.0 mainline(0.9.55)](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline) released. 30000 lines. -* v1.0, 2014-04-07, support [ingest](https://github.com/winlinvip/simple-rtmp-server/wiki/SampleIngest) file/stream/device. -* v1.0, 2014-04-05, support [http api](https://github.com/winlinvip/simple-rtmp-server/wiki/HTTPApi) and [http server](https://github.com/winlinvip/simple-rtmp-server/wiki/HTTPServer). +* v1.0, 2014-04-07, support [ingest](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleIngest) file/stream/device. +* v1.0, 2014-04-05, support [http api](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HTTPApi) and [http server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HTTPServer). * v1.0, 2014-04-03, implements http framework and api/v1/version. * v1.0, 2014-03-30, fix bug for st detecting epoll failed, force st to use epoll. -* v1.0, 2014-03-29, add wiki [Performance for RaspberryPi](https://github.com/winlinvip/simple-rtmp-server/wiki/RaspberryPi). +* v1.0, 2014-03-29, add wiki [Performance for RaspberryPi](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_RaspberryPi). * v1.0, 2014-03-29, add release binary package for raspberry-pi. * v1.0, 2014-03-26, support RTMP ATC for HLS/HDS to support backup(failover). * v1.0, 2014-03-23, support daemon, default start in daemon. @@ -324,20 +324,20 @@ Supported operating systems and hardware: * v1.0, 2014-03-19, add vn/an for FFMPEG to drop video/audio for radio stream. * v1.0, 2014-03-19, refine handshake, client support complex handshake, add utest. * v1.0, 2014-03-16, fix bug on arm of st, the sp change from 20 to 8, for respberry-pi, @see [commit](https://github.com/winlinvip/simple-rtmp-server/commit/5a4373d4835758188b9a1f03005cea0b6ddc62aa) -* v1.0, 2014-03-16, support ARM([debian armhf, v7cpu](https://github.com/winlinvip/simple-rtmp-server/wiki/SrsLinuxArm)) with rtmp/ssl/hls/librtmp. +* v1.0, 2014-03-16, support ARM([debian armhf, v7cpu](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLinuxArm)) with rtmp/ssl/hls/librtmp. * v1.0, 2014-03-12, finish utest for amf0 codec. * v1.0, 2014-03-06, add gperftools for mem leak detect, mem/cpu profile. * v1.0, 2014-03-04, add gest framework for utest, build success. -* v1.0, 2014-03-02, add wiki [srs-librtmp](https://github.com/winlinvip/simple-rtmp-server/wiki/SrsLibrtmp), [SRS for arm](https://github.com/winlinvip/simple-rtmp-server/wiki/SrsLinuxArm), [product](https://github.com/winlinvip/simple-rtmp-server/wiki/Product) +* v1.0, 2014-03-02, add wiki [srs-librtmp](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLibrtmp), [SRS for arm](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLinuxArm), [product](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Product) * v1.0, 2014-03-02, srs-librtmp, client publish/play library like librtmp. * v1.0, 2014-03-01, modularity, extract core/kernel/rtmp/app/main module. * v1.0, 2014-02-28, support arm build(SRS/ST), add ssl to 3rdparty package. -* v1.0, 2014-02-28, add wiki [BuildArm](https://github.com/winlinvip/simple-rtmp-server/wiki/Build), [FFMPEG](https://github.com/winlinvip/simple-rtmp-server/wiki/FFMPEG), [Reload](https://github.com/winlinvip/simple-rtmp-server/wiki/Reload) -* v1.0, 2014-02-27, add wiki [LowLatency](https://github.com/winlinvip/simple-rtmp-server/wiki/LowLatency), [HTTPCallback](https://github.com/winlinvip/simple-rtmp-server/wiki/HTTPCallback), [ServerSideScript](https://github.com/winlinvip/simple-rtmp-server/wiki/ServerSideScript), [IDE](https://github.com/winlinvip/simple-rtmp-server/wiki/IDE) -* v1.0, 2014-01-19, add wiki [DeliveryHLS](https://github.com/winlinvip/simple-rtmp-server/wiki/DeliveryHLS) -* v1.0, 2014-01-12, add wiki [HowToAskQuestion](https://github.com/winlinvip/simple-rtmp-server/wiki/HowToAskQuestion), [RtmpUrlVhost](https://github.com/winlinvip/simple-rtmp-server/wiki/RtmpUrlVhost) +* v1.0, 2014-02-28, add wiki [BuildArm](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Build), [FFMPEG](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_FFMPEG), [Reload](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Reload) +* v1.0, 2014-02-27, add wiki [LowLatency](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_LowLatency), [HTTPCallback](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HTTPCallback), [ServerSideScript](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_ServerSideScript), [IDE](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE) +* v1.0, 2014-01-19, add wiki [DeliveryHLS](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DeliveryHLS) +* v1.0, 2014-01-12, add wiki [HowToAskQuestion](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HowToAskQuestion), [RtmpUrlVhost](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_RtmpUrlVhost) * v1.0, 2014-01-11, fix jw/flower player pause bug, which send closeStream actually. -* v1.0, 2014-01-05, add wiki [Build](https://github.com/winlinvip/simple-rtmp-server/wiki/Build), [Performance](https://github.com/winlinvip/simple-rtmp-server/wiki/Performance), [Forward](https://github.com/winlinvip/simple-rtmp-server/wiki/Forward) +* v1.0, 2014-01-05, add wiki [Build](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Build), [Performance](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Performance), [Forward](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Forward) * v1.0, 2014-01-01, change listen(512), chunk-size(60000), to improve performance. * v1.0, 2013-12-27, merge from wenjie, the bandwidth test feature. * v0.9, 2013-12-25, [v0.9](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.9) released. 20926 lines. @@ -355,7 +355,7 @@ Supported operating systems and hardware: * v0.9, 2013-12-14, refine the thread model for the retry threads. * v0.9, 2013-12-10, auto install depends tools/libs on centos/ubuntu. * v0.8, 2013-12-08, [v0.8](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.8) released. 19186 lines. -* v0.8, 2013-12-08, support [http hooks](https://github.com/winlinvip/simple-rtmp-server/wiki/HTTPCallback): on_connect/close/publish/unpublish/play/stop. +* v0.8, 2013-12-08, support [http hooks](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HTTPCallback): on_connect/close/publish/unpublish/play/stop. * v0.8, 2013-12-08, support multiple http hooks for a event. * v0.8, 2013-12-07, support http callback hooks, on_connect. * v0.8, 2013-12-07, support network based cli and json result, add CherryPy 3.2.4. @@ -445,8 +445,8 @@ usr sys idl wai hiq siq| read writ| recv send| in out | int csw 32 17 33 0 0 18| 0 410B| 169M 169M| 0 0 |4518 3788 -* See also: [Performance for x86/x64 Test Guide](https://github.com/winlinvip/simple-rtmp-server/wiki/Performance) -* See also: [Performance for RaspberryPi](https://github.com/winlinvip/simple-rtmp-server/wiki/RaspberryPi) +* See also: [Performance for x86/x64 Test Guide](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Performance) +* See also: [Performance for RaspberryPi](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_RaspberryPi) ## Architecture @@ -520,7 +520,7 @@ Remark: to ingest any input to rtmp, push to SRS. -### [HDS/HLS origin backup](https://github.com/winlinvip/simple-rtmp-server/wiki/RTMP-ATC) +### [HDS/HLS origin backup](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_RTMP-ATC)
                         +----------+        +----------+
@@ -532,10 +532,10 @@ to ingest any input to rtmp, push to SRS.
                  RTMP   +----------+ RTMP   +----------+
 
-### [RTMP cluster(origin/edge) Architecture](https://github.com/winlinvip/simple-rtmp-server/wiki/Edge) +### [RTMP cluster(origin/edge) Architecture](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Edge) -Remark: cluster over edge, see [Edge](https://github.com/winlinvip/simple-rtmp-server/wiki/Edge) -Remark: cluster over forward, see [Forward](https://github.com/winlinvip/simple-rtmp-server/wiki/Forward) +Remark: cluster over edge, see [Edge](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Edge) +Remark: cluster over forward, see [Forward](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Forward)
 +---------+       +-----------------+     +-----------------------+ 
diff --git a/trunk/CMakeLists.txt b/trunk/CMakeLists.txt
index a8545c557a..fa80bcd477 100644
--- a/trunk/CMakeLists.txt
+++ b/trunk/CMakeLists.txt
@@ -21,31 +21,31 @@ IF(NOT EXISTS ${PROJECT_SOURCE_DIR}/objs/st/libst.a)
     EXEC_PROGRAM(./configure)
 ENDIF(NOT EXISTS ${PROJECT_SOURCE_DIR}/objs/st/libst.a)
 
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
 MESSAGE(STATUS "use ./configure && make, @see https://github.com/winlinvip/simple-rtmp-server#usage")
 
diff --git a/trunk/auto/depends.sh b/trunk/auto/depends.sh
index d7f3f521ce..961da54f59 100755
--- a/trunk/auto/depends.sh
+++ b/trunk/auto/depends.sh
@@ -226,7 +226,7 @@ if [ $SRS_EMBEDED_CPU = YES ]; then
         echo "st-1.9t for arm is ok.";
     else
         # TODO: FIXME: patch the bug.
-        # patch st for arm, @see: https://github.com/winlinvip/simple-rtmp-server/wiki/SrsLinuxArm#st-arm-bug-fix
+        # patch st for arm, @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLinuxArm#st-arm-bug-fix
         echo "build st-1.9t for arm"; 
         (
             rm -rf ${SRS_OBJS}/st-1.9 && cd ${SRS_OBJS} && 
diff --git a/trunk/auto/options.sh b/trunk/auto/options.sh
index 82e0cb51ba..7314ce6b6b 100755
--- a/trunk/auto/options.sh
+++ b/trunk/auto/options.sh
@@ -71,7 +71,7 @@ SRS_MIPS_UBUNTU12=NO
 SRS_DEV=NO
 # dev, open main server feature for dev, no utest/research/librtmp
 SRS_FAST_DEV=NO
-# demo, for the demo of srs, @see: https://github.com/winlinvip/simple-rtmp-server/wiki/SampleDemo
+# demo, for the demo of srs, @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleDemo
 SRS_DEMO=NO
 # raspberry-pi, open hls/ssl/static
 SRS_PI=NO
@@ -169,7 +169,7 @@ Presets:
   --disable-all             disable all features, only support vp6 RTMP.
   --dev                     for dev, open all features, no nginx/gperf/gprof/arm.
   --fast-dev                for dev fast compile, the RTMP server, without librtmp/utest/research.
-  --demo                    for srs demo, @see: https://github.com/winlinvip/simple-rtmp-server/wiki/SampleDemo
+  --demo                    for srs demo, @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleDemo
   --full                    enable all features, no gperf/gprof/arm.
   
 Conflicts:
diff --git a/trunk/conf/demo.19350.conf b/trunk/conf/demo.19350.conf
index f5b37a6c1d..e65001fe42 100644
--- a/trunk/conf/demo.19350.conf
+++ b/trunk/conf/demo.19350.conf
@@ -1,5 +1,5 @@
 # the config for srs demo
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/SampleDemo
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleDemo
 # @see full.conf for detail config.
 
 listen              19350;
diff --git a/trunk/conf/demo.conf b/trunk/conf/demo.conf
index 4396d44464..bf5468bd8e 100644
--- a/trunk/conf/demo.conf
+++ b/trunk/conf/demo.conf
@@ -1,5 +1,5 @@
 # the config for srs demo
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/SampleDemo
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleDemo
 # @see full.conf for detail config.
 
 listen              1935;
diff --git a/trunk/conf/dvr.segment.conf b/trunk/conf/dvr.segment.conf
index d42029a437..865dc1ed8b 100644
--- a/trunk/conf/dvr.segment.conf
+++ b/trunk/conf/dvr.segment.conf
@@ -1,5 +1,5 @@
 # the config for srs to dvr in segment mode
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/DVR
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DVR
 # @see full.conf for detail config.
 
 listen              1935;
diff --git a/trunk/conf/dvr.session.conf b/trunk/conf/dvr.session.conf
index 265f0f1b77..c93059f46f 100644
--- a/trunk/conf/dvr.session.conf
+++ b/trunk/conf/dvr.session.conf
@@ -1,5 +1,5 @@
 # the config for srs to dvr in session mode
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/DVR
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DVR
 # @see full.conf for detail config.
 
 listen              1935;
diff --git a/trunk/conf/edge.conf b/trunk/conf/edge.conf
index 407d9ff850..c90a3ee5c9 100644
--- a/trunk/conf/edge.conf
+++ b/trunk/conf/edge.conf
@@ -1,5 +1,5 @@
 # the config for srs origin-edge cluster
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/Edge
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Edge
 # @see full.conf for detail config.
 
 listen              1935;
diff --git a/trunk/conf/edge.token.traverse.conf b/trunk/conf/edge.token.traverse.conf
index bd6bc75137..c92ff61f45 100644
--- a/trunk/conf/edge.token.traverse.conf
+++ b/trunk/conf/edge.token.traverse.conf
@@ -1,5 +1,5 @@
 # the config for srs for token traverse authentication
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/DRM
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DRM
 # @see full.conf for detail config.
 
 listen              1935
diff --git a/trunk/conf/ffmpeg.transcode.conf b/trunk/conf/ffmpeg.transcode.conf
index 9179343410..63a0a09752 100644
--- a/trunk/conf/ffmpeg.transcode.conf
+++ b/trunk/conf/ffmpeg.transcode.conf
@@ -1,5 +1,5 @@
 # the config for srs use ffmpeg to transcode
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/SampleFFMPEG
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleFFMPEG
 # @see full.conf for detail config.
 
 listen              1935;
diff --git a/trunk/conf/forward.master.conf b/trunk/conf/forward.master.conf
index b0a6f51f24..9480e52de9 100644
--- a/trunk/conf/forward.master.conf
+++ b/trunk/conf/forward.master.conf
@@ -1,5 +1,5 @@
 # the config for srs to forward
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/SampleForward
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleForward
 # @see full.conf for detail config.
 
 listen              1935;
diff --git a/trunk/conf/forward.slave.conf b/trunk/conf/forward.slave.conf
index 1ff57db467..52649fd158 100644
--- a/trunk/conf/forward.slave.conf
+++ b/trunk/conf/forward.slave.conf
@@ -1,5 +1,5 @@
 # the config for srs to forward
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/SampleForward
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleForward
 # @see full.conf for detail config.
 
 listen              19350;
diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf
index 09d046395a..dbb9586dab 100644
--- a/trunk/conf/full.conf
+++ b/trunk/conf/full.conf
@@ -406,7 +406,7 @@ vhost debug.srs.com {
     # it's strongly recommend to open the debug_srs_upnode,
     # when connect to upnode, it will take the debug info, 
     # for example, the id, source id, pid.
-    # please see: https://github.com/winlinvip/simple-rtmp-server/wiki/SrsLog
+    # please see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLog
     # default: on
     debug_srs_upnode    on;
 }
diff --git a/trunk/conf/hls.conf b/trunk/conf/hls.conf
index 9e4981548b..9044956c2a 100644
--- a/trunk/conf/hls.conf
+++ b/trunk/conf/hls.conf
@@ -1,5 +1,5 @@
 # the config for srs to delivery hls
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/SampleHLS
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleHLS
 # @see full.conf for detail config.
 
 listen              1935;
diff --git a/trunk/conf/http.hls.conf b/trunk/conf/http.hls.conf
index 468a13a45e..27dbfb44ab 100644
--- a/trunk/conf/http.hls.conf
+++ b/trunk/conf/http.hls.conf
@@ -1,5 +1,5 @@
 # the config for srs to delivery hls
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/SampleHLS
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleHLS
 # @see full.conf for detail config.
 
 listen              1935;
diff --git a/trunk/conf/ingest.conf b/trunk/conf/ingest.conf
index 651fbec8d2..d82922fde3 100644
--- a/trunk/conf/ingest.conf
+++ b/trunk/conf/ingest.conf
@@ -1,5 +1,5 @@
 # use ffmpeg to ingest file/stream/device to SRS
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/SampleIngest
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleIngest
 # @see full.conf for detail config.
 
 listen              1935;
diff --git a/trunk/conf/origin.conf b/trunk/conf/origin.conf
index da9296a2ed..439e9071af 100644
--- a/trunk/conf/origin.conf
+++ b/trunk/conf/origin.conf
@@ -1,5 +1,5 @@
 # the config for srs origin-edge cluster
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/Edge
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Edge
 # @see full.conf for detail config.
 
 listen              19350;
diff --git a/trunk/conf/realtime.conf b/trunk/conf/realtime.conf
index b440762a5e..3adf218d1e 100644
--- a/trunk/conf/realtime.conf
+++ b/trunk/conf/realtime.conf
@@ -1,5 +1,5 @@
 # the config for srs to delivery realtime RTMP stream
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/SampleRealtime
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleRealtime
 # @see full.conf for detail config.
 
 listen              1935;
diff --git a/trunk/conf/rtmp.conf b/trunk/conf/rtmp.conf
index c43c58386b..96f232d29c 100644
--- a/trunk/conf/rtmp.conf
+++ b/trunk/conf/rtmp.conf
@@ -1,5 +1,5 @@
 # the config for srs to delivery RTMP
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/SampleRTMP
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleRTMP
 # @see full.conf for detail config.
 
 listen              1935;
diff --git a/trunk/conf/transcode2hls.audio.only.conf b/trunk/conf/transcode2hls.audio.only.conf
index b096ef2b1f..e5fd8318b6 100644
--- a/trunk/conf/transcode2hls.audio.only.conf
+++ b/trunk/conf/transcode2hls.audio.only.conf
@@ -1,5 +1,5 @@
 # the config for srs to delivery hls
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/SampleHLS
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleHLS
 # @see full.conf for detail config.
 
 listen              1935;
diff --git a/trunk/configure b/trunk/configure
index 71cb05cea7..a985b7c051 100755
--- a/trunk/configure
+++ b/trunk/configure
@@ -134,7 +134,7 @@ BLACK="\\${BLACK}"
 
 echo -e "\${GREEN}build summary:\${BLACK}"
 echo -e "     \${BLACK}+------------------------------------------------------------------------------------\${BLACK}"
-echo -e "     |${SrsGperfSummaryColor}gperf @see: https://github.com/winlinvip/simple-rtmp-server/wiki/GPERF\${BLACK}"
+echo -e "     |${SrsGperfSummaryColor}gperf @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_GPERF\${BLACK}"
 echo -e "     |     ${SrsGperfMCSummaryColor}gmc @see: http://google-perftools.googlecode.com/svn/trunk/doc/heap_checker.html\${BLACK}"
 echo -e "     |     ${SrsGperfMCSummaryColor}gmc: gperf memory check\${BLACK}"
 echo -e "     |             ${SrsGperfMCSummaryColor}env PPROF_PATH=./objs/pprof HEAPCHECK=normal ./objs/srs -c conf/console.conf # start gmc\${BLACK}"
@@ -150,7 +150,7 @@ echo -e "     |             ${SrsGperfCPSummaryColor}rm -f gperf.srs.gcp*; ./obj
 echo -e "     |             ${SrsGperfCPSummaryColor}killall -2 srs # or CTRL+C to stop gcp\${BLACK}"
 echo -e "     |             ${SrsGperfCPSummaryColor}./objs/pprof --text objs/srs gperf.srs.gcp* # to analysis cpu profile\${BLACK}"
 echo -e "     \${BLACK}+------------------------------------------------------------------------------------\${BLACK}"
-echo -e "     |${SrsGprofSummaryColor}gprof @see: https://github.com/winlinvip/simple-rtmp-server/wiki/GPROF\${BLACK}"
+echo -e "     |${SrsGprofSummaryColor}gprof @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_GPROF\${BLACK}"
 echo -e "     |${SrsGprofSummaryColor}gprof: GNU profile tool, @see: http://www.cs.utah.edu/dept/old/texinfo/as/gprof.html\${BLACK}"
 echo -e "     |     ${SrsGprofSummaryColor}rm -f gmon.out; ./objs/srs -c conf/console.conf # start gprof\${BLACK}"
 echo -e "     |     ${SrsGprofSummaryColor}killall -2 srs # or CTRL+C to stop gprof\${BLACK}"
@@ -160,7 +160,7 @@ echo -e "     |${SrsResearchSummaryColor}research: ./objs/research, api server,
 echo -e "     \${BLACK}+------------------------------------------------------------------------------------\${BLACK}"
 echo -e "     |${SrsUtestSummaryColor}utest: ./objs/srs_utest, the utest for srs\${BLACK}"
 echo -e "     \${BLACK}+------------------------------------------------------------------------------------\${BLACK}"
-echo -e "     |${SrsLibrtmpSummaryColor}librtmp @see: https://github.com/winlinvip/simple-rtmp-server/wiki/SrsLibrtmp\${BLACK}"
+echo -e "     |${SrsLibrtmpSummaryColor}librtmp @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLibrtmp\${BLACK}"
 echo -e "     |${SrsLibrtmpSummaryColor}librtmp: ./objs/include, ./objs/lib, the srs-librtmp library\${BLACK}"
 echo -e "     |     ${SrsLibrtmpSummaryColor}simple handshake: publish/play stream with simple handshake to server\${BLACK}"
 echo -e "     |     ${SrsLibrtmpSSLSummaryColor}complex handshake: it's not required for client, recommend disable it\${BLACK}"
@@ -171,30 +171,30 @@ echo -e "     |     ${SrsLibrtmpSummaryColor}librtmp-sample: ./research/librtmp/
 echo -e "     |     ${SrsLibrtmpSummaryColor}librtmp-sample: ./research/librtmp/objs/srs_bandwidth_check\${BLACK}"
 echo -e "     \${BLACK}+------------------------------------------------------------------------------------\${BLACK}"
 echo -e "     |\${GREEN}server: ./objs/srs -c conf/srs.conf, start the srs server\${BLACK}"
-echo -e "     |     ${SrsHlsSummaryColor}hls @see: https://github.com/winlinvip/simple-rtmp-server/wiki/DeliveryHLS\${BLACK}"
+echo -e "     |     ${SrsHlsSummaryColor}hls @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DeliveryHLS\${BLACK}"
 echo -e "     |     ${SrsHlsSummaryColor}hls: generate m3u8 and ts from rtmp stream\${BLACK}"
-echo -e "     |     ${SrsDvrSummaryColor}dvr @see: https://github.com/winlinvip/simple-rtmp-server/wiki/DVR\${BLACK}"
+echo -e "     |     ${SrsDvrSummaryColor}dvr @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DVR\${BLACK}"
 echo -e "     |     ${SrsDvrSummaryColor}dvr: record RTMP stream to flv files.\${BLACK}"
-echo -e "     |     ${SrsNginxSummaryColor}nginx @see: https://github.com/winlinvip/simple-rtmp-server/wiki/DeliveryHLS\${BLACK}"
+echo -e "     |     ${SrsNginxSummaryColor}nginx @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DeliveryHLS\${BLACK}"
 echo -e "     |     ${SrsNginxSummaryColor}nginx: delivery HLS stream by nginx\${BLACK}"
 echo -e "     |     ${SrsNginxSummaryColor}nginx: sudo ./objs/nginx/sbin/nginx\${BLACK}"
-echo -e "     |     ${SrsSslSummaryColor}ssl @see: https://github.com/winlinvip/simple-rtmp-server/wiki/RTMPHandshake\${BLACK}"
+echo -e "     |     ${SrsSslSummaryColor}ssl @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_RTMPHandshake\${BLACK}"
 echo -e "     |     ${SrsSslSummaryColor}ssl: support RTMP complex handshake for client required, for instance, flash\${BLACK}"
-echo -e "     |     ${SrsFfmpegSummaryColor}ffmpeg @see: https://github.com/winlinvip/simple-rtmp-server/wiki/FFMPEG\${BLACK}"
+echo -e "     |     ${SrsFfmpegSummaryColor}ffmpeg @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_FFMPEG\${BLACK}"
 echo -e "     |     ${SrsFfmpegSummaryColor}ffmpeg: transcode, mux, ingest tool\${BLACK}"
 echo -e "     |     ${SrsFfmpegSummaryColor}ffmpeg: ./objs/ffmpeg/bin/ffmpeg\${BLACK}"
-echo -e "     |     ${SrsTranscodeSummaryColor}transcode @see: https://github.com/winlinvip/simple-rtmp-server/wiki/FFMPEG\${BLACK}"
+echo -e "     |     ${SrsTranscodeSummaryColor}transcode @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_FFMPEG\${BLACK}"
 echo -e "     |     ${SrsTranscodeSummaryColor}transcode: support transcoding RTMP stream\${BLACK}"
-echo -e "     |     ${SrsIngestSummaryColor}ingest @see: https://github.com/winlinvip/simple-rtmp-server/wiki/Ingest\${BLACK}"
+echo -e "     |     ${SrsIngestSummaryColor}ingest @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Ingest\${BLACK}"
 echo -e "     |     ${SrsIngestSummaryColor}ingest: support ingest file/stream/device then push to SRS by RTMP stream\${BLACK}"
-echo -e "     |     ${SrsHttpCallbackSummaryColor}http-callback @see: https://github.com/winlinvip/simple-rtmp-server/wiki/HTTPCallback\${BLACK}"
+echo -e "     |     ${SrsHttpCallbackSummaryColor}http-callback @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HTTPCallback\${BLACK}"
 echo -e "     |     ${SrsHttpCallbackSummaryColor}http-callback: support http callback for authentication and event injection\${BLACK}"
-echo -e "     |     ${SrsHttpServerSummaryColor}http-server @see: https://github.com/winlinvip/simple-rtmp-server/wiki/HTTPServer\${BLACK}"
+echo -e "     |     ${SrsHttpServerSummaryColor}http-server @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HTTPServer\${BLACK}"
 echo -e "     |     ${SrsHttpServerSummaryColor}http-server: support http server to delivery http stream\${BLACK}"
-echo -e "     |     ${SrsHttpApiSummaryColor}http-api @see: https://github.com/winlinvip/simple-rtmp-server/wiki/HTTPApi\${BLACK}"
+echo -e "     |     ${SrsHttpApiSummaryColor}http-api @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HTTPApi\${BLACK}"
 echo -e "     |     ${SrsHttpApiSummaryColor}http-api: support http api to manage server\${BLACK}"
 echo -e "     \${BLACK}+------------------------------------------------------------------------------------\${BLACK}"
-echo -e "\${GREEN}binaries @see: https://github.com/winlinvip/simple-rtmp-server/wiki/Build\${BLACK}"
+echo -e "\${GREEN}binaries @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Build\${BLACK}"
 
 echo "you can:"
 echo "      ./objs/srs -c conf/srs.conf"
@@ -286,7 +286,7 @@ install-api: install
 	@echo "     sudo ln -sf \$(SRS_PREFIX)/etc/init.d/srs-api /etc/init.d/srs-api"
 	@echo "     /etc/init.d/srs-api start"
 	@echo "     http://\$(shell bash auto/local_ip.sh):8085"
-	@echo "@see: https://github.com/winlinvip/simple-rtmp-server/wiki/LinuxService"
+	@echo "@see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_LinuxService"
 
 install:
 	@echo "mkdir \$(__REAL_INSTALL)"
@@ -308,7 +308,7 @@ install:
 	@echo "srs installed, to link and start srs:"
 	@echo "     sudo ln -sf \$(SRS_PREFIX)/etc/init.d/srs /etc/init.d/srs"
 	@echo "     /etc/init.d/srs start"
-	@echo "@see: https://github.com/winlinvip/simple-rtmp-server/wiki/LinuxService"
+	@echo "@see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_LinuxService"
 
 END
 
diff --git a/trunk/research/arm/jmp.cpp b/trunk/research/arm/jmp.cpp
index d8f4d45457..2b2a599133 100644
--- a/trunk/research/arm/jmp.cpp
+++ b/trunk/research/arm/jmp.cpp
@@ -1,5 +1,5 @@
 /*
-# see: https://github.com/winlinvip/simple-rtmp-server/wiki/SrsLinuxArm
+# see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLinuxArm
  arm-linux-gnueabi-g++ -o jmp jmp.cpp -static
  arm-linux-gnueabi-strip jmp
 */
diff --git a/trunk/research/arm/jmp_sp.cpp b/trunk/research/arm/jmp_sp.cpp
index fa98b46f5c..24ea4bc9b6 100644
--- a/trunk/research/arm/jmp_sp.cpp
+++ b/trunk/research/arm/jmp_sp.cpp
@@ -1,5 +1,5 @@
 /*
-# see: https://github.com/winlinvip/simple-rtmp-server/wiki/SrsLinuxArm
+# see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLinuxArm
  arm-linux-gnueabi-g++ -g -o jmp_sp jmp_sp.cpp -static
  arm-linux-gnueabi-strip jmp_sp
 */
diff --git a/trunk/research/arm/test.cpp b/trunk/research/arm/test.cpp
index d21033be8f..970d04283b 100644
--- a/trunk/research/arm/test.cpp
+++ b/trunk/research/arm/test.cpp
@@ -1,5 +1,5 @@
 /*
-# see: https://github.com/winlinvip/simple-rtmp-server/wiki/SrsLinuxArm
+# see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLinuxArm
  arm-linux-gnueabi-g++ -o test test.cpp -static
  arm-linux-gnueabi-strip test
 */
diff --git a/trunk/research/librtmp/Makefile b/trunk/research/librtmp/Makefile
index aeac262e5c..af2b477cb0 100755
--- a/trunk/research/librtmp/Makefile
+++ b/trunk/research/librtmp/Makefile
@@ -32,7 +32,7 @@ help:
 	@echo "Remark: srs Makefile will auto invoke this by --with/without-ssl, "
 	@echo "     that is, if user specified ssl(by --with-ssl), srs will make this by 'make ssl'"
 	@echo "     that is, if user not use ssl(by --without-ssl), use 'make nossl'"
-	@echo "     see: https://github.com/winlinvip/simple-rtmp-server/wiki/Build"
+	@echo "     see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_v1_Build"
 	@echo "Remark: before make this sample, user must make the srs, with/without ssl"
     
 clean:
diff --git a/trunk/scripts/install.sh b/trunk/scripts/install.sh
index 289475cfd1..9f935e5ddf 100755
--- a/trunk/scripts/install.sh
+++ b/trunk/scripts/install.sh
@@ -108,7 +108,7 @@ else
 fi
 
 echo ""
-echo "see: https://github.com/winlinvip/simple-rtmp-server/wiki/LinuxService"
+echo "see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_LinuxService"
 echo "install success, you can:"
 echo -e "${GREEN}      sudo /etc/init.d/srs start${BLACK}"
 echo "srs root is ${INSTALL}"
diff --git a/trunk/scripts/package.sh b/trunk/scripts/package.sh
index 6141cb707e..2a6072bf36 100755
--- a/trunk/scripts/package.sh
+++ b/trunk/scripts/package.sh
@@ -108,7 +108,7 @@ fi
 ok_msg "real os is ${os_name}-${os_major_version} ${os_release} ${os_machine}"
 
 # build srs
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/Build
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Build
 ok_msg "start build srs"
 if [ $ARM = YES ]; then
     (
diff --git a/trunk/scripts/run.sh b/trunk/scripts/run.sh
index b54a378bf4..2f0082e3bb 100755
--- a/trunk/scripts/run.sh
+++ b/trunk/scripts/run.sh
@@ -51,4 +51,4 @@ echo -e "${GREEN}请在hosts中添加一行:${BLACK}"
 echo -e "${RED}    $ip demo.srs.com${BLACK}"
 echo -e "${GREEN}演示地址:${BLACK}"
 echo -e "${RED}    http://$ip:$port${BLACK}"
-echo -e "@see https://github.com/winlinvip/simple-rtmp-server/wiki/SampleDemo"
+echo -e "@see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleDemo"
diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp
index bd7172fa83..d625ab9d06 100644
--- a/trunk/src/app/srs_app_config.cpp
+++ b/trunk/src/app/srs_app_config.cpp
@@ -48,7 +48,7 @@ using namespace std;
 
 using namespace _srs_internal;
 
-#define SRS_WIKI_URL_LOG "https://github.com/winlinvip/simple-rtmp-server/wiki/SrsLog"
+#define SRS_WIKI_URL_LOG "https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLog"
 
 // '\n'
 #define __LF (char)0x0a
diff --git a/trunk/src/app/srs_app_reload.hpp b/trunk/src/app/srs_app_reload.hpp
index 3b98a70090..48b2715a04 100644
--- a/trunk/src/app/srs_app_reload.hpp
+++ b/trunk/src/app/srs_app_reload.hpp
@@ -36,7 +36,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 * when reload callback, the config is updated yet.
 * 
 * features not support reload, 
-* @see: https://github.com/winlinvip/simple-rtmp-server/wiki/Reload#notsupportedfeatures
+* @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Reload#notsupportedfeatures
 */
 class ISrsReloadHandler
 {
diff --git a/trunk/src/main/srs_main_server.cpp b/trunk/src/main/srs_main_server.cpp
index bd4f72f037..bd7f068f2d 100644
--- a/trunk/src/main/srs_main_server.cpp
+++ b/trunk/src/main/srs_main_server.cpp
@@ -194,7 +194,7 @@ int main(int argc, char** argv)
     // for special features.
 #ifdef SRS_AUTO_HTTP_SERVER
     srs_warn("http server is dev feature, "
-        "@see https://github.com/winlinvip/simple-rtmp-server/wiki/HTTPServer#feature");
+        "@see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HTTPServer#feature");
 #endif
     
     /**
diff --git a/trunk/src/utest/srs_utest_config.cpp b/trunk/src/utest/srs_utest_config.cpp
index 0513340c8f..c6b235fb1f 100644
--- a/trunk/src/utest/srs_utest_config.cpp
+++ b/trunk/src/utest/srs_utest_config.cpp
@@ -505,7 +505,7 @@ std::string __full_conf = ""
     "    # it's strongly recommend to open the debug_srs_upnode,                                                                            \n"
     "    # when connect to upnode, it will take the debug info,                                                                             \n"
     "    # for example, the id, source id, pid.                                                                                             \n"
-    "    # please see: https://github.com/winlinvip/simple-rtmp-server/wiki/SrsLog                                                          \n"
+    "    # please see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLog                                                          \n"
     "    # default: on                                                                                                                      \n"
     "    debug_srs_upnode    on;                                                                                                            \n"
     "}                                                                                                                                      \n"

From a16926209997e0f6aa644960ced029a8697730ce Mon Sep 17 00:00:00 2001
From: winlin 
Date: Fri, 24 Oct 2014 10:00:23 +0800
Subject: [PATCH 031/800] update readme, rename wiki/xxx to wiki/v1_xxx

---
 README.md                   | 1 +
 trunk/src/core/srs_core.hpp | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index b53d5622f2..609033ff07 100755
--- a/README.md
+++ b/README.md
@@ -220,6 +220,7 @@ Supported operating systems and hardware:
 * 2013-10-17, Created.
## History +* v2.0, 2014-10-24, rename wiki/xxx to wiki/v1_xxx. 2.0.3. * v2.0, 2014-10-19, fix [#184](https://github.com/winlinvip/simple-rtmp-server/issues/184), support AnnexB in RTMP body for HLS. 2.0.2 * v2.0, 2014-10-18, remove supports for OSX(darwin). 2.0.1. * v2.0, 2014-10-16, revert github srs README to English. 2.0.0. diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 0a50dc91a8..cddd81cc1e 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR "2" #define VERSION_MINOR "0" -#define VERSION_REVISION "2" +#define VERSION_REVISION "3" #define RTMP_SIG_SRS_VERSION VERSION_MAJOR"."VERSION_MINOR"."VERSION_REVISION // server info. #define RTMP_SIG_SRS_KEY "SRS" From 1bfc238fec9e6e2a899685ecf135fcf8b99bd5f9 Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 24 Oct 2014 10:35:40 +0800 Subject: [PATCH 032/800] hotfix for bug #186, drop connect args when not object. 2.0.4. --- README.md | 1 + trunk/src/core/srs_core.hpp | 2 +- trunk/src/rtmp/srs_protocol_stack.cpp | 16 ++++++++++++++-- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 609033ff07..068ff150a6 100755 --- a/README.md +++ b/README.md @@ -220,6 +220,7 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History +* v2.0, 2014-10-24, fix [#186](https://github.com/winlinvip/simple-rtmp-server/issues/186), hotfix for bug #186, drop connect args when not object. 2.0.4. * v2.0, 2014-10-24, rename wiki/xxx to wiki/v1_xxx. 2.0.3. * v2.0, 2014-10-19, fix [#184](https://github.com/winlinvip/simple-rtmp-server/issues/184), support AnnexB in RTMP body for HLS. 2.0.2 * v2.0, 2014-10-18, remove supports for OSX(darwin). 2.0.1. diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index cddd81cc1e..596311b33e 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR "2" #define VERSION_MINOR "0" -#define VERSION_REVISION "3" +#define VERSION_REVISION "4" #define RTMP_SIG_SRS_VERSION VERSION_MAJOR"."VERSION_MINOR"."VERSION_REVISION // server info. #define RTMP_SIG_SRS_KEY "SRS" diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp index 66d3798f7d..ce7222b6c8 100644 --- a/trunk/src/rtmp/srs_protocol_stack.cpp +++ b/trunk/src/rtmp/srs_protocol_stack.cpp @@ -1864,11 +1864,23 @@ int SrsConnectAppPacket::decode(SrsStream* stream) if (!stream->empty()) { srs_freep(args); - args = SrsAmf0Any::object(); - if ((ret = args->read(stream)) != ERROR_SUCCESS) { + + // see: https://github.com/winlinvip/simple-rtmp-server/issues/186 + // the args maybe any amf0, for instance, a string. we should drop if not object. + SrsAmf0Any* any = NULL; + if ((ret = SrsAmf0Any::discovery(stream, &any)) != ERROR_SUCCESS) { srs_error("amf0 decode connect args failed. ret=%d", ret); return ret; } + srs_assert(any); + + // drop when not an AMF0 object. + if (!any->is_object()) { + srs_warn("drop the args, see: '4.1.1. connect', marker=%#x", any->marker); + srs_freep(any); + } else { + args = any->to_object(); + } } srs_info("amf0 decode connect packet success"); From b65dfd718af031d7f35f93b34f72b0a19b865686 Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 24 Oct 2014 10:58:06 +0800 Subject: [PATCH 033/800] add comments for the RTMP packet fields, NULL or never NULL. 2.0.5 --- trunk/src/core/srs_core.hpp | 2 +- trunk/src/rtmp/srs_protocol_stack.hpp | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 596311b33e..ab349b290c 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR "2" #define VERSION_MINOR "0" -#define VERSION_REVISION "4" +#define VERSION_REVISION "5" #define RTMP_SIG_SRS_VERSION VERSION_MAJOR"."VERSION_MINOR"."VERSION_REVISION // server info. #define RTMP_SIG_SRS_KEY "SRS" diff --git a/trunk/src/rtmp/srs_protocol_stack.hpp b/trunk/src/rtmp/srs_protocol_stack.hpp index 5a2ed265aa..e8e4e44567 100644 --- a/trunk/src/rtmp/srs_protocol_stack.hpp +++ b/trunk/src/rtmp/srs_protocol_stack.hpp @@ -572,10 +572,12 @@ class SrsConnectAppPacket : public SrsPacket * Command information object which has the name-value pairs. * @remark: alloc in packet constructor, user can directly use it, * user should never alloc it again which will cause memory leak. + * @remark, never be NULL. */ SrsAmf0Object* command_object; /** * Any optional information + * @remark, optional, init to and maybe NULL. */ SrsAmf0Object* args; public: @@ -608,11 +610,13 @@ class SrsConnectAppResPacket : public SrsPacket double transaction_id; /** * Name-value pairs that describe the properties(fmsver etc.) of the connection. + * @remark, never be NULL. */ SrsAmf0Object* props; /** * Name-value pairs that describe the response from|the server. ‘code’, * ‘level’, ‘description’ are names of few among such information. + * @remark, never be NULL. */ SrsAmf0Object* info; public: @@ -650,10 +654,12 @@ class SrsCallPacket : public SrsPacket /** * If there exists any command info this * is set, else this is set to null type. + * @remark, optional, init to and maybe NULL. */ SrsAmf0Any* command_object; /** * Any optional arguments to be provided + * @remark, optional, init to and maybe NULL. */ SrsAmf0Any* arguments; public: @@ -686,10 +692,12 @@ class SrsCallResPacket : public SrsPacket double transaction_id; /** * If there exists any command info this is set, else this is set to null type. + * @remark, optional, init to and maybe NULL. */ SrsAmf0Any* command_object; /** * Response from the method that was called. + * @remark, optional, init to and maybe NULL. */ SrsAmf0Any* response; public: @@ -724,6 +732,7 @@ class SrsCreateStreamPacket : public SrsPacket double transaction_id; /** * If there exists any command info this is set, else this is set to null type. + * @remark, never be NULL, an AMF0 null instance. */ SrsAmf0Any* command_object; // null public: @@ -756,6 +765,7 @@ class SrsCreateStreamResPacket : public SrsPacket double transaction_id; /** * If there exists any command info this is set, else this is set to null type. + * @remark, never be NULL, an AMF0 null instance. */ SrsAmf0Any* command_object; // null /** @@ -793,6 +803,7 @@ class SrsCloseStreamPacket : public SrsPacket double transaction_id; /** * Command information object does not exist. Set to null type. + * @remark, never be NULL, an AMF0 null instance. */ SrsAmf0Any* command_object; // null public: @@ -819,6 +830,7 @@ class SrsFMLEStartPacket : public SrsPacket double transaction_id; /** * If there exists any command info this is set, else this is set to null type. + * @remark, never be NULL, an AMF0 null instance. */ SrsAmf0Any* command_object; // null /** @@ -859,10 +871,12 @@ class SrsFMLEStartResPacket : public SrsPacket double transaction_id; /** * If there exists any command info this is set, else this is set to null type. + * @remark, never be NULL, an AMF0 null instance. */ SrsAmf0Any* command_object; // null /** * the optional args, set to undefined. + * @remark, never be NULL, an AMF0 undefined instance. */ SrsAmf0Any* args; // undefined public: @@ -900,6 +914,7 @@ class SrsPublishPacket : public SrsPacket double transaction_id; /** * Command information object does not exist. Set to null type. + * @remark, never be NULL, an AMF0 null instance. */ SrsAmf0Any* command_object; // null /** @@ -952,6 +967,7 @@ class SrsPausePacket : public SrsPacket double transaction_id; /** * Command information object does not exist. Set to null type. + * @remark, never be NULL, an AMF0 null instance. */ SrsAmf0Any* command_object; // null /** @@ -990,6 +1006,7 @@ class SrsPlayPacket : public SrsPacket double transaction_id; /** * Command information does not exist. Set to null type. + * @remark, never be NULL, an AMF0 null instance. */ SrsAmf0Any* command_object; // null /** @@ -1069,12 +1086,14 @@ class SrsPlayResPacket : public SrsPacket double transaction_id; /** * Command information does not exist. Set to null type. + * @remark, never be NULL, an AMF0 null instance. */ SrsAmf0Any* command_object; // null /** * If the play command is successful, the client receives OnStatus message from * server which is NetStream.Play.Start. If the specified stream is not found, * NetStream.Play.StreamNotFound is received. + * @remark, never be NULL, an AMF0 object instance. */ SrsAmf0Object* desc; public: @@ -1105,6 +1124,7 @@ class SrsOnBWDonePacket : public SrsPacket double transaction_id; /** * Command information does not exist. Set to null type. + * @remark, never be NULL, an AMF0 null instance. */ SrsAmf0Any* args; // null public: @@ -1136,11 +1156,13 @@ class SrsOnStatusCallPacket : public SrsPacket double transaction_id; /** * Command information does not exist. Set to null type. + * @remark, never be NULL, an AMF0 null instance. */ SrsAmf0Any* args; // null /** * Name-value pairs that describe the response from the server. * ‘code’,‘level’, ‘description’ are names of few among such information. + * @remark, never be NULL, an AMF0 object instance. */ SrsAmf0Object* data; public: @@ -1176,11 +1198,13 @@ class SrsBandwidthPacket : public SrsPacket double transaction_id; /** * Command information does not exist. Set to null type. + * @remark, never be NULL, an AMF0 null instance. */ SrsAmf0Any* args; // null /** * Name-value pairs that describe the response from the server. * ‘code’,‘level’, ‘description’ are names of few among such information. + * @remark, never be NULL, an AMF0 object instance. */ SrsAmf0Object* data; public: @@ -1238,6 +1262,7 @@ class SrsOnStatusDataPacket : public SrsPacket /** * Name-value pairs that describe the response from the server. * ‘code’, are names of few among such information. + * @remark, never be NULL, an AMF0 object instance. */ SrsAmf0Object* data; public: @@ -1301,6 +1326,7 @@ class SrsOnMetaDataPacket : public SrsPacket std::string name; /** * Metadata of stream. + * @remark, never be NULL, an AMF0 object instance. */ SrsAmf0Object* metadata; public: From 8295750821dc53378a4b5dd0f76158bd111ded63 Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 24 Oct 2014 11:10:30 +0800 Subject: [PATCH 034/800] rename EN/CN home --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 068ff150a6..46165e333c 100755 --- a/README.md +++ b/README.md @@ -124,7 +124,7 @@ cd simple-rtmp-server/trunk Please select your language: * [English](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_ENHome) -* [Chinese](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CNHome) +* [Chinese](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Home) ## Donation @@ -179,7 +179,7 @@ Supported operating systems and hardware: 1. Support [bandwidth test](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_BandwidthTestTool) api and flash client. 1. Player, publisher(encoder), and [demo pages(jquery+bootstrap)](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleDemo). 1. [Demo](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleDemo) video meeting or chat(SRS+cherrypy+jquery+bootstrap). -1. Full documents in [wiki](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CNHome), in Chineses. +1. Full documents in [wiki](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Home), in Chineses. 1. Support RTMP(play-publish) library: [srs-librtmp](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLibrtmp) 1. Support ARM([debian armhf, v7cpu](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLinuxArm)) with rtmp/ssl/hls/librtmp. 1. Support [init.d](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_LinuxService) and packge script, log to file. From 59f68a26558e357e17d5101d8755eb4d6400b146 Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 24 Oct 2014 11:35:06 +0800 Subject: [PATCH 035/800] add prefix wiki/v1_CN_ or wiki/v1_EN_ to wiki. --- README.md | 190 +++++++++++------------ trunk/CMakeLists.txt | 52 +++---- trunk/auto/depends.sh | 2 +- trunk/auto/options.sh | 4 +- trunk/conf/demo.19350.conf | 2 +- trunk/conf/demo.conf | 2 +- trunk/conf/dvr.segment.conf | 2 +- trunk/conf/dvr.session.conf | 2 +- trunk/conf/edge.conf | 2 +- trunk/conf/edge.token.traverse.conf | 2 +- trunk/conf/ffmpeg.transcode.conf | 2 +- trunk/conf/forward.master.conf | 2 +- trunk/conf/forward.slave.conf | 2 +- trunk/conf/full.conf | 2 +- trunk/conf/hls.conf | 2 +- trunk/conf/http.hls.conf | 2 +- trunk/conf/ingest.conf | 2 +- trunk/conf/origin.conf | 2 +- trunk/conf/realtime.conf | 2 +- trunk/conf/rtmp.conf | 2 +- trunk/conf/transcode2hls.audio.only.conf | 2 +- trunk/configure | 32 ++-- trunk/research/arm/jmp.cpp | 2 +- trunk/research/arm/jmp_sp.cpp | 2 +- trunk/research/arm/test.cpp | 2 +- trunk/research/librtmp/Makefile | 2 +- trunk/scripts/install.sh | 2 +- trunk/scripts/package.sh | 2 +- trunk/scripts/run.sh | 2 +- trunk/src/app/srs_app_config.cpp | 2 +- trunk/src/app/srs_app_reload.hpp | 2 +- trunk/src/main/srs_main_server.cpp | 2 +- trunk/src/utest/srs_utest_config.cpp | 2 +- 33 files changed, 168 insertions(+), 168 deletions(-) diff --git a/README.md b/README.md index 46165e333c..f80bd5e401 100755 --- a/README.md +++ b/README.md @@ -23,29 +23,29 @@ SRS focus on small problem domain, which is the most complex for all software(se Because of lack of deveoper resource, SRS only provides features which is the most popular for internet. SRS is simple for and only for problem domain is simplified. -SRS is a simple, [RTMP](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DeliveryRTMP)/ -[HLS](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DeliveryHLS), -[high-performance](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Performance), +SRS is a simple, [RTMP](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryRTMP)/ +[HLS](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryHLS), +[high-performance](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Performance), single/multiple(plan) processes, edge/origin live server, -[x86/x64/arm](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLinuxArm), +[x86/x64/arm](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLinuxArm), compile depends on [st](http://sourceforge.net/projects/state-threads)(required), [ssl](http://www.openssl.org/) and [http-parser](https://github.com/joyent/http-parser), use [nginx](http://nginx.org/), [ffmpeg](http://ffmpeg.org/) and [cherrypy](http://www.cherrypy.org/) as external tools. that is, only need st to run srs for -minimum run. see [Build](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Build). - -SRS supports [vhost](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_RtmpUrlVhost), -rtmp([encoder push](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DeliveryRTMP), -client/[edge](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Edge) pull), -[ingester(srs pull)](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Ingest), -[HLS](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DeliveryHLS), -[HLS audio only](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DeliveryHLS#hlsaudioonly), -[transcoding](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_FFMPEG), -[forward](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_FFMPEG), -[http hooks](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HTTPCallback), -[http api](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HTTPApi), -[http server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HTTPServer), -[dvr](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DVR). +minimum run. see [Build](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Build). + +SRS supports [vhost](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RtmpUrlVhost), +rtmp([encoder push](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryRTMP), +client/[edge](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Edge) pull), +[ingester(srs pull)](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Ingest), +[HLS](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryHLS), +[HLS audio only](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryHLS#hlsaudioonly), +[transcoding](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_FFMPEG), +[forward](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_FFMPEG), +[http hooks](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPCallback), +[http api](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPApi), +[http server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPServer), +[dvr](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DVR). WebSite: [http://ossrs.net](http://ossrs.net)
Release: [http://winlinvip.github.io/srs.release](http://winlinvip.github.io/srs.release)
@@ -75,7 +75,7 @@ answer newbie questions, and generally made SRS that much better: [AUTHORS.txt](https://github.com/winlinvip/simple-rtmp-server/blob/master/AUTHORS.txt) A big THANK YOU goes to: -* [chnvideo](chnvideo.com) co-founders([wiseyoung](mailto:wiseyoung@chnvideo.com), [trueice](mailto:trueice@chnvideo.com), [leijian](mailto:leijian@chnvideo.com)) for [big supports](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Product#bigthanks). +* [chnvideo](chnvideo.com) co-founders([wiseyoung](mailto:wiseyoung@chnvideo.com), [trueice](mailto:trueice@chnvideo.com), [leijian](mailto:leijian@chnvideo.com)) for [big supports](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Product#bigthanks). * Genes amd Mabbott for creating [st](https://github.com/winlinvip/state-threads)([state-threads](http://sourceforge.net/projects/state-threads/)). * Michael Talyanksy for introducing us to use st. * Roman Arutyunyan for creating [nginx-rtmp](https://github.com/arut/nginx-rtmp-module) for SRS to refer to. @@ -94,7 +94,7 @@ cd simple-rtmp-server/trunk
Step 2: build SRS, -Requires Centos6.x/Ubuntu12 32/64bits, others see [Build](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Build) +Requires Centos6.x/Ubuntu12 32/64bits, others see [Build](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Build)
 ./configure && make
@@ -107,23 +107,23 @@ cd simple-rtmp-server/trunk
 
See also: -* [Usage: How to delivery RTMP?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleRTMP) -* [Usage: How to delivery HLS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleHLS) -* [Usage: How to delivery HLS for other codec?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleTranscode2HLS) -* [Usage: How to transode RTMP stream by SRS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleFFMPEG) -* [Usage: How to forward stream to other server?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleForward) -* [Usage: How to deploy low lantency application?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleRealtime) -* [Usage: How to deploy SRS on ARM?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleARM) -* [Usage: How to ingest file/stream/device to SRS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleIngest) -* [Usage: How to use SRS-HTTP-server to delivery HTTP/HLS stream?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleHTTP) -* [Usage: How to show the demo of SRS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleDemo) -* [Usage: Solution using SRS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Sample) -* [Usage: Why SRS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Product) +* [Usage: How to delivery RTMP?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleRTMP) +* [Usage: How to delivery HLS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleHLS) +* [Usage: How to delivery HLS for other codec?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleTranscode2HLS) +* [Usage: How to transode RTMP stream by SRS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleFFMPEG) +* [Usage: How to forward stream to other server?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleForward) +* [Usage: How to deploy low lantency application?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleRealtime) +* [Usage: How to deploy SRS on ARM?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleARM) +* [Usage: How to ingest file/stream/device to SRS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleIngest) +* [Usage: How to use SRS-HTTP-server to delivery HTTP/HLS stream?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleHTTP) +* [Usage: How to show the demo of SRS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleDemo) +* [Usage: Solution using SRS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Sample) +* [Usage: Why SRS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Product) ## Wiki Please select your language: -* [English](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_ENHome) +* [English](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Home) * [Chinese](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Home) ## Donation @@ -138,14 +138,14 @@ Donations:
## Mirrors Github: [https://github.com/winlinvip/simple-rtmp-server](https://github.com/winlinvip/simple-rtmp-server) -[Git Usage](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Git) +[Git Usage](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Git) ```bash git clone https://github.com/winlinvip/simple-rtmp-server.git ``` CSDN: [https://code.csdn.net/winlinvip/srs-csdn](https://code.csdn.net/winlinvip/srs-csdn) -[Git Usage](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Git) +[Git Usage](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Git) ```bash git clone https://code.csdn.net/winlinvip/srs-csdn.git @@ -158,41 +158,41 @@ Supported operating systems and hardware: ## Summary 1. Simple, also stable enough. -1. [High-performance](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Performance): single-thread, async socket, event/st-thread driven. -1. [High-concurrency](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Performance), 1800 connections(500kbps), 900Mbps, CPU 90.2%, 41MB -1. Support [RTMP Origin Server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DeliveryRTMP). -1. Support [RTMP Edge Server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Edge) for CDN, push/pull stream from any RTMP server +1. [High-performance](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Performance): single-thread, async socket, event/st-thread driven. +1. [High-concurrency](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Performance), 1800 connections(500kbps), 900Mbps, CPU 90.2%, 41MB +1. Support [RTMP Origin Server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryRTMP). +1. Support [RTMP Edge Server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Edge) for CDN, push/pull stream from any RTMP server 1. Support single process; no multiple processes. -1. Support [Vhost](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_RtmpUrlVhost), support \_\_defaultVhost\_\_. -1. Support [RTMP](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DeliveryRTMP) live streaming; no vod streaming. -1. Support Apple [HLS(m3u8)](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DeliveryHLS) live streaming. -1. Support [HLS audio-only](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DeliveryHLS#hlsaudioonly) live streaming. -1. Support [Reload](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Reload) config to enable changes. -1. Support [cache last gop](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_LowLatency#gop-cache) for flash player to fast startup. +1. Support [Vhost](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RtmpUrlVhost), support \_\_defaultVhost\_\_. +1. Support [RTMP](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryRTMP) live streaming; no vod streaming. +1. Support Apple [HLS(m3u8)](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryHLS) live streaming. +1. Support [HLS audio-only](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryHLS#hlsaudioonly) live streaming. +1. Support [Reload](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Reload) config to enable changes. +1. Support [cache last gop](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_LowLatency#gop-cache) for flash player to fast startup. 1. Support listen at multiple ports. 1. Support long time(>4.6hours) publish/play. -1. Support [Forward](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Forward) in master-slave mode. -1. Support live stream [Transcoding](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_FFMPEG) by ffmpeg. -1. Support [ffmpeg](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_FFMPEG) filters(logo/overlay/crop), x264 params, copy/vn/an. -1. Support audio [transcode](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_FFMPEG) only, speex/mp3 to aac -1. Support [http callback api hooks](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HTTPCallback)(for authentication and injection). -1. Support [bandwidth test](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_BandwidthTestTool) api and flash client. -1. Player, publisher(encoder), and [demo pages(jquery+bootstrap)](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleDemo). -1. [Demo](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleDemo) video meeting or chat(SRS+cherrypy+jquery+bootstrap). +1. Support [Forward](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Forward) in master-slave mode. +1. Support live stream [Transcoding](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_FFMPEG) by ffmpeg. +1. Support [ffmpeg](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_FFMPEG) filters(logo/overlay/crop), x264 params, copy/vn/an. +1. Support audio [transcode](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_FFMPEG) only, speex/mp3 to aac +1. Support [http callback api hooks](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPCallback)(for authentication and injection). +1. Support [bandwidth test](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_BandwidthTestTool) api and flash client. +1. Player, publisher(encoder), and [demo pages(jquery+bootstrap)](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleDemo). +1. [Demo](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleDemo) video meeting or chat(SRS+cherrypy+jquery+bootstrap). 1. Full documents in [wiki](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Home), in Chineses. -1. Support RTMP(play-publish) library: [srs-librtmp](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLibrtmp) -1. Support ARM([debian armhf, v7cpu](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLinuxArm)) with rtmp/ssl/hls/librtmp. -1. Support [init.d](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_LinuxService) and packge script, log to file. -1. Support [RTMP ATC](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_RTMP-ATC) for HLS/HDS to support backup(failover) -1. Support [HTTP RESTful management api](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HTTPApi). -1. Support [Ingest](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Ingest) FILE/HTTP/RTMP/RTSP(RTP, SDP) to RTMP using external tools(e.g ffmepg). -1. Support [DVR](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DVR), record live to flv file for vod. -1. Support [tracable log, session based log](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLog). -1. Support DRM [token traverse](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DRM#tokentraverse) for fms origin authenticate. +1. Support RTMP(play-publish) library: [srs-librtmp](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLibrtmp) +1. Support ARM([debian armhf, v7cpu](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLinuxArm)) with rtmp/ssl/hls/librtmp. +1. Support [init.d](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_LinuxService) and packge script, log to file. +1. Support [RTMP ATC](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RTMP-ATC) for HLS/HDS to support backup(failover) +1. Support [HTTP RESTful management api](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPApi). +1. Support [Ingest](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Ingest) FILE/HTTP/RTMP/RTSP(RTP, SDP) to RTMP using external tools(e.g ffmepg). +1. Support [DVR](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DVR), record live to flv file for vod. +1. Support [tracable log, session based log](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLog). +1. Support DRM [token traverse](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DRM#tokentraverse) for fms origin authenticate. 1. Support system full utest on gtest. -1. [experiment] Support embeded [HTTP server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleHTTP) for hls(live/vod) -1. [experiment] Support [vod stream(http flv/hls vod stream)](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_FlvVodStream). -1. [dev] Suppport [English wiki](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_v1_ENHome). +1. [experiment] Support embeded [HTTP server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleHTTP) for hls(live/vod) +1. [experiment] Support [vod stream(http flv/hls vod stream)](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_FlvVodStream). +1. [dev] Suppport [English wiki](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_v1_ENHome). 1. [dev] Research and simplify st, [bug #182](https://github.com/winlinvip/simple-rtmp-server/issues/182). 1. [no-plan] Support <500ms latency, FRSC(Fast RTMP-compatible Stream Channel tech). 1. [no-plan] Support RTMP 302 redirect [#92](https://github.com/winlinvip/simple-rtmp-server/issues/92). @@ -207,21 +207,21 @@ Supported operating systems and hardware: ## Releases * 2014-10-09, [Release v1.0-beta](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.beta), all bug fixed, 1.0.0, 59316 lines.
-* 2014-04-07, [Release v1.0-mainline](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline), support [arm](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLinuxArm), [init.d](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_LinuxService), http [server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HTTPServer)/[api](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HTTPApi), [ingest](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleIngest). 30000 lines.
-* 2013-12-25, [Release v0.9](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.9), support bandwidth test, player/encoder/chat [demos](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleDemo). 20926 lines.
-* 2013-12-08, [Release v0.8](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.8), support [http hooks callback](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HTTPCallback), update [st_load](https://github.com/winlinvip/st-load). 19186 lines.
-* 2013-12-03, [Release v0.7](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.7), support [live stream transcoding](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_FFMPEG). 17605 lines.
-* 2013-11-29, [Release v0.6](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.6), support [forward](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Forward) stream to origin/edge. 16094 lines.
-* 2013-11-26, [Release v0.5](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.5), support [HLS(m3u8)](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DeliveryHLS), fragment and window. 14449 lines.
-* 2013-11-10, [Release v0.4](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.4), support [reload](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Reload) config, pause, longtime publish/play. 12500 lines.
-* 2013-11-04, [Release v0.3](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.3), support [vhost](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_RtmpUrlVhost), refer, gop cache, listen multiple ports. 11773 lines.
-* 2013-10-25, [Release v0.2](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.2), support [rtmp](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_RTMPHandshake) flash publish, h264, time jitter correct. 10125 lines.
-* 2013-10-23, [Release v0.1](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.1), support [rtmp FMLE/FFMPEG publish](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DeliveryRTMP), vp6. 8287 lines.
+* 2014-04-07, [Release v1.0-mainline](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline), support [arm](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLinuxArm), [init.d](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_LinuxService), http [server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPServer)/[api](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPApi), [ingest](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleIngest). 30000 lines.
+* 2013-12-25, [Release v0.9](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.9), support bandwidth test, player/encoder/chat [demos](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleDemo). 20926 lines.
+* 2013-12-08, [Release v0.8](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.8), support [http hooks callback](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPCallback), update [st_load](https://github.com/winlinvip/st-load). 19186 lines.
+* 2013-12-03, [Release v0.7](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.7), support [live stream transcoding](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_FFMPEG). 17605 lines.
+* 2013-11-29, [Release v0.6](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.6), support [forward](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Forward) stream to origin/edge. 16094 lines.
+* 2013-11-26, [Release v0.5](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.5), support [HLS(m3u8)](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryHLS), fragment and window. 14449 lines.
+* 2013-11-10, [Release v0.4](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.4), support [reload](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Reload) config, pause, longtime publish/play. 12500 lines.
+* 2013-11-04, [Release v0.3](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.3), support [vhost](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RtmpUrlVhost), refer, gop cache, listen multiple ports. 11773 lines.
+* 2013-10-25, [Release v0.2](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.2), support [rtmp](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RTMPHandshake) flash publish, h264, time jitter correct. 10125 lines.
+* 2013-10-23, [Release v0.1](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.1), support [rtmp FMLE/FFMPEG publish](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryRTMP), vp6. 8287 lines.
* 2013-10-17, Created.
## History * v2.0, 2014-10-24, fix [#186](https://github.com/winlinvip/simple-rtmp-server/issues/186), hotfix for bug #186, drop connect args when not object. 2.0.4. -* v2.0, 2014-10-24, rename wiki/xxx to wiki/v1_xxx. 2.0.3. +* v2.0, 2014-10-24, rename wiki/xxx to wiki/v1_CN_xxx. 2.0.3. * v2.0, 2014-10-19, fix [#184](https://github.com/winlinvip/simple-rtmp-server/issues/184), support AnnexB in RTMP body for HLS. 2.0.2 * v2.0, 2014-10-18, remove supports for OSX(darwin). 2.0.1. * v2.0, 2014-10-16, revert github srs README to English. 2.0.0. @@ -268,8 +268,8 @@ Supported operating systems and hardware: * v1.0, 2014-06-27, SRS online 30days with RTMP/HLS. * v1.0, 2014-06-25, fix [#108](https://github.com/winlinvip/simple-rtmp-server/issues/108), support config time jitter for encoder non-monotonical stream. 0.9.133 * v1.0, 2014-06-23, support report summaries in heartbeat. 0.9.132 -* v1.0, 2014-06-22, performance refine, support [3k+](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Performance#%E6%80%A7%E8%83%BD%E4%BE%8B%E8%A1%8C%E6%8A%A5%E5%91%8A4k) connections(270kbps). 0.9.130 -* v1.0, 2014-06-21, support edge [token traverse](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DRM#tokentraverse), fix [#104](https://github.com/winlinvip/simple-rtmp-server/issues/104). 0.9.129 +* v1.0, 2014-06-22, performance refine, support [3k+](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Performance#%E6%80%A7%E8%83%BD%E4%BE%8B%E8%A1%8C%E6%8A%A5%E5%91%8A4k) connections(270kbps). 0.9.130 +* v1.0, 2014-06-21, support edge [token traverse](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DRM#tokentraverse), fix [#104](https://github.com/winlinvip/simple-rtmp-server/issues/104). 0.9.129 * v1.0, 2014-06-19, add connections count to api summaries. 0.9.127 * v1.0, 2014-06-19, add srs bytes and kbps to api summaries. 0.9.126 * v1.0, 2014-06-18, add network bytes to api summaries. 0.9.125 @@ -311,11 +311,11 @@ Supported operating systems and hardware: * v1.0, 2014-04-11, add speex1.2 to transcode flash encoder stream. 0.9.58 * v1.0, 2014-04-10, support reload ingesters(add/remov/update). 0.9.57 * v1.0, 2014-04-07, [1.0 mainline(0.9.55)](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline) released. 30000 lines. -* v1.0, 2014-04-07, support [ingest](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleIngest) file/stream/device. -* v1.0, 2014-04-05, support [http api](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HTTPApi) and [http server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HTTPServer). +* v1.0, 2014-04-07, support [ingest](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleIngest) file/stream/device. +* v1.0, 2014-04-05, support [http api](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPApi) and [http server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPServer). * v1.0, 2014-04-03, implements http framework and api/v1/version. * v1.0, 2014-03-30, fix bug for st detecting epoll failed, force st to use epoll. -* v1.0, 2014-03-29, add wiki [Performance for RaspberryPi](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_RaspberryPi). +* v1.0, 2014-03-29, add wiki [Performance for RaspberryPi](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RaspberryPi). * v1.0, 2014-03-29, add release binary package for raspberry-pi. * v1.0, 2014-03-26, support RTMP ATC for HLS/HDS to support backup(failover). * v1.0, 2014-03-23, support daemon, default start in daemon. @@ -326,20 +326,20 @@ Supported operating systems and hardware: * v1.0, 2014-03-19, add vn/an for FFMPEG to drop video/audio for radio stream. * v1.0, 2014-03-19, refine handshake, client support complex handshake, add utest. * v1.0, 2014-03-16, fix bug on arm of st, the sp change from 20 to 8, for respberry-pi, @see [commit](https://github.com/winlinvip/simple-rtmp-server/commit/5a4373d4835758188b9a1f03005cea0b6ddc62aa) -* v1.0, 2014-03-16, support ARM([debian armhf, v7cpu](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLinuxArm)) with rtmp/ssl/hls/librtmp. +* v1.0, 2014-03-16, support ARM([debian armhf, v7cpu](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLinuxArm)) with rtmp/ssl/hls/librtmp. * v1.0, 2014-03-12, finish utest for amf0 codec. * v1.0, 2014-03-06, add gperftools for mem leak detect, mem/cpu profile. * v1.0, 2014-03-04, add gest framework for utest, build success. -* v1.0, 2014-03-02, add wiki [srs-librtmp](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLibrtmp), [SRS for arm](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLinuxArm), [product](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Product) +* v1.0, 2014-03-02, add wiki [srs-librtmp](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLibrtmp), [SRS for arm](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLinuxArm), [product](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Product) * v1.0, 2014-03-02, srs-librtmp, client publish/play library like librtmp. * v1.0, 2014-03-01, modularity, extract core/kernel/rtmp/app/main module. * v1.0, 2014-02-28, support arm build(SRS/ST), add ssl to 3rdparty package. -* v1.0, 2014-02-28, add wiki [BuildArm](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Build), [FFMPEG](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_FFMPEG), [Reload](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Reload) -* v1.0, 2014-02-27, add wiki [LowLatency](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_LowLatency), [HTTPCallback](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HTTPCallback), [ServerSideScript](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_ServerSideScript), [IDE](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE) -* v1.0, 2014-01-19, add wiki [DeliveryHLS](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DeliveryHLS) -* v1.0, 2014-01-12, add wiki [HowToAskQuestion](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HowToAskQuestion), [RtmpUrlVhost](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_RtmpUrlVhost) +* v1.0, 2014-02-28, add wiki [BuildArm](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Build), [FFMPEG](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_FFMPEG), [Reload](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Reload) +* v1.0, 2014-02-27, add wiki [LowLatency](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_LowLatency), [HTTPCallback](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPCallback), [ServerSideScript](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_ServerSideScript), [IDE](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE) +* v1.0, 2014-01-19, add wiki [DeliveryHLS](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryHLS) +* v1.0, 2014-01-12, add wiki [HowToAskQuestion](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HowToAskQuestion), [RtmpUrlVhost](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RtmpUrlVhost) * v1.0, 2014-01-11, fix jw/flower player pause bug, which send closeStream actually. -* v1.0, 2014-01-05, add wiki [Build](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Build), [Performance](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Performance), [Forward](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Forward) +* v1.0, 2014-01-05, add wiki [Build](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Build), [Performance](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Performance), [Forward](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Forward) * v1.0, 2014-01-01, change listen(512), chunk-size(60000), to improve performance. * v1.0, 2013-12-27, merge from wenjie, the bandwidth test feature. * v0.9, 2013-12-25, [v0.9](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.9) released. 20926 lines. @@ -357,7 +357,7 @@ Supported operating systems and hardware: * v0.9, 2013-12-14, refine the thread model for the retry threads. * v0.9, 2013-12-10, auto install depends tools/libs on centos/ubuntu. * v0.8, 2013-12-08, [v0.8](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.8) released. 19186 lines. -* v0.8, 2013-12-08, support [http hooks](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HTTPCallback): on_connect/close/publish/unpublish/play/stop. +* v0.8, 2013-12-08, support [http hooks](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPCallback): on_connect/close/publish/unpublish/play/stop. * v0.8, 2013-12-08, support multiple http hooks for a event. * v0.8, 2013-12-07, support http callback hooks, on_connect. * v0.8, 2013-12-07, support network based cli and json result, add CherryPy 3.2.4. @@ -447,8 +447,8 @@ usr sys idl wai hiq siq| read writ| recv send| in out | int csw 32 17 33 0 0 18| 0 410B| 169M 169M| 0 0 |4518 3788 -* See also: [Performance for x86/x64 Test Guide](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Performance) -* See also: [Performance for RaspberryPi](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_RaspberryPi) +* See also: [Performance for x86/x64 Test Guide](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Performance) +* See also: [Performance for RaspberryPi](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RaspberryPi) ## Architecture @@ -522,7 +522,7 @@ Remark: to ingest any input to rtmp, push to SRS. -### [HDS/HLS origin backup](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_RTMP-ATC) +### [HDS/HLS origin backup](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RTMP-ATC)
                         +----------+        +----------+
@@ -534,10 +534,10 @@ to ingest any input to rtmp, push to SRS.
                  RTMP   +----------+ RTMP   +----------+
 
-### [RTMP cluster(origin/edge) Architecture](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Edge) +### [RTMP cluster(origin/edge) Architecture](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Edge) -Remark: cluster over edge, see [Edge](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Edge) -Remark: cluster over forward, see [Forward](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Forward) +Remark: cluster over edge, see [Edge](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Edge) +Remark: cluster over forward, see [Forward](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Forward)
 +---------+       +-----------------+     +-----------------------+ 
diff --git a/trunk/CMakeLists.txt b/trunk/CMakeLists.txt
index fa80bcd477..5d7ea46cca 100644
--- a/trunk/CMakeLists.txt
+++ b/trunk/CMakeLists.txt
@@ -21,31 +21,31 @@ IF(NOT EXISTS ${PROJECT_SOURCE_DIR}/objs/st/libst.a)
     EXEC_PROGRAM(./configure)
 ENDIF(NOT EXISTS ${PROJECT_SOURCE_DIR}/objs/st/libst.a)
 
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
-MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
+MESSAGE(STATUS "only for jetbrains IDE, @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_IDE#jetbrains")
 MESSAGE(STATUS "use ./configure && make, @see https://github.com/winlinvip/simple-rtmp-server#usage")
 
diff --git a/trunk/auto/depends.sh b/trunk/auto/depends.sh
index 961da54f59..42e3d186c2 100755
--- a/trunk/auto/depends.sh
+++ b/trunk/auto/depends.sh
@@ -226,7 +226,7 @@ if [ $SRS_EMBEDED_CPU = YES ]; then
         echo "st-1.9t for arm is ok.";
     else
         # TODO: FIXME: patch the bug.
-        # patch st for arm, @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLinuxArm#st-arm-bug-fix
+        # patch st for arm, @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLinuxArm#st-arm-bug-fix
         echo "build st-1.9t for arm"; 
         (
             rm -rf ${SRS_OBJS}/st-1.9 && cd ${SRS_OBJS} && 
diff --git a/trunk/auto/options.sh b/trunk/auto/options.sh
index 7314ce6b6b..06a802297c 100755
--- a/trunk/auto/options.sh
+++ b/trunk/auto/options.sh
@@ -71,7 +71,7 @@ SRS_MIPS_UBUNTU12=NO
 SRS_DEV=NO
 # dev, open main server feature for dev, no utest/research/librtmp
 SRS_FAST_DEV=NO
-# demo, for the demo of srs, @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleDemo
+# demo, for the demo of srs, @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleDemo
 SRS_DEMO=NO
 # raspberry-pi, open hls/ssl/static
 SRS_PI=NO
@@ -169,7 +169,7 @@ Presets:
   --disable-all             disable all features, only support vp6 RTMP.
   --dev                     for dev, open all features, no nginx/gperf/gprof/arm.
   --fast-dev                for dev fast compile, the RTMP server, without librtmp/utest/research.
-  --demo                    for srs demo, @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleDemo
+  --demo                    for srs demo, @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleDemo
   --full                    enable all features, no gperf/gprof/arm.
   
 Conflicts:
diff --git a/trunk/conf/demo.19350.conf b/trunk/conf/demo.19350.conf
index e65001fe42..0aada8e339 100644
--- a/trunk/conf/demo.19350.conf
+++ b/trunk/conf/demo.19350.conf
@@ -1,5 +1,5 @@
 # the config for srs demo
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleDemo
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleDemo
 # @see full.conf for detail config.
 
 listen              19350;
diff --git a/trunk/conf/demo.conf b/trunk/conf/demo.conf
index bf5468bd8e..191ff87f02 100644
--- a/trunk/conf/demo.conf
+++ b/trunk/conf/demo.conf
@@ -1,5 +1,5 @@
 # the config for srs demo
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleDemo
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleDemo
 # @see full.conf for detail config.
 
 listen              1935;
diff --git a/trunk/conf/dvr.segment.conf b/trunk/conf/dvr.segment.conf
index 865dc1ed8b..f29c28d30b 100644
--- a/trunk/conf/dvr.segment.conf
+++ b/trunk/conf/dvr.segment.conf
@@ -1,5 +1,5 @@
 # the config for srs to dvr in segment mode
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DVR
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DVR
 # @see full.conf for detail config.
 
 listen              1935;
diff --git a/trunk/conf/dvr.session.conf b/trunk/conf/dvr.session.conf
index c93059f46f..8b16b32eb7 100644
--- a/trunk/conf/dvr.session.conf
+++ b/trunk/conf/dvr.session.conf
@@ -1,5 +1,5 @@
 # the config for srs to dvr in session mode
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DVR
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DVR
 # @see full.conf for detail config.
 
 listen              1935;
diff --git a/trunk/conf/edge.conf b/trunk/conf/edge.conf
index c90a3ee5c9..a77986b6e5 100644
--- a/trunk/conf/edge.conf
+++ b/trunk/conf/edge.conf
@@ -1,5 +1,5 @@
 # the config for srs origin-edge cluster
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Edge
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Edge
 # @see full.conf for detail config.
 
 listen              1935;
diff --git a/trunk/conf/edge.token.traverse.conf b/trunk/conf/edge.token.traverse.conf
index c92ff61f45..bff8749f68 100644
--- a/trunk/conf/edge.token.traverse.conf
+++ b/trunk/conf/edge.token.traverse.conf
@@ -1,5 +1,5 @@
 # the config for srs for token traverse authentication
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DRM
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DRM
 # @see full.conf for detail config.
 
 listen              1935
diff --git a/trunk/conf/ffmpeg.transcode.conf b/trunk/conf/ffmpeg.transcode.conf
index 63a0a09752..04c434982c 100644
--- a/trunk/conf/ffmpeg.transcode.conf
+++ b/trunk/conf/ffmpeg.transcode.conf
@@ -1,5 +1,5 @@
 # the config for srs use ffmpeg to transcode
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleFFMPEG
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleFFMPEG
 # @see full.conf for detail config.
 
 listen              1935;
diff --git a/trunk/conf/forward.master.conf b/trunk/conf/forward.master.conf
index 9480e52de9..8200b1c5fa 100644
--- a/trunk/conf/forward.master.conf
+++ b/trunk/conf/forward.master.conf
@@ -1,5 +1,5 @@
 # the config for srs to forward
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleForward
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleForward
 # @see full.conf for detail config.
 
 listen              1935;
diff --git a/trunk/conf/forward.slave.conf b/trunk/conf/forward.slave.conf
index 52649fd158..ff2dd53ed5 100644
--- a/trunk/conf/forward.slave.conf
+++ b/trunk/conf/forward.slave.conf
@@ -1,5 +1,5 @@
 # the config for srs to forward
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleForward
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleForward
 # @see full.conf for detail config.
 
 listen              19350;
diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf
index dbb9586dab..61c8b3d497 100644
--- a/trunk/conf/full.conf
+++ b/trunk/conf/full.conf
@@ -406,7 +406,7 @@ vhost debug.srs.com {
     # it's strongly recommend to open the debug_srs_upnode,
     # when connect to upnode, it will take the debug info, 
     # for example, the id, source id, pid.
-    # please see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLog
+    # please see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLog
     # default: on
     debug_srs_upnode    on;
 }
diff --git a/trunk/conf/hls.conf b/trunk/conf/hls.conf
index 9044956c2a..375bc3629d 100644
--- a/trunk/conf/hls.conf
+++ b/trunk/conf/hls.conf
@@ -1,5 +1,5 @@
 # the config for srs to delivery hls
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleHLS
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleHLS
 # @see full.conf for detail config.
 
 listen              1935;
diff --git a/trunk/conf/http.hls.conf b/trunk/conf/http.hls.conf
index 27dbfb44ab..8c2c4cdc92 100644
--- a/trunk/conf/http.hls.conf
+++ b/trunk/conf/http.hls.conf
@@ -1,5 +1,5 @@
 # the config for srs to delivery hls
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleHLS
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleHLS
 # @see full.conf for detail config.
 
 listen              1935;
diff --git a/trunk/conf/ingest.conf b/trunk/conf/ingest.conf
index d82922fde3..12fa2b8e53 100644
--- a/trunk/conf/ingest.conf
+++ b/trunk/conf/ingest.conf
@@ -1,5 +1,5 @@
 # use ffmpeg to ingest file/stream/device to SRS
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleIngest
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleIngest
 # @see full.conf for detail config.
 
 listen              1935;
diff --git a/trunk/conf/origin.conf b/trunk/conf/origin.conf
index 439e9071af..401c5cd258 100644
--- a/trunk/conf/origin.conf
+++ b/trunk/conf/origin.conf
@@ -1,5 +1,5 @@
 # the config for srs origin-edge cluster
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Edge
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Edge
 # @see full.conf for detail config.
 
 listen              19350;
diff --git a/trunk/conf/realtime.conf b/trunk/conf/realtime.conf
index 3adf218d1e..6fd74b60e4 100644
--- a/trunk/conf/realtime.conf
+++ b/trunk/conf/realtime.conf
@@ -1,5 +1,5 @@
 # the config for srs to delivery realtime RTMP stream
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleRealtime
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleRealtime
 # @see full.conf for detail config.
 
 listen              1935;
diff --git a/trunk/conf/rtmp.conf b/trunk/conf/rtmp.conf
index 96f232d29c..38212b79e5 100644
--- a/trunk/conf/rtmp.conf
+++ b/trunk/conf/rtmp.conf
@@ -1,5 +1,5 @@
 # the config for srs to delivery RTMP
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleRTMP
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleRTMP
 # @see full.conf for detail config.
 
 listen              1935;
diff --git a/trunk/conf/transcode2hls.audio.only.conf b/trunk/conf/transcode2hls.audio.only.conf
index e5fd8318b6..69eb9525c1 100644
--- a/trunk/conf/transcode2hls.audio.only.conf
+++ b/trunk/conf/transcode2hls.audio.only.conf
@@ -1,5 +1,5 @@
 # the config for srs to delivery hls
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleHLS
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleHLS
 # @see full.conf for detail config.
 
 listen              1935;
diff --git a/trunk/configure b/trunk/configure
index a985b7c051..632e2bf0ff 100755
--- a/trunk/configure
+++ b/trunk/configure
@@ -134,7 +134,7 @@ BLACK="\\${BLACK}"
 
 echo -e "\${GREEN}build summary:\${BLACK}"
 echo -e "     \${BLACK}+------------------------------------------------------------------------------------\${BLACK}"
-echo -e "     |${SrsGperfSummaryColor}gperf @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_GPERF\${BLACK}"
+echo -e "     |${SrsGperfSummaryColor}gperf @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_GPERF\${BLACK}"
 echo -e "     |     ${SrsGperfMCSummaryColor}gmc @see: http://google-perftools.googlecode.com/svn/trunk/doc/heap_checker.html\${BLACK}"
 echo -e "     |     ${SrsGperfMCSummaryColor}gmc: gperf memory check\${BLACK}"
 echo -e "     |             ${SrsGperfMCSummaryColor}env PPROF_PATH=./objs/pprof HEAPCHECK=normal ./objs/srs -c conf/console.conf # start gmc\${BLACK}"
@@ -150,7 +150,7 @@ echo -e "     |             ${SrsGperfCPSummaryColor}rm -f gperf.srs.gcp*; ./obj
 echo -e "     |             ${SrsGperfCPSummaryColor}killall -2 srs # or CTRL+C to stop gcp\${BLACK}"
 echo -e "     |             ${SrsGperfCPSummaryColor}./objs/pprof --text objs/srs gperf.srs.gcp* # to analysis cpu profile\${BLACK}"
 echo -e "     \${BLACK}+------------------------------------------------------------------------------------\${BLACK}"
-echo -e "     |${SrsGprofSummaryColor}gprof @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_GPROF\${BLACK}"
+echo -e "     |${SrsGprofSummaryColor}gprof @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_GPROF\${BLACK}"
 echo -e "     |${SrsGprofSummaryColor}gprof: GNU profile tool, @see: http://www.cs.utah.edu/dept/old/texinfo/as/gprof.html\${BLACK}"
 echo -e "     |     ${SrsGprofSummaryColor}rm -f gmon.out; ./objs/srs -c conf/console.conf # start gprof\${BLACK}"
 echo -e "     |     ${SrsGprofSummaryColor}killall -2 srs # or CTRL+C to stop gprof\${BLACK}"
@@ -160,7 +160,7 @@ echo -e "     |${SrsResearchSummaryColor}research: ./objs/research, api server,
 echo -e "     \${BLACK}+------------------------------------------------------------------------------------\${BLACK}"
 echo -e "     |${SrsUtestSummaryColor}utest: ./objs/srs_utest, the utest for srs\${BLACK}"
 echo -e "     \${BLACK}+------------------------------------------------------------------------------------\${BLACK}"
-echo -e "     |${SrsLibrtmpSummaryColor}librtmp @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLibrtmp\${BLACK}"
+echo -e "     |${SrsLibrtmpSummaryColor}librtmp @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLibrtmp\${BLACK}"
 echo -e "     |${SrsLibrtmpSummaryColor}librtmp: ./objs/include, ./objs/lib, the srs-librtmp library\${BLACK}"
 echo -e "     |     ${SrsLibrtmpSummaryColor}simple handshake: publish/play stream with simple handshake to server\${BLACK}"
 echo -e "     |     ${SrsLibrtmpSSLSummaryColor}complex handshake: it's not required for client, recommend disable it\${BLACK}"
@@ -171,30 +171,30 @@ echo -e "     |     ${SrsLibrtmpSummaryColor}librtmp-sample: ./research/librtmp/
 echo -e "     |     ${SrsLibrtmpSummaryColor}librtmp-sample: ./research/librtmp/objs/srs_bandwidth_check\${BLACK}"
 echo -e "     \${BLACK}+------------------------------------------------------------------------------------\${BLACK}"
 echo -e "     |\${GREEN}server: ./objs/srs -c conf/srs.conf, start the srs server\${BLACK}"
-echo -e "     |     ${SrsHlsSummaryColor}hls @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DeliveryHLS\${BLACK}"
+echo -e "     |     ${SrsHlsSummaryColor}hls @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryHLS\${BLACK}"
 echo -e "     |     ${SrsHlsSummaryColor}hls: generate m3u8 and ts from rtmp stream\${BLACK}"
-echo -e "     |     ${SrsDvrSummaryColor}dvr @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DVR\${BLACK}"
+echo -e "     |     ${SrsDvrSummaryColor}dvr @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DVR\${BLACK}"
 echo -e "     |     ${SrsDvrSummaryColor}dvr: record RTMP stream to flv files.\${BLACK}"
-echo -e "     |     ${SrsNginxSummaryColor}nginx @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_DeliveryHLS\${BLACK}"
+echo -e "     |     ${SrsNginxSummaryColor}nginx @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryHLS\${BLACK}"
 echo -e "     |     ${SrsNginxSummaryColor}nginx: delivery HLS stream by nginx\${BLACK}"
 echo -e "     |     ${SrsNginxSummaryColor}nginx: sudo ./objs/nginx/sbin/nginx\${BLACK}"
-echo -e "     |     ${SrsSslSummaryColor}ssl @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_RTMPHandshake\${BLACK}"
+echo -e "     |     ${SrsSslSummaryColor}ssl @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RTMPHandshake\${BLACK}"
 echo -e "     |     ${SrsSslSummaryColor}ssl: support RTMP complex handshake for client required, for instance, flash\${BLACK}"
-echo -e "     |     ${SrsFfmpegSummaryColor}ffmpeg @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_FFMPEG\${BLACK}"
+echo -e "     |     ${SrsFfmpegSummaryColor}ffmpeg @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_FFMPEG\${BLACK}"
 echo -e "     |     ${SrsFfmpegSummaryColor}ffmpeg: transcode, mux, ingest tool\${BLACK}"
 echo -e "     |     ${SrsFfmpegSummaryColor}ffmpeg: ./objs/ffmpeg/bin/ffmpeg\${BLACK}"
-echo -e "     |     ${SrsTranscodeSummaryColor}transcode @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_FFMPEG\${BLACK}"
+echo -e "     |     ${SrsTranscodeSummaryColor}transcode @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_FFMPEG\${BLACK}"
 echo -e "     |     ${SrsTranscodeSummaryColor}transcode: support transcoding RTMP stream\${BLACK}"
-echo -e "     |     ${SrsIngestSummaryColor}ingest @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Ingest\${BLACK}"
+echo -e "     |     ${SrsIngestSummaryColor}ingest @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Ingest\${BLACK}"
 echo -e "     |     ${SrsIngestSummaryColor}ingest: support ingest file/stream/device then push to SRS by RTMP stream\${BLACK}"
-echo -e "     |     ${SrsHttpCallbackSummaryColor}http-callback @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HTTPCallback\${BLACK}"
+echo -e "     |     ${SrsHttpCallbackSummaryColor}http-callback @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPCallback\${BLACK}"
 echo -e "     |     ${SrsHttpCallbackSummaryColor}http-callback: support http callback for authentication and event injection\${BLACK}"
-echo -e "     |     ${SrsHttpServerSummaryColor}http-server @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HTTPServer\${BLACK}"
+echo -e "     |     ${SrsHttpServerSummaryColor}http-server @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPServer\${BLACK}"
 echo -e "     |     ${SrsHttpServerSummaryColor}http-server: support http server to delivery http stream\${BLACK}"
-echo -e "     |     ${SrsHttpApiSummaryColor}http-api @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HTTPApi\${BLACK}"
+echo -e "     |     ${SrsHttpApiSummaryColor}http-api @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPApi\${BLACK}"
 echo -e "     |     ${SrsHttpApiSummaryColor}http-api: support http api to manage server\${BLACK}"
 echo -e "     \${BLACK}+------------------------------------------------------------------------------------\${BLACK}"
-echo -e "\${GREEN}binaries @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Build\${BLACK}"
+echo -e "\${GREEN}binaries @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Build\${BLACK}"
 
 echo "you can:"
 echo "      ./objs/srs -c conf/srs.conf"
@@ -286,7 +286,7 @@ install-api: install
 	@echo "     sudo ln -sf \$(SRS_PREFIX)/etc/init.d/srs-api /etc/init.d/srs-api"
 	@echo "     /etc/init.d/srs-api start"
 	@echo "     http://\$(shell bash auto/local_ip.sh):8085"
-	@echo "@see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_LinuxService"
+	@echo "@see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_LinuxService"
 
 install:
 	@echo "mkdir \$(__REAL_INSTALL)"
@@ -308,7 +308,7 @@ install:
 	@echo "srs installed, to link and start srs:"
 	@echo "     sudo ln -sf \$(SRS_PREFIX)/etc/init.d/srs /etc/init.d/srs"
 	@echo "     /etc/init.d/srs start"
-	@echo "@see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_LinuxService"
+	@echo "@see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_LinuxService"
 
 END
 
diff --git a/trunk/research/arm/jmp.cpp b/trunk/research/arm/jmp.cpp
index 2b2a599133..d9469405dc 100644
--- a/trunk/research/arm/jmp.cpp
+++ b/trunk/research/arm/jmp.cpp
@@ -1,5 +1,5 @@
 /*
-# see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLinuxArm
+# see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLinuxArm
  arm-linux-gnueabi-g++ -o jmp jmp.cpp -static
  arm-linux-gnueabi-strip jmp
 */
diff --git a/trunk/research/arm/jmp_sp.cpp b/trunk/research/arm/jmp_sp.cpp
index 24ea4bc9b6..1fb0be89de 100644
--- a/trunk/research/arm/jmp_sp.cpp
+++ b/trunk/research/arm/jmp_sp.cpp
@@ -1,5 +1,5 @@
 /*
-# see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLinuxArm
+# see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLinuxArm
  arm-linux-gnueabi-g++ -g -o jmp_sp jmp_sp.cpp -static
  arm-linux-gnueabi-strip jmp_sp
 */
diff --git a/trunk/research/arm/test.cpp b/trunk/research/arm/test.cpp
index 970d04283b..1bad17d1cf 100644
--- a/trunk/research/arm/test.cpp
+++ b/trunk/research/arm/test.cpp
@@ -1,5 +1,5 @@
 /*
-# see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLinuxArm
+# see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLinuxArm
  arm-linux-gnueabi-g++ -o test test.cpp -static
  arm-linux-gnueabi-strip test
 */
diff --git a/trunk/research/librtmp/Makefile b/trunk/research/librtmp/Makefile
index af2b477cb0..7e0dc35bcf 100755
--- a/trunk/research/librtmp/Makefile
+++ b/trunk/research/librtmp/Makefile
@@ -32,7 +32,7 @@ help:
 	@echo "Remark: srs Makefile will auto invoke this by --with/without-ssl, "
 	@echo "     that is, if user specified ssl(by --with-ssl), srs will make this by 'make ssl'"
 	@echo "     that is, if user not use ssl(by --without-ssl), use 'make nossl'"
-	@echo "     see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_v1_Build"
+	@echo "     see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_v1_Build"
 	@echo "Remark: before make this sample, user must make the srs, with/without ssl"
     
 clean:
diff --git a/trunk/scripts/install.sh b/trunk/scripts/install.sh
index 9f935e5ddf..54b0900a98 100755
--- a/trunk/scripts/install.sh
+++ b/trunk/scripts/install.sh
@@ -108,7 +108,7 @@ else
 fi
 
 echo ""
-echo "see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_LinuxService"
+echo "see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_LinuxService"
 echo "install success, you can:"
 echo -e "${GREEN}      sudo /etc/init.d/srs start${BLACK}"
 echo "srs root is ${INSTALL}"
diff --git a/trunk/scripts/package.sh b/trunk/scripts/package.sh
index 2a6072bf36..901b342e61 100755
--- a/trunk/scripts/package.sh
+++ b/trunk/scripts/package.sh
@@ -108,7 +108,7 @@ fi
 ok_msg "real os is ${os_name}-${os_major_version} ${os_release} ${os_machine}"
 
 # build srs
-# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Build
+# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Build
 ok_msg "start build srs"
 if [ $ARM = YES ]; then
     (
diff --git a/trunk/scripts/run.sh b/trunk/scripts/run.sh
index 2f0082e3bb..4f48495477 100755
--- a/trunk/scripts/run.sh
+++ b/trunk/scripts/run.sh
@@ -51,4 +51,4 @@ echo -e "${GREEN}请在hosts中添加一行:${BLACK}"
 echo -e "${RED}    $ip demo.srs.com${BLACK}"
 echo -e "${GREEN}演示地址:${BLACK}"
 echo -e "${RED}    http://$ip:$port${BLACK}"
-echo -e "@see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SampleDemo"
+echo -e "@see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleDemo"
diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp
index d625ab9d06..285bb37a83 100644
--- a/trunk/src/app/srs_app_config.cpp
+++ b/trunk/src/app/srs_app_config.cpp
@@ -48,7 +48,7 @@ using namespace std;
 
 using namespace _srs_internal;
 
-#define SRS_WIKI_URL_LOG "https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLog"
+#define SRS_WIKI_URL_LOG "https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLog"
 
 // '\n'
 #define __LF (char)0x0a
diff --git a/trunk/src/app/srs_app_reload.hpp b/trunk/src/app/srs_app_reload.hpp
index 48b2715a04..85d5be016e 100644
--- a/trunk/src/app/srs_app_reload.hpp
+++ b/trunk/src/app/srs_app_reload.hpp
@@ -36,7 +36,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 * when reload callback, the config is updated yet.
 * 
 * features not support reload, 
-* @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_Reload#notsupportedfeatures
+* @see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Reload#notsupportedfeatures
 */
 class ISrsReloadHandler
 {
diff --git a/trunk/src/main/srs_main_server.cpp b/trunk/src/main/srs_main_server.cpp
index bd7f068f2d..6cef9d9ddd 100644
--- a/trunk/src/main/srs_main_server.cpp
+++ b/trunk/src/main/srs_main_server.cpp
@@ -194,7 +194,7 @@ int main(int argc, char** argv)
     // for special features.
 #ifdef SRS_AUTO_HTTP_SERVER
     srs_warn("http server is dev feature, "
-        "@see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_HTTPServer#feature");
+        "@see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPServer#feature");
 #endif
     
     /**
diff --git a/trunk/src/utest/srs_utest_config.cpp b/trunk/src/utest/srs_utest_config.cpp
index c6b235fb1f..072a16a7aa 100644
--- a/trunk/src/utest/srs_utest_config.cpp
+++ b/trunk/src/utest/srs_utest_config.cpp
@@ -505,7 +505,7 @@ std::string __full_conf = ""
     "    # it's strongly recommend to open the debug_srs_upnode,                                                                            \n"
     "    # when connect to upnode, it will take the debug info,                                                                             \n"
     "    # for example, the id, source id, pid.                                                                                             \n"
-    "    # please see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_SrsLog                                                          \n"
+    "    # please see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLog                                                          \n"
     "    # default: on                                                                                                                      \n"
     "    debug_srs_upnode    on;                                                                                                            \n"
     "}                                                                                                                                      \n"

From f4b779060be13ca5997807281c53492cd5e6dfd7 Mon Sep 17 00:00:00 2001
From: winlin 
Date: Fri, 24 Oct 2014 11:35:36 +0800
Subject: [PATCH 036/800] add prefix wiki/v1_CN_ or wiki/v1_EN_ to wiki.

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index f80bd5e401..0481ca09e0 100755
--- a/README.md
+++ b/README.md
@@ -123,7 +123,7 @@ cd simple-rtmp-server/trunk
 ## Wiki
 
 Please select your language:
-* [English](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Home)
+* [English](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN__Home)
 * [Chinese](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Home)
 
 ## Donation

From 7d70864b338e5c8d49c9587d31891b88cf8e3884 Mon Sep 17 00:00:00 2001
From: winlin 
Date: Fri, 24 Oct 2014 13:16:08 +0800
Subject: [PATCH 037/800] for bug #186, read the args when discovery it. 2.0.6

---
 trunk/src/core/srs_core.hpp           | 2 +-
 trunk/src/rtmp/srs_protocol_stack.cpp | 8 +++++++-
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp
index ab349b290c..c6f6ae378c 100644
--- a/trunk/src/core/srs_core.hpp
+++ b/trunk/src/core/srs_core.hpp
@@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 // current release version
 #define VERSION_MAJOR "2"
 #define VERSION_MINOR "0"
-#define VERSION_REVISION "5"
+#define VERSION_REVISION "6"
 #define RTMP_SIG_SRS_VERSION VERSION_MAJOR"."VERSION_MINOR"."VERSION_REVISION
 // server info.
 #define RTMP_SIG_SRS_KEY "SRS"
diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp
index ce7222b6c8..ccaffb2856 100644
--- a/trunk/src/rtmp/srs_protocol_stack.cpp
+++ b/trunk/src/rtmp/srs_protocol_stack.cpp
@@ -1869,11 +1869,17 @@ int SrsConnectAppPacket::decode(SrsStream* stream)
         // the args maybe any amf0, for instance, a string. we should drop if not object.
         SrsAmf0Any* any = NULL;
         if ((ret = SrsAmf0Any::discovery(stream, &any)) != ERROR_SUCCESS) {
-            srs_error("amf0 decode connect args failed. ret=%d", ret);
+            srs_error("amf0 find connect args failed. ret=%d", ret);
             return ret;
         }
         srs_assert(any);
         
+        // read the instance
+        if ((ret = any->read(stream)) != ERROR_SUCCESS) {
+            srs_error("amf0 decode connect args failed. ret=%d", ret);
+            return ret;
+        }
+        
         // drop when not an AMF0 object.
         if (!any->is_object()) {
             srs_warn("drop the args, see: '4.1.1. connect', marker=%#x", any->marker);

From 05cce971407e13d30e2b0470b2fa3c43f5135180 Mon Sep 17 00:00:00 2001
From: winlin 
Date: Fri, 24 Oct 2014 13:23:52 +0800
Subject: [PATCH 038/800] for bug #186, read the args when discovery it. 2.0.6

---
 trunk/src/rtmp/srs_protocol_stack.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp
index ccaffb2856..397095b5ff 100644
--- a/trunk/src/rtmp/srs_protocol_stack.cpp
+++ b/trunk/src/rtmp/srs_protocol_stack.cpp
@@ -1877,6 +1877,7 @@ int SrsConnectAppPacket::decode(SrsStream* stream)
         // read the instance
         if ((ret = any->read(stream)) != ERROR_SUCCESS) {
             srs_error("amf0 decode connect args failed. ret=%d", ret);
+            srs_freep(any);
             return ret;
         }
         

From 6a3418cd453f0d72c4bd853ed1bd4ebc5285be39 Mon Sep 17 00:00:00 2001
From: winlin 
Date: Sat, 25 Oct 2014 17:12:56 +0800
Subject: [PATCH 039/800] fix #185, AMF0 support 0x0B the date type codec.
 2.0.5.

---
 README.md                                     |   1 +
 .../players/srs_player/release/srs_player.swf | Bin 5572 -> 5586 bytes
 .../players/srs_player/src/srs_player.as      |   2 +-
 trunk/src/core/srs_core.hpp                   |   2 +-
 trunk/src/rtmp/srs_protocol_amf0.cpp          | 164 +++++++++++++++++-
 trunk/src/rtmp/srs_protocol_amf0.hpp          |  55 ++++++
 6 files changed, 220 insertions(+), 4 deletions(-)

diff --git a/README.md b/README.md
index 0481ca09e0..1a01609a35 100755
--- a/README.md
+++ b/README.md
@@ -220,6 +220,7 @@ Supported operating systems and hardware:
 * 2013-10-17, Created.
## History +* v2.0, 2014-10-25, fix [#185](https://github.com/winlinvip/simple-rtmp-server/issues/185), AMF0 support 0x0B the date type codec. 2.0.5. * v2.0, 2014-10-24, fix [#186](https://github.com/winlinvip/simple-rtmp-server/issues/186), hotfix for bug #186, drop connect args when not object. 2.0.4. * v2.0, 2014-10-24, rename wiki/xxx to wiki/v1_CN_xxx. 2.0.3. * v2.0, 2014-10-19, fix [#184](https://github.com/winlinvip/simple-rtmp-server/issues/184), support AnnexB in RTMP body for HLS. 2.0.2 diff --git a/trunk/research/players/srs_player/release/srs_player.swf b/trunk/research/players/srs_player/release/srs_player.swf index dff28fe19eb649a1a3679a967bffe4e3e7e4d346..82aca05c3bf3e820bd72ea73fc7d796b7f6e860b 100755 GIT binary patch delta 5475 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 ZJuJUw!BTk@zBo+%=Z_UY{|`V@0IVk`(O3Wg delta 5447 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 diff --git a/trunk/research/players/srs_player/src/srs_player.as b/trunk/research/players/srs_player/src/srs_player.as index a8fa5ec2ce..5cf1a3c535 100755 --- a/trunk/research/players/srs_player/src/srs_player.as +++ b/trunk/research/players/srs_player/src/srs_player.as @@ -377,7 +377,7 @@ package this.media_conn.connect(null); } else { var tcUrl:String = this.user_url.substr(0, this.user_url.lastIndexOf("/")); - this.media_conn.connect(tcUrl); + this.media_conn.connect(tcUrl, new Date()); } } diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index c6f6ae378c..25b19cb04b 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR "2" #define VERSION_MINOR "0" -#define VERSION_REVISION "6" +#define VERSION_REVISION "7" #define RTMP_SIG_SRS_VERSION VERSION_MAJOR"."VERSION_MINOR"."VERSION_REVISION // server info. #define RTMP_SIG_SRS_KEY "SRS" diff --git a/trunk/src/rtmp/srs_protocol_amf0.cpp b/trunk/src/rtmp/srs_protocol_amf0.cpp index 91df826144..2b6fdb95b2 100644 --- a/trunk/src/rtmp/srs_protocol_amf0.cpp +++ b/trunk/src/rtmp/srs_protocol_amf0.cpp @@ -109,6 +109,11 @@ bool SrsAmf0Any::is_strict_array() return marker == RTMP_AMF0_StrictArray; } +bool SrsAmf0Any::is_date() +{ + return marker == RTMP_AMF0_Date; +} + bool SrsAmf0Any::is_complex_object() { return is_object() || is_object_eof() || is_ecma_array() || is_strict_array(); @@ -142,6 +147,20 @@ double SrsAmf0Any::to_number() return p->value; } +int64_t SrsAmf0Any::to_date() +{ + SrsAmf0Date* p = dynamic_cast(this); + srs_assert(p != NULL); + return p->date(); +} + +int16_t SrsAmf0Any::to_date_time_zone() +{ + SrsAmf0Date* p = dynamic_cast(this); + srs_assert(p != NULL); + return p->time_zone(); +} + SrsAmf0Object* SrsAmf0Any::to_object() { SrsAmf0Object* p = dynamic_cast(this); @@ -189,6 +208,9 @@ void __srs_amf0_do_print(SrsAmf0Any* any, stringstream& ss, int level) ss << "Number " << std::fixed << any->to_number() << endl; } else if (any->is_string()) { ss << "String " << any->to_str() << endl; + } else if (any->is_date()) { + ss << "Date " << std::hex << any->to_date() + << "/" << std::hex << any->to_date_time_zone() << endl; } else if (any->is_null()) { ss << "Null" << endl; } else if (any->is_ecma_array()) { @@ -304,6 +326,11 @@ SrsAmf0StrictArray* SrsAmf0Any::strict_array() return new SrsAmf0StrictArray(); } +SrsAmf0Any* SrsAmf0Any::date(int64_t value) +{ + return new SrsAmf0Date(value); +} + int SrsAmf0Any::discovery(SrsStream* stream, SrsAmf0Any** ppvalue) { int ret = ERROR_SUCCESS; @@ -360,6 +387,10 @@ int SrsAmf0Any::discovery(SrsStream* stream, SrsAmf0Any** ppvalue) *ppvalue = SrsAmf0Any::strict_array(); return ret; } + case RTMP_AMF0_Date: { + *ppvalue = SrsAmf0Any::date(); + return ret; + } case RTMP_AMF0_Invalid: default: { ret = ERROR_RTMP_AMF0_INVALID; @@ -805,7 +836,7 @@ int SrsAmf0EcmaArray::read(SrsStream* stream) if (marker != RTMP_AMF0_EcmaArray) { ret = ERROR_RTMP_AMF0_DECODE; srs_error("amf0 check ecma_array marker failed. " - "marker=%#x, required=%#x, ret=%d", marker, RTMP_AMF0_Object, ret); + "marker=%#x, required=%#x, ret=%d", marker, RTMP_AMF0_EcmaArray, ret); return ret; } srs_verbose("amf0 read ecma_array marker success"); @@ -1003,7 +1034,7 @@ int SrsAmf0StrictArray::read(SrsStream* stream) if (marker != RTMP_AMF0_StrictArray) { ret = ERROR_RTMP_AMF0_DECODE; srs_error("amf0 check strict_array marker failed. " - "marker=%#x, required=%#x, ret=%d", marker, RTMP_AMF0_Object, ret); + "marker=%#x, required=%#x, ret=%d", marker, RTMP_AMF0_StrictArray, ret); return ret; } srs_verbose("amf0 read strict_array marker success"); @@ -1127,6 +1158,11 @@ int SrsAmf0Size::number() return 1 + 8; } +int SrsAmf0Size::date() +{ + return 1 + 8 + 2; +} + int SrsAmf0Size::null() { return 1; @@ -1278,6 +1314,130 @@ SrsAmf0Any* SrsAmf0Number::copy() return copy; } +SrsAmf0Date::SrsAmf0Date(int64_t value) +{ + marker = RTMP_AMF0_Date; + _date_value = value; + _time_zone = 0; +} + +SrsAmf0Date::~SrsAmf0Date() +{ +} + +int SrsAmf0Date::total_size() +{ + return SrsAmf0Size::date(); +} + +int SrsAmf0Date::read(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + // marker + if (!stream->require(1)) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 read date marker failed. ret=%d", ret); + return ret; + } + + char marker = stream->read_1bytes(); + if (marker != RTMP_AMF0_Date) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 check date marker failed. " + "marker=%#x, required=%#x, ret=%d", marker, RTMP_AMF0_Date, ret); + return ret; + } + srs_verbose("amf0 read date marker success"); + + // date value + // An ActionScript Date is serialized as the number of milliseconds + // elapsed since the epoch of midnight on 1st Jan 1970 in the UTC + // time zone. + if (!stream->require(8)) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 read date failed. ret=%d", ret); + return ret; + } + + _date_value = stream->read_8bytes(); + srs_verbose("amf0 read date success. date=%"PRId64, _date_value); + + // time zone + // While the design of this type reserves room for time zone offset + // information, it should not be filled in, nor used, as it is unconventional + // to change time zones when serializing dates on a network. It is suggested + // that the time zone be queried independently as needed. + if (!stream->require(2)) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 read time zone failed. ret=%d", ret); + return ret; + } + + _time_zone = stream->read_2bytes(); + srs_verbose("amf0 read time zone success. zone=%d", _time_zone); + + return ret; +} +int SrsAmf0Date::write(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + // marker + if (!stream->require(1)) { + ret = ERROR_RTMP_AMF0_ENCODE; + srs_error("amf0 write date marker failed. ret=%d", ret); + return ret; + } + + stream->write_1bytes(RTMP_AMF0_Date); + srs_verbose("amf0 write date marker success"); + + // date value + if (!stream->require(8)) { + ret = ERROR_RTMP_AMF0_ENCODE; + srs_error("amf0 write date failed. ret=%d", ret); + return ret; + } + + stream->write_8bytes(_date_value); + srs_verbose("amf0 write date success. date=%"PRId64, _date_value); + + // time zone + if (!stream->require(2)) { + ret = ERROR_RTMP_AMF0_ENCODE; + srs_error("amf0 write time zone failed. ret=%d", ret); + return ret; + } + + stream->write_2bytes(_time_zone); + srs_verbose("amf0 write time zone success. date=%d", _time_zone); + + srs_verbose("write date object success."); + + return ret; +} + +SrsAmf0Any* SrsAmf0Date::copy() +{ + SrsAmf0Date* copy = new SrsAmf0Date(0); + + copy->_date_value = _date_value; + copy->_time_zone = _time_zone; + + return copy; +} + +int64_t SrsAmf0Date::date() +{ + return _date_value; +} + +int16_t SrsAmf0Date::time_zone() +{ + return _time_zone; +} + SrsAmf0Null::SrsAmf0Null() { marker = RTMP_AMF0_Null; diff --git a/trunk/src/rtmp/srs_protocol_amf0.hpp b/trunk/src/rtmp/srs_protocol_amf0.hpp index 1ecc3f4ae4..bbf27459a7 100644 --- a/trunk/src/rtmp/srs_protocol_amf0.hpp +++ b/trunk/src/rtmp/srs_protocol_amf0.hpp @@ -43,6 +43,7 @@ namespace _srs_internal { class SrsUnSortedHashtable; class SrsAmf0ObjectEOF; + class SrsAmf0Date; } /* @@ -185,6 +186,12 @@ class SrsAmf0Any */ virtual bool is_strict_array(); /** + * whether current instance is an AMF0 date. + * @return true if instance is an AMF0 date; otherwise, false. + * @remark, if true, use to_date() to get its value. + */ + virtual bool is_date(); + /** * whether current instance is an AMF0 object, object-EOF, ecma-array or strict-array. */ virtual bool is_complex_object(); @@ -212,6 +219,12 @@ class SrsAmf0Any */ virtual double to_number(); /** + * convert instance to date, + * @remark assert is_date(), user must ensure the type then convert. + */ + virtual int64_t to_date(); + virtual int16_t to_date_time_zone(); + /** * convert instance to amf0 object, * @remark assert is_object(), user must ensure the type then convert. */ @@ -274,6 +287,10 @@ class SrsAmf0Any */ static SrsAmf0Any* number(double value = 0.0); /** + * create an AMF0 date instance + */ + static SrsAmf0Any* date(int64_t value = 0); + /** * create an AMF0 null instance */ static SrsAmf0Any* null(); @@ -532,6 +549,7 @@ class SrsAmf0Size static int utf8(std::string value); static int str(std::string value); static int number(); + static int date(); static int null(); static int undefined(); static int boolean(); @@ -673,6 +691,43 @@ namespace _srs_internal virtual SrsAmf0Any* copy(); }; + /** + * 2.13 Date Type + * time-zone = S16 ; reserved, not supported should be set to 0x0000 + * date-type = date-marker DOUBLE time-zone + * @see: https://github.com/winlinvip/simple-rtmp-server/issues/185 + */ + class SrsAmf0Date : public SrsAmf0Any + { + private: + int64_t _date_value; + int16_t _time_zone; + private: + friend class SrsAmf0Any; + /** + * make amf0 date to private, + * use should never declare it, use SrsAmf0Any::date() to create it. + */ + SrsAmf0Date(int64_t value); + public: + virtual ~SrsAmf0Date(); + // serialize/deserialize to/from stream. + public: + virtual int total_size(); + virtual int read(SrsStream* stream); + virtual int write(SrsStream* stream); + virtual SrsAmf0Any* copy(); + public: + /** + * get the date value. + */ + virtual int64_t date(); + /** + * get the time_zone. + */ + virtual int16_t time_zone(); + }; + /** * read amf0 null from stream. * 2.7 null Type From fb906f0afcb5a74173960c7b9c4c4960dc4eb74d Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 25 Oct 2014 17:15:33 +0800 Subject: [PATCH 040/800] revert the player for bug #185. --- .../players/srs_player/release/srs_player.swf | Bin 5586 -> 5573 bytes .../players/srs_player/src/srs_player.as | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/trunk/research/players/srs_player/release/srs_player.swf b/trunk/research/players/srs_player/release/srs_player.swf index 82aca05c3bf3e820bd72ea73fc7d796b7f6e860b..552aed8e177ef9ab5ef0469577e40246808cbc21 100755 GIT binary patch literal 5573 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 Tyb7NhrvJ0YN}&G-c8&a-E*IsQ literal 5586 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 diff --git a/trunk/research/players/srs_player/src/srs_player.as b/trunk/research/players/srs_player/src/srs_player.as index 5cf1a3c535..a8fa5ec2ce 100755 --- a/trunk/research/players/srs_player/src/srs_player.as +++ b/trunk/research/players/srs_player/src/srs_player.as @@ -377,7 +377,7 @@ package this.media_conn.connect(null); } else { var tcUrl:String = this.user_url.substr(0, this.user_url.lastIndexOf("/")); - this.media_conn.connect(tcUrl, new Date()); + this.media_conn.connect(tcUrl); } } From 85c2398e78544578bfa587d0ecff35f702248e71 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 25 Oct 2014 17:16:38 +0800 Subject: [PATCH 041/800] update readme. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1a01609a35..8abbcf82b2 100755 --- a/README.md +++ b/README.md @@ -220,8 +220,8 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History -* v2.0, 2014-10-25, fix [#185](https://github.com/winlinvip/simple-rtmp-server/issues/185), AMF0 support 0x0B the date type codec. 2.0.5. -* v2.0, 2014-10-24, fix [#186](https://github.com/winlinvip/simple-rtmp-server/issues/186), hotfix for bug #186, drop connect args when not object. 2.0.4. +* v2.0, 2014-10-25, fix [#185](https://github.com/winlinvip/simple-rtmp-server/issues/185), AMF0 support 0x0B the date type codec. 2.0.7. +* v2.0, 2014-10-24, fix [#186](https://github.com/winlinvip/simple-rtmp-server/issues/186), hotfix for bug #186, drop connect args when not object. 2.0.6. * v2.0, 2014-10-24, rename wiki/xxx to wiki/v1_CN_xxx. 2.0.3. * v2.0, 2014-10-19, fix [#184](https://github.com/winlinvip/simple-rtmp-server/issues/184), support AnnexB in RTMP body for HLS. 2.0.2 * v2.0, 2014-10-18, remove supports for OSX(darwin). 2.0.1. From 388305f44cdb368a58f285158832cd6bb6202116 Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 26 Oct 2014 13:43:11 +0800 Subject: [PATCH 042/800] update st research. --- trunk/research/st/srs.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/trunk/research/st/srs.c b/trunk/research/st/srs.c index 442925a3fe..aa6f60df09 100644 --- a/trunk/research/st/srs.c +++ b/trunk/research/st/srs.c @@ -1,7 +1,38 @@ +#include +#include + #include "public.h" +#define srs_trace(msg, ...) printf(msg, ##__VA_ARGS__);printf("\n") + +void* pfn(void* arg) +{ + st_sleep(1); + srs_trace("st thread is ok"); + return NULL; +} + int main(int argc, char** argv) { + if (st_set_eventsys(ST_EVENTSYS_ALT) < 0) { + srs_trace("st_set_eventsys failed"); + return -1; + } + + if (st_init() < 0) { + srs_trace("st_init failed"); + return -1; + } + + if (!st_thread_create(pfn, NULL, 0, 0)) { + srs_trace("st_thread_create failed"); + return -1; + } + + srs_trace("st is ok"); + + st_thread_exit(NULL); + return 0; } From 79ab9f05f3abe70dafdfbbc8b83d8dcda2035eb3 Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 26 Oct 2014 13:55:37 +0800 Subject: [PATCH 043/800] refs #182: update the st, use cond and mutex. --- trunk/research/st/srs.c | 44 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/trunk/research/st/srs.c b/trunk/research/st/srs.c index aa6f60df09..77bccd1596 100644 --- a/trunk/research/st/srs.c +++ b/trunk/research/st/srs.c @@ -5,10 +5,22 @@ #define srs_trace(msg, ...) printf(msg, ##__VA_ARGS__);printf("\n") +st_cond_t cond = NULL; +st_mutex_t mutex = NULL; + void* pfn(void* arg) { - st_sleep(1); - srs_trace("st thread is ok"); + st_usleep(100 * 1000); + st_cond_signal(cond); + + st_mutex_lock(mutex); + srs_trace("2. st mutex is ok"); + st_mutex_unlock(mutex); + + st_usleep(100 * 1000); + srs_trace("3. st thread is ok"); + st_cond_signal(cond); + return NULL; } @@ -23,14 +35,40 @@ int main(int argc, char** argv) srs_trace("st_init failed"); return -1; } + + if ((cond = st_cond_new()) == NULL) { + srs_trace("st_cond_new failed"); + return -1; + } + + if ((mutex = st_mutex_new()) == NULL) { + srs_trace("st_mutex_new failed"); + return -1; + } if (!st_thread_create(pfn, NULL, 0, 0)) { srs_trace("st_thread_create failed"); return -1; } - srs_trace("st is ok"); + // lock mutex to control thread. + st_mutex_lock(mutex); + + // wait thread to ready. + st_cond_wait(cond); + srs_trace("1. st cond is ok"); + + // release mutex to control thread + st_usleep(100 * 1000); + st_mutex_unlock(mutex); + + // wait thread to exit. + st_cond_wait(cond); + srs_trace("4. st is ok"); + // cleanup. + st_cond_destroy(cond); + st_mutex_destroy(mutex); st_thread_exit(NULL); return 0; From 9aba47cbe3752dff4d85024d0859babb02bf600f Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 26 Oct 2014 14:04:31 +0800 Subject: [PATCH 044/800] refs #182: research st, add multiple threadds. --- trunk/research/st/srs.c | 66 ++++++++++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 18 deletions(-) diff --git a/trunk/research/st/srs.c b/trunk/research/st/srs.c index 77bccd1596..3d10d24ff2 100644 --- a/trunk/research/st/srs.c +++ b/trunk/research/st/srs.c @@ -5,11 +5,16 @@ #define srs_trace(msg, ...) printf(msg, ##__VA_ARGS__);printf("\n") +st_mutex_t start = NULL; st_cond_t cond = NULL; st_mutex_t mutex = NULL; -void* pfn(void* arg) +void* master(void* arg) { + // wait for main to start this thread. + st_mutex_lock(start); + st_mutex_unlock(start); + st_usleep(100 * 1000); st_cond_signal(cond); @@ -24,6 +29,30 @@ void* pfn(void* arg) return NULL; } +void* slave(void* arg) +{ + // lock mutex to control thread. + st_mutex_lock(mutex); + + // wait for main to start this thread. + st_mutex_lock(start); + st_mutex_unlock(start); + + // wait thread to ready. + st_cond_wait(cond); + srs_trace("1. st cond is ok"); + + // release mutex to control thread + st_usleep(100 * 1000); + st_mutex_unlock(mutex); + + // wait thread to exit. + st_cond_wait(cond); + srs_trace("4. st is ok"); + + return NULL; +} + int main(int argc, char** argv) { if (st_set_eventsys(ST_EVENTSYS_ALT) < 0) { @@ -36,40 +65,41 @@ int main(int argc, char** argv) return -1; } + if ((start = st_mutex_new()) == NULL) { + srs_trace("st_mutex_new start failed"); + return -1; + } + st_mutex_lock(start); + if ((cond = st_cond_new()) == NULL) { - srs_trace("st_cond_new failed"); + srs_trace("st_cond_new cond failed"); return -1; } if ((mutex = st_mutex_new()) == NULL) { - srs_trace("st_mutex_new failed"); + srs_trace("st_mutex_new mutex failed"); return -1; } - if (!st_thread_create(pfn, NULL, 0, 0)) { + if (!st_thread_create(master, NULL, 0, 0)) { srs_trace("st_thread_create failed"); return -1; } - // lock mutex to control thread. - st_mutex_lock(mutex); - - // wait thread to ready. - st_cond_wait(cond); - srs_trace("1. st cond is ok"); - - // release mutex to control thread - st_usleep(100 * 1000); - st_mutex_unlock(mutex); + if (!st_thread_create(slave, NULL, 0, 0)) { + srs_trace("st_thread_create failed"); + return -1; + } - // wait thread to exit. - st_cond_wait(cond); - srs_trace("4. st is ok"); + // run all threads. + st_mutex_unlock(start); // cleanup. + st_thread_exit(NULL); + + st_mutex_destroy(start); st_cond_destroy(cond); st_mutex_destroy(mutex); - st_thread_exit(NULL); return 0; } From 69fdec411f808dbde95cbaf7887905211da5cc38 Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 26 Oct 2014 16:35:12 +0800 Subject: [PATCH 045/800] refs #182: rename to sync test. --- trunk/research/st/srs.c | 90 ++++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 42 deletions(-) diff --git a/trunk/research/st/srs.c b/trunk/research/st/srs.c index 3d10d24ff2..5e25888b14 100644 --- a/trunk/research/st/srs.c +++ b/trunk/research/st/srs.c @@ -5,102 +5,108 @@ #define srs_trace(msg, ...) printf(msg, ##__VA_ARGS__);printf("\n") -st_mutex_t start = NULL; -st_cond_t cond = NULL; -st_mutex_t mutex = NULL; +st_mutex_t sync_start = NULL; +st_cond_t sync_cond = NULL; +st_mutex_t sync_mutex = NULL; -void* master(void* arg) +void* sync_master(void* arg) { - // wait for main to start this thread. - st_mutex_lock(start); - st_mutex_unlock(start); + // wait for main to sync_start this thread. + st_mutex_lock(sync_start); + st_mutex_unlock(sync_start); st_usleep(100 * 1000); - st_cond_signal(cond); + st_cond_signal(sync_cond); - st_mutex_lock(mutex); + st_mutex_lock(sync_mutex); srs_trace("2. st mutex is ok"); - st_mutex_unlock(mutex); + st_mutex_unlock(sync_mutex); st_usleep(100 * 1000); srs_trace("3. st thread is ok"); - st_cond_signal(cond); + st_cond_signal(sync_cond); return NULL; } -void* slave(void* arg) +void* sync_slave(void* arg) { // lock mutex to control thread. - st_mutex_lock(mutex); + st_mutex_lock(sync_mutex); - // wait for main to start this thread. - st_mutex_lock(start); - st_mutex_unlock(start); + // wait for main to sync_start this thread. + st_mutex_lock(sync_start); + st_mutex_unlock(sync_start); // wait thread to ready. - st_cond_wait(cond); + st_cond_wait(sync_cond); srs_trace("1. st cond is ok"); // release mutex to control thread st_usleep(100 * 1000); - st_mutex_unlock(mutex); + st_mutex_unlock(sync_mutex); // wait thread to exit. - st_cond_wait(cond); + st_cond_wait(sync_cond); srs_trace("4. st is ok"); return NULL; } -int main(int argc, char** argv) +int sync_test() { - if (st_set_eventsys(ST_EVENTSYS_ALT) < 0) { - srs_trace("st_set_eventsys failed"); - return -1; - } - - if (st_init() < 0) { - srs_trace("st_init failed"); + if ((sync_start = st_mutex_new()) == NULL) { + srs_trace("st_mutex_new sync_start failed"); return -1; } + st_mutex_lock(sync_start); - if ((start = st_mutex_new()) == NULL) { - srs_trace("st_mutex_new start failed"); - return -1; - } - st_mutex_lock(start); - - if ((cond = st_cond_new()) == NULL) { + if ((sync_cond = st_cond_new()) == NULL) { srs_trace("st_cond_new cond failed"); return -1; } - if ((mutex = st_mutex_new()) == NULL) { + if ((sync_mutex = st_mutex_new()) == NULL) { srs_trace("st_mutex_new mutex failed"); return -1; } - if (!st_thread_create(master, NULL, 0, 0)) { + if (!st_thread_create(sync_master, NULL, 0, 0)) { srs_trace("st_thread_create failed"); return -1; } - if (!st_thread_create(slave, NULL, 0, 0)) { + if (!st_thread_create(sync_slave, NULL, 0, 0)) { srs_trace("st_thread_create failed"); return -1; } // run all threads. - st_mutex_unlock(start); + st_mutex_unlock(sync_start); + + return 0; +} + +int main(int argc, char** argv) +{ + if (st_set_eventsys(ST_EVENTSYS_ALT) < 0) { + srs_trace("st_set_eventsys failed"); + return -1; + } + + if (st_init() < 0) { + srs_trace("st_init failed"); + return -1; + } + + if (sync_test() < 0) { + srs_trace("sync_test failed"); + return -1; + } // cleanup. st_thread_exit(NULL); - st_mutex_destroy(start); - st_cond_destroy(cond); - st_mutex_destroy(mutex); - return 0; } From 511c814ffbaea3d48414d14d4066d791d1e7d1b9 Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 26 Oct 2014 16:38:00 +0800 Subject: [PATCH 046/800] refs #182: rename to sync test. --- trunk/research/st/srs.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/trunk/research/st/srs.c b/trunk/research/st/srs.c index 5e25888b14..0de7942da7 100644 --- a/trunk/research/st/srs.c +++ b/trunk/research/st/srs.c @@ -8,6 +8,7 @@ st_mutex_t sync_start = NULL; st_cond_t sync_cond = NULL; st_mutex_t sync_mutex = NULL; +st_cond_t sync_end = NULL; void* sync_master(void* arg) { @@ -50,11 +51,15 @@ void* sync_slave(void* arg) st_cond_wait(sync_cond); srs_trace("4. st is ok"); + st_cond_signal(sync_end); + return NULL; } int sync_test() { + srs_trace("sync test: start"); + if ((sync_start = st_mutex_new()) == NULL) { srs_trace("st_mutex_new sync_start failed"); return -1; @@ -65,6 +70,11 @@ int sync_test() srs_trace("st_cond_new cond failed"); return -1; } + + if ((sync_end = st_cond_new()) == NULL) { + srs_trace("st_cond_new end failed"); + return -1; + } if ((sync_mutex = st_mutex_new()) == NULL) { srs_trace("st_mutex_new mutex failed"); @@ -84,6 +94,9 @@ int sync_test() // run all threads. st_mutex_unlock(sync_start); + st_cond_wait(sync_end); + srs_trace("sync test: end"); + return 0; } From b8db8b0fa41df44488b2b82c8432b148245b664c Mon Sep 17 00:00:00 2001 From: winlin Date: Mon, 27 Oct 2014 11:32:34 +0800 Subject: [PATCH 047/800] add oschina mirror --- trunk/scripts/csdn.mirror.sh | 13 +---- trunk/scripts/oschina.mirror.sh | 96 +++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 12 deletions(-) create mode 100755 trunk/scripts/oschina.mirror.sh diff --git a/trunk/scripts/csdn.mirror.sh b/trunk/scripts/csdn.mirror.sh index e6e26a461b..39f47826c9 100755 --- a/trunk/scripts/csdn.mirror.sh +++ b/trunk/scripts/csdn.mirror.sh @@ -2,17 +2,6 @@ echo "更新CSDN镜像的脚本" -# 创建CSDN镜像的过程如下: -# 1. 在CSDN上创建项目,从https://github.com/winlinvip/simple-rtmp-server拷贝过来。 -# 2. 在本地虚拟机上: -# git clone git@code.csdn.net:winlinvip/srs-csdn.git -# 3. 创建同步的branch: -# git remote add upstream https://github.com/winlinvip/simple-rtmp-server.git -# git fetch upstream -# git checkout upstream/master -b srs.master -# 4. 执行本同步更新脚本,更新。 -# bash scripts/csdn.mirror.sh - echo "argv[0]=$0" if [[ ! -f $0 ]]; then echo "directly execute the scripts on shell."; @@ -39,7 +28,7 @@ ret=$?; if [[ 0 -ne $ret ]]; then 1. 在CSDN上创建项目,从https://github.com/winlinvip/simple-rtmp-server拷贝过来。 2. 在本地虚拟机上: git clone git@code.csdn.net:winlinvip/srs-csdn.git - git checkout master && git branch 1.0release && git push origin 1.0release + cd srs-csdn && git checkout master && git branch 1.0release && git push origin 1.0release 3. 创建同步的branch: git remote add upstream https://github.com/winlinvip/simple-rtmp-server.git git fetch upstream diff --git a/trunk/scripts/oschina.mirror.sh b/trunk/scripts/oschina.mirror.sh new file mode 100755 index 0000000000..a17ff587b6 --- /dev/null +++ b/trunk/scripts/oschina.mirror.sh @@ -0,0 +1,96 @@ +#!/bin/bash + +echo "更新OSChina镜像的脚本" + +echo "argv[0]=$0" +if [[ ! -f $0 ]]; then + echo "directly execute the scripts on shell."; + work_dir=`pwd` +else + echo "execute scripts in file: $0"; + work_dir=`dirname $0`; work_dir=`(cd ${work_dir} && pwd)` +fi +work_dir=`(cd ${work_dir}/.. && pwd)` +product_dir=$work_dir + +# allow start script from any dir +cd $work_dir + +. ${product_dir}/scripts/_log.sh +ret=$?; if [[ $ret -ne 0 ]]; then exit $ret; fi +ok_msg "导入脚本成功" + +git remote -v|grep git.oschina.net >/dev/null 2>&1 +ret=$?; if [[ 0 -ne $ret ]]; then + failed_msg "当前分支不是OSChina镜像"; + cat < Date: Mon, 27 Oct 2014 11:34:05 +0800 Subject: [PATCH 048/800] update readme --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 8abbcf82b2..ea83c27a73 100755 --- a/README.md +++ b/README.md @@ -151,6 +151,13 @@ CSDN: [https://code.csdn.net/winlinvip/srs-csdn](https://code.csdn.net/winlinvip git clone https://code.csdn.net/winlinvip/srs-csdn.git ``` +OSChina: [http://git.oschina.net/winlinvip/srs.oschina](http://git.oschina.net/winlinvip/srs.oschina) +[Git Usage](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Git) + +```bash +git clone https://git.oschina.net/winlinvip/srs.oschina.git +``` + ## System Requirements Supported operating systems and hardware: * All Linux , both 32 and 64 bits From e104a187635d7ccf3a1c8a84267799d52ba6cad2 Mon Sep 17 00:00:00 2001 From: winlin Date: Mon, 27 Oct 2014 11:40:59 +0800 Subject: [PATCH 049/800] update sync script on master --- trunk/scripts/csdn.mirror.sh | 1 + trunk/scripts/oschina.mirror.sh | 1 + 2 files changed, 2 insertions(+) diff --git a/trunk/scripts/csdn.mirror.sh b/trunk/scripts/csdn.mirror.sh index 39f47826c9..72343e8fda 100755 --- a/trunk/scripts/csdn.mirror.sh +++ b/trunk/scripts/csdn.mirror.sh @@ -91,6 +91,7 @@ for ((;;)); do break done +git checkout master ok_msg "CSDN同步git成功" exit 0 diff --git a/trunk/scripts/oschina.mirror.sh b/trunk/scripts/oschina.mirror.sh index a17ff587b6..85558b4c06 100755 --- a/trunk/scripts/oschina.mirror.sh +++ b/trunk/scripts/oschina.mirror.sh @@ -91,6 +91,7 @@ for ((;;)); do break done +git checkout master ok_msg "OSChina同步git成功" exit 0 From 60ab365660434ed00f194b3749f5eb4a3cd170d0 Mon Sep 17 00:00:00 2001 From: winlin Date: Mon, 27 Oct 2014 12:03:56 +0800 Subject: [PATCH 050/800] update mirror, use utils functions. --- trunk/scripts/_mirror.utils.sh | 94 +++++++++++++++++++++++++++++++++ trunk/scripts/csdn.mirror.sh | 76 +++----------------------- trunk/scripts/oschina.mirror.sh | 76 +++----------------------- 3 files changed, 110 insertions(+), 136 deletions(-) create mode 100755 trunk/scripts/_mirror.utils.sh diff --git a/trunk/scripts/_mirror.utils.sh b/trunk/scripts/_mirror.utils.sh new file mode 100755 index 0000000000..a5cee9756e --- /dev/null +++ b/trunk/scripts/_mirror.utils.sh @@ -0,0 +1,94 @@ +#!/bin/bash + +############################################# +# help for the first checkout. +############################################# +function first_checkout() +{ + mirror_name=$1 + git_url=$2 + project_dir=$3 + sync_script=$4 + + failed_msg "当前分支不是${mirror_name}镜像"; + + cat </dev/null 2>&1 ret=$?; if [[ 0 -ne $ret ]]; then - failed_msg "当前分支不是CSDN镜像"; - cat </dev/null 2>&1 ret=$?; if [[ 0 -ne $ret ]]; then - failed_msg "当前分支不是OSChina镜像"; - cat < Date: Mon, 27 Oct 2014 12:18:50 +0800 Subject: [PATCH 051/800] update readme --- README.md | 49 +++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index ea83c27a73..8cd78731d0 100755 --- a/README.md +++ b/README.md @@ -84,6 +84,29 @@ A big THANK YOU goes to: * [FFMPEG](http://ffmpeg.org/) and [libx264](http://www.videolan.org/) group for SRS to use to transcode. * Guido van Rossum for creating Python for api-server for SRS. +## Mirrors + +Github: [https://github.com/winlinvip/simple-rtmp-server](https://github.com/winlinvip/simple-rtmp-server) +[Git Usage](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Git) + +```bash +git clone https://github.com/winlinvip/simple-rtmp-server.git +``` + +CSDN: [https://code.csdn.net/winlinvip/srs-csdn](https://code.csdn.net/winlinvip/srs-csdn) +[Git Usage](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Git) + +```bash +git clone https://code.csdn.net/winlinvip/srs-csdn.git +``` + +OSChina: [http://git.oschina.net/winlinvip/srs.oschina](http://git.oschina.net/winlinvip/srs.oschina) +[Git Usage](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Git) + +```bash +git clone https://git.oschina.net/winlinvip/srs.oschina.git +``` + ## Usage Step 1: get SRS @@ -133,30 +156,8 @@ Donation:
[http://www.ossrs.net/srs.release/donation/index.html](http://www.ossrs.net/srs.release/donation/index.html) Donations:
-[https://github.com/winlinvip/simple-rtmp-server/blob/master/DONATIONS.txt](https://github.com/winlinvip/simple-rtmp-server/blob/master/DONATIONS.txt) - -## Mirrors - -Github: [https://github.com/winlinvip/simple-rtmp-server](https://github.com/winlinvip/simple-rtmp-server) -[Git Usage](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Git) - -```bash -git clone https://github.com/winlinvip/simple-rtmp-server.git -``` - -CSDN: [https://code.csdn.net/winlinvip/srs-csdn](https://code.csdn.net/winlinvip/srs-csdn) -[Git Usage](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Git) - -```bash -git clone https://code.csdn.net/winlinvip/srs-csdn.git -``` - -OSChina: [http://git.oschina.net/winlinvip/srs.oschina](http://git.oschina.net/winlinvip/srs.oschina) -[Git Usage](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Git) - -```bash -git clone https://git.oschina.net/winlinvip/srs.oschina.git -``` +[https://github.com/winlinvip/simple-rtmp-server/blob/master/DONATIONS.txt] +(https://github.com/winlinvip/simple-rtmp-server/blob/master/DONATIONS.txt) ## System Requirements Supported operating systems and hardware: From 9d7e6b9bb230e6ffca5e5cfd950235c883213627 Mon Sep 17 00:00:00 2001 From: winlin Date: Mon, 27 Oct 2014 12:56:04 +0800 Subject: [PATCH 052/800] update mirror utils --- trunk/scripts/_mirror.utils.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/trunk/scripts/_mirror.utils.sh b/trunk/scripts/_mirror.utils.sh index a5cee9756e..01f961a5e8 100755 --- a/trunk/scripts/_mirror.utils.sh +++ b/trunk/scripts/_mirror.utils.sh @@ -88,7 +88,8 @@ function sync_push() fi break done - + git checkout master ok_msg "${mirror_name}同步git成功" } + From 2dc6b7d1eab089ee1d37115d3a1ac76aef143ae2 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 5 Nov 2014 17:01:15 +0800 Subject: [PATCH 053/800] add io for st research --- trunk/research/st/srs.c | 120 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/trunk/research/st/srs.c b/trunk/research/st/srs.c index 0de7942da7..4fc25988fe 100644 --- a/trunk/research/st/srs.c +++ b/trunk/research/st/srs.c @@ -1,10 +1,20 @@ #include #include +#include +#include +#include +#include +#include +#include +#include + #include "public.h" #define srs_trace(msg, ...) printf(msg, ##__VA_ARGS__);printf("\n") +int io_port = 1990; + st_mutex_t sync_start = NULL; st_cond_t sync_cond = NULL; st_mutex_t sync_mutex = NULL; @@ -58,6 +68,7 @@ void* sync_slave(void* arg) int sync_test() { + srs_trace("==================================================="); srs_trace("sync test: start"); if ((sync_start = st_mutex_new()) == NULL) { @@ -100,6 +111,110 @@ int sync_test() return 0; } +void* io_client(void* arg) +{ + + int fd; + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + srs_trace("create linux socket error."); + return NULL; + } + srs_trace("6. client create linux socket success. fd=%d", fd); + + st_netfd_t stfd; + if ((stfd = st_netfd_open_socket(fd)) == NULL){ + srs_trace("st_netfd_open_socket open socket failed."); + return NULL; + } + srs_trace("7. client st open socket success. fd=%d", fd); + + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(io_port); + addr.sin_addr.s_addr = INADDR_ANY; + if (st_connect(stfd, (const struct sockaddr*)&addr, sizeof(struct sockaddr_in), ST_UTIME_NO_TIMEOUT) == -1) { + srs_trace("bind socket error."); + return NULL; + } + + char buf[1024]; + if (st_read_fully(stfd, buf, sizeof(buf), ST_UTIME_NO_TIMEOUT) != sizeof(buf)) { + srs_trace("st_read_fully failed"); + return NULL; + } + if (st_write(stfd, buf, sizeof(buf), ST_UTIME_NO_TIMEOUT) != sizeof(buf)) { + srs_trace("st_write failed"); + return NULL; + } + + return NULL; +} + +int io_test() +{ + srs_trace("==================================================="); + srs_trace("io test: start, port=%d", io_port); + + int fd; + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + srs_trace("create linux socket error."); + return -1; + } + srs_trace("1. server create linux socket success. fd=%d", fd); + + int reuse_socket = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse_socket, sizeof(int)) == -1) { + srs_trace("setsockopt reuse-addr error."); + return -1; + } + srs_trace("2. server setsockopt reuse-addr success. fd=%d", fd); + + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(io_port); + addr.sin_addr.s_addr = INADDR_ANY; + if (bind(fd, (const struct sockaddr*)&addr, sizeof(struct sockaddr_in)) == -1) { + srs_trace("bind socket error."); + return -1; + } + srs_trace("3. server bind socket success. fd=%d", fd); + + if (listen(fd, 10) == -1) { + srs_trace("listen socket error."); + return -1; + } + srs_trace("4. server listen socket success. fd=%d", fd); + + st_netfd_t stfd; + if ((stfd = st_netfd_open_socket(fd)) == NULL){ + srs_trace("st_netfd_open_socket open socket failed."); + return -1; + } + srs_trace("5. server st open socket success. fd=%d", fd); + + if (!st_thread_create(io_client, NULL, 0, 0)) { + srs_trace("st_thread_create failed"); + return -1; + } + + st_netfd_t client_stfd = st_accept(stfd, NULL, NULL, ST_UTIME_NO_TIMEOUT); + srs_trace("8. server get a client. fd=%d", st_netfd_fileno(client_stfd)); + + char buf[1024]; + if (st_write(client_stfd, buf, sizeof(buf), ST_UTIME_NO_TIMEOUT) != sizeof(buf)) { + srs_trace("st_write failed"); + return -1; + } + if (st_read_fully(client_stfd, buf, sizeof(buf), ST_UTIME_NO_TIMEOUT) != sizeof(buf)) { + srs_trace("st_read_fully failed"); + return -1; + } + srs_trace("9. server io completed."); + + srs_trace("io test: end"); + return 0; +} + int main(int argc, char** argv) { if (st_set_eventsys(ST_EVENTSYS_ALT) < 0) { @@ -117,6 +232,11 @@ int main(int argc, char** argv) return -1; } + if (io_test() < 0) { + srs_trace("io_test failed"); + return -1; + } + // cleanup. st_thread_exit(NULL); From 0faa38dc0cfc970befb8f589887119a09a392f69 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 5 Nov 2014 17:06:18 +0800 Subject: [PATCH 054/800] research st: remove the osguess.sh --- trunk/research/st/osguess.sh | 45 ------------------------------------ 1 file changed, 45 deletions(-) delete mode 100755 trunk/research/st/osguess.sh diff --git a/trunk/research/st/osguess.sh b/trunk/research/st/osguess.sh deleted file mode 100755 index 531681efe6..0000000000 --- a/trunk/research/st/osguess.sh +++ /dev/null @@ -1,45 +0,0 @@ -# -# This script can be used to automatically guess target OS. -# It requires the config.guess utility which is a part of GNU Autoconf. -# GNU Autoconf can be downloaded from ftp://ftp.gnu.org/gnu/autoconf/ -# -# Use "default" as a make target for automatic builds. -# - - -# Specify path to the config.guess utility (unless set via environment) -#CONFIG_GUESS_PATH= - - -if [ x"$CONFIG_GUESS_PATH" = x ]; then - echo "Error: CONFIG_GUESS_PATH variable is not set" - exit 1 -fi - -if [ ! -f "$CONFIG_GUESS_PATH/config.guess" ]; then - echo "Can't find $CONFIG_GUESS_PATH/config.guess utility. Wrong path?" - exit 1 -fi - -sys_info=`/bin/sh $CONFIG_GUESS_PATH/config.guess` - -echo "Building for $sys_info" - -case "$sys_info" in - *-ibm-aix4* ) OS=AIX ;; - *-freebsd* ) OS=FREEBSD ;; - hppa*-hp-hpux11*) OS=HPUX ;; - *-sgi-irix6* ) OS=IRIX ;; - *-linux* ) OS=LINUX ;; - *-netbsd* ) OS=NETBSD ;; - *-openbsd* ) OS=OPENBSD ;; - *-dec-osf* ) OS=OSF1 ;; - *-solaris2* ) OS=SOLARIS ;; - *-darwin* ) OS=DARWIN ;; - * ) OS= - echo "Sorry, unsupported OS" - exit 1 ;; -esac - -echo "Making with OS=$OS" - From 55bd1dd95e8d3d88d4efb5ab2de082ce18af7324 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 5 Nov 2014 17:10:54 +0800 Subject: [PATCH 055/800] research st: replace the TAB with 4spaces --- trunk/research/st/common.h | 80 +++++++++++------------ trunk/research/st/io.c | 126 ++++++++++++++++++------------------- trunk/research/st/md.S | 64 +++++++++---------- trunk/research/st/public.h | 38 +++++------ trunk/research/st/sched.c | 52 +++++++-------- trunk/research/st/stk.c | 2 +- trunk/research/st/sync.c | 6 +- 7 files changed, 184 insertions(+), 184 deletions(-) diff --git a/trunk/research/st/common.h b/trunk/research/st/common.h index 4df39e9e4a..a37e0b637b 100644 --- a/trunk/research/st/common.h +++ b/trunk/research/st/common.h @@ -61,7 +61,7 @@ #ifdef DEBUG #define ST_HIDDEN /*nothing*/ #else -#define ST_HIDDEN static +#define ST_HIDDEN static #endif #include "public.h" @@ -78,21 +78,21 @@ typedef struct _st_clist { } _st_clist_t; /* Insert element "_e" into the list, before "_l" */ -#define ST_INSERT_BEFORE(_e,_l) \ - ST_BEGIN_MACRO \ - (_e)->next = (_l); \ - (_e)->prev = (_l)->prev; \ - (_l)->prev->next = (_e); \ - (_l)->prev = (_e); \ +#define ST_INSERT_BEFORE(_e,_l) \ + ST_BEGIN_MACRO \ + (_e)->next = (_l); \ + (_e)->prev = (_l)->prev; \ + (_l)->prev->next = (_e); \ + (_l)->prev = (_e); \ ST_END_MACRO /* Insert element "_e" into the list, after "_l" */ -#define ST_INSERT_AFTER(_e,_l) \ - ST_BEGIN_MACRO \ - (_e)->next = (_l)->next; \ - (_e)->prev = (_l); \ - (_l)->next->prev = (_e); \ - (_l)->next = (_e); \ +#define ST_INSERT_AFTER(_e,_l) \ + ST_BEGIN_MACRO \ + (_e)->next = (_l)->next; \ + (_e)->prev = (_l); \ + (_l)->next->prev = (_e); \ + (_l)->next = (_e); \ ST_END_MACRO /* Return the element following element "_e" */ @@ -109,10 +109,10 @@ typedef struct _st_clist { #define ST_LIST_TAIL(_l) (_l)->prev /* Remove the element "_e" from it's circular list */ -#define ST_REMOVE_LINK(_e) \ - ST_BEGIN_MACRO \ - (_e)->prev->next = (_e)->next; \ - (_e)->next->prev = (_e)->prev; \ +#define ST_REMOVE_LINK(_e) \ + ST_BEGIN_MACRO \ + (_e)->prev->next = (_e)->next; \ + (_e)->next->prev = (_e)->prev; \ ST_END_MACRO /* Return non-zero if the given circular list "_l" is empty, */ @@ -122,9 +122,9 @@ typedef struct _st_clist { /* Initialize a circular list */ #define ST_INIT_CLIST(_l) \ - ST_BEGIN_MACRO \ - (_l)->next = (_l); \ - (_l)->prev = (_l); \ + ST_BEGIN_MACRO \ + (_l)->next = (_l); \ + (_l)->prev = (_l); \ ST_END_MACRO #define ST_INIT_STATIC_CLIST(_l) \ @@ -153,7 +153,7 @@ typedef struct _st_stack { typedef struct _st_cond { - _st_clist_t wait_q; /* Condition variable wait queue */ + _st_clist_t wait_q; /* Condition variable wait queue */ } _st_cond_t; @@ -167,7 +167,7 @@ struct _st_thread { void *arg; /* Argument of the start function */ void *retval; /* Return value of the start function */ - _st_stack_t *stack; /* Info about thread's stack */ + _st_stack_t *stack; /* Info about thread's stack */ _st_clist_t links; /* For putting on run/sleep/zombie queue */ _st_clist_t wait_links; /* For putting on mutex/condvar wait queue */ @@ -177,7 +177,7 @@ struct _st_thread { st_utime_t due; /* Wakeup time when thread is sleeping */ _st_thread_t *left; /* For putting in timeout heap */ - _st_thread_t *right; /* -- see docs/timeout_heap.txt for details */ + _st_thread_t *right; /* -- see docs/timeout_heap.txt for details */ int heap_index; void **private_data; /* Per thread private data */ @@ -229,11 +229,11 @@ typedef struct _st_vp { int pagesize; _st_thread_t *sleep_q; /* sleep queue for this vp */ - int sleepq_size; /* number of threads on sleep queue */ + int sleepq_size; /* number of threads on sleep queue */ #ifdef ST_SWITCH_CB - st_switch_cb_t switch_out_cb; /* called when a thread is switched out */ - st_switch_cb_t switch_in_cb; /* called when a thread is switched in */ + st_switch_cb_t switch_out_cb; /* called when a thread is switched out */ + st_switch_cb_t switch_in_cb; /* called when a thread is switched in */ #endif } _st_vp_t; @@ -252,7 +252,7 @@ typedef struct _st_netfd { * Current vp, thread, and event system */ -extern _st_vp_t _st_this_vp; +extern _st_vp_t _st_this_vp; extern _st_thread_t *_st_this_thread; extern _st_eventsys_t *_st_eventsys; @@ -287,7 +287,7 @@ extern _st_eventsys_t *_st_eventsys; #define _ST_DEL_RUNQ(_thr) ST_REMOVE_LINK(&(_thr)->links) #define _ST_ADD_SLEEPQ(_thr, _timeout) _st_add_sleep_q(_thr, _timeout) -#define _ST_DEL_SLEEPQ(_thr) _st_del_sleep_q(_thr) +#define _ST_DEL_SLEEPQ(_thr) _st_del_sleep_q(_thr) #define _ST_ADD_ZOMBIEQ(_thr) ST_APPEND_LINK(&(_thr)->links, &_ST_ZOMBIEQ) #define _ST_DEL_ZOMBIEQ(_thr) ST_REMOVE_LINK(&(_thr)->links) @@ -379,17 +379,17 @@ void _st_iterate_threads(void); #endif #ifdef ST_SWITCH_CB -#define ST_SWITCH_OUT_CB(_thread) \ - if (_st_this_vp.switch_out_cb != NULL && \ - _thread != _st_this_vp.idle_thread && \ - _thread->state != _ST_ST_ZOMBIE) { \ - _st_this_vp.switch_out_cb(); \ +#define ST_SWITCH_OUT_CB(_thread) \ + if (_st_this_vp.switch_out_cb != NULL && \ + _thread != _st_this_vp.idle_thread && \ + _thread->state != _ST_ST_ZOMBIE) { \ + _st_this_vp.switch_out_cb(); \ } -#define ST_SWITCH_IN_CB(_thread) \ - if (_st_this_vp.switch_in_cb != NULL && \ - _thread != _st_this_vp.idle_thread && \ - _thread->state != _ST_ST_ZOMBIE) { \ - _st_this_vp.switch_in_cb(); \ +#define ST_SWITCH_IN_CB(_thread) \ + if (_st_this_vp.switch_in_cb != NULL && \ + _thread != _st_this_vp.idle_thread && \ + _thread->state != _ST_ST_ZOMBIE) { \ + _st_this_vp.switch_in_cb(); \ } #else #define ST_SWITCH_OUT_CB(_thread) @@ -457,10 +457,10 @@ int st_cond_timedwait(_st_cond_t *cvar, st_utime_t timeout); int st_cond_signal(_st_cond_t *cvar); ssize_t st_read(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout); ssize_t st_write(_st_netfd_t *fd, const void *buf, size_t nbyte, - st_utime_t timeout); + st_utime_t timeout); int st_poll(struct pollfd *pds, int npds, st_utime_t timeout); _st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg, - int joinable, int stk_size); + int joinable, int stk_size); #endif /* !__ST_COMMON_H__ */ diff --git a/trunk/research/st/io.c b/trunk/research/st/io.c index f95ff8c6fa..7989112ce9 100644 --- a/trunk/research/st/io.c +++ b/trunk/research/st/io.c @@ -148,7 +148,7 @@ static _st_netfd_t *_st_netfd_new(int osfd, int nonblock, int is_socket) return fd; /* Do it the Posix way */ if ((flags = fcntl(osfd, F_GETFL, 0)) < 0 || - fcntl(osfd, F_SETFL, flags | O_NONBLOCK) < 0) { + fcntl(osfd, F_SETFL, flags | O_NONBLOCK) < 0) { st_netfd_free(fd); return NULL; } @@ -187,7 +187,7 @@ int st_netfd_fileno(_st_netfd_t *fd) void st_netfd_setspecific(_st_netfd_t *fd, void *value, - _st_destructor_t destructor) + _st_destructor_t destructor) { if (value != fd->private_data) { /* Free up previously set non-NULL data value */ @@ -248,7 +248,7 @@ static void _st_netfd_free_aux_data(_st_netfd_t *fd) } _st_netfd_t *st_accept(_st_netfd_t *fd, struct sockaddr *addr, int *addrlen, - st_utime_t timeout) + st_utime_t timeout) { int osfd, err; _st_netfd_t *newfd; @@ -335,7 +335,7 @@ static void _st_netfd_free_aux_data(_st_netfd_t *fd) } _st_netfd_t *st_accept(_st_netfd_t *fd, struct sockaddr *addr, int *addrlen, - st_utime_t timeout) + st_utime_t timeout) { int osfd, err; _st_netfd_t *newfd; @@ -350,7 +350,7 @@ _st_netfd_t *st_accept(_st_netfd_t *fd, struct sockaddr *addr, int *addrlen, /* Get the lock */ n = st_read(p[0], &c, 1, timeout); if (n < 0) - return NULL; + return NULL; ST_ASSERT(n == 1); /* Got the lock */ osfd = accept(fd->osfd, addr, (socklen_t *)addrlen); @@ -393,7 +393,7 @@ _st_netfd_t *st_accept(_st_netfd_t *fd, struct sockaddr *addr, int *addrlen, int st_connect(_st_netfd_t *fd, const struct sockaddr *addr, int addrlen, - st_utime_t timeout) + st_utime_t timeout) { int n, err = 0; @@ -408,18 +408,18 @@ int st_connect(_st_netfd_t *fd, const struct sockaddr *addr, int addrlen, * ("Interrupted connect"). */ if (errno != EINPROGRESS && (errno != EADDRINUSE || err == 0)) - return -1; + return -1; /* Wait until the socket becomes writable */ if (st_netfd_poll(fd, POLLOUT, timeout) < 0) - return -1; + return -1; /* Try to find out whether the connection setup succeeded or failed */ n = sizeof(int); if (getsockopt(fd->osfd, SOL_SOCKET, SO_ERROR, (char *)&err, - (socklen_t *)&n) < 0) - return -1; + (socklen_t *)&n) < 0) + return -1; if (err) { - errno = err; - return -1; + errno = err; + return -1; } break; } @@ -449,7 +449,7 @@ ssize_t st_read(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout) int st_read_resid(_st_netfd_t *fd, void *buf, size_t *resid, - st_utime_t timeout) + st_utime_t timeout) { struct iovec iov, *riov; int riov_size, rv; @@ -465,7 +465,7 @@ int st_read_resid(_st_netfd_t *fd, void *buf, size_t *resid, ssize_t st_readv(_st_netfd_t *fd, const struct iovec *iov, int iov_size, - st_utime_t timeout) + st_utime_t timeout) { ssize_t n; @@ -483,7 +483,7 @@ ssize_t st_readv(_st_netfd_t *fd, const struct iovec *iov, int iov_size, } int st_readv_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, - st_utime_t timeout) + st_utime_t timeout) { ssize_t n; @@ -494,23 +494,23 @@ int st_readv_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, n = readv(fd->osfd, *iov, *iov_size); if (n < 0) { if (errno == EINTR) - continue; + continue; if (!_IO_NOT_READY_ERROR) - return -1; + return -1; } else if (n == 0) break; else { while ((size_t) n >= (*iov)->iov_len) { - n -= (*iov)->iov_len; - (*iov)->iov_base = (char *) (*iov)->iov_base + (*iov)->iov_len; - (*iov)->iov_len = 0; - (*iov)++; - (*iov_size)--; - if (n == 0) - break; + n -= (*iov)->iov_len; + (*iov)->iov_base = (char *) (*iov)->iov_base + (*iov)->iov_len; + (*iov)->iov_len = 0; + (*iov)++; + (*iov_size)--; + if (n == 0) + break; } if (*iov_size == 0) - break; + break; (*iov)->iov_base = (char *) (*iov)->iov_base + n; (*iov)->iov_len -= n; } @@ -524,7 +524,7 @@ int st_readv_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, ssize_t st_read_fully(_st_netfd_t *fd, void *buf, size_t nbyte, - st_utime_t timeout) + st_utime_t timeout) { size_t resid = nbyte; return st_read_resid(fd, buf, &resid, timeout) == 0 ? @@ -533,12 +533,12 @@ ssize_t st_read_fully(_st_netfd_t *fd, void *buf, size_t nbyte, int st_write_resid(_st_netfd_t *fd, const void *buf, size_t *resid, - st_utime_t timeout) + st_utime_t timeout) { struct iovec iov, *riov; int riov_size, rv; - iov.iov_base = (void *) buf; /* we promise not to modify buf */ + iov.iov_base = (void *) buf; /* we promise not to modify buf */ iov.iov_len = *resid; riov = &iov; riov_size = 1; @@ -549,7 +549,7 @@ int st_write_resid(_st_netfd_t *fd, const void *buf, size_t *resid, ssize_t st_write(_st_netfd_t *fd, const void *buf, size_t nbyte, - st_utime_t timeout) + st_utime_t timeout) { size_t resid = nbyte; return st_write_resid(fd, buf, &resid, timeout) == 0 ? @@ -558,7 +558,7 @@ ssize_t st_write(_st_netfd_t *fd, const void *buf, size_t nbyte, ssize_t st_writev(_st_netfd_t *fd, const struct iovec *iov, int iov_size, - st_utime_t timeout) + st_utime_t timeout) { ssize_t n, rv; size_t nleft, nbyte; @@ -573,40 +573,40 @@ ssize_t st_writev(_st_netfd_t *fd, const struct iovec *iov, int iov_size, rv = (ssize_t)nbyte; nleft = nbyte; - tmp_iov = (struct iovec *) iov; /* we promise not to modify iov */ + tmp_iov = (struct iovec *) iov; /* we promise not to modify iov */ iov_cnt = iov_size; while (nleft > 0) { if (iov_cnt == 1) { if (st_write(fd, tmp_iov[0].iov_base, nleft, timeout) != (ssize_t) nleft) - rv = -1; + rv = -1; break; } if ((n = writev(fd->osfd, tmp_iov, iov_cnt)) < 0) { if (errno == EINTR) - continue; + continue; if (!_IO_NOT_READY_ERROR) { - rv = -1; - break; + rv = -1; + break; } } else { if ((size_t) n == nleft) - break; + break; nleft -= n; /* Find the next unwritten vector */ n = (ssize_t)(nbyte - nleft); for (index = 0; (size_t) n >= iov[index].iov_len; index++) - n -= iov[index].iov_len; + n -= iov[index].iov_len; if (tmp_iov == iov) { - /* Must copy iov's around */ - if (iov_size - index <= _LOCAL_MAXIOV) { - tmp_iov = local_iov; - } else { - tmp_iov = calloc(1, (iov_size - index) * sizeof(struct iovec)); - if (tmp_iov == NULL) - return -1; - } + /* Must copy iov's around */ + if (iov_size - index <= _LOCAL_MAXIOV) { + tmp_iov = local_iov; + } else { + tmp_iov = calloc(1, (iov_size - index) * sizeof(struct iovec)); + if (tmp_iov == NULL) + return -1; + } } /* Fill in the first partial read */ @@ -615,8 +615,8 @@ ssize_t st_writev(_st_netfd_t *fd, const struct iovec *iov, int iov_size, index++; /* Copy the remaining vectors */ for (iov_cnt = 1; index < iov_size; iov_cnt++, index++) { - tmp_iov[iov_cnt].iov_base = iov[index].iov_base; - tmp_iov[iov_cnt].iov_len = iov[index].iov_len; + tmp_iov[iov_cnt].iov_base = iov[index].iov_base; + tmp_iov[iov_cnt].iov_len = iov[index].iov_len; } } /* Wait until the socket becomes writable */ @@ -634,7 +634,7 @@ ssize_t st_writev(_st_netfd_t *fd, const struct iovec *iov, int iov_size, int st_writev_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, - st_utime_t timeout) + st_utime_t timeout) { ssize_t n; @@ -645,21 +645,21 @@ int st_writev_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, n = writev(fd->osfd, *iov, *iov_size); if (n < 0) { if (errno == EINTR) - continue; + continue; if (!_IO_NOT_READY_ERROR) - return -1; + return -1; } else { while ((size_t) n >= (*iov)->iov_len) { - n -= (*iov)->iov_len; - (*iov)->iov_base = (char *) (*iov)->iov_base + (*iov)->iov_len; - (*iov)->iov_len = 0; - (*iov)++; - (*iov_size)--; - if (n == 0) - break; + n -= (*iov)->iov_len; + (*iov)->iov_base = (char *) (*iov)->iov_base + (*iov)->iov_len; + (*iov)->iov_len = 0; + (*iov)++; + (*iov_size)--; + if (n == 0) + break; } if (*iov_size == 0) - break; + break; (*iov)->iov_base = (char *) (*iov)->iov_base + n; (*iov)->iov_len -= n; } @@ -676,12 +676,12 @@ int st_writev_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, * Simple I/O functions for UDP. */ int st_recvfrom(_st_netfd_t *fd, void *buf, int len, struct sockaddr *from, - int *fromlen, st_utime_t timeout) + int *fromlen, st_utime_t timeout) { int n; while ((n = recvfrom(fd->osfd, buf, len, 0, from, (socklen_t *)fromlen)) - < 0) { + < 0) { if (errno == EINTR) continue; if (!_IO_NOT_READY_ERROR) @@ -696,7 +696,7 @@ int st_recvfrom(_st_netfd_t *fd, void *buf, int len, struct sockaddr *from, int st_sendto(_st_netfd_t *fd, const void *msg, int len, - const struct sockaddr *to, int tolen, st_utime_t timeout) + const struct sockaddr *to, int tolen, st_utime_t timeout) { int n; @@ -715,7 +715,7 @@ int st_sendto(_st_netfd_t *fd, const void *msg, int len, int st_recvmsg(_st_netfd_t *fd, struct msghdr *msg, int flags, - st_utime_t timeout) + st_utime_t timeout) { int n; @@ -734,7 +734,7 @@ int st_recvmsg(_st_netfd_t *fd, struct msghdr *msg, int flags, int st_sendmsg(_st_netfd_t *fd, const struct msghdr *msg, int flags, - st_utime_t timeout) + st_utime_t timeout) { int n; diff --git a/trunk/research/st/md.S b/trunk/research/st/md.S index ab4f7b5577..f2ac6ce176 100644 --- a/trunk/research/st/md.S +++ b/trunk/research/st/md.S @@ -95,7 +95,7 @@ _st_md_cxt_save: ;; stf.spill.nta [r8] = f2,32 stf.spill.nta [r9] = f3,32 - mov r15 = rp + mov r15 = rp ;; stf.spill.nta [r8] = f4,32 stf.spill.nta [r9] = f5,32 @@ -143,7 +143,7 @@ _st_md_cxt_save: ;; st8.nta [r2] = r18,16 // b2 st8.nta [r3] = r19,16 // b3 - mov r26 = ar.rsc + mov r26 = ar.rsc ;; st8.nta [r2] = r20,16 // b4 st8.nta [r3] = r21,16 // b5 @@ -158,18 +158,18 @@ _st_md_cxt_save: st8.nta [r3] = in0,16 // &__jmp_buf (just in case) ;; st8.nta [r2] = r26 // ar.rsc - ;; - flushrs // flush dirty regs to backing store - ;; - and r27 = ~0x3,r26 // clear ar.rsc.mode - ;; - mov ar.rsc = r27 // put RSE in enforced lazy mode - ;; - mov r28 = ar.rnat - ;; - st8.nta [r3] = r28 // ar.rnat - mov ar.rsc = r26 // restore ar.rsc - ;; + ;; + flushrs // flush dirty regs to backing store + ;; + and r27 = ~0x3,r26 // clear ar.rsc.mode + ;; + mov ar.rsc = r27 // put RSE in enforced lazy mode + ;; + mov r28 = ar.rnat + ;; + st8.nta [r3] = r28 // ar.rnat + mov ar.rsc = r26 // restore ar.rsc + ;; mov r8 = 0 br.ret.sptk.few b0 .endp _st_md_cxt_save @@ -183,31 +183,31 @@ _st_md_cxt_save: _st_md_cxt_restore: alloc r8 = ar.pfs,2,0,0,0 add r2 = 0x88,in0 // r2 <- &jmpbuf.ar_bsp - mov r16 = ar.rsc - ;; - flushrs // flush dirty regs to backing store + mov r16 = ar.rsc + ;; + flushrs // flush dirty regs to backing store ;; - and r17 = ~0x3,r16 // clear ar.rsc.mode - ;; - mov ar.rsc = r17 // put RSE in enforced lazy mode + and r17 = ~0x3,r16 // clear ar.rsc.mode + ;; + mov ar.rsc = r17 // put RSE in enforced lazy mode ;; - invala // invalidate the ALAT - ;; + invala // invalidate the ALAT + ;; ld8 r23 = [r2],8 // r23 <- jmpbuf.ar_bsp ;; - mov ar.bspstore = r23 // write BSPSTORE + mov ar.bspstore = r23 // write BSPSTORE ld8 r25 = [r2],24 // r25 <- jmpbuf.ar_unat - ;; - ld8 r26 = [r2],-8 // r26 <- jmpbuf.ar_rnat + ;; + ld8 r26 = [r2],-8 // r26 <- jmpbuf.ar_rnat ;; - mov ar.rnat = r26 // write RNAT - ld8 r27 = [r2] // r27 <- jmpbuf.ar_rsc - ;; - mov ar.rsc = r27 // write RSE control + mov ar.rnat = r26 // write RNAT + ld8 r27 = [r2] // r27 <- jmpbuf.ar_rsc + ;; + mov ar.rsc = r27 // write RSE control mov r2 = in0 - ;; - mov ar.unat = r25 // write ar.unat - add r3 = 8,in0 + ;; + mov ar.unat = r25 // write ar.unat + add r3 = 8,in0 ;; ld8.fill.nta sp = [r2],16 // r12 (sp) ld8.fill.nta gp = [r3],16 // r1 (gp) diff --git a/trunk/research/st/public.h b/trunk/research/st/public.h index e0cc58dc41..968b4d17c1 100644 --- a/trunk/research/st/public.h +++ b/trunk/research/st/public.h @@ -45,7 +45,7 @@ #include #include -#define ST_VERSION "1.9" +#define ST_VERSION "1.9" #define ST_VERSION_MAJOR 1 #define ST_VERSION_MINOR 9 @@ -99,7 +99,7 @@ extern void st_thread_exit(void *retval); extern int st_thread_join(st_thread_t thread, void **retvalp); extern void st_thread_interrupt(st_thread_t thread); extern st_thread_t st_thread_create(void *(*start)(void *arg), void *arg, - int joinable, int stack_size); + int joinable, int stack_size); extern int st_randomize_stacks(int on); extern int st_set_utime_function(st_utime_t (*func)(void)); @@ -132,43 +132,43 @@ extern void st_netfd_free(st_netfd_t fd); extern int st_netfd_close(st_netfd_t fd); extern int st_netfd_fileno(st_netfd_t fd); extern void st_netfd_setspecific(st_netfd_t fd, void *value, - void (*destructor)(void *)); + void (*destructor)(void *)); extern void *st_netfd_getspecific(st_netfd_t fd); extern int st_netfd_serialize_accept(st_netfd_t fd); extern int st_netfd_poll(st_netfd_t fd, int how, st_utime_t timeout); extern int st_poll(struct pollfd *pds, int npds, st_utime_t timeout); extern st_netfd_t st_accept(st_netfd_t fd, struct sockaddr *addr, int *addrlen, - st_utime_t timeout); + st_utime_t timeout); extern int st_connect(st_netfd_t fd, const struct sockaddr *addr, int addrlen, - st_utime_t timeout); + st_utime_t timeout); extern ssize_t st_read(st_netfd_t fd, void *buf, size_t nbyte, - st_utime_t timeout); + st_utime_t timeout); extern ssize_t st_read_fully(st_netfd_t fd, void *buf, size_t nbyte, - st_utime_t timeout); + st_utime_t timeout); extern int st_read_resid(st_netfd_t fd, void *buf, size_t *resid, - st_utime_t timeout); + st_utime_t timeout); extern ssize_t st_readv(st_netfd_t fd, const struct iovec *iov, int iov_size, - st_utime_t timeout); + st_utime_t timeout); extern int st_readv_resid(st_netfd_t fd, struct iovec **iov, int *iov_size, - st_utime_t timeout); + st_utime_t timeout); extern ssize_t st_write(st_netfd_t fd, const void *buf, size_t nbyte, - st_utime_t timeout); + st_utime_t timeout); extern int st_write_resid(st_netfd_t fd, const void *buf, size_t *resid, - st_utime_t timeout); + st_utime_t timeout); extern ssize_t st_writev(st_netfd_t fd, const struct iovec *iov, int iov_size, - st_utime_t timeout); + st_utime_t timeout); extern int st_writev_resid(st_netfd_t fd, struct iovec **iov, int *iov_size, - st_utime_t timeout); + st_utime_t timeout); extern int st_recvfrom(st_netfd_t fd, void *buf, int len, - struct sockaddr *from, int *fromlen, - st_utime_t timeout); + struct sockaddr *from, int *fromlen, + st_utime_t timeout); extern int st_sendto(st_netfd_t fd, const void *msg, int len, - const struct sockaddr *to, int tolen, st_utime_t timeout); + const struct sockaddr *to, int tolen, st_utime_t timeout); extern int st_recvmsg(st_netfd_t fd, struct msghdr *msg, int flags, - st_utime_t timeout); + st_utime_t timeout); extern int st_sendmsg(st_netfd_t fd, const struct msghdr *msg, int flags, - st_utime_t timeout); + st_utime_t timeout); extern st_netfd_t st_open(const char *path, int oflags, mode_t mode); #ifdef DEBUG diff --git a/trunk/research/st/sched.c b/trunk/research/st/sched.c index d9c393ca1e..43830a24b6 100644 --- a/trunk/research/st/sched.c +++ b/trunk/research/st/sched.c @@ -94,7 +94,7 @@ int st_poll(struct pollfd *pds, int npds, st_utime_t timeout) /* Count the number of ready descriptors */ for (pd = pds; pd < epd; pd++) { if (pd->revents) - n++; + n++; } } @@ -165,7 +165,7 @@ int st_init(void) * Create idle thread */ _st_this_vp.idle_thread = st_thread_create(_st_idle_thread_start, - NULL, 0, 0); + NULL, 0, 0); if (!_st_this_vp.idle_thread) return -1; _st_this_vp.idle_thread->flags = _ST_FL_IDLE_THREAD; @@ -176,7 +176,7 @@ int st_init(void) * Initialize primordial thread */ thread = (_st_thread_t *) calloc(1, sizeof(_st_thread_t) + - (ST_KEYS_MAX * sizeof(void *))); + (ST_KEYS_MAX * sizeof(void *))); if (!thread) return -1; thread->private_data = (void **) (thread + 1); @@ -414,33 +414,33 @@ static void heap_delete(_st_thread_t *thread) { _st_thread_t *y; /* The younger child */ int index_tmp; if (t->left == NULL) - break; + break; else if (t->right == NULL) - y = t->left; + y = t->left; else if (t->left->due < t->right->due) - y = t->left; + y = t->left; else - y = t->right; + y = t->right; if (t->due > y->due) { - _st_thread_t *tl = y->left; - _st_thread_t *tr = y->right; - *p = y; - if (y == t->left) { - y->left = t; - y->right = t->right; - p = &y->left; - } else { - y->left = t->left; - y->right = t; - p = &y->right; - } - t->left = tl; - t->right = tr; - index_tmp = t->heap_index; - t->heap_index = y->heap_index; - y->heap_index = index_tmp; + _st_thread_t *tl = y->left; + _st_thread_t *tr = y->right; + *p = y; + if (y == t->left) { + y->left = t; + y->right = t->right; + p = &y->left; + } else { + y->left = t->left; + y->right = t; + p = &y->right; + } + t->left = tl; + t->right = tr; + index_tmp = t->heap_index; + t->heap_index = y->heap_index; + y->heap_index = index_tmp; } else { - break; + break; } } } @@ -518,7 +518,7 @@ void st_thread_interrupt(_st_thread_t *thread) _st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg, - int joinable, int stk_size) + int joinable, int stk_size) { _st_thread_t *thread; _st_stack_t *stack; diff --git a/trunk/research/st/stk.c b/trunk/research/st/stk.c index 344552eb65..34769697fb 100644 --- a/trunk/research/st/stk.c +++ b/trunk/research/st/stk.c @@ -48,7 +48,7 @@ /* How much space to leave between the stacks, at each end */ -#define REDZONE _ST_PAGE_SIZE +#define REDZONE _ST_PAGE_SIZE _st_clist_t _st_free_stacks = ST_INIT_STATIC_CLIST(&_st_free_stacks); int _st_num_free_stacks = 0; diff --git a/trunk/research/st/sync.c b/trunk/research/st/sync.c index a71876c5fb..2303f8a5b0 100644 --- a/trunk/research/st/sync.c +++ b/trunk/research/st/sync.c @@ -143,7 +143,7 @@ int st_usleep(st_utime_t usecs) int st_sleep(int secs) { return st_usleep((secs >= 0) ? secs * (st_utime_t) 1000000LL : - ST_UTIME_NO_TIMEOUT); + ST_UTIME_NO_TIMEOUT); } @@ -230,13 +230,13 @@ static int _st_cond_signal(_st_cond_t *cvar, int broadcast) thread = _ST_THREAD_WAITQ_PTR(q); if (thread->state == _ST_ST_COND_WAIT) { if (thread->flags & _ST_FL_ON_SLEEPQ) - _ST_DEL_SLEEPQ(thread); + _ST_DEL_SLEEPQ(thread); /* Make thread runnable */ thread->state = _ST_ST_RUNNABLE; _ST_ADD_RUNQ(thread); if (!broadcast) - break; + break; } } From bba2d3dd98c4680787e528187d2b52cb7e8802c4 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 5 Nov 2014 17:12:56 +0800 Subject: [PATCH 056/800] research st: replace the TAB with 4spaces --- trunk/research/st/common.h | 32 ++-- trunk/research/st/io.c | 26 +-- trunk/research/st/public.h | 366 ++++++++++++++++++------------------- trunk/research/st/sched.c | 6 +- trunk/research/st/sync.c | 2 +- 5 files changed, 215 insertions(+), 217 deletions(-) mode change 100644 => 100755 trunk/research/st/public.h diff --git a/trunk/research/st/common.h b/trunk/research/st/common.h index a37e0b637b..f22b7cb919 100644 --- a/trunk/research/st/common.h +++ b/trunk/research/st/common.h @@ -79,20 +79,20 @@ typedef struct _st_clist { /* Insert element "_e" into the list, before "_l" */ #define ST_INSERT_BEFORE(_e,_l) \ - ST_BEGIN_MACRO \ - (_e)->next = (_l); \ + ST_BEGIN_MACRO \ + (_e)->next = (_l); \ (_e)->prev = (_l)->prev; \ (_l)->prev->next = (_e); \ - (_l)->prev = (_e); \ + (_l)->prev = (_e); \ ST_END_MACRO /* Insert element "_e" into the list, after "_l" */ #define ST_INSERT_AFTER(_e,_l) \ - ST_BEGIN_MACRO \ + ST_BEGIN_MACRO \ (_e)->next = (_l)->next; \ - (_e)->prev = (_l); \ + (_e)->prev = (_l); \ (_l)->next->prev = (_e); \ - (_l)->next = (_e); \ + (_l)->next = (_e); \ ST_END_MACRO /* Return the element following element "_e" */ @@ -110,7 +110,7 @@ typedef struct _st_clist { /* Remove the element "_e" from it's circular list */ #define ST_REMOVE_LINK(_e) \ - ST_BEGIN_MACRO \ + ST_BEGIN_MACRO \ (_e)->prev->next = (_e)->next; \ (_e)->next->prev = (_e)->prev; \ ST_END_MACRO @@ -287,7 +287,7 @@ extern _st_eventsys_t *_st_eventsys; #define _ST_DEL_RUNQ(_thr) ST_REMOVE_LINK(&(_thr)->links) #define _ST_ADD_SLEEPQ(_thr, _timeout) _st_add_sleep_q(_thr, _timeout) -#define _ST_DEL_SLEEPQ(_thr) _st_del_sleep_q(_thr) +#define _ST_DEL_SLEEPQ(_thr) _st_del_sleep_q(_thr) #define _ST_ADD_ZOMBIEQ(_thr) ST_APPEND_LINK(&(_thr)->links, &_ST_ZOMBIEQ) #define _ST_DEL_ZOMBIEQ(_thr) ST_REMOVE_LINK(&(_thr)->links) @@ -379,17 +379,17 @@ void _st_iterate_threads(void); #endif #ifdef ST_SWITCH_CB -#define ST_SWITCH_OUT_CB(_thread) \ +#define ST_SWITCH_OUT_CB(_thread) \ if (_st_this_vp.switch_out_cb != NULL && \ _thread != _st_this_vp.idle_thread && \ _thread->state != _ST_ST_ZOMBIE) { \ - _st_this_vp.switch_out_cb(); \ + _st_this_vp.switch_out_cb(); \ } -#define ST_SWITCH_IN_CB(_thread) \ +#define ST_SWITCH_IN_CB(_thread) \ if (_st_this_vp.switch_in_cb != NULL && \ - _thread != _st_this_vp.idle_thread && \ - _thread->state != _ST_ST_ZOMBIE) { \ - _st_this_vp.switch_in_cb(); \ + _thread != _st_this_vp.idle_thread && \ + _thread->state != _ST_ST_ZOMBIE) { \ + _st_this_vp.switch_in_cb(); \ } #else #define ST_SWITCH_OUT_CB(_thread) @@ -457,10 +457,10 @@ int st_cond_timedwait(_st_cond_t *cvar, st_utime_t timeout); int st_cond_signal(_st_cond_t *cvar); ssize_t st_read(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout); ssize_t st_write(_st_netfd_t *fd, const void *buf, size_t nbyte, - st_utime_t timeout); + st_utime_t timeout); int st_poll(struct pollfd *pds, int npds, st_utime_t timeout); _st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg, - int joinable, int stk_size); + int joinable, int stk_size); #endif /* !__ST_COMMON_H__ */ diff --git a/trunk/research/st/io.c b/trunk/research/st/io.c index 7989112ce9..19cb4a9e2d 100644 --- a/trunk/research/st/io.c +++ b/trunk/research/st/io.c @@ -187,7 +187,7 @@ int st_netfd_fileno(_st_netfd_t *fd) void st_netfd_setspecific(_st_netfd_t *fd, void *value, - _st_destructor_t destructor) + _st_destructor_t destructor) { if (value != fd->private_data) { /* Free up previously set non-NULL data value */ @@ -248,7 +248,7 @@ static void _st_netfd_free_aux_data(_st_netfd_t *fd) } _st_netfd_t *st_accept(_st_netfd_t *fd, struct sockaddr *addr, int *addrlen, - st_utime_t timeout) + st_utime_t timeout) { int osfd, err; _st_netfd_t *newfd; @@ -335,7 +335,7 @@ static void _st_netfd_free_aux_data(_st_netfd_t *fd) } _st_netfd_t *st_accept(_st_netfd_t *fd, struct sockaddr *addr, int *addrlen, - st_utime_t timeout) + st_utime_t timeout) { int osfd, err; _st_netfd_t *newfd; @@ -415,7 +415,7 @@ int st_connect(_st_netfd_t *fd, const struct sockaddr *addr, int addrlen, /* Try to find out whether the connection setup succeeded or failed */ n = sizeof(int); if (getsockopt(fd->osfd, SOL_SOCKET, SO_ERROR, (char *)&err, - (socklen_t *)&n) < 0) + (socklen_t *)&n) < 0) return -1; if (err) { errno = err; @@ -449,7 +449,7 @@ ssize_t st_read(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout) int st_read_resid(_st_netfd_t *fd, void *buf, size_t *resid, - st_utime_t timeout) + st_utime_t timeout) { struct iovec iov, *riov; int riov_size, rv; @@ -465,7 +465,7 @@ int st_read_resid(_st_netfd_t *fd, void *buf, size_t *resid, ssize_t st_readv(_st_netfd_t *fd, const struct iovec *iov, int iov_size, - st_utime_t timeout) + st_utime_t timeout) { ssize_t n; @@ -483,7 +483,7 @@ ssize_t st_readv(_st_netfd_t *fd, const struct iovec *iov, int iov_size, } int st_readv_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, - st_utime_t timeout) + st_utime_t timeout) { ssize_t n; @@ -524,7 +524,7 @@ int st_readv_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, ssize_t st_read_fully(_st_netfd_t *fd, void *buf, size_t nbyte, - st_utime_t timeout) + st_utime_t timeout) { size_t resid = nbyte; return st_read_resid(fd, buf, &resid, timeout) == 0 ? @@ -533,7 +533,7 @@ ssize_t st_read_fully(_st_netfd_t *fd, void *buf, size_t nbyte, int st_write_resid(_st_netfd_t *fd, const void *buf, size_t *resid, - st_utime_t timeout) + st_utime_t timeout) { struct iovec iov, *riov; int riov_size, rv; @@ -549,7 +549,7 @@ int st_write_resid(_st_netfd_t *fd, const void *buf, size_t *resid, ssize_t st_write(_st_netfd_t *fd, const void *buf, size_t nbyte, - st_utime_t timeout) + st_utime_t timeout) { size_t resid = nbyte; return st_write_resid(fd, buf, &resid, timeout) == 0 ? @@ -558,7 +558,7 @@ ssize_t st_write(_st_netfd_t *fd, const void *buf, size_t nbyte, ssize_t st_writev(_st_netfd_t *fd, const struct iovec *iov, int iov_size, - st_utime_t timeout) + st_utime_t timeout) { ssize_t n, rv; size_t nleft, nbyte; @@ -634,7 +634,7 @@ ssize_t st_writev(_st_netfd_t *fd, const struct iovec *iov, int iov_size, int st_writev_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, - st_utime_t timeout) + st_utime_t timeout) { ssize_t n; @@ -676,7 +676,7 @@ int st_writev_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, * Simple I/O functions for UDP. */ int st_recvfrom(_st_netfd_t *fd, void *buf, int len, struct sockaddr *from, - int *fromlen, st_utime_t timeout) + int *fromlen, st_utime_t timeout) { int n; diff --git a/trunk/research/st/public.h b/trunk/research/st/public.h old mode 100644 new mode 100755 index 968b4d17c1..c596368bff --- a/trunk/research/st/public.h +++ b/trunk/research/st/public.h @@ -1,184 +1,182 @@ -/* - * The contents of this file are subject to the Mozilla Public - * License Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code is the Netscape Portable Runtime library. - * - * The Initial Developer of the Original Code is Netscape - * Communications Corporation. Portions created by Netscape are - * Copyright (C) 1994-2000 Netscape Communications Corporation. All - * Rights Reserved. - * - * Contributor(s): Silicon Graphics, Inc. - * - * Portions created by SGI are Copyright (C) 2000-2001 Silicon - * Graphics, Inc. All Rights Reserved. - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License Version 2 or later (the - * "GPL"), in which case the provisions of the GPL are applicable - * instead of those above. If you wish to allow use of your - * version of this file only under the terms of the GPL and not to - * allow others to use your version of this file under the MPL, - * indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by - * the GPL. If you do not delete the provisions above, a recipient - * may use your version of this file under either the MPL or the - * GPL. - */ - -#ifndef __ST_THREAD_H__ -#define __ST_THREAD_H__ - -#include -#include -#include -#include -#include -#include -#include - -#define ST_VERSION "1.9" -#define ST_VERSION_MAJOR 1 -#define ST_VERSION_MINOR 9 - -/* Undefine this to remove the context switch callback feature. */ -#define ST_SWITCH_CB - -#ifndef ETIME -#define ETIME ETIMEDOUT -#endif - -#ifndef ST_UTIME_NO_TIMEOUT -#define ST_UTIME_NO_TIMEOUT ((st_utime_t) -1LL) -#endif - -#ifndef ST_UTIME_NO_WAIT -#define ST_UTIME_NO_WAIT 0 -#endif - -#define ST_EVENTSYS_DEFAULT 0 -#define ST_EVENTSYS_SELECT 1 -#define ST_EVENTSYS_POLL 2 -#define ST_EVENTSYS_ALT 3 - -#ifdef __cplusplus -extern "C" { -#endif - -typedef unsigned long long st_utime_t; -typedef struct _st_thread * st_thread_t; -typedef struct _st_cond * st_cond_t; -typedef struct _st_mutex * st_mutex_t; -typedef struct _st_netfd * st_netfd_t; -#ifdef ST_SWITCH_CB -typedef void (*st_switch_cb_t)(void); -#endif - -extern int st_init(void); -extern int st_getfdlimit(void); - -extern int st_set_eventsys(int eventsys); -extern int st_get_eventsys(void); -extern const char *st_get_eventsys_name(void); - -#ifdef ST_SWITCH_CB -extern st_switch_cb_t st_set_switch_in_cb(st_switch_cb_t cb); -extern st_switch_cb_t st_set_switch_out_cb(st_switch_cb_t cb); -#endif - -extern st_thread_t st_thread_self(void); -extern void st_thread_exit(void *retval); -extern int st_thread_join(st_thread_t thread, void **retvalp); -extern void st_thread_interrupt(st_thread_t thread); -extern st_thread_t st_thread_create(void *(*start)(void *arg), void *arg, - int joinable, int stack_size); -extern int st_randomize_stacks(int on); -extern int st_set_utime_function(st_utime_t (*func)(void)); - -extern st_utime_t st_utime(void); -extern st_utime_t st_utime_last_clock(void); -extern int st_timecache_set(int on); -extern time_t st_time(void); -extern int st_usleep(st_utime_t usecs); -extern int st_sleep(int secs); -extern st_cond_t st_cond_new(void); -extern int st_cond_destroy(st_cond_t cvar); -extern int st_cond_timedwait(st_cond_t cvar, st_utime_t timeout); -extern int st_cond_wait(st_cond_t cvar); -extern int st_cond_signal(st_cond_t cvar); -extern int st_cond_broadcast(st_cond_t cvar); -extern st_mutex_t st_mutex_new(void); -extern int st_mutex_destroy(st_mutex_t lock); -extern int st_mutex_lock(st_mutex_t lock); -extern int st_mutex_unlock(st_mutex_t lock); -extern int st_mutex_trylock(st_mutex_t lock); - -extern int st_key_create(int *keyp, void (*destructor)(void *)); -extern int st_key_getlimit(void); -extern int st_thread_setspecific(int key, void *value); -extern void *st_thread_getspecific(int key); - -extern st_netfd_t st_netfd_open(int osfd); -extern st_netfd_t st_netfd_open_socket(int osfd); -extern void st_netfd_free(st_netfd_t fd); -extern int st_netfd_close(st_netfd_t fd); -extern int st_netfd_fileno(st_netfd_t fd); -extern void st_netfd_setspecific(st_netfd_t fd, void *value, - void (*destructor)(void *)); -extern void *st_netfd_getspecific(st_netfd_t fd); -extern int st_netfd_serialize_accept(st_netfd_t fd); -extern int st_netfd_poll(st_netfd_t fd, int how, st_utime_t timeout); - -extern int st_poll(struct pollfd *pds, int npds, st_utime_t timeout); -extern st_netfd_t st_accept(st_netfd_t fd, struct sockaddr *addr, int *addrlen, - st_utime_t timeout); -extern int st_connect(st_netfd_t fd, const struct sockaddr *addr, int addrlen, - st_utime_t timeout); -extern ssize_t st_read(st_netfd_t fd, void *buf, size_t nbyte, - st_utime_t timeout); -extern ssize_t st_read_fully(st_netfd_t fd, void *buf, size_t nbyte, - st_utime_t timeout); -extern int st_read_resid(st_netfd_t fd, void *buf, size_t *resid, - st_utime_t timeout); -extern ssize_t st_readv(st_netfd_t fd, const struct iovec *iov, int iov_size, - st_utime_t timeout); -extern int st_readv_resid(st_netfd_t fd, struct iovec **iov, int *iov_size, - st_utime_t timeout); -extern ssize_t st_write(st_netfd_t fd, const void *buf, size_t nbyte, - st_utime_t timeout); -extern int st_write_resid(st_netfd_t fd, const void *buf, size_t *resid, - st_utime_t timeout); -extern ssize_t st_writev(st_netfd_t fd, const struct iovec *iov, int iov_size, - st_utime_t timeout); -extern int st_writev_resid(st_netfd_t fd, struct iovec **iov, int *iov_size, - st_utime_t timeout); -extern int st_recvfrom(st_netfd_t fd, void *buf, int len, - struct sockaddr *from, int *fromlen, - st_utime_t timeout); -extern int st_sendto(st_netfd_t fd, const void *msg, int len, - const struct sockaddr *to, int tolen, st_utime_t timeout); -extern int st_recvmsg(st_netfd_t fd, struct msghdr *msg, int flags, - st_utime_t timeout); -extern int st_sendmsg(st_netfd_t fd, const struct msghdr *msg, int flags, - st_utime_t timeout); -extern st_netfd_t st_open(const char *path, int oflags, mode_t mode); - -#ifdef DEBUG -extern void _st_show_thread_stack(st_thread_t thread, const char *messg); -extern void _st_iterate_threads(void); -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* !__ST_THREAD_H__ */ - +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Portable Runtime library. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Silicon Graphics, Inc. + * + * Portions created by SGI are Copyright (C) 2000-2001 Silicon + * Graphics, Inc. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#ifndef __ST_THREAD_H__ +#define __ST_THREAD_H__ + +#include +#include +#include +#include +#include +#include +#include + +#define ST_VERSION "1.9" +#define ST_VERSION_MAJOR 1 +#define ST_VERSION_MINOR 9 + +/* Undefine this to remove the context switch callback feature. */ +#define ST_SWITCH_CB + +#ifndef ETIME + #define ETIME ETIMEDOUT +#endif + +#ifndef ST_UTIME_NO_TIMEOUT + #define ST_UTIME_NO_TIMEOUT ((st_utime_t) -1LL) +#endif + +#ifndef ST_UTIME_NO_WAIT + #define ST_UTIME_NO_WAIT 0 +#endif + +#define ST_EVENTSYS_DEFAULT 0 +#define ST_EVENTSYS_SELECT 1 +#define ST_EVENTSYS_POLL 2 +#define ST_EVENTSYS_ALT 3 + +#ifdef __cplusplus +extern "C" { +#endif + typedef unsigned long long st_utime_t; + typedef struct _st_thread * st_thread_t; + typedef struct _st_cond * st_cond_t; + typedef struct _st_mutex * st_mutex_t; + typedef struct _st_netfd * st_netfd_t; + #ifdef ST_SWITCH_CB + typedef void (*st_switch_cb_t)(void); + #endif + + extern int st_init(void); + extern int st_getfdlimit(void); + + extern int st_set_eventsys(int eventsys); + extern int st_get_eventsys(void); + extern const char *st_get_eventsys_name(void); + + #ifdef ST_SWITCH_CB + extern st_switch_cb_t st_set_switch_in_cb(st_switch_cb_t cb); + extern st_switch_cb_t st_set_switch_out_cb(st_switch_cb_t cb); + #endif + + extern st_thread_t st_thread_self(void); + extern void st_thread_exit(void *retval); + extern int st_thread_join(st_thread_t thread, void **retvalp); + extern void st_thread_interrupt(st_thread_t thread); + extern st_thread_t st_thread_create(void *(*start)(void *arg), void *arg, + int joinable, int stack_size); + extern int st_randomize_stacks(int on); + extern int st_set_utime_function(st_utime_t (*func)(void)); + + extern st_utime_t st_utime(void); + extern st_utime_t st_utime_last_clock(void); + extern int st_timecache_set(int on); + extern time_t st_time(void); + extern int st_usleep(st_utime_t usecs); + extern int st_sleep(int secs); + extern st_cond_t st_cond_new(void); + extern int st_cond_destroy(st_cond_t cvar); + extern int st_cond_timedwait(st_cond_t cvar, st_utime_t timeout); + extern int st_cond_wait(st_cond_t cvar); + extern int st_cond_signal(st_cond_t cvar); + extern int st_cond_broadcast(st_cond_t cvar); + extern st_mutex_t st_mutex_new(void); + extern int st_mutex_destroy(st_mutex_t lock); + extern int st_mutex_lock(st_mutex_t lock); + extern int st_mutex_unlock(st_mutex_t lock); + extern int st_mutex_trylock(st_mutex_t lock); + + extern int st_key_create(int *keyp, void (*destructor)(void *)); + extern int st_key_getlimit(void); + extern int st_thread_setspecific(int key, void *value); + extern void *st_thread_getspecific(int key); + + extern st_netfd_t st_netfd_open(int osfd); + extern st_netfd_t st_netfd_open_socket(int osfd); + extern void st_netfd_free(st_netfd_t fd); + extern int st_netfd_close(st_netfd_t fd); + extern int st_netfd_fileno(st_netfd_t fd); + extern void st_netfd_setspecific(st_netfd_t fd, void *value, + void (*destructor)(void *)); + extern void *st_netfd_getspecific(st_netfd_t fd); + extern int st_netfd_serialize_accept(st_netfd_t fd); + extern int st_netfd_poll(st_netfd_t fd, int how, st_utime_t timeout); + + extern int st_poll(struct pollfd *pds, int npds, st_utime_t timeout); + extern st_netfd_t st_accept(st_netfd_t fd, struct sockaddr *addr, int *addrlen, + st_utime_t timeout); + extern int st_connect(st_netfd_t fd, const struct sockaddr *addr, int addrlen, + st_utime_t timeout); + extern ssize_t st_read(st_netfd_t fd, void *buf, size_t nbyte, + st_utime_t timeout); + extern ssize_t st_read_fully(st_netfd_t fd, void *buf, size_t nbyte, + st_utime_t timeout); + extern int st_read_resid(st_netfd_t fd, void *buf, size_t *resid, + st_utime_t timeout); + extern ssize_t st_readv(st_netfd_t fd, const struct iovec *iov, int iov_size, + st_utime_t timeout); + extern int st_readv_resid(st_netfd_t fd, struct iovec **iov, int *iov_size, + st_utime_t timeout); + extern ssize_t st_write(st_netfd_t fd, const void *buf, size_t nbyte, + st_utime_t timeout); + extern int st_write_resid(st_netfd_t fd, const void *buf, size_t *resid, + st_utime_t timeout); + extern ssize_t st_writev(st_netfd_t fd, const struct iovec *iov, int iov_size, + st_utime_t timeout); + extern int st_writev_resid(st_netfd_t fd, struct iovec **iov, int *iov_size, + st_utime_t timeout); + extern int st_recvfrom(st_netfd_t fd, void *buf, int len, + struct sockaddr *from, int *fromlen, + st_utime_t timeout); + extern int st_sendto(st_netfd_t fd, const void *msg, int len, + const struct sockaddr *to, int tolen, st_utime_t timeout); + extern int st_recvmsg(st_netfd_t fd, struct msghdr *msg, int flags, + st_utime_t timeout); + extern int st_sendmsg(st_netfd_t fd, const struct msghdr *msg, int flags, + st_utime_t timeout); + extern st_netfd_t st_open(const char *path, int oflags, mode_t mode); + + #ifdef DEBUG + extern void _st_show_thread_stack(st_thread_t thread, const char *messg); + extern void _st_iterate_threads(void); + #endif +#ifdef __cplusplus +} +#endif + +#endif /* !__ST_THREAD_H__ */ + diff --git a/trunk/research/st/sched.c b/trunk/research/st/sched.c index 43830a24b6..4822dd42ae 100644 --- a/trunk/research/st/sched.c +++ b/trunk/research/st/sched.c @@ -165,7 +165,7 @@ int st_init(void) * Create idle thread */ _st_this_vp.idle_thread = st_thread_create(_st_idle_thread_start, - NULL, 0, 0); + NULL, 0, 0); if (!_st_this_vp.idle_thread) return -1; _st_this_vp.idle_thread->flags = _ST_FL_IDLE_THREAD; @@ -176,7 +176,7 @@ int st_init(void) * Initialize primordial thread */ thread = (_st_thread_t *) calloc(1, sizeof(_st_thread_t) + - (ST_KEYS_MAX * sizeof(void *))); + (ST_KEYS_MAX * sizeof(void *))); if (!thread) return -1; thread->private_data = (void **) (thread + 1); @@ -518,7 +518,7 @@ void st_thread_interrupt(_st_thread_t *thread) _st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg, - int joinable, int stk_size) + int joinable, int stk_size) { _st_thread_t *thread; _st_stack_t *stack; diff --git a/trunk/research/st/sync.c b/trunk/research/st/sync.c index 2303f8a5b0..49bceb3e31 100644 --- a/trunk/research/st/sync.c +++ b/trunk/research/st/sync.c @@ -143,7 +143,7 @@ int st_usleep(st_utime_t usecs) int st_sleep(int secs) { return st_usleep((secs >= 0) ? secs * (st_utime_t) 1000000LL : - ST_UTIME_NO_TIMEOUT); + ST_UTIME_NO_TIMEOUT); } From 315707d7267f313a1b140f82a0b3bf16f22c7942 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 5 Nov 2014 17:16:15 +0800 Subject: [PATCH 057/800] research st: refine public --- trunk/research/st/public.h | 347 ++++++++++++++++++------------------- 1 file changed, 165 insertions(+), 182 deletions(-) mode change 100755 => 100644 trunk/research/st/public.h diff --git a/trunk/research/st/public.h b/trunk/research/st/public.h old mode 100755 new mode 100644 index c596368bff..8c2ce8b8d6 --- a/trunk/research/st/public.h +++ b/trunk/research/st/public.h @@ -1,182 +1,165 @@ -/* - * The contents of this file are subject to the Mozilla Public - * License Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code is the Netscape Portable Runtime library. - * - * The Initial Developer of the Original Code is Netscape - * Communications Corporation. Portions created by Netscape are - * Copyright (C) 1994-2000 Netscape Communications Corporation. All - * Rights Reserved. - * - * Contributor(s): Silicon Graphics, Inc. - * - * Portions created by SGI are Copyright (C) 2000-2001 Silicon - * Graphics, Inc. All Rights Reserved. - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License Version 2 or later (the - * "GPL"), in which case the provisions of the GPL are applicable - * instead of those above. If you wish to allow use of your - * version of this file only under the terms of the GPL and not to - * allow others to use your version of this file under the MPL, - * indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by - * the GPL. If you do not delete the provisions above, a recipient - * may use your version of this file under either the MPL or the - * GPL. - */ - -#ifndef __ST_THREAD_H__ -#define __ST_THREAD_H__ - -#include -#include -#include -#include -#include -#include -#include - -#define ST_VERSION "1.9" -#define ST_VERSION_MAJOR 1 -#define ST_VERSION_MINOR 9 - -/* Undefine this to remove the context switch callback feature. */ -#define ST_SWITCH_CB - -#ifndef ETIME - #define ETIME ETIMEDOUT -#endif - -#ifndef ST_UTIME_NO_TIMEOUT - #define ST_UTIME_NO_TIMEOUT ((st_utime_t) -1LL) -#endif - -#ifndef ST_UTIME_NO_WAIT - #define ST_UTIME_NO_WAIT 0 -#endif - -#define ST_EVENTSYS_DEFAULT 0 -#define ST_EVENTSYS_SELECT 1 -#define ST_EVENTSYS_POLL 2 -#define ST_EVENTSYS_ALT 3 - -#ifdef __cplusplus -extern "C" { -#endif - typedef unsigned long long st_utime_t; - typedef struct _st_thread * st_thread_t; - typedef struct _st_cond * st_cond_t; - typedef struct _st_mutex * st_mutex_t; - typedef struct _st_netfd * st_netfd_t; - #ifdef ST_SWITCH_CB - typedef void (*st_switch_cb_t)(void); - #endif - - extern int st_init(void); - extern int st_getfdlimit(void); - - extern int st_set_eventsys(int eventsys); - extern int st_get_eventsys(void); - extern const char *st_get_eventsys_name(void); - - #ifdef ST_SWITCH_CB - extern st_switch_cb_t st_set_switch_in_cb(st_switch_cb_t cb); - extern st_switch_cb_t st_set_switch_out_cb(st_switch_cb_t cb); - #endif - - extern st_thread_t st_thread_self(void); - extern void st_thread_exit(void *retval); - extern int st_thread_join(st_thread_t thread, void **retvalp); - extern void st_thread_interrupt(st_thread_t thread); - extern st_thread_t st_thread_create(void *(*start)(void *arg), void *arg, - int joinable, int stack_size); - extern int st_randomize_stacks(int on); - extern int st_set_utime_function(st_utime_t (*func)(void)); - - extern st_utime_t st_utime(void); - extern st_utime_t st_utime_last_clock(void); - extern int st_timecache_set(int on); - extern time_t st_time(void); - extern int st_usleep(st_utime_t usecs); - extern int st_sleep(int secs); - extern st_cond_t st_cond_new(void); - extern int st_cond_destroy(st_cond_t cvar); - extern int st_cond_timedwait(st_cond_t cvar, st_utime_t timeout); - extern int st_cond_wait(st_cond_t cvar); - extern int st_cond_signal(st_cond_t cvar); - extern int st_cond_broadcast(st_cond_t cvar); - extern st_mutex_t st_mutex_new(void); - extern int st_mutex_destroy(st_mutex_t lock); - extern int st_mutex_lock(st_mutex_t lock); - extern int st_mutex_unlock(st_mutex_t lock); - extern int st_mutex_trylock(st_mutex_t lock); - - extern int st_key_create(int *keyp, void (*destructor)(void *)); - extern int st_key_getlimit(void); - extern int st_thread_setspecific(int key, void *value); - extern void *st_thread_getspecific(int key); - - extern st_netfd_t st_netfd_open(int osfd); - extern st_netfd_t st_netfd_open_socket(int osfd); - extern void st_netfd_free(st_netfd_t fd); - extern int st_netfd_close(st_netfd_t fd); - extern int st_netfd_fileno(st_netfd_t fd); - extern void st_netfd_setspecific(st_netfd_t fd, void *value, - void (*destructor)(void *)); - extern void *st_netfd_getspecific(st_netfd_t fd); - extern int st_netfd_serialize_accept(st_netfd_t fd); - extern int st_netfd_poll(st_netfd_t fd, int how, st_utime_t timeout); - - extern int st_poll(struct pollfd *pds, int npds, st_utime_t timeout); - extern st_netfd_t st_accept(st_netfd_t fd, struct sockaddr *addr, int *addrlen, - st_utime_t timeout); - extern int st_connect(st_netfd_t fd, const struct sockaddr *addr, int addrlen, - st_utime_t timeout); - extern ssize_t st_read(st_netfd_t fd, void *buf, size_t nbyte, - st_utime_t timeout); - extern ssize_t st_read_fully(st_netfd_t fd, void *buf, size_t nbyte, - st_utime_t timeout); - extern int st_read_resid(st_netfd_t fd, void *buf, size_t *resid, - st_utime_t timeout); - extern ssize_t st_readv(st_netfd_t fd, const struct iovec *iov, int iov_size, - st_utime_t timeout); - extern int st_readv_resid(st_netfd_t fd, struct iovec **iov, int *iov_size, - st_utime_t timeout); - extern ssize_t st_write(st_netfd_t fd, const void *buf, size_t nbyte, - st_utime_t timeout); - extern int st_write_resid(st_netfd_t fd, const void *buf, size_t *resid, - st_utime_t timeout); - extern ssize_t st_writev(st_netfd_t fd, const struct iovec *iov, int iov_size, - st_utime_t timeout); - extern int st_writev_resid(st_netfd_t fd, struct iovec **iov, int *iov_size, - st_utime_t timeout); - extern int st_recvfrom(st_netfd_t fd, void *buf, int len, - struct sockaddr *from, int *fromlen, - st_utime_t timeout); - extern int st_sendto(st_netfd_t fd, const void *msg, int len, - const struct sockaddr *to, int tolen, st_utime_t timeout); - extern int st_recvmsg(st_netfd_t fd, struct msghdr *msg, int flags, - st_utime_t timeout); - extern int st_sendmsg(st_netfd_t fd, const struct msghdr *msg, int flags, - st_utime_t timeout); - extern st_netfd_t st_open(const char *path, int oflags, mode_t mode); - - #ifdef DEBUG - extern void _st_show_thread_stack(st_thread_t thread, const char *messg); - extern void _st_iterate_threads(void); - #endif -#ifdef __cplusplus -} -#endif - -#endif /* !__ST_THREAD_H__ */ - +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Portable Runtime library. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Silicon Graphics, Inc. + * + * Portions created by SGI are Copyright (C) 2000-2001 Silicon + * Graphics, Inc. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#ifndef __ST_THREAD_H__ +#define __ST_THREAD_H__ + +#include +#include +#include +#include +#include +#include +#include + +#define ST_VERSION "1.9" +#define ST_VERSION_MAJOR 1 +#define ST_VERSION_MINOR 9 + +/* Undefine this to remove the context switch callback feature. */ +#define ST_SWITCH_CB + +#ifndef ETIME + #define ETIME ETIMEDOUT +#endif + +#ifndef ST_UTIME_NO_TIMEOUT + #define ST_UTIME_NO_TIMEOUT ((st_utime_t) -1LL) +#endif + +#ifndef ST_UTIME_NO_WAIT + #define ST_UTIME_NO_WAIT 0 +#endif + +#define ST_EVENTSYS_DEFAULT 0 +#define ST_EVENTSYS_SELECT 1 +#define ST_EVENTSYS_POLL 2 +#define ST_EVENTSYS_ALT 3 + +#ifdef __cplusplus +extern "C" { +#endif + typedef unsigned long long st_utime_t; + typedef struct _st_thread * st_thread_t; + typedef struct _st_cond * st_cond_t; + typedef struct _st_mutex * st_mutex_t; + typedef struct _st_netfd * st_netfd_t; + #ifdef ST_SWITCH_CB + typedef void (*st_switch_cb_t)(void); + #endif + + extern int st_init(void); + extern int st_getfdlimit(void); + + extern int st_set_eventsys(int eventsys); + extern int st_get_eventsys(void); + extern const char *st_get_eventsys_name(void); + + #ifdef ST_SWITCH_CB + extern st_switch_cb_t st_set_switch_in_cb(st_switch_cb_t cb); + extern st_switch_cb_t st_set_switch_out_cb(st_switch_cb_t cb); + #endif + + extern st_thread_t st_thread_self(void); + extern void st_thread_exit(void *retval); + extern int st_thread_join(st_thread_t thread, void **retvalp); + extern void st_thread_interrupt(st_thread_t thread); + extern st_thread_t st_thread_create(void *(*start)(void *arg), void *arg, int joinable, int stack_size); + extern int st_randomize_stacks(int on); + extern int st_set_utime_function(st_utime_t (*func)(void)); + + extern st_utime_t st_utime(void); + extern st_utime_t st_utime_last_clock(void); + extern int st_timecache_set(int on); + extern time_t st_time(void); + extern int st_usleep(st_utime_t usecs); + extern int st_sleep(int secs); + extern st_cond_t st_cond_new(void); + extern int st_cond_destroy(st_cond_t cvar); + extern int st_cond_timedwait(st_cond_t cvar, st_utime_t timeout); + extern int st_cond_wait(st_cond_t cvar); + extern int st_cond_signal(st_cond_t cvar); + extern int st_cond_broadcast(st_cond_t cvar); + extern st_mutex_t st_mutex_new(void); + extern int st_mutex_destroy(st_mutex_t lock); + extern int st_mutex_lock(st_mutex_t lock); + extern int st_mutex_unlock(st_mutex_t lock); + extern int st_mutex_trylock(st_mutex_t lock); + + extern int st_key_create(int *keyp, void (*destructor)(void *)); + extern int st_key_getlimit(void); + extern int st_thread_setspecific(int key, void *value); + extern void *st_thread_getspecific(int key); + + extern st_netfd_t st_netfd_open(int osfd); + extern st_netfd_t st_netfd_open_socket(int osfd); + extern void st_netfd_free(st_netfd_t fd); + extern int st_netfd_close(st_netfd_t fd); + extern int st_netfd_fileno(st_netfd_t fd); + extern void st_netfd_setspecific(st_netfd_t fd, void *value, + void (*destructor)(void *)); + extern void *st_netfd_getspecific(st_netfd_t fd); + extern int st_netfd_serialize_accept(st_netfd_t fd); + extern int st_netfd_poll(st_netfd_t fd, int how, st_utime_t timeout); + + extern int st_poll(struct pollfd *pds, int npds, st_utime_t timeout); + extern st_netfd_t st_accept(st_netfd_t fd, struct sockaddr *addr, int *addrlen, st_utime_t timeout); + extern int st_connect(st_netfd_t fd, const struct sockaddr *addr, int addrlen, st_utime_t timeout); + extern ssize_t st_read(st_netfd_t fd, void *buf, size_t nbyte, st_utime_t timeout); + extern ssize_t st_read_fully(st_netfd_t fd, void *buf, size_t nbyte, st_utime_t timeout); + extern int st_read_resid(st_netfd_t fd, void *buf, size_t *resid, st_utime_t timeout); + extern ssize_t st_readv(st_netfd_t fd, const struct iovec *iov, int iov_size, st_utime_t timeout); + extern int st_readv_resid(st_netfd_t fd, struct iovec **iov, int *iov_size, st_utime_t timeout); + extern ssize_t st_write(st_netfd_t fd, const void *buf, size_t nbyte, st_utime_t timeout); + extern int st_write_resid(st_netfd_t fd, const void *buf, size_t *resid, st_utime_t timeout); + extern ssize_t st_writev(st_netfd_t fd, const struct iovec *iov, int iov_size, st_utime_t timeout); + extern int st_writev_resid(st_netfd_t fd, struct iovec **iov, int *iov_size, st_utime_t timeout); + extern int st_recvfrom(st_netfd_t fd, void *buf, int len, struct sockaddr *from, int *fromlen, st_utime_t timeout); + extern int st_sendto(st_netfd_t fd, const void *msg, int len, const struct sockaddr *to, int tolen, st_utime_t timeout); + extern int st_recvmsg(st_netfd_t fd, struct msghdr *msg, int flags, st_utime_t timeout); + extern int st_sendmsg(st_netfd_t fd, const struct msghdr *msg, int flags, st_utime_t timeout); + extern st_netfd_t st_open(const char *path, int oflags, mode_t mode); + + #ifdef DEBUG + extern void _st_show_thread_stack(st_thread_t thread, const char *messg); + extern void _st_iterate_threads(void); + #endif +#ifdef __cplusplus +} +#endif + +#endif /* !__ST_THREAD_H__ */ + From 856ba07bd3596b843be58786019ccf7646127f89 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 5 Nov 2014 17:22:36 +0800 Subject: [PATCH 058/800] research st: update public and common. --- trunk/research/st/common.h | 227 ++++++++++++++++++------------------- trunk/research/st/md.h | 58 +++++----- trunk/research/st/public.h | 3 +- 3 files changed, 140 insertions(+), 148 deletions(-) diff --git a/trunk/research/st/common.h b/trunk/research/st/common.h index f22b7cb919..880b4f1399 100644 --- a/trunk/research/st/common.h +++ b/trunk/research/st/common.h @@ -50,7 +50,7 @@ /* Enable assertions only if DEBUG is defined */ #ifndef DEBUG -#define NDEBUG + #define NDEBUG #endif #include #define ST_ASSERT(expr) assert(expr) @@ -59,22 +59,21 @@ #define ST_END_MACRO } #ifdef DEBUG -#define ST_HIDDEN /*nothing*/ + #define ST_HIDDEN /*nothing*/ #else -#define ST_HIDDEN static + #define ST_HIDDEN static #endif #include "public.h" #include "md.h" - /***************************************** * Circular linked list definitions */ typedef struct _st_clist { - struct _st_clist *next; - struct _st_clist *prev; + struct _st_clist *next; + struct _st_clist *prev; } _st_clist_t; /* Insert element "_e" into the list, before "_l" */ @@ -137,114 +136,113 @@ typedef struct _st_clist { typedef void (*_st_destructor_t)(void *); - typedef struct _st_stack { - _st_clist_t links; - char *vaddr; /* Base of stack's allocated memory */ - int vaddr_size; /* Size of stack's allocated memory */ - int stk_size; /* Size of usable portion of the stack */ - char *stk_bottom; /* Lowest address of stack's usable portion */ - char *stk_top; /* Highest address of stack's usable portion */ - void *sp; /* Stack pointer from C's point of view */ -#ifdef __ia64__ - void *bsp; /* Register stack backing store pointer */ -#endif + _st_clist_t links; + char *vaddr; /* Base of stack's allocated memory */ + int vaddr_size; /* Size of stack's allocated memory */ + int stk_size; /* Size of usable portion of the stack */ + char *stk_bottom; /* Lowest address of stack's usable portion */ + char *stk_top; /* Highest address of stack's usable portion */ + void *sp; /* Stack pointer from C's point of view */ + #ifdef __ia64__ + void *bsp; /* Register stack backing store pointer */ + #endif } _st_stack_t; typedef struct _st_cond { - _st_clist_t wait_q; /* Condition variable wait queue */ + _st_clist_t wait_q; /* Condition variable wait queue */ } _st_cond_t; typedef struct _st_thread _st_thread_t; struct _st_thread { - int state; /* Thread's state */ - int flags; /* Thread's flags */ - - void *(*start)(void *arg); /* The start function of the thread */ - void *arg; /* Argument of the start function */ - void *retval; /* Return value of the start function */ - - _st_stack_t *stack; /* Info about thread's stack */ - - _st_clist_t links; /* For putting on run/sleep/zombie queue */ - _st_clist_t wait_links; /* For putting on mutex/condvar wait queue */ -#ifdef DEBUG - _st_clist_t tlink; /* For putting on thread queue */ -#endif - - st_utime_t due; /* Wakeup time when thread is sleeping */ - _st_thread_t *left; /* For putting in timeout heap */ - _st_thread_t *right; /* -- see docs/timeout_heap.txt for details */ - int heap_index; - - void **private_data; /* Per thread private data */ - - _st_cond_t *term; /* Termination condition variable for join */ - - jmp_buf context; /* Thread's context */ + int state; /* Thread's state */ + int flags; /* Thread's flags */ + + void *(*start)(void *arg); /* The start function of the thread */ + void *arg; /* Argument of the start function */ + void *retval; /* Return value of the start function */ + + _st_stack_t *stack; /* Info about thread's stack */ + + _st_clist_t links; /* For putting on run/sleep/zombie queue */ + _st_clist_t wait_links; /* For putting on mutex/condvar wait queue */ + #ifdef DEBUG + _st_clist_t tlink; /* For putting on thread queue */ + #endif + + st_utime_t due; /* Wakeup time when thread is sleeping */ + _st_thread_t *left; /* For putting in timeout heap */ + _st_thread_t *right; /* -- see docs/timeout_heap.txt for details */ + int heap_index; + + void **private_data; /* Per thread private data */ + + _st_cond_t *term; /* Termination condition variable for join */ + + jmp_buf context; /* Thread's context */ }; typedef struct _st_mutex { - _st_thread_t *owner; /* Current mutex owner */ - _st_clist_t wait_q; /* Mutex wait queue */ + _st_thread_t *owner; /* Current mutex owner */ + _st_clist_t wait_q; /* Mutex wait queue */ } _st_mutex_t; typedef struct _st_pollq { - _st_clist_t links; /* For putting on io queue */ - _st_thread_t *thread; /* Polling thread */ - struct pollfd *pds; /* Array of poll descriptors */ - int npds; /* Length of the array */ - int on_ioq; /* Is it on ioq? */ + _st_clist_t links; /* For putting on io queue */ + _st_thread_t *thread; /* Polling thread */ + struct pollfd *pds; /* Array of poll descriptors */ + int npds; /* Length of the array */ + int on_ioq; /* Is it on ioq? */ } _st_pollq_t; typedef struct _st_eventsys_ops { - const char *name; /* Name of this event system */ - int val; /* Type of this event system */ - int (*init)(void); /* Initialization */ - void (*dispatch)(void); /* Dispatch function */ - int (*pollset_add)(struct pollfd *, int); /* Add descriptor set */ - void (*pollset_del)(struct pollfd *, int); /* Delete descriptor set */ - int (*fd_new)(int); /* New descriptor allocated */ - int (*fd_close)(int); /* Descriptor closed */ - int (*fd_getlimit)(void); /* Descriptor hard limit */ + const char *name; /* Name of this event system */ + int val; /* Type of this event system */ + int (*init)(void); /* Initialization */ + void (*dispatch)(void); /* Dispatch function */ + int (*pollset_add)(struct pollfd *, int); /* Add descriptor set */ + void (*pollset_del)(struct pollfd *, int); /* Delete descriptor set */ + int (*fd_new)(int); /* New descriptor allocated */ + int (*fd_close)(int); /* Descriptor closed */ + int (*fd_getlimit)(void); /* Descriptor hard limit */ } _st_eventsys_t; typedef struct _st_vp { - _st_thread_t *idle_thread; /* Idle thread for this vp */ - st_utime_t last_clock; /* The last time we went into vp_check_clock() */ - - _st_clist_t run_q; /* run queue for this vp */ - _st_clist_t io_q; /* io queue for this vp */ - _st_clist_t zombie_q; /* zombie queue for this vp */ -#ifdef DEBUG - _st_clist_t thread_q; /* all threads of this vp */ -#endif - int pagesize; - - _st_thread_t *sleep_q; /* sleep queue for this vp */ - int sleepq_size; /* number of threads on sleep queue */ - -#ifdef ST_SWITCH_CB - st_switch_cb_t switch_out_cb; /* called when a thread is switched out */ - st_switch_cb_t switch_in_cb; /* called when a thread is switched in */ -#endif + _st_thread_t *idle_thread; /* Idle thread for this vp */ + st_utime_t last_clock; /* The last time we went into vp_check_clock() */ + + _st_clist_t run_q; /* run queue for this vp */ + _st_clist_t io_q; /* io queue for this vp */ + _st_clist_t zombie_q; /* zombie queue for this vp */ + #ifdef DEBUG + _st_clist_t thread_q; /* all threads of this vp */ + #endif + int pagesize; + + _st_thread_t *sleep_q; /* sleep queue for this vp */ + int sleepq_size; /* number of threads on sleep queue */ + + #ifdef ST_SWITCH_CB + st_switch_cb_t switch_out_cb; /* called when a thread is switched out */ + st_switch_cb_t switch_in_cb; /* called when a thread is switched in */ + #endif } _st_vp_t; typedef struct _st_netfd { - int osfd; /* Underlying OS file descriptor */ - int inuse; /* In-use flag */ - void *private_data; /* Per descriptor private data */ - _st_destructor_t destructor; /* Private data destructor function */ - void *aux_data; /* Auxiliary data for internal use */ - struct _st_netfd *next; /* For putting on the free list */ + int osfd; /* Underlying OS file descriptor */ + int inuse; /* In-use flag */ + void *private_data; /* Per descriptor private data */ + _st_destructor_t destructor; /* Private data destructor function */ + void *aux_data; /* Auxiliary data for internal use */ + struct _st_netfd *next; /* For putting on the free list */ } _st_netfd_t; @@ -265,7 +263,7 @@ extern _st_eventsys_t *_st_eventsys; #define _ST_IOQ (_st_this_vp.io_q) #define _ST_ZOMBIEQ (_st_this_vp.zombie_q) #ifdef DEBUG -#define _ST_THREADQ (_st_this_vp.thread_q) + #define _ST_THREADQ (_st_this_vp.thread_q) #endif #define _ST_PAGE_SIZE (_st_this_vp.pagesize) @@ -293,8 +291,8 @@ extern _st_eventsys_t *_st_eventsys; #define _ST_DEL_ZOMBIEQ(_thr) ST_REMOVE_LINK(&(_thr)->links) #ifdef DEBUG -#define _ST_ADD_THREADQ(_thr) ST_APPEND_LINK(&(_thr)->tlink, &_ST_THREADQ) -#define _ST_DEL_THREADQ(_thr) ST_REMOVE_LINK(&(_thr)->tlink) + #define _ST_ADD_THREADQ(_thr) ST_APPEND_LINK(&(_thr)->tlink, &_ST_THREADQ) + #define _ST_DEL_THREADQ(_thr) ST_REMOVE_LINK(&(_thr)->tlink) #endif @@ -317,13 +315,12 @@ extern _st_eventsys_t *_st_eventsys; #define _ST_FL_INTERRUPT 0x08 #define _ST_FL_TIMEDOUT 0x10 - /***************************************** * Pointer conversion */ #ifndef offsetof -#define offsetof(type, identifier) ((size_t)&(((type *)0)->identifier)) + #define offsetof(type, identifier) ((size_t)&(((type *)0)->identifier)) #endif #define _ST_THREAD_PTR(_qp) \ @@ -339,8 +336,8 @@ extern _st_eventsys_t *_st_eventsys; ((_st_pollq_t *)((char *)(_qp) - offsetof(_st_pollq_t, links))) #ifdef DEBUG -#define _ST_THREAD_THREADQ_PTR(_qp) \ - ((_st_thread_t *)((char *)(_qp) - offsetof(_st_thread_t, tlink))) + #define _ST_THREAD_THREADQ_PTR(_qp) \ + ((_st_thread_t *)((char *)(_qp) - offsetof(_st_thread_t, tlink))) #endif @@ -349,21 +346,21 @@ extern _st_eventsys_t *_st_eventsys; */ #ifndef ST_UTIME_NO_TIMEOUT -#define ST_UTIME_NO_TIMEOUT ((st_utime_t) -1LL) + #define ST_UTIME_NO_TIMEOUT ((st_utime_t) -1LL) #endif #ifndef __ia64__ -#define ST_DEFAULT_STACK_SIZE (64*1024) + #define ST_DEFAULT_STACK_SIZE (64*1024) #else -#define ST_DEFAULT_STACK_SIZE (128*1024) /* Includes register stack size */ + #define ST_DEFAULT_STACK_SIZE (128*1024) /* Includes register stack size */ #endif #ifndef ST_KEYS_MAX -#define ST_KEYS_MAX 16 + #define ST_KEYS_MAX 16 #endif #ifndef ST_MIN_POLLFDS_SIZE -#define ST_MIN_POLLFDS_SIZE 64 + #define ST_MIN_POLLFDS_SIZE 64 #endif @@ -372,28 +369,28 @@ extern _st_eventsys_t *_st_eventsys; */ #ifdef DEBUG -void _st_iterate_threads(void); -#define ST_DEBUG_ITERATE_THREADS() _st_iterate_threads() + void _st_iterate_threads(void); + #define ST_DEBUG_ITERATE_THREADS() _st_iterate_threads() #else -#define ST_DEBUG_ITERATE_THREADS() + #define ST_DEBUG_ITERATE_THREADS() #endif #ifdef ST_SWITCH_CB -#define ST_SWITCH_OUT_CB(_thread) \ - if (_st_this_vp.switch_out_cb != NULL && \ + #define ST_SWITCH_OUT_CB(_thread) \ + if (_st_this_vp.switch_out_cb != NULL && \ + _thread != _st_this_vp.idle_thread && \ + _thread->state != _ST_ST_ZOMBIE) { \ + _st_this_vp.switch_out_cb(); \ + } + #define ST_SWITCH_IN_CB(_thread) \ + if (_st_this_vp.switch_in_cb != NULL && \ _thread != _st_this_vp.idle_thread && \ _thread->state != _ST_ST_ZOMBIE) { \ - _st_this_vp.switch_out_cb(); \ - } -#define ST_SWITCH_IN_CB(_thread) \ - if (_st_this_vp.switch_in_cb != NULL && \ - _thread != _st_this_vp.idle_thread && \ - _thread->state != _ST_ST_ZOMBIE) { \ - _st_this_vp.switch_in_cb(); \ - } + _st_this_vp.switch_in_cb(); \ + } #else -#define ST_SWITCH_OUT_CB(_thread) -#define ST_SWITCH_IN_CB(_thread) + #define ST_SWITCH_OUT_CB(_thread) + #define ST_SWITCH_IN_CB(_thread) #endif /* @@ -424,9 +421,9 @@ void _st_iterate_threads(void); * Initialize the thread context preparing it to execute _main */ #ifdef MD_INIT_CONTEXT -#define _ST_INIT_CONTEXT MD_INIT_CONTEXT + #define _ST_INIT_CONTEXT MD_INIT_CONTEXT #else -#error Unknown OS + #error Unknown OS #endif /* @@ -456,11 +453,9 @@ int st_cond_destroy(_st_cond_t *cvar); int st_cond_timedwait(_st_cond_t *cvar, st_utime_t timeout); int st_cond_signal(_st_cond_t *cvar); ssize_t st_read(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout); -ssize_t st_write(_st_netfd_t *fd, const void *buf, size_t nbyte, - st_utime_t timeout); +ssize_t st_write(_st_netfd_t *fd, const void *buf, size_t nbyte, st_utime_t timeout); int st_poll(struct pollfd *pds, int npds, st_utime_t timeout); -_st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg, - int joinable, int stk_size); +_st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg, int joinable, int stk_size); #endif /* !__ST_COMMON_H__ */ diff --git a/trunk/research/st/md.h b/trunk/research/st/md.h index 5bf795f242..61a0fac96c 100644 --- a/trunk/research/st/md.h +++ b/trunk/research/st/md.h @@ -43,15 +43,15 @@ #define __ST_MD_H__ #if defined(ETIMEDOUT) && !defined(ETIME) -#define ETIME ETIMEDOUT + #define ETIME ETIMEDOUT #endif #if defined(MAP_ANONYMOUS) && !defined(MAP_ANON) -#define MAP_ANON MAP_ANONYMOUS + #define MAP_ANON MAP_ANONYMOUS #endif #ifndef MAP_FAILED -#define MAP_FAILED -1 + #define MAP_FAILED -1 #endif /***************************************** @@ -59,35 +59,33 @@ */ #if defined (AIX) - -#define MD_STACK_GROWS_DOWN -#define MD_USE_SYSV_ANON_MMAP -#define MD_ACCEPT_NB_INHERITED -#define MD_ALWAYS_UNSERIALIZED_ACCEPT - -#ifndef MD_HAVE_SOCKLEN_T -#define MD_HAVE_SOCKLEN_T -#define socklen_t unsigned long -#endif - -#define MD_SETJMP(env) _setjmp(env) -#define MD_LONGJMP(env, val) _longjmp(env, val) - -#define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - (_thread)->context[3] = (long) (_sp); \ - ST_END_MACRO - -#define MD_GET_UTIME() \ - timebasestruct_t rt; \ - (void) read_real_time(&rt, TIMEBASE_SZ); \ - (void) time_base_to_time(&rt, TIMEBASE_SZ); \ - return (rt.tb_high * 1000000LL + rt.tb_low / 1000) + #define MD_STACK_GROWS_DOWN + #define MD_USE_SYSV_ANON_MMAP + #define MD_ACCEPT_NB_INHERITED + #define MD_ALWAYS_UNSERIALIZED_ACCEPT + + #ifndef MD_HAVE_SOCKLEN_T + #define MD_HAVE_SOCKLEN_T + #define socklen_t unsigned long + #endif + + #define MD_SETJMP(env) _setjmp(env) + #define MD_LONGJMP(env, val) _longjmp(env, val) + + #define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + (_thread)->context[3] = (long) (_sp); \ + ST_END_MACRO + + #define MD_GET_UTIME() \ + timebasestruct_t rt; \ + (void) read_real_time(&rt, TIMEBASE_SZ); \ + (void) time_base_to_time(&rt, TIMEBASE_SZ); \ + return (rt.tb_high * 1000000LL + rt.tb_low / 1000) #elif defined (CYGWIN) - #define MD_STACK_GROWS_DOWN #define MD_USE_BSD_ANON_MMAP #define MD_ACCEPT_NB_NOT_INHERITED diff --git a/trunk/research/st/public.h b/trunk/research/st/public.h index 8c2ce8b8d6..e306e3e8c0 100644 --- a/trunk/research/st/public.h +++ b/trunk/research/st/public.h @@ -129,8 +129,7 @@ extern "C" { extern void st_netfd_free(st_netfd_t fd); extern int st_netfd_close(st_netfd_t fd); extern int st_netfd_fileno(st_netfd_t fd); - extern void st_netfd_setspecific(st_netfd_t fd, void *value, - void (*destructor)(void *)); + extern void st_netfd_setspecific(st_netfd_t fd, void *value, void (*destructor)(void *)); extern void *st_netfd_getspecific(st_netfd_t fd); extern int st_netfd_serialize_accept(st_netfd_t fd); extern int st_netfd_poll(st_netfd_t fd, int how, st_utime_t timeout); From 0f293802c80da5d6dab26620a809ca6417d9d501 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 5 Nov 2014 17:42:57 +0800 Subject: [PATCH 059/800] research st: refine the md.h --- trunk/research/st/md.h | 964 ++++++++++++++++++++--------------------- 1 file changed, 471 insertions(+), 493 deletions(-) diff --git a/trunk/research/st/md.h b/trunk/research/st/md.h index 61a0fac96c..3b6acc5a5f 100644 --- a/trunk/research/st/md.h +++ b/trunk/research/st/md.h @@ -57,7 +57,6 @@ /***************************************** * Platform specifics */ - #if defined (AIX) #define MD_STACK_GROWS_DOWN #define MD_USE_SYSV_ANON_MMAP @@ -86,539 +85,518 @@ return (rt.tb_high * 1000000LL + rt.tb_low / 1000) #elif defined (CYGWIN) -#define MD_STACK_GROWS_DOWN -#define MD_USE_BSD_ANON_MMAP -#define MD_ACCEPT_NB_NOT_INHERITED -#define MD_ALWAYS_UNSERIALIZED_ACCEPT - -#define MD_SETJMP(env) setjmp(env) -#define MD_LONGJMP(env, val) longjmp(env, val) - -#define MD_JB_SP 7 - -#define MD_GET_SP(_t) (_t)->context[MD_JB_SP] + #define MD_STACK_GROWS_DOWN + #define MD_USE_BSD_ANON_MMAP + #define MD_ACCEPT_NB_NOT_INHERITED + #define MD_ALWAYS_UNSERIALIZED_ACCEPT + + #define MD_SETJMP(env) setjmp(env) + #define MD_LONGJMP(env, val) longjmp(env, val) + + #define MD_JB_SP 7 + + #define MD_GET_SP(_t) (_t)->context[MD_JB_SP] -#define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - MD_GET_SP(_thread) = (long) (_sp); \ - ST_END_MACRO + #define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + MD_GET_SP(_thread) = (long) (_sp); \ + ST_END_MACRO -#define MD_GET_UTIME() \ - struct timeval tv; \ - (void) gettimeofday(&tv, NULL); \ - return (tv.tv_sec * 1000000LL + tv.tv_usec) + #define MD_GET_UTIME() \ + struct timeval tv; \ + (void) gettimeofday(&tv, NULL); \ + return (tv.tv_sec * 1000000LL + tv.tv_usec) #elif defined (DARWIN) + #define MD_STACK_GROWS_DOWN + #define MD_USE_BSD_ANON_MMAP + #define MD_ACCEPT_NB_INHERITED + #define MD_ALWAYS_UNSERIALIZED_ACCEPT + #define MD_HAVE_SOCKLEN_T + + #define MD_SETJMP(env) _setjmp(env) + #define MD_LONGJMP(env, val) _longjmp(env, val) + + #if defined(__ppc__) + #define MD_JB_SP 0 + #elif defined(__i386__) + #define MD_JB_SP 9 + #elif defined(__x86_64__) + #define MD_JB_SP 4 + #else + #error Unknown CPU architecture + #endif -#define MD_STACK_GROWS_DOWN -#define MD_USE_BSD_ANON_MMAP -#define MD_ACCEPT_NB_INHERITED -#define MD_ALWAYS_UNSERIALIZED_ACCEPT -#define MD_HAVE_SOCKLEN_T - -#define MD_SETJMP(env) _setjmp(env) -#define MD_LONGJMP(env, val) _longjmp(env, val) - -#if defined(__ppc__) -#define MD_JB_SP 0 -#elif defined(__i386__) -#define MD_JB_SP 9 -#elif defined(__x86_64__) -#define MD_JB_SP 4 -#else -#error Unknown CPU architecture -#endif - -#define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - *((long *)&((_thread)->context[MD_JB_SP])) = (long) (_sp); \ - ST_END_MACRO + #define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + *((long *)&((_thread)->context[MD_JB_SP])) = (long) (_sp); \ + ST_END_MACRO -#define MD_GET_UTIME() \ - struct timeval tv; \ - (void) gettimeofday(&tv, NULL); \ - return (tv.tv_sec * 1000000LL + tv.tv_usec) + #define MD_GET_UTIME() \ + struct timeval tv; \ + (void) gettimeofday(&tv, NULL); \ + return (tv.tv_sec * 1000000LL + tv.tv_usec) #elif defined (FREEBSD) + #define MD_STACK_GROWS_DOWN + #define MD_USE_BSD_ANON_MMAP + #define MD_ACCEPT_NB_INHERITED + #define MD_ALWAYS_UNSERIALIZED_ACCEPT + + #define MD_SETJMP(env) _setjmp(env) + #define MD_LONGJMP(env, val) _longjmp(env, val) -#define MD_STACK_GROWS_DOWN -#define MD_USE_BSD_ANON_MMAP -#define MD_ACCEPT_NB_INHERITED -#define MD_ALWAYS_UNSERIALIZED_ACCEPT - -#define MD_SETJMP(env) _setjmp(env) -#define MD_LONGJMP(env, val) _longjmp(env, val) - -#if defined(__i386__) -#define MD_JB_SP 2 -#elif defined(__alpha__) -#define MD_JB_SP 34 -#elif defined(__amd64__) -#define MD_JB_SP 2 -#else -#error Unknown CPU architecture -#endif + #if defined(__i386__) + #define MD_JB_SP 2 + #elif defined(__alpha__) + #define MD_JB_SP 34 + #elif defined(__amd64__) + #define MD_JB_SP 2 + #else + #error Unknown CPU architecture + #endif -#define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - (_thread)->context[0]._jb[MD_JB_SP] = (long) (_sp); \ - ST_END_MACRO + #define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + (_thread)->context[0]._jb[MD_JB_SP] = (long) (_sp); \ + ST_END_MACRO -#define MD_GET_UTIME() \ - struct timeval tv; \ - (void) gettimeofday(&tv, NULL); \ - return (tv.tv_sec * 1000000LL + tv.tv_usec) + #define MD_GET_UTIME() \ + struct timeval tv; \ + (void) gettimeofday(&tv, NULL); \ + return (tv.tv_sec * 1000000LL + tv.tv_usec) #elif defined (HPUX) - -#define MD_STACK_GROWS_UP -#define MD_USE_BSD_ANON_MMAP -#define MD_ACCEPT_NB_INHERITED -#define MD_ALWAYS_UNSERIALIZED_ACCEPT - -#define MD_SETJMP(env) _setjmp(env) -#define MD_LONGJMP(env, val) _longjmp(env, val) - -#ifndef __LP64__ -/* 32-bit mode (ILP32 data model) */ -#define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - ((long *)((_thread)->context))[1] = (long) (_sp); \ - ST_END_MACRO -#else -/* 64-bit mode (LP64 data model) */ -#define MD_STACK_PAD_SIZE 256 -/* Last stack frame must be preserved */ -#define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - memcpy((char *)(_sp) - MD_STACK_PAD_SIZE, \ - ((char **)((_thread)->context))[1] - MD_STACK_PAD_SIZE, \ - MD_STACK_PAD_SIZE); \ - ((long *)((_thread)->context))[1] = (long) (_sp); \ - ST_END_MACRO -#endif /* !__LP64__ */ - -#define MD_GET_UTIME() \ - struct timeval tv; \ - (void) gettimeofday(&tv, NULL); \ - return (tv.tv_sec * 1000000LL + tv.tv_usec) + #define MD_STACK_GROWS_UP + #define MD_USE_BSD_ANON_MMAP + #define MD_ACCEPT_NB_INHERITED + #define MD_ALWAYS_UNSERIALIZED_ACCEPT + + #define MD_SETJMP(env) _setjmp(env) + #define MD_LONGJMP(env, val) _longjmp(env, val) + + #ifndef __LP64__ + /* 32-bit mode (ILP32 data model) */ + #define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + ((long *)((_thread)->context))[1] = (long) (_sp); \ + ST_END_MACRO + #else + /* 64-bit mode (LP64 data model) */ + #define MD_STACK_PAD_SIZE 256 + /* Last stack frame must be preserved */ + #define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + memcpy((char *)(_sp) - MD_STACK_PAD_SIZE, \ + ((char **)((_thread)->context))[1] - MD_STACK_PAD_SIZE, \ + MD_STACK_PAD_SIZE); \ + ((long *)((_thread)->context))[1] = (long) (_sp); \ + ST_END_MACRO + #endif /* !__LP64__ */ + + #define MD_GET_UTIME() \ + struct timeval tv; \ + (void) gettimeofday(&tv, NULL); \ + return (tv.tv_sec * 1000000LL + tv.tv_usec) #elif defined (IRIX) + #include + + #define MD_STACK_GROWS_DOWN + #define MD_USE_SYSV_ANON_MMAP + #define MD_ACCEPT_NB_INHERITED + #define MD_ALWAYS_UNSERIALIZED_ACCEPT + + #define MD_SETJMP(env) setjmp(env) + #define MD_LONGJMP(env, val) longjmp(env, val) -#include - -#define MD_STACK_GROWS_DOWN -#define MD_USE_SYSV_ANON_MMAP -#define MD_ACCEPT_NB_INHERITED -#define MD_ALWAYS_UNSERIALIZED_ACCEPT - -#define MD_SETJMP(env) setjmp(env) -#define MD_LONGJMP(env, val) longjmp(env, val) - -#define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - (void) MD_SETJMP((_thread)->context); \ - (_thread)->context[JB_SP] = (long) (_sp); \ - (_thread)->context[JB_PC] = (long) _main; \ - ST_END_MACRO - -#define MD_GET_UTIME() \ - static int inited = 0; \ - static clockid_t clock_id = CLOCK_SGI_CYCLE; \ - struct timespec ts; \ - if (!inited) { \ - if (syssgi(SGI_CYCLECNTR_SIZE) < 64) \ - clock_id = CLOCK_REALTIME; \ - inited = 1; \ - } \ - (void) clock_gettime(clock_id, &ts); \ - return (ts.tv_sec * 1000000LL + ts.tv_nsec / 1000) + #define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + (void) MD_SETJMP((_thread)->context); \ + (_thread)->context[JB_SP] = (long) (_sp); \ + (_thread)->context[JB_PC] = (long) _main; \ + ST_END_MACRO -/* - * Cap the stack by zeroing out the saved return address register - * value. This allows libexc, used by SpeedShop, to know when to stop - * backtracing since it won't find main, start, or any other known - * stack root function in a state thread's stack. Without this libexc - * traces right off the stack and crashes. - * The function preamble stores ra at 8(sp), this stores zero there. - * N.B. This macro is compiler/ABI dependent. It must change if ANY more - * automatic variables are added to the _st_thread_main() routine, because - * the address where ra is stored will change. - */ -#if !defined(__GNUC__) && defined(_MIPS_SIM) && _MIPS_SIM != _ABIO32 -#define MD_CAP_STACK(var_addr) \ - (((volatile __uint64_t *)(var_addr))[1] = 0) -#endif + #define MD_GET_UTIME() \ + static int inited = 0; \ + static clockid_t clock_id = CLOCK_SGI_CYCLE; \ + struct timespec ts; \ + if (!inited) { \ + if (syssgi(SGI_CYCLECNTR_SIZE) < 64) \ + clock_id = CLOCK_REALTIME; \ + inited = 1; \ + } \ + (void) clock_gettime(clock_id, &ts); \ + return (ts.tv_sec * 1000000LL + ts.tv_nsec / 1000) + + /* + * Cap the stack by zeroing out the saved return address register + * value. This allows libexc, used by SpeedShop, to know when to stop + * backtracing since it won't find main, start, or any other known + * stack root function in a state thread's stack. Without this libexc + * traces right off the stack and crashes. + * The function preamble stores ra at 8(sp), this stores zero there. + * N.B. This macro is compiler/ABI dependent. It must change if ANY more + * automatic variables are added to the _st_thread_main() routine, because + * the address where ra is stored will change. + */ + #if !defined(__GNUC__) && defined(_MIPS_SIM) && _MIPS_SIM != _ABIO32 + #define MD_CAP_STACK(var_addr) \ + (((volatile __uint64_t *)(var_addr))[1] = 0) + #endif #elif defined (LINUX) - -/* - * These are properties of the linux kernel and are the same on every - * flavor and architecture. - */ -#define MD_USE_BSD_ANON_MMAP -#define MD_ACCEPT_NB_NOT_INHERITED -#define MD_ALWAYS_UNSERIALIZED_ACCEPT -/* - * Modern GNU/Linux is Posix.1g compliant. - */ -#define MD_HAVE_SOCKLEN_T - -/* - * All architectures and flavors of linux have the gettimeofday - * function but if you know of a faster way, use it. - */ -#define MD_GET_UTIME() \ - struct timeval tv; \ - (void) gettimeofday(&tv, NULL); \ - return (tv.tv_sec * 1000000LL + tv.tv_usec) - -#if defined(__ia64__) -#define MD_STACK_GROWS_DOWN - -/* - * IA-64 architecture. Besides traditional memory call stack, IA-64 - * uses general register stack. Thus each thread needs a backing store - * for register stack in addition to memory stack. Standard - * setjmp()/longjmp() cannot be used for thread context switching - * because their implementation implicitly assumes that only one - * register stack exists. - */ -#ifdef USE_LIBC_SETJMP -#undef USE_LIBC_SETJMP -#endif -#define MD_USE_BUILTIN_SETJMP - -#define MD_STACK_PAD_SIZE 128 -/* Last register stack frame must be preserved */ -#define MD_INIT_CONTEXT(_thread, _sp, _bsp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - memcpy((char *)(_bsp) - MD_STACK_PAD_SIZE, \ - (char *)(_thread)->context[0].__jmpbuf[17] - MD_STACK_PAD_SIZE, \ - MD_STACK_PAD_SIZE); \ - (_thread)->context[0].__jmpbuf[0] = (long) (_sp); \ - (_thread)->context[0].__jmpbuf[17] = (long) (_bsp); \ - ST_END_MACRO - -#elif defined(__mips__) -#define MD_STACK_GROWS_DOWN - -#define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - MD_SETJMP((_thread)->context); \ - _thread->context[0].__jmpbuf[0].__pc = (__ptr_t) _main; \ - _thread->context[0].__jmpbuf[0].__sp = _sp; \ - ST_END_MACRO - -#else /* Not IA-64 or mips */ - -/* - * On linux, there are a few styles of jmpbuf format. These vary based - * on architecture/glibc combination. - * - * Most of the glibc based toggles were lifted from: - * mozilla/nsprpub/pr/include/md/_linux.h - */ - -/* - * Starting with glibc 2.4, JB_SP definitions are not public anymore. - * They, however, can still be found in glibc source tree in - * architecture-specific "jmpbuf-offsets.h" files. - * Most importantly, the content of jmp_buf is mangled by setjmp to make - * it completely opaque (the mangling can be disabled by setting the - * LD_POINTER_GUARD environment variable before application execution). - * Therefore we will use built-in _st_md_cxt_save/_st_md_cxt_restore - * functions as a setjmp/longjmp replacement wherever they are available - * unless USE_LIBC_SETJMP is defined. - */ - -#if defined(__powerpc__) -#define MD_STACK_GROWS_DOWN - -#if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1) -#ifndef JB_GPR1 -#define JB_GPR1 0 -#endif -#define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_GPR1] -#else -/* not an error but certainly cause for caution */ -#error "Untested use of old glibc on powerpc" -#define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__misc[0] -#endif /* glibc 2.1 or later */ - -#elif defined(__alpha) -#define MD_STACK_GROWS_DOWN - -#if defined(__GLIBC__) && __GLIBC__ >= 2 -#ifndef JB_SP -#define JB_SP 8 -#endif -#define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_SP] -#else -/* not an error but certainly cause for caution */ -#error "Untested use of old glibc on alpha" -#define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__sp -#endif - -#elif defined(__mc68000__) -#define MD_STACK_GROWS_DOWN - -/* m68k still uses old style sigjmp_buf */ -#define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__sp - -#elif defined(__sparc__) -#define MD_STACK_GROWS_DOWN - -#if defined(__GLIBC__) && __GLIBC__ >= 2 -#ifndef JB_SP -#define JB_SP 0 -#endif -#define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_SP] -#else -/* not an error but certainly cause for caution */ -#error "Untested use of old glic on sparc -- also using odd mozilla derived __fp" -#define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__fp -#endif - -#elif defined(__i386__) -#define MD_STACK_GROWS_DOWN -#define MD_USE_BUILTIN_SETJMP - -#if defined(__GLIBC__) && __GLIBC__ >= 2 -#ifndef JB_SP -#define JB_SP 4 -#endif -#define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_SP] -#else -/* not an error but certainly cause for caution */ -#error "Untested use of old glibc on i386" -#define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__sp -#endif - -#elif defined(__amd64__) || defined(__x86_64__) -#define MD_STACK_GROWS_DOWN -#define MD_USE_BUILTIN_SETJMP - -#ifndef JB_RSP -#define JB_RSP 6 -#endif -#define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_RSP] - -#elif defined(__arm__) -#define MD_STACK_GROWS_DOWN - -#if defined(__GLIBC__) && __GLIBC__ >= 2 -#define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[20] -#else -#error "ARM/Linux pre-glibc2 not supported yet" -#endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */ - -#elif defined(__s390__) -#define MD_STACK_GROWS_DOWN - -/* There is no JB_SP in glibc at this time. (glibc 2.2.5) - */ -#define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__gregs[9] - -#elif defined(__hppa__) -#define MD_STACK_GROWS_UP - -/* yes, this is gross, unfortunately at the moment (2002/08/01) there is - * a bug in hppa's glibc header definition for JB_SP, so we can't - * use that... - */ -#define MD_GET_SP(_t) (*(long *)(((char *)&(_t)->context[0].__jmpbuf[0]) + 76)) - -#else -#error "Unknown CPU architecture" -#endif /* Cases with common MD_INIT_CONTEXT and different SP locations */ - -#define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - MD_GET_SP(_thread) = (long) (_sp); \ - ST_END_MACRO - -#endif /* Cases with different MD_INIT_CONTEXT */ - -#if defined(MD_USE_BUILTIN_SETJMP) && !defined(USE_LIBC_SETJMP) -#define MD_SETJMP(env) _st_md_cxt_save(env) -#define MD_LONGJMP(env, val) _st_md_cxt_restore(env, val) - -extern int _st_md_cxt_save(jmp_buf env); -extern void _st_md_cxt_restore(jmp_buf env, int val); -#else -#define MD_SETJMP(env) setjmp(env) -#define MD_LONGJMP(env, val) longjmp(env, val) -#endif + /* + * These are properties of the linux kernel and are the same on every + * flavor and architecture. + */ + #define MD_USE_BSD_ANON_MMAP + #define MD_ACCEPT_NB_NOT_INHERITED + #define MD_ALWAYS_UNSERIALIZED_ACCEPT + /* + * Modern GNU/Linux is Posix.1g compliant. + */ + #define MD_HAVE_SOCKLEN_T + + /* + * All architectures and flavors of linux have the gettimeofday + * function but if you know of a faster way, use it. + */ + #define MD_GET_UTIME() \ + struct timeval tv; \ + (void) gettimeofday(&tv, NULL); \ + return (tv.tv_sec * 1000000LL + tv.tv_usec) + + #if defined(__ia64__) + #define MD_STACK_GROWS_DOWN + + /* + * IA-64 architecture. Besides traditional memory call stack, IA-64 + * uses general register stack. Thus each thread needs a backing store + * for register stack in addition to memory stack. Standard + * setjmp()/longjmp() cannot be used for thread context switching + * because their implementation implicitly assumes that only one + * register stack exists. + */ + #ifdef USE_LIBC_SETJMP + #undef USE_LIBC_SETJMP + #endif + #define MD_USE_BUILTIN_SETJMP + + #define MD_STACK_PAD_SIZE 128 + /* Last register stack frame must be preserved */ + #define MD_INIT_CONTEXT(_thread, _sp, _bsp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + memcpy((char *)(_bsp) - MD_STACK_PAD_SIZE, \ + (char *)(_thread)->context[0].__jmpbuf[17] - MD_STACK_PAD_SIZE, \ + MD_STACK_PAD_SIZE); \ + (_thread)->context[0].__jmpbuf[0] = (long) (_sp); \ + (_thread)->context[0].__jmpbuf[17] = (long) (_bsp); \ + ST_END_MACRO + #elif defined(__mips__) + #define MD_STACK_GROWS_DOWN + + #define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + MD_SETJMP((_thread)->context); \ + _thread->context[0].__jmpbuf[0].__pc = (__ptr_t) _main; \ + _thread->context[0].__jmpbuf[0].__sp = _sp; \ + ST_END_MACRO + #else /* Not IA-64 or mips */ + /* + * On linux, there are a few styles of jmpbuf format. These vary based + * on architecture/glibc combination. + * + * Most of the glibc based toggles were lifted from: + * mozilla/nsprpub/pr/include/md/_linux.h + */ + /* + * Starting with glibc 2.4, JB_SP definitions are not public anymore. + * They, however, can still be found in glibc source tree in + * architecture-specific "jmpbuf-offsets.h" files. + * Most importantly, the content of jmp_buf is mangled by setjmp to make + * it completely opaque (the mangling can be disabled by setting the + * LD_POINTER_GUARD environment variable before application execution). + * Therefore we will use built-in _st_md_cxt_save/_st_md_cxt_restore + * functions as a setjmp/longjmp replacement wherever they are available + * unless USE_LIBC_SETJMP is defined. + */ + #if defined(__powerpc__) + #define MD_STACK_GROWS_DOWN + + #if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1) + #ifndef JB_GPR1 + #define JB_GPR1 0 + #endif + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_GPR1] + #else + /* not an error but certainly cause for caution */ + #error "Untested use of old glibc on powerpc" + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__misc[0] + #endif /* glibc 2.1 or later */ + #elif defined(__alpha) + #define MD_STACK_GROWS_DOWN + + #if defined(__GLIBC__) && __GLIBC__ >= 2 + #ifndef JB_SP + #define JB_SP 8 + #endif + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_SP] + #else + /* not an error but certainly cause for caution */ + #error "Untested use of old glibc on alpha" + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__sp + #endif + #elif defined(__mc68000__) + #define MD_STACK_GROWS_DOWN + + /* m68k still uses old style sigjmp_buf */ + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__sp + #elif defined(__sparc__) + #define MD_STACK_GROWS_DOWN + + #if defined(__GLIBC__) && __GLIBC__ >= 2 + #ifndef JB_SP + #define JB_SP 0 + #endif + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_SP] + #else + /* not an error but certainly cause for caution */ + #error "Untested use of old glic on sparc -- also using odd mozilla derived __fp" + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__fp + #endif + #elif defined(__i386__) + #define MD_STACK_GROWS_DOWN + #define MD_USE_BUILTIN_SETJMP + + #if defined(__GLIBC__) && __GLIBC__ >= 2 + #ifndef JB_SP + #define JB_SP 4 + #endif + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_SP] + #else + /* not an error but certainly cause for caution */ + #error "Untested use of old glibc on i386" + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__sp + #endif + #elif defined(__amd64__) || defined(__x86_64__) + #define MD_STACK_GROWS_DOWN + #define MD_USE_BUILTIN_SETJMP + + #ifndef JB_RSP + #define JB_RSP 6 + #endif + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_RSP] + #elif defined(__arm__) + #define MD_STACK_GROWS_DOWN + + #if defined(__GLIBC__) && __GLIBC__ >= 2 + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[8] + #else + #error "ARM/Linux pre-glibc2 not supported yet" + #endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */ + #elif defined(__s390__) + #define MD_STACK_GROWS_DOWN + + /* There is no JB_SP in glibc at this time. (glibc 2.2.5) + */ + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__gregs[9] + #elif defined(__hppa__) + #define MD_STACK_GROWS_UP + + /* yes, this is gross, unfortunately at the moment (2002/08/01) there is + * a bug in hppa's glibc header definition for JB_SP, so we can't + * use that... + */ + #define MD_GET_SP(_t) (*(long *)(((char *)&(_t)->context[0].__jmpbuf[0]) + 76)) + #else + #error "Unknown CPU architecture" + #endif /* Cases with common MD_INIT_CONTEXT and different SP locations */ + + #define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + MD_GET_SP(_thread) = (long) (_sp); \ + ST_END_MACRO + #endif /* Cases with different MD_INIT_CONTEXT */ + + #if defined(MD_USE_BUILTIN_SETJMP) && !defined(USE_LIBC_SETJMP) + #define MD_SETJMP(env) _st_md_cxt_save(env) + #define MD_LONGJMP(env, val) _st_md_cxt_restore(env, val) + + extern int _st_md_cxt_save(jmp_buf env); + extern void _st_md_cxt_restore(jmp_buf env, int val); + #else + #define MD_SETJMP(env) setjmp(env) + #define MD_LONGJMP(env, val) longjmp(env, val) + #endif #elif defined (NETBSD) - -#define MD_STACK_GROWS_DOWN -#define MD_USE_BSD_ANON_MMAP -#define MD_ACCEPT_NB_INHERITED -#define MD_ALWAYS_UNSERIALIZED_ACCEPT -#define MD_HAVE_SOCKLEN_T - -#define MD_SETJMP(env) _setjmp(env) -#define MD_LONGJMP(env, val) _longjmp(env, val) - -#if defined(__i386__) -#define MD_JB_SP 2 -#elif defined(__alpha__) -#define MD_JB_SP 34 -#elif defined(__sparc__) -#define MD_JB_SP 0 -#elif defined(__vax__) -#define MD_JB_SP 2 -#else -#error Unknown CPU architecture -#endif - -#define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - (_thread)->context[MD_JB_SP] = (long) (_sp); \ - ST_END_MACRO - -#define MD_GET_UTIME() \ - struct timeval tv; \ - (void) gettimeofday(&tv, NULL); \ - return (tv.tv_sec * 1000000LL + tv.tv_usec) + #define MD_STACK_GROWS_DOWN + #define MD_USE_BSD_ANON_MMAP + #define MD_ACCEPT_NB_INHERITED + #define MD_ALWAYS_UNSERIALIZED_ACCEPT + #define MD_HAVE_SOCKLEN_T + + #define MD_SETJMP(env) _setjmp(env) + #define MD_LONGJMP(env, val) _longjmp(env, val) + + #if defined(__i386__) + #define MD_JB_SP 2 + #elif defined(__alpha__) + #define MD_JB_SP 34 + #elif defined(__sparc__) + #define MD_JB_SP 0 + #elif defined(__vax__) + #define MD_JB_SP 2 + #else + #error Unknown CPU architecture + #endif + + #define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + (_thread)->context[MD_JB_SP] = (long) (_sp); \ + ST_END_MACRO + + #define MD_GET_UTIME() \ + struct timeval tv; \ + (void) gettimeofday(&tv, NULL); \ + return (tv.tv_sec * 1000000LL + tv.tv_usec) #elif defined (OPENBSD) - -#define MD_STACK_GROWS_DOWN -#define MD_USE_BSD_ANON_MMAP -#define MD_ACCEPT_NB_INHERITED -#define MD_ALWAYS_UNSERIALIZED_ACCEPT - -#define MD_SETJMP(env) _setjmp(env) -#define MD_LONGJMP(env, val) _longjmp(env, val) - -#if defined(__i386__) -#define MD_JB_SP 2 -#elif defined(__alpha__) -#define MD_JB_SP 34 -#elif defined(__sparc__) -#define MD_JB_SP 0 -#elif defined(__amd64__) -#define MD_JB_SP 6 -#else -#error Unknown CPU architecture -#endif - -#define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - (_thread)->context[MD_JB_SP] = (long) (_sp); \ - ST_END_MACRO - -#define MD_GET_UTIME() \ - struct timeval tv; \ - (void) gettimeofday(&tv, NULL); \ - return (tv.tv_sec * 1000000LL + tv.tv_usec) + #define MD_STACK_GROWS_DOWN + #define MD_USE_BSD_ANON_MMAP + #define MD_ACCEPT_NB_INHERITED + #define MD_ALWAYS_UNSERIALIZED_ACCEPT + + #define MD_SETJMP(env) _setjmp(env) + #define MD_LONGJMP(env, val) _longjmp(env, val) + + #if defined(__i386__) + #define MD_JB_SP 2 + #elif defined(__alpha__) + #define MD_JB_SP 34 + #elif defined(__sparc__) + #define MD_JB_SP 0 + #elif defined(__amd64__) + #define MD_JB_SP 6 + #else + #error Unknown CPU architecture + #endif + + #define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + (_thread)->context[MD_JB_SP] = (long) (_sp); \ + ST_END_MACRO + + #define MD_GET_UTIME() \ + struct timeval tv; \ + (void) gettimeofday(&tv, NULL); \ + return (tv.tv_sec * 1000000LL + tv.tv_usec) #elif defined (OSF1) - -#include - -#define MD_STACK_GROWS_DOWN -#define MD_USE_SYSV_ANON_MMAP -#define MD_ACCEPT_NB_NOT_INHERITED -#define MD_ALWAYS_UNSERIALIZED_ACCEPT - -#define MD_SETJMP(env) _setjmp(env) -#define MD_LONGJMP(env, val) _longjmp(env, val) - -#define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - ((struct sigcontext *)((_thread)->context))->sc_sp = (long) (_sp); \ - ST_END_MACRO - -#define MD_GET_UTIME() \ - struct timeval tv; \ - (void) gettimeofday(&tv, NULL); \ - return (tv.tv_sec * 1000000LL + tv.tv_usec) + #include + + #define MD_STACK_GROWS_DOWN + #define MD_USE_SYSV_ANON_MMAP + #define MD_ACCEPT_NB_NOT_INHERITED + #define MD_ALWAYS_UNSERIALIZED_ACCEPT + + #define MD_SETJMP(env) _setjmp(env) + #define MD_LONGJMP(env, val) _longjmp(env, val) + + #define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + ((struct sigcontext *)((_thread)->context))->sc_sp = (long) (_sp); \ + ST_END_MACRO + + #define MD_GET_UTIME() \ + struct timeval tv; \ + (void) gettimeofday(&tv, NULL); \ + return (tv.tv_sec * 1000000LL + tv.tv_usec) #elif defined (SOLARIS) - -#include -extern int getpagesize(void); - -#define MD_STACK_GROWS_DOWN -#define MD_USE_SYSV_ANON_MMAP -#define MD_ACCEPT_NB_NOT_INHERITED - -#define MD_SETJMP(env) setjmp(env) -#define MD_LONGJMP(env, val) longjmp(env, val) - -#if defined(sparc) || defined(__sparc) -#ifdef _LP64 -#define MD_STACK_PAD_SIZE 4095 -#endif -#define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - (void) MD_SETJMP((_thread)->context); \ - (_thread)->context[1] = (long) (_sp); \ - (_thread)->context[2] = (long) _main; \ - ST_END_MACRO -#elif defined(i386) || defined(__i386) -#define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - (void) MD_SETJMP((_thread)->context); \ - (_thread)->context[4] = (long) (_sp); \ - (_thread)->context[5] = (long) _main; \ - ST_END_MACRO -#elif defined(__amd64__) -#define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - (_thread)->context[6] = (long) (_sp); \ - ST_END_MACRO -#else -#error Unknown CPU architecture -#endif - -#define MD_GET_UTIME() \ - return (gethrtime() / 1000) + #include + extern int getpagesize(void); + + #define MD_STACK_GROWS_DOWN + #define MD_USE_SYSV_ANON_MMAP + #define MD_ACCEPT_NB_NOT_INHERITED + + #define MD_SETJMP(env) setjmp(env) + #define MD_LONGJMP(env, val) longjmp(env, val) + + #if defined(sparc) || defined(__sparc) + #ifdef _LP64 + #define MD_STACK_PAD_SIZE 4095 + #endif + #define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + (void) MD_SETJMP((_thread)->context); \ + (_thread)->context[1] = (long) (_sp); \ + (_thread)->context[2] = (long) _main; \ + ST_END_MACRO + #elif defined(i386) || defined(__i386) + #define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + (void) MD_SETJMP((_thread)->context); \ + (_thread)->context[4] = (long) (_sp); \ + (_thread)->context[5] = (long) _main; \ + ST_END_MACRO + #elif defined(__amd64__) + #define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + (_thread)->context[6] = (long) (_sp); \ + ST_END_MACRO + #else + #error Unknown CPU architecture + #endif + + #define MD_GET_UTIME() \ + return (gethrtime() / 1000) #else -#error Unknown OS + #error Unknown OS #endif /* OS */ +/***************************************** + * Other defines + */ #if !defined(MD_HAVE_POLL) && !defined(MD_DONT_HAVE_POLL) -#define MD_HAVE_POLL + #define MD_HAVE_POLL #endif #ifndef MD_STACK_PAD_SIZE -#define MD_STACK_PAD_SIZE 128 + #define MD_STACK_PAD_SIZE 128 #endif #if !defined(MD_HAVE_SOCKLEN_T) && !defined(socklen_t) -#define socklen_t int + #define socklen_t int #endif #ifndef MD_CAP_STACK -#define MD_CAP_STACK(var_addr) + #define MD_CAP_STACK(var_addr) #endif #endif /* !__ST_MD_H__ */ From f8f18fffa75fc18fe1d29ceab1b8383c89aafbd3 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 6 Nov 2014 12:50:27 +0800 Subject: [PATCH 060/800] research st: update test for st. --- trunk/research/st/srs.c | 56 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/trunk/research/st/srs.c b/trunk/research/st/srs.c index 4fc25988fe..017caee9a7 100644 --- a/trunk/research/st/srs.c +++ b/trunk/research/st/srs.c @@ -147,6 +147,8 @@ void* io_client(void* arg) return NULL; } + st_netfd_close(stfd); + return NULL; } @@ -211,10 +213,59 @@ int io_test() } srs_trace("9. server io completed."); + st_netfd_close(stfd); + st_netfd_close(client_stfd); + srs_trace("io test: end"); return 0; } +int pipe_test() +{ + srs_trace("==================================================="); + srs_trace("pipe test: start"); + + int fds[2]; + if (pipe(fds) < 0) { + srs_trace("pipe failed"); + return -1; + } + srs_trace("1. pipe ok, %d=>%d", fds[1], fds[0]); + + st_netfd_t fdw; + if ((fdw = st_netfd_open_socket(fds[1])) == NULL) { + srs_trace("st_netfd_open_socket open socket failed."); + return -1; + } + srs_trace("2. open write fd ok"); + + st_netfd_t fdr; + if ((fdr = st_netfd_open_socket(fds[0])) == NULL) { + srs_trace("st_netfd_open_socket open socket failed."); + return -1; + } + srs_trace("3. open read fd ok"); + + char buf[1024]; + if (st_write(fdw, buf, sizeof(buf), ST_UTIME_NO_TIMEOUT) < 0) { + srs_trace("st_write socket failed."); + return -1; + } + srs_trace("4. write to pipe ok"); + + if (st_read(fdr, buf, sizeof(buf), ST_UTIME_NO_TIMEOUT) < 0) { + srs_trace("st_read socket failed."); + return -1; + } + srs_trace("5. read from pipe ok"); + + st_netfd_close(fdw); + st_netfd_close(fdr); + + srs_trace("pipe test: end"); + return 0; +} + int main(int argc, char** argv) { if (st_set_eventsys(ST_EVENTSYS_ALT) < 0) { @@ -237,6 +288,11 @@ int main(int argc, char** argv) return -1; } + if (pipe_test() < 0) { + srs_trace("pipe_test failed"); + return -1; + } + // cleanup. st_thread_exit(NULL); From fd0c85b324f63c5e0b21e0a5943db0336da283d9 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 6 Nov 2014 13:11:34 +0800 Subject: [PATCH 061/800] research st: refine io. --- trunk/research/st/io.c | 1162 +++++++++++++++++++++------------------- 1 file changed, 596 insertions(+), 566 deletions(-) diff --git a/trunk/research/st/io.c b/trunk/research/st/io.c index 19cb4a9e2d..866b5f7270 100644 --- a/trunk/research/st/io.c +++ b/trunk/research/st/io.c @@ -54,9 +54,9 @@ #if EAGAIN != EWOULDBLOCK -#define _IO_NOT_READY_ERROR ((errno == EAGAIN) || (errno == EWOULDBLOCK)) + #define _IO_NOT_READY_ERROR ((errno == EAGAIN) || (errno == EWOULDBLOCK)) #else -#define _IO_NOT_READY_ERROR (errno == EAGAIN) + #define _IO_NOT_READY_ERROR (errno == EAGAIN) #endif #define _LOCAL_MAXIOV 16 @@ -70,138 +70,147 @@ static void _st_netfd_free_aux_data(_st_netfd_t *fd); int _st_io_init(void) { - struct sigaction sigact; - struct rlimit rlim; - int fdlim; + struct sigaction sigact; + struct rlimit rlim; + int fdlim; - /* Ignore SIGPIPE */ - sigact.sa_handler = SIG_IGN; - sigemptyset(&sigact.sa_mask); - sigact.sa_flags = 0; - if (sigaction(SIGPIPE, &sigact, NULL) < 0) - return -1; - - /* Set maximum number of open file descriptors */ - if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) - return -1; + /* Ignore SIGPIPE */ + sigact.sa_handler = SIG_IGN; + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + if (sigaction(SIGPIPE, &sigact, NULL) < 0) { + return -1; + } - fdlim = (*_st_eventsys->fd_getlimit)(); - if (fdlim > 0 && rlim.rlim_max > (rlim_t) fdlim) { - rlim.rlim_max = fdlim; - } - rlim.rlim_cur = rlim.rlim_max; - if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) - return -1; - _st_osfd_limit = (int) rlim.rlim_max; + /* Set maximum number of open file descriptors */ + if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) { + return -1; + } - return 0; + fdlim = (*_st_eventsys->fd_getlimit)(); + if (fdlim > 0 && rlim.rlim_max > (rlim_t) fdlim) { + rlim.rlim_max = fdlim; + } + rlim.rlim_cur = rlim.rlim_max; + if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) { + return -1; + } + _st_osfd_limit = (int) rlim.rlim_max; + + return 0; } int st_getfdlimit(void) { - return _st_osfd_limit; + return _st_osfd_limit; } void st_netfd_free(_st_netfd_t *fd) { - if (!fd->inuse) - return; - - fd->inuse = 0; - if (fd->aux_data) - _st_netfd_free_aux_data(fd); - if (fd->private_data && fd->destructor) - (*(fd->destructor))(fd->private_data); - fd->private_data = NULL; - fd->destructor = NULL; - fd->next = _st_netfd_freelist; - _st_netfd_freelist = fd; + if (!fd->inuse) { + return; + } + + fd->inuse = 0; + if (fd->aux_data) { + _st_netfd_free_aux_data(fd); + } + if (fd->private_data && fd->destructor) { + (*(fd->destructor))(fd->private_data); + } + fd->private_data = NULL; + fd->destructor = NULL; + fd->next = _st_netfd_freelist; + _st_netfd_freelist = fd; } static _st_netfd_t *_st_netfd_new(int osfd, int nonblock, int is_socket) { - _st_netfd_t *fd; - int flags = 1; - - if ((*_st_eventsys->fd_new)(osfd) < 0) - return NULL; - - if (_st_netfd_freelist) { - fd = _st_netfd_freelist; - _st_netfd_freelist = _st_netfd_freelist->next; - } else { - fd = calloc(1, sizeof(_st_netfd_t)); - if (!fd) - return NULL; - } - - fd->osfd = osfd; - fd->inuse = 1; - fd->next = NULL; - - if (nonblock) { - /* Use just one system call */ - if (is_socket && ioctl(osfd, FIONBIO, &flags) != -1) - return fd; - /* Do it the Posix way */ - if ((flags = fcntl(osfd, F_GETFL, 0)) < 0 || - fcntl(osfd, F_SETFL, flags | O_NONBLOCK) < 0) { - st_netfd_free(fd); - return NULL; + _st_netfd_t *fd; + int flags = 1; + + if ((*_st_eventsys->fd_new)(osfd) < 0) { + return NULL; } - } - - return fd; + + if (_st_netfd_freelist) { + fd = _st_netfd_freelist; + _st_netfd_freelist = _st_netfd_freelist->next; + } else { + fd = calloc(1, sizeof(_st_netfd_t)); + if (!fd) { + return NULL; + } + } + + fd->osfd = osfd; + fd->inuse = 1; + fd->next = NULL; + + if (nonblock) { + /* Use just one system call */ + if (is_socket && ioctl(osfd, FIONBIO, &flags) != -1) { + return fd; + } + /* Do it the Posix way */ + if ((flags = fcntl(osfd, F_GETFL, 0)) < 0 || fcntl(osfd, F_SETFL, flags | O_NONBLOCK) < 0) { + st_netfd_free(fd); + return NULL; + } + } + + return fd; } _st_netfd_t *st_netfd_open(int osfd) { - return _st_netfd_new(osfd, 1, 0); + return _st_netfd_new(osfd, 1, 0); } _st_netfd_t *st_netfd_open_socket(int osfd) { - return _st_netfd_new(osfd, 1, 1); + return _st_netfd_new(osfd, 1, 1); } int st_netfd_close(_st_netfd_t *fd) { - if ((*_st_eventsys->fd_close)(fd->osfd) < 0) - return -1; + if ((*_st_eventsys->fd_close)(fd->osfd) < 0) { + return -1; + } - st_netfd_free(fd); - return close(fd->osfd); + st_netfd_free(fd); + return close(fd->osfd); } int st_netfd_fileno(_st_netfd_t *fd) { - return (fd->osfd); + return (fd->osfd); } -void st_netfd_setspecific(_st_netfd_t *fd, void *value, - _st_destructor_t destructor) +void st_netfd_setspecific(_st_netfd_t *fd, void *value, _st_destructor_t destructor) { - if (value != fd->private_data) { - /* Free up previously set non-NULL data value */ - if (fd->private_data && fd->destructor) - (*(fd->destructor))(fd->private_data); - } - fd->private_data = value; - fd->destructor = destructor; + if (value != fd->private_data) { + /* Free up previously set non-NULL data value */ + if (fd->private_data && fd->destructor) { + (*(fd->destructor))(fd->private_data); + } + } + fd->private_data = value; + fd->destructor = destructor; } void *st_netfd_getspecific(_st_netfd_t *fd) { - return (fd->private_data); + return (fd->private_data); } @@ -210,76 +219,78 @@ void *st_netfd_getspecific(_st_netfd_t *fd) */ int st_netfd_poll(_st_netfd_t *fd, int how, st_utime_t timeout) { - struct pollfd pd; - int n; - - pd.fd = fd->osfd; - pd.events = (short) how; - pd.revents = 0; - - if ((n = st_poll(&pd, 1, timeout)) < 0) - return -1; - if (n == 0) { - /* Timed out */ - errno = ETIME; - return -1; - } - if (pd.revents & POLLNVAL) { - errno = EBADF; - return -1; - } - - return 0; + struct pollfd pd; + int n; + + pd.fd = fd->osfd; + pd.events = (short) how; + pd.revents = 0; + + if ((n = st_poll(&pd, 1, timeout)) < 0) { + return -1; + } + if (n == 0) { + /* Timed out */ + errno = ETIME; + return -1; + } + if (pd.revents & POLLNVAL) { + errno = EBADF; + return -1; + } + + return 0; } - #ifdef MD_ALWAYS_UNSERIALIZED_ACCEPT /* No-op */ int st_netfd_serialize_accept(_st_netfd_t *fd) { - fd->aux_data = NULL; - return 0; + fd->aux_data = NULL; + return 0; } /* No-op */ static void _st_netfd_free_aux_data(_st_netfd_t *fd) { - fd->aux_data = NULL; -} - -_st_netfd_t *st_accept(_st_netfd_t *fd, struct sockaddr *addr, int *addrlen, - st_utime_t timeout) -{ - int osfd, err; - _st_netfd_t *newfd; - - while ((osfd = accept(fd->osfd, addr, (socklen_t *)addrlen)) < 0) { - if (errno == EINTR) - continue; - if (!_IO_NOT_READY_ERROR) - return NULL; - /* Wait until the socket becomes readable */ - if (st_netfd_poll(fd, POLLIN, timeout) < 0) - return NULL; - } - - /* On some platforms the new socket created by accept() inherits */ - /* the nonblocking attribute of the listening socket */ + fd->aux_data = NULL; +} + +_st_netfd_t *st_accept(_st_netfd_t *fd, struct sockaddr *addr, int *addrlen, st_utime_t timeout) +{ + int osfd, err; + _st_netfd_t *newfd; + + while ((osfd = accept(fd->osfd, addr, (socklen_t *)addrlen)) < 0) { + if (errno == EINTR) { + continue; + } + if (!_IO_NOT_READY_ERROR) { + return NULL; + } + /* Wait until the socket becomes readable */ + if (st_netfd_poll(fd, POLLIN, timeout) < 0) { + return NULL; + } + } + + /* On some platforms the new socket created by accept() inherits */ + /* the nonblocking attribute of the listening socket */ #if defined (MD_ACCEPT_NB_INHERITED) - newfd = _st_netfd_new(osfd, 0, 1); + newfd = _st_netfd_new(osfd, 0, 1); #elif defined (MD_ACCEPT_NB_NOT_INHERITED) - newfd = _st_netfd_new(osfd, 1, 1); + newfd = _st_netfd_new(osfd, 1, 1); #else -#error Unknown OS + #error Unknown OS #endif - - if (!newfd) { - err = errno; - close(osfd); - errno = err; - } - - return newfd; + + if (!newfd) { + err = errno; + close(osfd); + errno = err; + } + + return newfd; } #else /* MD_ALWAYS_UNSERIALIZED_ACCEPT */ @@ -291,488 +302,507 @@ _st_netfd_t *st_accept(_st_netfd_t *fd, struct sockaddr *addr, int *addrlen, */ int st_netfd_serialize_accept(_st_netfd_t *fd) { - _st_netfd_t **p; - int osfd[2], err; - - if (fd->aux_data) { - errno = EINVAL; - return -1; - } - if ((p = (_st_netfd_t **)calloc(2, sizeof(_st_netfd_t *))) == NULL) - return -1; - if (pipe(osfd) < 0) { + _st_netfd_t **p; + int osfd[2], err; + + if (fd->aux_data) { + errno = EINVAL; + return -1; + } + if ((p = (_st_netfd_t **)calloc(2, sizeof(_st_netfd_t *))) == NULL) { + return -1; + } + if (pipe(osfd) < 0) { + free(p); + return -1; + } + if ((p[0] = st_netfd_open(osfd[0])) != NULL && (p[1] = st_netfd_open(osfd[1])) != NULL && write(osfd[1], " ", 1) == 1) { + fd->aux_data = p; + return 0; + } + /* Error */ + err = errno; + if (p[0]) { + st_netfd_free(p[0]); + } + if (p[1]) { + st_netfd_free(p[1]); + } + close(osfd[0]); + close(osfd[1]); free(p); + errno = err; + return -1; - } - if ((p[0] = st_netfd_open(osfd[0])) != NULL && - (p[1] = st_netfd_open(osfd[1])) != NULL && - write(osfd[1], " ", 1) == 1) { - fd->aux_data = p; - return 0; - } - /* Error */ - err = errno; - if (p[0]) - st_netfd_free(p[0]); - if (p[1]) - st_netfd_free(p[1]); - close(osfd[0]); - close(osfd[1]); - free(p); - errno = err; - - return -1; } static void _st_netfd_free_aux_data(_st_netfd_t *fd) { - _st_netfd_t **p = (_st_netfd_t **) fd->aux_data; - - st_netfd_close(p[0]); - st_netfd_close(p[1]); - free(p); - fd->aux_data = NULL; -} - -_st_netfd_t *st_accept(_st_netfd_t *fd, struct sockaddr *addr, int *addrlen, - st_utime_t timeout) -{ - int osfd, err; - _st_netfd_t *newfd; - _st_netfd_t **p = (_st_netfd_t **) fd->aux_data; - ssize_t n; - char c; - - for ( ; ; ) { - if (p == NULL) { - osfd = accept(fd->osfd, addr, (socklen_t *)addrlen); - } else { - /* Get the lock */ - n = st_read(p[0], &c, 1, timeout); - if (n < 0) - return NULL; - ST_ASSERT(n == 1); - /* Got the lock */ - osfd = accept(fd->osfd, addr, (socklen_t *)addrlen); - /* Unlock */ - err = errno; - n = st_write(p[1], &c, 1, timeout); - ST_ASSERT(n == 1); - errno = err; - } - if (osfd >= 0) - break; - if (errno == EINTR) - continue; - if (!_IO_NOT_READY_ERROR) - return NULL; - /* Wait until the socket becomes readable */ - if (st_netfd_poll(fd, POLLIN, timeout) < 0) - return NULL; - } - - /* On some platforms the new socket created by accept() inherits */ - /* the nonblocking attribute of the listening socket */ + _st_netfd_t **p = (_st_netfd_t **) fd->aux_data; + + st_netfd_close(p[0]); + st_netfd_close(p[1]); + free(p); + fd->aux_data = NULL; +} + +_st_netfd_t *st_accept(_st_netfd_t *fd, struct sockaddr *addr, int *addrlen, st_utime_t timeout) +{ + int osfd, err; + _st_netfd_t *newfd; + _st_netfd_t **p = (_st_netfd_t **) fd->aux_data; + ssize_t n; + char c; + + for ( ; ; ) { + if (p == NULL) { + osfd = accept(fd->osfd, addr, (socklen_t *)addrlen); + } else { + /* Get the lock */ + n = st_read(p[0], &c, 1, timeout); + if (n < 0) { + return NULL; + } + ST_ASSERT(n == 1); + /* Got the lock */ + osfd = accept(fd->osfd, addr, (socklen_t *)addrlen); + /* Unlock */ + err = errno; + n = st_write(p[1], &c, 1, timeout); + ST_ASSERT(n == 1); + errno = err; + } + if (osfd >= 0) { + break; + } + if (errno == EINTR) { + continue; + } + if (!_IO_NOT_READY_ERROR) { + return NULL; + } + /* Wait until the socket becomes readable */ + if (st_netfd_poll(fd, POLLIN, timeout) < 0) { + return NULL; + } + } + + /* On some platforms the new socket created by accept() inherits */ + /* the nonblocking attribute of the listening socket */ #if defined (MD_ACCEPT_NB_INHERITED) - newfd = _st_netfd_new(osfd, 0, 1); + newfd = _st_netfd_new(osfd, 0, 1); #elif defined (MD_ACCEPT_NB_NOT_INHERITED) - newfd = _st_netfd_new(osfd, 1, 1); + newfd = _st_netfd_new(osfd, 1, 1); #else -#error Unknown OS + #error Unknown OS #endif - - if (!newfd) { - err = errno; - close(osfd); - errno = err; - } - - return newfd; + + if (!newfd) { + err = errno; + close(osfd); + errno = err; + } + + return newfd; } #endif /* MD_ALWAYS_UNSERIALIZED_ACCEPT */ -int st_connect(_st_netfd_t *fd, const struct sockaddr *addr, int addrlen, - st_utime_t timeout) -{ - int n, err = 0; - - while (connect(fd->osfd, addr, addrlen) < 0) { - if (errno != EINTR) { - /* - * On some platforms, if connect() is interrupted (errno == EINTR) - * after the kernel binds the socket, a subsequent connect() - * attempt will fail with errno == EADDRINUSE. Ignore EADDRINUSE - * iff connect() was previously interrupted. See Rich Stevens' - * "UNIX Network Programming," Vol. 1, 2nd edition, p. 413 - * ("Interrupted connect"). - */ - if (errno != EINPROGRESS && (errno != EADDRINUSE || err == 0)) - return -1; - /* Wait until the socket becomes writable */ - if (st_netfd_poll(fd, POLLOUT, timeout) < 0) - return -1; - /* Try to find out whether the connection setup succeeded or failed */ - n = sizeof(int); - if (getsockopt(fd->osfd, SOL_SOCKET, SO_ERROR, (char *)&err, - (socklen_t *)&n) < 0) - return -1; - if (err) { - errno = err; - return -1; - } - break; +int st_connect(_st_netfd_t *fd, const struct sockaddr *addr, int addrlen, st_utime_t timeout) +{ + int n, err = 0; + + while (connect(fd->osfd, addr, addrlen) < 0) { + if (errno != EINTR) { + /* + * On some platforms, if connect() is interrupted (errno == EINTR) + * after the kernel binds the socket, a subsequent connect() + * attempt will fail with errno == EADDRINUSE. Ignore EADDRINUSE + * iff connect() was previously interrupted. See Rich Stevens' + * "UNIX Network Programming," Vol. 1, 2nd edition, p. 413 + * ("Interrupted connect"). + */ + if (errno != EINPROGRESS && (errno != EADDRINUSE || err == 0)) { + return -1; + } + /* Wait until the socket becomes writable */ + if (st_netfd_poll(fd, POLLOUT, timeout) < 0) { + return -1; + } + /* Try to find out whether the connection setup succeeded or failed */ + n = sizeof(int); + if (getsockopt(fd->osfd, SOL_SOCKET, SO_ERROR, (char *)&err, (socklen_t *)&n) < 0) { + return -1; + } + if (err) { + errno = err; + return -1; + } + break; + } + err = 1; } - err = 1; - } - - return 0; + + return 0; } ssize_t st_read(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout) { - ssize_t n; - - while ((n = read(fd->osfd, buf, nbyte)) < 0) { - if (errno == EINTR) - continue; - if (!_IO_NOT_READY_ERROR) - return -1; - /* Wait until the socket becomes readable */ - if (st_netfd_poll(fd, POLLIN, timeout) < 0) - return -1; - } - - return n; + ssize_t n; + + while ((n = read(fd->osfd, buf, nbyte)) < 0) { + if (errno == EINTR) { + continue; + } + if (!_IO_NOT_READY_ERROR) { + return -1; + } + /* Wait until the socket becomes readable */ + if (st_netfd_poll(fd, POLLIN, timeout) < 0) { + return -1; + } + } + + return n; } -int st_read_resid(_st_netfd_t *fd, void *buf, size_t *resid, - st_utime_t timeout) +int st_read_resid(_st_netfd_t *fd, void *buf, size_t *resid, st_utime_t timeout) { - struct iovec iov, *riov; - int riov_size, rv; - - iov.iov_base = buf; - iov.iov_len = *resid; - riov = &iov; - riov_size = 1; - rv = st_readv_resid(fd, &riov, &riov_size, timeout); - *resid = iov.iov_len; - return rv; + struct iovec iov, *riov; + int riov_size, rv; + + iov.iov_base = buf; + iov.iov_len = *resid; + riov = &iov; + riov_size = 1; + rv = st_readv_resid(fd, &riov, &riov_size, timeout); + *resid = iov.iov_len; + return rv; } -ssize_t st_readv(_st_netfd_t *fd, const struct iovec *iov, int iov_size, - st_utime_t timeout) -{ - ssize_t n; - - while ((n = readv(fd->osfd, iov, iov_size)) < 0) { - if (errno == EINTR) - continue; - if (!_IO_NOT_READY_ERROR) - return -1; - /* Wait until the socket becomes readable */ - if (st_netfd_poll(fd, POLLIN, timeout) < 0) - return -1; - } - - return n; -} - -int st_readv_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, - st_utime_t timeout) +ssize_t st_readv(_st_netfd_t *fd, const struct iovec *iov, int iov_size, st_utime_t timeout) { - ssize_t n; - - while (*iov_size > 0) { - if (*iov_size == 1) - n = read(fd->osfd, (*iov)->iov_base, (*iov)->iov_len); - else - n = readv(fd->osfd, *iov, *iov_size); - if (n < 0) { - if (errno == EINTR) - continue; - if (!_IO_NOT_READY_ERROR) - return -1; - } else if (n == 0) - break; - else { - while ((size_t) n >= (*iov)->iov_len) { - n -= (*iov)->iov_len; - (*iov)->iov_base = (char *) (*iov)->iov_base + (*iov)->iov_len; - (*iov)->iov_len = 0; - (*iov)++; - (*iov_size)--; - if (n == 0) - break; - } - if (*iov_size == 0) - break; - (*iov)->iov_base = (char *) (*iov)->iov_base + n; - (*iov)->iov_len -= n; + ssize_t n; + + while ((n = readv(fd->osfd, iov, iov_size)) < 0) { + if (errno == EINTR) { + continue; + } + if (!_IO_NOT_READY_ERROR) { + return -1; + } + /* Wait until the socket becomes readable */ + if (st_netfd_poll(fd, POLLIN, timeout) < 0) { + return -1; + } } - /* Wait until the socket becomes readable */ - if (st_netfd_poll(fd, POLLIN, timeout) < 0) - return -1; - } - - return 0; + + return n; +} + +int st_readv_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, st_utime_t timeout) +{ + ssize_t n; + + while (*iov_size > 0) { + if (*iov_size == 1) { + n = read(fd->osfd, (*iov)->iov_base, (*iov)->iov_len); + } else { + n = readv(fd->osfd, *iov, *iov_size); + } + if (n < 0) { + if (errno == EINTR) { + continue; + } + if (!_IO_NOT_READY_ERROR) { + return -1; + } + } else if (n == 0) { + break; + } else { + while ((size_t) n >= (*iov)->iov_len) { + n -= (*iov)->iov_len; + (*iov)->iov_base = (char *) (*iov)->iov_base + (*iov)->iov_len; + (*iov)->iov_len = 0; + (*iov)++; + (*iov_size)--; + if (n == 0) { + break; + } + } + if (*iov_size == 0) { + break; + } + (*iov)->iov_base = (char *) (*iov)->iov_base + n; + (*iov)->iov_len -= n; + } + /* Wait until the socket becomes readable */ + if (st_netfd_poll(fd, POLLIN, timeout) < 0) { + return -1; + } + } + + return 0; } - -ssize_t st_read_fully(_st_netfd_t *fd, void *buf, size_t nbyte, - st_utime_t timeout) +ssize_t st_read_fully(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout) { - size_t resid = nbyte; - return st_read_resid(fd, buf, &resid, timeout) == 0 ? - (ssize_t) (nbyte - resid) : -1; + size_t resid = nbyte; + return st_read_resid(fd, buf, &resid, timeout) == 0 ? (ssize_t) (nbyte - resid) : -1; } - -int st_write_resid(_st_netfd_t *fd, const void *buf, size_t *resid, - st_utime_t timeout) +int st_write_resid(_st_netfd_t *fd, const void *buf, size_t *resid, st_utime_t timeout) { - struct iovec iov, *riov; - int riov_size, rv; - - iov.iov_base = (void *) buf; /* we promise not to modify buf */ - iov.iov_len = *resid; - riov = &iov; - riov_size = 1; - rv = st_writev_resid(fd, &riov, &riov_size, timeout); - *resid = iov.iov_len; - return rv; + struct iovec iov, *riov; + int riov_size, rv; + + iov.iov_base = (void *) buf; /* we promise not to modify buf */ + iov.iov_len = *resid; + riov = &iov; + riov_size = 1; + rv = st_writev_resid(fd, &riov, &riov_size, timeout); + *resid = iov.iov_len; + return rv; } - -ssize_t st_write(_st_netfd_t *fd, const void *buf, size_t nbyte, - st_utime_t timeout) +ssize_t st_write(_st_netfd_t *fd, const void *buf, size_t nbyte, st_utime_t timeout) { - size_t resid = nbyte; - return st_write_resid(fd, buf, &resid, timeout) == 0 ? - (ssize_t) (nbyte - resid) : -1; + size_t resid = nbyte; + return st_write_resid(fd, buf, &resid, timeout) == 0 ? (ssize_t) (nbyte - resid) : -1; } - -ssize_t st_writev(_st_netfd_t *fd, const struct iovec *iov, int iov_size, - st_utime_t timeout) +ssize_t st_writev(_st_netfd_t *fd, const struct iovec *iov, int iov_size, st_utime_t timeout) { - ssize_t n, rv; - size_t nleft, nbyte; - int index, iov_cnt; - struct iovec *tmp_iov; - struct iovec local_iov[_LOCAL_MAXIOV]; - - /* Calculate the total number of bytes to be sent */ - nbyte = 0; - for (index = 0; index < iov_size; index++) - nbyte += iov[index].iov_len; - - rv = (ssize_t)nbyte; - nleft = nbyte; - tmp_iov = (struct iovec *) iov; /* we promise not to modify iov */ - iov_cnt = iov_size; - - while (nleft > 0) { - if (iov_cnt == 1) { - if (st_write(fd, tmp_iov[0].iov_base, nleft, timeout) != (ssize_t) nleft) - rv = -1; - break; + ssize_t n, rv; + size_t nleft, nbyte; + int index, iov_cnt; + struct iovec *tmp_iov; + struct iovec local_iov[_LOCAL_MAXIOV]; + + /* Calculate the total number of bytes to be sent */ + nbyte = 0; + for (index = 0; index < iov_size; index++) { + nbyte += iov[index].iov_len; } - if ((n = writev(fd->osfd, tmp_iov, iov_cnt)) < 0) { - if (errno == EINTR) - continue; - if (!_IO_NOT_READY_ERROR) { - rv = -1; - break; - } - } else { - if ((size_t) n == nleft) - break; - nleft -= n; - /* Find the next unwritten vector */ - n = (ssize_t)(nbyte - nleft); - for (index = 0; (size_t) n >= iov[index].iov_len; index++) - n -= iov[index].iov_len; - - if (tmp_iov == iov) { - /* Must copy iov's around */ - if (iov_size - index <= _LOCAL_MAXIOV) { - tmp_iov = local_iov; - } else { - tmp_iov = calloc(1, (iov_size - index) * sizeof(struct iovec)); - if (tmp_iov == NULL) - return -1; - } - } - - /* Fill in the first partial read */ - tmp_iov[0].iov_base = &(((char *)iov[index].iov_base)[n]); - tmp_iov[0].iov_len = iov[index].iov_len - n; - index++; - /* Copy the remaining vectors */ - for (iov_cnt = 1; index < iov_size; iov_cnt++, index++) { - tmp_iov[iov_cnt].iov_base = iov[index].iov_base; - tmp_iov[iov_cnt].iov_len = iov[index].iov_len; - } + + rv = (ssize_t)nbyte; + nleft = nbyte; + tmp_iov = (struct iovec *) iov; /* we promise not to modify iov */ + iov_cnt = iov_size; + + while (nleft > 0) { + if (iov_cnt == 1) { + if (st_write(fd, tmp_iov[0].iov_base, nleft, timeout) != (ssize_t) nleft) { + rv = -1; + } + break; + } + if ((n = writev(fd->osfd, tmp_iov, iov_cnt)) < 0) { + if (errno == EINTR) { + continue; + } + if (!_IO_NOT_READY_ERROR) { + rv = -1; + break; + } + } else { + if ((size_t) n == nleft) { + break; + } + nleft -= n; + /* Find the next unwritten vector */ + n = (ssize_t)(nbyte - nleft); + for (index = 0; (size_t) n >= iov[index].iov_len; index++) { + n -= iov[index].iov_len; + } + + if (tmp_iov == iov) { + /* Must copy iov's around */ + if (iov_size - index <= _LOCAL_MAXIOV) { + tmp_iov = local_iov; + } else { + tmp_iov = calloc(1, (iov_size - index) * sizeof(struct iovec)); + if (tmp_iov == NULL) { + return -1; + } + } + } + + /* Fill in the first partial read */ + tmp_iov[0].iov_base = &(((char *)iov[index].iov_base)[n]); + tmp_iov[0].iov_len = iov[index].iov_len - n; + index++; + /* Copy the remaining vectors */ + for (iov_cnt = 1; index < iov_size; iov_cnt++, index++) { + tmp_iov[iov_cnt].iov_base = iov[index].iov_base; + tmp_iov[iov_cnt].iov_len = iov[index].iov_len; + } + } + /* Wait until the socket becomes writable */ + if (st_netfd_poll(fd, POLLOUT, timeout) < 0) { + rv = -1; + break; + } } - /* Wait until the socket becomes writable */ - if (st_netfd_poll(fd, POLLOUT, timeout) < 0) { - rv = -1; - break; + + if (tmp_iov != iov && tmp_iov != local_iov) { + free(tmp_iov); } - } - - if (tmp_iov != iov && tmp_iov != local_iov) - free(tmp_iov); - - return rv; -} - - -int st_writev_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, - st_utime_t timeout) -{ - ssize_t n; - - while (*iov_size > 0) { - if (*iov_size == 1) - n = write(fd->osfd, (*iov)->iov_base, (*iov)->iov_len); - else - n = writev(fd->osfd, *iov, *iov_size); - if (n < 0) { - if (errno == EINTR) - continue; - if (!_IO_NOT_READY_ERROR) - return -1; - } else { - while ((size_t) n >= (*iov)->iov_len) { - n -= (*iov)->iov_len; - (*iov)->iov_base = (char *) (*iov)->iov_base + (*iov)->iov_len; - (*iov)->iov_len = 0; - (*iov)++; - (*iov_size)--; - if (n == 0) - break; - } - if (*iov_size == 0) - break; - (*iov)->iov_base = (char *) (*iov)->iov_base + n; - (*iov)->iov_len -= n; + + return rv; +} + +int st_writev_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, st_utime_t timeout) +{ + ssize_t n; + + while (*iov_size > 0) { + if (*iov_size == 1) { + n = write(fd->osfd, (*iov)->iov_base, (*iov)->iov_len); + } else { + n = writev(fd->osfd, *iov, *iov_size); + } + if (n < 0) { + if (errno == EINTR) { + continue; + } + if (!_IO_NOT_READY_ERROR) { + return -1; + } + } else { + while ((size_t) n >= (*iov)->iov_len) { + n -= (*iov)->iov_len; + (*iov)->iov_base = (char *) (*iov)->iov_base + (*iov)->iov_len; + (*iov)->iov_len = 0; + (*iov)++; + (*iov_size)--; + if (n == 0) { + break; + } + } + if (*iov_size == 0) { + break; + } + (*iov)->iov_base = (char *) (*iov)->iov_base + n; + (*iov)->iov_len -= n; + } + /* Wait until the socket becomes writable */ + if (st_netfd_poll(fd, POLLOUT, timeout) < 0) { + return -1; + } } - /* Wait until the socket becomes writable */ - if (st_netfd_poll(fd, POLLOUT, timeout) < 0) - return -1; - } - - return 0; + + return 0; } - /* * Simple I/O functions for UDP. */ -int st_recvfrom(_st_netfd_t *fd, void *buf, int len, struct sockaddr *from, - int *fromlen, st_utime_t timeout) -{ - int n; - - while ((n = recvfrom(fd->osfd, buf, len, 0, from, (socklen_t *)fromlen)) - < 0) { - if (errno == EINTR) - continue; - if (!_IO_NOT_READY_ERROR) - return -1; - /* Wait until the socket becomes readable */ - if (st_netfd_poll(fd, POLLIN, timeout) < 0) - return -1; - } - - return n; -} - - -int st_sendto(_st_netfd_t *fd, const void *msg, int len, - const struct sockaddr *to, int tolen, st_utime_t timeout) -{ - int n; - - while ((n = sendto(fd->osfd, msg, len, 0, to, tolen)) < 0) { - if (errno == EINTR) - continue; - if (!_IO_NOT_READY_ERROR) - return -1; - /* Wait until the socket becomes writable */ - if (st_netfd_poll(fd, POLLOUT, timeout) < 0) - return -1; - } - - return n; -} - - -int st_recvmsg(_st_netfd_t *fd, struct msghdr *msg, int flags, - st_utime_t timeout) -{ - int n; - - while ((n = recvmsg(fd->osfd, msg, flags)) < 0) { - if (errno == EINTR) - continue; - if (!_IO_NOT_READY_ERROR) - return -1; - /* Wait until the socket becomes readable */ - if (st_netfd_poll(fd, POLLIN, timeout) < 0) - return -1; - } - - return n; -} - - -int st_sendmsg(_st_netfd_t *fd, const struct msghdr *msg, int flags, - st_utime_t timeout) -{ - int n; - - while ((n = sendmsg(fd->osfd, msg, flags)) < 0) { - if (errno == EINTR) - continue; - if (!_IO_NOT_READY_ERROR) - return -1; - /* Wait until the socket becomes writable */ - if (st_netfd_poll(fd, POLLOUT, timeout) < 0) - return -1; - } - - return n; +int st_recvfrom(_st_netfd_t *fd, void *buf, int len, struct sockaddr *from, int *fromlen, st_utime_t timeout) +{ + int n; + + while ((n = recvfrom(fd->osfd, buf, len, 0, from, (socklen_t *)fromlen)) < 0) { + if (errno == EINTR) { + continue; + } + if (!_IO_NOT_READY_ERROR) { + return -1; + } + /* Wait until the socket becomes readable */ + if (st_netfd_poll(fd, POLLIN, timeout) < 0) { + return -1; + } + } + + return n; +} + +int st_sendto(_st_netfd_t *fd, const void *msg, int len, const struct sockaddr *to, int tolen, st_utime_t timeout) +{ + int n; + + while ((n = sendto(fd->osfd, msg, len, 0, to, tolen)) < 0) { + if (errno == EINTR) { + continue; + } + if (!_IO_NOT_READY_ERROR) { + return -1; + } + /* Wait until the socket becomes writable */ + if (st_netfd_poll(fd, POLLOUT, timeout) < 0) { + return -1; + } + } + + return n; +} + + +int st_recvmsg(_st_netfd_t *fd, struct msghdr *msg, int flags, st_utime_t timeout) +{ + int n; + + while ((n = recvmsg(fd->osfd, msg, flags)) < 0) { + if (errno == EINTR) { + continue; + } + if (!_IO_NOT_READY_ERROR) { + return -1; + } + /* Wait until the socket becomes readable */ + if (st_netfd_poll(fd, POLLIN, timeout) < 0) { + return -1; + } + } + + return n; +} + +int st_sendmsg(_st_netfd_t *fd, const struct msghdr *msg, int flags, st_utime_t timeout) +{ + int n; + + while ((n = sendmsg(fd->osfd, msg, flags)) < 0) { + if (errno == EINTR) { + continue; + } + if (!_IO_NOT_READY_ERROR) { + return -1; + } + /* Wait until the socket becomes writable */ + if (st_netfd_poll(fd, POLLOUT, timeout) < 0) { + return -1; + } + } + + return n; } - - /* * To open FIFOs or other special files. */ _st_netfd_t *st_open(const char *path, int oflags, mode_t mode) { - int osfd, err; - _st_netfd_t *newfd; - - while ((osfd = open(path, oflags | O_NONBLOCK, mode)) < 0) { - if (errno != EINTR) - return NULL; - } - - newfd = _st_netfd_new(osfd, 0, 0); - if (!newfd) { - err = errno; - close(osfd); - errno = err; - } - - return newfd; + int osfd, err; + _st_netfd_t *newfd; + + while ((osfd = open(path, oflags | O_NONBLOCK, mode)) < 0) { + if (errno != EINTR) { + return NULL; + } + } + + newfd = _st_netfd_new(osfd, 0, 0); + if (!newfd) { + err = errno; + close(osfd); + errno = err; + } + + return newfd; } From c38a54578040cebe22ab5b666e923d2f44c67c44 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 6 Nov 2014 13:17:54 +0800 Subject: [PATCH 062/800] reserach st: refine key.c --- trunk/research/st/key.c | 73 +++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/trunk/research/st/key.c b/trunk/research/st/key.c index 9708c355d8..b1b8f986c4 100644 --- a/trunk/research/st/key.c +++ b/trunk/research/st/key.c @@ -56,51 +56,52 @@ static int key_max = 0; */ int st_key_create(int *keyp, _st_destructor_t destructor) { - if (key_max >= ST_KEYS_MAX) { - errno = EAGAIN; - return -1; - } - - *keyp = key_max++; - _st_destructors[*keyp] = destructor; - - return 0; + if (key_max >= ST_KEYS_MAX) { + errno = EAGAIN; + return -1; + } + + *keyp = key_max++; + _st_destructors[*keyp] = destructor; + + return 0; } int st_key_getlimit(void) { - return ST_KEYS_MAX; + return ST_KEYS_MAX; } int st_thread_setspecific(int key, void *value) { - _st_thread_t *me = _ST_CURRENT_THREAD(); - - if (key < 0 || key >= key_max) { - errno = EINVAL; - return -1; - } - - if (value != me->private_data[key]) { - /* free up previously set non-NULL data value */ - if (me->private_data[key] && _st_destructors[key]) { - (*_st_destructors[key])(me->private_data[key]); + _st_thread_t *me = _ST_CURRENT_THREAD(); + + if (key < 0 || key >= key_max) { + errno = EINVAL; + return -1; } - me->private_data[key] = value; - } - - return 0; + + if (value != me->private_data[key]) { + /* free up previously set non-NULL data value */ + if (me->private_data[key] && _st_destructors[key]) { + (*_st_destructors[key])(me->private_data[key]); + } + me->private_data[key] = value; + } + + return 0; } void *st_thread_getspecific(int key) { - if (key < 0 || key >= key_max) - return NULL; - - return ((_ST_CURRENT_THREAD())->private_data[key]); + if (key < 0 || key >= key_max) { + return NULL; + } + + return ((_ST_CURRENT_THREAD())->private_data[key]); } @@ -109,13 +110,13 @@ void *st_thread_getspecific(int key) */ void _st_thread_cleanup(_st_thread_t *thread) { - int key; - - for (key = 0; key < key_max; key++) { - if (thread->private_data[key] && _st_destructors[key]) { - (*_st_destructors[key])(thread->private_data[key]); - thread->private_data[key] = NULL; + int key; + + for (key = 0; key < key_max; key++) { + if (thread->private_data[key] && _st_destructors[key]) { + (*_st_destructors[key])(thread->private_data[key]); + thread->private_data[key] = NULL; + } } - } } From f5f8e8946f506f9584626be6d48dd1b728077717 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 6 Nov 2014 13:40:24 +0800 Subject: [PATCH 063/800] research st: refine event, io and key. --- trunk/research/st/event.c | 262 +++++++++++++++++++------------------- trunk/research/st/io.c | 16 --- trunk/research/st/key.c | 6 - 3 files changed, 129 insertions(+), 155 deletions(-) diff --git a/trunk/research/st/event.c b/trunk/research/st/event.c index cb14aed5a0..16f5fefb7b 100644 --- a/trunk/research/st/event.c +++ b/trunk/research/st/event.c @@ -152,7 +152,6 @@ static struct _st_epolldata { _st_eventsys_t *_st_eventsys = NULL; - /***************************************** * select event system */ @@ -160,8 +159,9 @@ _st_eventsys_t *_st_eventsys = NULL; ST_HIDDEN int _st_select_init(void) { _st_select_data = (struct _st_seldata *) malloc(sizeof(*_st_select_data)); - if (!_st_select_data) + if (!_st_select_data) { return -1; + } memset(_st_select_data, 0, sizeof(*_st_select_data)); _st_select_data->maxfd = -1; @@ -176,8 +176,7 @@ ST_HIDDEN int _st_select_pollset_add(struct pollfd *pds, int npds) /* Do checks up front */ for (pd = pds; pd < epd; pd++) { - if (pd->fd < 0 || pd->fd >= FD_SETSIZE || !pd->events || - (pd->events & ~(POLLIN | POLLOUT | POLLPRI))) { + if (pd->fd < 0 || pd->fd >= FD_SETSIZE || !pd->events || (pd->events & ~(POLLIN | POLLOUT | POLLPRI))) { errno = EINVAL; return -1; } @@ -210,16 +209,19 @@ ST_HIDDEN void _st_select_pollset_del(struct pollfd *pds, int npds) for (pd = pds; pd < epd; pd++) { if (pd->events & POLLIN) { - if (--_ST_SELECT_READ_CNT(pd->fd) == 0) + if (--_ST_SELECT_READ_CNT(pd->fd) == 0) { FD_CLR(pd->fd, &_ST_SELECT_READ_SET); + } } if (pd->events & POLLOUT) { - if (--_ST_SELECT_WRITE_CNT(pd->fd) == 0) + if (--_ST_SELECT_WRITE_CNT(pd->fd) == 0) { FD_CLR(pd->fd, &_ST_SELECT_WRITE_SET); + } } if (pd->events & POLLPRI) { - if (--_ST_SELECT_EXCEP_CNT(pd->fd) == 0) + if (--_ST_SELECT_EXCEP_CNT(pd->fd) == 0) { FD_CLR(pd->fd, &_ST_SELECT_EXCEP_SET); + } } } } @@ -244,8 +246,9 @@ ST_HIDDEN void _st_select_find_bad_fd(void) for (pds = pq->pds; pds < epds; pds++) { osfd = pds->fd; pds->revents = 0; - if (pds->events == 0) + if (pds->events == 0) { continue; + } if (fcntl(osfd, F_GETFL, 0) < 0) { pds->revents = POLLNVAL; notify = 1; @@ -282,13 +285,15 @@ ST_HIDDEN void _st_select_find_bad_fd(void) } } - if (pq->thread->flags & _ST_FL_ON_SLEEPQ) + if (pq->thread->flags & _ST_FL_ON_SLEEPQ) { _ST_DEL_SLEEPQ(pq->thread); + } pq->thread->state = _ST_ST_RUNNABLE; _ST_ADD_RUNQ(pq->thread); } else { - if (_ST_SELECT_MAX_OSFD < pq_max_osfd) + if (_ST_SELECT_MAX_OSFD < pq_max_osfd) { _ST_SELECT_MAX_OSFD = pq_max_osfd; + } } } } @@ -320,8 +325,7 @@ ST_HIDDEN void _st_select_dispatch(void) if (_ST_SLEEPQ == NULL) { tvp = NULL; } else { - min_timeout = (_ST_SLEEPQ->due <= _ST_LAST_CLOCK) ? 0 : - (_ST_SLEEPQ->due - _ST_LAST_CLOCK); + min_timeout = (_ST_SLEEPQ->due <= _ST_LAST_CLOCK) ? 0 : (_ST_SLEEPQ->due - _ST_LAST_CLOCK); timeout.tv_sec = (int) (min_timeout / 1000000); timeout.tv_usec = (int) (min_timeout % 1000000); tvp = &timeout; @@ -387,13 +391,15 @@ ST_HIDDEN void _st_select_dispatch(void) } } - if (pq->thread->flags & _ST_FL_ON_SLEEPQ) + if (pq->thread->flags & _ST_FL_ON_SLEEPQ) { _ST_DEL_SLEEPQ(pq->thread); + } pq->thread->state = _ST_ST_RUNNABLE; _ST_ADD_RUNQ(pq->thread); } else { - if (_ST_SELECT_MAX_OSFD < pq_max_osfd) + if (_ST_SELECT_MAX_OSFD < pq_max_osfd) { _ST_SELECT_MAX_OSFD = pq_max_osfd; + } } } } else if (nfd < 0) { @@ -401,8 +407,9 @@ ST_HIDDEN void _st_select_dispatch(void) * It can happen when a thread closes file descriptor * that is being used by some other thread -- BAD! */ - if (errno == EBADF) + if (errno == EBADF) { _st_select_find_bad_fd(); + } } } @@ -418,8 +425,7 @@ ST_HIDDEN int _st_select_fd_new(int osfd) ST_HIDDEN int _st_select_fd_close(int osfd) { - if (_ST_SELECT_READ_CNT(osfd) || _ST_SELECT_WRITE_CNT(osfd) || - _ST_SELECT_EXCEP_CNT(osfd)) { + if (_ST_SELECT_READ_CNT(osfd) || _ST_SELECT_WRITE_CNT(osfd) || _ST_SELECT_EXCEP_CNT(osfd)) { errno = EBUSY; return -1; } @@ -444,7 +450,6 @@ static _st_eventsys_t _st_select_eventsys = { _st_select_fd_getlimit }; - #ifdef MD_HAVE_POLL /***************************************** * poll event system @@ -453,11 +458,11 @@ static _st_eventsys_t _st_select_eventsys = { ST_HIDDEN int _st_poll_init(void) { _st_poll_data = (struct _st_polldata *) malloc(sizeof(*_st_poll_data)); - if (!_st_poll_data) + if (!_st_poll_data) { return -1; + } - _ST_POLLFDS = (struct pollfd *) malloc(ST_MIN_POLLFDS_SIZE * - sizeof(struct pollfd)); + _ST_POLLFDS = (struct pollfd *) malloc(ST_MIN_POLLFDS_SIZE * sizeof(struct pollfd)); if (!_ST_POLLFDS) { free(_st_poll_data); _st_poll_data = NULL; @@ -508,8 +513,7 @@ ST_HIDDEN void _st_poll_dispatch(void) ST_ASSERT(_ST_POLL_OSFD_CNT >= 0); if (_ST_POLL_OSFD_CNT > _ST_POLLFDS_SIZE) { free(_ST_POLLFDS); - _ST_POLLFDS = (struct pollfd *) malloc((_ST_POLL_OSFD_CNT + 10) * - sizeof(struct pollfd)); + _ST_POLLFDS = (struct pollfd *) malloc((_ST_POLL_OSFD_CNT + 10) * sizeof(struct pollfd)); ST_ASSERT(_ST_POLLFDS != NULL); _ST_POLLFDS_SIZE = _ST_POLL_OSFD_CNT + 10; } @@ -526,8 +530,7 @@ ST_HIDDEN void _st_poll_dispatch(void) if (_ST_SLEEPQ == NULL) { timeout = -1; } else { - min_timeout = (_ST_SLEEPQ->due <= _ST_LAST_CLOCK) ? 0 : - (_ST_SLEEPQ->due - _ST_LAST_CLOCK); + min_timeout = (_ST_SLEEPQ->due <= _ST_LAST_CLOCK) ? 0 : (_ST_SLEEPQ->due - _ST_LAST_CLOCK); timeout = (int) (min_timeout / 1000); } @@ -541,16 +544,18 @@ ST_HIDDEN void _st_poll_dispatch(void) pq = _ST_POLLQUEUE_PTR(q); epds = pollfds + pq->npds; for (pds = pollfds; pds < epds; pds++) { - if (pds->revents) + if (pds->revents) { break; + } } if (pds < epds) { memcpy(pq->pds, pollfds, sizeof(struct pollfd) * pq->npds); ST_REMOVE_LINK(&pq->links); pq->on_ioq = 0; - if (pq->thread->flags & _ST_FL_ON_SLEEPQ) + if (pq->thread->flags & _ST_FL_ON_SLEEPQ) { _ST_DEL_SLEEPQ(pq->thread); + } pq->thread->state = _ST_ST_RUNNABLE; _ST_ADD_RUNQ(pq->thread); @@ -609,8 +614,9 @@ ST_HIDDEN int _st_kq_init(void) int rv = 0; _st_kq_data = (struct _st_kqdata *) calloc(1, sizeof(*_st_kq_data)); - if (!_st_kq_data) + if (!_st_kq_data) { return -1; + } if ((_st_kq_data->kq = kqueue()) < 0) { err = errno; @@ -625,8 +631,7 @@ ST_HIDDEN int _st_kq_init(void) * FD_SETSIZE looks like good initial size. */ _st_kq_data->fd_data_size = FD_SETSIZE; - _st_kq_data->fd_data = (_kq_fd_data_t *)calloc(_st_kq_data->fd_data_size, - sizeof(_kq_fd_data_t)); + _st_kq_data->fd_data = (_kq_fd_data_t *)calloc(_st_kq_data->fd_data_size, sizeof(_kq_fd_data_t)); if (!_st_kq_data->fd_data) { err = errno; rv = -1; @@ -635,14 +640,11 @@ ST_HIDDEN int _st_kq_init(void) /* Allocate event lists */ _st_kq_data->evtlist_size = ST_KQ_MIN_EVTLIST_SIZE; - _st_kq_data->evtlist = (struct kevent *)malloc(_st_kq_data->evtlist_size * - sizeof(struct kevent)); + _st_kq_data->evtlist = (struct kevent *)malloc(_st_kq_data->evtlist_size * sizeof(struct kevent)); _st_kq_data->addlist_size = ST_KQ_MIN_EVTLIST_SIZE; - _st_kq_data->addlist = (struct kevent *)malloc(_st_kq_data->addlist_size * - sizeof(struct kevent)); + _st_kq_data->addlist = (struct kevent *)malloc(_st_kq_data->addlist_size * sizeof(struct kevent)); _st_kq_data->dellist_size = ST_KQ_MIN_EVTLIST_SIZE; - _st_kq_data->dellist = (struct kevent *)malloc(_st_kq_data->dellist_size * - sizeof(struct kevent)); + _st_kq_data->dellist = (struct kevent *)malloc(_st_kq_data->dellist_size * sizeof(struct kevent)); if (!_st_kq_data->evtlist || !_st_kq_data->addlist || !_st_kq_data->dellist) { err = ENOMEM; @@ -651,8 +653,9 @@ ST_HIDDEN int _st_kq_init(void) cleanup_kq: if (rv < 0) { - if (_st_kq_data->kq >= 0) + if (_st_kq_data->kq >= 0) { close(_st_kq_data->kq); + } free(_st_kq_data->fd_data); free(_st_kq_data->evtlist); free(_st_kq_data->addlist); @@ -670,16 +673,16 @@ ST_HIDDEN int _st_kq_fd_data_expand(int maxfd) _kq_fd_data_t *ptr; int n = _st_kq_data->fd_data_size; - while (maxfd >= n) + while (maxfd >= n) { n <<= 1; + } - ptr = (_kq_fd_data_t *)realloc(_st_kq_data->fd_data, - n * sizeof(_kq_fd_data_t)); - if (!ptr) + ptr = (_kq_fd_data_t *)realloc(_st_kq_data->fd_data, n * sizeof(_kq_fd_data_t)); + if (!ptr) { return -1; + } - memset(ptr + _st_kq_data->fd_data_size, 0, - (n - _st_kq_data->fd_data_size) * sizeof(_kq_fd_data_t)); + memset(ptr + _st_kq_data->fd_data_size, 0, (n - _st_kq_data->fd_data_size) * sizeof(_kq_fd_data_t)); _st_kq_data->fd_data = ptr; _st_kq_data->fd_data_size = n; @@ -692,13 +695,14 @@ ST_HIDDEN int _st_kq_addlist_expand(int avail) struct kevent *ptr; int n = _st_kq_data->addlist_size; - while (avail > n - _st_kq_data->addlist_cnt) + while (avail > n - _st_kq_data->addlist_cnt) { n <<= 1; + } - ptr = (struct kevent *)realloc(_st_kq_data->addlist, - n * sizeof(struct kevent)); - if (!ptr) + ptr = (struct kevent *)realloc(_st_kq_data->addlist, n * sizeof(struct kevent)); + if (!ptr) { return -1; + } _st_kq_data->addlist = ptr; _st_kq_data->addlist_size = n; @@ -707,8 +711,7 @@ ST_HIDDEN int _st_kq_addlist_expand(int avail) * Try to expand the result event list too * (although we don't have to do it). */ - ptr = (struct kevent *)realloc(_st_kq_data->evtlist, - n * sizeof(struct kevent)); + ptr = (struct kevent *)realloc(_st_kq_data->evtlist, n * sizeof(struct kevent)); if (ptr) { _st_kq_data->evtlist = ptr; _st_kq_data->evtlist_size = n; @@ -720,8 +723,7 @@ ST_HIDDEN int _st_kq_addlist_expand(int avail) ST_HIDDEN void _st_kq_addlist_add(const struct kevent *kev) { ST_ASSERT(_st_kq_data->addlist_cnt < _st_kq_data->addlist_size); - memcpy(_st_kq_data->addlist + _st_kq_data->addlist_cnt, kev, - sizeof(struct kevent)); + memcpy(_st_kq_data->addlist + _st_kq_data->addlist_cnt, kev, sizeof(struct kevent)); _st_kq_data->addlist_cnt++; } @@ -733,8 +735,7 @@ ST_HIDDEN void _st_kq_dellist_add(const struct kevent *kev) struct kevent *ptr; n <<= 1; - ptr = (struct kevent *)realloc(_st_kq_data->dellist, - n * sizeof(struct kevent)); + ptr = (struct kevent *)realloc(_st_kq_data->dellist, n * sizeof(struct kevent)); if (!ptr) { /* See comment in _st_kq_pollset_del() */ return; @@ -744,8 +745,7 @@ ST_HIDDEN void _st_kq_dellist_add(const struct kevent *kev) _st_kq_data->dellist_size = n; } - memcpy(_st_kq_data->dellist + _st_kq_data->dellist_cnt, kev, - sizeof(struct kevent)); + memcpy(_st_kq_data->dellist + _st_kq_data->dellist_cnt, kev, sizeof(struct kevent)); _st_kq_data->dellist_cnt++; } @@ -767,9 +767,9 @@ ST_HIDDEN int _st_kq_pollset_add(struct pollfd *pds, int npds) errno = EINVAL; return -1; } - if (pd->fd >= _st_kq_data->fd_data_size && - _st_kq_fd_data_expand(pd->fd) < 0) + if (pd->fd >= _st_kq_data->fd_data_size && _st_kq_fd_data_expand(pd->fd) < 0) { return -1; + } } /* @@ -777,9 +777,9 @@ ST_HIDDEN int _st_kq_pollset_add(struct pollfd *pds, int npds) * descriptors as in the pollset (for both READ and WRITE filters). */ npds <<= 1; - if (npds > _st_kq_data->addlist_size - _st_kq_data->addlist_cnt && - _st_kq_addlist_expand(npds) < 0) + if (npds > _st_kq_data->addlist_size - _st_kq_data->addlist_cnt && _st_kq_addlist_expand(npds) < 0) { return -1; + } for (pd = pds; pd < epd; pd++) { if ((pd->events & POLLIN) && (_ST_KQ_READ_CNT(pd->fd)++ == 0)) { @@ -841,8 +841,7 @@ ST_HIDDEN void _st_kq_pollset_del(struct pollfd *pds, int npds) int rv; do { /* This kevent() won't block since result list size is 0 */ - rv = kevent(_st_kq_data->kq, _st_kq_data->dellist, - _st_kq_data->dellist_cnt, NULL, 0, NULL); + rv = kevent(_st_kq_data->kq, _st_kq_data->dellist, _st_kq_data->dellist_cnt, NULL, 0, NULL); } while (rv < 0 && errno == EINTR); } } @@ -861,8 +860,7 @@ ST_HIDDEN void _st_kq_dispatch(void) if (_ST_SLEEPQ == NULL) { tsp = NULL; } else { - min_timeout = (_ST_SLEEPQ->due <= _ST_LAST_CLOCK) ? 0 : - (_ST_SLEEPQ->due - _ST_LAST_CLOCK); + min_timeout = (_ST_SLEEPQ->due <= _ST_LAST_CLOCK) ? 0 : (_ST_SLEEPQ->due - _ST_LAST_CLOCK); timeout.tv_sec = (time_t) (min_timeout / 1000000); timeout.tv_nsec = (long) ((min_timeout % 1000000) * 1000); tsp = &timeout; @@ -870,9 +868,8 @@ ST_HIDDEN void _st_kq_dispatch(void) retry_kevent: /* Check for I/O operations */ - nfd = kevent(_st_kq_data->kq, - _st_kq_data->addlist, _st_kq_data->addlist_cnt, - _st_kq_data->evtlist, _st_kq_data->evtlist_size, tsp); + nfd = kevent(_st_kq_data->kq, _st_kq_data->addlist, _st_kq_data->addlist_cnt, + _st_kq_data->evtlist, _st_kq_data->evtlist_size, tsp); _st_kq_data->addlist_cnt = 0; @@ -927,16 +924,14 @@ ST_HIDDEN void _st_kq_dispatch(void) * We set EV_ONESHOT flag so we only need to delete * descriptor if it didn't fire. */ - if ((events & POLLIN) && (--_ST_KQ_READ_CNT(osfd) == 0) && - ((_ST_KQ_REVENTS(osfd) & POLLIN) == 0)) { + if ((events & POLLIN) && (--_ST_KQ_READ_CNT(osfd) == 0) && ((_ST_KQ_REVENTS(osfd) & POLLIN) == 0)) { memset(&kev, 0, sizeof(kev)); kev.ident = osfd; kev.filter = EVFILT_READ; kev.flags = EV_DELETE; _st_kq_dellist_add(&kev); } - if ((events & POLLOUT) && (--_ST_KQ_WRITE_CNT(osfd) == 0) - && ((_ST_KQ_REVENTS(osfd) & POLLOUT) == 0)) { + if ((events & POLLOUT) && (--_ST_KQ_WRITE_CNT(osfd) == 0) && ((_ST_KQ_REVENTS(osfd) & POLLOUT) == 0)) { memset(&kev, 0, sizeof(kev)); kev.ident = osfd; kev.filter = EVFILT_WRITE; @@ -945,8 +940,9 @@ ST_HIDDEN void _st_kq_dispatch(void) } } - if (pq->thread->flags & _ST_FL_ON_SLEEPQ) + if (pq->thread->flags & _ST_FL_ON_SLEEPQ) { _ST_DEL_SLEEPQ(pq->thread); + } pq->thread->state = _ST_ST_RUNNABLE; _ST_ADD_RUNQ(pq->thread); } @@ -956,8 +952,7 @@ ST_HIDDEN void _st_kq_dispatch(void) int rv; do { /* This kevent() won't block since result list size is 0 */ - rv = kevent(_st_kq_data->kq, _st_kq_data->dellist, - _st_kq_data->dellist_cnt, NULL, 0, NULL); + rv = kevent(_st_kq_data->kq, _st_kq_data->dellist, _st_kq_data->dellist_cnt, NULL, 0, NULL); } while (rv < 0 && errno == EINTR); } @@ -965,7 +960,6 @@ ST_HIDDEN void _st_kq_dispatch(void) osfd = _st_kq_data->evtlist[i].ident; _ST_KQ_REVENTS(osfd) = 0; } - } else if (nfd < 0) { if (errno == EBADF && _st_kq_data->pid != getpid()) { /* We probably forked, reinitialize kqueue */ @@ -976,8 +970,7 @@ ST_HIDDEN void _st_kq_dispatch(void) fcntl(_st_kq_data->kq, F_SETFD, FD_CLOEXEC); _st_kq_data->pid = getpid(); /* Re-register all descriptors on ioq with new kqueue */ - memset(_st_kq_data->fd_data, 0, - _st_kq_data->fd_data_size * sizeof(_kq_fd_data_t)); + memset(_st_kq_data->fd_data, 0, _st_kq_data->fd_data_size * sizeof(_kq_fd_data_t)); for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) { pq = _ST_POLLQUEUE_PTR(q); _st_kq_pollset_add(pq->pds, pq->npds); @@ -989,8 +982,9 @@ ST_HIDDEN void _st_kq_dispatch(void) ST_HIDDEN int _st_kq_fd_new(int osfd) { - if (osfd >= _st_kq_data->fd_data_size && _st_kq_fd_data_expand(osfd) < 0) + if (osfd >= _st_kq_data->fd_data_size && _st_kq_fd_data_expand(osfd) < 0) { return -1; + } return 0; } @@ -1024,7 +1018,6 @@ static _st_eventsys_t _st_kq_eventsys = { }; #endif /* MD_HAVE_KQUEUE */ - #ifdef MD_HAVE_EPOLL /***************************************** * epoll event system @@ -1036,14 +1029,13 @@ ST_HIDDEN int _st_epoll_init(void) int err = 0; int rv = 0; - _st_epoll_data = - (struct _st_epolldata *) calloc(1, sizeof(*_st_epoll_data)); - if (!_st_epoll_data) + _st_epoll_data = (struct _st_epolldata *) calloc(1, sizeof(*_st_epoll_data)); + if (!_st_epoll_data) { return -1; + } fdlim = st_getfdlimit(); - _st_epoll_data->fd_hint = (fdlim > 0 && fdlim < ST_EPOLL_EVTLIST_SIZE) ? - fdlim : ST_EPOLL_EVTLIST_SIZE; + _st_epoll_data->fd_hint = (fdlim > 0 && fdlim < ST_EPOLL_EVTLIST_SIZE) ? fdlim : ST_EPOLL_EVTLIST_SIZE; if ((_st_epoll_data->epfd = epoll_create(_st_epoll_data->fd_hint)) < 0) { err = errno; @@ -1055,9 +1047,7 @@ ST_HIDDEN int _st_epoll_init(void) /* Allocate file descriptor data array */ _st_epoll_data->fd_data_size = _st_epoll_data->fd_hint; - _st_epoll_data->fd_data = - (_epoll_fd_data_t *)calloc(_st_epoll_data->fd_data_size, - sizeof(_epoll_fd_data_t)); + _st_epoll_data->fd_data = (_epoll_fd_data_t *)calloc(_st_epoll_data->fd_data_size, sizeof(_epoll_fd_data_t)); if (!_st_epoll_data->fd_data) { err = errno; rv = -1; @@ -1066,9 +1056,7 @@ ST_HIDDEN int _st_epoll_init(void) /* Allocate event lists */ _st_epoll_data->evtlist_size = _st_epoll_data->fd_hint; - _st_epoll_data->evtlist = - (struct epoll_event *)malloc(_st_epoll_data->evtlist_size * - sizeof(struct epoll_event)); + _st_epoll_data->evtlist = (struct epoll_event *)malloc(_st_epoll_data->evtlist_size * sizeof(struct epoll_event)); if (!_st_epoll_data->evtlist) { err = errno; rv = -1; @@ -1076,8 +1064,9 @@ ST_HIDDEN int _st_epoll_init(void) cleanup_epoll: if (rv < 0) { - if (_st_epoll_data->epfd >= 0) + if (_st_epoll_data->epfd >= 0) { close(_st_epoll_data->epfd); + } free(_st_epoll_data->fd_data); free(_st_epoll_data->evtlist); free(_st_epoll_data); @@ -1093,16 +1082,16 @@ ST_HIDDEN int _st_epoll_fd_data_expand(int maxfd) _epoll_fd_data_t *ptr; int n = _st_epoll_data->fd_data_size; - while (maxfd >= n) + while (maxfd >= n) { n <<= 1; + } - ptr = (_epoll_fd_data_t *)realloc(_st_epoll_data->fd_data, - n * sizeof(_epoll_fd_data_t)); - if (!ptr) + ptr = (_epoll_fd_data_t *)realloc(_st_epoll_data->fd_data, n * sizeof(_epoll_fd_data_t)); + if (!ptr) { return -1; + } - memset(ptr + _st_epoll_data->fd_data_size, 0, - (n - _st_epoll_data->fd_data_size) * sizeof(_epoll_fd_data_t)); + memset(ptr + _st_epoll_data->fd_data_size, 0, (n - _st_epoll_data->fd_data_size) * sizeof(_epoll_fd_data_t)); _st_epoll_data->fd_data = ptr; _st_epoll_data->fd_data_size = n; @@ -1115,11 +1104,11 @@ ST_HIDDEN void _st_epoll_evtlist_expand(void) struct epoll_event *ptr; int n = _st_epoll_data->evtlist_size; - while (_st_epoll_data->evtlist_cnt > n) + while (_st_epoll_data->evtlist_cnt > n) { n <<= 1; + } - ptr = (struct epoll_event *)realloc(_st_epoll_data->evtlist, - n * sizeof(struct epoll_event)); + ptr = (struct epoll_event *)realloc(_st_epoll_data->evtlist, n * sizeof(struct epoll_event)); if (ptr) { _st_epoll_data->evtlist = ptr; _st_epoll_data->evtlist_size = n; @@ -1141,12 +1130,15 @@ ST_HIDDEN void _st_epoll_pollset_del(struct pollfd *pds, int npds) for (pd = pds; pd < epd; pd++) { old_events = _ST_EPOLL_EVENTS(pd->fd); - if (pd->events & POLLIN) + if (pd->events & POLLIN) { _ST_EPOLL_READ_CNT(pd->fd)--; - if (pd->events & POLLOUT) + } + if (pd->events & POLLOUT) { _ST_EPOLL_WRITE_CNT(pd->fd)--; - if (pd->events & POLLPRI) + } + if (pd->events & POLLPRI) { _ST_EPOLL_EXCEP_CNT(pd->fd)--; + } events = _ST_EPOLL_EVENTS(pd->fd); /* @@ -1158,8 +1150,7 @@ ST_HIDDEN void _st_epoll_pollset_del(struct pollfd *pds, int npds) op = events ? EPOLL_CTL_MOD : EPOLL_CTL_DEL; ev.events = events; ev.data.fd = pd->fd; - if (epoll_ctl(_st_epoll_data->epfd, op, pd->fd, &ev) == 0 && - op == EPOLL_CTL_DEL) { + if (epoll_ctl(_st_epoll_data->epfd, op, pd->fd, &ev) == 0 && op == EPOLL_CTL_DEL) { _st_epoll_data->evtlist_cnt--; } } @@ -1175,39 +1166,42 @@ ST_HIDDEN int _st_epoll_pollset_add(struct pollfd *pds, int npds) /* Do as many checks as possible up front */ for (i = 0; i < npds; i++) { fd = pds[i].fd; - if (fd < 0 || !pds[i].events || - (pds[i].events & ~(POLLIN | POLLOUT | POLLPRI))) { + if (fd < 0 || !pds[i].events || (pds[i].events & ~(POLLIN | POLLOUT | POLLPRI))) { errno = EINVAL; return -1; } - if (fd >= _st_epoll_data->fd_data_size && - _st_epoll_fd_data_expand(fd) < 0) + if (fd >= _st_epoll_data->fd_data_size && _st_epoll_fd_data_expand(fd) < 0) { return -1; + } } for (i = 0; i < npds; i++) { fd = pds[i].fd; old_events = _ST_EPOLL_EVENTS(fd); - if (pds[i].events & POLLIN) + if (pds[i].events & POLLIN) { _ST_EPOLL_READ_CNT(fd)++; - if (pds[i].events & POLLOUT) + } + if (pds[i].events & POLLOUT) { _ST_EPOLL_WRITE_CNT(fd)++; - if (pds[i].events & POLLPRI) + } + if (pds[i].events & POLLPRI) { _ST_EPOLL_EXCEP_CNT(fd)++; + } events = _ST_EPOLL_EVENTS(fd); if (events != old_events) { op = old_events ? EPOLL_CTL_MOD : EPOLL_CTL_ADD; ev.events = events; ev.data.fd = fd; - if (epoll_ctl(_st_epoll_data->epfd, op, fd, &ev) < 0 && - (op != EPOLL_CTL_ADD || errno != EEXIST)) + if (epoll_ctl(_st_epoll_data->epfd, op, fd, &ev) < 0 && (op != EPOLL_CTL_ADD || errno != EEXIST)) { break; + } if (op == EPOLL_CTL_ADD) { _st_epoll_data->evtlist_cnt++; - if (_st_epoll_data->evtlist_cnt > _st_epoll_data->evtlist_size) + if (_st_epoll_data->evtlist_cnt > _st_epoll_data->evtlist_size) { _st_epoll_evtlist_expand(); + } } } } @@ -1238,8 +1232,7 @@ ST_HIDDEN void _st_epoll_dispatch(void) if (_ST_SLEEPQ == NULL) { timeout = -1; } else { - min_timeout = (_ST_SLEEPQ->due <= _ST_LAST_CLOCK) ? 0 : - (_ST_SLEEPQ->due - _ST_LAST_CLOCK); + min_timeout = (_ST_SLEEPQ->due <= _ST_LAST_CLOCK) ? 0 : (_ST_SLEEPQ->due - _ST_LAST_CLOCK); timeout = (int) (min_timeout / 1000); } @@ -1255,8 +1248,7 @@ ST_HIDDEN void _st_epoll_dispatch(void) _st_epoll_data->pid = getpid(); /* Put all descriptors on ioq into new epoll set */ - memset(_st_epoll_data->fd_data, 0, - _st_epoll_data->fd_data_size * sizeof(_epoll_fd_data_t)); + memset(_st_epoll_data->fd_data, 0, _st_epoll_data->fd_data_size * sizeof(_epoll_fd_data_t)); _st_epoll_data->evtlist_cnt = 0; for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) { pq = _ST_POLLQUEUE_PTR(q); @@ -1265,8 +1257,7 @@ ST_HIDDEN void _st_epoll_dispatch(void) } /* Check for I/O operations */ - nfd = epoll_wait(_st_epoll_data->epfd, _st_epoll_data->evtlist, - _st_epoll_data->evtlist_size, timeout); + nfd = epoll_wait(_st_epoll_data->epfd, _st_epoll_data->evtlist, _st_epoll_data->evtlist_size, timeout); if (nfd > 0) { for (i = 0; i < nfd; i++) { @@ -1291,16 +1282,21 @@ ST_HIDDEN void _st_epoll_dispatch(void) osfd = pds->fd; events = pds->events; revents = 0; - if ((events & POLLIN) && (_ST_EPOLL_REVENTS(osfd) & EPOLLIN)) + if ((events & POLLIN) && (_ST_EPOLL_REVENTS(osfd) & EPOLLIN)) { revents |= POLLIN; - if ((events & POLLOUT) && (_ST_EPOLL_REVENTS(osfd) & EPOLLOUT)) + } + if ((events & POLLOUT) && (_ST_EPOLL_REVENTS(osfd) & EPOLLOUT)) { revents |= POLLOUT; - if ((events & POLLPRI) && (_ST_EPOLL_REVENTS(osfd) & EPOLLPRI)) + } + if ((events & POLLPRI) && (_ST_EPOLL_REVENTS(osfd) & EPOLLPRI)) { revents |= POLLPRI; - if (_ST_EPOLL_REVENTS(osfd) & EPOLLERR) + } + if (_ST_EPOLL_REVENTS(osfd) & EPOLLERR) { revents |= POLLERR; - if (_ST_EPOLL_REVENTS(osfd) & EPOLLHUP) + } + if (_ST_EPOLL_REVENTS(osfd) & EPOLLHUP) { revents |= POLLHUP; + } pds->revents = revents; if (revents) { @@ -1316,8 +1312,9 @@ ST_HIDDEN void _st_epoll_dispatch(void) */ _st_epoll_pollset_del(pq->pds, pq->npds); - if (pq->thread->flags & _ST_FL_ON_SLEEPQ) + if (pq->thread->flags & _ST_FL_ON_SLEEPQ) { _ST_DEL_SLEEPQ(pq->thread); + } pq->thread->state = _ST_ST_RUNNABLE; _ST_ADD_RUNQ(pq->thread); } @@ -1331,8 +1328,7 @@ ST_HIDDEN void _st_epoll_dispatch(void) op = events ? EPOLL_CTL_MOD : EPOLL_CTL_DEL; ev.events = events; ev.data.fd = osfd; - if (epoll_ctl(_st_epoll_data->epfd, op, osfd, &ev) == 0 && - op == EPOLL_CTL_DEL) { + if (epoll_ctl(_st_epoll_data->epfd, op, osfd, &ev) == 0 && op == EPOLL_CTL_DEL) { _st_epoll_data->evtlist_cnt--; } } @@ -1341,17 +1337,16 @@ ST_HIDDEN void _st_epoll_dispatch(void) ST_HIDDEN int _st_epoll_fd_new(int osfd) { - if (osfd >= _st_epoll_data->fd_data_size && - _st_epoll_fd_data_expand(osfd) < 0) + if (osfd >= _st_epoll_data->fd_data_size && _st_epoll_fd_data_expand(osfd) < 0) { return -1; + } return 0; } ST_HIDDEN int _st_epoll_fd_close(int osfd) { - if (_ST_EPOLL_READ_CNT(osfd) || _ST_EPOLL_WRITE_CNT(osfd) || - _ST_EPOLL_EXCEP_CNT(osfd)) { + if (_ST_EPOLL_READ_CNT(osfd) || _ST_EPOLL_WRITE_CNT(osfd) || _ST_EPOLL_EXCEP_CNT(osfd)) { errno = EBUSY; return -1; } @@ -1425,8 +1420,9 @@ int st_set_eventsys(int eventsys) #if defined (MD_HAVE_KQUEUE) _st_eventsys = &_st_kq_eventsys; #elif defined (MD_HAVE_EPOLL) - if (_st_epoll_is_supported()) + if (_st_epoll_is_supported()) { _st_eventsys = &_st_epoll_eventsys; + } #endif break; default: diff --git a/trunk/research/st/io.c b/trunk/research/st/io.c index 866b5f7270..bc77dc8e10 100644 --- a/trunk/research/st/io.c +++ b/trunk/research/st/io.c @@ -52,7 +52,6 @@ #include #include "common.h" - #if EAGAIN != EWOULDBLOCK #define _IO_NOT_READY_ERROR ((errno == EAGAIN) || (errno == EWOULDBLOCK)) #else @@ -100,13 +99,11 @@ int _st_io_init(void) return 0; } - int st_getfdlimit(void) { return _st_osfd_limit; } - void st_netfd_free(_st_netfd_t *fd) { if (!fd->inuse) { @@ -126,7 +123,6 @@ void st_netfd_free(_st_netfd_t *fd) _st_netfd_freelist = fd; } - static _st_netfd_t *_st_netfd_new(int osfd, int nonblock, int is_socket) { _st_netfd_t *fd; @@ -165,19 +161,16 @@ static _st_netfd_t *_st_netfd_new(int osfd, int nonblock, int is_socket) return fd; } - _st_netfd_t *st_netfd_open(int osfd) { return _st_netfd_new(osfd, 1, 0); } - _st_netfd_t *st_netfd_open_socket(int osfd) { return _st_netfd_new(osfd, 1, 1); } - int st_netfd_close(_st_netfd_t *fd) { if ((*_st_eventsys->fd_close)(fd->osfd) < 0) { @@ -188,13 +181,11 @@ int st_netfd_close(_st_netfd_t *fd) return close(fd->osfd); } - int st_netfd_fileno(_st_netfd_t *fd) { return (fd->osfd); } - void st_netfd_setspecific(_st_netfd_t *fd, void *value, _st_destructor_t destructor) { if (value != fd->private_data) { @@ -207,13 +198,11 @@ void st_netfd_setspecific(_st_netfd_t *fd, void *value, _st_destructor_t destruc fd->destructor = destructor; } - void *st_netfd_getspecific(_st_netfd_t *fd) { return (fd->private_data); } - /* * Wait for I/O on a single descriptor. */ @@ -407,7 +396,6 @@ _st_netfd_t *st_accept(_st_netfd_t *fd, struct sockaddr *addr, int *addrlen, st_ } #endif /* MD_ALWAYS_UNSERIALIZED_ACCEPT */ - int st_connect(_st_netfd_t *fd, const struct sockaddr *addr, int addrlen, st_utime_t timeout) { int n, err = 0; @@ -446,7 +434,6 @@ int st_connect(_st_netfd_t *fd, const struct sockaddr *addr, int addrlen, st_uti return 0; } - ssize_t st_read(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout) { ssize_t n; @@ -467,7 +454,6 @@ ssize_t st_read(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout) return n; } - int st_read_resid(_st_netfd_t *fd, void *buf, size_t *resid, st_utime_t timeout) { struct iovec iov, *riov; @@ -482,7 +468,6 @@ int st_read_resid(_st_netfd_t *fd, void *buf, size_t *resid, st_utime_t timeout) return rv; } - ssize_t st_readv(_st_netfd_t *fd, const struct iovec *iov, int iov_size, st_utime_t timeout) { ssize_t n; @@ -741,7 +726,6 @@ int st_sendto(_st_netfd_t *fd, const void *msg, int len, const struct sockaddr * return n; } - int st_recvmsg(_st_netfd_t *fd, struct msghdr *msg, int flags, st_utime_t timeout) { int n; diff --git a/trunk/research/st/key.c b/trunk/research/st/key.c index b1b8f986c4..49d778a187 100644 --- a/trunk/research/st/key.c +++ b/trunk/research/st/key.c @@ -43,14 +43,12 @@ #include #include "common.h" - /* * Destructor table for per-thread private data */ static _st_destructor_t _st_destructors[ST_KEYS_MAX]; static int key_max = 0; - /* * Return a key to be used for thread specific data */ @@ -67,13 +65,11 @@ int st_key_create(int *keyp, _st_destructor_t destructor) return 0; } - int st_key_getlimit(void) { return ST_KEYS_MAX; } - int st_thread_setspecific(int key, void *value) { _st_thread_t *me = _ST_CURRENT_THREAD(); @@ -94,7 +90,6 @@ int st_thread_setspecific(int key, void *value) return 0; } - void *st_thread_getspecific(int key) { if (key < 0 || key >= key_max) { @@ -104,7 +99,6 @@ void *st_thread_getspecific(int key) return ((_ST_CURRENT_THREAD())->private_data[key]); } - /* * Free up all per-thread private data */ From 22710db9117a0406d7ac9767af6296d7febff975 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 6 Nov 2014 13:52:15 +0800 Subject: [PATCH 064/800] research st: refine sched. --- trunk/research/st/sched.c | 1010 +++++++++++++++++++------------------ 1 file changed, 511 insertions(+), 499 deletions(-) diff --git a/trunk/research/st/sched.c b/trunk/research/st/sched.c index 4822dd42ae..faa66e38df 100644 --- a/trunk/research/st/sched.c +++ b/trunk/research/st/sched.c @@ -47,7 +47,6 @@ #include #include "common.h" - /* Global data */ _st_vp_t _st_this_vp; /* This VP */ _st_thread_t *_st_this_thread; /* Current thread */ @@ -56,575 +55,586 @@ int _st_active_count = 0; /* Active thread count */ time_t _st_curr_time = 0; /* Current time as returned by time(2) */ st_utime_t _st_last_tset; /* Last time it was fetched */ - int st_poll(struct pollfd *pds, int npds, st_utime_t timeout) { - struct pollfd *pd; - struct pollfd *epd = pds + npds; - _st_pollq_t pq; - _st_thread_t *me = _ST_CURRENT_THREAD(); - int n; - - if (me->flags & _ST_FL_INTERRUPT) { - me->flags &= ~_ST_FL_INTERRUPT; - errno = EINTR; - return -1; - } - - if ((*_st_eventsys->pollset_add)(pds, npds) < 0) - return -1; - - pq.pds = pds; - pq.npds = npds; - pq.thread = me; - pq.on_ioq = 1; - _ST_ADD_IOQ(pq); - if (timeout != ST_UTIME_NO_TIMEOUT) - _ST_ADD_SLEEPQ(me, timeout); - me->state = _ST_ST_IO_WAIT; - - _ST_SWITCH_CONTEXT(me); - - n = 0; - if (pq.on_ioq) { - /* If we timed out, the pollq might still be on the ioq. Remove it */ - _ST_DEL_IOQ(pq); - (*_st_eventsys->pollset_del)(pds, npds); - } else { - /* Count the number of ready descriptors */ - for (pd = pds; pd < epd; pd++) { - if (pd->revents) - n++; - } - } - - if (me->flags & _ST_FL_INTERRUPT) { - me->flags &= ~_ST_FL_INTERRUPT; - errno = EINTR; - return -1; - } - - return n; + struct pollfd *pd; + struct pollfd *epd = pds + npds; + _st_pollq_t pq; + _st_thread_t *me = _ST_CURRENT_THREAD(); + int n; + + if (me->flags & _ST_FL_INTERRUPT) { + me->flags &= ~_ST_FL_INTERRUPT; + errno = EINTR; + return -1; + } + + if ((*_st_eventsys->pollset_add)(pds, npds) < 0) { + return -1; + } + + pq.pds = pds; + pq.npds = npds; + pq.thread = me; + pq.on_ioq = 1; + _ST_ADD_IOQ(pq); + if (timeout != ST_UTIME_NO_TIMEOUT) { + _ST_ADD_SLEEPQ(me, timeout); + } + me->state = _ST_ST_IO_WAIT; + + _ST_SWITCH_CONTEXT(me); + + n = 0; + if (pq.on_ioq) { + /* If we timed out, the pollq might still be on the ioq. Remove it */ + _ST_DEL_IOQ(pq); + (*_st_eventsys->pollset_del)(pds, npds); + } else { + /* Count the number of ready descriptors */ + for (pd = pds; pd < epd; pd++) { + if (pd->revents) { + n++; + } + } + } + + if (me->flags & _ST_FL_INTERRUPT) { + me->flags &= ~_ST_FL_INTERRUPT; + errno = EINTR; + return -1; + } + + return n; } - void _st_vp_schedule(void) { - _st_thread_t *thread; - - if (_ST_RUNQ.next != &_ST_RUNQ) { - /* Pull thread off of the run queue */ - thread = _ST_THREAD_PTR(_ST_RUNQ.next); - _ST_DEL_RUNQ(thread); - } else { - /* If there are no threads to run, switch to the idle thread */ - thread = _st_this_vp.idle_thread; - } - ST_ASSERT(thread->state == _ST_ST_RUNNABLE); - - /* Resume the thread */ - thread->state = _ST_ST_RUNNING; - _ST_RESTORE_CONTEXT(thread); + _st_thread_t *thread; + + if (_ST_RUNQ.next != &_ST_RUNQ) { + /* Pull thread off of the run queue */ + thread = _ST_THREAD_PTR(_ST_RUNQ.next); + _ST_DEL_RUNQ(thread); + } else { + /* If there are no threads to run, switch to the idle thread */ + thread = _st_this_vp.idle_thread; + } + ST_ASSERT(thread->state == _ST_ST_RUNNABLE); + + /* Resume the thread */ + thread->state = _ST_ST_RUNNING; + _ST_RESTORE_CONTEXT(thread); } - /* * Initialize this Virtual Processor */ int st_init(void) { - _st_thread_t *thread; - - if (_st_active_count) { - /* Already initialized */ - return 0; - } - - /* We can ignore return value here */ - st_set_eventsys(ST_EVENTSYS_DEFAULT); - - if (_st_io_init() < 0) - return -1; - - memset(&_st_this_vp, 0, sizeof(_st_vp_t)); - - ST_INIT_CLIST(&_ST_RUNQ); - ST_INIT_CLIST(&_ST_IOQ); - ST_INIT_CLIST(&_ST_ZOMBIEQ); + _st_thread_t *thread; + + if (_st_active_count) { + /* Already initialized */ + return 0; + } + + /* We can ignore return value here */ + st_set_eventsys(ST_EVENTSYS_DEFAULT); + + if (_st_io_init() < 0) { + return -1; + } + + memset(&_st_this_vp, 0, sizeof(_st_vp_t)); + + ST_INIT_CLIST(&_ST_RUNQ); + ST_INIT_CLIST(&_ST_IOQ); + ST_INIT_CLIST(&_ST_ZOMBIEQ); #ifdef DEBUG - ST_INIT_CLIST(&_ST_THREADQ); + ST_INIT_CLIST(&_ST_THREADQ); #endif - - if ((*_st_eventsys->init)() < 0) - return -1; - - _st_this_vp.pagesize = getpagesize(); - _st_this_vp.last_clock = st_utime(); - - /* - * Create idle thread - */ - _st_this_vp.idle_thread = st_thread_create(_st_idle_thread_start, - NULL, 0, 0); - if (!_st_this_vp.idle_thread) - return -1; - _st_this_vp.idle_thread->flags = _ST_FL_IDLE_THREAD; - _st_active_count--; - _ST_DEL_RUNQ(_st_this_vp.idle_thread); - - /* - * Initialize primordial thread - */ - thread = (_st_thread_t *) calloc(1, sizeof(_st_thread_t) + - (ST_KEYS_MAX * sizeof(void *))); - if (!thread) - return -1; - thread->private_data = (void **) (thread + 1); - thread->state = _ST_ST_RUNNING; - thread->flags = _ST_FL_PRIMORDIAL; - _ST_SET_CURRENT_THREAD(thread); - _st_active_count++; + + if ((*_st_eventsys->init)() < 0) { + return -1; + } + + _st_this_vp.pagesize = getpagesize(); + _st_this_vp.last_clock = st_utime(); + + /* + * Create idle thread + */ + _st_this_vp.idle_thread = st_thread_create(_st_idle_thread_start, NULL, 0, 0); + if (!_st_this_vp.idle_thread) { + return -1; + } + _st_this_vp.idle_thread->flags = _ST_FL_IDLE_THREAD; + _st_active_count--; + _ST_DEL_RUNQ(_st_this_vp.idle_thread); + + /* + * Initialize primordial thread + */ + thread = (_st_thread_t *) calloc(1, sizeof(_st_thread_t) + + (ST_KEYS_MAX * sizeof(void *))); + if (!thread) { + return -1; + } + thread->private_data = (void **) (thread + 1); + thread->state = _ST_ST_RUNNING; + thread->flags = _ST_FL_PRIMORDIAL; + _ST_SET_CURRENT_THREAD(thread); + _st_active_count++; #ifdef DEBUG - _ST_ADD_THREADQ(thread); + _ST_ADD_THREADQ(thread); #endif - - return 0; + + return 0; } - #ifdef ST_SWITCH_CB st_switch_cb_t st_set_switch_in_cb(st_switch_cb_t cb) { - st_switch_cb_t ocb = _st_this_vp.switch_in_cb; - _st_this_vp.switch_in_cb = cb; - return ocb; + st_switch_cb_t ocb = _st_this_vp.switch_in_cb; + _st_this_vp.switch_in_cb = cb; + return ocb; } st_switch_cb_t st_set_switch_out_cb(st_switch_cb_t cb) { - st_switch_cb_t ocb = _st_this_vp.switch_out_cb; - _st_this_vp.switch_out_cb = cb; - return ocb; + st_switch_cb_t ocb = _st_this_vp.switch_out_cb; + _st_this_vp.switch_out_cb = cb; + return ocb; } #endif - /* * Start function for the idle thread */ /* ARGSUSED */ void *_st_idle_thread_start(void *arg) { - _st_thread_t *me = _ST_CURRENT_THREAD(); - - while (_st_active_count > 0) { - /* Idle vp till I/O is ready or the smallest timeout expired */ - _ST_VP_IDLE(); - - /* Check sleep queue for expired threads */ - _st_vp_check_clock(); - - me->state = _ST_ST_RUNNABLE; - _ST_SWITCH_CONTEXT(me); - } - - /* No more threads */ - exit(0); - - /* NOTREACHED */ - return NULL; + _st_thread_t *me = _ST_CURRENT_THREAD(); + + while (_st_active_count > 0) { + /* Idle vp till I/O is ready or the smallest timeout expired */ + _ST_VP_IDLE(); + + /* Check sleep queue for expired threads */ + _st_vp_check_clock(); + + me->state = _ST_ST_RUNNABLE; + _ST_SWITCH_CONTEXT(me); + } + + /* No more threads */ + exit(0); + + /* NOTREACHED */ + return NULL; } - void st_thread_exit(void *retval) { - _st_thread_t *thread = _ST_CURRENT_THREAD(); - - thread->retval = retval; - _st_thread_cleanup(thread); - _st_active_count--; - if (thread->term) { - /* Put thread on the zombie queue */ - thread->state = _ST_ST_ZOMBIE; - _ST_ADD_ZOMBIEQ(thread); - - /* Notify on our termination condition variable */ - st_cond_signal(thread->term); - - /* Switch context and come back later */ - _ST_SWITCH_CONTEXT(thread); - - /* Continue the cleanup */ - st_cond_destroy(thread->term); - thread->term = NULL; - } - + _st_thread_t *thread = _ST_CURRENT_THREAD(); + + thread->retval = retval; + _st_thread_cleanup(thread); + _st_active_count--; + if (thread->term) { + /* Put thread on the zombie queue */ + thread->state = _ST_ST_ZOMBIE; + _ST_ADD_ZOMBIEQ(thread); + + /* Notify on our termination condition variable */ + st_cond_signal(thread->term); + + /* Switch context and come back later */ + _ST_SWITCH_CONTEXT(thread); + + /* Continue the cleanup */ + st_cond_destroy(thread->term); + thread->term = NULL; + } + #ifdef DEBUG - _ST_DEL_THREADQ(thread); + _ST_DEL_THREADQ(thread); #endif - - if (!(thread->flags & _ST_FL_PRIMORDIAL)) - _st_stack_free(thread->stack); - - /* Find another thread to run */ - _ST_SWITCH_CONTEXT(thread); - /* Not going to land here */ + + if (!(thread->flags & _ST_FL_PRIMORDIAL)) { + _st_stack_free(thread->stack); + } + + /* Find another thread to run */ + _ST_SWITCH_CONTEXT(thread); + /* Not going to land here */ } - int st_thread_join(_st_thread_t *thread, void **retvalp) { - _st_cond_t *term = thread->term; - - /* Can't join a non-joinable thread */ - if (term == NULL) { - errno = EINVAL; - return -1; - } - if (_ST_CURRENT_THREAD() == thread) { - errno = EDEADLK; - return -1; - } - - /* Multiple threads can't wait on the same joinable thread */ - if (term->wait_q.next != &term->wait_q) { - errno = EINVAL; - return -1; - } - - while (thread->state != _ST_ST_ZOMBIE) { - if (st_cond_timedwait(term, ST_UTIME_NO_TIMEOUT) != 0) - return -1; - } - - if (retvalp) - *retvalp = thread->retval; - - /* - * Remove target thread from the zombie queue and make it runnable. - * When it gets scheduled later, it will do the clean up. - */ - thread->state = _ST_ST_RUNNABLE; - _ST_DEL_ZOMBIEQ(thread); - _ST_ADD_RUNQ(thread); - - return 0; + _st_cond_t *term = thread->term; + + /* Can't join a non-joinable thread */ + if (term == NULL) { + errno = EINVAL; + return -1; + } + if (_ST_CURRENT_THREAD() == thread) { + errno = EDEADLK; + return -1; + } + + /* Multiple threads can't wait on the same joinable thread */ + if (term->wait_q.next != &term->wait_q) { + errno = EINVAL; + return -1; + } + + while (thread->state != _ST_ST_ZOMBIE) { + if (st_cond_timedwait(term, ST_UTIME_NO_TIMEOUT) != 0) { + return -1; + } + } + + if (retvalp) { + *retvalp = thread->retval; + } + + /* + * Remove target thread from the zombie queue and make it runnable. + * When it gets scheduled later, it will do the clean up. + */ + thread->state = _ST_ST_RUNNABLE; + _ST_DEL_ZOMBIEQ(thread); + _ST_ADD_RUNQ(thread); + + return 0; } - void _st_thread_main(void) { - _st_thread_t *thread = _ST_CURRENT_THREAD(); - - /* - * Cap the stack by zeroing out the saved return address register - * value. This allows some debugging/profiling tools to know when - * to stop unwinding the stack. It's a no-op on most platforms. - */ - MD_CAP_STACK(&thread); - - /* Run thread main */ - thread->retval = (*thread->start)(thread->arg); - - /* All done, time to go away */ - st_thread_exit(thread->retval); + _st_thread_t *thread = _ST_CURRENT_THREAD(); + + /* + * Cap the stack by zeroing out the saved return address register + * value. This allows some debugging/profiling tools to know when + * to stop unwinding the stack. It's a no-op on most platforms. + */ + MD_CAP_STACK(&thread); + + /* Run thread main */ + thread->retval = (*thread->start)(thread->arg); + + /* All done, time to go away */ + st_thread_exit(thread->retval); } - /* * Insert "thread" into the timeout heap, in the position * specified by thread->heap_index. See docs/timeout_heap.txt * for details about the timeout heap. */ -static _st_thread_t **heap_insert(_st_thread_t *thread) { - int target = thread->heap_index; - int s = target; - _st_thread_t **p = &_ST_SLEEPQ; - int bits = 0; - int bit; - int index = 1; - - while (s) { - s >>= 1; - bits++; - } - for (bit = bits - 2; bit >= 0; bit--) { - if (thread->due < (*p)->due) { - _st_thread_t *t = *p; - thread->left = t->left; - thread->right = t->right; - *p = thread; - thread->heap_index = index; - thread = t; - } - index <<= 1; - if (target & (1 << bit)) { - p = &((*p)->right); - index |= 1; - } else { - p = &((*p)->left); +static _st_thread_t **heap_insert(_st_thread_t *thread) +{ + int target = thread->heap_index; + int s = target; + _st_thread_t **p = &_ST_SLEEPQ; + int bits = 0; + int bit; + int index = 1; + + while (s) { + s >>= 1; + bits++; } - } - thread->heap_index = index; - *p = thread; - thread->left = thread->right = NULL; - return p; + + for (bit = bits - 2; bit >= 0; bit--) { + if (thread->due < (*p)->due) { + _st_thread_t *t = *p; + thread->left = t->left; + thread->right = t->right; + *p = thread; + thread->heap_index = index; + thread = t; + } + index <<= 1; + if (target & (1 << bit)) { + p = &((*p)->right); + index |= 1; + } else { + p = &((*p)->left); + } + } + + thread->heap_index = index; + *p = thread; + thread->left = thread->right = NULL; + + return p; } - /* * Delete "thread" from the timeout heap. */ -static void heap_delete(_st_thread_t *thread) { - _st_thread_t *t, **p; - int bits = 0; - int s, bit; - - /* First find and unlink the last heap element */ - p = &_ST_SLEEPQ; - s = _ST_SLEEPQ_SIZE; - while (s) { - s >>= 1; - bits++; - } - for (bit = bits - 2; bit >= 0; bit--) { - if (_ST_SLEEPQ_SIZE & (1 << bit)) { - p = &((*p)->right); - } else { - p = &((*p)->left); +static void heap_delete(_st_thread_t *thread) +{ + _st_thread_t *t, **p; + int bits = 0; + int s, bit; + + /* First find and unlink the last heap element */ + p = &_ST_SLEEPQ; + s = _ST_SLEEPQ_SIZE; + while (s) { + s >>= 1; + bits++; } - } - t = *p; - *p = NULL; - --_ST_SLEEPQ_SIZE; - if (t != thread) { - /* - * Insert the unlinked last element in place of the element we are deleting - */ - t->heap_index = thread->heap_index; - p = heap_insert(t); + + for (bit = bits - 2; bit >= 0; bit--) { + if (_ST_SLEEPQ_SIZE & (1 << bit)) { + p = &((*p)->right); + } else { + p = &((*p)->left); + } + } + t = *p; - t->left = thread->left; - t->right = thread->right; - - /* - * Reestablish the heap invariant. - */ - for (;;) { - _st_thread_t *y; /* The younger child */ - int index_tmp; - if (t->left == NULL) - break; - else if (t->right == NULL) - y = t->left; - else if (t->left->due < t->right->due) - y = t->left; - else - y = t->right; - if (t->due > y->due) { - _st_thread_t *tl = y->left; - _st_thread_t *tr = y->right; - *p = y; - if (y == t->left) { - y->left = t; - y->right = t->right; - p = &y->left; - } else { - y->left = t->left; - y->right = t; - p = &y->right; - } - t->left = tl; - t->right = tr; - index_tmp = t->heap_index; - t->heap_index = y->heap_index; - y->heap_index = index_tmp; - } else { - break; - } - } - } - thread->left = thread->right = NULL; + *p = NULL; + --_ST_SLEEPQ_SIZE; + if (t != thread) { + /* + * Insert the unlinked last element in place of the element we are deleting + */ + t->heap_index = thread->heap_index; + p = heap_insert(t); + t = *p; + t->left = thread->left; + t->right = thread->right; + + /* + * Reestablish the heap invariant. + */ + for (;;) { + _st_thread_t *y; /* The younger child */ + int index_tmp; + + if (t->left == NULL) { + break; + } else if (t->right == NULL) { + y = t->left; + } else if (t->left->due < t->right->due) { + y = t->left; + } else { + y = t->right; + } + + if (t->due > y->due) { + _st_thread_t *tl = y->left; + _st_thread_t *tr = y->right; + *p = y; + if (y == t->left) { + y->left = t; + y->right = t->right; + p = &y->left; + } else { + y->left = t->left; + y->right = t; + p = &y->right; + } + t->left = tl; + t->right = tr; + index_tmp = t->heap_index; + t->heap_index = y->heap_index; + y->heap_index = index_tmp; + } else { + break; + } + } + } + + thread->left = thread->right = NULL; } - void _st_add_sleep_q(_st_thread_t *thread, st_utime_t timeout) { - thread->due = _ST_LAST_CLOCK + timeout; - thread->flags |= _ST_FL_ON_SLEEPQ; - thread->heap_index = ++_ST_SLEEPQ_SIZE; - heap_insert(thread); + thread->due = _ST_LAST_CLOCK + timeout; + thread->flags |= _ST_FL_ON_SLEEPQ; + thread->heap_index = ++_ST_SLEEPQ_SIZE; + heap_insert(thread); } - void _st_del_sleep_q(_st_thread_t *thread) { - heap_delete(thread); - thread->flags &= ~_ST_FL_ON_SLEEPQ; + heap_delete(thread); + thread->flags &= ~_ST_FL_ON_SLEEPQ; } - void _st_vp_check_clock(void) { - _st_thread_t *thread; - st_utime_t elapsed, now; - - now = st_utime(); - elapsed = now - _ST_LAST_CLOCK; - _ST_LAST_CLOCK = now; - - if (_st_curr_time && now - _st_last_tset > 999000) { - _st_curr_time = time(NULL); - _st_last_tset = now; - } - - while (_ST_SLEEPQ != NULL) { - thread = _ST_SLEEPQ; - ST_ASSERT(thread->flags & _ST_FL_ON_SLEEPQ); - if (thread->due > now) - break; - _ST_DEL_SLEEPQ(thread); - - /* If thread is waiting on condition variable, set the time out flag */ - if (thread->state == _ST_ST_COND_WAIT) - thread->flags |= _ST_FL_TIMEDOUT; - - /* Make thread runnable */ - ST_ASSERT(!(thread->flags & _ST_FL_IDLE_THREAD)); - thread->state = _ST_ST_RUNNABLE; - _ST_ADD_RUNQ(thread); - } + _st_thread_t *thread; + st_utime_t elapsed, now; + + now = st_utime(); + elapsed = now - _ST_LAST_CLOCK; + _ST_LAST_CLOCK = now; + + if (_st_curr_time && now - _st_last_tset > 999000) { + _st_curr_time = time(NULL); + _st_last_tset = now; + } + + while (_ST_SLEEPQ != NULL) { + thread = _ST_SLEEPQ; + ST_ASSERT(thread->flags & _ST_FL_ON_SLEEPQ); + if (thread->due > now) { + break; + } + _ST_DEL_SLEEPQ(thread); + + /* If thread is waiting on condition variable, set the time out flag */ + if (thread->state == _ST_ST_COND_WAIT) { + thread->flags |= _ST_FL_TIMEDOUT; + } + + /* Make thread runnable */ + ST_ASSERT(!(thread->flags & _ST_FL_IDLE_THREAD)); + thread->state = _ST_ST_RUNNABLE; + _ST_ADD_RUNQ(thread); + } } - void st_thread_interrupt(_st_thread_t *thread) { - /* If thread is already dead */ - if (thread->state == _ST_ST_ZOMBIE) - return; - - thread->flags |= _ST_FL_INTERRUPT; - - if (thread->state == _ST_ST_RUNNING || thread->state == _ST_ST_RUNNABLE) - return; - - if (thread->flags & _ST_FL_ON_SLEEPQ) - _ST_DEL_SLEEPQ(thread); - - /* Make thread runnable */ - thread->state = _ST_ST_RUNNABLE; - _ST_ADD_RUNQ(thread); + /* If thread is already dead */ + if (thread->state == _ST_ST_ZOMBIE) { + return; + } + + thread->flags |= _ST_FL_INTERRUPT; + + if (thread->state == _ST_ST_RUNNING || thread->state == _ST_ST_RUNNABLE) { + return; + } + + if (thread->flags & _ST_FL_ON_SLEEPQ) { + _ST_DEL_SLEEPQ(thread); + } + + /* Make thread runnable */ + thread->state = _ST_ST_RUNNABLE; + _ST_ADD_RUNQ(thread); } - -_st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg, - int joinable, int stk_size) +_st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg, int joinable, int stk_size) { - _st_thread_t *thread; - _st_stack_t *stack; - void **ptds; - char *sp; + _st_thread_t *thread; + _st_stack_t *stack; + void **ptds; + char *sp; #ifdef __ia64__ - char *bsp; + char *bsp; #endif - - /* Adjust stack size */ - if (stk_size == 0) - stk_size = ST_DEFAULT_STACK_SIZE; - stk_size = ((stk_size + _ST_PAGE_SIZE - 1) / _ST_PAGE_SIZE) * _ST_PAGE_SIZE; - stack = _st_stack_new(stk_size); - if (!stack) - return NULL; - - /* Allocate thread object and per-thread data off the stack */ + + /* Adjust stack size */ + if (stk_size == 0) { + stk_size = ST_DEFAULT_STACK_SIZE; + } + stk_size = ((stk_size + _ST_PAGE_SIZE - 1) / _ST_PAGE_SIZE) * _ST_PAGE_SIZE; + stack = _st_stack_new(stk_size); + if (!stack) { + return NULL; + } + + /* Allocate thread object and per-thread data off the stack */ #if defined (MD_STACK_GROWS_DOWN) - sp = stack->stk_top; -#ifdef __ia64__ - /* - * The stack segment is split in the middle. The upper half is used - * as backing store for the register stack which grows upward. - * The lower half is used for the traditional memory stack which - * grows downward. Both stacks start in the middle and grow outward - * from each other. - */ - sp -= (stk_size >> 1); - bsp = sp; - /* Make register stack 64-byte aligned */ - if ((unsigned long)bsp & 0x3f) - bsp = bsp + (0x40 - ((unsigned long)bsp & 0x3f)); - stack->bsp = bsp + _ST_STACK_PAD_SIZE; -#endif - sp = sp - (ST_KEYS_MAX * sizeof(void *)); - ptds = (void **) sp; - sp = sp - sizeof(_st_thread_t); - thread = (_st_thread_t *) sp; - - /* Make stack 64-byte aligned */ - if ((unsigned long)sp & 0x3f) - sp = sp - ((unsigned long)sp & 0x3f); - stack->sp = sp - _ST_STACK_PAD_SIZE; + sp = stack->stk_top; + #ifdef __ia64__ + /* + * The stack segment is split in the middle. The upper half is used + * as backing store for the register stack which grows upward. + * The lower half is used for the traditional memory stack which + * grows downward. Both stacks start in the middle and grow outward + * from each other. + */ + sp -= (stk_size >> 1); + bsp = sp; + /* Make register stack 64-byte aligned */ + if ((unsigned long)bsp & 0x3f) { + bsp = bsp + (0x40 - ((unsigned long)bsp & 0x3f)); + } + stack->bsp = bsp + _ST_STACK_PAD_SIZE; + #endif + sp = sp - (ST_KEYS_MAX * sizeof(void *)); + ptds = (void **) sp; + sp = sp - sizeof(_st_thread_t); + thread = (_st_thread_t *) sp; + + /* Make stack 64-byte aligned */ + if ((unsigned long)sp & 0x3f) { + sp = sp - ((unsigned long)sp & 0x3f); + } + stack->sp = sp - _ST_STACK_PAD_SIZE; #elif defined (MD_STACK_GROWS_UP) - sp = stack->stk_bottom; - thread = (_st_thread_t *) sp; - sp = sp + sizeof(_st_thread_t); - ptds = (void **) sp; - sp = sp + (ST_KEYS_MAX * sizeof(void *)); - - /* Make stack 64-byte aligned */ - if ((unsigned long)sp & 0x3f) - sp = sp + (0x40 - ((unsigned long)sp & 0x3f)); - stack->sp = sp + _ST_STACK_PAD_SIZE; + sp = stack->stk_bottom; + thread = (_st_thread_t *) sp; + sp = sp + sizeof(_st_thread_t); + ptds = (void **) sp; + sp = sp + (ST_KEYS_MAX * sizeof(void *)); + + /* Make stack 64-byte aligned */ + if ((unsigned long)sp & 0x3f) { + sp = sp + (0x40 - ((unsigned long)sp & 0x3f)); + } + stack->sp = sp + _ST_STACK_PAD_SIZE; #else -#error Unknown OS + #error Unknown OS #endif - - memset(thread, 0, sizeof(_st_thread_t)); - memset(ptds, 0, ST_KEYS_MAX * sizeof(void *)); - - /* Initialize thread */ - thread->private_data = ptds; - thread->stack = stack; - thread->start = start; - thread->arg = arg; - + + memset(thread, 0, sizeof(_st_thread_t)); + memset(ptds, 0, ST_KEYS_MAX * sizeof(void *)); + + /* Initialize thread */ + thread->private_data = ptds; + thread->stack = stack; + thread->start = start; + thread->arg = arg; + #ifndef __ia64__ - _ST_INIT_CONTEXT(thread, stack->sp, _st_thread_main); + _ST_INIT_CONTEXT(thread, stack->sp, _st_thread_main); #else - _ST_INIT_CONTEXT(thread, stack->sp, stack->bsp, _st_thread_main); + _ST_INIT_CONTEXT(thread, stack->sp, stack->bsp, _st_thread_main); #endif - - /* If thread is joinable, allocate a termination condition variable */ - if (joinable) { - thread->term = st_cond_new(); - if (thread->term == NULL) { - _st_stack_free(thread->stack); - return NULL; - } - } - - /* Make thread runnable */ - thread->state = _ST_ST_RUNNABLE; - _st_active_count++; - _ST_ADD_RUNQ(thread); + + /* If thread is joinable, allocate a termination condition variable */ + if (joinable) { + thread->term = st_cond_new(); + if (thread->term == NULL) { + _st_stack_free(thread->stack); + return NULL; + } + } + + /* Make thread runnable */ + thread->state = _ST_ST_RUNNABLE; + _st_active_count++; + _ST_ADD_RUNQ(thread); #ifdef DEBUG - _ST_ADD_THREADQ(thread); + _ST_ADD_THREADQ(thread); #endif - - return thread; + + return thread; } - _st_thread_t *st_thread_self(void) { - return _ST_CURRENT_THREAD(); + return _ST_CURRENT_THREAD(); } - #ifdef DEBUG /* ARGSUSED */ void _st_show_thread_stack(_st_thread_t *thread, const char *messg) { - } /* To be set from debugger */ @@ -632,41 +642,43 @@ int _st_iterate_threads_flag = 0; void _st_iterate_threads(void) { - static _st_thread_t *thread = NULL; - static jmp_buf orig_jb, save_jb; - _st_clist_t *q; - - if (!_st_iterate_threads_flag) { + static _st_thread_t *thread = NULL; + static jmp_buf orig_jb, save_jb; + _st_clist_t *q; + + if (!_st_iterate_threads_flag) { + if (thread) { + memcpy(thread->context, save_jb, sizeof(jmp_buf)); + MD_LONGJMP(orig_jb, 1); + } + return; + } + if (thread) { - memcpy(thread->context, save_jb, sizeof(jmp_buf)); - MD_LONGJMP(orig_jb, 1); + memcpy(thread->context, save_jb, sizeof(jmp_buf)); + _st_show_thread_stack(thread, NULL); + } else { + if (MD_SETJMP(orig_jb)) { + _st_iterate_threads_flag = 0; + thread = NULL; + _st_show_thread_stack(thread, "Iteration completed"); + return; + } + thread = _ST_CURRENT_THREAD(); + _st_show_thread_stack(thread, "Iteration started"); } - return; - } - - if (thread) { - memcpy(thread->context, save_jb, sizeof(jmp_buf)); - _st_show_thread_stack(thread, NULL); - } else { - if (MD_SETJMP(orig_jb)) { - _st_iterate_threads_flag = 0; - thread = NULL; - _st_show_thread_stack(thread, "Iteration completed"); - return; - } - thread = _ST_CURRENT_THREAD(); - _st_show_thread_stack(thread, "Iteration started"); - } - - q = thread->tlink.next; - if (q == &_ST_THREADQ) - q = q->next; - ST_ASSERT(q != &_ST_THREADQ); - thread = _ST_THREAD_THREADQ_PTR(q); - if (thread == _ST_CURRENT_THREAD()) - MD_LONGJMP(orig_jb, 1); - memcpy(save_jb, thread->context, sizeof(jmp_buf)); - MD_LONGJMP(thread->context, 1); + + q = thread->tlink.next; + if (q == &_ST_THREADQ) { + q = q->next; + } + ST_ASSERT(q != &_ST_THREADQ); + thread = _ST_THREAD_THREADQ_PTR(q); + if (thread == _ST_CURRENT_THREAD()) { + MD_LONGJMP(orig_jb, 1); + } + memcpy(save_jb, thread->context, sizeof(jmp_buf)); + MD_LONGJMP(thread->context, 1); } #endif /* DEBUG */ From 3377df0bea2a96e3d2ffd649c9dd7a3cbd2ff434 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 6 Nov 2014 13:55:06 +0800 Subject: [PATCH 065/800] research st: refine skt. --- trunk/research/st/stk.c | 165 ++++++++++++++++++++-------------------- 1 file changed, 83 insertions(+), 82 deletions(-) diff --git a/trunk/research/st/stk.c b/trunk/research/st/stk.c index 34769697fb..ced5717bb4 100644 --- a/trunk/research/st/stk.c +++ b/trunk/research/st/stk.c @@ -46,7 +46,6 @@ #include #include "common.h" - /* How much space to leave between the stacks, at each end */ #define REDZONE _ST_PAGE_SIZE @@ -58,116 +57,118 @@ static char *_st_new_stk_segment(int size); _st_stack_t *_st_stack_new(int stack_size) { - _st_clist_t *qp; - _st_stack_t *ts; - int extra; - - for (qp = _st_free_stacks.next; qp != &_st_free_stacks; qp = qp->next) { - ts = _ST_THREAD_STACK_PTR(qp); - if (ts->stk_size >= stack_size) { - /* Found a stack that is big enough */ - ST_REMOVE_LINK(&ts->links); - _st_num_free_stacks--; - ts->links.next = NULL; - ts->links.prev = NULL; - return ts; + _st_clist_t *qp; + _st_stack_t *ts; + int extra; + + for (qp = _st_free_stacks.next; qp != &_st_free_stacks; qp = qp->next) { + ts = _ST_THREAD_STACK_PTR(qp); + if (ts->stk_size >= stack_size) { + /* Found a stack that is big enough */ + ST_REMOVE_LINK(&ts->links); + _st_num_free_stacks--; + ts->links.next = NULL; + ts->links.prev = NULL; + return ts; + } } - } - - /* Make a new thread stack object. */ - if ((ts = (_st_stack_t *)calloc(1, sizeof(_st_stack_t))) == NULL) - return NULL; - extra = _st_randomize_stacks ? _ST_PAGE_SIZE : 0; - ts->vaddr_size = stack_size + 2*REDZONE + extra; - ts->vaddr = _st_new_stk_segment(ts->vaddr_size); - if (!ts->vaddr) { - free(ts); - return NULL; - } - ts->stk_size = stack_size; - ts->stk_bottom = ts->vaddr + REDZONE; - ts->stk_top = ts->stk_bottom + stack_size; - + + /* Make a new thread stack object. */ + if ((ts = (_st_stack_t *)calloc(1, sizeof(_st_stack_t))) == NULL) { + return NULL; + } + extra = _st_randomize_stacks ? _ST_PAGE_SIZE : 0; + ts->vaddr_size = stack_size + 2*REDZONE + extra; + ts->vaddr = _st_new_stk_segment(ts->vaddr_size); + if (!ts->vaddr) { + free(ts); + return NULL; + } + ts->stk_size = stack_size; + ts->stk_bottom = ts->vaddr + REDZONE; + ts->stk_top = ts->stk_bottom + stack_size; + #ifdef DEBUG - mprotect(ts->vaddr, REDZONE, PROT_NONE); - mprotect(ts->stk_top + extra, REDZONE, PROT_NONE); + mprotect(ts->vaddr, REDZONE, PROT_NONE); + mprotect(ts->stk_top + extra, REDZONE, PROT_NONE); #endif - - if (extra) { - long offset = (random() % extra) & ~0xf; - - ts->stk_bottom += offset; - ts->stk_top += offset; - } - - return ts; + + if (extra) { + long offset = (random() % extra) & ~0xf; + + ts->stk_bottom += offset; + ts->stk_top += offset; + } + + return ts; } - /* * Free the stack for the current thread */ void _st_stack_free(_st_stack_t *ts) { - if (!ts) - return; - - /* Put the stack on the free list */ - ST_APPEND_LINK(&ts->links, _st_free_stacks.prev); - _st_num_free_stacks++; + if (!ts) { + return; + } + + /* Put the stack on the free list */ + ST_APPEND_LINK(&ts->links, _st_free_stacks.prev); + _st_num_free_stacks++; } - static char *_st_new_stk_segment(int size) { #ifdef MALLOC_STACK - void *vaddr = malloc(size); -#else - static int zero_fd = -1; - int mmap_flags = MAP_PRIVATE; - void *vaddr; - -#if defined (MD_USE_SYSV_ANON_MMAP) - if (zero_fd < 0) { - if ((zero_fd = open("/dev/zero", O_RDWR, 0)) < 0) - return NULL; - fcntl(zero_fd, F_SETFD, FD_CLOEXEC); - } -#elif defined (MD_USE_BSD_ANON_MMAP) - mmap_flags |= MAP_ANON; + void *vaddr = malloc(size); #else -#error Unknown OS + static int zero_fd = -1; + int mmap_flags = MAP_PRIVATE; + void *vaddr; + + #if defined (MD_USE_SYSV_ANON_MMAP) + if (zero_fd < 0) { + if ((zero_fd = open("/dev/zero", O_RDWR, 0)) < 0) { + return NULL; + } + fcntl(zero_fd, F_SETFD, FD_CLOEXEC); + } + #elif defined (MD_USE_BSD_ANON_MMAP) + mmap_flags |= MAP_ANON; + #else + #error Unknown OS + #endif + + vaddr = mmap(NULL, size, PROT_READ | PROT_WRITE, mmap_flags, zero_fd, 0); + if (vaddr == (void *)MAP_FAILED) { + return NULL; + } + #endif - - vaddr = mmap(NULL, size, PROT_READ | PROT_WRITE, mmap_flags, zero_fd, 0); - if (vaddr == (void *)MAP_FAILED) - return NULL; - -#endif /* MALLOC_STACK */ - - return (char *)vaddr; + + return (char *)vaddr; } - /* Not used */ #if 0 void _st_delete_stk_segment(char *vaddr, int size) { #ifdef MALLOC_STACK - free(vaddr); + free(vaddr); #else - (void) munmap(vaddr, size); + (void) munmap(vaddr, size); #endif } #endif int st_randomize_stacks(int on) { - int wason = _st_randomize_stacks; - - _st_randomize_stacks = on; - if (on) - srandom((unsigned int) st_utime()); - - return wason; + int wason = _st_randomize_stacks; + + _st_randomize_stacks = on; + if (on) { + srandom((unsigned int) st_utime()); + } + + return wason; } From 8521762cb38f6c8ff80067857e603ca112d98513 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 6 Nov 2014 14:00:27 +0800 Subject: [PATCH 066/800] research st: refine sync. --- trunk/research/st/sync.c | 433 +++++++++++++++++++-------------------- 1 file changed, 208 insertions(+), 225 deletions(-) diff --git a/trunk/research/st/sync.c b/trunk/research/st/sync.c index 49bceb3e31..3e5324084a 100644 --- a/trunk/research/st/sync.c +++ b/trunk/research/st/sync.c @@ -44,326 +44,309 @@ #include #include "common.h" - extern time_t _st_curr_time; extern st_utime_t _st_last_tset; extern int _st_active_count; static st_utime_t (*_st_utime)(void) = NULL; - /***************************************** * Time functions */ st_utime_t st_utime(void) { - if (_st_utime == NULL) { + if (_st_utime == NULL) { #ifdef MD_GET_UTIME - MD_GET_UTIME(); + MD_GET_UTIME(); #else -#error Unknown OS + #error Unknown OS #endif - } - - return (*_st_utime)(); + } + + return (*_st_utime)(); } - int st_set_utime_function(st_utime_t (*func)(void)) { - if (_st_active_count) { - errno = EINVAL; - return -1; - } - - _st_utime = func; - - return 0; + if (_st_active_count) { + errno = EINVAL; + return -1; + } + + _st_utime = func; + + return 0; } - st_utime_t st_utime_last_clock(void) { - return _ST_LAST_CLOCK; + return _ST_LAST_CLOCK; } - int st_timecache_set(int on) { - int wason = (_st_curr_time) ? 1 : 0; - - if (on) { - _st_curr_time = time(NULL); - _st_last_tset = st_utime(); - } else - _st_curr_time = 0; - - return wason; + int wason = (_st_curr_time) ? 1 : 0; + + if (on) { + _st_curr_time = time(NULL); + _st_last_tset = st_utime(); + } else { + _st_curr_time = 0; + } + + return wason; } - time_t st_time(void) { - if (_st_curr_time) - return _st_curr_time; - - return time(NULL); + if (_st_curr_time) { + return _st_curr_time; + } + + return time(NULL); } - int st_usleep(st_utime_t usecs) { - _st_thread_t *me = _ST_CURRENT_THREAD(); - - if (me->flags & _ST_FL_INTERRUPT) { - me->flags &= ~_ST_FL_INTERRUPT; - errno = EINTR; - return -1; - } - - if (usecs != ST_UTIME_NO_TIMEOUT) { - me->state = _ST_ST_SLEEPING; - _ST_ADD_SLEEPQ(me, usecs); - } else - me->state = _ST_ST_SUSPENDED; - - _ST_SWITCH_CONTEXT(me); - - if (me->flags & _ST_FL_INTERRUPT) { - me->flags &= ~_ST_FL_INTERRUPT; - errno = EINTR; - return -1; - } - - return 0; + _st_thread_t *me = _ST_CURRENT_THREAD(); + + if (me->flags & _ST_FL_INTERRUPT) { + me->flags &= ~_ST_FL_INTERRUPT; + errno = EINTR; + return -1; + } + + if (usecs != ST_UTIME_NO_TIMEOUT) { + me->state = _ST_ST_SLEEPING; + _ST_ADD_SLEEPQ(me, usecs); + } else { + me->state = _ST_ST_SUSPENDED; + } + + _ST_SWITCH_CONTEXT(me); + + if (me->flags & _ST_FL_INTERRUPT) { + me->flags &= ~_ST_FL_INTERRUPT; + errno = EINTR; + return -1; + } + + return 0; } - int st_sleep(int secs) { - return st_usleep((secs >= 0) ? secs * (st_utime_t) 1000000LL : - ST_UTIME_NO_TIMEOUT); + return st_usleep((secs >= 0) ? secs * (st_utime_t) 1000000LL : ST_UTIME_NO_TIMEOUT); } - /***************************************** * Condition variable functions */ - _st_cond_t *st_cond_new(void) { - _st_cond_t *cvar; - - cvar = (_st_cond_t *) calloc(1, sizeof(_st_cond_t)); - if (cvar) { - ST_INIT_CLIST(&cvar->wait_q); - } - - return cvar; + _st_cond_t *cvar; + + cvar = (_st_cond_t *) calloc(1, sizeof(_st_cond_t)); + if (cvar) { + ST_INIT_CLIST(&cvar->wait_q); + } + + return cvar; } - int st_cond_destroy(_st_cond_t *cvar) { - if (cvar->wait_q.next != &cvar->wait_q) { - errno = EBUSY; - return -1; - } - - free(cvar); - - return 0; + if (cvar->wait_q.next != &cvar->wait_q) { + errno = EBUSY; + return -1; + } + + free(cvar); + + return 0; } - int st_cond_timedwait(_st_cond_t *cvar, st_utime_t timeout) { - _st_thread_t *me = _ST_CURRENT_THREAD(); - int rv; - - if (me->flags & _ST_FL_INTERRUPT) { - me->flags &= ~_ST_FL_INTERRUPT; - errno = EINTR; - return -1; - } - - /* Put caller thread on the condition variable's wait queue */ - me->state = _ST_ST_COND_WAIT; - ST_APPEND_LINK(&me->wait_links, &cvar->wait_q); - - if (timeout != ST_UTIME_NO_TIMEOUT) - _ST_ADD_SLEEPQ(me, timeout); - - _ST_SWITCH_CONTEXT(me); - - ST_REMOVE_LINK(&me->wait_links); - rv = 0; - - if (me->flags & _ST_FL_TIMEDOUT) { - me->flags &= ~_ST_FL_TIMEDOUT; - errno = ETIME; - rv = -1; - } - if (me->flags & _ST_FL_INTERRUPT) { - me->flags &= ~_ST_FL_INTERRUPT; - errno = EINTR; - rv = -1; - } - - return rv; + _st_thread_t *me = _ST_CURRENT_THREAD(); + int rv; + + if (me->flags & _ST_FL_INTERRUPT) { + me->flags &= ~_ST_FL_INTERRUPT; + errno = EINTR; + return -1; + } + + /* Put caller thread on the condition variable's wait queue */ + me->state = _ST_ST_COND_WAIT; + ST_APPEND_LINK(&me->wait_links, &cvar->wait_q); + + if (timeout != ST_UTIME_NO_TIMEOUT) { + _ST_ADD_SLEEPQ(me, timeout); + } + + _ST_SWITCH_CONTEXT(me); + + ST_REMOVE_LINK(&me->wait_links); + rv = 0; + + if (me->flags & _ST_FL_TIMEDOUT) { + me->flags &= ~_ST_FL_TIMEDOUT; + errno = ETIME; + rv = -1; + } + if (me->flags & _ST_FL_INTERRUPT) { + me->flags &= ~_ST_FL_INTERRUPT; + errno = EINTR; + rv = -1; + } + + return rv; } - int st_cond_wait(_st_cond_t *cvar) { - return st_cond_timedwait(cvar, ST_UTIME_NO_TIMEOUT); + return st_cond_timedwait(cvar, ST_UTIME_NO_TIMEOUT); } - static int _st_cond_signal(_st_cond_t *cvar, int broadcast) { - _st_thread_t *thread; - _st_clist_t *q; - - for (q = cvar->wait_q.next; q != &cvar->wait_q; q = q->next) { - thread = _ST_THREAD_WAITQ_PTR(q); - if (thread->state == _ST_ST_COND_WAIT) { - if (thread->flags & _ST_FL_ON_SLEEPQ) - _ST_DEL_SLEEPQ(thread); - - /* Make thread runnable */ - thread->state = _ST_ST_RUNNABLE; - _ST_ADD_RUNQ(thread); - if (!broadcast) - break; + _st_thread_t *thread; + _st_clist_t *q; + + for (q = cvar->wait_q.next; q != &cvar->wait_q; q = q->next) { + thread = _ST_THREAD_WAITQ_PTR(q); + if (thread->state == _ST_ST_COND_WAIT) { + if (thread->flags & _ST_FL_ON_SLEEPQ) { + _ST_DEL_SLEEPQ(thread); + } + + /* Make thread runnable */ + thread->state = _ST_ST_RUNNABLE; + _ST_ADD_RUNQ(thread); + if (!broadcast) { + break; + } + } } - } - - return 0; + + return 0; } - int st_cond_signal(_st_cond_t *cvar) { - return _st_cond_signal(cvar, 0); + return _st_cond_signal(cvar, 0); } - int st_cond_broadcast(_st_cond_t *cvar) { - return _st_cond_signal(cvar, 1); + return _st_cond_signal(cvar, 1); } - /***************************************** * Mutex functions */ - _st_mutex_t *st_mutex_new(void) { - _st_mutex_t *lock; - - lock = (_st_mutex_t *) calloc(1, sizeof(_st_mutex_t)); - if (lock) { - ST_INIT_CLIST(&lock->wait_q); - lock->owner = NULL; - } - - return lock; + _st_mutex_t *lock; + + lock = (_st_mutex_t *) calloc(1, sizeof(_st_mutex_t)); + if (lock) { + ST_INIT_CLIST(&lock->wait_q); + lock->owner = NULL; + } + + return lock; } - int st_mutex_destroy(_st_mutex_t *lock) { - if (lock->owner != NULL || lock->wait_q.next != &lock->wait_q) { - errno = EBUSY; - return -1; - } - - free(lock); - - return 0; + if (lock->owner != NULL || lock->wait_q.next != &lock->wait_q) { + errno = EBUSY; + return -1; + } + + free(lock); + + return 0; } - int st_mutex_lock(_st_mutex_t *lock) { - _st_thread_t *me = _ST_CURRENT_THREAD(); - - if (me->flags & _ST_FL_INTERRUPT) { - me->flags &= ~_ST_FL_INTERRUPT; - errno = EINTR; - return -1; - } - - if (lock->owner == NULL) { - /* Got the mutex */ - lock->owner = me; + _st_thread_t *me = _ST_CURRENT_THREAD(); + + if (me->flags & _ST_FL_INTERRUPT) { + me->flags &= ~_ST_FL_INTERRUPT; + errno = EINTR; + return -1; + } + + if (lock->owner == NULL) { + /* Got the mutex */ + lock->owner = me; + return 0; + } + + if (lock->owner == me) { + errno = EDEADLK; + return -1; + } + + /* Put caller thread on the mutex's wait queue */ + me->state = _ST_ST_LOCK_WAIT; + ST_APPEND_LINK(&me->wait_links, &lock->wait_q); + + _ST_SWITCH_CONTEXT(me); + + ST_REMOVE_LINK(&me->wait_links); + + if ((me->flags & _ST_FL_INTERRUPT) && lock->owner != me) { + me->flags &= ~_ST_FL_INTERRUPT; + errno = EINTR; + return -1; + } + return 0; - } - - if (lock->owner == me) { - errno = EDEADLK; - return -1; - } - - /* Put caller thread on the mutex's wait queue */ - me->state = _ST_ST_LOCK_WAIT; - ST_APPEND_LINK(&me->wait_links, &lock->wait_q); - - _ST_SWITCH_CONTEXT(me); - - ST_REMOVE_LINK(&me->wait_links); - - if ((me->flags & _ST_FL_INTERRUPT) && lock->owner != me) { - me->flags &= ~_ST_FL_INTERRUPT; - errno = EINTR; - return -1; - } - - return 0; } - int st_mutex_unlock(_st_mutex_t *lock) { - _st_thread_t *thread; - _st_clist_t *q; - - if (lock->owner != _ST_CURRENT_THREAD()) { - errno = EPERM; - return -1; - } - - for (q = lock->wait_q.next; q != &lock->wait_q; q = q->next) { - thread = _ST_THREAD_WAITQ_PTR(q); - if (thread->state == _ST_ST_LOCK_WAIT) { - lock->owner = thread; - /* Make thread runnable */ - thread->state = _ST_ST_RUNNABLE; - _ST_ADD_RUNQ(thread); - return 0; + _st_thread_t *thread; + _st_clist_t *q; + + if (lock->owner != _ST_CURRENT_THREAD()) { + errno = EPERM; + return -1; } - } - - /* No threads waiting on this mutex */ - lock->owner = NULL; - - return 0; + + for (q = lock->wait_q.next; q != &lock->wait_q; q = q->next) { + thread = _ST_THREAD_WAITQ_PTR(q); + if (thread->state == _ST_ST_LOCK_WAIT) { + lock->owner = thread; + /* Make thread runnable */ + thread->state = _ST_ST_RUNNABLE; + _ST_ADD_RUNQ(thread); + return 0; + } + } + + /* No threads waiting on this mutex */ + lock->owner = NULL; + + return 0; } - int st_mutex_trylock(_st_mutex_t *lock) { - if (lock->owner != NULL) { - errno = EBUSY; - return -1; - } - - /* Got the mutex */ - lock->owner = _ST_CURRENT_THREAD(); - - return 0; + if (lock->owner != NULL) { + errno = EBUSY; + return -1; + } + + /* Got the mutex */ + lock->owner = _ST_CURRENT_THREAD(); + + return 0; } From fa53250202230809c18a8863df5b86893d1670ad Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 6 Nov 2014 14:23:08 +0800 Subject: [PATCH 067/800] refine the macro, use int as version type. 2.0.8 --- trunk/research/st/srs.c | 6 +++ trunk/src/core/srs_core.hpp | 15 +++++-- trunk/src/libs/srs_librtmp.cpp | 6 +-- trunk/src/main/srs_main_server.cpp | 71 ++++++++++++++++-------------- 4 files changed, 58 insertions(+), 40 deletions(-) diff --git a/trunk/research/st/srs.c b/trunk/research/st/srs.c index 017caee9a7..00add8cc26 100644 --- a/trunk/research/st/srs.c +++ b/trunk/research/st/srs.c @@ -11,6 +11,12 @@ #include "public.h" +#if !defined(__ia64__) + #error "IA64 not supported" +#else + #error "Only IA64 supported" +#endif + #define srs_trace(msg, ...) printf(msg, ##__VA_ARGS__);printf("\n") int io_port = 1990; diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 25b19cb04b..720159efa3 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -29,10 +29,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // current release version -#define VERSION_MAJOR "2" -#define VERSION_MINOR "0" -#define VERSION_REVISION "7" -#define RTMP_SIG_SRS_VERSION VERSION_MAJOR"."VERSION_MINOR"."VERSION_REVISION +#define VERSION_MAJOR 2 +#define VERSION_MINOR 0 +#define VERSION_REVISION 8 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" @@ -46,6 +45,14 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define RTMP_SIG_SRS_PRIMARY_AUTHROS "winlin,wenjie.zhao" #define RTMP_SIG_SRS_CONTRIBUTORS_URL RTMP_SIG_SRS_URL"/blob/master/AUTHORS.txt" #define RTMP_SIG_SRS_HANDSHAKE RTMP_SIG_SRS_KEY"("RTMP_SIG_SRS_VERSION")" +#define RTMP_SIG_SRS_RELEASE "https://github.com/winlinvip/simple-rtmp-server/tree/1.0release" +#define RTMP_SIG_SRS_HTTP_SERVER "https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPServer#feature" +#define RTMP_SIG_SRS_VERSION __SRS_XSTR(VERSION_MAJOR)"."__SRS_XSTR(VERSION_MINOR)"."__SRS_XSTR(VERSION_REVISION) + +// internal macros, covert macro values to str, +// see: read https://gcc.gnu.org/onlinedocs/cpp/Stringification.html#Stringification +#define __SRS_XSTR(v) __SRS_STR(v) +#define __SRS_STR(v) #v /** * the core provides the common defined macros, utilities, diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index c42056c5d3..d1838f10e6 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -491,17 +491,17 @@ int srs_write_packet(srs_rtmp_t rtmp, int type, u_int32_t timestamp, char* data, int srs_version_major() { - return ::atoi(VERSION_MAJOR); + return VERSION_MAJOR; } int srs_version_minor() { - return ::atoi(VERSION_MINOR); + return VERSION_MINOR; } int srs_version_revision() { - return ::atoi(VERSION_REVISION); + return VERSION_REVISION; } int64_t srs_get_time_ms() diff --git a/trunk/src/main/srs_main_server.cpp b/trunk/src/main/srs_main_server.cpp index 6cef9d9ddd..7e6e269d9d 100644 --- a/trunk/src/main/srs_main_server.cpp +++ b/trunk/src/main/srs_main_server.cpp @@ -60,75 +60,87 @@ SrsServer* _srs_server = new SrsServer(); void show_macro_features() { #ifdef SRS_AUTO_SSL - srs_trace("rtmp handshake: on"); + srs_trace("check feature rtmp handshake: on"); #else - srs_warn("rtmp handshake: off"); + srs_warn("check feature rtmp handshake: off"); #endif #ifdef SRS_AUTO_HLS - srs_trace("hls: on"); + srs_trace("check feature hls: on"); #else - srs_warn("hls: off"); + srs_warn("check feature hls: off"); #endif #ifdef SRS_AUTO_HTTP_CALLBACK - srs_trace("http callback: on"); + srs_trace("check feature http callback: on"); #else - srs_warn("http callback: off"); + srs_warn("check feature http callback: off"); #endif #ifdef SRS_AUTO_HTTP_API - srs_trace("http api: on"); + srs_trace("check feature http api: on"); #else - srs_warn("http api: off"); + srs_warn("check feature http api: off"); #endif #ifdef SRS_AUTO_HTTP_SERVER - srs_trace("http server: on"); + srs_trace("check feature http server: on"); #else - srs_warn("http server: off"); + srs_warn("check feature http server: off"); #endif #ifdef SRS_AUTO_HTTP_PARSER - srs_trace("http parser: on"); + srs_trace("check feature http parser: on"); #else - srs_warn("http parser: off"); + srs_warn("check feature http parser: off"); #endif #ifdef SRS_AUTO_DVR - srs_trace("dvr: on"); + srs_trace("check feature dvr: on"); #else - srs_warn("dvr: off"); + srs_warn("check feature dvr: off"); #endif #ifdef SRS_AUTO_TRANSCODE - srs_trace("transcode: on"); + srs_trace("check feature transcode: on"); #else - srs_warn("transcode: off"); + srs_warn("check feature transcode: off"); #endif #ifdef SRS_AUTO_INGEST - srs_trace("ingest: on"); + srs_trace("check feature ingest: on"); #else - srs_warn("ingest: off"); + srs_warn("check feature ingest: off"); #endif #ifdef SRS_AUTO_STAT - srs_trace("system stat: on"); + srs_trace("check feature system stat: on"); #else - srs_warn("system stat: off"); + srs_warn("check feature system stat: off"); #endif #ifdef SRS_AUTO_NGINX - srs_trace("compile nginx: on"); + srs_trace("check feature compile nginx: on"); #else - srs_warn("compile nginx: off"); + srs_warn("check feature compile nginx: off"); #endif #ifdef SRS_AUTO_FFMPEG_TOOL - srs_trace("compile ffmpeg: on"); + srs_trace("check feature compile ffmpeg: on"); #else - srs_warn("compile ffmpeg: off"); + srs_warn("check feature compile ffmpeg: off"); +#endif +} + +void check_macro_features() +{ + // for special features. +#ifdef SRS_AUTO_HTTP_SERVER + srs_warn("http server is dev feature, @see %s", RTMP_SIG_SRS_HTTP_SERVER); +#endif + +#if VERSION_MAJOR > 1 + srs_warn("SRS %s is develop branch, please use %s instead", RTMP_SIG_SRS_VERSION, RTMP_SIG_SRS_RELEASE); #endif } @@ -149,14 +161,12 @@ int main(int argc, char** argv) ProfilerStart("gperf.srs.gcp"); #endif -#ifdef SRS_AUTO_GPERF_MC - #ifdef SRS_AUTO_GPERF_MP +#if defined(SRS_AUTO_GPERF_MC) && defined(SRS_AUTO_GPERF_MP) srs_error("option --with-gmc confict with --with-gmp, " "@see: http://google-perftools.googlecode.com/svn/trunk/doc/heap_checker.html\n" "Note that since the heap-checker uses the heap-profiling framework internally, " "it is not possible to run both the heap-checker and heap profiler at the same time"); return -1; - #endif #endif // never use srs log(srs_trace, srs_error, etc) before config parse the option, @@ -190,12 +200,7 @@ int main(int argc, char** argv) // features show_macro_features(); - - // for special features. -#ifdef SRS_AUTO_HTTP_SERVER - srs_warn("http server is dev feature, " - "@see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPServer#feature"); -#endif + check_macro_features(); /** * we do nothing in the constructor of server, From 5fd308c64e89a8c58f45fa77909ed0c90a6682f8 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 6 Nov 2014 15:18:10 +0800 Subject: [PATCH 068/800] update code, warning when compile dev branch. --- trunk/research/st/srs.c | 4 +--- trunk/src/main/srs_main_server.cpp | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/trunk/research/st/srs.c b/trunk/research/st/srs.c index 00add8cc26..c659940899 100644 --- a/trunk/research/st/srs.c +++ b/trunk/research/st/srs.c @@ -11,10 +11,8 @@ #include "public.h" -#if !defined(__ia64__) +#if defined(__ia64__) #error "IA64 not supported" -#else - #error "Only IA64 supported" #endif #define srs_trace(msg, ...) printf(msg, ##__VA_ARGS__);printf("\n") diff --git a/trunk/src/main/srs_main_server.cpp b/trunk/src/main/srs_main_server.cpp index 7e6e269d9d..df489cffbd 100644 --- a/trunk/src/main/srs_main_server.cpp +++ b/trunk/src/main/srs_main_server.cpp @@ -140,6 +140,7 @@ void check_macro_features() #endif #if VERSION_MAJOR > 1 + #warning "using develop SRS, please use release instead." srs_warn("SRS %s is develop branch, please use %s instead", RTMP_SIG_SRS_VERSION, RTMP_SIG_SRS_RELEASE); #endif } From d67454ecc2b3537afdcbe4c2e0b11338e1d47fee Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 6 Nov 2014 15:43:28 +0800 Subject: [PATCH 069/800] research st: only support linux --- trunk/research/st/md.h | 664 +++++++++++------------------------------ 1 file changed, 178 insertions(+), 486 deletions(-) diff --git a/trunk/research/st/md.h b/trunk/research/st/md.h index 3b6acc5a5f..90fe305296 100644 --- a/trunk/research/st/md.h +++ b/trunk/research/st/md.h @@ -57,528 +57,220 @@ /***************************************** * Platform specifics */ -#if defined (AIX) - #define MD_STACK_GROWS_DOWN - #define MD_USE_SYSV_ANON_MMAP - #define MD_ACCEPT_NB_INHERITED - #define MD_ALWAYS_UNSERIALIZED_ACCEPT - - #ifndef MD_HAVE_SOCKLEN_T - #define MD_HAVE_SOCKLEN_T - #define socklen_t unsigned long - #endif - - #define MD_SETJMP(env) _setjmp(env) - #define MD_LONGJMP(env, val) _longjmp(env, val) - - #define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - (_thread)->context[3] = (long) (_sp); \ - ST_END_MACRO - - #define MD_GET_UTIME() \ - timebasestruct_t rt; \ - (void) read_real_time(&rt, TIMEBASE_SZ); \ - (void) time_base_to_time(&rt, TIMEBASE_SZ); \ - return (rt.tb_high * 1000000LL + rt.tb_low / 1000) - +#if defined (LINUX) + /* linux ok, defined bellow */ +#elif defined (AIX) + #error "AIX not supported" #elif defined (CYGWIN) - #define MD_STACK_GROWS_DOWN - #define MD_USE_BSD_ANON_MMAP - #define MD_ACCEPT_NB_NOT_INHERITED - #define MD_ALWAYS_UNSERIALIZED_ACCEPT - - #define MD_SETJMP(env) setjmp(env) - #define MD_LONGJMP(env, val) longjmp(env, val) - - #define MD_JB_SP 7 - - #define MD_GET_SP(_t) (_t)->context[MD_JB_SP] - - #define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - MD_GET_SP(_thread) = (long) (_sp); \ - ST_END_MACRO - - #define MD_GET_UTIME() \ - struct timeval tv; \ - (void) gettimeofday(&tv, NULL); \ - return (tv.tv_sec * 1000000LL + tv.tv_usec) - + #error "CYGWIN not supported" #elif defined (DARWIN) - #define MD_STACK_GROWS_DOWN - #define MD_USE_BSD_ANON_MMAP - #define MD_ACCEPT_NB_INHERITED - #define MD_ALWAYS_UNSERIALIZED_ACCEPT - #define MD_HAVE_SOCKLEN_T - - #define MD_SETJMP(env) _setjmp(env) - #define MD_LONGJMP(env, val) _longjmp(env, val) - - #if defined(__ppc__) - #define MD_JB_SP 0 - #elif defined(__i386__) - #define MD_JB_SP 9 - #elif defined(__x86_64__) - #define MD_JB_SP 4 - #else - #error Unknown CPU architecture - #endif + #error "DARWIN not supported" +#elif defined (FREEBSD) + #error "FREEBSD not supported" +#elif defined (HPUX) + #error "HPUX not supported" +#elif defined (IRIX) + #error "IRIX not supported" +#elif defined (NETBSD) + #error "IRIX not supported" +#elif defined (OPENBSD) + #error "IRIX not supported" +#elif defined (OSF1) + #error "IRIX not supported" +#elif defined (SOLARIS) + #error "IRIX not supported" +#else + #error "Unknown OS" +#endif /* OS */ - #define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - *((long *)&((_thread)->context[MD_JB_SP])) = (long) (_sp); \ - ST_END_MACRO +/* linux only, defined bellow */ +/* + * These are properties of the linux kernel and are the same on every + * flavor and architecture. + */ +#define MD_USE_BSD_ANON_MMAP +#define MD_ACCEPT_NB_NOT_INHERITED +#define MD_ALWAYS_UNSERIALIZED_ACCEPT +/* + * Modern GNU/Linux is Posix.1g compliant. + */ +#define MD_HAVE_SOCKLEN_T - #define MD_GET_UTIME() \ - struct timeval tv; \ - (void) gettimeofday(&tv, NULL); \ - return (tv.tv_sec * 1000000LL + tv.tv_usec) +/* + * All architectures and flavors of linux have the gettimeofday + * function but if you know of a faster way, use it. + */ +#define MD_GET_UTIME() \ + struct timeval tv; \ + (void) gettimeofday(&tv, NULL); \ + return (tv.tv_sec * 1000000LL + tv.tv_usec) -#elif defined (FREEBSD) +#if defined(__ia64__) #define MD_STACK_GROWS_DOWN - #define MD_USE_BSD_ANON_MMAP - #define MD_ACCEPT_NB_INHERITED - #define MD_ALWAYS_UNSERIALIZED_ACCEPT - #define MD_SETJMP(env) _setjmp(env) - #define MD_LONGJMP(env, val) _longjmp(env, val) - - #if defined(__i386__) - #define MD_JB_SP 2 - #elif defined(__alpha__) - #define MD_JB_SP 34 - #elif defined(__amd64__) - #define MD_JB_SP 2 - #else - #error Unknown CPU architecture + /* + * IA-64 architecture. Besides traditional memory call stack, IA-64 + * uses general register stack. Thus each thread needs a backing store + * for register stack in addition to memory stack. Standard + * setjmp()/longjmp() cannot be used for thread context switching + * because their implementation implicitly assumes that only one + * register stack exists. + */ + #ifdef USE_LIBC_SETJMP + #undef USE_LIBC_SETJMP #endif + #define MD_USE_BUILTIN_SETJMP - #define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - (_thread)->context[0]._jb[MD_JB_SP] = (long) (_sp); \ + #define MD_STACK_PAD_SIZE 128 + /* Last register stack frame must be preserved */ + #define MD_INIT_CONTEXT(_thread, _sp, _bsp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + memcpy((char *)(_bsp) - MD_STACK_PAD_SIZE, \ + (char *)(_thread)->context[0].__jmpbuf[17] - MD_STACK_PAD_SIZE, \ + MD_STACK_PAD_SIZE); \ + (_thread)->context[0].__jmpbuf[0] = (long) (_sp); \ + (_thread)->context[0].__jmpbuf[17] = (long) (_bsp); \ ST_END_MACRO - - #define MD_GET_UTIME() \ - struct timeval tv; \ - (void) gettimeofday(&tv, NULL); \ - return (tv.tv_sec * 1000000LL + tv.tv_usec) - -#elif defined (HPUX) - #define MD_STACK_GROWS_UP - #define MD_USE_BSD_ANON_MMAP - #define MD_ACCEPT_NB_INHERITED - #define MD_ALWAYS_UNSERIALIZED_ACCEPT - - #define MD_SETJMP(env) _setjmp(env) - #define MD_LONGJMP(env, val) _longjmp(env, val) - - #ifndef __LP64__ - /* 32-bit mode (ILP32 data model) */ - #define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - ((long *)((_thread)->context))[1] = (long) (_sp); \ - ST_END_MACRO - #else - /* 64-bit mode (LP64 data model) */ - #define MD_STACK_PAD_SIZE 256 - /* Last stack frame must be preserved */ - #define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - memcpy((char *)(_sp) - MD_STACK_PAD_SIZE, \ - ((char **)((_thread)->context))[1] - MD_STACK_PAD_SIZE, \ - MD_STACK_PAD_SIZE); \ - ((long *)((_thread)->context))[1] = (long) (_sp); \ - ST_END_MACRO - #endif /* !__LP64__ */ - - #define MD_GET_UTIME() \ - struct timeval tv; \ - (void) gettimeofday(&tv, NULL); \ - return (tv.tv_sec * 1000000LL + tv.tv_usec) - -#elif defined (IRIX) - #include - +#elif defined(__mips__) #define MD_STACK_GROWS_DOWN - #define MD_USE_SYSV_ANON_MMAP - #define MD_ACCEPT_NB_INHERITED - #define MD_ALWAYS_UNSERIALIZED_ACCEPT - #define MD_SETJMP(env) setjmp(env) - #define MD_LONGJMP(env, val) longjmp(env, val) - - #define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - (void) MD_SETJMP((_thread)->context); \ - (_thread)->context[JB_SP] = (long) (_sp); \ - (_thread)->context[JB_PC] = (long) _main; \ + #define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + MD_SETJMP((_thread)->context); \ + _thread->context[0].__jmpbuf[0].__pc = (__ptr_t) _main; \ + _thread->context[0].__jmpbuf[0].__sp = _sp; \ ST_END_MACRO - - #define MD_GET_UTIME() \ - static int inited = 0; \ - static clockid_t clock_id = CLOCK_SGI_CYCLE; \ - struct timespec ts; \ - if (!inited) { \ - if (syssgi(SGI_CYCLECNTR_SIZE) < 64) \ - clock_id = CLOCK_REALTIME; \ - inited = 1; \ - } \ - (void) clock_gettime(clock_id, &ts); \ - return (ts.tv_sec * 1000000LL + ts.tv_nsec / 1000) - +#else /* Not IA-64 or mips */ /* - * Cap the stack by zeroing out the saved return address register - * value. This allows libexc, used by SpeedShop, to know when to stop - * backtracing since it won't find main, start, or any other known - * stack root function in a state thread's stack. Without this libexc - * traces right off the stack and crashes. - * The function preamble stores ra at 8(sp), this stores zero there. - * N.B. This macro is compiler/ABI dependent. It must change if ANY more - * automatic variables are added to the _st_thread_main() routine, because - * the address where ra is stored will change. + * On linux, there are a few styles of jmpbuf format. These vary based + * on architecture/glibc combination. + * + * Most of the glibc based toggles were lifted from: + * mozilla/nsprpub/pr/include/md/_linux.h */ - #if !defined(__GNUC__) && defined(_MIPS_SIM) && _MIPS_SIM != _ABIO32 - #define MD_CAP_STACK(var_addr) \ - (((volatile __uint64_t *)(var_addr))[1] = 0) - #endif - -#elif defined (LINUX) /* - * These are properties of the linux kernel and are the same on every - * flavor and architecture. + * Starting with glibc 2.4, JB_SP definitions are not public anymore. + * They, however, can still be found in glibc source tree in + * architecture-specific "jmpbuf-offsets.h" files. + * Most importantly, the content of jmp_buf is mangled by setjmp to make + * it completely opaque (the mangling can be disabled by setting the + * LD_POINTER_GUARD environment variable before application execution). + * Therefore we will use built-in _st_md_cxt_save/_st_md_cxt_restore + * functions as a setjmp/longjmp replacement wherever they are available + * unless USE_LIBC_SETJMP is defined. */ - #define MD_USE_BSD_ANON_MMAP - #define MD_ACCEPT_NB_NOT_INHERITED - #define MD_ALWAYS_UNSERIALIZED_ACCEPT - /* - * Modern GNU/Linux is Posix.1g compliant. - */ - #define MD_HAVE_SOCKLEN_T + #if defined(__powerpc__) + #define MD_STACK_GROWS_DOWN - /* - * All architectures and flavors of linux have the gettimeofday - * function but if you know of a faster way, use it. - */ - #define MD_GET_UTIME() \ - struct timeval tv; \ - (void) gettimeofday(&tv, NULL); \ - return (tv.tv_sec * 1000000LL + tv.tv_usec) - - #if defined(__ia64__) + #if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1) + #ifndef JB_GPR1 + #define JB_GPR1 0 + #endif + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_GPR1] + #else + /* not an error but certainly cause for caution */ + #error "Untested use of old glibc on powerpc" + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__misc[0] + #endif /* glibc 2.1 or later */ + #elif defined(__alpha) #define MD_STACK_GROWS_DOWN - - /* - * IA-64 architecture. Besides traditional memory call stack, IA-64 - * uses general register stack. Thus each thread needs a backing store - * for register stack in addition to memory stack. Standard - * setjmp()/longjmp() cannot be used for thread context switching - * because their implementation implicitly assumes that only one - * register stack exists. - */ - #ifdef USE_LIBC_SETJMP - #undef USE_LIBC_SETJMP - #endif - #define MD_USE_BUILTIN_SETJMP - #define MD_STACK_PAD_SIZE 128 - /* Last register stack frame must be preserved */ - #define MD_INIT_CONTEXT(_thread, _sp, _bsp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - memcpy((char *)(_bsp) - MD_STACK_PAD_SIZE, \ - (char *)(_thread)->context[0].__jmpbuf[17] - MD_STACK_PAD_SIZE, \ - MD_STACK_PAD_SIZE); \ - (_thread)->context[0].__jmpbuf[0] = (long) (_sp); \ - (_thread)->context[0].__jmpbuf[17] = (long) (_bsp); \ - ST_END_MACRO - #elif defined(__mips__) + #if defined(__GLIBC__) && __GLIBC__ >= 2 + #ifndef JB_SP + #define JB_SP 8 + #endif + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_SP] + #else + /* not an error but certainly cause for caution */ + #error "Untested use of old glibc on alpha" + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__sp + #endif + #elif defined(__mc68000__) #define MD_STACK_GROWS_DOWN - #define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - MD_SETJMP((_thread)->context); \ - _thread->context[0].__jmpbuf[0].__pc = (__ptr_t) _main; \ - _thread->context[0].__jmpbuf[0].__sp = _sp; \ - ST_END_MACRO - #else /* Not IA-64 or mips */ - /* - * On linux, there are a few styles of jmpbuf format. These vary based - * on architecture/glibc combination. - * - * Most of the glibc based toggles were lifted from: - * mozilla/nsprpub/pr/include/md/_linux.h - */ - /* - * Starting with glibc 2.4, JB_SP definitions are not public anymore. - * They, however, can still be found in glibc source tree in - * architecture-specific "jmpbuf-offsets.h" files. - * Most importantly, the content of jmp_buf is mangled by setjmp to make - * it completely opaque (the mangling can be disabled by setting the - * LD_POINTER_GUARD environment variable before application execution). - * Therefore we will use built-in _st_md_cxt_save/_st_md_cxt_restore - * functions as a setjmp/longjmp replacement wherever they are available - * unless USE_LIBC_SETJMP is defined. - */ - #if defined(__powerpc__) - #define MD_STACK_GROWS_DOWN - - #if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1) - #ifndef JB_GPR1 - #define JB_GPR1 0 - #endif - #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_GPR1] - #else - /* not an error but certainly cause for caution */ - #error "Untested use of old glibc on powerpc" - #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__misc[0] - #endif /* glibc 2.1 or later */ - #elif defined(__alpha) - #define MD_STACK_GROWS_DOWN + /* m68k still uses old style sigjmp_buf */ + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__sp + #elif defined(__sparc__) + #define MD_STACK_GROWS_DOWN + + #if defined(__GLIBC__) && __GLIBC__ >= 2 + #ifndef JB_SP + #define JB_SP 0 + #endif + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_SP] + #else + /* not an error but certainly cause for caution */ + #error "Untested use of old glic on sparc -- also using odd mozilla derived __fp" + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__fp + #endif + #elif defined(__i386__) + #define MD_STACK_GROWS_DOWN + #define MD_USE_BUILTIN_SETJMP - #if defined(__GLIBC__) && __GLIBC__ >= 2 - #ifndef JB_SP - #define JB_SP 8 - #endif - #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_SP] - #else - /* not an error but certainly cause for caution */ - #error "Untested use of old glibc on alpha" - #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__sp + #if defined(__GLIBC__) && __GLIBC__ >= 2 + #ifndef JB_SP + #define JB_SP 4 #endif - #elif defined(__mc68000__) - #define MD_STACK_GROWS_DOWN - - /* m68k still uses old style sigjmp_buf */ + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_SP] + #else + /* not an error but certainly cause for caution */ + #error "Untested use of old glibc on i386" #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__sp - #elif defined(__sparc__) - #define MD_STACK_GROWS_DOWN + #endif + #elif defined(__amd64__) || defined(__x86_64__) + #define MD_STACK_GROWS_DOWN + #define MD_USE_BUILTIN_SETJMP - #if defined(__GLIBC__) && __GLIBC__ >= 2 - #ifndef JB_SP - #define JB_SP 0 - #endif - #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_SP] - #else - /* not an error but certainly cause for caution */ - #error "Untested use of old glic on sparc -- also using odd mozilla derived __fp" - #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__fp - #endif - #elif defined(__i386__) - #define MD_STACK_GROWS_DOWN - #define MD_USE_BUILTIN_SETJMP - - #if defined(__GLIBC__) && __GLIBC__ >= 2 - #ifndef JB_SP - #define JB_SP 4 - #endif - #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_SP] - #else - /* not an error but certainly cause for caution */ - #error "Untested use of old glibc on i386" - #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__sp - #endif - #elif defined(__amd64__) || defined(__x86_64__) - #define MD_STACK_GROWS_DOWN - #define MD_USE_BUILTIN_SETJMP - - #ifndef JB_RSP - #define JB_RSP 6 - #endif - #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_RSP] - #elif defined(__arm__) - #define MD_STACK_GROWS_DOWN - - #if defined(__GLIBC__) && __GLIBC__ >= 2 - #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[8] - #else - #error "ARM/Linux pre-glibc2 not supported yet" - #endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */ - #elif defined(__s390__) - #define MD_STACK_GROWS_DOWN - - /* There is no JB_SP in glibc at this time. (glibc 2.2.5) - */ - #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__gregs[9] - #elif defined(__hppa__) - #define MD_STACK_GROWS_UP - - /* yes, this is gross, unfortunately at the moment (2002/08/01) there is - * a bug in hppa's glibc header definition for JB_SP, so we can't - * use that... - */ - #define MD_GET_SP(_t) (*(long *)(((char *)&(_t)->context[0].__jmpbuf[0]) + 76)) + #ifndef JB_RSP + #define JB_RSP 6 + #endif + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_RSP] + #elif defined(__arm__) + #define MD_STACK_GROWS_DOWN + + #if defined(__GLIBC__) && __GLIBC__ >= 2 + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[8] #else - #error "Unknown CPU architecture" - #endif /* Cases with common MD_INIT_CONTEXT and different SP locations */ + #error "ARM/Linux pre-glibc2 not supported yet" + #endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */ + #elif defined(__s390__) + #define MD_STACK_GROWS_DOWN - #define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - MD_GET_SP(_thread) = (long) (_sp); \ - ST_END_MACRO - #endif /* Cases with different MD_INIT_CONTEXT */ - - #if defined(MD_USE_BUILTIN_SETJMP) && !defined(USE_LIBC_SETJMP) - #define MD_SETJMP(env) _st_md_cxt_save(env) - #define MD_LONGJMP(env, val) _st_md_cxt_restore(env, val) + /* There is no JB_SP in glibc at this time. (glibc 2.2.5) + */ + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__gregs[9] + #elif defined(__hppa__) + #define MD_STACK_GROWS_UP - extern int _st_md_cxt_save(jmp_buf env); - extern void _st_md_cxt_restore(jmp_buf env, int val); - #else - #define MD_SETJMP(env) setjmp(env) - #define MD_LONGJMP(env, val) longjmp(env, val) - #endif - -#elif defined (NETBSD) - #define MD_STACK_GROWS_DOWN - #define MD_USE_BSD_ANON_MMAP - #define MD_ACCEPT_NB_INHERITED - #define MD_ALWAYS_UNSERIALIZED_ACCEPT - #define MD_HAVE_SOCKLEN_T - - #define MD_SETJMP(env) _setjmp(env) - #define MD_LONGJMP(env, val) _longjmp(env, val) - - #if defined(__i386__) - #define MD_JB_SP 2 - #elif defined(__alpha__) - #define MD_JB_SP 34 - #elif defined(__sparc__) - #define MD_JB_SP 0 - #elif defined(__vax__) - #define MD_JB_SP 2 - #else - #error Unknown CPU architecture - #endif - - #define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - (_thread)->context[MD_JB_SP] = (long) (_sp); \ - ST_END_MACRO - - #define MD_GET_UTIME() \ - struct timeval tv; \ - (void) gettimeofday(&tv, NULL); \ - return (tv.tv_sec * 1000000LL + tv.tv_usec) - -#elif defined (OPENBSD) - #define MD_STACK_GROWS_DOWN - #define MD_USE_BSD_ANON_MMAP - #define MD_ACCEPT_NB_INHERITED - #define MD_ALWAYS_UNSERIALIZED_ACCEPT - - #define MD_SETJMP(env) _setjmp(env) - #define MD_LONGJMP(env, val) _longjmp(env, val) - - #if defined(__i386__) - #define MD_JB_SP 2 - #elif defined(__alpha__) - #define MD_JB_SP 34 - #elif defined(__sparc__) - #define MD_JB_SP 0 - #elif defined(__amd64__) - #define MD_JB_SP 6 + /* yes, this is gross, unfortunately at the moment (2002/08/01) there is + * a bug in hppa's glibc header definition for JB_SP, so we can't + * use that... + */ + #define MD_GET_SP(_t) (*(long *)(((char *)&(_t)->context[0].__jmpbuf[0]) + 76)) #else - #error Unknown CPU architecture - #endif - - #define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - (_thread)->context[MD_JB_SP] = (long) (_sp); \ - ST_END_MACRO - - #define MD_GET_UTIME() \ - struct timeval tv; \ - (void) gettimeofday(&tv, NULL); \ - return (tv.tv_sec * 1000000LL + tv.tv_usec) - -#elif defined (OSF1) - #include - - #define MD_STACK_GROWS_DOWN - #define MD_USE_SYSV_ANON_MMAP - #define MD_ACCEPT_NB_NOT_INHERITED - #define MD_ALWAYS_UNSERIALIZED_ACCEPT + #error "Unknown CPU architecture" + #endif /* Cases with common MD_INIT_CONTEXT and different SP locations */ - #define MD_SETJMP(env) _setjmp(env) - #define MD_LONGJMP(env, val) _longjmp(env, val) - - #define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - ((struct sigcontext *)((_thread)->context))->sc_sp = (long) (_sp); \ + #define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + MD_GET_SP(_thread) = (long) (_sp); \ ST_END_MACRO - - #define MD_GET_UTIME() \ - struct timeval tv; \ - (void) gettimeofday(&tv, NULL); \ - return (tv.tv_sec * 1000000LL + tv.tv_usec) +#endif /* Cases with different MD_INIT_CONTEXT */ -#elif defined (SOLARIS) - #include - extern int getpagesize(void); - - #define MD_STACK_GROWS_DOWN - #define MD_USE_SYSV_ANON_MMAP - #define MD_ACCEPT_NB_NOT_INHERITED +#if defined(MD_USE_BUILTIN_SETJMP) && !defined(USE_LIBC_SETJMP) + #define MD_SETJMP(env) _st_md_cxt_save(env) + #define MD_LONGJMP(env, val) _st_md_cxt_restore(env, val) + extern int _st_md_cxt_save(jmp_buf env); + extern void _st_md_cxt_restore(jmp_buf env, int val); +#else #define MD_SETJMP(env) setjmp(env) #define MD_LONGJMP(env, val) longjmp(env, val) - - #if defined(sparc) || defined(__sparc) - #ifdef _LP64 - #define MD_STACK_PAD_SIZE 4095 - #endif - #define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - (void) MD_SETJMP((_thread)->context); \ - (_thread)->context[1] = (long) (_sp); \ - (_thread)->context[2] = (long) _main; \ - ST_END_MACRO - #elif defined(i386) || defined(__i386) - #define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - (void) MD_SETJMP((_thread)->context); \ - (_thread)->context[4] = (long) (_sp); \ - (_thread)->context[5] = (long) _main; \ - ST_END_MACRO - #elif defined(__amd64__) - #define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - (_thread)->context[6] = (long) (_sp); \ - ST_END_MACRO - #else - #error Unknown CPU architecture - #endif - - #define MD_GET_UTIME() \ - return (gethrtime() / 1000) - -#else - #error Unknown OS -#endif /* OS */ +#endif /***************************************** * Other defines From 0f41e0d26f1d0c6230cab48202a7f0499688dafa Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 6 Nov 2014 15:45:31 +0800 Subject: [PATCH 070/800] research st: only support linux --- trunk/research/st/md.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/trunk/research/st/md.h b/trunk/research/st/md.h index 90fe305296..5660d23a3e 100644 --- a/trunk/research/st/md.h +++ b/trunk/research/st/md.h @@ -72,13 +72,13 @@ #elif defined (IRIX) #error "IRIX not supported" #elif defined (NETBSD) - #error "IRIX not supported" + #error "NETBSD not supported" #elif defined (OPENBSD) - #error "IRIX not supported" + #error "OPENBSD not supported" #elif defined (OSF1) - #error "IRIX not supported" + #error "OSF1 not supported" #elif defined (SOLARIS) - #error "IRIX not supported" + #error "SOLARIS not supported" #else #error "Unknown OS" #endif /* OS */ From 1db69b153ea08f982a37df07eb83ad9c5efd97de Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 7 Nov 2014 16:47:49 +0800 Subject: [PATCH 071/800] update the research of arm --- trunk/research/arm/jmp.cpp | 34 +++++------ trunk/research/arm/jmp_sp.cpp | 109 +++++++++++++++++++++++++++++----- 2 files changed, 110 insertions(+), 33 deletions(-) diff --git a/trunk/research/arm/jmp.cpp b/trunk/research/arm/jmp.cpp index d9469405dc..7f6606cd43 100644 --- a/trunk/research/arm/jmp.cpp +++ b/trunk/research/arm/jmp.cpp @@ -1,48 +1,48 @@ /* # see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLinuxArm - arm-linux-gnueabi-g++ -o jmp jmp.cpp -static - arm-linux-gnueabi-strip jmp + g++ -g -O0 -o jmp jmp.cpp + arm-linux-gnueabi-g++ -o jmp jmp.cpp -static + arm-linux-gnueabi-strip jmp */ #include #include +#include #include +bool func1_ok = false, func2_ok = false; jmp_buf env_func1, env_func2; int sum = 0; void func1() { int ret = setjmp(env_func1); - printf("setjmp func1 ret=%d\n", ret); + printf("[func1] setjmp ret=%d, sum++=%d\n", ret, sum++); + func1_ok = true; - if (sum <= 0) { - return; - } - - if (sum++ > 1000) { - return; - } + sleep(1); // jmp to func2 - longjmp(env_func2, 3); + if (func2_ok) { + longjmp(env_func2, 1); + } } void func2() { int ret = setjmp(env_func2); - printf("setjmp func2 ret=%d\n", ret); + printf("[func2] setjmp ret=%d, sum++=%d\n", ret, sum++); + func2_ok = true; - if (sum <= 0) { - return; - } + sleep(1); // jmp to func1 - longjmp(env_func1, 2); + if (func1_ok) { + longjmp(env_func1, 2); + } } int main(int argc, char** argv) { printf("hello, setjmp/longjmp!\n"); func1(); - sum++; func2(); printf("jmp finished, sum=%d\n", sum); return 0; diff --git a/trunk/research/arm/jmp_sp.cpp b/trunk/research/arm/jmp_sp.cpp index 1fb0be89de..b39695c2fc 100644 --- a/trunk/research/arm/jmp_sp.cpp +++ b/trunk/research/arm/jmp_sp.cpp @@ -1,39 +1,116 @@ /* # see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLinuxArm - arm-linux-gnueabi-g++ -g -o jmp_sp jmp_sp.cpp -static - arm-linux-gnueabi-strip jmp_sp + g++ -g -O0 -o jmp_sp jmp_sp.cpp + arm-linux-gnueabi-g++ -g -o jmp_sp jmp_sp.cpp -static + arm-linux-gnueabi-strip jmp_sp */ #include #include #include -int main(int argc, char** argv) { +jmp_buf env_func1; + +void func1() +{ #if defined(__amd64__) || defined(__x86_64__) - printf("x86_64 sizeof(long int)=%d, sizeof(long)=%d, sizeof(int)=%d\n", (int)sizeof(long int), (int)sizeof(long), (int)sizeof(int)); -#else - printf("arm sizeof(long int)=%d, sizeof(long)=%d, sizeof(int)=%d\n", (int)sizeof(long int), (int)sizeof(long), (int)sizeof(int)); -#endif - - jmp_buf env; + register long int rsp0 asm("rsp"); + printf("in func1, rsp=%#lx, longjmp to func0\n", rsp0); - int ret = setjmp(env); - printf("setjmp func1 ret=%d\n", ret); + longjmp(env_func1, 0x101); + printf("func1 terminated\n"); +#endif +} + +void func0(int* pv0, int* pv1) +{ + /** + the definition of jmp_buf: + typedef struct __jmp_buf_tag jmp_buf[1]; + struct __jmp_buf_tag { + __jmp_buf __jmpbuf; + int __mask_was_saved; + __sigset_t __saved_mask; + }; + */ #if defined(__amd64__) || defined(__x86_64__) - // typedef lint64_t __jmp_buf[8]; + /** + here, the __jmp_buf is 8*8=64 bytes + # if __WORDSIZE == 64 + typedef long int __jmp_buf[8]; + */ + /** + the layout for setjmp of x86_64 + # + # The jmp_buf is assumed to contain the following, in order: + # %rbx + # %rsp (post-return) + # %rbp + # %r12 + # %r13 + # %r14 + # %r15 + # + # + */ + register long int rsp0 asm("rsp"); + + int ret = setjmp(env_func1); + printf("setjmp func0 ret=%d, rsp=%#lx\n", ret, rsp0); + printf("after setjmp: "); for (int i = 0; i < 8; i++) { - printf("env[%d]=%#x, ", i, (int)env[0].__jmpbuf[i]); + printf("env[%d]=%#x, ", i, (int)env_func1[0].__jmpbuf[i]); } printf("\n"); #else - // typedef int32_t __jmp_buf[64] __attribute__((__aligned__ (8))); + /** + /usr/arm-linux-gnueabi/include/bits/setjmp.h + #ifndef _ASM + The exact set of registers saved may depend on the particular core + in use, as some coprocessor registers may need to be saved. The C + Library ABI requires that the buffer be 8-byte aligned, and + recommends that the buffer contain 64 words. The first 28 words + are occupied by v1-v6, sl, fp, sp, pc, d8-d15, and fpscr. (Note + that d8-15 require 17 words, due to the use of fstmx.) + typedef int __jmp_buf[64] __attribute__((__aligned__ (8))); + + the layout of setjmp for arm: + 0-5: v1-v6 + 6: sl + 7: fp + 8: sp + 9: pc + 10-26: d8-d15 17words + 27: fpscr + */ + int ret = setjmp(env_func1); + printf("setjmp func1 ret=%d\n", ret); + printf("after setjmp: "); for (int i = 0; i < 64; i++) { - printf("env[%d]=%#x, ", i, (int)env[0].__jmpbuf[i]); + printf("env[%d]=%#x, ", i, (int)env_func1[0].__jmpbuf[i]); } - printf("\n"); + + printf("func0 terminated\n"); #endif +} + +int main(int argc, char** argv) { +#if defined(__amd64__) || defined(__x86_64__) + printf("x86_64 sizeof(long int)=%d, sizeof(long)=%d, sizeof(int)=%d, __WORDSIZE=%d\n", + (int)sizeof(long int), (int)sizeof(long), (int)sizeof(int), (int)__WORDSIZE); +#else + printf("arm sizeof(long int)=%d, sizeof(long)=%d, sizeof(int)=%d\n", + (int)sizeof(long int), (int)sizeof(long), (int)sizeof(int)); +#endif + + int pv0 = 0xf0; + int pv1 = 0xf1; + func0(&pv0, &pv1); + func1(); + + printf("terminated\n"); return 0; } From b964a6c534333685460437773b6131f584f041bd Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 7 Nov 2014 21:06:44 +0800 Subject: [PATCH 072/800] update for bug #66, add the srs-librtmp sample for publish h.264 raw stream. --- trunk/research/arm/jmp_sp.cpp | 34 ++++++-- trunk/research/librtmp/Makefile | 6 +- trunk/research/librtmp/srs_h264_raw_publish.c | 86 +++++++++++++++++++ trunk/src/srs/srs.upp | 1 + 4 files changed, 117 insertions(+), 10 deletions(-) create mode 100644 trunk/research/librtmp/srs_h264_raw_publish.c diff --git a/trunk/research/arm/jmp_sp.cpp b/trunk/research/arm/jmp_sp.cpp index b39695c2fc..396414c139 100644 --- a/trunk/research/arm/jmp_sp.cpp +++ b/trunk/research/arm/jmp_sp.cpp @@ -1,4 +1,5 @@ /* +# see: https://github.com/winlinvip/simple-rtmp-server/issues/190 # see: https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLinuxArm g++ -g -O0 -o jmp_sp jmp_sp.cpp arm-linux-gnueabi-g++ -g -o jmp_sp jmp_sp.cpp -static @@ -14,15 +15,13 @@ void func1() { #if defined(__amd64__) || defined(__x86_64__) register long int rsp0 asm("rsp"); - printf("in func1, rsp=%#lx, longjmp to func0\n", rsp0); - longjmp(env_func1, 0x101); - - printf("func1 terminated\n"); + int ret = setjmp(env_func1); + printf("setjmp func0 ret=%d, rsp=%#lx\n", ret, rsp0); #endif } -void func0(int* pv0, int* pv1) +void func0() { /** the definition of jmp_buf: @@ -63,6 +62,8 @@ void func0(int* pv0, int* pv1) printf("env[%d]=%#x, ", i, (int)env_func1[0].__jmpbuf[i]); } printf("\n"); + + func1(); #else /** /usr/arm-linux-gnueabi/include/bits/setjmp.h @@ -84,6 +85,24 @@ void func0(int* pv0, int* pv1) 10-26: d8-d15 17words 27: fpscr */ + /** + For example, on raspberry-pi, armv6 cpu: + (gdb) x /64 env_func1[0].__jmpbuf + v1, 0: 0x00 0x00 0x00 0x00 + v2, 1: 0x00 0x00 0x00 0x00 + v3, 2: 0x2c 0x84 0x00 0x00 + v4, 3: 0x00 0x00 0x00 0x00 + v5, 4: 0x00 0x00 0x00 0x00 + v6, 5: 0x00 0x00 0x00 0x00 + sl, 6: 0x00 0xf0 0xff 0xb6 + fp, 7: 0x9c 0xfb 0xff 0xbe + sp, 8: 0x88 0xfb 0xff 0xbe + pc, 9: 0x08 0x85 0x00 0x00 + (gdb) p /x $sp + $5 = 0xbefffb88 + (gdb) p /x $pc + $4 = 0x850c + */ int ret = setjmp(env_func1); printf("setjmp func1 ret=%d\n", ret); @@ -105,10 +124,7 @@ int main(int argc, char** argv) { (int)sizeof(long int), (int)sizeof(long), (int)sizeof(int)); #endif - int pv0 = 0xf0; - int pv1 = 0xf1; - func0(&pv0, &pv1); - func1(); + func0(); printf("terminated\n"); diff --git a/trunk/research/librtmp/Makefile b/trunk/research/librtmp/Makefile index 7e0dc35bcf..8b285f559d 100755 --- a/trunk/research/librtmp/Makefile +++ b/trunk/research/librtmp/Makefile @@ -6,7 +6,7 @@ else ST_ALL = objs/srs_flv_parser \ objs/srs_flv_injecter objs/srs_publish objs/srs_play \ objs/srs_ingest_flv objs/srs_ingest_rtmp objs/srs_detect_rtmp \ - objs/srs_bandwidth_check + objs/srs_bandwidth_check objs/srs_h264_raw_publish endif .PHONY: default clean help ssl nossl @@ -23,6 +23,7 @@ help: @echo " srs_flv_parser parse flv file, print detail info." @echo " srs_flv_injecter inject keyframes information to metadata." @echo " srs_publish publish program using srs-librtmp" + @echo " srs_h264_raw_publish publish raw h.264 stream to SSR by srs-librtmp" @echo " srs_play play program using srs-librtmp" @echo " srs_ingest_flv ingest flv file and publish to RTMP server." @echo " srs_ingest_rtmp ingest RTMP and publish to RTMP server." @@ -81,6 +82,9 @@ objs/srs_flv_injecter: srs_flv_injecter.c $(SRS_RESEARCH_DEPS) $(SRS_LIBRTMP_I) objs/srs_publish: srs_publish.c $(SRS_RESEARCH_DEPS) $(SRS_LIBRTMP_I) $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(GCC) srs_publish.c $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(EXTRA_CXX_FLAG) -o objs/srs_publish +objs/srs_h264_raw_publish: srs_h264_raw_publish.c $(SRS_RESEARCH_DEPS) $(SRS_LIBRTMP_I) $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) + $(GCC) srs_h264_raw_publish.c $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(EXTRA_CXX_FLAG) -o objs/srs_h264_raw_publish + objs/srs_play: srs_play.c $(SRS_RESEARCH_DEPS) $(SRS_LIBRTMP_I) $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(GCC) srs_play.c $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(EXTRA_CXX_FLAG) -o objs/srs_play diff --git a/trunk/research/librtmp/srs_h264_raw_publish.c b/trunk/research/librtmp/srs_h264_raw_publish.c new file mode 100644 index 0000000000..ce30c61fac --- /dev/null +++ b/trunk/research/librtmp/srs_h264_raw_publish.c @@ -0,0 +1,86 @@ +/* +The MIT License (MIT) + +Copyright (c) 2013-2014 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +/** +gcc srs_h264_raw_publish.c ../../objs/lib/srs_librtmp.a -g -O0 -lstdc++ -o srs_h264_raw_publish +*/ + +#include +#include +#include + +#include "../../objs/include/srs_librtmp.h" + +#define srs_trace(msg, ...) printf(msg, ##__VA_ARGS__);printf("\n") + +int main(int argc, char** argv) +{ + srs_rtmp_t rtmp; + + // packet data + int type, size; + u_int32_t timestamp = 0; + char* data; + + srs_trace("publish raw h.264 as rtmp stream to server like FMLE/FFMPEG/Encoder"); + srs_trace("srs(simple-rtmp-server) client librtmp library."); + srs_trace("version: %d.%d.%d\n", srs_version_major(), srs_version_minor(), srs_version_revision()); + + rtmp = srs_rtmp_create("rtmp://127.0.0.1:1935/live/livestream"); + + if (srs_simple_handshake(rtmp) != 0) { + srs_trace("simple handshake failed."); + goto rtmp_destroy; + } + srs_trace("simple handshake success"); + + if (srs_connect_app(rtmp) != 0) { + srs_trace("connect vhost/app failed."); + goto rtmp_destroy; + } + srs_trace("connect vhost/app success"); + + if (srs_publish_stream(rtmp) != 0) { + srs_trace("publish stream failed."); + goto rtmp_destroy; + } + srs_trace("publish stream success"); + + for (;;) { + type = SRS_RTMP_TYPE_VIDEO; + timestamp += 40; + size = 4096; + data = (char*)malloc(4096); + + if (srs_write_packet(rtmp, type, timestamp, data, size) != 0) { + goto rtmp_destroy; + } + srs_trace("sent packet: type=%s, time=%d, size=%d", srs_type2string(type), timestamp, size); + + usleep(40 * 1000); + } + +rtmp_destroy: + srs_rtmp_destroy(rtmp); + + return 0; +} diff --git a/trunk/src/srs/srs.upp b/trunk/src/srs/srs.upp index 63c2cfa74b..290caa48c4 100755 --- a/trunk/src/srs/srs.upp +++ b/trunk/src/srs/srs.upp @@ -132,6 +132,7 @@ file ..\..\research\librtmp\srs_detect_rtmp.c, ..\..\research\librtmp\srs_flv_injecter.c, ..\..\research\librtmp\srs_flv_parser.c, + ..\..\research\librtmp\srs_h264_raw_publish.c, ..\..\research\librtmp\srs_ingest_flv.c, ..\..\research\librtmp\srs_ingest_rtmp.c, ..\..\research\librtmp\srs_play.c, From e4af098d0696a574edd77ad2c358e3cbffb219cd Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 7 Nov 2014 21:16:41 +0800 Subject: [PATCH 073/800] refine code for bug #66 --- trunk/research/librtmp/srs_h264_raw_publish.c | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/trunk/research/librtmp/srs_h264_raw_publish.c b/trunk/research/librtmp/srs_h264_raw_publish.c index ce30c61fac..f4326498ee 100644 --- a/trunk/research/librtmp/srs_h264_raw_publish.c +++ b/trunk/research/librtmp/srs_h264_raw_publish.c @@ -34,18 +34,26 @@ gcc srs_h264_raw_publish.c ../../objs/lib/srs_librtmp.a -g -O0 -lstdc++ -o srs_h int main(int argc, char** argv) { - srs_rtmp_t rtmp; - - // packet data - int type, size; - u_int32_t timestamp = 0; - char* data; - srs_trace("publish raw h.264 as rtmp stream to server like FMLE/FFMPEG/Encoder"); srs_trace("srs(simple-rtmp-server) client librtmp library."); - srs_trace("version: %d.%d.%d\n", srs_version_major(), srs_version_minor(), srs_version_revision()); + srs_trace("version: %d.%d.%d", srs_version_major(), srs_version_minor(), srs_version_revision()); - rtmp = srs_rtmp_create("rtmp://127.0.0.1:1935/live/livestream"); + if (argc <= 2) { + srs_trace("Usage: %s ", argv[0]); + srs_trace(" h264_raw_file: the raw h264 steam file."); + srs_trace(" rtmp_publish_url: the rtmp publish url."); + srs_trace("For example:"); + srs_trace(" %s ./720p.h264.raw rtmp://127.0.0.1:1935/live/livestream", argv[0]); + srs_trace("Where the file: http://winlinvip.github.io/srs.release/3rdparty/720p.h264.raw"); + srs_trace("See: https://github.com/winlinvip/simple-rtmp-server/issues/66"); + exit(-1); + } + + const char* raw_file = argv[1]; + const char* rtmp_url = argv[2]; + srs_trace("raw_file=%s, rtmp_url=%s", raw_file, rtmp_url); + + srs_rtmp_t rtmp = srs_rtmp_create(rtmp_url); if (srs_simple_handshake(rtmp) != 0) { srs_trace("simple handshake failed."); @@ -65,11 +73,12 @@ int main(int argc, char** argv) } srs_trace("publish stream success"); + u_int32_t timestamp = 0; for (;;) { - type = SRS_RTMP_TYPE_VIDEO; + int type = SRS_RTMP_TYPE_VIDEO; timestamp += 40; - size = 4096; - data = (char*)malloc(4096); + int size = 4096; + char* data = (char*)malloc(4096); if (srs_write_packet(rtmp, type, timestamp, data, size) != 0) { goto rtmp_destroy; From 1c237a821a00a5c24df5c1120e29d7999d63f92f Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 7 Nov 2014 22:06:30 +0800 Subject: [PATCH 074/800] add api convert h264 to rtmp packet, for bug #66 --- trunk/research/librtmp/srs_h264_raw_publish.c | 44 +++- trunk/src/libs/srs_librtmp.cpp | 7 + trunk/src/libs/srs_librtmp.hpp | 199 +++++++++++------- 3 files changed, 165 insertions(+), 85 deletions(-) diff --git a/trunk/research/librtmp/srs_h264_raw_publish.c b/trunk/research/librtmp/srs_h264_raw_publish.c index f4326498ee..df1a76c403 100644 --- a/trunk/research/librtmp/srs_h264_raw_publish.c +++ b/trunk/research/librtmp/srs_h264_raw_publish.c @@ -28,6 +28,11 @@ gcc srs_h264_raw_publish.c ../../objs/lib/srs_librtmp.a -g -O0 -lstdc++ -o srs_h #include #include +// for open h264 raw file. +#include +#include +#include + #include "../../objs/include/srs_librtmp.h" #define srs_trace(msg, ...) printf(msg, ##__VA_ARGS__);printf("\n") @@ -40,7 +45,7 @@ int main(int argc, char** argv) if (argc <= 2) { srs_trace("Usage: %s ", argv[0]); - srs_trace(" h264_raw_file: the raw h264 steam file."); + srs_trace(" h264_raw_file: the h264 raw steam file."); srs_trace(" rtmp_publish_url: the rtmp publish url."); srs_trace("For example:"); srs_trace(" %s ./720p.h264.raw rtmp://127.0.0.1:1935/live/livestream", argv[0]); @@ -53,6 +58,14 @@ int main(int argc, char** argv) const char* rtmp_url = argv[2]; srs_trace("raw_file=%s, rtmp_url=%s", raw_file, rtmp_url); + // open file + int raw_fd = open(raw_file, O_RDONLY); + if (raw_fd < 0) { + srs_trace("open h264 raw file %s failed.", raw_fd); + goto rtmp_destroy; + } + + // connect rtmp context srs_rtmp_t rtmp = srs_rtmp_create(rtmp_url); if (srs_simple_handshake(rtmp) != 0) { @@ -73,23 +86,42 @@ int main(int argc, char** argv) } srs_trace("publish stream success"); - u_int32_t timestamp = 0; + u_int32_t dts = 0; + u_int32_t pts = 0; for (;;) { - int type = SRS_RTMP_TYPE_VIDEO; - timestamp += 40; + // read from file, or get h264 raw data from device, whatever. int size = 4096; char* data = (char*)malloc(4096); + if ((size = read(raw_fd, data, size)) < 0) { + srs_trace("read h264 raw data failed. nread=%d", size); + goto rtmp_destroy; + } + if (size == 0) { + srs_trace("publish h264 raw data completed."); + goto rtmp_destroy; + } + + char* rtmp_data = NULL; + int rtmp_size = 0; + u_int32_t timestamp = 0; + if (srs_h264_to_rtmp(data, size, dts, pts, &rtmp_data, &rtmp_size, ×tamp) < 0) { + srs_trace("h264 raw data to rtmp data failed."); + goto rtmp_destroy; + } - if (srs_write_packet(rtmp, type, timestamp, data, size) != 0) { + int type = SRS_RTMP_TYPE_VIDEO; + if (srs_write_packet(rtmp, type, timestamp, rtmp_data, rtmp_size) != 0) { goto rtmp_destroy; } - srs_trace("sent packet: type=%s, time=%d, size=%d", srs_type2string(type), timestamp, size); + srs_trace("sent packet: type=%s, time=%d, size=%d", srs_type2string(type), timestamp, rtmp_size); usleep(40 * 1000); } rtmp_destroy: srs_rtmp_destroy(rtmp); + close(raw_fd); return 0; } + diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index d1838f10e6..d8eede766b 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -996,6 +996,13 @@ char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize) return any->human_print(pdata, psize); } +int srs_h264_to_rtmp(char* h264_raw_data, int h264_raw_size, u_int32_t dts, u_int32_t pts, char** prtmp_data, int* prtmp_size, u_int32_t* ptimestamp) +{ + *prtmp_data = h264_raw_data; + *prtmp_size = h264_raw_size; + return 0; +} + #ifdef __cplusplus } #endif diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index 8db2adbd34..384820a913 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -42,6 +42,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. extern "C"{ #endif +/************************************************************* +************************************************************** +* RTMP protocol context +************************************************************** +*************************************************************/ // the RTMP handler. typedef void* srs_rtmp_t; @@ -51,7 +56,7 @@ typedef void* srs_rtmp_t; * rtmp://localhost/live/livestream * @return a rtmp handler, or NULL if error occured. */ -srs_rtmp_t srs_rtmp_create(const char* url); +extern srs_rtmp_t srs_rtmp_create(const char* url); /** * create rtmp with url, used for connection specified application. * @param url the tcUrl, for exmple: @@ -59,13 +64,18 @@ srs_rtmp_t srs_rtmp_create(const char* url); * @remark this is used to create application connection-oriented, * for example, the bandwidth client used this, no stream specified. */ -srs_rtmp_t srs_rtmp_create2(const char* url); +extern srs_rtmp_t srs_rtmp_create2(const char* url); /** * close and destroy the rtmp stack. * @remark, user should use the rtmp again. */ -void srs_rtmp_destroy(srs_rtmp_t rtmp); +extern void srs_rtmp_destroy(srs_rtmp_t rtmp); +/************************************************************* +************************************************************** +* RTMP protocol stack +************************************************************** +*************************************************************/ /** * connect and handshake with server * category: publish/play @@ -84,13 +94,13 @@ void srs_rtmp_destroy(srs_rtmp_t rtmp); * __srs_do_simple_handshake() * user can use these functions if needed. */ -int srs_simple_handshake(srs_rtmp_t rtmp); +extern int srs_simple_handshake(srs_rtmp_t rtmp); // parse uri, create socket, resolve host -int __srs_dns_resolve(srs_rtmp_t rtmp); +extern int __srs_dns_resolve(srs_rtmp_t rtmp); // connect socket to server -int __srs_connect_server(srs_rtmp_t rtmp); +extern int __srs_connect_server(srs_rtmp_t rtmp); // do simple handshake over socket. -int __srs_do_simple_handshake(srs_rtmp_t rtmp); +extern int __srs_do_simple_handshake(srs_rtmp_t rtmp); /** * connect to rtmp vhost/app @@ -99,7 +109,7 @@ int __srs_do_simple_handshake(srs_rtmp_t rtmp); * next: publish or play * @return 0, success; otherwise, failed. */ -int srs_connect_app(srs_rtmp_t rtmp); +extern int srs_connect_app(srs_rtmp_t rtmp); /** * connect to server, get the debug srs info. @@ -112,7 +122,7 @@ int srs_connect_app(srs_rtmp_t rtmp); * @param srs_id, int, debug info, client id in server log. * @param srs_pid, int, debug info, server pid in log. */ -int srs_connect_app2(srs_rtmp_t rtmp, +extern int srs_connect_app2(srs_rtmp_t rtmp, char srs_server_ip[128], char srs_server[128], char srs_primary_authors[128], char srs_version[32], int* srs_id, int* srs_pid ); @@ -124,7 +134,7 @@ int srs_connect_app2(srs_rtmp_t rtmp, * next: destroy * @return 0, success; otherwise, failed. */ -int srs_play_stream(srs_rtmp_t rtmp); +extern int srs_play_stream(srs_rtmp_t rtmp); /** * publish a live stream. @@ -133,7 +143,7 @@ int srs_play_stream(srs_rtmp_t rtmp); * next: destroy * @return 0, success; otherwise, failed. */ -int srs_publish_stream(srs_rtmp_t rtmp); +extern int srs_publish_stream(srs_rtmp_t rtmp); /** * do bandwidth check with srs server. @@ -148,7 +158,7 @@ int srs_publish_stream(srs_rtmp_t rtmp); * @param play_duration, output the play/download test duration, in ms. * @param publish_duration, output the publish/upload test duration, in ms. */ -int srs_bandwidth_check(srs_rtmp_t rtmp, +extern int srs_bandwidth_check(srs_rtmp_t rtmp, int64_t* start_time, int64_t* end_time, int* play_kbps, int* publish_kbps, int* play_bytes, int* publish_bytes, @@ -173,7 +183,7 @@ int srs_bandwidth_check(srs_rtmp_t rtmp, * @remark user never free the return char*, * it's static shared const string. */ -const char* srs_type2string(int type); +extern const char* srs_type2string(int type); /** * read a audio/video/script-data packet from rtmp stream. * @param type, output the packet type, macros: @@ -191,113 +201,144 @@ const char* srs_type2string(int type); * @remark: for read, user must free the data. * @remark: for write, user should never free the data, even if error. */ -int srs_read_packet(srs_rtmp_t rtmp, int* type, u_int32_t* timestamp, char** data, int* size); -int srs_write_packet(srs_rtmp_t rtmp, int type, u_int32_t timestamp, char* data, int size); +extern int srs_read_packet(srs_rtmp_t rtmp, + int* type, u_int32_t* timestamp, char** data, int* size +); +extern int srs_write_packet(srs_rtmp_t rtmp, + int type, u_int32_t timestamp, char* data, int size +); -/** -* get protocol stack version -*/ -int srs_version_major(); -int srs_version_minor(); -int srs_version_revision(); +// get protocol stack version +extern int srs_version_major(); +extern int srs_version_minor(); +extern int srs_version_revision(); -/** +/************************************************************* +************************************************************** * utilities -*/ -int64_t srs_get_time_ms(); -int64_t srs_get_nsend_bytes(srs_rtmp_t rtmp); -int64_t srs_get_nrecv_bytes(srs_rtmp_t rtmp); +************************************************************** +*************************************************************/ +extern int64_t srs_get_time_ms(); +extern int64_t srs_get_nsend_bytes(srs_rtmp_t rtmp); +extern int64_t srs_get_nrecv_bytes(srs_rtmp_t rtmp); -/** + +/************************************************************* +************************************************************** * flv codec -*/ +************************************************************** +*************************************************************/ typedef void* srs_flv_t; typedef int flv_bool; /* open flv file for both read/write. */ -srs_flv_t srs_flv_open_read(const char* file); -srs_flv_t srs_flv_open_write(const char* file); -void srs_flv_close(srs_flv_t flv); +extern srs_flv_t srs_flv_open_read(const char* file); +extern srs_flv_t srs_flv_open_write(const char* file); +extern void srs_flv_close(srs_flv_t flv); /* read the flv header. 9bytes header. drop the 4bytes zero previous tag size */ -int srs_flv_read_header(srs_flv_t flv, char header[9]); +extern int srs_flv_read_header(srs_flv_t flv, char header[9]); /* read the flv tag header, 1bytes tag, 3bytes data_size, 4bytes time, 3bytes stream id. */ -int srs_flv_read_tag_header(srs_flv_t flv, char* ptype, int32_t* pdata_size, u_int32_t* ptime); +extern int srs_flv_read_tag_header(srs_flv_t flv, + char* ptype, int32_t* pdata_size, u_int32_t* ptime +); /* read the tag data. drop the 4bytes previous tag size */ -int srs_flv_read_tag_data(srs_flv_t flv, char* data, int32_t size); +extern int srs_flv_read_tag_data(srs_flv_t flv, char* data, int32_t size); /* write flv header to file, auto write the 4bytes zero previous tag size. */ -int srs_flv_write_header(srs_flv_t flv, char header[9]); +extern int srs_flv_write_header(srs_flv_t flv, char header[9]); /* write flv tag to file, auto write the 4bytes previous tag size */ -int srs_flv_write_tag(srs_flv_t flv, char type, int32_t time, char* data, int size); +extern int srs_flv_write_tag(srs_flv_t flv, char type, int32_t time, char* data, int size); /* get the tag size, for flv injecter to adjust offset, size=tag_header+data+previous_tag */ -int srs_flv_size_tag(int data_size); +extern int srs_flv_size_tag(int data_size); /* file stream */ /* file stream tellg to get offset */ -int64_t srs_flv_tellg(srs_flv_t flv); +extern int64_t srs_flv_tellg(srs_flv_t flv); /* seek file stream, offset is form the start of file */ -void srs_flv_lseek(srs_flv_t flv, int64_t offset); +extern void srs_flv_lseek(srs_flv_t flv, int64_t offset); /* error code */ /* whether the error code indicates EOF */ -flv_bool srs_flv_is_eof(int error_code); +extern flv_bool srs_flv_is_eof(int error_code); /* media codec */ /* whether the video body is sequence header */ -flv_bool srs_flv_is_sequence_header(char* data, int32_t size); +extern flv_bool srs_flv_is_sequence_header(char* data, int32_t size); /* whether the video body is keyframe */ -flv_bool srs_flv_is_keyframe(char* data, int32_t size); +extern flv_bool srs_flv_is_keyframe(char* data, int32_t size); -/** +/************************************************************* +************************************************************** * amf0 codec -*/ +************************************************************** +*************************************************************/ /* the output handler. */ typedef void* srs_amf0_t; typedef int amf0_bool; typedef double amf0_number; -srs_amf0_t srs_amf0_parse(char* data, int size, int* nparsed); -srs_amf0_t srs_amf0_create_number(amf0_number value); -srs_amf0_t srs_amf0_create_ecma_array(); -srs_amf0_t srs_amf0_create_strict_array(); -srs_amf0_t srs_amf0_create_object(); -void srs_amf0_free(srs_amf0_t amf0); -void srs_amf0_free_bytes(char* data); +extern srs_amf0_t srs_amf0_parse(char* data, int size, int* nparsed); +extern srs_amf0_t srs_amf0_create_number(amf0_number value); +extern srs_amf0_t srs_amf0_create_ecma_array(); +extern srs_amf0_t srs_amf0_create_strict_array(); +extern srs_amf0_t srs_amf0_create_object(); +extern void srs_amf0_free(srs_amf0_t amf0); +extern void srs_amf0_free_bytes(char* data); /* size and to bytes */ -int srs_amf0_size(srs_amf0_t amf0); -int srs_amf0_serialize(srs_amf0_t amf0, char* data, int size); +extern int srs_amf0_size(srs_amf0_t amf0); +extern int srs_amf0_serialize(srs_amf0_t amf0, char* data, int size); /* type detecter */ -amf0_bool srs_amf0_is_string(srs_amf0_t amf0); -amf0_bool srs_amf0_is_boolean(srs_amf0_t amf0); -amf0_bool srs_amf0_is_number(srs_amf0_t amf0); -amf0_bool srs_amf0_is_null(srs_amf0_t amf0); -amf0_bool srs_amf0_is_object(srs_amf0_t amf0); -amf0_bool srs_amf0_is_ecma_array(srs_amf0_t amf0); -amf0_bool srs_amf0_is_strict_array(srs_amf0_t amf0); +extern amf0_bool srs_amf0_is_string(srs_amf0_t amf0); +extern amf0_bool srs_amf0_is_boolean(srs_amf0_t amf0); +extern amf0_bool srs_amf0_is_number(srs_amf0_t amf0); +extern amf0_bool srs_amf0_is_null(srs_amf0_t amf0); +extern amf0_bool srs_amf0_is_object(srs_amf0_t amf0); +extern amf0_bool srs_amf0_is_ecma_array(srs_amf0_t amf0); +extern amf0_bool srs_amf0_is_strict_array(srs_amf0_t amf0); /* value converter */ -const char* srs_amf0_to_string(srs_amf0_t amf0); -amf0_bool srs_amf0_to_boolean(srs_amf0_t amf0); -amf0_number srs_amf0_to_number(srs_amf0_t amf0); +extern const char* srs_amf0_to_string(srs_amf0_t amf0); +extern amf0_bool srs_amf0_to_boolean(srs_amf0_t amf0); +extern amf0_number srs_amf0_to_number(srs_amf0_t amf0); /* value setter */ -void srs_amf0_set_number(srs_amf0_t amf0, amf0_number value); +extern void srs_amf0_set_number(srs_amf0_t amf0, amf0_number value); /* object value converter */ -int srs_amf0_object_property_count(srs_amf0_t amf0); -const char* srs_amf0_object_property_name_at(srs_amf0_t amf0, int index); -srs_amf0_t srs_amf0_object_property_value_at(srs_amf0_t amf0, int index); -srs_amf0_t srs_amf0_object_property(srs_amf0_t amf0, const char* name); -void srs_amf0_object_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value); -void srs_amf0_object_clear(srs_amf0_t amf0); +extern int srs_amf0_object_property_count(srs_amf0_t amf0); +extern const char* srs_amf0_object_property_name_at(srs_amf0_t amf0, int index); +extern srs_amf0_t srs_amf0_object_property_value_at(srs_amf0_t amf0, int index); +extern srs_amf0_t srs_amf0_object_property(srs_amf0_t amf0, const char* name); +extern void srs_amf0_object_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value); +extern void srs_amf0_object_clear(srs_amf0_t amf0); /* ecma array value converter */ -int srs_amf0_ecma_array_property_count(srs_amf0_t amf0); -const char* srs_amf0_ecma_array_property_name_at(srs_amf0_t amf0, int index); -srs_amf0_t srs_amf0_ecma_array_property_value_at(srs_amf0_t amf0, int index); -srs_amf0_t srs_amf0_ecma_array_property(srs_amf0_t amf0, const char* name); -void srs_amf0_ecma_array_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value); +extern int srs_amf0_ecma_array_property_count(srs_amf0_t amf0); +extern const char* srs_amf0_ecma_array_property_name_at(srs_amf0_t amf0, int index); +extern srs_amf0_t srs_amf0_ecma_array_property_value_at(srs_amf0_t amf0, int index); +extern srs_amf0_t srs_amf0_ecma_array_property(srs_amf0_t amf0, const char* name); +extern void srs_amf0_ecma_array_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value); /* strict array value converter */ -int srs_amf0_strict_array_property_count(srs_amf0_t amf0); -srs_amf0_t srs_amf0_strict_array_property_at(srs_amf0_t amf0, int index); -void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value); +extern int srs_amf0_strict_array_property_count(srs_amf0_t amf0); +extern srs_amf0_t srs_amf0_strict_array_property_at(srs_amf0_t amf0, int index); +extern void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value); /** * human readable print * @param pdata, output the heap data, NULL to ignore. * user must use srs_amf0_free_bytes to free it. * @return return the *pdata for print. NULL to ignore. */ -char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize); +extern char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize); + +/************************************************************* +************************************************************** +* h264 raw codec +************************************************************** +*************************************************************/ +/** +convert h264 stream data to rtmp packet. +@param h264_raw_data the input h264 raw data, a encoded h.264 I/P/B frame data. +@paam h264_raw_size the size of h264 raw data. +@param dts the dts of h.264 raw data. +@param pts the pts of h.264 raw data. +@param prtmp_data the output rtmp format packet, which can be send by srs_write_packet. +@param prtmp_size the size of rtmp packet, for srs_write_packet. +@param ptimestamp the timestamp of rtmp packet, for srs_write_packet. +*/ +extern int srs_h264_to_rtmp( + char* h264_raw_data, int h264_raw_size, u_int32_t dts, u_int32_t pts, + char** prtmp_data, int* prtmp_size, u_int32_t* ptimestamp +); #ifdef __cplusplus } From 955859ce8258c1f422ae3732020992b4d079dd28 Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 7 Nov 2014 22:15:56 +0800 Subject: [PATCH 075/800] refine librtmp, add pefix to srs_amf0_number and srs_amf0_bool --- trunk/src/libs/srs_librtmp.cpp | 22 +++++++++++----------- trunk/src/libs/srs_librtmp.hpp | 26 +++++++++++++------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index d8eede766b..1de6c2eb34 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -735,7 +735,7 @@ srs_amf0_t srs_amf0_parse(char* data, int size, int* nparsed) return amf0; } -srs_amf0_t srs_amf0_create_number(amf0_number value) +srs_amf0_t srs_amf0_create_number(srs_amf0_number value) { return SrsAmf0Any::number(value); } @@ -790,43 +790,43 @@ int srs_amf0_serialize(srs_amf0_t amf0, char* data, int size) return ret; } -amf0_bool srs_amf0_is_string(srs_amf0_t amf0) +srs_amf0_bool srs_amf0_is_string(srs_amf0_t amf0) { SrsAmf0Any* any = (SrsAmf0Any*)amf0; return any->is_string(); } -amf0_bool srs_amf0_is_boolean(srs_amf0_t amf0) +srs_amf0_bool srs_amf0_is_boolean(srs_amf0_t amf0) { SrsAmf0Any* any = (SrsAmf0Any*)amf0; return any->is_boolean(); } -amf0_bool srs_amf0_is_number(srs_amf0_t amf0) +srs_amf0_bool srs_amf0_is_number(srs_amf0_t amf0) { SrsAmf0Any* any = (SrsAmf0Any*)amf0; return any->is_number(); } -amf0_bool srs_amf0_is_null(srs_amf0_t amf0) +srs_amf0_bool srs_amf0_is_null(srs_amf0_t amf0) { SrsAmf0Any* any = (SrsAmf0Any*)amf0; return any->is_null(); } -amf0_bool srs_amf0_is_object(srs_amf0_t amf0) +srs_amf0_bool srs_amf0_is_object(srs_amf0_t amf0) { SrsAmf0Any* any = (SrsAmf0Any*)amf0; return any->is_object(); } -amf0_bool srs_amf0_is_ecma_array(srs_amf0_t amf0) +srs_amf0_bool srs_amf0_is_ecma_array(srs_amf0_t amf0) { SrsAmf0Any* any = (SrsAmf0Any*)amf0; return any->is_ecma_array(); } -amf0_bool srs_amf0_is_strict_array(srs_amf0_t amf0) +srs_amf0_bool srs_amf0_is_strict_array(srs_amf0_t amf0) { SrsAmf0Any* any = (SrsAmf0Any*)amf0; return any->is_strict_array(); @@ -838,19 +838,19 @@ const char* srs_amf0_to_string(srs_amf0_t amf0) return any->to_str_raw(); } -amf0_bool srs_amf0_to_boolean(srs_amf0_t amf0) +srs_amf0_bool srs_amf0_to_boolean(srs_amf0_t amf0) { SrsAmf0Any* any = (SrsAmf0Any*)amf0; return any->to_boolean(); } -amf0_number srs_amf0_to_number(srs_amf0_t amf0) +srs_amf0_number srs_amf0_to_number(srs_amf0_t amf0) { SrsAmf0Any* any = (SrsAmf0Any*)amf0; return any->to_number(); } -void srs_amf0_set_number(srs_amf0_t amf0, amf0_number value) +void srs_amf0_set_number(srs_amf0_t amf0, srs_amf0_number value) { SrsAmf0Any* any = (SrsAmf0Any*)amf0; any->set_number(value); diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index 384820a913..a4f770907b 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -269,10 +269,10 @@ extern flv_bool srs_flv_is_keyframe(char* data, int32_t size); *************************************************************/ /* the output handler. */ typedef void* srs_amf0_t; -typedef int amf0_bool; -typedef double amf0_number; +typedef int srs_amf0_bool; +typedef double srs_amf0_number; extern srs_amf0_t srs_amf0_parse(char* data, int size, int* nparsed); -extern srs_amf0_t srs_amf0_create_number(amf0_number value); +extern srs_amf0_t srs_amf0_create_number(srs_amf0_number value); extern srs_amf0_t srs_amf0_create_ecma_array(); extern srs_amf0_t srs_amf0_create_strict_array(); extern srs_amf0_t srs_amf0_create_object(); @@ -282,19 +282,19 @@ extern void srs_amf0_free_bytes(char* data); extern int srs_amf0_size(srs_amf0_t amf0); extern int srs_amf0_serialize(srs_amf0_t amf0, char* data, int size); /* type detecter */ -extern amf0_bool srs_amf0_is_string(srs_amf0_t amf0); -extern amf0_bool srs_amf0_is_boolean(srs_amf0_t amf0); -extern amf0_bool srs_amf0_is_number(srs_amf0_t amf0); -extern amf0_bool srs_amf0_is_null(srs_amf0_t amf0); -extern amf0_bool srs_amf0_is_object(srs_amf0_t amf0); -extern amf0_bool srs_amf0_is_ecma_array(srs_amf0_t amf0); -extern amf0_bool srs_amf0_is_strict_array(srs_amf0_t amf0); +extern srs_amf0_bool srs_amf0_is_string(srs_amf0_t amf0); +extern srs_amf0_bool srs_amf0_is_boolean(srs_amf0_t amf0); +extern srs_amf0_bool srs_amf0_is_number(srs_amf0_t amf0); +extern srs_amf0_bool srs_amf0_is_null(srs_amf0_t amf0); +extern srs_amf0_bool srs_amf0_is_object(srs_amf0_t amf0); +extern srs_amf0_bool srs_amf0_is_ecma_array(srs_amf0_t amf0); +extern srs_amf0_bool srs_amf0_is_strict_array(srs_amf0_t amf0); /* value converter */ extern const char* srs_amf0_to_string(srs_amf0_t amf0); -extern amf0_bool srs_amf0_to_boolean(srs_amf0_t amf0); -extern amf0_number srs_amf0_to_number(srs_amf0_t amf0); +extern srs_amf0_bool srs_amf0_to_boolean(srs_amf0_t amf0); +extern srs_amf0_number srs_amf0_to_number(srs_amf0_t amf0); /* value setter */ -extern void srs_amf0_set_number(srs_amf0_t amf0, amf0_number value); +extern void srs_amf0_set_number(srs_amf0_t amf0, srs_amf0_number value); /* object value converter */ extern int srs_amf0_object_property_count(srs_amf0_t amf0); extern const char* srs_amf0_object_property_name_at(srs_amf0_t amf0, int index); From bd25626f0e0835600973d3d97288c8894a03a7ba Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 7 Nov 2014 22:36:25 +0800 Subject: [PATCH 076/800] refine code for bug #66 --- trunk/research/librtmp/srs_h264_raw_publish.c | 43 +++++++++++++++++-- trunk/src/libs/srs_librtmp.hpp | 18 +++++++- 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/trunk/research/librtmp/srs_h264_raw_publish.c b/trunk/research/librtmp/srs_h264_raw_publish.c index df1a76c403..b0b6d7379e 100644 --- a/trunk/research/librtmp/srs_h264_raw_publish.c +++ b/trunk/research/librtmp/srs_h264_raw_publish.c @@ -65,6 +65,27 @@ int main(int argc, char** argv) goto rtmp_destroy; } + off_t file_size = lseek(raw_fd, 0, SEEK_END); + if (file_size <= 0) { + srs_trace("h264 raw file %s empty.", raw_file); + goto rtmp_destroy; + } + srs_trace("read entirely h264 raw file, size=%dKB", (int)(file_size / 1024)); + + char* h264_raw = (char*)malloc(file_size); + if (!h264_raw) { + srs_trace("alloc raw buffer failed for file %s.", raw_file); + goto rtmp_destroy; + } + + lseek(raw_fd, 0, SEEK_SET); + ssize_t nb_read = 0; + if ((nb_read = read(raw_fd, h264_raw, file_size)) != file_size) { + srs_trace("buffer %s failed, expect=%dKB, actual=%dKB.", + raw_file, (int)(file_size / 1024), (int)(nb_read / 1024)); + goto rtmp_destroy; + } + // connect rtmp context srs_rtmp_t rtmp = srs_rtmp_create(rtmp_url); @@ -86,10 +107,13 @@ int main(int argc, char** argv) } srs_trace("publish stream success"); + // @remark, the dts and pts if read from device, for instance, the encode lib, + // so we assume the fps is 25, and each h264 frame is 1000ms/25fps=40ms/f. + u_int32_t fps = 25; u_int32_t dts = 0; u_int32_t pts = 0; for (;;) { - // read from file, or get h264 raw data from device, whatever. + // get h264 raw data from device, whatever. int size = 4096; char* data = (char*)malloc(4096); if ((size = read(raw_fd, data, size)) < 0) { @@ -101,26 +125,37 @@ int main(int argc, char** argv) goto rtmp_destroy; } + // convert the h264 packet to rtmp packet. char* rtmp_data = NULL; int rtmp_size = 0; u_int32_t timestamp = 0; - if (srs_h264_to_rtmp(data, size, dts, pts, &rtmp_data, &rtmp_size, ×tamp) < 0) { + if (srs_h264_to_rtmp(data, size, dts, pts, &rtmp_data, &rtmp_size, ×tamp) != 0) { srs_trace("h264 raw data to rtmp data failed."); goto rtmp_destroy; } + // send out the rtmp packet. int type = SRS_RTMP_TYPE_VIDEO; if (srs_write_packet(rtmp, type, timestamp, rtmp_data, rtmp_size) != 0) { goto rtmp_destroy; } - srs_trace("sent packet: type=%s, time=%d, size=%d", srs_type2string(type), timestamp, rtmp_size); + srs_trace("sent packet: type=%s, time=%d, size=%d, fps=%d", + srs_type2string(type), timestamp, rtmp_size, fps); + + // @remark, please get the dts and pts from device, + // we assume there is no B frame, and the fps can guess the fps and dts, + // while the dts and pts must read from encode lib or device. + dts += 1000 / fps; + pts = dts; - usleep(40 * 1000); + // @remark, when use encode device, it not need to sleep. + usleep(1000 / fps * 1000); } rtmp_destroy: srs_rtmp_destroy(rtmp); close(raw_fd); + free(h264_raw); return 0; } diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index a4f770907b..f6e988fae5 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -54,6 +54,7 @@ typedef void* srs_rtmp_t; * create/destroy a rtmp protocol stack. * @url rtmp url, for example: * rtmp://localhost/live/livestream +* * @return a rtmp handler, or NULL if error occured. */ extern srs_rtmp_t srs_rtmp_create(const char* url); @@ -63,6 +64,8 @@ extern srs_rtmp_t srs_rtmp_create(const char* url); * rtmp://localhost/live * @remark this is used to create application connection-oriented, * for example, the bandwidth client used this, no stream specified. +* +* @return a rtmp handler, or NULL if error occured. */ extern srs_rtmp_t srs_rtmp_create2(const char* url); /** @@ -81,7 +84,8 @@ extern void srs_rtmp_destroy(srs_rtmp_t rtmp); * category: publish/play * previous: rtmp-create * next: connect-app -* @return 0, success; otherwise, failed. +* +* @return 0, success; otherswise, failed. */ /** * simple handshake specifies in rtmp 1.0, @@ -107,7 +111,8 @@ extern int __srs_do_simple_handshake(srs_rtmp_t rtmp); * category: publish/play * previous: handshake * next: publish or play -* @return 0, success; otherwise, failed. +* +* @return 0, success; otherswise, failed. */ extern int srs_connect_app(srs_rtmp_t rtmp); @@ -121,6 +126,8 @@ extern int srs_connect_app(srs_rtmp_t rtmp); * @param srs_version, 32bytes, server version. * @param srs_id, int, debug info, client id in server log. * @param srs_pid, int, debug info, server pid in log. +* +* @return 0, success; otherswise, failed. */ extern int srs_connect_app2(srs_rtmp_t rtmp, char srs_server_ip[128], char srs_server[128], char srs_primary_authors[128], @@ -157,6 +164,8 @@ extern int srs_publish_stream(srs_rtmp_t rtmp); * @param publish_bytes, output the publish/upload bytes. * @param play_duration, output the play/download test duration, in ms. * @param publish_duration, output the publish/upload test duration, in ms. +* +* @return 0, success; otherswise, failed. */ extern int srs_bandwidth_check(srs_rtmp_t rtmp, int64_t* start_time, int64_t* end_time, @@ -200,6 +209,8 @@ extern const char* srs_type2string(int type); * * @remark: for read, user must free the data. * @remark: for write, user should never free the data, even if error. +* +* @return 0, success; otherswise, failed. */ extern int srs_read_packet(srs_rtmp_t rtmp, int* type, u_int32_t* timestamp, char** data, int* size @@ -334,6 +345,9 @@ convert h264 stream data to rtmp packet. @param prtmp_data the output rtmp format packet, which can be send by srs_write_packet. @param prtmp_size the size of rtmp packet, for srs_write_packet. @param ptimestamp the timestamp of rtmp packet, for srs_write_packet. +@remark, user should never free the h264_raw_data. +@remark, user should free the prtmp_data if success. +@return 0, success; otherswise, failed. */ extern int srs_h264_to_rtmp( char* h264_raw_data, int h264_raw_size, u_int32_t dts, u_int32_t pts, From 72ad6894ca3ac84c93ffd88fbe87d793b7b126ad Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 7 Nov 2014 23:07:31 +0800 Subject: [PATCH 077/800] move the annexb decode utility to protocol. --- trunk/research/librtmp/srs_h264_raw_publish.c | 65 +++++++++++++------ trunk/src/app/srs_app_avc_aac.cpp | 1 + trunk/src/app/srs_app_utility.cpp | 29 --------- trunk/src/app/srs_app_utility.hpp | 9 --- trunk/src/libs/srs_librtmp.cpp | 15 ++++- trunk/src/libs/srs_librtmp.hpp | 37 +++++++---- trunk/src/rtmp/srs_protocol_utility.cpp | 30 +++++++++ trunk/src/rtmp/srs_protocol_utility.hpp | 11 ++++ 8 files changed, 128 insertions(+), 69 deletions(-) diff --git a/trunk/research/librtmp/srs_h264_raw_publish.c b/trunk/research/librtmp/srs_h264_raw_publish.c index b0b6d7379e..6af340f8af 100644 --- a/trunk/research/librtmp/srs_h264_raw_publish.c +++ b/trunk/research/librtmp/srs_h264_raw_publish.c @@ -37,6 +37,40 @@ gcc srs_h264_raw_publish.c ../../objs/lib/srs_librtmp.a -g -O0 -lstdc++ -o srs_h #define srs_trace(msg, ...) printf(msg, ##__VA_ARGS__);printf("\n") +int read_h264_frame(char* data, int size, char** p, int fps, + char** frame, int* frame_size, int* dts, int* pts) +{ + // @remark, for this demo, to publish h264 raw file to SRS, + // we search the h264 frame from the buffer which cached the h264 data. + // please get h264 raw data from device, it always a encoded frame. + int pnb_start_code = 0; + if (!srs_h264_startswith_annexb(*p, size - (*p - data), &pnb_start_code)) { + srs_trace("h264 raw data invalid."); + return -1; + } + *p += pnb_start_code; + + *frame = *p; + for (;*p < data + size; *p = *p + 1) { + if (srs_h264_startswith_annexb(*p, size - (*p - data), &pnb_start_code)) { + break; + } + } + *frame_size = *p - *frame; + if (*frame_size <= 0) { + srs_trace("h264 raw data invalid."); + return -1; + } + + // @remark, please get the dts and pts from device, + // we assume there is no B frame, and the fps can guess the fps and dts, + // while the dts and pts must read from encode lib or device. + *dts += 1000 / fps; + *pts = *dts; + + return 0; +} + int main(int argc, char** argv) { srs_trace("publish raw h.264 as rtmp stream to server like FMLE/FFMPEG/Encoder"); @@ -107,24 +141,22 @@ int main(int argc, char** argv) } srs_trace("publish stream success"); + u_int32_t dts = 0; + u_int32_t pts = 0; // @remark, the dts and pts if read from device, for instance, the encode lib, // so we assume the fps is 25, and each h264 frame is 1000ms/25fps=40ms/f. u_int32_t fps = 25; - u_int32_t dts = 0; - u_int32_t pts = 0; - for (;;) { - // get h264 raw data from device, whatever. - int size = 4096; - char* data = (char*)malloc(4096); - if ((size = read(raw_fd, data, size)) < 0) { - srs_trace("read h264 raw data failed. nread=%d", size); + // @remark, to decode the file. + char* p = h264_raw; + for (;p < h264_raw + file_size;) { + // @remark, read a frame from file buffer. + char* data = NULL; + int size = 0; + if (read_h264_frame(h264_raw, file_size, &p, fps, &data, &size, &dts, &pts) < 0) { + srs_trace("read a frame from file buffer failed."); goto rtmp_destroy; } - if (size == 0) { - srs_trace("publish h264 raw data completed."); - goto rtmp_destroy; - } - + // convert the h264 packet to rtmp packet. char* rtmp_data = NULL; int rtmp_size = 0; @@ -142,15 +174,10 @@ int main(int argc, char** argv) srs_trace("sent packet: type=%s, time=%d, size=%d, fps=%d", srs_type2string(type), timestamp, rtmp_size, fps); - // @remark, please get the dts and pts from device, - // we assume there is no B frame, and the fps can guess the fps and dts, - // while the dts and pts must read from encode lib or device. - dts += 1000 / fps; - pts = dts; - // @remark, when use encode device, it not need to sleep. usleep(1000 / fps * 1000); } + srs_trace("h264 raw data completed"); rtmp_destroy: srs_rtmp_destroy(rtmp); diff --git a/trunk/src/app/srs_app_avc_aac.cpp b/trunk/src/app/srs_app_avc_aac.cpp index 4c0f2843e7..3f5ce2cb03 100644 --- a/trunk/src/app/srs_app_avc_aac.cpp +++ b/trunk/src/app/srs_app_avc_aac.cpp @@ -28,6 +28,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #include +#include SrsCodecSampleUnit::SrsCodecSampleUnit() { diff --git a/trunk/src/app/srs_app_utility.cpp b/trunk/src/app/srs_app_utility.cpp index c11716e750..f6253747ed 100644 --- a/trunk/src/app/srs_app_utility.cpp +++ b/trunk/src/app/srs_app_utility.cpp @@ -107,35 +107,6 @@ int srs_get_log_level(string level) } } -bool srs_avc_startswith_annexb(SrsStream* stream, int* pnb_start_code) -{ - char* bytes = stream->data() + stream->pos(); - char* p = bytes; - - for (;;) { - if (!stream->require(p - bytes + 3)) { - return false; - } - - // not match - if (p[0] != 0x00 || p[1] != 0x00) { - return false; - } - - // match N[00] 00 00 01, where N>=0 - if (p[2] == 0x01) { - if (pnb_start_code) { - *pnb_start_code = (int)(p - bytes) + 3; - } - return true; - } - - p++; - } - - return false; -} - static SrsRusage _srs_system_rusage; SrsRusage::SrsRusage() diff --git a/trunk/src/app/srs_app_utility.hpp b/trunk/src/app/srs_app_utility.hpp index a310483b6c..1a546fab02 100644 --- a/trunk/src/app/srs_app_utility.hpp +++ b/trunk/src/app/srs_app_utility.hpp @@ -50,15 +50,6 @@ extern int srs_socket_connect(std::string server, int port, int64_t timeout, st_ */ extern int srs_get_log_level(std::string level); -/** -* whether stream starts with the avc NALU in "AnnexB" -* from H.264-AVC-ISO_IEC_14496-10.pdf, page 211. -* start code must be "N[00] 00 00 01" where N>=0 -* @param pnb_start_code output the size of start code, must >=3. -* NULL to ignore. -*/ -extern bool srs_avc_startswith_annexb(SrsStream* stream, int* pnb_start_code = NULL); - // current process resouce usage. // @see: man getrusage class SrsRusage diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index 1de6c2eb34..7be8f4fb61 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -998,11 +998,24 @@ char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize) int srs_h264_to_rtmp(char* h264_raw_data, int h264_raw_size, u_int32_t dts, u_int32_t pts, char** prtmp_data, int* prtmp_size, u_int32_t* ptimestamp) { - *prtmp_data = h264_raw_data; + *prtmp_data = new char[h264_raw_size]; + memcpy(*prtmp_data, h264_raw_data, h264_raw_size); + *prtmp_size = h264_raw_size; + return 0; } +int srs_h264_startswith_annexb(char* h264_raw_data, int h264_raw_size, int* pnb_start_code) +{ + SrsStream stream; + if (stream.initialize(h264_raw_data, h264_raw_size) != ERROR_SUCCESS) { + return false; + } + + return srs_avc_startswith_annexb(&stream, pnb_start_code); +} + #ifdef __cplusplus } #endif diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index f6e988fae5..744d36eea0 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -337,22 +337,37 @@ extern char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize); ************************************************************** *************************************************************/ /** -convert h264 stream data to rtmp packet. -@param h264_raw_data the input h264 raw data, a encoded h.264 I/P/B frame data. -@paam h264_raw_size the size of h264 raw data. -@param dts the dts of h.264 raw data. -@param pts the pts of h.264 raw data. -@param prtmp_data the output rtmp format packet, which can be send by srs_write_packet. -@param prtmp_size the size of rtmp packet, for srs_write_packet. -@param ptimestamp the timestamp of rtmp packet, for srs_write_packet. -@remark, user should never free the h264_raw_data. -@remark, user should free the prtmp_data if success. -@return 0, success; otherswise, failed. +* convert h264 stream data to rtmp packet. +* @param h264_raw_data the input h264 raw data, a encoded h.264 I/P/B frame data. +* @paam h264_raw_size the size of h264 raw data. +* @param dts the dts of h.264 raw data. +* @param pts the pts of h.264 raw data. +* @param prtmp_data the output rtmp format packet, which can be send by srs_write_packet. +* @param prtmp_size the size of rtmp packet, for srs_write_packet. +* @param ptimestamp the timestamp of rtmp packet, for srs_write_packet. +* @remark, user should free the h264_raw_data. +* @remark, user should free the prtmp_data if success. +* +* @return 0, success; otherswise, failed. */ extern int srs_h264_to_rtmp( char* h264_raw_data, int h264_raw_size, u_int32_t dts, u_int32_t pts, char** prtmp_data, int* prtmp_size, u_int32_t* ptimestamp ); +/** +* whether h264 raw data starts with the annexb, +* which bytes sequence matches N[00] 00 00 01, where N>=0. +* @param h264_raw_data the input h264 raw data, a encoded h.264 I/P/B frame data. +* @paam h264_raw_size the size of h264 raw data. +* @param pnb_start_code output the size of start code, must >=3. +* NULL to ignore. +* +* @return 0 false; otherwise, true. +*/ +extern int srs_h264_startswith_annexb( + char* h264_raw_data, int h264_raw_size, + int* pnb_start_code +); #ifdef __cplusplus } diff --git a/trunk/src/rtmp/srs_protocol_utility.cpp b/trunk/src/rtmp/srs_protocol_utility.cpp index ccf99b8d57..a4ee30e565 100644 --- a/trunk/src/rtmp/srs_protocol_utility.cpp +++ b/trunk/src/rtmp/srs_protocol_utility.cpp @@ -28,6 +28,7 @@ using namespace std; #include #include +#include void srs_discovery_tc_url( string tcUrl, @@ -155,3 +156,32 @@ bool srs_bytes_equals(void* pa, void* pb, int size) return true; } +bool srs_avc_startswith_annexb(SrsStream* stream, int* pnb_start_code) +{ + char* bytes = stream->data() + stream->pos(); + char* p = bytes; + + for (;;) { + if (!stream->require(p - bytes + 3)) { + return false; + } + + // not match + if (p[0] != 0x00 || p[1] != 0x00) { + return false; + } + + // match N[00] 00 00 01, where N>=0 + if (p[2] == 0x01) { + if (pnb_start_code) { + *pnb_start_code = (int)(p - bytes) + 3; + } + return true; + } + + p++; + } + + return false; +} + diff --git a/trunk/src/rtmp/srs_protocol_utility.hpp b/trunk/src/rtmp/srs_protocol_utility.hpp index 359c4f26ee..b4293ab107 100644 --- a/trunk/src/rtmp/srs_protocol_utility.hpp +++ b/trunk/src/rtmp/srs_protocol_utility.hpp @@ -33,6 +33,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include +class SrsStream; + /** * parse the tcUrl, output the schema, host, vhost, app and port. * @param tcUrl, the input tcUrl, for example, @@ -85,5 +87,14 @@ extern std::string srs_generate_tc_url( */ extern bool srs_bytes_equals(void* pa, void* pb, int size); +/** +* whether stream starts with the avc NALU in "AnnexB" +* from H.264-AVC-ISO_IEC_14496-10.pdf, page 211. +* start code must be "N[00] 00 00 01" where N>=0 +* @param pnb_start_code output the size of start code, must >=3. +* NULL to ignore. +*/ +extern bool srs_avc_startswith_annexb(SrsStream* stream, int* pnb_start_code = NULL); + #endif From 6407baffd5c38a316f28d10fbcb9c3bc8350bc38 Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 7 Nov 2014 23:19:31 +0800 Subject: [PATCH 078/800] for bug #66, output the h264 frame type. --- trunk/research/librtmp/srs_h264_raw_publish.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/trunk/research/librtmp/srs_h264_raw_publish.c b/trunk/research/librtmp/srs_h264_raw_publish.c index 6af340f8af..c2e4686719 100644 --- a/trunk/research/librtmp/srs_h264_raw_publish.c +++ b/trunk/research/librtmp/srs_h264_raw_publish.c @@ -171,8 +171,13 @@ int main(int argc, char** argv) if (srs_write_packet(rtmp, type, timestamp, rtmp_data, rtmp_size) != 0) { goto rtmp_destroy; } - srs_trace("sent packet: type=%s, time=%d, size=%d, fps=%d", - srs_type2string(type), timestamp, rtmp_size, fps); + + // 5bits, 7.3.1 NAL unit syntax, + // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. + u_int8_t nut = (char)data[0] & 0x1f; + srs_trace("sent packet: type=%s, time=%d, size=%d, fps=%d, b[0]=%#x(%s)", + srs_type2string(type), timestamp, rtmp_size, fps, nut, + (nut == 7? "SPS":(nut == 8? "PPS":(nut == 5? "I":(nut == 1? "P":"Unknown"))))); // @remark, when use encode device, it not need to sleep. usleep(1000 / fps * 1000); From 496f4246f4eec2ad4b202d16cd284d10a3901613 Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 7 Nov 2014 23:55:00 +0800 Subject: [PATCH 079/800] complete the h264 to rtmp, but the sps and pps should send in a rtmp packet. for bug #66 --- trunk/src/libs/srs_librtmp.cpp | 51 +++++++++++++++++++++++++++++++--- trunk/src/libs/srs_librtmp.hpp | 4 ++- 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index 7be8f4fb61..926937e1eb 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -998,10 +998,53 @@ char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize) int srs_h264_to_rtmp(char* h264_raw_data, int h264_raw_size, u_int32_t dts, u_int32_t pts, char** prtmp_data, int* prtmp_size, u_int32_t* ptimestamp) { - *prtmp_data = new char[h264_raw_size]; - memcpy(*prtmp_data, h264_raw_data, h264_raw_size); - - *prtmp_size = h264_raw_size; + srs_assert(h264_raw_size > 0); + + // the timestamp in rtmp message header is dts. + *ptimestamp = dts; + + // for h264 in RTMP video payload, there is 5bytes header: + // 1bytes, FrameType | CodecID + // 1bytes, AVCPacketType + // 3bytes, CompositionTime, the cts. + // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78 + *prtmp_size = h264_raw_size + 5; + char* p = new char[*prtmp_size]; + memcpy(p + 5, h264_raw_data, h264_raw_size); + *prtmp_data = p; + + // 5bits, 7.3.1 NAL unit syntax, + // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. + // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame + u_int8_t nal_unit_type = (char)h264_raw_data[0] & 0x1f; + + // Frame Type, Type of video frame. + // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78 + int8_t frame_type = SrsCodecVideoAVCFrameInterFrame; + if (nal_unit_type != 1) { + frame_type = SrsCodecVideoAVCFrameKeyFrame; + } + // CodecID, Codec Identifier. + int8_t codec_id = SrsCodecVideoAVC; + // set the rtmp header + *p++ = (frame_type << 4) | codec_id; + + // AVCPacketType + if (nal_unit_type == 7 || nal_unit_type == 8) { + *p++ = SrsCodecVideoAVCTypeSequenceHeader; + } else { + *p++ = SrsCodecVideoAVCTypeNALU; + } + + // CompositionTime + // pts = dts + cts, or + // cts = pts - dts. + // where cts is the header in rtmp video packet payload header. + u_int32_t cts = pts - dts; + char* pp = (char*)&cts; + *p++ = pp[2]; + *p++ = pp[1]; + *p++ = pp[0]; return 0; } diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index 744d36eea0..9d20230729 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -339,14 +339,16 @@ extern char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize); /** * convert h264 stream data to rtmp packet. * @param h264_raw_data the input h264 raw data, a encoded h.264 I/P/B frame data. -* @paam h264_raw_size the size of h264 raw data. +* @paam h264_raw_size the size of h264 raw data. assert > 0. * @param dts the dts of h.264 raw data. * @param pts the pts of h.264 raw data. * @param prtmp_data the output rtmp format packet, which can be send by srs_write_packet. * @param prtmp_size the size of rtmp packet, for srs_write_packet. * @param ptimestamp the timestamp of rtmp packet, for srs_write_packet. +* * @remark, user should free the h264_raw_data. * @remark, user should free the prtmp_data if success. +* @remark, the tbn of dts/pts is 1/1000 for RTMP, that is, in ms. * * @return 0, success; otherswise, failed. */ From 1074c8d9b27752048eee47500c66220fa331e033 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 8 Nov 2014 09:12:52 +0800 Subject: [PATCH 080/800] for bug #66, use new api to directly sendout h264 raw data. --- trunk/research/librtmp/srs_h264_raw_publish.c | 17 +++------- trunk/src/libs/srs_librtmp.cpp | 32 +++++++++++++------ trunk/src/libs/srs_librtmp.hpp | 19 +++++------ 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/trunk/research/librtmp/srs_h264_raw_publish.c b/trunk/research/librtmp/srs_h264_raw_publish.c index c2e4686719..de3006af10 100644 --- a/trunk/research/librtmp/srs_h264_raw_publish.c +++ b/trunk/research/librtmp/srs_h264_raw_publish.c @@ -157,18 +157,9 @@ int main(int argc, char** argv) goto rtmp_destroy; } - // convert the h264 packet to rtmp packet. - char* rtmp_data = NULL; - int rtmp_size = 0; - u_int32_t timestamp = 0; - if (srs_h264_to_rtmp(data, size, dts, pts, &rtmp_data, &rtmp_size, ×tamp) != 0) { - srs_trace("h264 raw data to rtmp data failed."); - goto rtmp_destroy; - } - - // send out the rtmp packet. - int type = SRS_RTMP_TYPE_VIDEO; - if (srs_write_packet(rtmp, type, timestamp, rtmp_data, rtmp_size) != 0) { + // send out the h264 packet over RTMP + if (srs_write_h264_raw_frame(rtmp, data, size, dts, pts) != 0) { + srs_trace("send h264 raw data failed."); goto rtmp_destroy; } @@ -176,7 +167,7 @@ int main(int argc, char** argv) // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. u_int8_t nut = (char)data[0] & 0x1f; srs_trace("sent packet: type=%s, time=%d, size=%d, fps=%d, b[0]=%#x(%s)", - srs_type2string(type), timestamp, rtmp_size, fps, nut, + srs_type2string(SRS_RTMP_TYPE_VIDEO), dts, size, fps, nut, (nut == 7? "SPS":(nut == 8? "PPS":(nut == 5? "I":(nut == 1? "P":"Unknown"))))); // @remark, when use encode device, it not need to sleep. diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index 926937e1eb..9ed416acdb 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -70,6 +70,9 @@ struct Context SimpleSocketStream* skt; int stream_id; + // for h264 raw stream + SrsStream raw_stream; + Context() { rtmp = NULL; skt = NULL; @@ -996,22 +999,31 @@ char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize) return any->human_print(pdata, psize); } -int srs_h264_to_rtmp(char* h264_raw_data, int h264_raw_size, u_int32_t dts, u_int32_t pts, char** prtmp_data, int* prtmp_size, u_int32_t* ptimestamp) +int srs_write_h264_raw_frame(srs_rtmp_t rtmp, char* frame, int frame_size, u_int32_t dts, u_int32_t pts) { - srs_assert(h264_raw_size > 0); + int ret = ERROR_SUCCESS; + + srs_assert(frame_size > 0); - // the timestamp in rtmp message header is dts. - *ptimestamp = dts; + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + + if ((ret = context->raw_stream.initialize(frame, frame_size)) != ERROR_SUCCESS) { + return ret; + } + + + /*// the timestamp in rtmp message header is dts. + u_int32_t timestamp = dts; // for h264 in RTMP video payload, there is 5bytes header: // 1bytes, FrameType | CodecID // 1bytes, AVCPacketType // 3bytes, CompositionTime, the cts. // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78 - *prtmp_size = h264_raw_size + 5; - char* p = new char[*prtmp_size]; - memcpy(p + 5, h264_raw_data, h264_raw_size); - *prtmp_data = p; + int size = h264_raw_size + 5; + char* data = new char[size]; + memcpy(data + 5, h264_raw_data, h264_raw_size); // 5bits, 7.3.1 NAL unit syntax, // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. @@ -1044,9 +1056,9 @@ int srs_h264_to_rtmp(char* h264_raw_data, int h264_raw_size, u_int32_t dts, u_in char* pp = (char*)&cts; *p++ = pp[2]; *p++ = pp[1]; - *p++ = pp[0]; + *p++ = pp[0];*/ - return 0; + return ret; } int srs_h264_startswith_annexb(char* h264_raw_data, int h264_raw_size, int* pnb_start_code) diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index 9d20230729..718add2e27 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -337,24 +337,21 @@ extern char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize); ************************************************************** *************************************************************/ /** -* convert h264 stream data to rtmp packet. -* @param h264_raw_data the input h264 raw data, a encoded h.264 I/P/B frame data. -* @paam h264_raw_size the size of h264 raw data. assert > 0. +* write h.264 raw frame to rtmp server. +* @param frame the input h264 raw data, an encoded h.264 I/P/B frame data. +* it must be prefixed by h.264 annexb format, by N[00] 00 00 01, where N>=0, +* for instance, 00 00 00 01 67 42 80 29 95 A0 14 01 6E 40 +* @paam frame_size the size of h264 raw data. assert frame_size > 0. * @param dts the dts of h.264 raw data. * @param pts the pts of h.264 raw data. -* @param prtmp_data the output rtmp format packet, which can be send by srs_write_packet. -* @param prtmp_size the size of rtmp packet, for srs_write_packet. -* @param ptimestamp the timestamp of rtmp packet, for srs_write_packet. * -* @remark, user should free the h264_raw_data. -* @remark, user should free the prtmp_data if success. +* @remark, user should free the frame. * @remark, the tbn of dts/pts is 1/1000 for RTMP, that is, in ms. * * @return 0, success; otherswise, failed. */ -extern int srs_h264_to_rtmp( - char* h264_raw_data, int h264_raw_size, u_int32_t dts, u_int32_t pts, - char** prtmp_data, int* prtmp_size, u_int32_t* ptimestamp +extern int srs_write_h264_raw_frame(srs_rtmp_t rtmp, + char* frame, int frame_size, u_int32_t dts, u_int32_t pts ); /** * whether h264 raw data starts with the annexb, From cd5c58ba5f3f272c4f59b1f035169e6c6fde96b2 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 8 Nov 2014 09:21:09 +0800 Subject: [PATCH 081/800] for bug #66, refine the api and demo. --- trunk/research/librtmp/srs_h264_raw_publish.c | 20 +++++++++++-------- trunk/src/libs/srs_librtmp.cpp | 18 +++++------------ trunk/src/libs/srs_librtmp.hpp | 9 +++++---- 3 files changed, 22 insertions(+), 25 deletions(-) diff --git a/trunk/research/librtmp/srs_h264_raw_publish.c b/trunk/research/librtmp/srs_h264_raw_publish.c index de3006af10..dc633df96e 100644 --- a/trunk/research/librtmp/srs_h264_raw_publish.c +++ b/trunk/research/librtmp/srs_h264_raw_publish.c @@ -37,26 +37,30 @@ gcc srs_h264_raw_publish.c ../../objs/lib/srs_librtmp.a -g -O0 -lstdc++ -o srs_h #define srs_trace(msg, ...) printf(msg, ##__VA_ARGS__);printf("\n") -int read_h264_frame(char* data, int size, char** p, int fps, +int read_h264_frame(char* data, int size, char** pp, int fps, char** frame, int* frame_size, int* dts, int* pts) { + char* p = *pp; + // @remark, for this demo, to publish h264 raw file to SRS, // we search the h264 frame from the buffer which cached the h264 data. // please get h264 raw data from device, it always a encoded frame. int pnb_start_code = 0; - if (!srs_h264_startswith_annexb(*p, size - (*p - data), &pnb_start_code)) { + if (!srs_h264_startswith_annexb(p, size - (p - data), &pnb_start_code)) { srs_trace("h264 raw data invalid."); return -1; } - *p += pnb_start_code; + p += pnb_start_code; - *frame = *p; - for (;*p < data + size; *p = *p + 1) { - if (srs_h264_startswith_annexb(*p, size - (*p - data), &pnb_start_code)) { + *frame = p; + for (;p < data + size; p++) { + if (srs_h264_startswith_annexb(p, size - (p - data), &pnb_start_code)) { break; } } - *frame_size = *p - *frame; + + *pp = p; + *frame_size = p - *frame; if (*frame_size <= 0) { srs_trace("h264 raw data invalid."); return -1; @@ -167,7 +171,7 @@ int main(int argc, char** argv) // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. u_int8_t nut = (char)data[0] & 0x1f; srs_trace("sent packet: type=%s, time=%d, size=%d, fps=%d, b[0]=%#x(%s)", - srs_type2string(SRS_RTMP_TYPE_VIDEO), dts, size, fps, nut, + srs_type2string(SRS_RTMP_TYPE_VIDEO), dts, size, fps, (char)data[0], (nut == 7? "SPS":(nut == 8? "PPS":(nut == 5? "I":(nut == 1? "P":"Unknown"))))); // @remark, when use encode device, it not need to sleep. diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index 9ed416acdb..1875362461 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -70,9 +70,6 @@ struct Context SimpleSocketStream* skt; int stream_id; - // for h264 raw stream - SrsStream raw_stream; - Context() { rtmp = NULL; skt = NULL; @@ -1003,15 +1000,15 @@ int srs_write_h264_raw_frame(srs_rtmp_t rtmp, char* frame, int frame_size, u_int { int ret = ERROR_SUCCESS; - srs_assert(frame_size > 0); + srs_assert(frame_size > 1); srs_assert(rtmp != NULL); Context* context = (Context*)rtmp; - if ((ret = context->raw_stream.initialize(frame, frame_size)) != ERROR_SUCCESS) { - return ret; - } - + // 5bits, 7.3.1 NAL unit syntax, + // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. + // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame + u_int8_t nal_unit_type = (char)frame[0] & 0x1f; /*// the timestamp in rtmp message header is dts. u_int32_t timestamp = dts; @@ -1025,11 +1022,6 @@ int srs_write_h264_raw_frame(srs_rtmp_t rtmp, char* frame, int frame_size, u_int char* data = new char[size]; memcpy(data + 5, h264_raw_data, h264_raw_size); - // 5bits, 7.3.1 NAL unit syntax, - // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. - // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame - u_int8_t nal_unit_type = (char)h264_raw_data[0] & 0x1f; - // Frame Type, Type of video frame. // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78 int8_t frame_type = SrsCodecVideoAVCFrameInterFrame; diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index 718add2e27..10758dd67b 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -337,11 +337,12 @@ extern char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize); ************************************************************** *************************************************************/ /** -* write h.264 raw frame to rtmp server. +* write h.264 raw frame over RTMP to rtmp server. * @param frame the input h264 raw data, an encoded h.264 I/P/B frame data. -* it must be prefixed by h.264 annexb format, by N[00] 00 00 01, where N>=0, -* for instance, 00 00 00 01 67 42 80 29 95 A0 14 01 6E 40 -* @paam frame_size the size of h264 raw data. assert frame_size > 0. +* the frame without h.264 annexb header, by N[00] 00 00 01, where N>=0, +* for instance, header(00 00 00 01) + frame(67 42 80 29 95 A0 14 01 6E 40) +* @paam frame_size the size of h264 raw data. +* assert frame_size > 1, at least has 1 bytes header. * @param dts the dts of h.264 raw data. * @param pts the pts of h.264 raw data. * From 0075779d38be8e5af13f1c5e207cc06c1244cc1c Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 8 Nov 2014 10:28:59 +0800 Subject: [PATCH 082/800] for bug #66, refine the api to send h264 frames. --- trunk/research/librtmp/srs_h264_raw_publish.c | 8 +++- trunk/src/libs/srs_librtmp.cpp | 10 ++-- trunk/src/libs/srs_librtmp.hpp | 46 +++++++++++++++---- 3 files changed, 50 insertions(+), 14 deletions(-) diff --git a/trunk/research/librtmp/srs_h264_raw_publish.c b/trunk/research/librtmp/srs_h264_raw_publish.c index dc633df96e..c51b930a45 100644 --- a/trunk/research/librtmp/srs_h264_raw_publish.c +++ b/trunk/research/librtmp/srs_h264_raw_publish.c @@ -50,9 +50,13 @@ int read_h264_frame(char* data, int size, char** pp, int fps, srs_trace("h264 raw data invalid."); return -1; } - p += pnb_start_code; + // @see srs_write_h264_raw_frames + // each frame prefixed h.264 annexb header, by N[00] 00 00 01, where N>=0, + // for instance, frame = header(00 00 00 01) + payload(67 42 80 29 95 A0 14 01 6E 40) *frame = p; + p += pnb_start_code; + for (;p < data + size; p++) { if (srs_h264_startswith_annexb(p, size - (p - data), &pnb_start_code)) { break; @@ -162,7 +166,7 @@ int main(int argc, char** argv) } // send out the h264 packet over RTMP - if (srs_write_h264_raw_frame(rtmp, data, size, dts, pts) != 0) { + if (srs_write_h264_raw_frames(rtmp, data, size, dts, pts) != 0) { srs_trace("send h264 raw data failed."); goto rtmp_destroy; } diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index 1875362461..e0bb223e70 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -996,21 +996,23 @@ char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize) return any->human_print(pdata, psize); } -int srs_write_h264_raw_frame(srs_rtmp_t rtmp, char* frame, int frame_size, u_int32_t dts, u_int32_t pts) +int srs_write_h264_raw_frames(srs_rtmp_t rtmp, char* frames, int frames_size, u_int32_t dts, u_int32_t pts) { int ret = ERROR_SUCCESS; - srs_assert(frame_size > 1); + srs_assert(frames_size > 1); srs_assert(rtmp != NULL); Context* context = (Context*)rtmp; - // 5bits, 7.3.1 NAL unit syntax, + /*// 5bits, 7.3.1 NAL unit syntax, // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame u_int8_t nal_unit_type = (char)frame[0] & 0x1f; - /*// the timestamp in rtmp message header is dts. + // the RTMP packet header + + // the timestamp in rtmp message header is dts. u_int32_t timestamp = dts; // for h264 in RTMP video payload, there is 5bytes header: diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index 10758dd67b..bb07e3bb05 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -338,21 +338,51 @@ extern char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize); *************************************************************/ /** * write h.264 raw frame over RTMP to rtmp server. -* @param frame the input h264 raw data, an encoded h.264 I/P/B frame data. -* the frame without h.264 annexb header, by N[00] 00 00 01, where N>=0, -* for instance, header(00 00 00 01) + frame(67 42 80 29 95 A0 14 01 6E 40) -* @paam frame_size the size of h264 raw data. -* assert frame_size > 1, at least has 1 bytes header. +* @param frames the input h264 raw data, encoded h.264 I/P/B frames data. +* frames can be one or more than one frame, +* each frame prefixed h.264 annexb header, by N[00] 00 00 01, where N>=0, +* for instance, frame = header(00 00 00 01) + payload(67 42 80 29 95 A0 14 01 6E 40) +* @paam frames_size the size of h264 raw data. +* assert frames_size > 1, at least has 1 bytes header. * @param dts the dts of h.264 raw data. * @param pts the pts of h.264 raw data. * -* @remark, user should free the frame. +* @remark, user should free the frames. * @remark, the tbn of dts/pts is 1/1000 for RTMP, that is, in ms. * * @return 0, success; otherswise, failed. */ -extern int srs_write_h264_raw_frame(srs_rtmp_t rtmp, - char* frame, int frame_size, u_int32_t dts, u_int32_t pts +/** +For the example file: + http://winlinvip.github.io/srs.release/3rdparty/720p.h264.raw +The data sequence is: + // SPS + 000000016742802995A014016E40 + // PPS + 0000000168CE3880 + // IFrame + 0000000165B8041014C038008B0D0D3A071..... + // PFrame + 0000000141E02041F8CDDC562BBDEFAD2F..... +User can send the SPS+PPS, then each frame: + // SPS+PPS + srs_write_h264_raw_frame('000000016742802995A014016E400000000168CE3880', size, dts, pts) + // IFrame + srs_write_h264_raw_frame('0000000165B8041014C038008B0D0D3A071......', size, dts, pts) + // PFrame + srs_write_h264_raw_frame('0000000141E02041F8CDDC562BBDEFAD2F......', size, dts, pts) +User also can send one by one: + // SPS + srs_write_h264_raw_frame('000000016742802995A014016E4', size, dts, pts) + // PPS + srs_write_h264_raw_frame('00000000168CE3880', size, dts, pts) + // IFrame + srs_write_h264_raw_frame('0000000165B8041014C038008B0D0D3A071......', size, dts, pts) + // PFrame + srs_write_h264_raw_frame('0000000141E02041F8CDDC562BBDEFAD2F......', size, dts, pts) +*/ +extern int srs_write_h264_raw_frames(srs_rtmp_t rtmp, + char* frames, int frames_size, u_int32_t dts, u_int32_t pts ); /** * whether h264 raw data starts with the annexb, From 3358570be64f20ac88be3318e7a0c0043a3681ae Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 8 Nov 2014 10:44:00 +0800 Subject: [PATCH 083/800] refine for bug#66, implements the usage. --- trunk/research/librtmp/srs_h264_raw_publish.c | 20 ++++--- trunk/src/app/srs_app_avc_aac.cpp | 2 +- trunk/src/kernel/srs_kernel_error.hpp | 1 + trunk/src/libs/srs_librtmp.cpp | 59 ++++++++++++++++--- trunk/src/libs/srs_librtmp.hpp | 1 + 5 files changed, 64 insertions(+), 19 deletions(-) diff --git a/trunk/research/librtmp/srs_h264_raw_publish.c b/trunk/research/librtmp/srs_h264_raw_publish.c index c51b930a45..f36c332076 100644 --- a/trunk/research/librtmp/srs_h264_raw_publish.c +++ b/trunk/research/librtmp/srs_h264_raw_publish.c @@ -37,7 +37,7 @@ gcc srs_h264_raw_publish.c ../../objs/lib/srs_librtmp.a -g -O0 -lstdc++ -o srs_h #define srs_trace(msg, ...) printf(msg, ##__VA_ARGS__);printf("\n") -int read_h264_frame(char* data, int size, char** pp, int fps, +int read_h264_frame(char* data, int size, char** pp, int* pnb_start_code, int fps, char** frame, int* frame_size, int* dts, int* pts) { char* p = *pp; @@ -45,8 +45,7 @@ int read_h264_frame(char* data, int size, char** pp, int fps, // @remark, for this demo, to publish h264 raw file to SRS, // we search the h264 frame from the buffer which cached the h264 data. // please get h264 raw data from device, it always a encoded frame. - int pnb_start_code = 0; - if (!srs_h264_startswith_annexb(p, size - (p - data), &pnb_start_code)) { + if (!srs_h264_startswith_annexb(p, size - (p - data), pnb_start_code)) { srs_trace("h264 raw data invalid."); return -1; } @@ -55,10 +54,10 @@ int read_h264_frame(char* data, int size, char** pp, int fps, // each frame prefixed h.264 annexb header, by N[00] 00 00 01, where N>=0, // for instance, frame = header(00 00 00 01) + payload(67 42 80 29 95 A0 14 01 6E 40) *frame = p; - p += pnb_start_code; + p += *pnb_start_code; for (;p < data + size; p++) { - if (srs_h264_startswith_annexb(p, size - (p - data), &pnb_start_code)) { + if (srs_h264_startswith_annexb(p, size - (p - data), NULL)) { break; } } @@ -160,7 +159,10 @@ int main(int argc, char** argv) // @remark, read a frame from file buffer. char* data = NULL; int size = 0; - if (read_h264_frame(h264_raw, file_size, &p, fps, &data, &size, &dts, &pts) < 0) { + int nb_start_code = 0; + if (read_h264_frame(h264_raw, file_size, &p, &nb_start_code, fps, + &data, &size, &dts, &pts) < 0 + ) { srs_trace("read a frame from file buffer failed."); goto rtmp_destroy; } @@ -173,9 +175,9 @@ int main(int argc, char** argv) // 5bits, 7.3.1 NAL unit syntax, // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. - u_int8_t nut = (char)data[0] & 0x1f; - srs_trace("sent packet: type=%s, time=%d, size=%d, fps=%d, b[0]=%#x(%s)", - srs_type2string(SRS_RTMP_TYPE_VIDEO), dts, size, fps, (char)data[0], + u_int8_t nut = (char)data[nb_start_code] & 0x1f; + srs_trace("sent packet: type=%s, time=%d, size=%d, fps=%d, b[%d]=%#x(%s)", + srs_type2string(SRS_RTMP_TYPE_VIDEO), dts, size, fps, nb_start_code, (char)data[nb_start_code], (nut == 7? "SPS":(nut == 8? "PPS":(nut == 5? "I":(nut == 1? "P":"Unknown"))))); // @remark, when use encode device, it not need to sleep. diff --git a/trunk/src/app/srs_app_avc_aac.cpp b/trunk/src/app/srs_app_avc_aac.cpp index 3f5ce2cb03..182da61aed 100644 --- a/trunk/src/app/srs_app_avc_aac.cpp +++ b/trunk/src/app/srs_app_avc_aac.cpp @@ -531,7 +531,7 @@ int SrsAvcAacCodec::avc_demux_annexb_format(SrsStream* stream, SrsCodecSample* s // get the last matched NALU while (!stream->empty()) { - if (srs_avc_startswith_annexb(stream, &nb_start_code)) { + if (srs_avc_startswith_annexb(stream, NULL)) { break; } diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index 4eb2f3426f..21ca885deb 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -184,6 +184,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define ERROR_KERNEL_STREAM_INIT 3038 #define ERROR_EDGE_VHOST_REMOVED 3039 #define ERROR_HLS_AVC_TRY_OTHERS 3040 +#define ERROR_H264_API_NO_PREFIXED 3041 /** * whether the error code is an system control error. diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index e0bb223e70..06a183b830 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -70,6 +70,10 @@ struct Context SimpleSocketStream* skt; int stream_id; + // for h264 raw stream, + // see: https://github.com/winlinvip/simple-rtmp-server/issues/66#issuecomment-62240521 + SrsStream raw_stream; + Context() { rtmp = NULL; skt = NULL; @@ -995,15 +999,6 @@ char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize) return any->human_print(pdata, psize); } - -int srs_write_h264_raw_frames(srs_rtmp_t rtmp, char* frames, int frames_size, u_int32_t dts, u_int32_t pts) -{ - int ret = ERROR_SUCCESS; - - srs_assert(frames_size > 1); - - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; /*// 5bits, 7.3.1 NAL unit syntax, // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. @@ -1051,6 +1046,52 @@ int srs_write_h264_raw_frames(srs_rtmp_t rtmp, char* frames, int frames_size, u_ *p++ = pp[2]; *p++ = pp[1]; *p++ = pp[0];*/ + +int srs_write_h264_raw_frame(Context* context, char* frame, int frame_size, u_int32_t dts, u_int32_t pts) +{ + int ret = ERROR_SUCCESS; + return ret; +} + +int srs_write_h264_raw_frames(srs_rtmp_t rtmp, char* frames, int frames_size, u_int32_t dts, u_int32_t pts) +{ + int ret = ERROR_SUCCESS; + + srs_assert(frames_size > 1); + + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + + if ((ret = context->raw_stream.initialize(frames, frames_size)) != ERROR_SUCCESS) { + return ret; + } + + // send each frame. + while (!context->raw_stream.empty()) { + // each frame must prefixed by annexb format. + // about annexb, @see H.264-AVC-ISO_IEC_14496-10.pdf, page 211. + int pnb_start_code = 0; + if (!srs_avc_startswith_annexb(&context->raw_stream, &pnb_start_code)) { + return ERROR_H264_API_NO_PREFIXED; + } + int start = context->raw_stream.pos() + pnb_start_code; + + // find the last frame prefixed by annexb format. + context->raw_stream.skip(pnb_start_code); + while (!context->raw_stream.empty()) { + if (srs_avc_startswith_annexb(&context->raw_stream, NULL)) { + break; + } + context->raw_stream.skip(1); + } + int size = context->raw_stream.pos() - start; + + // send out the frame. + char* frame = context->raw_stream.data() + start; + if ((ret = srs_write_h264_raw_frame(context, frame, size, dts, pts)) != ERROR_SUCCESS) { + return ret; + } + } return ret; } diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index bb07e3bb05..6406b0118c 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -342,6 +342,7 @@ extern char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize); * frames can be one or more than one frame, * each frame prefixed h.264 annexb header, by N[00] 00 00 01, where N>=0, * for instance, frame = header(00 00 00 01) + payload(67 42 80 29 95 A0 14 01 6E 40) +* about annexb, @see H.264-AVC-ISO_IEC_14496-10.pdf, page 211. * @paam frames_size the size of h264 raw data. * assert frames_size > 1, at least has 1 bytes header. * @param dts the dts of h.264 raw data. From 106bef802f3e894ef4120b0927e68981dd7da4a2 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 8 Nov 2014 12:36:00 +0800 Subject: [PATCH 084/800] fix #66, srs-librtmp support write h264 raw packet. 2.0.9. --- README.md | 1 + trunk/doc/H.264-AVC-ISO_IEC_14496-10-2012.pdf | Bin 0 -> 5038366 bytes trunk/doc/readme.txt | 5 + trunk/src/core/srs_core.hpp | 2 +- trunk/src/libs/srs_librtmp.cpp | 241 ++++++++++++++++-- trunk/src/libs/srs_librtmp.hpp | 3 +- 6 files changed, 224 insertions(+), 28 deletions(-) create mode 100644 trunk/doc/H.264-AVC-ISO_IEC_14496-10-2012.pdf diff --git a/README.md b/README.md index 8cd78731d0..ee65c1644b 100755 --- a/README.md +++ b/README.md @@ -228,6 +228,7 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History +* v2.0, 2014-11-08, fix [#66](https://github.com/winlinvip/simple-rtmp-server/issues/66), srs-librtmp support write h264 raw packet. 2.0.9. * v2.0, 2014-10-25, fix [#185](https://github.com/winlinvip/simple-rtmp-server/issues/185), AMF0 support 0x0B the date type codec. 2.0.7. * v2.0, 2014-10-24, fix [#186](https://github.com/winlinvip/simple-rtmp-server/issues/186), hotfix for bug #186, drop connect args when not object. 2.0.6. * v2.0, 2014-10-24, rename wiki/xxx to wiki/v1_CN_xxx. 2.0.3. diff --git a/trunk/doc/H.264-AVC-ISO_IEC_14496-10-2012.pdf b/trunk/doc/H.264-AVC-ISO_IEC_14496-10-2012.pdf new file mode 100644 index 0000000000000000000000000000000000000000..99f7e5eddc4340cd1a1fe434373e1b7efea01784 GIT binary patch literal 5038366 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
=f6G zXWmq=?ZX+hkD2bjKsQ#nztYxBfSI(K9(TYgb}W8eA7ZAB!>0|h63$k!Iq9u@yy(cR zTr$;S)kd!X-4oWXfn8913iXXov_>xDUtCK&DL2*Bv^2RlC%kI)=Mvz+C|FNW1e}$5 zth-h9SL2fTyhnf|6ZP(LWs41;s3Q*N>1xUxwm3aizTDo`7L8e2HnQJ&b1F2SiQtwp ztLzOGDQmUt8rTuJv~|NBwvM~fKyw-o@MYt>d2RTb-SftaE=s7+yVx8|2#J zueQ$3xn0S$s6V*dUYN`{(8!Fq+i81ovb3b)N(pq!E?SkVLp{Jh+?+;^EPIh~2npDt zLr7jba8^Gx*@^A<@L7j1TukNi{NPNwpQ_|!P5Uv@Hpne7fJa!)W6%3{^(JdD%hKhc zsvPFbQZ{HSQj);?C%drS2cY6BXFKEir;`e!hLpjZDlIBn#hkPBv(xt+=(cO}{_<9E*4^yxiMf zoB#b?d9Sl-C-F(UW9+@l5o@?J<(%Vb5oB!(t*?iBCadONev*oz0n$v+fVFmYch$^4 z9g@OYG5}o3et$Vf-!zh?IlYn@EeoqOqaBQvLH0#@hkZ#A@}*2>+?aVcvT|>UCYwC%}Rs&as z#>eR{o3ARX-?ouAzjA^KXJu#@;k8dxE-uiw66+R&8dz+m0ng-zNhCV!E{^O6GmL12 zjE%G6WM2;cw|h`YM5r)ws+fx)vy+v8#^Uqjac8h0RkU_PybKMdQb+a%o+uQ4*&$wt zZD&mm+b_%9{156o`^x6E z)NQmCASb8VvK_`mf<19oSfUwA>+gIe%E%3ddnYjy=QuFU3SN}=`|`hWb-?)2T%{S6 z8@Y<5IPEQEY3tif1X|l~E^`l(8X9|?Vw4H;AKZgi#-Nh9LoROQ3=jGzhQz&)mil

+|bp^&s3Z6ZNJR*aa>mBO)TKK*hw@?Q`GnKB3 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ℜ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×i`M9h;t!admBottt1*W5-_MD4wE|IulDO09Fksv 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

n!@fk#WRl5`B4fkV6jGDRn9vBvlqIg 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%0D~s$y*P0Z$1qo{|3NbDi6jSBok_n)ox|(Q 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�oifjhI@yMGgA=w@@f-n57d8P(-ZCk>=u_Cw>zPU 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

b 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

bC$ZpmUKT38e7{1Wo3B7jJUMvFH zmN#M6!H%fXAQgTeBp{R!%#_I;bs%Itl%V+@m!N6(pcv%-OpMcD6p^oMRooVWv7J&& z_)7{^9&s|O1XIF>{@N9?g`bmL`x!Qa;_9urF7B|J+ zzw0f`*U*eL+Bke<_7t^5f^X~(p+e|En+YRxVZ{ewPG5Etr_S%}YQK%$_X%7{;*qj7 zcL@5g7ASCqq>LF~%6X$%D@#dmRC*iyti26k=`l|OrY4G0LLEo+E*omdI00pW5i2=E zB#*|Reu?|N6xgQcz#iDBU65kKcQxPZBAASGy}=N|Fqqhr$H*|$8$U)e#_qpSzC>o#A3|a;L_7Oqb6*vok z$-!mq61|$f|Kg>SyDY_tZVX2L6wMgO=l_wUAn>`Zkmx&|3TfDkoNf!A-#f&og2q#* z)t$S6DWHa&>0%>pXhjZ%826%@I9X%s)CCd`Zj~XRooj}gO@DR;=;JI>x^dZsBlQR` zyD1$?x243`H?8VN(#w-aFwYIUwMAf}B?YF#m=b^{d~p8|@68W)a6mLJl%D9!OC3iM zjaYoJpJH(SSyIK{=w-d_yv0j-aLzmaL3vn5;r6}#l=-ez6kipLO}z`BA^3L~&$*iif+H{WdD7_=sqYYo&UGMcPivG{A&#dtvsETg8PxZN@`_OyCGcl+E zY|#nboHMZ5@@G4T>NbILD|kn%H-R}|G+IrKhf_R)g=MJR0XQ?%CGw73<)vC`qh0CG z1;2Z%jeNTMfC#bW@>e?M$aJoI(5gppFmXYqvzF0uJNx&v^B)rxWuN9^;$KC)D!%i* z8k#(c^j^h-g|!Z1dy~_+xL8LxlhDK{<8h}UjZ%-bG-Y6M={J|PfR=9lp{()kXX0-Z zi-p7rxU*&zAj}(%jbyPaDo&Sl%%1X|%e!H89js;+2+VeC*TjTb^^XM%a2$KGYZH5^kiE8^ zEra!JbuWl1cKpbFh}U|58N6-TRG9vj%6w{ndn(eedh22alS0oWt2-Ax4`8QL$FE9) zY-p;kU}zvGCDjW$WLvH`n&$6je56Qxgrau~epo9EqTW(=5tj3!R-9SRGu+Z0zYgW3 z44uYkM%){K$D(U^>ab21X#uIu4pm@Q*9B6IU=~E_8?8T8&G_yin*DZuFlxoun~x{jrJ0puQg;D5(w&vRq!_tcMSUv^_Ev1E5Gi?ZpCFS{^36thNxLF1_d-eF zLrnmTKZV@F$Q^n!1CF5gUMM$BC&uLb82DIG5E!C}8NlB@JZbWoStcCMeshnk&p4ED zV@xmP$ah5;pKtAxqDcUc>?6DB%_9Km!YvV)`hm^MgGjt^R;@$}>*n$kng( z6(S_7$Rva!-MT^$rr&Z*@@~1&c?RCJqvOu~i0OUvJ3)f~@i6Qm3;ZG|Hu!Pr*93S8 zZOlMxDG{pm;>FnDl>d-T>cO;)>XVZXIawgbJUV%MxEN(Julr%JYx&LgH1x{UTL)%O zSb=HBQgSJ?o%Pnp1>>>|J)`)@c=3Lj`ra5j{T`N!^Bz^XdD-vuqYY{s50<6LJ78Cb zNOt%~_#R^SX1g6ySc_fPSI)tiufxWb`$xS$CyI=_Zy1r2p09>V0fY12osWtCg~Od< zLpyz6vNFrR-q`*rWB>m%lsW%3{(nokS=j$?%6+A^?Qk?w-LqSI zCbh7hzy5XF{%DhBq;wfQsd@!{nG`X&FD@;5%2jC3e3}E}?Yj#)Dk-N*o@%iGhvT{t_vDw>rkRVF>Z+ujWm;h2bQIh~>aD97+ zFKUL?hp)=x?5JAKeeF+w_r$<+K`YC?t^!&TAs47V#_`rTHPQ_~G8q@4bH@iUdmSTt zUh3S#*sr;{%CfOZg4Z7Ni@vVTkI##Uiv@!FA0%N7gPQI+pPN#FbCLX9H@1$K@2DQE zKfbj;9{o^Z6OTmVCKUrl2FKvHb5=3iAz)M86^Wy6jkcGWn$S#FG?hek`Lz6hA~-?5 zL79o3;9Z5Q)vjGkrs<<|=O{!WYZNG2qr=Lr?ab$%yxZCZ0lQ% zwuq^)Lu5X!>>bwJ+9=F-pzqInd`q0mdisNB@9othVHI}2N?9o4<|JjhyV2^`t0~~2 zVAc!dS}C5yFlq%bW|HYy%^s%cUV@ja#fl{vHH=vTIXZ`Cgt(LPp-<-7zhehG-sp5Pac9!7MT6#^Bt}2E zj}?BoWKPUNf^MIUHbt>3wx2%Ji-%~dOu91Vn+Z6SxjcmYZY+6MOJ}<&>Q&Br#|^QV z${%ReQk%E+HUn*jorl%&Xo`bZCb}b>lb{nx8cyse z?$9W&4&4?f+~eJW#IqU#M5rlCw^AzCe6N6N0G8)&u3fHDC@;mG#(td6wCy!{gXMBl zYTZm}D#n^p#lCg=&F$}+}NwoP6Ll9AoWfO0q_@~uC+RN}2{Zs|j4Ac7 zWY>@n@@dt7ETY+^+x5H2gyfCp9vb*^kKxR#QN|zg_qj`ch^C#oEC&fcY=ME_D6ey&=|f~Fr*LhI zsYAZHS6{Sc6Qm&anjoS5C1n||^h&LP)knC0pG7U}1XEOgqNg&RFPHI5&$Of5aj!OE7}|=F5bGm+`!y1CQ}b z+y3?$8^`IoJco1Me3><3nYAFKz+$1papwDI75-1E*@5!oiQoLW9!ijgW(C|xBn2J( z*s*vwNGwDwQtTS6s$Lz3xMVaxX^992w;UQtdNVY6r&EB+?W2~RYIwV31T@VHzTuof@6e*u?{9+SORWUvsESI{qMTTI0jJSlkQjRu}>RzrlpH*`^B(u zyC9-M14^gZLN`%~GKz6Sk#Uv`Kj6Z6@BBKyQR2@vo0#RBjG!#XCj09^u=kg-9e5ee zGoz!WQVA~O>^OjecB18`!Fs&#!~yPwy>|7n<9dfXJ(ZL~6pdrmAB!xXRA55rduRTz8kTA6{u2qOFqsS?> zgD%1YXvQa|04*zm`EYRd%*{KeMwGyI&c;=c;;1HP!B`^JrV8kj8X^ccAX%66YE;MZ z35hs@cF`PFRqb$I0pWqn{IDfD0IJHfu0wI-3&CF(f)?_(RHE3KM2X---tu5C={ku; zhK*+Blc(pi9lOt?fmPGfKICCF0B>++J?8AEpXe%L=j2ECl=eLIVZ8O=0&`OuOtG-U z<4EHuvzsgEhrRPd6(BDbW$TJ@Rz+h#e=DgIka>uBCK4Bl=f;c@B)uigdI?Oc47euU zO=+0NjZwsX>)=of`i+1q5vJqsh~{&|`B*h;inj7Sse7B1X@e&9x6pR6RI89urY$OD z7YIRvMj}ulT9E!@(S!~5( zN)IosiIkk2XK%cw4^}fnA?WJ~6cUSa#Zo!3FO*xOEb2b3rLm!sLhm}8=3lle5Ic}J zS-ZKe_UJ`9i`jyTqyI&+2ws7Cmqd#@+cj~1{vrbkWnjU&bf*yeo0G=}+ zx)k|+%D4?aKO4gNhM;sSQawaHA@L9k_f91y5)yrxy1fI>-*q^ zhPcLf7IJ0Rj3llQ_{BnHy^8pB6+j~ylF(Ch+iIC59T>ORIg4H#36~h2wq85$XiYG; zT$Em;vQ4i75I+aJf7KnThk&|RV5V^H-Spk85-Q^JpBObZh29Lg;45Bjur7=318iPG4qmZC4 z$}jdixmkF!QYpM$Bl;{A@^JX~QN|nFA?6GtVF3)Slss01MGiwJR6_4|CkxJo)`UOa z)bls6G%$_z~Zlb-1`}42120QWQmX^BYCp1a2f?F(j{(9 z(7We*N2{9<{MM$dDK_9#+fDz)R(8W4ofs%gxtjB)KSJ)WV7ilpfO_qGfh8op4@>6K zWESPv9+U1IzOr~mZcxIy7A}W3ZyCgQtvJRtD@4EegWN?0yH}@%#U>LiiVb@+L!$oL zG4WPa6iWn}9mNP`aBtZ|OaMRubN?D;7z*{{4Tj>^PAtp>)?6<`3~C69r$HY>uTd5xWx+-Kj6nQya?BmUVE0c@H(1`V~)AB+IPv$ zle5P_du=kwpd>(eJcc}JI4DbDVoE|ga%7C7wqOs%ADVG2mtZ5X9k0_lGUz?OHiu3& z*mKWEQ18h4C%=yl4bb36J3(%>*ouJaMV#u#<-#k0GK1gDzqsEPt>b^aaSgNL?T)o2 z$K5M*_>wYc84jR}zE62Y(?7Sm{Va7|_6QPr@iLs-iT@Zamq!HB%r-;<2l`f87TZY0 zmFRQo1MO6ZD{iZ#ApQ-E{7iPZuV=wT%uS;pUzX;ub-qn#bbwO2D9(ZR?fh8yinN97 zk3K0YE`5#h(`-HpdPTyL#lNi_efe>-?Ku3-kP#q4Pr~TCGLGb1Bam+RVnW9a?U;{e zGa?eX7-NpFfF0cy->_*3{n4d43K6V^Y4sub^a39gR}~h_rwJ?6kin75(8owi!jWa2 z3Utfi8_?1>RFgO_=giAjZa5|hllN_^j+43x``e3#s$tQW^ zJv|qn{lf|vvER|aB)%&G&0-r(iq4!?&Z0Z*S z`DON&g$O$ZzFHiENQxqE8hxdvXxCs4`}P)!fvoWvLk71}eogn|#X1i@)={<f31D z_!WEslOTeB2DOM4 zyJa~-y@o*Wv)$1UD@cDuk0A|lvW(x%u;4|3?k9TwN;lEH%CuF2yqlad`q_@n0v$k_i(VR*ig$|Cg?krY!TwbGb z;#@N1ljM0*`vBZ9S@6_jE%ny>!4I3BGMPjm_T$0ZFA(t+9@(7JMZp0Keb;$W!xRTUZW_*D%J8j zl4E}oSCG7+SB5d&pUc{_ZH$}#al3`0#%priEiuPsbnM;zPs9;M0V%}xtB(> zeUr!>%QOhB^rLh&ac03f0HQR)zC}1F8?nCy{}UJrnW&0@NQ+=Vc3qY*?HB*oO8Bne zS3FMqkWH4&hwCEJp&0x8x6H#5Xi5<=R))fW$BGY$H)#!Y-34P^Uis42?cha)sj zQ^JorW8d3(@2W^H%hl>CvaRPTr8MG=1w5_|R;p#$djy$w%D7lc*C9Dw3D(4X$bagA zeTi$s|C|V2u-(k4nTB+1s95&+%X_??X^UjvzyErukLQLk$7AJieFxLU6qrrou(L@6 zdoBE8jWPkb@Ns+y<%(%S54q+}m5@($wa#!BMC=;g;Gk^W)X3W!otc$ zP`k6|m|3%tH(+|>Wb=3mIq36diiHPmTQk3;Ev4oVBU#PEhz-VhD7cNKz`HM#Z(lqY z!<`8F5O#L%i+h`dT?Id+Myx5c3}@3)Yd~rj-><9MG;1|CNY_hK`#toc4WwGNcSLC$ z=eE5!wl?Z4Fh0e0LT%Jc%H=WX1Gm9v;TD2TT4V_MB=DmNt9Dx_Q6BN`QD=PYBu0nz z(S~D23jV9qtGCPJ86OY~0Q)50;6JB$|CV zXG|}zZ)(iP2WxC&^e<}0$nYOT?tcTc{|AbvtjtKj%)syubo?L0c>fl6@L$NPjI3<` zE~^%4-Nqb=+WhH}Q}_)eF560z`~xoF_ckNT=I0H4yM+r>J3r@CS&BOA^eVB$`YI=LIbB<^3Pbcl4uCB)@-`8JJxleD#`*-J*G8nri0+>)92q6knXHN*C z(g)-P^)ZoMFb+Nq_Bkm67vJi(z>hySwU^u$(t$&tvwbGv??=J&t3FssIk4YDkyqrq zvk(?vxwk)-4Dmx?BA6#bW81mzG?}kFe~wVgbK&e0-p8RI-E~1$UbX_?bGvx%91ob{ zf7KT%QxxaXAY)=Tco3Hm&_)V!RcRo}k|+@3_;^{hG;=O3*Vq{Q;MQlkKPx$Rv1qxc z&$#0vYiOl8qx@|bJDXD3vQH@y{Jag_pzkV_+ZI1C{RL@ITg+a21LhZrzj!MMO!B@*PVQBojKe5Rn~+3?Yh*sLoE-d*i}U6PX*6%fc&rb_~YJ<;zweW zld2%L(~4?s^j)gauO*uf%^fxjK?D~;Z{ru@rfEd)nzv<_3u2CmahpRhQaW}))7#P< z{z~w@8Z!kbouoK)Ldl6OTEX#1elvX&W7HctbiMK0aC1=}FKJtBcVavYQJ%Y8-0u`# z2q*%-3w>j(Yal)7J+SX^Tg8*KBG!wB15$3OMOBX+kZ4rdx%qf9X?ihtL|s&(K-Z!_ z!LGeXq}!CW3-u4Q$PR;k3;1}Eox4WC?NPza55BHPzc|k1ik_@+3wR8e2!91*@_xe% zE%)thc?U1c)DIUhtX_8p?j*^NBWt+B_Yt=c#&A$$iEU}X&m&LfPjfc?k9=E3K zEL4JWlk@xzmc!N3H39>7R|tS->Yjc1ebkw$a!R7B!FOi=*wz!fis`?`u&0vry| zNmYyji!s}6+BSZ4>NSsP(yS`7dL=jEg+HK>+~I;&M}M2W(YDR>R&qUYD3fX4Y<6Bv zU&O1$5Nabrm3-j1$Tmo$DlR~gBsmIWHQ3C#!I#roz`y(!jM0ax%gnXvvGOPv{dhcQM>-|7KBcp${4!iP%D;75tniGH*9?jT9@G3!FxkCirr; z%$UJJ^~qQ9T{=zG{7kP%Os_d~vhXKHcP3%t2@<&RLWW*Gg0$z&F6C78HeG+h6<4;wR z)0N2MpVPi49K-5e2+F z{1Zk=qFw`qO#1G7$LTq}+f8emhf#~=b|_%1dr?IQHsj{K-t_~^cIlwvl{nQRBmc1W zkNtAr->8Pf5$OF3S7H?OoT41ZYod>>cE{&0A`gx0wSKO%SCR;Y%w$V$%91(acq2T_ zx=~$-CUmhYl~H=AvOXHLK}D7fm}mFu`NpBB^|7=&v>0AS$~E~P$=(1S zYZI0ho89>Kylh-%HAWT}=re`>`zl5p@4iD`*4!kK1c=A6MON2?1J zbVr*@+Sp0i+RJ=2Q6JA@0f55zAW9}KzD}!#-syWluhyeJ;O*lSF9X$~f=Yc`gLMmL z;(Dfs^yQ=~#OV`yChLq@sl{cdEWKjV=xTN?mfRs6-kcg*N^|Eb_RrA^f0$Sd)(F%H z1G6P0_5>SfRmBz&$n%$sE45e2gHfQ5IdL1$j?~_djkl`g z4J5=^qI_A%1XdQP6+n6`(j-w>lPTiQ;K}@j7-Sn!uiuu*_QH~Jr0+{v6=99ai}I}&-U0XveX z#K7wkLL${`b#@)*4chQRo6t>9^5Hg>E+z%pp_#G|C;nh#x=wgY)xIg5xg!c0K?I;y zBKuPux;o`6#OmvH8gHcy$5&kY?N1w5K8_(JR}49pnNL8+4Gr z>M}~N>FIJ1H;|U}1hT@478FDUncC+!4Av#-tb}SGYwf2jgS`n`1XmB)>N?5QR z%yu8$i63rA>&jok=3aKs@8e|;`8<1W)v=lk;MGrHcBl-500IsgnBXT9!re4PN#)43 z$B*WPa1w0d2NoIK9vKQofYQmcRmwl+L7oZ95YCZyR?1 z794N{0hkJ$0veTM$%WoymJ4Ow1znhM`FKxzk8h74rr8;=`od8Q*TeR=#x{|G@;%+> zTSij-Z042}lrLdn%pid;1|8WqRHlhVoJy)=;BPQfaou)4)6~n@bPI;zr(X;od!Ic? zA9&NRKl{_Z-w6`Vc1i@yt8ul__O0$}_0?~GWay%wa1e^pX>C)$0WqORH1v?$vBR8C z+9|*`lV6){K4k%BJHAJ`TObzdaJZDXu-445ZWUl4#Ha{iZvVU(W0cmb4bRlG*z-nv zetx?mS;JJ%yV9*4%5-6Dze8SHfhgvBu_;pz*3Y8{+Z(i$u*?6(hghQ=ooKN zI~B>sqUEh8u3?41-1BvcI3lpwf`VqmW4QWz^Od6%HteM&OXd_tW-5}$ zCFlJ5N=|UXAWS(VF7aIhkGhGy+D6)c-0y{L^`cGbY#K-XcAS!Er>VZb0AB~&_CTM( zSRWdtcx11LmQ%i<-(_9`CWYK-N2U-uhF}BA(#vbEEXP+UXd`S%Jk5e}?wU8Zg)-tD zFZAkj#!IGxixJZ)ioWp5gsEpp^J#?WIy!hGoM~?@ie@q8nVS$row{H7Siecmkats^? z_83JR!+FS%MX&v!?qxIAO817%xi%nGSf?FSUUK{2o~$3lq(^78E>xgk1S5a<<%I__ zkV_C*a1ux-suDqxDbq)xXJJF4qH$xI3|%Fij77vq2hROj8IMHl{4f|Ijg#`(G!slW ze=CWbgf#3ObAR@OR+=ThG5#N}XM!PoDjyOw@5~^?BI#St)@N(&hvjvIaNk)Ey|4BUHj&-|JCF?;}#S)58U`P@a)!^|I4$56Pgw;LInxR`!HMnZAQ&MfPJjuyfD1NhJI3#@6ZY97nWP8v+$sx-0hq}9 z%R^@L$^)P_5&~voN4$pI5$GYT!H&+R{Zd3s##3OQ=$rtXm;E+&j9zk#UVIcx7%_dg zsaZl$qy#gCl*%D@5|+?L3{|bRsEM9sMu2C4aOXAl8ty2nqC!4K8VcX~$guE7PY%JM zR7AXTdA!HBd8eaLs+XHHq2d7}GuQ)*nNXTYG5_`JAd$Kz9aH|89T+nOH~aKhTHOUH zxxx4$FRxwbsG`>%^y7plvoAYcaLk6XKu0sGU;SDMy8~`RlGug_nBC>MV?A?n zL+2C2)Qs z-kJT1^Nf3UeJe( z)F?JIS{)jB#9YwYDFxFbDL2uB6o1MF!zjbjTHRsPm-;bjPz&;CA?Io(4RD*;k*DYFAVIw#rNL6W$I ziGKf;4h(X{Fe?WcD3{YM$P|th)u%VBR4!T!OuiV1n|>evnJ<5 zMhB80=U5``H3rlK76JZt-p3+?w^E-WkrC4B_yqs;8WO><;(wfUVO`w15xlr0IUKc6 zdHDRH)6L!do8wB4eVC9#^jPOxToA?}hA(vu`o zjnUpBPmC>lda&VIN7Sdi=f*oI9n>n~b@F7mwZPxI-`2qy-Ow$Xgscz?!kg8P$C4h= zPaY$!Tg^@P!qkp7EdkMZUATSg`5rgp%aTP0`MFtu5(!rF9@<-1MO%S2?yyF?A?s%c z94ow&6_LuXgwYWYj-$WFpAq!e(y((4=4kI@lJ!ohsio+xLj*x^~dO_zRYq zeUP2_B$mOt9J+W^BAfB zS_ZUT`Ec!7KWUhYC<#p0<@|Ro)OJ%|NDkfP%}fQ^#s%W`VtqHGA9Z66q6iuS-wM;J zVmTTFaWw!_3u2Q~Mr~>~=EUg=>|CntkQS>}F=VQv;-qb3QFQ;jixq-k{jO?SIki)Y z)OMO?C1XES9SgQ8-Fh*P#)fu0#HN;^&Q&re6G-54wRT{R2>|_}E#{5ud2BWuFWSRL zJ)^LV3xFWGhFTgI-KoV+;>M|cE6j_2vbyB8&{kj@yQ^AOC)i=eHLNDpiTYd*!@|uF0%k)%x@Vd%QONA3NhnQ;GkYXP@JO^ zxzm|GRZQ)NK9*c*O(NE+9%QSH*n>%0heK5H)h#XS$(m|$1HQ|w5JXv&3 z*y>){HD2bJQ-{#+c$9V&mNMdUHA~w%=eQXzB_i@L9uqbj@|cP(O{}Js#wgLT7z|3N zym>vHy?I0-9bT|mK5(38227=~l>{fm56i9^2P(DWW~iOUG%k_|r(_>VY_@nG!L^4a zRVa5kviOK$K4B1UT&(Ea=PM*;l0H;Zs(uV^$o*$T|5)KSy3XKnUY}frU3}FJfz`&r zdyr{(jeMSDe@ox&f;w(6gcH7b|WRQx-XKH}1tWdjmy!|qjcg2|=L-Ky! zU6g}Zsq5hJKtn&=I_Q_dahNJE)jnp7`LzoYE^}{H%{{QkzfPd<6tF1gGujYV4acL- zxhvX3dmux{9bjMP5Th7bvzOm+JmlwOBuf4F>%>>M)nIp~7Fn@E> zeC9R-6VIxc_b(e(ha~lzo}R9yX9Zoi0&m8fc3!|hgA>`ZRm15n7I{)RM)>SJq{{#5U$zxz%4 zm2-CNE3a_lHcVBbdi6Iqs|VPFYwEtd(-sP6=eucDph5vnNjya7$g1>%Fzry>Rb+b= zYB)-duwgN4I;8zi|L#iCJ>a;{7V7>}j&|~~qejmU?#1?jtFTs{!Q<3c9!Yb@nvm-Y z>|f}(aoA^FFB3Z&cCt@zdhOyUyGHZ(sR3dfUN?+C!hw{dAJNHPa)fdpfFe5QsTlt8wPahN zs2ke0F>2m8-U;>}+whED8*07xgkTxEd%kH0Q|OpA4D>MD?=&=`#Z9v+o62$5-AhQk zD<^@ukWxl6IlfhZ*O%(VOZ8+s@M_-1pMB3wvkUg7LE59Ca15~nq;WpN*4CJ9&40IW z+kYS#02-tgaQ|}#`){eH|A)acGW>J5|9_OhGBW%lHU5_j_Meabe;Dk4WlysGhfDb% zv+(iJ%RAT_Dj7Rz(aQ^q(kmOgIqCcx{QYO-|JI&lWn}-qu6Z`0Klw6&@AiWUMY|;&9@AeRtyD zs@po-D$mL)m!H1hD4C)A%e`AYA3rZE8EacNy2JBa4P*trZ>np~7BZN_mlLAl?cQFf z>EIu}o%_FLYk`n=e!ycZB-tZ+j45Lq-a8@~%y30o_5=jd9|1BLmLbjOuCZ?r$hZ7^ zgc+57(#317G?Xui-qx&EPPVV*Dzz#mSIqBhEi+3`QVAk@;TxS%*C?a|*M5*fIGZ!> zc5VNBM9b*Aizw76>Fh?7PCNNTp8J-z8)TYX(zvxUm)7OEzwA@xI~cOD#KX=|ym&9> zNT-3zg-)Muv7a!cX>v84^;zHUc(ItJE)TzV&^ShJKR(%Cl+-f#IvVmlO&=RJdTy@9 z>f?aQv=Nzfm1>v+_Y=!8vyd--m9RE9;k#E0gi_^j4y-z-RT&;nPssE}tQJoryMl1m z>K|wK-|BbF1coF3$_bnQB%@iCnEQoa#t>9*7ew|!dZ#&kLSq=tsc6$4&B5=>PQEj( zfjuQW+{6^Lg0~o8%pcuf#&~DU{L&NejLE3~a!!Y}Gsaq#f>~))8mBC}!r;J| zsw?U-O_jX0q9FUq-I&v@&6+LyX%o-P*LY5HACT9mk@ubeu0w`d&z_<{XqFZJIT6Mn z2g;M23#{AoKo<3Gg_f1o)r==FWF=$2d|mGf4u9H3*3vOa(Hk$V->ccIRyYsFep{H~ zKHfY$^;KH>`CR+R_LVx!s(tbseabGuCWbf7h+1Zd)Qb}W95;2L*_DLRoCpiE)C&NC zDaD#Ak>hZ1pV|F_T-2po+*))g2=IA?HG6Y#JnQ9o+^XY68K0n^1YSZT`h$8Rnax*! z!&+#SBp-MA!F;1e=ecZ?^`m%uZ;@k}u+h(_VMaof{G(p0bAn7~((rX_Ee8pV{V&J;uHkQ*W1oWN zQz))!(Ipc(XVe(xQTv5vfpLm`(J)*-UkM&zSsW z&}A0^u@TR7I?{+IB zOj%Z8b}f1;;3a5|B_3Ert|scm(o1_ceG|3EloFTs{763)G-CTr^^IBWlD{fNlxmLo zY55v7B#vv;(HSM)2N0;y!mo|Em>6*-ROfwsdBX3Jl`Mx59OK*LpllGb=OWXE(c^=p zN%B!F#a3@=J@yf(tG#_w%F}dEF(#bU0)Pm>lEeJBEK|;B&NLYUJ0DPMx`==~JgnTS zYRt2cM*DxmR_=lpm-@80(Uc@UhpQ%v&d(Pu6)CKS3P$RiKvNkp!Wn#Sf*+qux(qCO z1i+>L!DU%%c`qSDMkE79RQ-Dq;iptUO;WlEz`P+C>k-wgF+_AwCqIN`(oU`5sWR`{ zgR5~T?4b9bU~&b>gnq4l0Wwi~GPx5JO01 zLis2C9@cq)NhORjhJABGFj{|>r(F5B+6ClCxxOi(@fD~u^14&G_0s*cV)vgpli&=C zLu@fc6DzM?eKw8Fh5YcE2{hGK)d=KpG}dNCgWl#L0e-n;<}wvOF;KUot-@ga*DDud zmru*dqi05kw-@29^ApfJ0A?kANCptf{B5Lu73OykevJ&DF_ojF*`SJWs|h!YSqm;S zf%pu(rpA1UN}N0KpK*|6dH$%c+WuxT-WwX^hKNx=6k?TN9Mci zu$w>#+Xs(!TuzPVV4zo5V-TnvUz!mA;)<4RFI!xptOmnauhWb)`|@`{VPE6)QfK8@TA?P6e7JPe16s+5 zLSv0UEe$(y_J8;Ki0d`GY>KP3+wMgmoRh)svL%CUyp?hRju)&X;c>^7S3cH<(mdH!Q4^R;C(fq2X;VPo5**?5;aPNoK;Qza0sFLC z71<#e8LFOPqd&Cbw74~1Uz&`ZU2m?o2rPO_irpv%X>nGnMaKtd!x9(6afehn)qE7BGP04;$0>UDjYUGCvWVdXeo0Zbij#_jm)W6N_?V0Cy|sS13D<{{)&HRM?%(*sHgYD6$hw9f)cw90&s7Tp}8N$8OPK$VVX z%zW^i7xIVd#Y6R~Jj90Rs;#(wwk|>sf=Rz95)ze?gh8k*@`LhmSP$dY`}*zpOzZ(5 zPbp{}jDyf_=7|uM;v>XmG7$5Zj8j=g&dK!)B3h+^NG96i0K8#AeB4N+>FsniTOtT1 zS;;|2a;SDWZH3O%Vle#)QfOC83Y6^bOgjz?+f=R_2ET~<-5DBLwfCz8`9K{lDJ798 zLh2nVP3FacY_B<&8?wIDWy3HcLdsUz61U zQ+>O{5l78HWTRR_jFr^$JXz?}5e55(g=6YCoiWr|j@5|dQLn9n$hZhrjt1{f4u<$= z8#8{|%`8w>B!P9L+DO-x!CzH28b`IpyRFbvNTE^W40K$zk{kY5>ZqI#py+_E*}pTn zTpm5Vzsdrg(G0Ja9x_6w;qw&#ew;;?$(Ote>wiyb@iZ zbYypqErOpm*b`g_5MYQp6e#!b9FR%1MdHY|PZVV}QrrWSL8}}?@#B*^ISg{G**%!Y zMa|gKSm2 z18qfB12X%mFu}>S)`v}_7}_nzXtc&XL;bkqcTAOM(ejwgJ0cZIHw$q?U~ovVo74@3 z8pB+G1>$+CB>do<;BkESycbBOL)!i>L_Dakk^GExC{$zqLxHcuu1FK;7tafW6N+{= za_?~Y5(&Y**$k~oicEQv5K_hxs##=%`zyAb0qdpjgw>4wiw)sXCCl%YaZQHhO+o;&K zZ5tK4YxU}buY2s#>tv7p6Q0B8y63#6UQ@8PJm!Wz!5NBze$8Mu4yu-sf0Rna3H8@J zR|3WCt3Q&QcIbRFA>yY>q2rmTxbn%TB=fD}3)JTFix@ON8?5mKsL#s`rGnHwYDI$z z1!piiD6lCPCja%l#8i)saOUA2JuULaOC^%lunK(h>c7*P_$tJ%@X{M>pmv~C4(Vjb zsu2X$snVgeyy6AT;rw94)0(d#7y_%>-nm@%+-Vb^kv=BQE<~M86~JQ_d|!^gdx^7k6ek^u^##hF#PCW^gxaTj83tZ(Qb@}8 z_cJBSJ?v(u`He7R`BURGs%69;jephrJr6MP8B=yZOEo;Z@kGCNcwN6xVLWr$Uc1=L zSb2Q<)vuIcoxk9})*m-KC3leYn>!COfCYS);X8kloYZzuJ*lLE#(XAtJSGZU34>IF z2le19%kRBE+(MQdnw+sd(SoYON>OHp(%E+Is$E-mN4S;nBB2xgakC>7HCyI~k6E2t4t09K5O; z%oEN#8a>#5GEd*=Me}M7H;h0CN)ZEu2(DeaRw0B4m)0KNs5`bn7nytJtK6W1aIJzd z8)?YSdA%AHg$rP9b5h2Nwm6-omt~2=wX!%L=*ME?39#lPk!Y66Dy${C82ymq;R3H zh|&DEn{+y%T=`(8d-58?Shr3wutMs(u_z_#Hxq@UFUH59eGA7!^k_+Y6x8uItwh9- zKNz>l7(yVHCl_J*91r!giM80*8`(mq8~^*R!xaj{LGr#xkL0023K1P!tloh%cCx{Uf!J6&!wE%&BE-qQ!A%Nt$c3l?|AE; zF?L6ZHK%Fo_$W?q>FJX?alqf3$wf)OsjrV)PB?uY)V~5SHB<8jyiWKO)H%A>a={z3 z`{T?E=rH>?3haIa-K#}VLELEJd-u=7arT|PrPxExe{DD?c>F6O@GTEczf4aGh7 z)h_rGK&w`dRhWbPj3;#lA1m8iduMnNX?0E&H%sZ8qk4)h*)ZgZn)TxB)!1 zVSYw~;W|q8w!!>nM193uKfFU?u6_{X)^1Xj<@U}_x&LdAhL(8Q1Qznkl|R&=rds!~ z32_*wXs1lrf&#B?NQUM0guBj>k{36}LeF+ZlZlBa1h2$zX# z`cGgC9*e7jm~aG4@FXlGv4Z-!A%MM9h(OCP;ZTU(0ReroNN461iz2+5T7SuoRaBZwXRAzPfg*Bp zNNQr5nUVN(^G}quY}0E3P;EuA`6Kl`dmdw(G19{C&-yZZ`PQX8=`&L3Y0T*+_QnwK zz*^BCdVIov(*%kh!r^(s#W!Z^;*C5~7go25Vp|>1oQ_SH+6xU=O4|g22qcu}t%P|7 zXN*-G3}HLl55RiPg^*pX7(a-1zu(x2carnn=kfq!mm>4%wDXLV(|Qj_rzdRUKfNIz zVVZ6Kh4tkA#bK`xn*0A(D3%HEUy|1U2SENOiv92Y`Tu4V`(Fk6|Dn{*1o)4Z{r|=| z?Eg$+|D#O*{}X^^VPg5u0Bp0?cI`hB+w-F2Y>RPp6fX4ZCy1Ki;0l*gyBZ?!L^^jIiu^I}5?5 zn{-q{(uf0qNDr>=VC0}zmVWhN5nTw*-gOSSNunp;%0B@f53ht(Vi*4vfF-WiFywPL zc&y2TC@~Y_zbE?TuI3v+3mTl zdo+B-{g{hB9UX&YL6CES`-?C(m0b7Ufk>k{a&$xj~Rf@eADNaTMW^B;37NG3dSdqAR3lB;}b{q-gODe7DX)Twg zvq&IqV!nw?8XrEn_<~^c&S-UAzHuSn8F1Y;(h%v-z?(%y+t|T}+)>?oy)n;-jlC_A zYc*m_tY+EGg5x5WhP*f`8tQyzV-jA$Kk*>=(kBEbYS?pcBfXS|V-VOs8w8c6=FX)} ziBJ2k>N(r8zBrT!9a>sQXl&+Hnr{&aj33(MHFuH|*7NL& z5iAG%^$c2>pGIxIx~Pf1b&cK;?8gI)=#%$#WH2;^6PbW19lM}njd_pE(q~HRN9F9; zp2CSY({4<+5`dir;UWy9+JKv6e0rz*~C`md(W*SKh{mFt4 zPL=HPfj=4zoCnL23@&d^3V&C>4zI6sL?+Dr^rq0ijKf4WfkpBiV8uCL{RPi#6-CQZ z1(2wQLDd9dkzl|wsNWi_!)C||cs}##x~tpE^kf7jBnutew}2pwHD4a$)S~#!g}q1t z&n-Tg?Nx{f!U~62`b}477NWs)KoRTcmFuu%S(+8<)IBV7b6@V}m?Fp+`~KTaOI~Q$ zYX-Fa=wG!NvGZ)`z2t0S!C2(?I|rf=%E(-DRfhj83wf9uKi_x%tA{kV_>|hiWCKNv zkl@c2yPS$#sBO+d3g|hzFnxQQ> zids!Xn!D&QO>pbl8GB#Wjm)pH7YtMOL;y`T8CeVdLT1-5R2rL0mSPm62wD5YWr0o8 zt7t;>g0zWv$ez`O7`(Uz4cvu)X zZ=(`5#M4@Ik&V^PDE;KAtkn7Nm&|+p2oDD~1til9mJ#y=tf;-fW6iTzi{5sqm;hB% z{6XVFue6%U)9yU(;1NzGqN2r|!XqW&fEdLRzU)B6IqmUS5i*Nb-Si7stuzwSj3Z;> zKlG~R0dHRx)$`^KSb{o^BHBl(Q^_-x4IUVaC4A5ZX5RD-^^CS9s>RejC&4?7K8lEB znBqjrv9`wjY9$`ob*vzm1h*bl zA);qC#*45E%3F*MS(gueF@*Z|%3qc&j7-0jdr@D;*`RKds|_IOgxun{iBe=St0a!g zZBTF`tX1nL$kGI48lPH`a7n0OpkPWez=HjPW(-gMc_Rq#pSXKTJk|vQgObRZ&Z9HX z&|FFWT3syK{&QO5B5wv*(_G6rvrE~L!~fkX<=~%o4Gp4UVqA7cb26+Ri>e` zyHY?Gmu8-PV2Vzr)AU#>1a4;X5GE&0VnyYo-Flf+fc$flZGLIt!d!C{YGU#AVAmZ4MElmm|yLde}OGO9V}lx+og~(y5I=Y)uPfk%u89)CJ+o!~Sxz zqk3S=qA>nIrPyIdv!4#{MEpk7;vv;4a+qW)|C3}Guw z5(3{{?8L1%Xm~WnkBD0~o0dI~vUc{&n4dBv%tE4(is*)WjN_r-O(9l9pMn9(FG50! z8@hJ0d?u%ybYFk5QCs?8P7==GrGIjmKu^Pi?C<10c3B-N%HNU}Zx;s?&d5lLH>3?( z5eck9yI{*#9%wyNu&@B|rr9)Ngx@5w4h*^a2ig1T+U>$yY&Tm6o8Y9-=hz^)VsL}` zgUCg2nKf{~(icH!di1eI)tGN_2s}_EYg&E&aNf5x4ZiGJx0@jp{&H1UmV1d_K~VL5 z)>6I^#4ilNR=Ul)MsyMS@LlZLhRZh{H8fV)&l=)42ZE-7^_?0IbF*Y;9L!)#>gbf` zdtX6v5A$sAb8Ph{P*vk;c`xHNR!#N=V zzq9(z8xtatg0(zLfxywmJe1TejR0w6-?*ll%uY@(9Dj)<}zdEUv>J$McG<}rMUr=sZm zw=e8i&8jknynWB0?h>DDDLgEJ6+~FP#9YY7nl65Rwwq55AvMCZOVUKLtM*G0Wn>33 zE=*XMm2OqleC;ae);vVp1L1XhPLfM#DqGsHAq{5Ej|_z!qh?w*a;p`cS%Q*ze-`DC z`qX%!GPfc)Z;=&v4fl~?o(xtkb` zu>t}ju{TitTa-&ek$K}fTUNJMnvzVD8As;3v|DsKX_JG05xUT<8QsIc`0qmu*cdCr zy($O^E?9oB?(68&L_DD-*z?xsYL66Qu*bkg+%fztrNCXLALU0jgjobb%bOCVAhqvxpB9fDw zvD!(&e{1H%cpyDTaf)`Wx?S3{6EAji4eEcr$}1&PmsE%jc66Gt0kvi+oQ2rN#!t8S4p3Q8BL5aVfh_lf@o{3gxxig7!jVg5 zWQc>7rN5>UTVXFwv#HFWUH|&TB!?KIu4G2<)r~RYu|@zU>qunok;msS7vP655K+GO zS6AZ?Usq-qFmFwk-2%O*b@k!Hp?Wri8J|tDIg=-Yi+?-QSu;s3gBSyd&j@h&C3i%f zCi7}fJ01P=sbK`Z>q`VaGYrpw;$PY_o~4@1+<})zg`W`USJD3J^+o>9i~mb|cWula z|KlrzNG~r~D}#3SL`Ui)xfxD9NWbHJxW-#U3=p9!d=)5i_lQqK$rPz!6_;^qVd(Dz$-&@^@bz)g*zug%E1PEL zMm(MLd}Q>26K*qGXrMs4@PQ*k{Kz&GDT-s`x<_1|zTN=GdQI;5?5WIie5)O7PL{Ye zE7$Je)U549?g&8Cg02N0xT6l$aG(F~H2ebwe7RitMmLyDR>t|P2E>D?@RFON;@x7k zyuNJ*>{uTnFH1)1f=kB*n5O&Q*n=VC+?W-l+)4F4v<{bSOu$No$8;CE1=cKxo{O}t`HX|}}U4xL;(YQ_`Ph;%?IM0pZ=L&ge+MDBu{|lJ}fSIw&B=K`$ zp})&$ITDD|yfM4O)g2yBfce7&HW02Mi*;EWnnU5Ylg)D3j5V8|__b^KA54VHOv}k* zEOBeS4O3@grh@<~T3bg2;RE_E`b6mLh=X!Hxsiy-NP>U!PVvP&Fz;Ypv&+mNp9YD) zX#8!GcP9G*%VdRQd?Fh-$lY))(NG94KRv%gY+7FwzD?U2PZ!*IB+q z8GBCkvMbTuHDnvH=GutlR(A#QsRXgof}N&v+5FIr(b`oXiy&?05xdc|O zjwGB1^Z2>8G9%7s=5D5K`C?(x_fvHAj+8musU~~=Sx{5e>bGi0-c30YXdBG3wh10- znVC_`CE_;9D7fJV5~?3b;&sCQ&0*_l2)AC;X9Ynq8~wYDXtx&CjAW}hD2a`INtOY? z(!p`FFud*+xui7b?!#z<&}=JdDXzLR2~7MYCsiISg?n-f8dFZiQ!_qp*1uxBv8-wr z#*a_ZWu}rk2D3eIvK}fzr_X3OwRcFbRL1f7z1cCI310KLaN6!_XO80^l zYcQi18jYN6h0vzSjAHzP6#>m#> zqR?esoQo0`bu^aopA$4K0_w(ZHUi=ofu9pO+pb>w^<=0jbnoqIal~Q8+BW-5 z9g0O!Ybu57S~R<`xH6CJY=LYPbdNlpE+tL=_r%3zm~o4QRrz55FG}St8+rMxtyT6y ziIsA0GS%I762Wksv`jJ7D9K~9{^adh86J^K86VUA3zp@9q-y|Zm6bfvUHZZR1247A zw1VJ7t|{ThV7T^~gX#?j_*-?=ma~OSv6zd3L?X#;gfxv(6wt6X&;l7127a&#yXh;* zq9$QGltf%3acqtR&rBm*tgv#C6De2{3-}5!GjpYDO;hFU+7Y%M=9~JTd$eIt$KTB> zo2tkSD>=6Db1LOPi@Kq~+KXV~{ZQW6W?FE*+d2NA=Z+b1x*m@T)j)jeFQ99t>{VGnJiT7)TZ zR||frwJ$wu7ZG>bS6|`wAkgWnhDr}Is7W@aCdXt3B;r_&X%m7v!B)RM^9Jj8zAgvS z{FTrcCT7SIZzfy{A-`ZjYucmtF-3EewG%<`aH#)!irV2%eG~1|D3xeN2*Lchk!3|K z>U?KawQMmakW#M5Ln{jywxqQ?#OaU1&6r5BcgWp^ULMo1JRz@mSDb$ARz^p~W^PGs z-k2OvmTG?Sq8F{*tXZMAwS;>cy3wy`S#x{ZVc=4r`kgXJQ*=92tej7OS-s7(v)Qe! zqa5{-p=QBG;V2&2i+j$g=7b{rTha6#H_}Quz+fuSBfvOrXq^a>(wag^CpgdZDt-s_}%FDS|~5=`3R%rQ3(eY5;8-AQk3rK!J3X&A3?acM@6@?tW^VQ zn68~j9=YMv!Q&6z$Bm!>18!){Q6_v4$ILpIVhjqX6z3+tb0n4vO8j&=kwigOqQ~#C zp7<(RD6I%;@ws5P5NU@;#h%QooO~*2fw4vE#oY#KE|4tI$Y@N3c+&@toTfD$Ug2Px z4#?UssN0IPWl^pC^NF`oXP5B%@9M+U(Dm!DBp<&x@Ma>*La z%paAZm{tWe%rz=1DQh6mQf4C7jvrLjdT3Suurb5g+k#6OZH(nsC|^rAt%t8KKiiuE zy3Nvly+01QnTP_$z0gfI=tBzW5Wl{WLOH$J_8DLNyo?m3brq5~ol9p=c)Ts82)_RS zwok-^Lox;z<4~jTw?dD9L5IrWcSRR$n*yf+G3~fDb;@5F?7g7+vKSC#H*6#9GJwkG zg4I}*n<5?ponMEy$oe9)4VzN>?&1sLf~Sd!R!%cD&&jn(_$xiq!G|#pg<*wKC#~Ic z(rjIHBNF5bbhQZhROV@V*vc&q)^^T>J)oM|$q`>GYj zV%Z&U)u-1Nx1D?T*^(y_da|uTxn1^)tdP=Q{jr@^9oNIFqg98dq1uIdN^H zjq(6P0})@BRN&Brt)6umShG-S7764`nk~Nw2~cyL1@{k2WGkDJogs-Qr*2}O`?(>H z+;kVSBMj+iRDuTa^yk`026}~e79QtoR6BKv$6{XskXQ8&iV4O@|u z1Iw(FmBOhMhupTCWbpEj`p&$(1_LF&w*kV7S;acuy_4v!(xiZk zvVWFTA#TsSm9K|qk7S2Z2b>K}E>^E|VK7M_r!5BV{FPoj3t6I4tYCD-z-72 zo%^|K&Al}+$C=}&egCR&Dx2TN0=}J#Jt3v=;UH;xY8L#u#h{-2bd{o?ZSYANpWw2; z1o`TOD`{puX%K&XD-X)<0;~2>gni{KeuvihWbV}E7lCcjnng=0N14-TC2R$K+}tqxTrrh>W=s^ZDlHmq+Az{}4Av85D^C&c?-y>_Y|s&eGu z)r15yMeevh#z5CFlG2H#0%0Gg%20kcXgMKf1H*=%DOA&ATRCY9>#q|0{9-TN~~t?!cVDi(`U<0>B%yoczZmU!BXf zzER|E0E0Hr9ARuNcj|E~rvAAe6&R8dh`*7xe}TVe-9mLM|0#M7)g;2N|pjpYDl(x^xkxA zSw-@K9>>2+mGBlT$ZU97BtJV*o0P%S^_OKsYf>|?rp*N!14ui9bAURmo}9>9I`tK0 zpJaFD?MFoI!!NY9Pow2r)R61aTeXO@Y94R9j?Fn?Ruo92Z(lx=HOvqVwCIgjUXwl_ zO+NWPyg|ypLyb-cdC|9&PvFg~*&qLj;^e~_hN>r^h@I&XXU-2&Q8feb*^tN6b*51O z3z12Sx1B`}#}L3$wqLhjr(r-4-}QhzD=PjfGd784v@^nkC~OC^3mTwPXD+r3$pAL~ zf;__&?4>)nVTrLdmB3yPZ|{i`u9`*a#AUp)3XAs%6Uw>m13Tz=hX1YHHoq$fxZ7l| zhEMfqb_8*dDhq+VI10$esu`!f$&YcoS|)tG1VE6a(2KW;Hn4tdQ<>5tA59dfhwQY8 z9ETgg^wlY+*oOQ~4Fc%A!fOw!;|;?w#p?7*62ZXBQ|s5+8LHKh0EE0CuFqkynLYatYa!S z!%9SmX7X}?Cla;F0@`J?ZIfU=pH;#~M9hUh+{kv?Fp=h{sDeF-h>iXb8Dai`vgrh% zRw0F+=EY$~HW#BPN=V!T+H5QedSI+7AgzGP(v*Z)mnh#ekdA`O3iS0)-GKB6qlbh>V1$m|vdh zVuEpCat@vYuN}yK$_RRhEa%fi#fW6AihVH)F>--2#`C-KI*l`E-EZrnt2kCdkPEy7 zbcd$$=s2`rTST-;l3;cjC_R7_^7XQXqif-Nvfm)jB9PI~Ov=zrwhukFjf~M_O>$pd z;(v?twshToCVYydt--etk6Aaz2K&_1OkD1%mdsNn#ODTSfMy-8B?$k~2ao!#YkJ=Q zCzCJWF8k|6gF!JB785{i)qllV6nw~ zmyOUcl_M0evY$hzKa=R$;xEC|NqjP_2OOi+S{S&f0ea~ z6NpPn_xbJs4o^~FoaD-cuY!|7k${v40^f{ToBumvT_0 z8qi=)35iXb)-#5r#rgjGS=o49nS$|;hh-y)o0xrW82%^-75}IEkC}Q5)!4M%$tG%u zV~>8Ze~kfLfxka?qp89@|DoxKBz^gEprA^=WY~>5Vtt=pyuLlHcpgb;{+Oc8S{&$# z`KXg0h+8OGiy=4M$}O*YS*U2D0adt?+=-GOCu{xW$fN;C5g(-)Bw})`XwD65-fX$K zSTEwm1NNgVyyfu_KFF6am;5$SF&e@cdxc0RvdMTx*FUE!fv3DHX(z%50zSmKqZ<)s zL2w!f+Xc}TmjbXO$mB54*f>x+o)uTEh_J;gPn470swmE&!WrQuX}z=9lC~@w?Jc9T zEi@d+y!ZJ?R3jN<3+<0Ec>)KMwJizaY)XfbTRCJYBx(F93vct&?-5z}*wSp(Ek5{Z zMQvfm$6|AZ35sm?*GWx$j+&;0dozV(3;XAmkKX~P3XDLGie}c79v8~=6}1QcG>5{S z!o{?Y9Xi*9;0#XSYO)$A?@C4?)PyV!fibST!2ZzR94KlX?K;8qbwzw46j8h|(bu_Z zL?BsFhwM&dQLOepZRWr?o{Cp6(n&mc;A8qicsPhVM-Ovj7x|{s%Ugp<3a}eC{80KW za6yU>(u6*HMWtLQMRx_hi57dmgf~p3wF}wVbOkv6aC~sU_uNAaa=}K<1%fnW2BdB5 z+q#iA*DvrAMUiGKz`5Jp{EQ=786W)iVEVVFo`M%ZP0h2EuuXIS{(LB+?b8$RL=6mCD|R_SiH35p7g}G4=cf-s(1jWo zgo*6o$sOKskSvB;%VQ^bWhyd6Hj4KMjhWJ3!NG^c>|dhBYa!B=sd74{0TgEe{vgv?`Nh7@Q;4k*em;>F>{5U*mC%7wgvw%&^C3p(Io2g83lsdBM z(p|`Flvs6{fR=x4@kFh3?NFpyh4@8y=(@Zx2pjr*T=|aEZpw+WHOktv zf{RNtszpyk%G?Bx16Ii38b<(9fN_v#@U`9GQ40qkW+Gn98LWHh`cO7- z2VZozN39r(1d4Pt#m{8!)J>#>;p4#5r(*)+vJ;%@yNXD@NyB^kN5h!~m6%5B z275d+S!{s@lY4q=nMF2i-WPq|A4nWwu&vg|PKL4_DCEI3l%y#}Y>Dhuc(P5%y4(7w z0QtZ|lZ$r48|f9vwR-a1jAtxy&`g3c4HFDBc~jI7+6*i5^@eS%gNk|&9Ke>CmoL;X z**NtF@F2+3xG>>B=lXc_oL+}L^B1W)O2Jl?kLmoch*}PQ?o!#7^XGAo4`bk_a4ax> zDS`%r+Gxc*4W1L_Km;`Ue1tD|frA5Zaj_CuiVKmF*k`GromS@%2q+UzB*b$ED2v=? zP@|RK>2x4CClZll(V9+f6rPK)vXw(7GOvxrJRMx1Wo zVbf2(e-kHwhtQYGE;@it(#&C}tqP=XX@E7+6bIL4 z)}K5MTg*rZt&Jhqo@jH;LKX9l0-dk_!o~N?SSFh(r>`8y;LNpqb-hV$D@~+ZiR7lV z&FH8A<(8Lg!85-^Ub+eaj>sE$yYnuT6%erM@W1`%P9%sU?ZNNFve552kZEE$<*u)% z?rD2qMnEb%kZF|K|6Hol?rMLzm;n7k3K!yu9MoX9zI*bV>dDPMH5088cRfm+S@dqj z5DWxQ2ArZPMn=ORcl$uC`w}mA7zik|uZ2rQBjTz09K}eST5fF1OfpD3*d%j(m7bCt z&!wEOTUyAE*F?9W#k5(h)K&{F+T?!Rk6Y)|gsSGkvL~g8ad}u=JW^G1P8Fy@pcF}7Q8#vmkm?b4X(?7g(S~`gyAUKdH7Oe8>TC*^R8R5P z0Js22#Hqsmfug8JcdRXxvaEr_0&)^Xx7s#hzm_s|?Kk!)3EqOUJpi8*d9reZLp`5J zfu@CnuZ-oFjX)0sTMi}|dYft`;#8Xws3yqu_s+zN;ZM}Ky4nY;#wKCAII;o!GM_M( zvD74CF_8$NS_I`K%2v}XS|%=w2!>S+Dq|QrBb*_1>b^I>iF)~v!cV9S4|OyJwdiT; zYK+sMXgrG_@L-&`I0>Z7RJA8kJHmdyD!^zYWB6RnVfJrI8tQJ4k24GqoiK#6;M0~% zI|&X8mS!MA=Y4ZM#9TxiwQn;F(z||aP-$PgCgwRFN7O@?<7SnCTqw3RFh>uwCgKYXFh7d4plr z#(XnuHtWbs``1OsOUwX<561%{+DXhlW4222eN5P@dFnvnsc(v${L39X!KA+C%^BVq zm}C+3HaMK63*!b-KK+9FHdj!muTbyYJdVVLND$Z){m3&A`s3~2(tKCJG@vVh#sr7K z6Xy@N_MAb&vtYoG{sH}+#WYnXLO&k#jaHA~D5DS6>bbq1T0dyoK?ZhViVP$$(#?*s8=k62zlt$5 zIVTt}CN1*bfd);|Tr8T?H>jK3mV@M}B_u`1dv8F$LaIVO6INtq02xww(9`!LQ0@vc zlfY5&F}cyG)v6*~l2WY$tvvcVV#5{WFEBH3)rJ<)0VV6n&k<88*2$%7e8z<^5R`fB zdmX9@TVra@y$P(k#l8sqNo{G&zc3wZ!n+0$XXWeTnTyv9&-U;`^R4wH_I3FC46Z0* zlNjH%`$k&`P0+)5eW)-FN(@*zA^2_=B{A0i+6s8L$#|Pav)AgxT4?$Oe2jW-gAzR! z)UmndM!=E3zH{#vFt;I93Pgt~(gaBBF14@sgfXIwd-nXfUE)UQ5T5QEp{|LW<=MWo zX^qLy-7jeamc>!blQs0Ks9=1xLt=EP%9WiaGEoiV^=R_MJv}Z@fuov?RRRa}gafwN zAhGWxM=;mXe6{UY9+fv}REO7N#4)J!Z}f^nuS5?+Hv0^n zr%0-fCZw&`9EbUgYogiHbxMu)54SZ-IND8BbZS%rVd`p0pc!>EoC~9%*;iM13XGNf^#GcsHs52B#Dc=aAegA9NCS{ z4DqRV?#Ou4Hi(Ia!%(}RE{b1VwP#g1Uh!6w3??hn4?VHOgGzetE?QBR;cl2bfjVv& zb4*Q@9>3@7j%O;f4nF)RpcD!zorSAD(!cL?5^*9ciZZaxob^J!n zrk7|(D2H?+tA=Z+WNy&**Og%AAf~|W8wt+~z_lV8=>R5REDhz(18sWT_9xQz#>;*5 zS~HOa)rd3U``+ooiE^)7Ed`V_qnQt^5gl6lNL@Qg3{H)J@6i41NTK3lw%0&{n_U`p z&3)%6mE;ha$N{0R}7SX(-nQx^<1;*~k(04ap^4hUXp zXNIun3W7`_sZ2AxV^+EAIcDl47h$KGCm^x^d(48sY?PuC%-hQ{FLMGHJW86k^*IbD63Jj?(kVgr})KJv5^-6Pf# zsq*Bw?+QwUf4RA`OtVb9e+B{lgW_lw7+)oUCX;_ciPgJ01d4l-7=(DmeSVJdYk~NT zI7hwuBx4q;eU-V zpkrPL?iRSyrNfskyQwE5%k1;0JY6x3hv2vnFH?{w8`;7xet#ovm* z=Ff9V+m)owJBk=_6#CY`Kc!Q(FGKYM#OoSk*bl0U2yrM&C`|3GD^sQbB!&BWvV*gC zeQ0|4-$&n_ktUba z9oLn9yjarvkh_lB5iCif1{he%WIKm=nk`RWJ8SsqRcz7SG+RcN(WZ;k!O1>|tokOT zgip+gTo=;Nx_%b2$tHJ__Cm-;eC*9Bdi@+TrQlkZK&tX zwfh-&>K;6z1#Zbe-W1=nyyDuM^)uLp6%dLporn42Wgxh{%#2vkYS)T)mV=2eS^Udt zh9kriU4@?s&)^W;u4J3-$aka$V@@@wv2)6L_tMPYu+9COJJNhe+ab zMlKirq*kidlhx@gU7b|lPs(XX56Xk-K{tiAj9US*qeZHjcp>>#2qNZS=Eu;nKN}&E zcHw|^^0tT{{!jpc@ospaXGO&15ZwinE?QBqaB!9FmOO=gCAa-JWx2`i4#V`NkT{|* zjCmqw``TL(rB`>Z*rFZ(7B1EE%4~^6I-v_2`Std_{R0I6@-IZi`d=R>|HD)MAIC|i z|F-=6Z$3``_d4SLpbq*Em(l;pW5XE}#atw~HF2MV;01K1XqlOrZ>B!) z33qT$9lRf&zo+9~UJ8t|KL&A8Uj@#<9uf64&PEH8AV(Mnh#YV7cEM&yzk2IlXHVH{ z54V3HMuL_0l)prqLX~RQ5{8XIo0j4dMdxO;gVs4sZ)#2C27k>=~N|>)M7&fuv59%zs zDa%Z6hsQqoL%QG`eKzY0w6Cap@p&|D!f*R&3wq{9cl*+^5$w$OtyNa1{nW1`<hK?;cdk7S^Y7kxI3Q}zGYx4Y5L6)%d86ln-tk&= zg8EGE*uWOynhb{x`SHldK)CMtglxLS5~6Tt0N#Sgl?66)hV~5onm+zkF^dacem8uL zZUpWEwGZusLs&bd0^#j-@~db&koV<~p^hhmfDova8R&SPi|o$8U!-eW2F$wt@pGIA zsi{lE$KCPc(pR{sB1EfoHYD3{E3vdbaa`$f{#23n;(p*bp?(ygTu%JH!dD;B&gQT>|pf@5v0$MxU47 zAP&RMXo+R#=As9v5u)$#^!bSjZ1q`jat+|PC9Gvg!miHtj<8+Su9q^=N-D*J!48ngy**W2W;iwG>?| z$ZjLZuw9EaaFsb)M!aFeWE{s=-!+!?c6^wv2Tpzqb2sukA0MVLihQdiXRI%*(}yeS z;MD}uA4^oI28cp_vYK}l($(E-z!?h#YEn7m#kRy}!sp-Qq5)T|));ec30C56k8V6i ziwsWgC4HX7;?n{Vh=Dt4zZOxjp;Uno!*6pGCM?j8=HIVHq1e#?ho&C(*+OU0^auBb zo#aP{*G9#C6_~<8F>9oEIS*{?PvS+EGXoRc zD*rwKA!3~p7%gtMM(6O61o=}m0tc$E!_!6uw_7;k*HBg_K$iGwY&=pTfhdLz;7EUr z9!K;0Cs}aE;8WB7<*%?DD5eq%8Jxmbc&=Mx02iLQ}?H*N_w~NAAYfy|!^no|c_&Q zOg~E%M^G7>O(QFLXes&l$5d{2=8>b$sRl1_XPhO!tSr5}J zV1W4x|A3B7HP6A#<$5G1Ey1zKa~hKlC6tRDSNfBB(l3CTuC8N#ge_W3nLn7vs8AV!{66L*}>{39V2}BAoppKMQiluxn{p zG%`u#y%1#p>A)@7{$}EhNTPW=eu?!0@B&l*yGZp^^ABAzq;(@{|3EMXlMsiR&u(#@ zCN-q;t69>9K!=zhX30v%qP{pKqgrtInMOMpTJxi@=op%N{6P%|)coQLlmEYVZ? z)fgwukRp0whzOV5;ixb?TKl3tDOcoWm+68Y_4`;o_ot)ef_85QGRKp#AhkY(e_6kz ze!;eQ@?0n}G?L{_N!U(Vvd$0L{H*R_YEv~IXbF-|LlY1{qPy@NT8P*Sm3Z|?2di;p zwpigcREVVU1?3q}pc~@0;*|vUV+qP}nwyU~q+g-NRW!tuGThr&9x%fxS zGx5wD@62UJWJK=V7dtc7`mWD92c0AanOjFIdIqZCzmeaHgsiW{!(Avpg zpgX{E#$EC{2=`~hy~Kx+1~x$rj!tYk*6YiIOS=cQHsz{1b^neRU`y*zP-I?VaVWSyA{cqe&sXFg65Ig@K-r2x~J~}npqx?hz zinWK2z!m{?@7+U~0vlnC?>`XziCJ$o@Rl+cy3nEy0TodeD(qNRg%clQn$X%ca!LA2 zqmcqd32V*#5q;?)-gw`f{X4l3vP7DaRc3(m#D7Slp?Kh{NCi~vG(;Bwx*=d)y+U{g zF|$;UYtx41v6X#6u2cmrsx;gGSfa$_Y zhNlhv+cPY0(QQm;$kqr3m>DaqlK@9)XAa>vP^@gWh`T-b?bI04n~YUUZ6)jOwn*se z3%pW2h?(42saI*y0muR~- zLVwGX$ZLt~#3*MUoEX)PJilc=2A-XD5sh?P13JTsGE7mTu~FmupB zT;?t+CKuAjXo3W7NF_n=MxoYhCxN?-^Pi;X3NG75b8u^Esh}B+=*cFtI-+eOW9%UIVF-!cl?{ zsgc++3usb+w#<28->CKXrI$sM@LaDzN(zpwxg=bsQ#!0z7@{_HwMRS%_{s;@1muh| z((eU)@Po0?kubMQa$nb;zR`Y3UhqTmTY>!d`!EHTTI1cABLq(~bl=PMbtTgRaFZ@-gWwt>l#vhCbQ=cN6ySr~$Du?mRSfbrM;pLV7PaBe7{|CKQ`1Q;M zy82$lLjw`xn14zgEmX}rgxiTR*#SGh=^x~2Z;qGL;ElU)w+g4sYe=AlL6vNW%elU8 zvE8wDRDuEr(RQ^x+#gw-U+@^$CkU7hwG2Oftz6Do*lUT1v9sIFUZyuWf!NDq;q8xM zZ6i)QEK#bIV&|$BtyHLi@l}0$7LHx`2!8ptoIY~?yb(dL#Q}mKni50IvmEu3IJ&t_ ztrdj9XHJy{Jl`Tou0^i4_FxSY6RmSuZd3JJvEU{@c}Gk2xv%vMH7HLaHO?zW z@j?Q0sjLn(ACw^uNtdJ+4f5_h;o&zL+zm5#nZh*z!Ym<#Ta1)*|98{KFga-&OJcs8 z2@+J$js+Txg{EqFJNNPCuth{rI$}V-_yBOplyFJbabd;9mou>FXgmWw1m>oFX8f z!o1vN-14;zt+me977!$z0w4ypEC3hy%LR{ss(Qa&?eNb?VSCjPgN>{q!L}?Xl_Kg> z&}>vB!nD`sz`oOSu$Wlwv9QV*V|{*YH7;$L;DH>x=OOm1Ngaee1<0Vk=UU%ie0UpG zXa#KT>X$sqk-ayN;P6=i&>Tr>TA{1p&LtxMTFADIe6jufTwS1+ZHnu3L2D zwF0AX7Dd!X>^>(=BM;kpj_vh=*%nS#I{y{B&-+1?>J9MQ8oell5<=06`1u(S%+B`& z#mbX%CLjhrM_6HIsba(ll~;FIjn%9nLWqRs56{DwCBMayxuonkTQ5rM5$nuGI)~^Q z-na$%DxYPLhjy?nl}@LIsj)RgdRrzWB~Jym-OtFcGie*hA!emJ^bq}{Z$k)E+YtSY zdp}+XtqwVoLdXbX)F47V8u6P}WfB%Mx&`kA6;ewHhVTmvSk43HszfXAtfFSOt^9$l zGo22-ZH$5`Izh6d{Li(fq8o{8=#g|sz+Pvf7i?5WGtMoOgBiFOg{IRps#^NYM~aa( zYs{@ohik2L1m?C6<2Wpr#zijlVj6bW->juN!>}~cqWMmt!Awhk$vJY`tNX#bkZ>R+ z&wbtQEGzxn7jgpjt^$G`?I9MQctoG5SUHe`&x3+pCRDD2e$q;heOGM-o8B{d6i`rjaofN7!nAhuLzS9*dqvbBsz#bvWIEfzPOvW$q0Mz|`Ff1Bs7D|s# zc32atTlwvg&CU$?*<&I`TY@T8uC-9m1%Ccv(F0n;MbUT8CR9i0j!@E@Z3<5A*I zjJ&Ln1%Rw7VRh}*lxurNn-@iY*WLMXn5Bz`&XV-tC+?3Wo=I0vN6yw9^c~q^r3jx> zg#<01eEJ_^*N!=D^_GQSyOHzht$yU2MRc!c#Ak-Ra=+#wx;9fY)wn?Wtyumv8>_s^ ztG+iMTLB@XNe7i?sV%c2CsIqXc<#xRD-XUI*GQBxc+S;te1tia0fvg8>Sz?>aMzio zCvT`t=inN;V>$O&c2a7r|5Ey;`GF2B0fy8tyS!hRMe(bg{n4TuQ|>(eFD+^f z9RW4a_ye#@tX>=tG>(UUEXI8b9dr7MDWJi2db@cvN`8;|YI^o8Cg$Vd%Ff>AzBTAZ zDQdt@(^nmq`4BDjKbkt{GP#~|WUXrM8a;iA*0kIGfsB0>4=V1{UKvzN?;hz(kRL%{ z!noR9cqC(+Gr0@rUbN(dOA3&;POrl5Q+hqxPtvfpYs_^VIyJV^!(q4mIOA=vixu&+ zC!=H=X&0UWU(Ko7Jv&Dcy^|B6U^oG4RNg7IM({V*T)4LmzZ&|WKlZr&c zx67BFqlm78dPd9(Dky@i{!xR$Q&TWi@V&YRA?*t(!arzwf7 z#OFfXs*1IOfFx8~GM~n3BnW?`X1SOazf?iHG4geGgpNq64C&RZ53?iuUz02FIESt{#Jp502a8n*J7}^G2Y|KE*CYgdnD@UUVsF~poX&xft(jUjmGmz zm;0Zc_8`HNzxq#*$;ANMj~MOu)1d86C~7MCIv5cD{f;WvQ%!Lnu1Nsz+0VuK=7;IdOwj~yj=?i?rHB%IyV zuo9DHiL-1sZxx`!M+fL>T!w9Y03d6K^1TnsW5}|EchA-wdNej)G&aD5)s=!no3YY& z<%yj2Hccj%Rkw!7#;`4~5QB-U)xGwl;CRl8k+!3q(1xmBO=`4_4$$#%+G<&&uOPupxqFyC);%(tk)Y-Zl zMlGcTM@M7f6dgPJ{$N>9Sll1*t0m4R42K=SngmG)*^BWW*B;&yGU{eJKgxWVmsiZT zC~oNS(A2M9Adgt%LP99+14Vc^pK>|px|wNQ%~u&AntH~`#-dOC&@)8)aM@=4 z_+{qZdUSM4Q|Hr|?^^^zApfAQkMbk=U9b>^4~&mscze71^lSru!?m5Z|Yqc{=FCfy<5P> z%JR=BQ`X;}f&WCA0&d6{TZ9@iC)s-7;_e60!Ob>?xP}OCus~AtnWI<|R0~ZzUkP6; zo*&E4%1E{5qS$!pxDDCfo7!}9%4vOHk6TFBVru?|na;-Fy|B|yeDBAMEldldzIMy% z%Ni82(;MqF@HY9n#`0-C{kCs&V{7)vTfgzI78knEc~E1#RvB#=oh2J4R5vJW8L#+} zIkV$9H^0NN)3UHF?fjkZZ7B(dT%LB`8EqoZ<&gaw-0{lW;VyYb#Wj= zHE*MU9-Jup32xzMJImN74{nozfsLIHL3iHN<<-;9>xs-mN_xE8KEL}(OV}qreT9xG zA3Ch)WliSpqnr8L5r z!m%a1!h=;=(6T*hYu`m$g=Gk0X{D{?LF%X`-B`>~#x}`L?4da>FFB=%=9dJ;n;rtu zQ$h3=GaF*6sG0JR0zLbJXe52tl2{B+2OIeYXERMk-AeFeg;o23TQhg&nEvkG`YEo2 zxZGqER6@icDiegwD%2el(ix>xta7;l;9!Xb>>pBc;wEklU8Z|PL&z1NI4GA8<9lfD z4eyzype2uRWtJ3;S{7T|jBnH|riq*lZ_Yec!0%gbUKgv9Xckq8pN}2(;ysF&i#}sS zi(Qx%BGRuLMfa!;aRwQ!shF#LlC;oW^Q#bSi*vU+ zbuSgmMth{bO}~Y1XseyvMQLisrHdR9fo^Dw$`Kiq*ANNTi!Y3F#!x4#qri1g0HAzU9gXYs3W$j`IA1NV0}t zl?Uq}iJQAN?0Y7sAJX0dTmaUW>>*1-4?b@RFi~f0AFD1fFcx$M&r6MrJq}lI#tjC+ zNOR|C@ep=q(NLj!*KE1SfDl>lDpXP`BtA~gBXR~#&nbx;t!F07Pbg)vRk&KVBz((A zwG^?GN?zj&a-{0(?8Bzp696{sO!&fAAG&iFSvS>KTsodjxyu+L0ZpH_6St>ri-!Oe zYG!26!d1dr!MDmXQu(ByDcMGV*J@947F#t2etL;YTV}BesW(`t9|@)52F8k|c3<)rNlQIjPQ2Ta zyYxNJ%?puVi&JQ1kYTY9Nm>cfMF$&PHl~9f!9eHAGZ_;um?voVm=l;k(LFYFxFw4_ z!(6Dle!jSvMk*f}k^kToM19z|g*4&LlawRx9=aFLujCdzAEi|mG9QgvzRSlCOQ%8U zID}0$j9KpY9iJXBnc&Q)skBs6t*rf3sWYC&Oi4{vmL4(X&`ha&tWh8hJBaOrQydz` zQ4_W;)LnJMM56pC;i42;bIx>W|Sy(#jacrmw|Gf%o&P%8SBX@OXd?wAVDxnOAAeGb@Wq9u#@r_>%Wc zHdiB=2^D4M?~eD-^>RBSyb#5bZ87eyutfaWO;IGkX_~GaSP1Xy<+^ z4Q&3r6{sNnCK$OxnLIq%@yhN1o;2VWV6R%GcS?ncrQ%m}E{@)b|IJc57%WTr3L5;k z`L#ZtuuesxOk1@mqG*$%R6CR>+T7C?nPc6q#4XM(6gq;LC zmez<%ql>yLVKDKyH<4k(MUdSQbH#MVSde#(4+z)*U_lFxkdT`knAJDKbMlLG1-C}4 z^wbbHa{UJnJw45NX2DebS|rH4{fyFGQqg2Xbtmc8loECH*U|K3?zJ6~XA|7CiLYAl z@r2!xQ+0roYKrb@yUTHEi4s0i^V<$74_)_V#r>TsjS{GPP!WOzX{^D;B9csH5ZZf4 z;_aYo6+D?MqQ?9*tRABjyZNP9@s?6pBp0S2!H@BBS41^qTS794w))B+WWcmAR4l=@ z;MN6F>d4;#AQlCTIZb_%RLg6MA|+_E5)b;8rKJ5C?(?~_oz12;-@eTtEUp#XpB$67Psh16(0qa|uW4L;t+fs7bhZusTgQzWty=Cl(MTxh(EBqg7+(+5@G5-ik* zb}N45aCzKD;`g0fAcGqxsBt2U5LYOykQR*h0jLGQ!$>J6-`Sgd59}q{LND@Tt6>qe z@boG&3Nr*kPq}e_5^9Kuhak>Ds~Q)(;;AG4$)^^k`Cbh_!Nu1sJ7fH6=d{)@elR;2 zoDoKjsCc|FuBn3RH89$UMz=Z}(?V6PqGPX*vSBsiBOIB9mRWKi?^}@+5sT9|CTFfo zEuF{C&dCBwtSHv%*-%Tb`|Q?rU$-!b;6V6YI{j&t88nVG+`ZlOBSY~b~RMmE4b1D^C*djPglu{?Y`Ju5FzNcBDWQDWvaw* zp3MD4TmgLp;+QCs&u)zuVcaa(BBFOE=b*ZV17fZBbIuht|{jaPVucUsa5%7 z(1b534O{a7S){tB+uqREy$tAaskG4$g2aEH3epq%J^_}b2#MqVcybr^VEd&T_Z$oV zxkV+Xa}V9sj{|D8e2xqq`UgG^!5x&xX}J2im*J7xnBr#<{Fx-dO^>*)PdK_EJn$K! z)tHhWmG_g{>QnbEhMt&iKYPXrd0L@PouNae`k(Ays1^kFW^s(jX^r?CHhMlH`I5uh zNh|?${z87KnQq}FQyo1s9?!cC6I?Yo%AS^xq{C)4_5*syB6lDb4*7bFI-5{ zKiAE?RM_E%B(l*PEmeEbz$L|Cg$QeIQX!*@y_ho!ZwNr$r^|utoUv{_G}5)QQy$N) zvQ@Q#JmH;_fJpAq-Y>v?exH8*POc#uf#cc{4kqO-V79zDf)r_HFo9W7Bfr>302NP; z56q;KTudAbl}no2i-Bl0ldRb-;9iA*H)0ID>X2ko`!p4{@k0o7;AU{mG=?B+qeT2^Ndade{TiV892n0G&7JO6rhtu)w^LPT zr`-$zHXg(>*d%S-!dq>_*#0}-Z|yj%8V4uFy3P>t;%eh?jw&a*SZhN{t3*Wt=wJ=0 z0)yjP&$XWaNA6nKc;YZwm2o2Uz2U$d>rJhbI%(i$nQ=GMAD>D!3gI@BQ#V<8bbu&`zL&8-o7?!njI-k}$i zS_|SZ78UvlMiJJ;$l(lKKZzjolQbME9CM9oKF)Z!^t@JZAE&=H?xYR8-t>j0o-ohr5P|mV5QK)7dg=>Ss%Qe=HH*c$Q?FPa zvr&8w3IIJv4)|S%DSRLTdi{VTyfXa@;!s&>#nVBqH+%YrKyMgRCI2Dln$`n_CF)JQ zOz9wLn4mi*mE?>O9be>ca`!(}JkFa4{pl|m6*hI`{nI;PbF6F(U`p;di0tph`oAGi z6sF1ZLlVc}K?tyR`9Y^20|1mM;Fo#C?NPk+P4W!u(;UBnEf?!_b5H5PAz$Pq5L$>H zvQ&;pZOzOobV2Su@M*AKb%w zI|fDF;x$Qu1*7E36pR#t>Lst60fpHz0ueZ=3pO4Q_=StEBk!#p7yJpJ+Te8doSopF zyE>d~PHj_pmbkuoU$;&vUaAONK&{qA05ZkiO}|T)2~bZ}#-}F%$nMCf9+NqHo;9uh z$avflv2{!7PM{N{%)t0*&#;mM6XYVcp12bDqYaPf5`5huYRc;&y{{-AszC>Ka~ozP zP5x6dU7_8=8hhY1E6lxV$h!S;&+x!op7U&cShYxE+?b|v10Vn;ZVcL|xVv%nU>Rqg z)qRA@nWRfc7}Iazpb?STlJ&3~dIh%oCz{P$y998iz+CgQbZ^br;Uuj2OX;Ii?<*E* zSWSdpdf)BsB?a=<%zS;m!$Wm1UG~Z4;q!O&_TZKr(w&V9)4;rBwk%7iZr&7SZ?|YT zMtkg+$?EvH)yq-vt*<$mpnCl|bq8K%U3W;KFzpGSYyX#?-|dTBlOL0_UFi2^cc9&e zPoIa?<*it2;kH$f6IOwn#2)6aXm`<(ZR}fb-{%05Tz}x`gHtVGeBY&o71he zGGWYS+HuH&!3~BN(hw>Tz*SU9MGAdfcrYTdi%;fvZ2B9WgL+_8n$C=U5h|+kAmc{^ z`h@nc^h_Z0k>oFe>m>g}w9PP+{XUH^MsPKpUhBh9jx@jC-V1~=lN6Zwai)4yYzMA8 zj-F(qo}w7D7>MN#f-WeBKac{rs-3kI>C!t$IwHwX#bGb%#AG$Uh8L@DZ%uxkcrYcnt%*u4OfwZTGPz| z1=(zo)cBl4Ch^bzIp+?!K?)`5__;#+`3N(7Hg;?mXq}N9wfFrn7JILxT_l_faD5rQ zHS*rp)B(>Afi3g@I3&R3Xi72WgkGSjxeoQ6&qbJCMJDEe+jQoAoVCRu17Ro8r*1v$ z2UBRT&c3l<5yQgtJkaj2?U<4&u3{}>3Lt1$)A8IXrUg00;3FJ1uGxC2QJ@QNFLXdP zetd^kFKP;9sa+5yRN(>zIiU3ddWQOamy?dzHdtr5u*nA!D z+y>%U5{?K^$Nf}iq4!T0*v1SNyDe1hB1j}><&GVKkEKQ0*$y5-dt6^EtZh|AR(r`i z0PmF0xjYt`GQ*sGz2>GY}Qkn)$tzs0vio~^;CkfI(6ROC>xK~aA4${5TnNJnTnl+9VeQuSgZ&@ zw7pDaX?SN(^f}MH=qE1nddXL=hL0$CdMiS3@Qy|r(DALO&uTb?6^dAS*8+X#hkRnKj?(2=lE;iO9TbhkwDh{+IhSiuoQ|ZU0G8N`?4|O7)E@=~oHP?c(i!J*L_m^d+#{G2qLEyZFd}PJ z5w%Sly}P+~WET9`k=^s9zNf9hP^Nom)A@Z2Vxj$X7Ywl)A20tgBy*UU+lQ z0t@;I={V@@e#;Bg!9C6-1#kwfV93ydFxEDpH&D9pZD$T;pk=uob+aIFDyPYJ1tZ%2%YgwY-3A2sPf^?nQO?t@xn|H zZav-ZapBvHS!Q6usjfm$+kfqs+(ZJrV-AD@?lk#y1H3UuQWakBHVO0=KX4b(@YV8? zUSr!w=BV|kdIJx}d@wSzQZApmT{~mR^CvaF&H_{?>eKM_xIf$G6qu-sFZ)P%Pg7;7 zE~||JmHVC5^jGdgeC0Kc$K@iwgkRs_c_3Q4Fx$H0HjGPO!~NWQHtOKpmWj=YrLBsY zuLYua-j*YAEW3zk_b&2dT$#lm6Q??%LUEAT6{qwD*%Gb>kwWMc9EVX}G#uoRrzHya z)Lz%1nS&^`{qZ}SOlvIWFlAQ?%Rlx-ihLm(QsOF_ubB^8W0*n666tcaPy1~3w~;Ey z??mr=+OxF>TaM$AUvQr*m3raO_++KzPXZh9;y2-E6U=vr`M{aq4E@qBwQ2NaDk0wq z=PHG)`YiMitd4HadxkU=m#SfCX;{!S*<+R1sPG+x?CBAMqWWMmEBi>XYkZg><)$c8 zZ(Ev;7rhXfHzl<2<#BxE;WfCNJ|6D!{gIgy0X3k=4ruFH&{XTKjJ#)bhfaIgPI{+_ z;{EF>{AEjD)h36@mXne$HEg^Xg4aH(y$HO!%F*^|uAF*XBhv#_pLmPA?u4t%HP~z3 zT?aWibgJyI?HVbElH#CkA^e#hLF0qx<(>S?f1Nz+7ZobEblzI>_ll~;f^LxZ4j?9O zB6C(<*B&^k+ICT93>w-L_0H&`ZNpCJFzI~W3>OZ0B^_#=&e%#s+bjf6i`6hE~1$DhOl~!YJ zsuUigiC%WFbLqWY;1%eBJH4Rj+?-ONfs0d}|9Q)2{w*HcyP);<{I2$mpbzwm&x-56 z7X1Iw)$q>{z<*$3|33tOj{jK}Y~p0+;%H>zM8Lqn@GlzLe=GR^1DW=JAo%~k68ZnZ zN&DYdSg5xVY(3bA|tX$XB%rfBj|gc{TjV$+%RR>Bb7v zHxl%~*L6oO4p0hWB`=_7J>PGixj5bS9QV`R_88Lp9@8izO4JCm7p`Bq2cQeg>dya9e??h~|7**mU)7RwI#~E~jX)B~5uL8N-^2Xxhx{2i zZtTy&XC_OQui?k;X|x1k0fi6_!uqejd4NNrDLGw>PiJ0Ij}CW&++&eA@+Yd0B!)t= zYi+hKN!ofdd;VWxbui?5={UL4sur91r9f970h8c_ z){X_!7s>6`Wu0=mVqOSVR#@wlb1*)S$GR)pqZ6(1#M%6H8y5=;10SLHKtw_tGb|Z% zBQnU#pX%*87u8}J)i+=?EnFYAGF)lMUDGQyo~7*$LQHFtS*Mh=ffJ^|8iC_{de+7F_P#5U#=^TQN0rGp=_HY`#OsI`14Z4%O{6T`<`nhfoN-GAiy( z21xX*JrI>#&~uEGqKb_qwd9gIZht?Yw;lpy_{Q-wkk)MAuOb#V9s!ZwcJ8d_v#$3N zv#wG$1^wmY))=_{zpQ~O)}$l|wI~ppP=bJzA{UTwXEX+-nYbfwrAWsRw5;w4Xpc+S zZOa#BaD!Td8t1|rb<3eY^L_~5tL3_BLYDL11vzI33oZ!7(vt$$vkRoln1^)lDgKmS z)|)t;aJGjO#t7yv4C^x{X6l`u!vyNq3-5tsD8`n9ckNde!4?ghEe;H)xweyFnHbr! zz4)CpX=1o8M4~;$J+_1KK2gEvush~Qxz)d7#5#dPy}v$z#5EfCyh3IJZn@%54r!f+ zJ}!x-TAbmsKVTN+naEk3@5nF#=T9L-mDi?bP$Jdx+s0qJ}P&Z;nrv>n7 z)?7sVB-t_?63EmHZdrVEgZjzWQQyZ6kN$#e88K?AXHQ1@a z`7r6#<9Hc|@;g-Ty;05Kh#a8;qn}>z~)0CJt67PJI96<2aq`7d$gsaEFYHB+t>%h>x zFd6Fl&UD-CNtq+vn>GEP0qXxBQn602?5xYJeab*ezKCU`&%XH(Zet9;4 zFLVAhWh2U?+LW3iZo;BLzDv`>zllx}xuuo{5T+?lgHn|$Omrl50Z@LmX|=az@g`vuvP? z(ROhS_gfGCWK8Dcy!QpuX?oPYF*v$ZYV&|t`cRFD{GD~_H3KbCKlzAhF&bX~If^6p zIe3`eKM)-=&f#LR~%fhgvdp=Xmrwq~(7W!8)*X{fU}zOJdlz)+3Zh-_V(y_d`o ztGbq(u3SqCverki1Xno3Rnn;fVJ7=HA6{S3hccD~QB@7Gjb$}8?^L`CuOkPgL5*vm z^@V|%Ds5r7yt72*^hWL~>dkn65M$4BAWqf11zDz=71~9VLMB4}1K?qhwZtty*n7ere@BL&3CPA1Zv@3;s%p=0jE0z+3D|Dhn_| zqKVdD89}J#b;wPj2%eA!SoGzf?dpiwL{8?T@#p`R2%hg#uX*r2JK_c6C?{CV^u(Uf z%Z*)FOcw}G6KGxG$eHNHhg#>Kfev{gl)8H~;k^d$aeoBi5 zF;z9D;zxq8XYEL8Ut=g*C}>90@2iJ+L=9X%AMTQXj$ml4SaY}lSqFj3rxuDxV2b74 zj5*?Y&=l5M8npv*W=1pzm$`t~c6vJYMYWk<~jZ=5w z;rC$6Qrtv@UAy4&(derdXG;ZPq9`8>c^CF|ND}|r>bpFF1>MIf5y)KN%GE5!kp4cZ zRnXcrv2hdTTBHpp*AE~sXbG9tv|!h~q_HF>H)396B%o>JKQ}1&@m}kI$m>RCJbImH zGH{6ToRe)yJ}g;@V$y1`h0&jgRYqy((zvSqo5=EEMOY7qZCF*?EhadJ<3vLo<-t%} zoi&>}wN^PS-csu>G&=60nJ@PoSoO5*+Vw{}YVz)?s3Dvw8r}{Pk3GixJ>Qphuvsi$W<4+WaiA;7unCdlzOP)k>UJDPgjlO|rjkZW&HTTepq%*AmslTpo zu4V6fgc3;}femK1K*G+>_37oUzL*`x7p$k3ku3q*UEJr(QP3(Kun?>wPG{IM?`@**EcE5=4EYVSg)!3Y zzSiF6pv=V`rChk_ zC`xrVs#W>46b*c(QO8Tby;ATXlBS(skv2j5z&w@RcdpYlNMYxtkZQ;whdJI}iuyI^ zWeG>3o;@r=tRyph2``qdZT0ls;L4i8o_piu2nBBfTpB4K(nJL*dNr_lmbOjcXjeQG z4r^QRcz5P~$N^^}#g4QqF$}{wSf2z7 z7#C@^+(kx8&QI0^3MEtdBM8WZh`EYh_;-@{7YWGxp&JWf8ibrrfs19Ta@qz$(-M2E zBr&S5R@c@k#)y2b;Xc!;tJaPsmlD8KW>RutElvCGbViIrvFC6vTF`(=8AN*+eiupCF3mBy{8YMf3{qoP5cJEhGrER3NAgyu z3)V5XIk50BJ!gIJps0THb302s}8$UV0i|gLNxmv`b256UybQe5e zJT+xZiwsAeqC+Hw76_zPGrvo_e((KY2wArM_7;iXKXJVSy&QnA+Ygm6_!h!gYWEAe zF3A$Q15jSS3=K&VgebX(JrpwSfivyW9g8bnqb7A_?(q|uEsQyxjP&xz{+4()D>IZ* zRG0Y6U!Ro2l?zCGQPvg;4XHpOhEbJ-QIhP_HHjkYz`ExKg6eF}{lu^DzB0i(V`-&HeOBae@MC!o}?-;$zNDO{?RdA9FDQkl2 zFN_cPAbttIH3~dLEfn)Vr#J#mw6-v30H%v+hp4Ad*TD?eSy<3X7u388I#{8m8%}3x z9qm6vw^`cAEuM=LS}&*-oX%*l5M!91(1DtS@N?kSEX2IZ5T7|V&9E>oBg!peDmgup z=<8H)r<^#~-Bd;%$@>$LZ z#hG_*_lyifI6y7j*IlRD$5Nra3p>)5*gatip&r_bgi&p+Mqsz6Gw@2Wj_YBxWV&Wa z?P~$4`3j;{Y?a3e5;F02Rp=YL z0gpdSccZAQ(cmP=bY{3*LYxH&Z-K+n-;_j$>)b~}x!#2&>5^uCyrNxv{X6Kv6iCg7 z`x8y9_Pl>8tNe-j?ngfrrtcTk#isLUjKNi{Dh=@NePGZ*O%!+u*s5pMAo*(gkcdg^ zsq|6voOM(trS^Drd@MP0J6)pRk6M3&wQ)q>qc2nNsZEfjY90PWZVAS%HVJ%1x3dYD z$K_@MB^CIowt$3{PBteVfHBc3$*Gg;-Dja^PuVYG@oe8&T7r2Ay6D0Ts4~e#5wiq` z0b#!O`(~mU9w;k|GLcJmVqS7Mzpgfl;G|Z#0UVF^Oo{>0Q{Yy#K$%H#WdddVRu7

Owj-|`5LqpmGEd5Q|iEIFx9GSgEOW=WgmMPhGm@;wA9BDYwy?HYN zFA-X%bS!GN?SNX-9?%>$hze>WqlLik;;`(IBLkD`T<480?H{RDA+2Hl_kz}E57KoT z7B^2M9hY<3Glnvr{Q{(Wt2h<0Qh?&hH1>2Awu|tuF0r!RNlZI5hKX$7k=p89cuSvN zZ?LEQOG7h>(nEXt&~tAD&I~m_l?b}QB2|aK8*Ed8TD-e%j+rI{1g!<*uMrDT&%im{ zD-(KQydNOcmdK`P4a56qWk^6)u243p(3Q9&ZBJA8;PG92%9&`%o|1!!(~Cn+y~Y!m z@@^*Y92I8UD}G!LD5(2PhhD%Cfzb#Ns|%gFF4IvQ`7zu zO`uR{6>+`Sg*h-M;%fwH0{Qa*B7`pESx~b?iS?_7obm4SS|Y}C}7_)D#oT0sa; zf>_)Z)CMk(ce$si!&us4YR=7HX8vR9JYrKQ*|RMVV|!a3Cs%c2GcoI4Y_4)v791<==`y1031jbQz$vKT`b30QEbG9(uN&J!|d-}O-VAGN|gy3BCIx}Ez zQcH|DdNV7SZPz*w&>aNG{+ z>t+>bD^eaBVRpOicEkZQ*Iwi>oD|rulzT@-3uAX0C~lgR@(_MsiOGn6U}v+tlgB_i z+md$N@vP3t*!!O#tN{nC>6{NBbHhz&oB~Oqt1eF9tTx>J*v@t~PAGla9EZap<&wnq zT&?rNh12rWpD(q4rZhRoT$5YdyP&3f5r~xO#$@mPbhj)zIZvX6plbXCac|oN5`W?l z^+}qru~9sNpq;MmJ;7N++{on*MdOF79AD*+aTWfZRj-o0WbZsx4zH zcB42$h!{8sEayA5k4mZ-dFt#^CG7u0esqyHowdiHN?ZIM$-9AN(}nboSvIT2Niue4 zLA&UXs5(o9+NpRaR8~^|X`$Ku;hW?&#t2@m4((mnA-zt~ol>CqR}J9l!uD`+!EQOk zxqbn;ejc(>n<}}Z&IUA$QjT)WYfJ?Ku1td?*>BOrj3JOX4gH(%s0;T`?w)$e6C-n5 z)xkF?j4_|iq02mcoZR|<6*1~x1*Ov zGjPq^M@JrkWb>WKf8y3|TnZ&Pd_w1Y(BCWYb`Gi+2VP5CJ0aVbK1>q9qg!gNn7JTG zpuW>^^^Qwdq2!70dLdr~R+Mcyi#TD8y_`I}DpCMfp$!RVKR51pbe~v(ac^+%otJnZ zj&5!|e%Up-1y|9G{}4zv-K+w~rcuGteN=AWe5oAfK?kfyqS^c3A?+=(0R^-1IJJob zIRy1vTgqNlAy2@7Vo|ghJTKCqB!EC^#S2rZP+r66hj0(~o$W#*j6Y*9Pg{IFP2Z7aoy+Ki+t zsObaR`3>I#A$7U51B(T%)&wVr`!b|WY;Mx#fWH!eY)N_U(Qskv5A=CIZ|`#8i@1ZTMNYk$oc} z=u_y*8Rrgq{l%}JlxhlO6N}_xXK;1fG${-^ewsS5gl*FDD!sLee51*K+cx!;jpNo+ zg46|_k&H|?*Ergj8$(57f#bYxh3l+%xYqf=2WiOeF_K+vpVlB9&8^T#=Pl?0;BYVv ze@WO1sB9!&qI`C-ZF_j>A|!@;o7+%m=)$l#pd}K>u`5^or1Q;xBX}Hs8gUTIHX|?p+qb(x%P5LMp$@&e``ckRG zER{!rpmApROXVAHBu~h>7O@ZN_h}_bJP#iF1!w8(ALXd)2D4$ToibUZHiN6`Kq=*V}X7EO{HVB|JR%aA^{z20E|L$4+#ZNJEu>a>d%F2uc4D_rl|9KTmezXA{z$w}ZVkavxOS5H7G2~Z zNCTeN=Fr3%#&RG0Cagb+LTa7lT%4lYa_oy2+g_G9V^KMtSF~vANQbbfsPGU?VE5~F zcVVyV$5A(4_v_@o5n5sI{!u1#cp|m<0eYcQr4aKMLMSS8*uwbu-!D{|08f|A zf#7~RnfsKAYS+MD()~(};`@}|F>}AGI)qn@j{U5)_pB}VHNSh@FR!FVq^jv zYYrj_$pm8w?G_1vz3GdOSmM*=ZwWBW+r9cR%@{3mY$kgOnZFTs$p%r6gy6P=!-JgG zGUkfa8-$BkZ%y1FF%9>_b#m?)r@X{wA44sCs?zSOi^(HUDk}`x>CJ@L+yarjpDT7% z*M_*-MBzyc)~hEojNHyzKU(kuztH>hl>{Y^wY0=>4yE-~%I^))w2A_`VBiIuh1x}C z2LOE{1UvKjRoR6`5y(wZ`KpQwnm6=L`AdQel3|*V3RV=BG$Jx=0WCX3Tgrd1YWK{n zAcsmrDh;D)E&1`X`?_WSXd=?;7|kP>0mr?uVEw4J4x@BWU;ME|w>$-;lmFx@4K_C# zUTE7Rv=p|W=J+zlZby3fTWpIQaIXdYPTd1#64j%LC#%(LDv4&|YBjO6?c$e3dK&ON z%u{3BSssCH$5Uc8H{m>a#2f|%>3jA+SbL}7T!8*dH%@kJ+fH_D+fLrtw#^;ewryKG zwr$%^&i-cVRL#_xIsdts`@ZR}uCA`Ne(PE6e%lj0=k{pDdk7zQ30(-!LgU0y!N^nI zaWc5*9@rE%Ym||hRpZgan+i-j~- zY3lPWANxuPqdBu-9==+$`p?&IHQIv;>T^l|2PyIMtiM#-xi@Z9eU10JfUxln@XQmF zYXexS*v2j<*?jTqh6B#aAlvTagYSl)Lu`5P)m=_9lGQ0D4LJQLyzC6Fdu9tdv)1eLf*Ot=5KqUvBT*m8)-1VLLig9#?RbdAH1f}jqT-)?Pu0b%Ilks4ejM? zn=FUehE5x^ngW3XYnzL$?Nn510(rRC7SAcovY8DT*+Y8uEe`2jt}11}%7e3i`t*`( z#~w3N?H#8BTR0_y`c>ROQg*v>BVzYM)b`AQR7)Pe#@ugU^H{E84dozJ5uK^fja4)D z;Q~`AnsD6c2M%E~1O7Q~%Pl3%)fP5Zx4iv=QBo`9rrMY3u;`Ff3A|q(vEeix-vO}) z;QwG8Fb6|U5@#}t_T)p`$#$!qL$Eq~h^jopFjLaPDswa z%gX&60=!!c6t-qd+qKV?Fy&R`Il>x>uuiKCK4+&7qY-D0g=S~2qQ4zvpMP{1r%e4W z0I%Bbm0kBD8PpG#o_Ej4w=Nsc$+a8rG^~uI!4+XBzPnHs7i3DRbSkO;Ba_y1H$}tH_+V;0~jn5&h-&2EAxOc8F<$+fdrI zN5I-rh1B5({P}@lH{=6$9}m(Qjmp^-&!BAvNRrL=U@hGhF8Pvb8jIj9kVv~So0#}a z+0>9sWyiDdt{*2ofGK8IYs3MeA9^k@mpx$HK;b?Zz~ZU5fOs5Ol-W-ogsd2#=v0az+=^cXxVfT zkG$9L&Jj0Jv#w2-1Q|0n=-}tv%e!0TUIUy4j`HzkQFL8cl@XzHCfe7;jablG4pvnR zb_X%{QsW_8({c>7DF^LAlb##(QkP!%5d3sPIMb(Ab-lZ`^PonKvlZa8_!HaLjqf%% zF?gTr7|%r#aOaY;oqdx*>;DqEFIFpg>}q9>~njML@=@QA7)+m8Yn>)V&8s--6lrtPVM3#61PsKa@7j4b+LrFao@sz^G)Wp$Hj!62bj1S_Le7NkZ!v~Uf%84_mm9%nSip=kBCza4eIoG74-?Zbru;}M#_+C z^-nt{Y9WDe2YOyE!$Ews5!)L=Z74Ke96_?AK9cTZ$62aPy5BoKLKCAdD6navhtv`z z-)orolPz0n3hwq0-f39YKWkq+8d-pC6u-5YxwTk<)$X{=Ul(xBPXdMrUK-qFVk#{51u;sDOixcvK>iUK6Z5UGjKpZ(~ z4-HRoEAnk0U2FAcXfwa1USgvql@r$zTRT7U)HL~K*O{klG?(u*M}EMq<(g%3`BTwv zgAp+;vhB^)IG1tcwA|`6DlCIu>h>L=@bgCsAQ}+M2*Lt4XSuOXXhi z2_!nLkxIgiIQD;HYaVmUU$PtDBbo{A`bh~DS%Z82ES(A*#)HB zoF)byRM&tfVt8L^2OF6LY|jC@Vmx*h3wP{A3!1@T*KjY3KrJDAj|Oa9kg|Y)u>(I- zxEK|WVO4lvbnVE%iv%f{)@pWTNQ(<^VoR(8kz1mq)$d=&cM-iZAYmT{2=Wvq=&l6B zQy^eF{Z$=0tjA)%Q~Tm87&tse7KP37Xr~Z~wt(*!v3pRd%|I*+<+9mwlGH*vhC=Lx}zzDQ(H<>>fc% zTPR{G9bEsJg6j&(WK|zGLDT>O)^lzCZMyrG%@4~jwyK8WI@_PB<2LtDWl?Ch8Zcc9 zs4VD^R-v>_2~<26TC4beC@ZA{w-9$JYY`lQx-aR?SC!?KbCfaRvp|o+&EjJ(*t40= z2I=MQ_Vhb(MAWe`7KV@P(?gjeQ%F2xYn83MT(S}IPKBF{m>2pbhLqtVUD?#rSuQDC zt~O;YqVD<1=8+j8O078K9ksQ=|(aB0uD;S%5 zs@e_ICLmZbANxRsksE1!MmL|GV`QC4-V7Lg=#zGIGvV*rT_+O|869C3uX4X42g{AB zqxU)_WIgQSCUAGN1*#?%Xd^X!iU7{4Hx+jy)rg2?K{RZyS*JJAjx15Gk=&UN+^VI}Vda0MvNZ zQSctwS)s`@)@v*FV4UYh$={7{H}r*|5PAZ1=k#HJBJxX3_e7pB(gz0TonJs&nVPny ztc%XFrze$ehz-{DvO6Y6Ez!qAgs5!E>XD({oK=P_bz+g1)g}=wd(H-a=dr{Hlt+df52>TWW?$m$Hhg8=s2ui*S0-`mg6Ao)g(mDK4@ z&74D=n)m=;w9kthgU3)yJ#SY>3^U#o%~@k}9^Z?RR!XKf{kbMi?YG?0frUvjDC(@G zz0@j5=1B$=LK5yv)ctAVAHqvP zQL}=mTcF-4Nj4GX&tPMrcumAd(=qdf(sGy(H6_o3YQ!W!_o)g>4vroF`A?;5WMd%J z1epAp=(}n%E<-`v5u1v<{cqOy5U?(7e2(9y4ZB3meWVetKl?o5PODN5? zGiO<86jq0Hzil`tO@FpKTh%%vQz zokjUB1iT%X*c&lbYUu}pr#kGul5~K4$?K*~bf;ylRS|(bXPLJeJ{O_Qv6n4;6>b4AU1rw>uw~Eph^=Z)Ot{8~86&Mak2EjKrnBsJ z88^JFbSn&Ku-_o9i}_usXTeDjFc^NSCd^gBMxQ35?WU<0DnlFD@<%jYi{M)svN0rz zp~#)u%~mQgQXa?a;F;4;`AHn4SoMDL{wQZIYT2gZ>}FC?ePrz2X6|7Nc6Iz8S?Yx%R&r0ObIBH6 z6`0Jwd2EObSkU=F6ig2Jdh(MCz9WHMQ{5Mxlbt|#XPBuO&Ao-ukptu7@ZDMTfSw?U zBOVG*Dk6*RovwL9GMi_jLXE| zyJ~Nsryd;D?+(rdqGv4Jg~Ta;WU~afjil_cc=Sdhmro47eXm$@hee2Gj-nY^;V~XM zT4%fP8uTxMYQ8*HCVU~05r*l9CRK$0Jvi(#vt@BqQCrM*79B*zwx=XBi?rha)>QpU0N zdC+-wrgg)W#v~pei-~gvgSMU1hs}IkS-1-tnxWL0h>LNpXu$#D4q#(gIVCJcK2N>M zoMf+o!rTqVBt`of>2aX)sZx^h~(yGh) z8FXGqk{PGHFB>EQJ%j~zoIWiq`7$Xv=9-DsHi+bKmIZj6&yDG4dd7rWt~X>lRQlgf|T7l?4gGi50( zSOCW0Q=^Jh$21aKFEyRM2ee7Pk;6!|EZ!uf0X^Md1~Et%Gq!5_J=N)dnGVhpU@*r- zC_s7FVEwA5%Z;;@&k_pDsAIahHEm3$!u1kGLRlx20pa zjJTQbM6M~zPE@%BhEZ#D_#lI7Tvh5~eI2>o2mmS0Z7t&zbr?I=uhy7dbRN|I7}g>s z54{i+K+>G4!VzU$?aSz{FE3SsEX)I7mpqACcx=kZR5ADOxlC+CbF6A1ROl8zTb^z8 zn>>b1qi@kI!Ao%(%K2D_vk5$7H`e-q=QbDS3zO*7S`giO0^Ukb68j>sYIE6IkCwEzdZZn2p@6IT=?_ciEpn?Cv6U z3hvwgyMKFL?K(z7tqdQnie!Av(!{}jOm4(qo;$VT&k4GE%Vx%fJ+m4p=?H=V%VYn& z5v~H|WLoha@2p$eEpN}~Te#`il|P9E&eu3qE^f#r-YgtNm`dG8E&xoThh8FlVG-@e zYg_}PtNhA+etM#)je%BEVecX@QIT;#VmX&;j#i}?QWETN2HHBG|LTvUNldM<1){MiT^BjT;|0kaO-7O-pJfoVa>V6yJIRB(CiFIlU22m3 zq?RDjw<-+E9~Y~7+^&6OXaYZPY=ROIn>F{H!IqGwiDj4_%MZrdI(2ciH zeqm#-EFsS6Yo#$9_w-FXk#V zr|CxiK8$3>bAR@pTgK=fEbslGm5Y(vbf>g)wgwZna47;iu$c(Cb6hk25!87EupV_d?qo}dgY zkf3Hi5iJCk(?-TX$ncsB4)k;Sx!fC$taF&JT6LkmGyDvbSF~&$H3)c4OmM5C`jBZ_ z2|LkRnc_iA+v;i?r8lNr26kmtZ!3u3hdhtra94NTHSfQik8X}G@e9CMkDw$|)DU@Z zU1Fv9trR(G{Ax{Fah>AcJ`pRR4r+Z=6Xmfh?>24dk~A#yha69d`dnG>mQM&h2Nk?_ zq}N5GKI(18XHeN{*hyVqBjy2Bl1&x20>{`b! zf!INgSN_-WU^D74I&i%V>08I`SK)y%MdlX%5p6-fm_bsbb2EJMw(Pj%`Xiy|CiNEX zF^77m;#4|NsVf_9R{a59fydhSQg=%KJ3_DpdT2(9nj#gp(bH5Ce zoE7ocpne3FI2|1JiDNK+Pn0xovTf8Tk#4KEYIbtwwB-rI)+YTm6U?>dXccY`gN;aK zG!*l+iY0wZC!z6XeZ0u9xKe*?_NeAB0Lt$LUZ@F?wlW)TubU_C9yiO-_~3D@4u$!S z(x86i7JCQS-btR(ubVvW9A>96LkS)IR|-Kh1LS)IIS73xSL=%)v=Hq#uD-EbBlJW> zQM?^#%Rq@>(TVmh8Lrt5n(aJ)W6A5G#0+;RcvC*auDKyvT3_7B7%;UI?T&>0X~Q-^ zYNjG4z;7;^WT#c!4BfX5gfcFdxz*c)WwvGYCYinM&`W$)ZxY*Kl_6*A;i_~fGyHfA z`DKfmcG7_@*b=daw*7udGga7*Eu9~f-SlDsWb3->C-j0}qv}JAtX{cCx#eV+hD4cL zTA8Dk^_4@}}>PAzOvfmWW%A%#ItOFH^=$u4@rxV%u8-s2+HMC4Rqjv^VJ{lbq*Y0dywW zogPM`q~7@73Cx!(I~-q1s?%UGU%=pN$ZIhQ6S~3dmH%pUR+y-IJ7{PFws)Da&g{3X z*N$spZI22*nrX;4>e6T($#k7mQ&s=OzuBP%#mlQrnPNcFo;h0R_7=hEw6)|1F*KnF zJCI~}96W+ldtU?In%5Q&Xc1xyu-C9eJPNV9b%74ZTjaf!e9`t0$^Xc=^pjbg$wa*! z*B#^7(CI)N7iXTix9xLp{KMXCgH~c>9&sBILu~h}fB7dheRg%`KX=mzqt%FS9H>(~pe!~78#*eWAAP`&NV9i=8D7I1+qV*&BJ-@Sgy2nb zOYN-e*W+Vw{RzHjcn8iWwpH4VWCE^03|fN5n)^+$J4ckYU03{9o9P@q;+$$^7v@NXQAqL?fh)j@K-lX($_wb)A4x}WB7cu=B#1*uTeRn7#kh9q}^|5To#aV#G3}+{s@3>jDDY zzD| zOBz*F$TfKC_>B3pJstsF5=|#^Ssp|etMWgoSQbpH;A9X-T0UTC_C)qX(q!DqNLLrO z<1iSpOMBs9uvo^htN115g97DOM&T9B=)R}9h7zTw5#b$#gm|CfkIDp5eZy91Zm zWI0H_=N@+A6>pd|<>nTj-H|2_!ZYI6`}4X6uFWF7T7n5j*S9dL0>}U@`{WCWQ;)_Q zc-e|x`TP%xYVCf91Mw+N+`VBXu z5xf=$EEh~`5c`MZo{{M+O(9nl;4&bVQo#za3ivE@FV&&J&MOB5L_D`XdcOyOLj{U< z=429F400;VcRf~-MWYu3zZFemi-et~R6zDB7iM!}#QA=+e&vIC3@*J~<9fR5maHnLBJkA-<6UP}m%**Z`I}miV7Ww(dTy zbz@GpZiP_x^{Is}T9{Kwa=|eErhPm2mC~8Z*&m1j^z^v`uztfaN^rehs~>Bjq?qX7 z0Od6r^$d0h*mzt^Rb4Q_>(w4A3=p z35N6Sv|^aNHAUq*RVi@p(I6i>6QX_uFrP4OjJwf9d%h|aqVAPf#S8@84;F%lpV9f~ z1KP=&i6%OTDv~uy(w?Sh=$I(plU|`;zH}xK4K#Cnq zS3@VD;Cn+fsxo0=0GuUBTceN@{2-J?ib(JsV@-)~?!0=XwiJhumSi@G?W*-u?a1~R zK&eZ}+sw;c8`mT$J2`G387~WR3wLkU?|YxH5o-IYxBc)SzGPX@^o3umIb6b@ew)z0 zPz=COCp&&hsa83EQ>O(+X8iOlm;>tDGUufBFU)mS{*i9z>w7p0bDMgwYH1N;7zXZc zT0((*zi$5u`Fk8l@S|Nv3v@F_|Dm>QG2oF4<_o9oTmZ7T9UmYyi*~ooBOyQ_;q(`)aPgpsko`MT|UzHt_;RIlYlp6U9@$h8s zt(h0>!ODf@a4krc!kEt^tt3xr-Cd^ziX1+P6C09q8{~ZTY)qS$1fR?WVAulWIr4SR z2=7&T^5l6!?HuRVMb((-ZRP|(0LA0MgD4LIeL{Vu@M4ElroW(!wy~kvM0Nd2ajZzR7X$2Dk1C1kGsZI} z(XH&ISZz0a^4;TuB$%m3{M0zhzc-an@s?jQva$^>*i8jLTajUuZrA&$}l@4qK}O#z5Z{@;ApOmiDXDI2`_8j1%64ya@s`dSiU>BVdqW>U%>;b-8FCaek z9VYo5`A~uN>TIybd2(F0a2(vB_SpUVV{d3$QXtLQM8xJLt1)nUTo0g)e5?b zwgRH0KWpJPt#szlyskT4Y*Hye_8Pg~90a9jdVW|#D^M+6YG7v^_;AwM%-ceHB$sN< zu!2IFjdE3IH(Pov7Tn>er{muw5%W>&L(ms&tZu3rJ>AM`$~eZYD6l5=sXaAWci^px zXzVN46R_yeWIVy6q-2aHEH_xx(eOpWI~^DiwDVtN$F(xdKrzC?;;&kyq#VcMce11!!d zcjZBRq~cSmj7*;<%R^;k8t+&F-U`v`PVb|ZrbQhOYnQ?~lEsCI!$&ZKf`3^oHYqRa za&i?UCpYY7{^;{#mhmH&FxY1yZ7GrJ9FO~FTBR9*77!J!@=q?J(Y`1eL#j}X2I3Sb zPjF6JB}uY+31Z~R$B(ca>S%-bhcrHG(Is$j31ds1alNK^Kj*kubkMt%EJ$b8Lo-)? zsZc8#^fuQtv=9Nd`f5%NGHb~YvH11AlDW7h8`Mm3rkE+i+Dopv1QI8N{3p-rwP(ZM zj5=L6_K*tcWDgZizpLqB2{1(NGr#^If?2i)PRb(f>^tn-Kw>+|Giai|i>FTe(pjYfQJ`lmcnM+w)|1qL0*rAUv z+U>DVOpF+_fVqj-rEc|%IH{~yqo{I68zT-aO}n8N{k6$bbB~=$-2QrqGbCH&%h!bb zD-g_1)G9W&n^O9M=Qa%IlznzjF1#r_kC9(DSd(Ii@R{%;zAhlMC$5gY&Rzr8{ooG< zTrYh6{^`-I2fuuY0kddj7ghcQjQ4q_K)s8B{lO36eG3G8Oqdsz9R$aI-_k`3Oe(h@ z9V*P4u%XI+4}Z5%{O#AjnaNFH#h>ZR-4ZRRV5LoG820Y?4>8?u=-)`h+R^HO$!fgE z*cz_~J?9&Sj+c}9?#SRw56i|2r2YHfp>Mguz>(jgIe*k@WR8=@a7o0I=-i`Nt6u>u!wav%mE(7h@EE~ZLzqQy$`)+%LzlAl%v9Bp-jRsU#u7y%gPt@n zr`g3~>(Cqv>uU`%bct*t0Sz(b#eEBQg1w9T4_oFGVdbSIlrQu~UaUy%UG$DwBvum< zokRBZW22S#2J2dVh2PfQu&9g%^^EWAY7CCnu z^BzrBt!+Lr`xJt?--^0RO*dcNJHd<$;wa}5F)8&SRRJLd4bkv0`4c!zs)oP+&Sx06 zA~+-fhkZeklLN~shHu_0b+C!HznR?Y8&?6WT7beDrJHKui_w4LUnxfn9c8SjO>a#r z$Yr=fF?2*GrX7$>Gn5goexL~^1n*iM$DJfzso>&rHBcyxaa741Zj=RT#cXwRaSwhvpn;db3+Hv;-Cq@pYc)7*8@FJ^ zbIz){g|~3A;DT8S51ZR2%Vku};MKCq_OuyGeah9P4hbw-Yd1)4u%|^ReeSU+Xt62i zyJ1!j{k>A*tJxZaM4ArwONAZ(2?QCx87(#=RHwOfb@5~mZ>vMI+hVyYW^Belf1c6q zaa)(##njpWg}+jkllEDTESs){8-~Y!bjkSX?kN%$%LIxN>0wl7$*bjg(a_7VITO!x z8=l+lemrWQb0sW(0!)ia(iYpyVJ^3F{SCrh*^W5v86exYYrVW-;kRdOXIDzm{HsRG zdzN(U<9$)D96d+1Cpnl9I&qL}rW?k{VU$>q+O8cuH2c)yVVL*bZkRv`6u<#Su+9V3 z1Osh}e`1J#vhIprxl+Hh;WZ&lcp50t@vw9+sP)meOQ=PCa@pjiRbj4jpWqSA0Y$Nj zIN{zHkw!dk$C~X;S}>p+1i$4W+2#bmmykQcCfE_=2)vX3EKni)^itP*%cn>Y{4!^FxihMPhsRmd@+ z@;TI?SUTz@PFxx0|BIC!!rF&+gXpVeq^P;`Md8V6HBLhS%IPI`_Vh_y)iyPLuSK8} ziYh8WN%1@!ea`Oki3hh_cb8ycQGrr^RCSSV5>1?VSN*VNx(~}yf04feC`6Mtxcwl3 zV+yoI5s_*PO}_^$+|itC^9OTCRn1KCaf`kYH%?!jvwUJ2RC)#$i-6m*{PyMUT}l{+ z0z!FfO6Crvp1!=hMsIr_TG66m5{s>RXRZ4Sb)}8xBtboqi_GvwsrIn&N0aJ^*G4eb z=~kecRi-T|k&v7cAu5lw9y&NNlHl+V9!;}mxRmQq6#7#^B}No07Pc6dx~F<-y^XI- z+Mc;Rb_V7b&$q(jZYoh(ts-|Op|Jiom(hmXI$h)XSz>chWKMx*UdwY0m7dwzs}j{K z7GW}0J1m3C2T=3=p_Y7509bvKl#-F4QifN`#N3k#Ju&I!Al`l1x}Wuo;)UA18kg)X zrU$tGK4%MASh%aAN+0Cw+PzC;p0-6YwffNp)|yv`em^B#*5|=PpY9c-uiTtS79`&v zT|Fast0l&P)RW$(BWUNN-zC-W1HgCcGQ(m$xMTj#;aRR&8mh=zERusxwMl8L8cvniheAd0{eJK^*e;k-BITt2Es z&Q2QJ&aU2hhU>*I3c(n8c!v>%8ogTdBhe0ED*nyx!I5Nq?rdytI;dh0VBBB#YMfop z+3G!6vE{i=6x1trKut1bzy(VhL~a_Vfr%FywRz)Kg5chm6!%=7YOt*p{$viszsZ$q zV5rbK41KXlj^&IaA9JW~JA6u?g@&{yWYDn=^xb0{PqSgX`dBRm|^T5$V<8W*9VnL)! zP~%K5o>|fzc8Fj9UKpV}JaKj=#@q?9s&u$+sF822Lu+V$#4$vzZQo7Qv&n9 z5QhId)$RYXF#I1Z-G3aUu+&=8~2rPb3fB(>xR?KvYWg@)yzJ z7Qm`yTFYaTiGX2(pC@^bNB7Ui&Q5v$?X6(S*lT>gBS1zlW#Z54;-4m1#mplMj0WNk zukR$$*!!O?&7Zd?Wn411pUUcvWvww~Rm)0+Qk}nI7Ae7EIKw*SaK?S}5J_TgKUD;W z!M=nDfEwPPR_AVLn}uT8&)h+-XkWhmR+dGRN7m_kf<)@8{Wvm{tk7CX zC25UOlN)^n1I7lKi78HMUi3d6O}Nn|HfTB1<)$N3BN_cYK6-GrV51O5!OO zIhW6; z;5F;<*Q*@x?pWVmd`@kw#+5FK&VtDkTj|8}>+KQ+edv!`$w%p4FT5Dy(Q%PhY9VZA zrAi)~j;cjICN{sEle`J){SPx45YepgDUfESatG}CMWjSErs1}^f4DQ7^~EQlERQoAZj@3vs`o;@g(cTYz#8jdZ@p(7K zW83E4Wj0kze(bdsQs69_nW>ugY?YPC+gD5D2z0{R4wDU1!f8#~)FSIT8Oa4!h;Ha# zO*)-7bgLh2Yt_E5uaEIh?tOS~>L-|(L6a$hp%f-FZSFBV z)IZW9{}O!TK1mjey+Jj;6lJQ8=2#>OH_S)#Rm(|I*ki5=v5}UQ^THww8mt~nJRy>~ zd^YIh>LP%CT&(SV=rCZvQ1;t+E!mgk>b-=U+k94lw%#drp6|;(G8Vk%8I3E4mOIx+ z_2Bctj0%0iqvyBV5{1E(+wcVrKP|_=wDiD5Y8g41VuiE?ME*O*o2BuMwzA?G)8|{j zRU4x;j(?Dowo0GqUK^#MBA0iI-%#$7g~0E;xw2p=6y)VH0C50e+4qh>oQcK8Hdw%- z>ybER+xDiS{*>L|?(*qomRO2CD7gAX)k>ucfqq_1RHJq)-0)7SjfVRl>5QkhCk{#| z_`uPx!rfaSeinol^0Bw9(?CxL%da~Lu7|c*&lr~%gxnNwQB{y4tHpBHyTTgA*lH&o z>qq<3E5%ewr|oO@jtH)A{IhwOclPwZDYdDDi*OTzbch8lUqFA@GjO1~-((TDakHIh zjB%jmgrLVC;%n7Bd(~<9`|?F_rY42&|2Fe6hw4R0F$BDprW$l7H}Lqn8!btO3)E0f z8kibuCA9{fOxWHU(V%YRw~THV;GF~E@W6q<6re3_9)O?;-i`Gb7xwNnXr~u8?mwd0 z^%cgMgXf)3+q%Io&F52d*zA9$>*69&I5b(8!;FWQV2<9sFa9*BTBum2(C8VEDJ){p z3~E+l@^lnaR(MpSEw4{=VH&5JiV=AGm?nWgZ9LgW;Ufq$T!hOpw#id}5JtAP4Rvtl z`d<5-U0dmuQdsLU3NMUpuhF!?Il(>3xJ2zjx|T)8{(8**yS+KsUK=;#_yB{Ge0|7# z2r!AYom346efyLHM2=Tou<9(0$F3eeE!R{mzqeFuW8$Dxu4{HjFQ-&LH{!l{7Q1+L z-?x;gMSJf|;~T;E!Fi)4tAM{2V`N>>Q!NwiXo7-&EBecywrpL#Id5I+HBL%FEHd@Z zkK$W+`q(x9eNkiUV8gLV5-_8T0n)Q6IM9Cv?6od7rbnpssNj4?K9;}#3qgH;@r#u$ z;Q#PZZ&$ode~MuIM7I9|UbptZ<8w42WN8?W<`zB6*99d@M}&iJ{YW$Q25u7RCZ-|^yR~?Ypq<@!+s9ITG%9h{%@st zOmDw|-3+1m-WM4rwE^l*(Lk0}!;DSyq1ORgG$z?V~JD@bwbTb#e47lwykYO=r8Khu+!8%}Hrx zj~=(h0BqKiS81}vDlTVx#9^c15I>RA_VYZ}1w176vJDe(E@_`QfE>(#7cnuP=s^$* zCm+B`r}(cu7kLrAS2EgD(&Gvv(gK_*6Z$hG-w`nWrN3YO5*j;SpY13l$>)PKgD?fF z;Ru^4N&1LqQ>@-%+8=lJEY|JZ_dy&@5y%+G=PHE1IctfY0E#h~a{i@w8Ll(267o}WaR(34p}`bvAFz}^?&6M8TA;p`%$jch zVZ4~kz<%cJji2%gyOmcFtT``lAw}c~FJz@ee8u0%W!zjR?R02@Pw;;zS5^>@A?l`z0%2axxsGbIX7Ymjw@1XFRM$pwwM{+&Q}sQ=gqP6bW6f{v_BIaqo4?fI8bd>_2?hxUsep~<8)eF^})qy;C@9@u5LZtNC)<1 znQ1k+qCU&iUR}DY_{U)k6O*0@0?L@UjT6C1k!?CJxO#lKrPP`BuVs1bfF1y^71HJW zV#w0n_-h{7ronD}gJgX&mOEm66h=RQ%V|)VcIeMe?4py@Ekwcn%bi&A$|fpNl@sDd z<({r7IGmYLH`pdVZ!DC?3YtHRFSNmo$t&>7#wlef6(UbJ(oAcLU*m@rw!U+%{`FJ) zg!3R*(Yh>5bgLFk!=YZjl$8G5sKm+>#3jtP%c%z34Aju9m#W>l{ZJ5%zcjlNPiZ`{ z-=j@sa*PX-JC0cU*5uokyF}0E-pnc0fO2Oq!fsH%aKS+h-98Qm5UafZVuU>{`lUHt zD*wsHR&ruifrFpG*z(L;cXNea_hWPZ&uU--gSjT5 zSm{GW(ANU>GL_RC>+TSh5#qfHBW{MIZu{T=iE+w4&op4A8}; zy%7-UwUw8jYgPOv8e2*k5n&ga%#2N9y?GH(Gm^5jM4l3CzTE-UV7L#SX)~{5bd_Wu zlYTzz?&1-j#iaU{nt5=@pO@u=b#uyM)qjjohgwBwqgCKYZ;sAG-g0o#> z#f+@pbfi?n6nTtnrLZP+N-O)IJ4As#dJ}@mgaQTs`>GwG&WaQxB92nWz}-yUbg^yg zA5LaxMgy(kBz3mH=xzd!PxMe>$OpWm_C)>e^6Q!-kWo=&s5%B*XpbC8PT%O@y=n z!~>1Kg>TP@Oj*I^q3t#5$M&wijC&BP>Q^iqJ|qEpdO6bxC7Nkp6_e(VF5wp| zK?bJ$rRRu@L15$0uvY90HX790zo|AU?=XX3RR75O(%1ab3F8>L@RKzcQzgHwKB8@W zK#i7JbaJ#6!P(wZNL86B0-&Fv| z7yq*_Hj%V#nGjSYgJr9ftK6WUMC+dre>BbxBlK63Lj+Doa6n3lk(tyG?Y>xj3h&<6 zf5@nyRK&&o00LOVkLj1$vw;I@ei5dsU&^wU`i;J#0FrzFLKIx4Ina411WoRyOEWA! z3T02qER%Njn@`e}*nR%UP~)4^zWBt{n0#ANhAmwdXUFhFMKO%EHyeFDxZIfxri|Qf zzXu34dYj9aO==iiNq@e@$pO!#Z8i5s0EAW3)vxS~y%tke)D@|~ojNVL^kBTzQMZn4 z1TD5zPHx%=6I2y;r)j64qLYg8$D%z$Zmt8Mts(Q4P=d6c#k2%`kM(C{+x8GNdWJ1W zpW_s)vT_<8y5BZ%xZjq{N4A#?q z0chUCfix=nzx&05W5 z<+)86|5_VPq@t;+e#Sk`*{O8^4?Z*CwZ(MvuCt8!Hgwa8!%rEnmPLbv;5m11K`ar3 zkatse2BX?1_mxe1U75#;q+{LD_|ZRF*vf#j8!a;%4qC_MAq1{P@S+#Eg3qW~3pzrH zv|y7ea!3!+;a_LXtvpFQJhNF|GE4POeTD>-B~~b0LI}pCnGdiMvPK#^Opsdc{<`AG z@3QSoS4m>iya058y>(*z?clC+vUWL9&A?mPfs1Q18ad(S%?MUfXlYYyGI4s||7B!C z#^K1t_f)mlJC|XmKTpNKAF0{75stT5lpk~KxJ**!F7xss+=LBehWha= zzHoPkguX0?Jx%GTM{;&0dkQhP?v6xH&D!Q<>&iQfv3bO!mHw76!r+O}=mwr$(C zZQHhO+s@uq|GD@&x~l(kqN^j`%XPCNR;>BPGv+g91-M*2ef3}7cyFm;0yd6vI{_Vw zFqN^e)I~A(2*VzK($nw9gHPU-W1q7>bpMh`f7n#Qz1LWLdbc@4;IB6vFqs+6I}aVclfPm*IFxy zrks7=;^@UX!M6Gvr#@DOzBm;zV$uXJh+kay(vBnvU$q;9E_}qU-?#r`u-DOUhj`Wh5 z)KRtdO`nCwhHPtry9zOx*HUXSF9aPWf=@*UZkX|&;R(bEa|iO~Q)Lq3*?#POvO_HWfIbKJyE6 zVBB$)(|sEczWV1`$%bQ*lO<|1Bh6|$-wt#*55#Mz(~#{wSVqy_3`vijpXaM-`kJ1+ zJ{d(+pjv1T)YcNyYVyzeTcD@z$Ds*4e>VP1KP(gKFfr<(MR?%Q$C8qQ@Ct2HI-EMo z5G*bw=J~qw+_{YI^OZ=3oFxZCbEE?diOwwc)((|P_q>u%jf-@&u3nxfd1w5rU61+v z8u>;+xFm2D%Gj!Zu7T900vp~4vAe`7H|Qkyv|l3Z8q?bha{hj6H|fAAq3(Rn zZ&_Zn{6t4vNLl-Rz!aoK(b!s_-*)EukfO(Ti#x5{Sdg_Q*Tfd?z)bFg&*Fu1?(@sq3&X#?REe)kK7Ld;CG?UK=X!|TwAy9l(!YMGh@l;s zX$q@-lSf;WIoeTYa{4l6C|?h39|?z*er{IbV+(hM zp+M#@T+p*X&qC(glH(li>v}wB5DgL^ABqT}A`D&>lCr&+Ti! zoRLtwdT`}EvmWaaal{$^j#SO=R|>p zJWNqmUC+vcQ^3K6JODs+7DaBsY1;UX(N{k7O|?9kT$E%LlH+oHW`ojc51s zM#N8Z&&5$&A8lVeDxOgY_$LZBc4NB7;W|;9)^^i&ma-r59SvLOup;8xGLxh%BR^G2 zy(Hdk1CW@u6bNVZ&;ijID5L#bl2**zn-{l|``fvrOG zSQCfaj~b04ZPMhe8<)30)kSGO*-wrV2()|KI@eVh80P-IR&Bgb^O}aHdxEB3B-K<5w7C_4FK{{=K-s`Gk&SZN*KWuP#5 zPmHHprkqehwY##R6f;T6QK4hmu!9Dp|L0k}WFgi@I5nV@9GPjW?;GuSE^S;1IX?{o z^eZn>x4y$%?&7Zfykta?(wr82bdI{20#dL#JxE5CJb+QoS?Nr@Y`+HIb|*Z_?OdQ| z=(q5K94TpX_6d%V$EP;yXxP^>nS#@VG_mxrmZ4U-7Gjx+ck^gIKX)i;;e5){ZvUhN zEuPBeR8=3;FqUeec?rGAZOd_eU#Wr6zM7MENnxfZ%K43Okh5;%+Ii#;%lg^+y0kI( z;wlF-FV=Ej8#$tSDpFZb&}dS_`uwi!^2wMJt{J3dF|Mr;6}8OBeVsCq(6Le~kpN)x z6%CCu#9?%+O$3|OSWZOJAg~9=k_smYSSD?R@Tzf+2dao+jmk-^`u>gHJ;yl|6eW-C zYuq<7f&Ck1<_PXqE{V#k2uk5U<7hPXg!Hdu3g|64Ax&|sqZpu8TkLpZQKSA#07<4z zvXQ5@@=^)rKUY_)q&Pp5!)#n-KF$N-5TBMno|sB>=lMU;W3Iq@1({a#vI?G?J1}lk zJGRJcr|P5Jb0g4NmAwl#o}(++^UxpUFzF=V{k2xNRe()i=pQ4>K=;~;%g9z5!-?32Jd7%snY*b~w31D~%){1;~ zz-52P#pe#GmrX2g4Znj1Frf3Gp+$#2u-+{yEG6rPX=p=?WghHtI<-)4@N7k`LOLi} zN=zA_2n+`kcrN*53uxmO!u7a5m-Z^_(pYlKscC3slgd#V(PirR;3$rmB!SAX7D=2E zF2h=l5j&@mg)WS0+T@Uz3dty-d?CX@F3k_@mr%^pHFPeOO`)S=9hmH$4jyBGm`;%f z1mttUw94ox7JetykQVvT1%ca3LnI=t4eAH08kVVQt2EIYW#*>PrjtkfsvR9?G~cAb zFzUtZg}6;e-@7E6-Ct+64Gqa5eqL;qjhU0?OHtgu5rofRsksisV5hU&bY(+blfqiT zz%2?QAdnFOI}$fqSKumu;1&Q+Xibi=ON`#+V(ArbX63ZpH?alQori=pq z*!Z$4n-XSmmwDC#C%N*Rz;{j>juvS*)}@!ofVWG|;rhJZx*OK$?~4o<;D7;7c1y)X zHSSGdMV~mJ{XPWI)sl$kK)Tp-wND`>aKJCAu~|iA4|kDQv7*-@<6EqVHbft(CFz^z zkS~52T+$Zf77N?N2})yoK`E6YE40cODtCccxZ}9kiCri6+SB#$+==DL`-5nGrFX=n zKdyxvn64)-K&-33_{Em_o`ab`JX+3QJOmPXk-+6Q#TRQ-hYO0vwKv=j$6ZTq?%@93 zN_XYZW04<80XD=))sY^oVH%{l2dC}*2z_3IWU8zIT`n=VV5jD2@K|3q5|cD}*x>b4 zDim`F@}^p!3NLO;zYj0wTsr#+$~!_Dn7*4>j-8%7n9ngiSt6WAjZD_NtRUw++6Z={ zr~yPM!e=;FV}p(pDyk4lp2dl~%3`oKm6_~JVVqBj@?u6tm3uMxO&q|7OCiJDeZNID z>Ajn=X4}ZTjbl1IXJS)jy4trjn7I_{WYUv6_LG1jz2IxVHrbZNviR-}4sxRUMC^3{ zqb+dq#W_sqE=jSQjDa+Z4^Y{mr^0HzZ=taM5X^N5NbrnB>T;Vjfl$6_YU$P(r;SnZ zuyfJ6xAxUG8qn}gDEcT)CiJ>$QGMTXUo3{y6RINAEz=&f%ySL8m%I@xr%IAPStCA1 z79dc^w&dgr+ih`rUfo!SexkZA&y6!GWL+Bq2+gsT%5s1yth|YM zobz2Cz@8G4G8qJ`L9qogcd#iKRHU0Xt~~GV$V1MFXq#h!QwuDSR+*M;t1%iFS9iiUM)7#5 zkaV4Jt$ATGcQyjlMD2zZ?ZHzOo75-C@;Mi#M|4-MoQRBMkU3P&c1#w`qf>Dv z1_fzpv2AI9&<1MS!@D`oidAEYK4>5w@Lz_ZWDZ4FVQu&=NagH(K2xlwoFZ5F(dkj7 zO@74bPCDRmMvyy|v5`w&c#huDiysJqU#cf-bcc9=+ks$hN4R+}+;@q1k#-4EPWF}w+zwWWyc!= z*o{?*DRqc{_zLFn17smI?n67l-gd3E)NQ9b9<3VaBFnm`^CZbj!R8JK3Ye95dfPTC&9<=pXI~90)@eMX{ z?$o#H;|OdFAlZlymj*jb|1sG#7ee^g<5X@0Gkn4Pe2j+VXwZ2H7#ajUvyBWDnr|PC z1&um~0Auw^70zfnjKM|0Xxew>gZcV7rm6smUf+jQdWcbqqBEco%Qdtn2%i}SYaCrT zB_WX?Eu>HeV0v!Y&;6lZXLI$ySIckfcBZ9Rrp}*lWp+ph0P}b1&fa4fAjbY3E4|S* zs)jt#AQ{?Qmt|C@88uz`u#14a4A6K`TMl*i02Gd0T>G;fYxGao zjl`+DfXH3qliS8{p$BI5Ey~6w9OY$#t?7pZcCdo@+l6?F*|OJmN|x-qK@H$2$|t|X zN7ITJw;WKPGL~+oCFEWUOH%fZX!!@oEOS*5J2OYhZdLiBY6I64I~^DkeM5s<@u>Eu zINz8|bIF3n;(uQdKu|<1XDBz7yOp15`mphs0p$OVcqctbxiYiODg9CHt7x;AS8=5) zgqlJ7M)*$jiU=>J>?aYMsD$=!++;97%<>9XYHn1J{&3%WaKud$L6ta~E6jRrAG-42 zx;g6HQ3z;h@ki@o8h2|-L4w>pa5PFi_^y^;Xr)a+Mzsc-2DpDP8t}_CmCNnZvgpGO zevHnBvi=~6eP9t&R!`4- z$07)R&WJO=4th++GVl0imykL8ohuEPOWs}l$FmVWp_*C+iSF72CmrmE!0a4^8UQ=l zmQ@@qyY)|hl9n`fC4~4v)|e)+sM=OCLZLOGfvA-qp8PlUn*C4(%3%EZUF|`D60WU^ z^I(np@P7HrrA%~cKfw@BK^S|4qnxTm=CE1?tHGYAb>Zx&rA1+Fpp=K~f-B^H{fkPv zLIjs6=e`RdKauWcm#Q^lVYvq|d>~YpRrT;liKH+1v2-70Mz2~wXf-I+g@1`M)?+2~ z4I_*@S-X9I(KaABt@gBGAw@ll1%3DUx(jRKlGDYiwh~@$s6ZvVbW+|aU(TFdV7#(N z=0bEdnK;Oc%^iR~;2I3H?}@GdL|JJaAkex?KM*5vLDR;$dcY@Qhk}92x!BdoUk&QR zrdD>K&@Q0CWjHIybgx#W-)_{T!t$nxIHb{)z7mNhCe%tx&@j=5u(|#QnAT5G59~{m z#CU$D)z$TACY|BEwLFcn?S$P6$+UAN=SMW*Q*MV(ljK|Nb;Dg|FUE|MAqIbL*^2>f?@E!5 z=R@!#{P`Rior*|#dm)?W5{Cc3fppFqf0 z3>vB*TSEzY?2+6XwlGw$l@h_=@{Y@fnOI@QmfAa`S)GR z+4Xh%4?|z=)5v56!THaxcgQeh$>yAu0%&xt7coo|fZw|%r}aEGF4m9YjpPsJ*W=J$ z1J5B#wOj;ty@)n&k~|BJX81pCJ6fLIeNx>q5cwFEH;XJvYExVW=!1_1Cq2PHyX>lc zzbuB^f4d|JO?9+o4x&rkrU8J7JQqBKeU;xiHUZgQZ*}IPX)-)cR;XS358_I6>TOTc zPyVhS#MazU8=tn%?rJc6hKufSUfCfGyR4=qo{_IO(-Xzs-$2@NXFjYcWtx0v{xcef zf1yhi3#Tq)CQ?~nMK|P}Rm4#J1y#EeG*RE!NVXJSB&E+ix3D#cXw6{d{C884e<^m; zd&gl(Xa8sk!&l#=UX6oSv#{MWcfzKd`w3bR)mvJ z^6eC&n0vdeJ&AJ3?gRnVrM>Hr)q1jm;jh~EbKLi(`kkabAV&`jw-JQnso^cmdrwdtx^B3rw7MLBJWs5cE zB(04uxX4hiuYFn0fHFjkn?ngJMiNd1Z45CitBEO@%YXLv%+wnsbc-F)jU_Z4b+?)3 ztTH+NLrEpn=LHpq7Pjv4B=#BQVAp^d6o} z59Mr4)IR%4)P^5FXziBM6C}qFmi?Z%{ie}g#bkmJ6kJa?tKoiSr`I+Kt6Ym5=Lxu< zhc;F0O}jm!)5{s~IgATJr-g=r<;M3+7v?|THii_y#$+?s7vpa9$C^gb)ft|NoSv9; z$L`Lx5?4=Q#CR3>o*au!2*z31<(n-a9f-@A7(Xd@s1?9AbZN5{6&AYy1d-X12rnnQZvFePLC%fELGt*?i~tt-;aJ4Qr6QRViM`+YVGmO5Kvjr`)u4fd2`mWsRPw52?GWIp% z?J`*~8_CmROdV8C@@1IFhOSc3tYlPpp&uJUR;a?(1tLdzl)ppw0QDxen7vQWEcNXj zAA1>g=2iT5c{VZ|zUP4;aXj8S!sPV#2{n_&O~$1v_*GQs)JVeBBl938BJ-)=Dyc{+A`yD~hweWH`5uNnADb54^%hz$N(gw>A4K6YLWflc?- zRHyz_YBbR|{e(|CNk;IFO{wvi6%bYdIra07kUx?bntQxVK4C!1lH9tDxX-=JM)H^` z)6#Pi<5&@;NQK1so=L9Af28gnQc1-{fCtsJNw9SG%rxI(KJjubrmsX?D`|#sf)?9f zXJ6{MmcfJ7&3IcueDe@=p2Z6;i_T%0nP`rOo!xC`Qe6>6ntnU#vlu?Q_x}hSY;r~MT^Je;ma)>rG@%4A9y?9 z58gD|{hP|EGNMugtsvB<7bW%szq}f$Nt;;%Ksq{`_HfZ?|~R2Gt)nT*yLZ!(Z;ZY&y7so?Vs}6-_$v3cr@{hW?GA3 zbDD8HztF(51+x|t1d|FqyS;z;Y)?#(|Yc6PfcG>Pd|9S#Pn!9toFR! zT~E2VxYX4hTR#j7>mg@^{#6`>4lpts84gfHBRaUaBBhDE`?T!y%-8@mw0^+jC?#1V zdl`|(gg?}V(~?Ezx9;-sB|QM7(=I`p&2I77f@j!*zlTx^d?)bI9MqTg6TGe%qolvM z{4{qKv{On3_&^dbf=BQ-l@>VXVF`=*KBksC|BH@%R|X@z#)y(`MZ^pcl6g z8}B$i>FF5&ZBy>k!WkY@%(lBPHNk%f@#kEfk5S(5D&7>XQYGu5r@G6{;~`@#Fv)NJ zs;}$6XtcHh^Hrm?N(BXzjaJ7%MM-KVW8#E&2OdU4mfXtt;|Sk~LYR$mdx-{WHNye+ z&}rof0|}(W-N=rMW(~9(>>P(kkEvC%25*ayy!xjEN-yn4XexEfQv##nlJD}lM5z7^M84dbxgt{Sb&dy(P?eiwX| z-i2pThNiq$T{wXh{FoP_ihcq~*lZ|1?WYFV`K?N+kSQk|n@;FG1@518yWgg0!+iP# zjV+Df_@u7}L6;PXX#>o3bs>V?aUu1PV}c#N%vEC^#{elT`05*c$3DX4?U;`pw+`Xn zvrPTIQK|m5K1V*>cXy{9GG)u!{F+R~vDYEcQ|tnbeL63;^fz30G;L@a-UC>yrk$H> zx$R4jX6S{TOLbCp^3LifX5W>&@<$&C$jIQ>Eau42uEl2$OAl)qw4&r207+#QGVKAm zN&&2jWtFhyKY}WPT|Xa@WnGFa&R>*LA1|~^9}-uz=VDN|97m!_cDwcChTbe@EiH0t zcnh*-q{_MR&aWgU6wj|-Jdqo|Dx9UEj7o;04EObpR{vE}bto%yFFAHD&Or4^Mo#yI$>4pje`=!@e_hNwh?fWb6I zDuO!LEk&|vNy!Q&;f(+=PPw)F@)?h+3f_xZzGWAsd`vj*^+al#ymEF@mGlAMK_c6i zO&dDN1seb4ke|S_n(nsJiC*(J(_vJG0GtgX&Ug*osrE08k6+635i+qUc;Kh+dVm13 z5-lRYYxPOaLJ&Sv0t`@0=$nrI$>P~e&cbPb-<7)uQ^%rA%v<=0&2C?Oxv@o*g^xxH zo={DsGku8Zo0|_ee!K-TQUml#_!gKZHOw6r>Gu*qIz$qi;a{z0wysN^8gn`=_jiSQ zM}pVAB6QfzAMH6AK%r@X5Oq2R^3)3KTSEQa@uvA&E`m}&7|mD5(?z|;>r4H;8y|`E z)sM#0CcD>QSj`wL)q3?vRZI|rM*suy2&kP2qmU=(+DRCH;!~W!JM)mF~hCWW>L&qOH0aRros(ZP&UDDXp=en4w|Va~S+aww9+u zU5@@RqsPBt|Nog+TIEA#hC2kFil@T_>84+cDpG zZ!v)t@*&c3)ox;670t+il=KziU^g;HJ?wA6Anghjx8A&GVr^-G?w;LXboMtrV{qvN zp1{hw%8~J;g`+;h-$O;c=p1acSWyE%n3fl-yXpJIHn@l2SQ2UyEC--9%VgLUW_6WA%t(G>6RQG%j)^_-CHCW!X^oe=o`BNPxTwB3>(MBTNV zdg6cAYzZSv;YU!BZq?*oM99bF_-JPA6v=T>nsDd*Qqo$5=9W8~WtmD?a3;(qHi zi>AP%TR#)N30m!l#2%LqFu7q^=T}Q5ks4itPrP-KD#nMeDLuTA?$P01#Ysi_#8OVN z)TUIE5FtYF7Y*XJ6BDCDK5jDm~Bax zW$C+J{J_WyKAax2vzW$qOMGuFH-7!YVtN4pZ&m97zP@u3b|?hLq10dJnSCK=37xmZ zZ`uSj@oe zLvZ-V!BWeOSb_BswUTL%rWXfLW_2{sm_l`YRIV=D*3cNsC!lNQ=ujoed+;D3u-jW< ztPn2A1cHIA40i3DBD8Uhe87}cLBv&v*j`0&L%EA2wp+fa9y&d>M4V^(Fhy}{i&6$n zezG()XF~q;K$mU@b@F~-{oG=n^|{FIB?XjwS6?R#fV*P0J5_LTJ?*u7YAYrCX}bQh zco*r87K!N0ij9CXQVu$3xSuds>3;7n~uewliLpHB{*gUh-_?S4f91XA4Jet3hq`oDoRq9Vdie7el zCE2NoW16rzQ(>JsPtUjWOT(M__bRi9zaXoRYbi^HPW$_8t;@t)6Vz#Uz4N2NPBNAe zTYCVZ6L<+lE|q)zp7-t*(lr^!(Dt9>fFK8Tls;YQtk%+31^f`mH4-G(A+E6`6m};! zQ<=-V+%mXnLyAERZGKSv;;3WyWhwTnwC?b*q*yMZ1bQpsx6?Js#R24#Y?BA1tch~& z7g&co{?RZww$Uxe?=ezKAgTtR^(^VBbg^3IdU&altQpIvN8m}+d600Tw9L!t3Jea? z4~H&HfG1BZXAeI)kOMfHRT(zxhF_RDL|3Q~F)f#Q*ukg=j;wosrotKbHkg=x<)4p* zjz^Hk*CxyM(-&M!{`T-_&K&JiXTs;H1OH%^qKWF6B(s%dqLEOT91EIPPPh*GhI}m=`mhbwuARYywOHf%>SwP51 zUy3ePGa#QtQ_H+tNc>|i7q(147>7j_>H)5gh+?cs>bmA&h>spFy-1Kwe|N^~=2 zu3B`8lE=O}*rirLf7>`HrI0Hx=eQGs>DoQ5E@NZ6=@LCCz7etj1dpvCy&{E|DT6>8 z`%Q97Ai`3>uBKUM;1qeTDr3tcayP5)92xdb#O_T(kQ8VX@V9vs)H=?YC@6wOdghp= zE4Vt?mKNH>w~(Vb9CkzB4CxU?kXgmZhGx&BlBHBZ8d30SX^^$dN^$G4$=O?m5pn~Z}Ti`;CtGE zfwIoegQ{F)e*qS|su!g}@##`Q;;?6&>5$W9L8K$XKoA!}UYQJ{VTVxP7v_{^p^UcT z##Ux3?{SX+zk9OEOIabKgQ%+)>(X^8R9`Hl2q#Fth-?N|Xr6Mbl|V@on80Z)Y~it2{8jg;7X#r?d*tK}*93)|ovBn1uOw0AH=TfP2O{ zEkuhR>Lb#oq!VF=oJ5%^4Wk$g zRZ%_s$wP_ab0C1|#vldj)wd%x3Hgvzq^vf*5YbSGZnoE9n03r+OVHg?&M%CjgePQm zcNwfA36phAS+~+Yu6E3~kTJR{gbW)`CbZ81>*Ta1DmUa@yK1+V&X07sDIhzLfX&51 zJHJ36<_Qj`kvGUJpu_15HX92oC%)V16yN%CuHz1=mr8`*T7vgj&sE1F7U33y(|>`* z;$|{4`eRN^&PfhZV3PtHZ5^VDiQrLv{XMyJ5dVArcKQ(6lCcq#2A zp7fUE%X1LzYmr~KKS+!s6}QjnWA-yLd1)D@6N9df7-xpAt7vd259T$wdEj^+lkvQ? z96IbrP!jAZSpvj95-~JDlQ9sOz!$!>YXrW|>ixrhUT?ah@K-dqCH9VJ-)9b>O5_Ou ztr$3IEF(Y&%0-m^C22ly!Hag>-f6tBFp5oai}flj$5o!gbL>#$kef2F4=Ud)GUvWU z$;Za2Y=J}f?E1KFcfQVv#T%ZB@Q06}V9zyJou6v;=TVfwYM}#^*5(&>B1#3F-3jt$ zCb8g1J@0QUDz!hc{=I2b0ft(Za$AvftW&>4fdzL&V=D~@=ueT;9`K-QrZn$;g92i( z*D#~DX=NQNwN7Wu|MLOMFXRBQZ0{g5lL)F=(NyuUn|kte2wMu@T)6raMSiSvAbr}q7jzZiv12Wy7{b?XW5kSbO*`rm z4{S69V5kXY`7^IDOJFq%8!n%oEByRq&`VoTa!s=k`?u6VN1l5XY3p7e>UiirI38zS zg@DBtdLSwv!+v1`YM%*qey=mAem~cB2CDo;uatJRC^(ntdL!iY4zVy3flxUIytTnZ z@rF0we>9Xtp)~=(z9aTD=u)m{blKSO{^#STUHrog;eI|Af=4oTLQ^yz1S}-J% zN80JZCB>^+#fflgkevaN|KJH0mr5v53wZ%tT5waBhocE(8BI*e$tNt$#1ZOuQDT?P zYB>t~!Z}L8v3=S6(=qvcId|`{{UXFjh-GHM#5F2Yf2*13;jZ{9tL<^5G0jjx7Tr$d zh3cw%GgJ%B4r72dR;{K))_96}x|yC;^~w#^Ay+(wVM0ne6QqmxwV`h*Smk6MK=EZf zBT01@gkv?7$8rqu58D<8qghD+wt(aM67!PEQifgIb!GYKAZ71*+&k#QyE>%dlTmZS2WecMDuo-kZM+BJdK# z9_aOWi;n}pm|dr&lHG8s!ZiG~XgM$FmbPK(aupt9PAE^#Ic7dmI%H*7g1$EB!I&gL zG8zxR){Nkr#sN$Y<20v@G~+u1ViT5z=8H7~^am2IA+N;nWLU=AsefH7MqD~{saNj| z&bJ1#a(u>HAW)iX-^OekEpk}|^4i!Cwg)qu4S!u@oN5@ICX3K4_W*0<8m3V5RQc8o zCjWy2L$yaf37|InZdkEt4nP z^$&6S`!1YPcaEl&aJpz)u#9cPhc{zLGP+(&7a_;G%c40+vecq_aoj*N&TqNj)fHY_ z79_=~VEXZ{+@^{Jw}f>Q{cHB*SL%%LPLeqO#Gj-GUg^&HyIv4Kq$|}^V3m35a4iuMwlvBlea*1b7^xB75&wpAw zN1FV`p=8y1MCg$f&^wgw$HaPB$o4YZT!%wJJh4cW)Lh9C_MMF&48s;uDDjXU(d^{! ziV(ZvF@`yLr0Kg03E!Q_16xhO2Z3x&42j>4#KnbPPdM_+1-D%ijZ}w7C|`ZVCf}xv zmlf0LPb3Dg3+av*CT*e*NZyz2>~Mm%TN`r){*>4?69k!Jowp<_Sa6Dz;vp%TTe$_z zR561qZGe=nd`^ilFcUWU4#%fsrl7$~E>1jWU)#jgvheR=NeTbXR-B{Q;|+Y|Bdeuz z5iME3+8ahjB+wcEWl;nWaiee>mAKyr-S|*Pe`~iLu`u;#8(H& z(XB^85Vb=4k@@=Mz?Ky8D&(M$ymj*8D3OI=CQF;(-XvlmWzmu+2)y^kF3G}i&h{9jbZLMLP1pV@AM!{JY-7p+SU9Dz;sFAS`B~RM|rkwkjjg;^6}#;1Wkyff+7%) zfuVjH-r<=p!;^RuX4}IL&zI#pF2zH@iH%UAX~Q`FA|eZqed_%w@$XlrQ*5-r*o}(P zK7ec)CWk$3-C(4R?vMf&8b2vK(L8Fwu=!KlBElXy`wAs&U08t2@@m{sW3@f)T&Gr6 zd1U)U=7Boi?b=6?j07%`@{Sl`wf(+oquy!1oIb!6{&n4h@kQB___~Xh-Va@EZwS%X zSX%u0Y}C0RdX672DC)+e1^!Jcn%`zp$H0vtv-w%?>yvI(5o5fDBR!!-R(IYFb-} zdFgG0&2P7C`si3&<7(BU31eDI;MClpX6$Wi%>`Jxn~VNCPSWYzxl`ln5Koh(0C&!X z(D?Q^5{V!uGuYDkBkwFy0$Xx?(1%Ew;yL`YY=Ovy5=-u&`phN0+&c}4<_t~#Nyy*l zNQwIkC%A-vsI_GKF@0J4=H-~+!JCwjQR~Oyqp%7Kf5gND;E_@R=Ws0>8+f*T5Sla> z$*b+5VsAFY0z`swSJ#rb$9 z9P=kd`&xLee5Qx(UWjMM;?9HWLPnpcz@}iq@t8DeEwUEJLiyPHIPX}uO>nj8tt^2t zME%p8xSq{k+v#+t41xx=74nl0TN;wC4zk@i^sr3Zdk?qGqs>=k@zY+qfAK95l~YvQ zr$Xy+t;6$$)UCf#Oy3~gYDj(Q(<-Bwd9@Qu2bSe5|GiR+uG(o$aG#hvFQWGM z7zrH%@@A|V9D}P;LPr!=ZPc9yF;)j9 ztYb{~6V_r_T}$X7rVEo5FvUoL&+ryq1=oh!>BV9! zo(6(GO1@oPGk5pw2-5J&Lg(eCi5V-py1i6LwqF(5phUL5dO&{z%n?oG{g+IN;XjUQ z{4WyoPbT#*``Q06lll*R+rMjUG5o{v_WzAZ{ad9P!$0h8|GiR;j)Cd_R?E6np0eK% zhUxwwe{SVKqT4+B$xD%55YA$MTD&~eHDM$YQZpx-CoUVl4tR9?08|yXpLf0oz>XO@ zqW1lpg#o|5hiFvb3|H7pRIC`CB`O^qj|cOeDu(i?8BvF3jSFBRQ%5SgwL&cce@_H^ zb8DFyjqix^aG#;QRmLbGCUi>#5FkI{!B`2Ns|F@ZZuSimj5iRBnjYm-lKTt(%rpC` zC-+c`Taxfq?toE8Ep@?NStNi2Ip>8nIlT-QvML=5yd4PP)j{7AOzw)lVQ(GPC(O9C zk4oClldo>^7r6m^pnr-kB-H5jcazjj2t94h9AO?U>ex~Q4cxR?l@wc5q#w}s#%Z=} z6~ZYoJU(@^sO>rir232c9HKq3HeP5kT)j(UzdKb_TR8WlII?kIx;y5JT6dNOpMj~b z{OVabm#rx_X{bf8@h;MqXg4|DoXtYQ8*=evtCi#@N-`8 zCQ@|D`X4(rPgS=RJh?P@16A zwc3~d+$TN6TI;N(Nr&>12FO&~0cruHpEa8~e2 zY{58Y=!#DX;CrqVCaAM`V#whdPg`wefIg)FzCgPt@N?fiM>#r5Z^|)VgO^`>akrF9 z^ay~ctd2I2073_;2k_&-o*;|^S1oiV;J}bjDUcpPyBl?i%^$%!AY#|J=_o%^H5`hs z1c!rQOO!1Idn`6t0avXj9tIW;&98z-%B~?Hcia)4)iK!7pN3WjngOc zPbDK=nJ+}Zkg6u+N|k2>F_T~f8@&#E^(zH1C`uru70k0msJl}|1cB?gU1Hq^6LdKD z6~5%&#w^|F(HMPAbFPc(Sa+sO8h|I3I!as~u%dknK78PQPOzjIJim_Qr6U~n_bl+a zwLcaaYbAHIp#9Oy+$`Hb#TjCpm=F?Zv0w|cm7<_dUp|`SD)@*qO8kL$;-^Jjy(K$% z*1sAs)FIRItAuX>rfI^Ex_N&oN^h-*Aa`9JPrInt!5R3q<1?AmABKJ*?5$ifbMVqRTPyMyXymn}?M4%3cJ zO`rSlK2K9GxHa9JnqGFSfx|2zEh^hh)Qr?$gf+#p&v{|fEe^I?4z4~&={E~M@GfsU zKke&G9|JLI=>_s=2_4R!OjkoycDYiy8duIGd8zc-U9A>p5KQnX9YC^ zna9rSUq|27R@zwK&R(KE%#B~yF;>4njyJL%bS^`jw?SKOT(~_uc4EY=o_%;e&rHet zhMXn;r0ZkmYc4xgD~}(moW)CQ4P0foyi7S>d6hF?e{Xblc6m_Ze%>B$aW{Lv)b?Dj zTf4j{Je=+D?D$x2b8{xQbenQ_f9z3z@wNFDRdi{Kxgi{vVJ|qbD*E^=&J=M~r)69` z_H;t5IM<;Yto(H|F}+YYJ$+?lcoKtlPrsD6;cVDlvp+5iJu<`858GJp?6APwWB^^4 zIIn(n-mKfX(4=H@7vvy%aW!q7D}35#<>1)xcuHP)`#ju*{A}(F&fFbeb!^-W!C9RQ zlk4KlK9w`;9{xZV!8Ny7k%dAemw1&W2~>TEU-3vTdtEct}%`= z#k{E4sOYzodx^VB*5{mv8QvJQlLUFSk2(U&Uz`xK9kL$z*xb6Af;TkhATd*L!Mxek z6%WK}>dvcW{oyM5;Iuxap0t8N-ExwypYe&A$iiY2FKOymFJ z?Hz!0i;?HYJCzrL%mtT)a0whud%1qy@F@dVVD3_EfEhbxE zrGI`M>Z$s4VG9gXeq5i7N;G3do0SrxuH4IT3b0f^AtcoDp6+K6ULIOJQVBUa@L$=L zKgdL5Ggj}kf6dLRqaezeprB;W-e(`w9tb2yFNO%U759ri$z)MimKv=u!XD+S{ zL;?j|NQVvM((|N>G9ClO4So`Bede9VYd?kj+MLGbaVQpoUZ9coaueekjDu&>R_j)1 z+NIT_jm^CMqw`=JOpHX%C-aI}hy*Rg`%LJeNh=8itP73u&S8riia{Ee$lBG4qttCuJ!b^s|brrMUe1h9Y|2?z z+KQr=>)sAwer=JwVy2A9B?(%Qin7p;rVF}qFP)|+_t?YEy%S5d1jYB2VlWpX3Z6`s zLW0{_5^-rw5z5MzqN8T0sOM)f1DQx=)_**bdSP8-5ju69qWaG&6`~?PQyKlX;#(_| z9L3B@iXewmx`i3xvSe&2G0h(&)fTgo<|LmCwUQ)zXUW^tTt!*p!rXofGu>e#s&k^T z#NyD!SEEE2J7+R?2cej80+gLI+`9u49LJHsrTNYM(P-`?1kg&gZ%Px#L`YH!09Dth zO28Nf5dK>G`DBZj8Xixp4nENL9l-c&q^yM)MTPYtkyF^k8G!Or%D-+lspv(w^vG?#A{g%Bd%30f_u+kp%&F3gKl3w~gC*2C5}n+I+?F)*ua6cr2&<-$zV7M= zDCXgPSk`Q$bCb+?UqB10(57m5C`y{prfzt47oJOpqvhCHc+V7$nP8^SqO@}LExh$N z-UGLD5Lmaa1H8HY%(!1D%5aMEo1!D0ur2sK$#E+peTkUPeTL~{fod{+J`&WAaIn(dJ zAmZ=s_WR<;x1|z7PI7>kx!2cp1j~2eZvuoUQ@>wAU?BU5`sjpy7R-P!LO}A-pni%- z0C}a~7@*&Q`A>TECf}o(_k7H4u=286qs*6Ri-i=@ePii`D4Tq^EbB-xEZBxvJlb+s~PFWUo@--^f*RtUHdrsuQhv=#8CrA=eTdk3Z8b zWXX^i&y`nS*}X!nX0Aa^*N=aJC|v{ZA5~*Bb-R)s`5;D%@sTtZC@(bGD|iU8F#B1w zjZx-!J{;$|X-$6iA3=+K&9#0DL)^)I+=}^r-O^G43(mrzOoaV786tUF{Ew3X6=K2z z^fCh|M!)4M-26D%CuGqRT10#I?}1MPm!lQ3NR}JT^VTyVRJ^rgNdJ=rTU4qXqb=Rb1?69xCwd!2Kap?5OI;(t8Z>V@n{v>^sug8;D3d??4iWbo(?0+M5_hYV|H{U<*K2i3+viTJQZV!=W+;9K!fL|1B z$DWt@Zh5UA@0I;*{ao_x&&Dh*!2d6}j%z*7WBY2(8%9Z`QN~lG#T*K$!J+irVe6~A zlJ5_n`3Txge&G#_u)jR9543E(F!SO#FU&mOVTs2+1&tBWEYscFKya|LC^nbk8e^_tK zPmc?z3WJCcMdoK94p2S|Ko0}R&vN7|8U7ud|741C0Uy!k+ne@2yqfivH!H?>*pMte z@1r1-(MMd7CDGPl@3XaVJs++2iS4lBH9GuVO!sQH96Ou#R{vY}v*+K4yapwUs}-`_#yjuCkJJ+HR8<)X_blvnjMGgdLNp(t&FncbDZ_+8zG; zq4s&_!}gjP3vggf(fEz54*=5dkpG`E2LGrl_8-Y>=6`A*{x>rQ{}6Bdw~WER zC_MfTGY0?mkpHcOf{EpSY!rQ@SRbppfE zWL%AP+QHP}EmZZgZZ17;y~5tzz@)j_;XeJ##PRF(jgsN?VmnIj=|v{&WuaVh$d%X9 z;g6}i4YX*9s7?BSSiZe#P}``5O>2HtMup_p>?D&T@B8s;6zAUx>8}(Hy$m@C8(;Ci z6%yJxDdEn!a|`pO65lNE@8WTD#(ATbzm1+ubH*2WrtuPsCH~?bs~gp5FB4FexVhjn zv|mY|B_94nt_RqCn$6RHjV>p5SREWXdYFjN=U%k0FK~L8E2UapfkOOoJX^_hSx$H9 z7F#}+NewYFxSAJ*2!x8Ox*Y|u}xa3AOTX_ z(GDt`o};XNl8GqNKRgK054;yqE-39?l}x>2>-+O)9w0SESN1th^xct%A<@Z)iPZ83 zbvn)It!}fz$jyee(#5%~f3IFJyoZ)hS7!_Q0@-;B5^?_XZKg^9&#(fk&w0v(cXU!s zet3nY{y8T@#$iw&?Gg?cjcX2fvK1d7$`Rdr$aaDv+JcBS=+j5#e`U@cZ>?LZTwv123gUx+J3!W~7) z_oGEtu{U92DNCJYxv&pFwxs#4LmY9pM?yRj1IamblQPJZcNYNjR zV*4!h-k4js`<<-EweuzEQn<5Cx`5im+|qU#9=FjOCtf_}Tt}pgb`#+w^g!&jxcQBL zECnF!+c@jg$WpJ6U?aoN{W~d61annu!BiG2%7Mzui2_0FPEm@;d?Q>6PjwH3kIzQi z7|87J3~8Z&A}z-9@&uWkH0j*w{64`tSBR+J#XL@fu}5+jUfJxC9ux4)=er8gSn)hd z(bQuuxaqzcXdSmR5x9u57sOD#wLzvIW=;m5!+oP*lMjg13sb1lxBxs^%d!EK;FRtt zr%PPp*TwPk1+s_hC|>u_zoJ{aMgImKnfRW`;Vo(D3g|TNZ9JS5?6)V?$9B-b`Fl%Z zsZNc!J6%K1|MU&HA-r0_ZZRQXy_-z=;)dsm(Mx;K@lBEs#kF=2?&&TtZ4Vb)1FRPY zuVKT$*;`D?oQmCWIvNxFoV0?5H*RzBshsS+ww~!94m6c$srJ4)1nb}eJwrXwn!Hxy z44fmx0K#K(3|^R};$C7zrUUSzr(**pxHK8iG@`t_kBW~ZBHglWoK?sYgl<}sih9t1 z6rpFRsoroIbvEKVGx9CzxnD$DP;00`ak*HW0NTq%`^A=5XR74^fin7fsz7-}Fl3K# z72VH=9(oQuBJ!L8t3)&s{5nTiaZhLF{nj&ocPFYl!cs)#Z0Fu z9iazhDBeOT8py{N6`-V94@x-VDtJ2pvcb{q9#4H!4eA6A1FL475g4W>O2DL)m)*vO z@F)wK3+&K3SLV@~f$m^g!_xwDUNC)wSvRG*=F{*J(yx;-u9-1M)&Y1KUb>v3uB;YJ za7H--wj#g`$%PofWK@tE>lQcb?0BjNd7y|tdjv73*aMG=G#OdIm6(=@gmytctxy*9 z;P09o?*_LuBxqL+ax92&FoP^ZX{S9t4ryf~`Kq8A(_(h(GJ!e14s%ytdM5>Vu7fwi z+CU&zM=XyeWFzVY-Y|dp47Ps}A1SQIst4dRPlF)QYy~-3CZiTjxJPaXjFC=4sNnAa zj8$ewUQtxyy)yn(BA*^Z_*MHsbnG8_^9(e`2&Q2xc1{Cagy5UBt!QpYids5=xIZ*6 zm@XZ4+N@qmiVG7((Plq7Y1hF{?UwAvY`IJ!ce4814Ok(WxYFB2f0kXkYdRm8n)3rs z-N{cR^qVKrW!qrZz-V^2do#Zvw#u4`^}w(<4Gd=^P~VC6g2b&OaoG^GKMZpP8upp6 z$n-a~LyC%`@}FUki5kiBH9&Xzd+Xn}qIC{81253PSZ1>I5E8S_6Ccgz2c9XR3JWhd zG}n&w8>%aC2C?(xT%hP*QfdBXfLO;%wJ6Z;8u9EDicf|_*UV+0 zX2OdNXN9wmN@7d*o4?ANd?8WCC$qP;58yRjl_uUi(O5T^m0BT`u z5I(jkakSG{hhaFhztrlVxGOFGEWm*4nvDlvmebzFc29V2w|N_hKsnfPS5#(vr?S6l zw+Rn)M1z+vs$0Al;e3SQ|qN{sz50KV>n!_tGU zOD(M|Hr+rqED=CfIW~#5O+I$qQE4dhq1wxY)gtbKQomDiu;sjRj4Z4CZYJ=_Jj!tR zpb6d3?s!pkG3edU2I3f%!3XF7#>6|=d_=PFT^fi!(p(o$pSo?hg-4yNExKnputVGY zV0~)17pI1gDS<^Qui797rj*i%57*p$1`s$xlUzmaUEWPzk7G`EXjoUWNpeNF$YS&m4lW^ z#c;%I==-^ffi;DdPCym~fn`hiurODIi zT|JdfEk-f16co%C-$baYcBBDo>fBu35NpcR${8hLTca~*9*|7)1`i223*Q&kBPN25 z=DY(=vZ9c}r!h-jN=zge)g<1-5;elL0E?ix_j?rTN>LK}50ww#Q+aTgwq*?|N6WyM z>&n6izOj?{#SRmUGGFzI!A5Ufu?;OXP{u5UoI1DwUMucD)t5iQk&?;%5}ewF$Eom| zAW9|2liDb<(Y&9phNlsG_Gq=BN=cAi%b&x&zB_>ThpX%!5z>x}e@v9d74okvf0&-o zyB+xO>`|C&okVLr@! zqPK*TSL0a?6E646E&LIicM)=5&iN%0D#p6dYfg-2&iSS_CUx4Gy|@pY%Ujy`>hVix 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

Step 2: build SRS, -Requires Centos6.x/Ubuntu12 32/64bits, others see [Build](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Build) +Requires Centos6.x/Ubuntu12 32/64bits, others see Build( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Build), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Build) +).
 ./configure && make
@@ -161,7 +224,10 @@ cd simple-rtmp-server/trunk
 
See also: -* [Usage: How to delivery RTMP?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleRTMP) +* Usage: How to delivery RTMP?( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleRTMP), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_SampleRTMP) +) * [Usage: How to delivery HLS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleHLS) * [Usage: How to delivery HLS for other codec?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleTranscode2HLS) * [Usage: How to transode RTMP stream by SRS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleFFMPEG) From 81b0f246f58fd954e6d71be8787718aac82c0cf4 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 15 Nov 2014 20:08:19 +0800 Subject: [PATCH 178/800] update readme for wiki --- README.md | 84 ++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 67 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index f0ad2a9433..c632b41f0b 100755 --- a/README.md +++ b/README.md @@ -174,25 +174,31 @@ A big THANK YOU goes to: ## Mirrors -Github: [https://github.com/winlinvip/simple-rtmp-server](https://github.com/winlinvip/simple-rtmp-server) +Github: [https://github.com/winlinvip/simple-rtmp-server](https://github.com/winlinvip/simple-rtmp-server), +the GIT usage( [CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Git), [EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Git) +) ```bash git clone https://github.com/winlinvip/simple-rtmp-server.git ``` -CSDN: [https://code.csdn.net/winlinvip/srs-csdn](https://code.csdn.net/winlinvip/srs-csdn) +CSDN: [https://code.csdn.net/winlinvip/srs-csdn](https://code.csdn.net/winlinvip/srs-csdn) , +the GIT usage( [CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Git), [EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Git) +) ```bash git clone https://code.csdn.net/winlinvip/srs-csdn.git ``` -OSChina: [http://git.oschina.net/winlinvip/srs.oschina](http://git.oschina.net/winlinvip/srs.oschina) +OSChina: [http://git.oschina.net/winlinvip/srs.oschina](http://git.oschina.net/winlinvip/srs.oschina) , +the GIT usage( [CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Git), [EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Git) +) ```bash git clone https://git.oschina.net/winlinvip/srs.oschina.git @@ -228,24 +234,68 @@ cd simple-rtmp-server/trunk [CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleRTMP), [EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_SampleRTMP) ) -* [Usage: How to delivery HLS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleHLS) -* [Usage: How to delivery HLS for other codec?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleTranscode2HLS) -* [Usage: How to transode RTMP stream by SRS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleFFMPEG) -* [Usage: How to forward stream to other server?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleForward) -* [Usage: How to deploy low lantency application?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleRealtime) -* [Usage: How to deploy SRS on ARM?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleARM) -* [Usage: How to ingest file/stream/device to SRS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleIngest) -* [Usage: How to use SRS-HTTP-server to delivery HTTP/HLS stream?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleHTTP) -* [Usage: How to show the demo of SRS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleDemo) -* [Usage: How to publish h.264 raw stream to SRS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_SrsLibrtmp#publish-h264-raw-data) -* [Usage: Solution using SRS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Sample) -* [Usage: Why SRS?](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Product) +* Usage: How to delivery HLS?( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleHLS), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_SampleHLS) +) +* Usage: How to delivery HLS for other codec?( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleTranscode2HLS), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_SampleTranscode2HLS) +) +* Usage: How to transode RTMP stream by SRS?( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleFFMPEG), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_SampleFFMPEG) +) +* Usage: How to forward stream to other server?( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleForward), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_SampleForward) +) +* Usage: How to deploy low lantency application?( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleRealtime), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_SampleRealtime) +) +* Usage: How to deploy SRS on ARM?( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleARM), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_SampleARM) +) +* Usage: How to ingest file/stream/device to SRS?( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleIngest), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_SampleIngest) +) +* Usage: How to use SRS-HTTP-server to delivery HTTP/HLS stream?( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleHTTP), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_SampleHTTP) +) +* Usage: How to show the demo of SRS?( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleDemo), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_SampleDemo) +) +* Usage: How to publish h.264 raw stream to SRS?( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_SrsLibrtmp#publish-h264-raw-data), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_SrsLibrtmp#publish-h264-raw-data) +) +* Usage: Solution using SRS?( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Sample), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Sample) +) +* Usage: Why SRS?( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Product), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Product) +) ## Wiki +SRS 1.0 wiki + +Please select your language: +* [SRS 1.0 English](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Home) +* [SRS 1.0 Chinese](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Home) + +SRS 2.0 wiki + Please select your language: -* [English](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Home) -* [Chinese](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Home) +* [SRS 2.0 English](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_Home) +* [SRS 2.0 Chinese](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_Home) ## Donation From 3247121619304b7a443c18e61a7018b4abd08fac Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 15 Nov 2014 20:09:05 +0800 Subject: [PATCH 179/800] update readme for wiki --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c632b41f0b..edf1b77de0 100755 --- a/README.md +++ b/README.md @@ -314,7 +314,10 @@ Supported operating systems and hardware: ## Summary 1. Simple, also stable enough. -1. [High-performance](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Performance): single-thread, async socket, event/st-thread driven. +1. High-performance( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Performance), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Performance) +): single-thread, async socket, event/st-thread driven. 1. [High-concurrency](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Performance), 1800 connections(500kbps), 900Mbps, CPU 90.2%, 41MB 1. Support [RTMP Origin Server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryRTMP). 1. Support [RTMP Edge Server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Edge) for CDN, push/pull stream from any RTMP server From 68a09d5b5cc2577632242b86b14caea011a22a41 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 15 Nov 2014 20:10:48 +0800 Subject: [PATCH 180/800] update readme for wiki --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index edf1b77de0..5b98fc0ede 100755 --- a/README.md +++ b/README.md @@ -215,8 +215,8 @@ cd simple-rtmp-server/trunk Step 2: build SRS, Requires Centos6.x/Ubuntu12 32/64bits, others see Build( -[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Build), -[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Build) +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_Build), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_Build) ).
@@ -318,7 +318,10 @@ Supported operating systems and hardware:
 [CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Performance),
 [EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Performance)
 ): single-thread, async socket, event/st-thread driven.
-1. [High-concurrency](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Performance), 1800 connections(500kbps), 900Mbps, CPU 90.2%, 41MB
+1. High-concurrency(
+[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Performance),
+[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Performance)
+), 6000+ connections(500kbps), 900Mbps, CPU 90.2%, 41MB
 1. Support [RTMP Origin Server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryRTMP).
 1. Support [RTMP Edge Server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Edge) for CDN, push/pull stream from any RTMP server
 1. Support single process; no multiple processes.

From 3f9e11ea838b3aaf3ab797b6fb859049066ea497 Mon Sep 17 00:00:00 2001
From: winlin 
Date: Sat, 15 Nov 2014 21:13:24 +0800
Subject: [PATCH 181/800] update readme for wiki

---
 README.md | 143 +++++++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 114 insertions(+), 29 deletions(-)

diff --git a/README.md b/README.md
index 5b98fc0ede..edea2faa7a 100755
--- a/README.md
+++ b/README.md
@@ -322,39 +322,124 @@ Supported operating systems and hardware:
 [CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Performance),
 [EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Performance)
 ), 6000+ connections(500kbps), 900Mbps, CPU 90.2%, 41MB
-1. Support [RTMP Origin Server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryRTMP).
-1. Support [RTMP Edge Server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Edge) for CDN, push/pull stream from any RTMP server
+1. Support RTMP Origin Server(
+[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryRTMP),
+[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_DeliveryRTMP)
+)
+1. Support RTMP Edge Server(
+[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Edge),
+[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Edge)
+) for CDN, push/pull stream from any RTMP server
 1. Support single process; no multiple processes.
-1. Support [Vhost](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RtmpUrlVhost), support \_\_defaultVhost\_\_.
-1. Support [RTMP](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryRTMP) live streaming; no vod streaming.
-1. Support Apple [HLS(m3u8)](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryHLS) live streaming.
-1. Support [HLS audio-only](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryHLS#hlsaudioonly) live streaming.
-1. Support [Reload](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Reload) config to enable changes.
-1. Support [cache last gop](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_LowLatency#gop-cache) for flash player to fast startup.
+1. Support Vhost(
+[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RtmpUrlVhost),
+[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_RtmpUrlVhost)
+), support \_\_defaultVhost\_\_.
+1. Support RTMP(
+[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryRTMP),
+[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_DeliveryRTMP)
+) live streaming; no vod streaming.
+1. Support Apple HLS(m3u8)(
+[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryHLS),
+[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_DeliveryHLS)
+) live streaming.
+1. Support HLS audio-only(
+[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryHLS#hlsaudioonly),
+[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_DeliveryHLS#hlsaudioonly)
+) live streaming.
+1. Support Reload(
+[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Reload),
+[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Reload)
+) config to enable changes.
+1. Support cache last gop(
+[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_LowLatency#gop-cache),
+[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_LowLatency#gop-cache)
+) for flash player to fast startup.
 1. Support listen at multiple ports.
 1. Support long time(>4.6hours) publish/play.
-1. Support [Forward](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Forward) in master-slave mode.
-1. Support live stream [Transcoding](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_FFMPEG) by ffmpeg.
-1. Support [ffmpeg](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_FFMPEG) filters(logo/overlay/crop), x264 params, copy/vn/an.
-1. Support audio [transcode](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_FFMPEG) only, speex/mp3 to aac
-1. Support [http callback api hooks](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPCallback)(for authentication and injection).
-1. Support [bandwidth test](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_BandwidthTestTool) api and flash client.
-1. Player, publisher(encoder), and [demo pages(jquery+bootstrap)](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleDemo). 
-1. [Demo](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleDemo) video meeting or chat(SRS+cherrypy+jquery+bootstrap). 
-1. Full documents in [wiki](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Home), in Chineses. 
-1. Support RTMP(play-publish) library: [srs-librtmp](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLibrtmp)
-1. Support ARM([debian armhf, v7cpu](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLinuxArm)) with rtmp/ssl/hls/librtmp.
-1. Support [init.d](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_LinuxService) and packge script, log to file. 
-1. Support [RTMP ATC](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RTMP-ATC) for HLS/HDS to support backup(failover)
-1. Support [HTTP RESTful management api](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPApi).
-1. Support [Ingest](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Ingest) FILE/HTTP/RTMP/RTSP(RTP, SDP) to RTMP using external tools(e.g ffmepg).
-1. Support [DVR](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DVR), record live to flv file for vod.
-1. Support [tracable log, session based log](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLog).
-1. Support DRM [token traverse](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DRM#tokentraverse) for fms origin authenticate.
+1. Support Forward(
+[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Forward),
+[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Forward)
+) in master-slave mode.
+1. Support live stream Transcoding(
+[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_FFMPEG),
+[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_FFMPEG)
+) by ffmpeg.
+1. Support ffmpeg(
+[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_FFMPEG),
+[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_FFMPEG)
+) filters(logo/overlay/crop), x264 params, copy/vn/an.
+1. Support audio transcode(
+[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_FFMPEG),
+[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_FFMPEG)
+) only, speex/mp3 to aac
+1. Support http callback api hooks(
+[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPCallback),
+[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_HTTPCallback)
+)(for authentication and injection).
+1. Support bandwidth test(
+[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_BandwidthTestTool),
+[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_BandwidthTestTool)
+) api and flash client.
+1. Player, publisher(encoder), and demo pages(jquery+bootstrap)(
+[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleDemo),
+[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_SampleDemo)
+). 
+1. Demo(
+[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleDemo),
+[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_SampleDemo)
+) video meeting or chat(SRS+cherrypy+jquery+bootstrap). 
+1. Full documents in wiki(
+[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Home),
+[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Home)
+), both Chinese and English. 
+1. Support RTMP(play-publish) library: srs-librtmp(
+[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_SrsLibrtmp),
+[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_SrsLibrtmp)
+)
+1. Support ARM cpu arch(
+[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLinuxArm),
+[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_SrsLinuxArm)
+) with rtmp/ssl/hls/librtmp.
+1. Support init.d(
+[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_LinuxService),
+[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_LinuxService)
+) and packge script, log to file. 
+1. Support RTMP ATC(
+[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RTMP-ATC),
+[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_RTMP-ATC)
+) for HLS/HDS to support backup(failover)
+1. Support HTTP RESTful management api(
+[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPApi),
+[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_HTTPApi)
+).
+1. Support Ingest(
+[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Ingest),
+[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Ingest)
+) FILE/HTTP/RTMP/RTSP(RTP, SDP) to RTMP using external tools(e.g ffmepg).
+1. Support DVR(
+[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DVR),
+[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_DVR)
+), record live to flv file for vod.
+1. Support tracable log, session based log(
+[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLog),
+[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_SrsLog)
+).
+1. Support DRM token traverse(
+[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DRM#tokentraverse),
+[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_DRM#tokentraverse)
+) for fms origin authenticate.
 1. Support system full utest on gtest.
-1. [experiment] Support embeded [HTTP server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleHTTP) for hls(live/vod)
-1. [experiment] Support [vod stream(http flv/hls vod stream)](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_FlvVodStream).
-1. Stable [1.0release branch](https://github.com/winlinvip/simple-rtmp-server/tree/1.0release) and [2.0dev branch](https://github.com/winlinvip/simple-rtmp-server/tree/master).
+1. [experiment] Support embeded HTTP server(
+[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleHTTP),
+[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_SampleHTTP)
+) for hls(live/vod)
+1. [experiment] Support vod stream(http flv/hls vod stream)(
+[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_FlvVodStream),
+[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_FlvVodStream)
+).
+1. Stable [1.0release branch](https://github.com/winlinvip/simple-rtmp-server/tree/1.0release) and 
+[2.0dev branch](https://github.com/winlinvip/simple-rtmp-server/tree/master).
 1. Support [publish h264 raw stream](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_SrsLibrtmp#publish-h264-raw-data) by srs-librtmp.
 1. Support [6k+ clients](https://github.com/winlinvip/simple-rtmp-server/issues/194), 4Gbps per process.
 1. [dev] Suppport [English wiki](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_v1_ENHome).

From 64a1d975d798b2ee58a3d8a467d61b041a700111 Mon Sep 17 00:00:00 2001
From: winlin 
Date: Sat, 15 Nov 2014 21:16:11 +0800
Subject: [PATCH 182/800] update readme for wiki

---
 README.md | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index edea2faa7a..e0c4a57639 100755
--- a/README.md
+++ b/README.md
@@ -440,9 +440,12 @@ Supported operating systems and hardware:
 ).
 1. Stable [1.0release branch](https://github.com/winlinvip/simple-rtmp-server/tree/1.0release) and 
 [2.0dev branch](https://github.com/winlinvip/simple-rtmp-server/tree/master).
-1. Support [publish h264 raw stream](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_SrsLibrtmp#publish-h264-raw-data) by srs-librtmp.
+1. Support publish h264 raw stream(
+[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_SrsLibrtmp#publish-h264-raw-data),
+[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_SrsLibrtmp#publish-h264-raw-data)
+) by srs-librtmp.
 1. Support [6k+ clients](https://github.com/winlinvip/simple-rtmp-server/issues/194), 4Gbps per process.
-1. [dev] Suppport [English wiki](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_v1_ENHome).
+1. [dev] Suppport [English wiki](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Home).
 1. [dev] Research and simplify st, [bug #182](https://github.com/winlinvip/simple-rtmp-server/issues/182).
 1. [no-plan] Support <500ms latency, FRSC(Fast RTMP-compatible Stream Channel tech).
 1. [no-plan] Support RTMP 302 redirect [#92](https://github.com/winlinvip/simple-rtmp-server/issues/92).

From c3986d9d6db24f5f6552d47cb22d47ecee9b4973 Mon Sep 17 00:00:00 2001
From: winlin 
Date: Sat, 15 Nov 2014 21:25:18 +0800
Subject: [PATCH 183/800] update readme for wiki

---
 README.md | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/README.md b/README.md
index e0c4a57639..263ecd4523 100755
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
 #Simple-RTMP-Server
 
+SRS定位是运营级的互联网直播服务器集群,追求更好的概念完整性和最简单实现的代码。
+
 SRS is industrial-strength live streaming cluster, 
 for the best conceptual integrity and the simplest implementation.
 
@@ -460,6 +462,12 @@ Supported operating systems and hardware:
 
 ## Releases
 * 2014-10-09, [Release v1.0-beta](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.beta), all bug fixed, 1.0.0, 59316 lines.
+* 2014-08-03, [Release v1.0-mainline7](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline7), config utest, all bug fixed. 57432 lines.
+* 2014-07-13, [Release v1.0-mainline6](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline6), core/kernel/rtmp utest, refine bandwidth(as/js/srslibrtmp library). 50029 lines.
+* 2014-06-27, [Release v1.0-mainline5](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline5), refine perf 3k+ clients, edge token traverse, [srs monitor](http://ossrs.net:1977), 30days online. 41573 lines.
+* 2014-05-28, [Release v1.0-mainline4](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline4), support heartbeat, tracable log, fix mem leak and bugs. 39200 lines.
+* 2014-05-18, [Release v1.0-mainline3](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline3), support mips, fms origin, json(http-api). 37594 lines.
+* 2014-04-28, [Release v1.0-mainline2](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline2), support [dvr](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DVR), android, [edge](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Edge). 35255 lines.
* 2014-04-07, [Release v1.0-mainline](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline), support [arm](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SrsLinuxArm), [init.d](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_LinuxService), http [server](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPServer)/[api](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPApi), [ingest](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleIngest). 30000 lines.
* 2013-12-25, [Release v0.9](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.9), support bandwidth test, player/encoder/chat [demos](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleDemo). 20926 lines.
* 2013-12-08, [Release v0.8](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.8), support [http hooks callback](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPCallback), update [st_load](https://github.com/winlinvip/st-load). 19186 lines.
From 0c39a704c27d30d928cf8df1538c3338fe3d4f47 Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 16 Nov 2014 11:29:50 +0800 Subject: [PATCH 184/800] update build srs librtmp single --- trunk/auto/generate-srs-librtmp-single.sh | 9 +++++---- trunk/configure | 2 ++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/trunk/auto/generate-srs-librtmp-single.sh b/trunk/auto/generate-srs-librtmp-single.sh index 0205c4267f..38ec75afb2 100755 --- a/trunk/auto/generate-srs-librtmp-single.sh +++ b/trunk/auto/generate-srs-librtmp-single.sh @@ -93,12 +93,12 @@ SRS_LIBRTMP_OBJS="${LIBS_OBJS[@]}" && build_module_cpp # create example.cpp FILE=$SRS_EXPORT_LIBRTMP_SINGLE/example.c -COMPILE='gcc example.c srs_librtmp.cpp -g -O0 -lstdc++ -o example' +SRS_SINGLE_LIBRTMP_COMPILE='gcc example.c srs_librtmp.cpp -g -O0 -lstdc++ -o example' cat << END >$FILE /** # Example to use srs-librtmp # see: https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_SrsLibrtmp - ${COMPILE} + ${SRS_SINGLE_LIBRTMP_COMPILE} */ #include #include "srs_librtmp.h" @@ -119,9 +119,10 @@ int main(int argc, char** argv) END # compile the example -(cd $SRS_EXPORT_LIBRTMP_SINGLE && `${COMPILE}` && ./example && rm -f example) +(cd $SRS_EXPORT_LIBRTMP_SINGLE && echo "${SRS_SINGLE_LIBRTMP_COMPILE}" && +`${SRS_SINGLE_LIBRTMP_COMPILE}` && ./example && rm -f example) ret=$?; if [[ $ret -ne 0 ]]; then - echo "(cd $SRS_EXPORT_LIBRTMP_SINGLE && ${COMPILE} && ./example && rm -f example)" + echo "(cd $SRS_EXPORT_LIBRTMP_SINGLE && ${SRS_SINGLE_LIBRTMP_COMPILE} && ./example && rm -f example)" echo -e "${RED}failed to compile example.${BLACK}" exit $ret fi diff --git a/trunk/configure b/trunk/configure index a380aba72c..219af140a4 100755 --- a/trunk/configure +++ b/trunk/configure @@ -599,6 +599,8 @@ else echo -e "${GREEN} $SRS_EXPORT_LIBRTMP_PROJECT/srs_librtmp.h ${BLACK}" echo -e "${GREEN} $SRS_EXPORT_LIBRTMP_PROJECT/srs_librtmp.cpp ${BLACK}" echo -e "${GREEN} $SRS_EXPORT_LIBRTMP_PROJECT/example.c ${BLACK}" + echo -e "${GREEN}To compile the example: ${BLACK}" + echo -e "${GREEN} cd $SRS_EXPORT_LIBRTMP_PROJECT && $SRS_SINGLE_LIBRTMP_COMPILE ${BLACK}" # for srs-librtmp project. else echo -e "${GREEN}Please use the srs-librtmp project: ${BLACK}" From ba5b3b92b18b2d3adb89958530bd113e706b77d1 Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 16 Nov 2014 21:08:06 +0800 Subject: [PATCH 185/800] update readme, add qq group. --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 263ecd4523..4b59f8916b 100755 --- a/README.md +++ b/README.md @@ -140,9 +140,7 @@ h.264 raw stream( WebSite: [http://ossrs.net](http://ossrs.net)
Release: [http://winlinvip.github.io/srs.release](http://winlinvip.github.io/srs.release)
Blog: [http://blog.csdn.net/win_lin](http://blog.csdn.net/win_lin)
-CSDN mirror: [https://code.csdn.net/winlinvip/srs-csdn](https://code.csdn.net/winlinvip/srs-csdn)
-See also: [https://github.com/winlinvip/simple-rtmp-server](https://github.com/winlinvip/simple-rtmp-server)
-Github DEMO: [demo with your SRS](http://winlinvip.github.io/srs.release/trunk/research/players/srs_player.html?server=192.168.1.170&vhost=192.168.1.170)
+QQ Group: [365936885, by wenjie](http://jq.qq.com/?_wv=1027&k=SvTiJl)
Wiki: [https://github.com/winlinvip/simple-rtmp-server/wiki](https://github.com/winlinvip/simple-rtmp-server/wiki)
StreamServers:[BLS](https://github.com/wenjiegit/Bull-Live-Server)/[BLE](https://github.com/wenjiegit/Bull-Live-Encoder), [NGINX-RTMP](https://github.com/arut/nginx-rtmp-module), [CRTMPD](http://www.rtmpd.com/), From 520764fba33bd501d290c178d49a12bc28be415a Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 16 Nov 2014 21:11:49 +0800 Subject: [PATCH 186/800] update readme, add qq group. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4b59f8916b..4d3cc4c7ae 100755 --- a/README.md +++ b/README.md @@ -140,7 +140,7 @@ h.264 raw stream( WebSite: [http://ossrs.net](http://ossrs.net)
Release: [http://winlinvip.github.io/srs.release](http://winlinvip.github.io/srs.release)
Blog: [http://blog.csdn.net/win_lin](http://blog.csdn.net/win_lin)
-QQ Group: [365936885, by wenjie](http://jq.qq.com/?_wv=1027&k=SvTiJl)
+QQ Group: 365936885, by wenjie
Wiki: [https://github.com/winlinvip/simple-rtmp-server/wiki](https://github.com/winlinvip/simple-rtmp-server/wiki)
StreamServers:[BLS](https://github.com/wenjiegit/Bull-Live-Server)/[BLE](https://github.com/wenjiegit/Bull-Live-Encoder), [NGINX-RTMP](https://github.com/arut/nginx-rtmp-module), [CRTMPD](http://www.rtmpd.com/), From 9f60a6738abeb98ece5d16548ad47409f3a4da8e Mon Sep 17 00:00:00 2001 From: winlin Date: Mon, 17 Nov 2014 10:27:05 +0800 Subject: [PATCH 187/800] add comments for listen --- trunk/src/app/srs_app_server.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/trunk/src/app/srs_app_server.cpp b/trunk/src/app/srs_app_server.cpp index a7e9ea3379..bb1d29ed58 100644 --- a/trunk/src/app/srs_app_server.cpp +++ b/trunk/src/app/srs_app_server.cpp @@ -130,18 +130,18 @@ int SrsListener::listen(int port) if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { ret = ERROR_SOCKET_CREATE; - srs_error("create linux socket error. ret=%d", ret); + srs_error("create linux socket error. port=%d, ret=%d", port, ret); return ret; } - srs_verbose("create linux socket success. fd=%d", fd); + srs_verbose("create linux socket success. port=%d, fd=%d", port, fd); int reuse_socket = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse_socket, sizeof(int)) == -1) { ret = ERROR_SOCKET_SETREUSE; - srs_error("setsockopt reuse-addr error. ret=%d", ret); + srs_error("setsockopt reuse-addr error. port=%d, ret=%d", port, ret); return ret; } - srs_verbose("setsockopt reuse-addr success. fd=%d", fd); + srs_verbose("setsockopt reuse-addr success. port=%d, fd=%d", port, fd); sockaddr_in addr; addr.sin_family = AF_INET; @@ -149,34 +149,34 @@ int SrsListener::listen(int port) addr.sin_addr.s_addr = INADDR_ANY; if (bind(fd, (const sockaddr*)&addr, sizeof(sockaddr_in)) == -1) { ret = ERROR_SOCKET_BIND; - srs_error("bind socket error. ret=%d", ret); + srs_error("bind socket error. port=%d, ret=%d", port, ret); return ret; } - srs_verbose("bind socket success. fd=%d", fd); + srs_verbose("bind socket success. port=%d, fd=%d", port, fd); if (::listen(fd, SERVER_LISTEN_BACKLOG) == -1) { ret = ERROR_SOCKET_LISTEN; - srs_error("listen socket error. ret=%d", ret); + srs_error("listen socket error. port=%d, ret=%d", port, ret); return ret; } - srs_verbose("listen socket success. fd=%d", fd); + srs_verbose("listen socket success. port=%d, fd=%d", port, fd); if ((stfd = st_netfd_open_socket(fd)) == NULL){ ret = ERROR_ST_OPEN_SOCKET; - srs_error("st_netfd_open_socket open socket failed. ret=%d", ret); + srs_error("st_netfd_open_socket open socket failed. port=%d, ret=%d", port, ret); return ret; } - srs_verbose("st open socket success. fd=%d", fd); + srs_verbose("st open socket success. port=%d, fd=%d", port, fd); if ((ret = pthread->start()) != ERROR_SUCCESS) { - srs_error("st_thread_create listen thread error. ret=%d", ret); + srs_error("st_thread_create listen thread error. port=%d, ret=%d", port, ret); return ret; } - srs_verbose("create st listen thread success."); + srs_verbose("create st listen thread success, port=%d", port); srs_trace("listen thread cid=%d, current_cid=%d, " - "listen at port=%d, type=%d, fd=%d started success", - pthread->cid(), _srs_context->get_id(), _port, _type, fd); + "listen at port=%d, type=%d, fd=%d started success, port=%d", + pthread->cid(), _srs_context->get_id(), _port, _type, fd, port); return ret; } From fbcb63f77596cbf16afe6927d285ebbcab4b6d3c Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 18 Nov 2014 13:40:58 +0800 Subject: [PATCH 188/800] v2.0, 2014-11-18, all wiki translated to English. 2.0.23. --- README.md | 1 + trunk/src/core/srs_core.hpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4d3cc4c7ae..ae7c230c20 100755 --- a/README.md +++ b/README.md @@ -479,6 +479,7 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History +* v2.0, 2014-11-18, all wiki translated to English. 2.0.23. * v2.0, 2014-11-15, fix [#204](https://github.com/winlinvip/simple-rtmp-server/issues/204), srs-librtmp drop duplicated sps/pps(sequence header). 2.0.22. * v2.0, 2014-11-15, fix [#203](https://github.com/winlinvip/simple-rtmp-server/issues/203), srs-librtmp drop any video before sps/pps(sequence header). 2.0.21. * v2.0, 2014-11-15, fix [#202](https://github.com/winlinvip/simple-rtmp-server/issues/202), fix memory leak of h.264 raw packet send in srs-librtmp. 2.0.20. diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index e9c3779573..274a50761e 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 22 +#define VERSION_REVISION 23 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" From 1bf32a15e1d763cd4562db2c628e162fdca4a970 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 18 Nov 2014 16:31:26 +0800 Subject: [PATCH 189/800] refs #1670: update readme, SRS1.0/2.0 supports 500 clients to publish streams. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ae7c230c20..8bcdcd4edc 100755 --- a/README.md +++ b/README.md @@ -692,7 +692,7 @@ Performance benchmark history, on virtual box: * 2014-11-11, SRS 1.0.5, 2700clients, 85%CPU, 66MB. (1.0 equals 2.0.12) * 2014-11-12, SRS 2.0.14, 2700clients, 69%CPU, 59MB. * 2014-11-12, SRS 2.0.14, 3500clients, 95%CPU, 78MB. -* 2014-11-13, SRS 2.0.15, 6000clients, 82%CPU, 203MB. +* 2014-11-13, SRS 2.0.15, 6000clients, 82%CPU, 203MB. (500 publishers). Latest benchmark(2014-07-12): From 749fac414b894f3ac4a9500f021a5b5ffd7f01d0 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 18 Nov 2014 17:30:40 +0800 Subject: [PATCH 190/800] refine code, donot response call when transaction id is zero. --- trunk/src/app/srs_app_rtmp_conn.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index 418236a896..165fa0eeee 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -922,7 +922,9 @@ int SrsRtmpConn::process_play_control_msg(SrsConsumer* consumer, SrsMessage* msg // @see https://github.com/winlinvip/simple-rtmp-server/issues/106 // TODO: FIXME: response in right way, or forward in edge mode. SrsCallPacket* call = dynamic_cast(pkt); - if (call) { + // only response it when transaction id not zero, + // for the zero means donot need response. + if (call && call->transaction_id > 0) { SrsCallResPacket* res = new SrsCallResPacket(call->transaction_id); res->command_object = SrsAmf0Any::null(); res->response = SrsAmf0Any::null(); From 89110d9748d5bb3fe9e60d020c255249420b4dac Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 18 Nov 2014 17:52:41 +0800 Subject: [PATCH 191/800] add report bug url. update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8bcdcd4edc..8d1603026e 100755 --- a/README.md +++ b/README.md @@ -137,6 +137,7 @@ h.264 raw stream( [EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_SrsLibrtmp#publish-h264-raw-data) ). +Report Bug: [https://github.com/winlinvip/simple-rtmp-server/issues/new](https://github.com/winlinvip/simple-rtmp-server/issues/new)
WebSite: [http://ossrs.net](http://ossrs.net)
Release: [http://winlinvip.github.io/srs.release](http://winlinvip.github.io/srs.release)
Blog: [http://blog.csdn.net/win_lin](http://blog.csdn.net/win_lin)
From df35f75df1b1fa2c1ffedb24f637a6ef1430baa7 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 18 Nov 2014 17:55:38 +0800 Subject: [PATCH 192/800] refine response call, 2.0.24 --- trunk/src/app/srs_app_rtmp_conn.cpp | 20 +++++++++++--------- trunk/src/core/srs_core.hpp | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index 165fa0eeee..8b6304fa7b 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -922,15 +922,17 @@ int SrsRtmpConn::process_play_control_msg(SrsConsumer* consumer, SrsMessage* msg // @see https://github.com/winlinvip/simple-rtmp-server/issues/106 // TODO: FIXME: response in right way, or forward in edge mode. SrsCallPacket* call = dynamic_cast(pkt); - // only response it when transaction id not zero, - // for the zero means donot need response. - if (call && call->transaction_id > 0) { - SrsCallResPacket* res = new SrsCallResPacket(call->transaction_id); - res->command_object = SrsAmf0Any::null(); - res->response = SrsAmf0Any::null(); - if ((ret = rtmp->send_and_free_packet(res, 0)) != ERROR_SUCCESS) { - srs_warn("response call failed. ret=%d", ret); - return ret; + if (call) { + // only response it when transaction id not zero, + // for the zero means donot need response. + if (call->transaction_id > 0) { + SrsCallResPacket* res = new SrsCallResPacket(call->transaction_id); + res->command_object = SrsAmf0Any::null(); + res->response = SrsAmf0Any::null(); + if ((ret = rtmp->send_and_free_packet(res, 0)) != ERROR_SUCCESS) { + srs_warn("response call failed. ret=%d", ret); + return ret; + } } return ret; } diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 274a50761e..7f454aaa72 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 23 +#define VERSION_REVISION 24 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" From 5f48d4f566a9696fd303764da330aedb9784c55b Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 19 Nov 2014 10:44:50 +0800 Subject: [PATCH 193/800] refine comments for SrsMessageArray. 2.0.25 --- trunk/src/app/srs_app_edge.cpp | 3 ++- trunk/src/app/srs_app_forward.cpp | 3 ++- trunk/src/app/srs_app_rtmp_conn.cpp | 5 ++--- trunk/src/core/srs_core.hpp | 2 +- trunk/src/rtmp/srs_protocol_msg_array.cpp | 3 +++ 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/trunk/src/app/srs_app_edge.cpp b/trunk/src/app/srs_app_edge.cpp index 52f020ecb9..426bbbe4e0 100644 --- a/trunk/src/app/srs_app_edge.cpp +++ b/trunk/src/app/srs_app_edge.cpp @@ -498,6 +498,7 @@ int SrsEdgeForwarder::cycle() } // forward all messages. + // each msg in msgs.msgs must be free, for the SrsMessageArray never free them. int count = 0; if ((ret = queue->dump_packets(msgs.max, msgs.msgs, count)) != ERROR_SUCCESS) { srs_error("get message to push to origin failed. ret=%d", ret); @@ -522,7 +523,7 @@ int SrsEdgeForwarder::cycle() continue; } - // all msgs to forward to origin. + // sendout messages, all messages are freed by send_and_free_messages(). if ((ret = client->send_and_free_messages(msgs.msgs, count, stream_id)) != ERROR_SUCCESS) { srs_error("edge publish push message to server failed. ret=%d", ret); return ret; diff --git a/trunk/src/app/srs_app_forward.cpp b/trunk/src/app/srs_app_forward.cpp index 9c32600cb4..6afd57a646 100644 --- a/trunk/src/app/srs_app_forward.cpp +++ b/trunk/src/app/srs_app_forward.cpp @@ -416,6 +416,7 @@ int SrsForwarder::forward() } // forward all messages. + // each msg in msgs.msgs must be free, for the SrsMessageArray never free them. int count = 0; if ((ret = queue->dump_packets(msgs.max, msgs.msgs, count)) != ERROR_SUCCESS) { srs_error("get message to forward failed. ret=%d", ret); @@ -438,7 +439,7 @@ int SrsForwarder::forward() continue; } - // all msgs to forward. + // sendout messages, all messages are freed by send_and_free_messages(). if ((ret = client->send_and_free_messages(msgs.msgs, count, stream_id)) != ERROR_SUCCESS) { srs_error("forwarder messages to server failed. ret=%d", ret); return ret; diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index 8b6304fa7b..f2986c52eb 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -558,6 +558,7 @@ int SrsRtmpConn::playing(SrsSource* source) pithy_print.elapse(); // get messages from consumer. + // each msg in msgs.msgs must be free, for the SrsMessageArray never free them. int count = 0; if ((ret = consumer->dump_packets(msgs.max, msgs.msgs, count)) != ERROR_SUCCESS) { srs_error("get messages from consumer failed. ret=%d", ret); @@ -591,9 +592,7 @@ int SrsRtmpConn::playing(SrsSource* source) } } - // sendout messages - // @remark, becareful, all msgs must be free explicitly, - // free by send_and_free_message or srs_freep. + // sendout messages, all messages are freed by send_and_free_messages(). if (count > 0) { // no need to assert msg, for the rtmp will assert it. if ((ret = rtmp->send_and_free_messages(msgs.msgs, count, res->stream_id)) != ERROR_SUCCESS) { diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 7f454aaa72..506c6f025e 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 24 +#define VERSION_REVISION 25 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/rtmp/srs_protocol_msg_array.cpp b/trunk/src/rtmp/srs_protocol_msg_array.cpp index c38569777a..4c002bcf84 100644 --- a/trunk/src/rtmp/srs_protocol_msg_array.cpp +++ b/trunk/src/rtmp/srs_protocol_msg_array.cpp @@ -40,6 +40,9 @@ SrsMessageArray::SrsMessageArray(int max_msgs) SrsMessageArray::~SrsMessageArray() { + // we just free the msgs itself, + // both delete and delete[] is ok, + // for each msg in msgs is already freed by send_and_free_messages. srs_freep(msgs); } From d6072b16c07346f6ad8c8a91e16c7b6c8fbf6934 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 19 Nov 2014 16:16:04 +0800 Subject: [PATCH 194/800] support compile srs-librtmp on windows. 2.0.26 --- README.md | 7 +- trunk/auto/generate-srs-librtmp-single.sh | 4 +- trunk/src/core/srs_core.hpp | 6 +- trunk/src/kernel/srs_kernel_file.cpp | 12 +- trunk/src/kernel/srs_kernel_flv.cpp | 10 +- trunk/src/kernel/srs_kernel_utility.cpp | 6 +- trunk/src/libs/srs_lib_bandwidth.cpp | 16 +- trunk/src/libs/srs_lib_simple_socket.cpp | 14 +- trunk/src/libs/srs_librtmp.cpp | 359 +++++++++++++++++++++- trunk/src/libs/srs_librtmp.hpp | 42 +++ trunk/src/rtmp/srs_protocol_io.hpp | 3 + trunk/src/rtmp/srs_protocol_rtmp.cpp | 10 +- trunk/src/rtmp/srs_protocol_stack.cpp | 8 +- trunk/src/rtmp/srs_protocol_stack.hpp | 4 + 14 files changed, 468 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 8d1603026e..c7d5326887 100755 --- a/README.md +++ b/README.md @@ -446,8 +446,10 @@ Supported operating systems and hardware: [EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_SrsLibrtmp#publish-h264-raw-data) ) by srs-librtmp. 1. Support [6k+ clients](https://github.com/winlinvip/simple-rtmp-server/issues/194), 4Gbps per process. -1. [dev] Suppport [English wiki](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Home). -1. [dev] Research and simplify st, [bug #182](https://github.com/winlinvip/simple-rtmp-server/issues/182). +1. Suppport [English wiki](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Home). +1. Research and simplify st, [bug #182](https://github.com/winlinvip/simple-rtmp-server/issues/182). +1. Support compile [srs-librtmp on windows](https://github.com/winlinvip/srs.librtmp), +[bug #213](https://github.com/winlinvip/simple-rtmp-server/issues/213). 1. [no-plan] Support <500ms latency, FRSC(Fast RTMP-compatible Stream Channel tech). 1. [no-plan] Support RTMP 302 redirect [#92](https://github.com/winlinvip/simple-rtmp-server/issues/92). 1. [no-plan] Support multiple processes, for both origin and edge @@ -480,6 +482,7 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History +* v2.0, 2014-11-19, fix [#213](https://github.com/winlinvip/simple-rtmp-server/issues/213), support compile [srs-librtmp on windows](https://github.com/winlinvip/srs.librtmp), [bug #213](https://github.com/winlinvip/simple-rtmp-server/issues/213). 2.0.26 * v2.0, 2014-11-18, all wiki translated to English. 2.0.23. * v2.0, 2014-11-15, fix [#204](https://github.com/winlinvip/simple-rtmp-server/issues/204), srs-librtmp drop duplicated sps/pps(sequence header). 2.0.22. * v2.0, 2014-11-15, fix [#203](https://github.com/winlinvip/simple-rtmp-server/issues/203), srs-librtmp drop any video before sps/pps(sequence header). 2.0.21. diff --git a/trunk/auto/generate-srs-librtmp-single.sh b/trunk/auto/generate-srs-librtmp-single.sh index 38ec75afb2..00035946fc 100755 --- a/trunk/auto/generate-srs-librtmp-single.sh +++ b/trunk/auto/generate-srs-librtmp-single.sh @@ -105,11 +105,13 @@ cat << END >$FILE int main(int argc, char** argv) { + srs_rtmp_t rtmp; + printf("Example for srs-librtmp\n"); printf("SRS(simple-rtmp-server) client librtmp library.\n"); printf("version: %d.%d.%d\n", srs_version_major(), srs_version_minor(), srs_version_revision()); - srs_rtmp_t rtmp = srs_rtmp_create("rtmp://ossrs.net/live/livestream"); + rtmp = srs_rtmp_create("rtmp://ossrs.net/live/livestream"); srs_lib_trace("create rtmp success"); srs_rtmp_destroy(rtmp); diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 506c6f025e..390e5427f5 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 25 +#define VERSION_REVISION 26 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" @@ -70,7 +70,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS #endif + +// for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 +#ifndef _WIN32 #include +#endif #include #define srs_assert(expression) assert(expression) diff --git a/trunk/src/kernel/srs_kernel_file.cpp b/trunk/src/kernel/srs_kernel_file.cpp index 17f92586fe..02079d3c54 100644 --- a/trunk/src/kernel/srs_kernel_file.cpp +++ b/trunk/src/kernel/srs_kernel_file.cpp @@ -23,8 +23,12 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include -#include +// for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 +#ifndef _WIN32 #include +#endif + +#include #include using namespace std; @@ -173,19 +177,19 @@ int64_t SrsFileReader::tellg() void SrsFileReader::skip(int64_t size) { - ::lseek(fd, size, SEEK_CUR); + ::lseek(fd, (off_t)size, SEEK_CUR); } int64_t SrsFileReader::lseek(int64_t offset) { - return (int64_t)::lseek(fd, offset, SEEK_SET); + return (int64_t)::lseek(fd, (off_t)offset, SEEK_SET); } int64_t SrsFileReader::filesize() { int64_t cur = tellg(); int64_t size = (int64_t)::lseek(fd, 0, SEEK_END); - ::lseek(fd, cur, SEEK_SET); + ::lseek(fd, (off_t)cur, SEEK_SET); return size; } diff --git a/trunk/src/kernel/srs_kernel_flv.cpp b/trunk/src/kernel/srs_kernel_flv.cpp index c2b93461e9..4de67ed645 100644 --- a/trunk/src/kernel/srs_kernel_flv.cpp +++ b/trunk/src/kernel/srs_kernel_flv.cpp @@ -23,8 +23,12 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include -#include +// for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 +#ifndef _WIN32 #include +#endif + +#include #include using namespace std; @@ -157,7 +161,7 @@ int SrsFlvEncoder::write_audio(int64_t timestamp, char* data, int size) return ret; } tag_stream->write_3bytes(size); - tag_stream->write_3bytes(timestamp); + tag_stream->write_3bytes((int32_t)timestamp); // default to little-endian tag_stream->write_1bytes((timestamp >> 24) & 0xFF); @@ -191,7 +195,7 @@ int SrsFlvEncoder::write_video(int64_t timestamp, char* data, int size) return ret; } tag_stream->write_3bytes(size); - tag_stream->write_3bytes(timestamp); + tag_stream->write_3bytes((int32_t)timestamp); // default to little-endian tag_stream->write_1bytes((timestamp >> 24) & 0xFF); diff --git a/trunk/src/kernel/srs_kernel_utility.cpp b/trunk/src/kernel/srs_kernel_utility.cpp index 1bbfbf516f..d51e594728 100644 --- a/trunk/src/kernel/srs_kernel_utility.cpp +++ b/trunk/src/kernel/srs_kernel_utility.cpp @@ -23,11 +23,15 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include +// for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 +#ifndef _WIN32 #include -#include #include #include #include +#endif + +#include using namespace std; diff --git a/trunk/src/libs/srs_lib_bandwidth.cpp b/trunk/src/libs/srs_lib_bandwidth.cpp index 88c32c37dd..c3816293d0 100644 --- a/trunk/src/libs/srs_lib_bandwidth.cpp +++ b/trunk/src/libs/srs_lib_bandwidth.cpp @@ -23,7 +23,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include +// for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 +#ifndef _WIN32 #include +#endif #include using namespace std; @@ -293,11 +296,6 @@ int SrsBandwidthClient::publish_checking(int duration_ms, int play_kbps) return ret; } - // send play data to client - int size = 1024; // TODO: FIXME: magic number - char random_data[size]; - memset(random_data, 'A', size); - int data_count = 1; srs_update_system_time_ms(); int64_t starttime = srs_get_system_time_ms(); @@ -321,13 +319,13 @@ int SrsBandwidthClient::publish_checking(int duration_ms, int play_kbps) // use the play kbps to control the publish srs_update_system_time_ms(); - int elaps = srs_get_system_time_ms() - starttime; + int elaps = (int)(srs_get_system_time_ms() - starttime); if (elaps > 0) { - int current_kbps = _rtmp->get_send_bytes() * 8 / elaps; + int current_kbps = (int)(_rtmp->get_send_bytes() * 8 / elaps); while (current_kbps > play_kbps) { srs_update_system_time_ms(); - elaps = srs_get_system_time_ms() - starttime; - current_kbps = _rtmp->get_send_bytes() * 8 / elaps; + elaps = (int)(srs_get_system_time_ms() - starttime); + current_kbps = (int)(_rtmp->get_send_bytes() * 8 / elaps); usleep(100 * 1000); // TODO: FIXME: magic number. } } diff --git a/trunk/src/libs/srs_lib_simple_socket.cpp b/trunk/src/libs/srs_lib_simple_socket.cpp index cc84767787..e7346b6a06 100644 --- a/trunk/src/libs/srs_lib_simple_socket.cpp +++ b/trunk/src/libs/srs_lib_simple_socket.cpp @@ -25,13 +25,17 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include +// for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 +#ifndef _WIN32 #include -#include #include #include #include -#include #include +#endif + +#include +#include #include @@ -82,7 +86,7 @@ int SimpleSocketStream::read(void* buf, size_t size, ssize_t* nread) { int ret = ERROR_SUCCESS; - ssize_t nb_read = ::recv(fd, buf, size, 0); + ssize_t nb_read = ::recv(fd, (char*)buf, size, 0); if (nread) { *nread = nb_read; @@ -188,7 +192,7 @@ int SimpleSocketStream::read_fully(void* buf, size_t size, ssize_t* nread) } nb_read += this_nread; - left -= this_nread; + left -= (size_t)this_nread; } if (nread) { @@ -203,7 +207,7 @@ int SimpleSocketStream::write(void* buf, size_t size, ssize_t* nwrite) { int ret = ERROR_SUCCESS; - ssize_t nb_write = ::send(fd, (void*)buf, size, 0); + ssize_t nb_write = ::send(fd, (char*)buf, size, 0); if (nwrite) { *nwrite = nb_write; diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index e2d3f80da2..f2be538f4e 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -24,7 +24,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include + +// for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 +#ifndef _WIN32 #include +#endif #include #include @@ -98,6 +102,356 @@ struct Context } }; +// for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 +#ifdef _WIN32 + int gettimeofday(struct timeval* tv, struct timezone* tz) + { + time_t clock; + struct tm tm; + SYSTEMTIME win_time; + + GetLocalTime(&win_time); + + tm.tm_year = win_time.wYear - 1900; + tm.tm_mon = win_time.wMonth - 1; + tm.tm_mday = win_time.wDay; + tm.tm_hour = win_time.wHour; + tm.tm_min = win_time.wMinute; + tm.tm_sec = win_time.wSecond; + tm.tm_isdst = -1; + + clock = mktime(&tm); + + tv->tv_sec = (long)clock; + tv->tv_usec = win_time.wMilliseconds * 1000; + + return 0; + } + + int open(const char *pathname, int flags) + { + return open(pathname, flags, 0); + } + + int open(const char *pathname, int flags, mode_t mode) + { + FILE* file = NULL; + + if ((flags & O_RDONLY) == O_RDONLY) { + file = fopen(pathname, "r"); + } else { + file = fopen(pathname, "w+"); + } + + if (file == NULL) { + return -1; + } + + return (int)file; + } + + int close(int fd) + { + FILE* file = (FILE*)fd; + return fclose(file); + } + + off_t lseek(int fd, off_t offset, int whence) + { + return (off_t)fseek((FILE*)fd, offset, whence); + } + + ssize_t write(int fd, const void *buf, size_t count) + { + return (ssize_t)fwrite(buf, count, 1, (FILE*)fd); + } + + ssize_t read(int fd, void *buf, size_t count) + { + return (ssize_t)fread(buf, count, 1, (FILE*)fd); + } + + pid_t getpid(void) + { + return (pid_t)GetCurrentProcessId(); + } + + int usleep(useconds_t usec) + { + Sleep((DWORD)(usec / 1000)); + return 0; + } + + ssize_t writev(int fd, const struct iovec *iov, int iovcnt) + { + ssize_t nwrite = 0; + for (int i = 0; i < iovcnt; i++) { + const struct iovec* current = iov + i; + + int nsent = ::send(fd, (char*)current->iov_base, current->iov_len, 0); + if (nsent < 0) { + return nsent; + } + + nwrite += nsent; + if (nsent == 0) { + return nwrite; + } + } + return nwrite; + } + + //////////////////////// strlcpy.c (modified) ////////////////////////// + + /* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */ + + /*- + * Copyright (c) 1998 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + //#include // **** + //#include // **** + // __FBSDID("$FreeBSD: stable/9/sys/libkern/strlcpy.c 243811 2012-12-03 18:08:44Z delphij $"); // **** + + // #include // **** + // #include // **** + + /* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ + + //#define __restrict // **** + + std::size_t strlcpy(char * __restrict dst, const char * __restrict src, size_t siz) + { + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0) { + while (--n != 0) { + if ((*d++ = *s++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ + } + + // http://www.cplusplus.com/forum/general/141779///////////////////////// inet_ntop.c (modified) ////////////////////////// + /* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + // #if defined(LIBC_SCCS) && !defined(lint) // **** + //static const char rcsid[] = "$Id: inet_ntop.c,v 1.3.18.2 2005/11/03 23:02:22 marka Exp $"; + // #endif /* LIBC_SCCS and not lint */ // **** + // #include // **** + // __FBSDID("$FreeBSD: stable/9/sys/libkern/inet_ntop.c 213103 2010-09-24 15:01:45Z attilio $"); // **** + + //#define _WIN32_WINNT _WIN32_WINNT_WIN8 // **** + //#include // **** + #pragma comment(lib, "Ws2_32.lib") // **** + //#include // **** + + // #include // **** + // #include // **** + // #include // **** + + // #include // **** + + /*% + * WARNING: Don't even consider trying to compile this on a system where + * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. + */ + + static char *inet_ntop4(const u_char *src, char *dst, socklen_t size); + static char *inet_ntop6(const u_char *src, char *dst, socklen_t size); + + /* char * + * inet_ntop(af, src, dst, size) + * convert a network format address to presentation format. + * return: + * pointer to presentation format address (`dst'), or NULL (see errno). + * author: + * Paul Vixie, 1996. + */ + const char* inet_ntop(int af, const void *src, char *dst, socklen_t size) + { + switch (af) { + case AF_INET: + return (inet_ntop4( (unsigned char*)src, (char*)dst, size)); // **** + #ifdef AF_INET6 + #error "IPv6 not supported" + //case AF_INET6: + // return (char*)(inet_ntop6( (unsigned char*)src, (char*)dst, size)); // **** + #endif + default: + // return (NULL); // **** + return 0 ; // **** + } + /* NOTREACHED */ + } + + /* const char * + * inet_ntop4(src, dst, size) + * format an IPv4 address + * return: + * `dst' (as a const) + * notes: + * (1) uses no statics + * (2) takes a u_char* not an in_addr as input + * author: + * Paul Vixie, 1996. + */ + static char * inet_ntop4(const u_char *src, char *dst, socklen_t size) + { + static const char fmt[128] = "%u.%u.%u.%u"; + char tmp[sizeof "255.255.255.255"]; + int l; + + l = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]); // **** + if (l <= 0 || (socklen_t) l >= size) { + return (NULL); + } + strlcpy(dst, tmp, size); + return (dst); + } + + /* const char * + * inet_ntop6(src, dst, size) + * convert IPv6 binary address into presentation (printable) format + * author: + * Paul Vixie, 1996. + */ + static char * inet_ntop6(const u_char *src, char *dst, socklen_t size) + { + /* + * Note that int32_t and int16_t need only be "at least" large enough + * to contain a value of the specified size. On some systems, like + * Crays, there is no such thing as an integer variable with 16 bits. + * Keep this in mind if you think this function should have been coded + * to use pointer overlays. All the world's not a VAX. + */ + char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp; + struct { int base, len; } best, cur; + #define NS_IN6ADDRSZ 16 + #define NS_INT16SZ 2 + u_int words[NS_IN6ADDRSZ / NS_INT16SZ]; + int i; + + /* + * Preprocess: + * Copy the input (bytewise) array into a wordwise array. + * Find the longest run of 0x00's in src[] for :: shorthanding. + */ + memset(words, '\0', sizeof words); + for (i = 0; i < NS_IN6ADDRSZ; i++) + words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); + best.base = -1; + best.len = 0; + cur.base = -1; + cur.len = 0; + for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { + if (words[i] == 0) { + if (cur.base == -1) + cur.base = i, cur.len = 1; + else + cur.len++; + } else { + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + cur.base = -1; + } + } + } + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + } + if (best.base != -1 && best.len < 2) + best.base = -1; + + /* + * Format the result. + */ + tp = tmp; + for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { + /* Are we inside the best run of 0x00's? */ + if (best.base != -1 && i >= best.base && + i < (best.base + best.len)) { + if (i == best.base) + *tp++ = ':'; + continue; + } + /* Are we following an initial run of 0x00s or any real hex? */ + if (i != 0) + *tp++ = ':'; + /* Is this address an encapsulated IPv4? */ + if (i == 6 && best.base == 0 && (best.len == 6 || + (best.len == 7 && words[7] != 0x0001) || + (best.len == 5 && words[5] == 0xffff))) { + if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp))) + return (NULL); + tp += strlen(tp); + break; + } + tp += std::sprintf(tp, "%x", words[i]); // **** + } + /* Was it a trailing run of 0x00's? */ + if (best.base != -1 && (best.base + best.len) == + (NS_IN6ADDRSZ / NS_INT16SZ)) + *tp++ = ':'; + *tp++ = '\0'; + + /* + * Check for overflow, copy, and we're done. + */ + if ((socklen_t)(tp - tmp) > size) { + return (NULL); + } + strcpy(dst, tmp); + return (dst); + } +#endif + int srs_librtmp_context_parse_uri(Context* context) { int ret = ERROR_SUCCESS; @@ -596,7 +950,7 @@ const char* srs_format_time() // to calendar time struct tm* tm; - if ((tm = localtime(&tv.tv_sec)) == NULL) { + if ((tm = localtime((const time_t*)&tv.tv_sec)) == NULL) { return buf; } @@ -605,6 +959,9 @@ const char* srs_format_time() 1900 + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, (int)(tv.tv_usec / 1000)); + + // for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 + buf[sizeof(buf) - 1] = 0; return buf; } diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index a72c270c1d..8a9d148c97 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -30,6 +30,48 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include +// for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 +#ifdef _WIN32 + #define _CRT_SECURE_NO_WARNINGS + typedef unsigned long long u_int64_t; + typedef long long int64_t; + typedef unsigned int u_int32_t; + typedef int int32_t; + typedef unsigned char u_int8_t; + typedef char int8_t; + typedef unsigned short u_int16_t; + typedef short int16_t; + typedef int64_t ssize_t; + struct iovec { + void *iov_base; /* Starting address */ + size_t iov_len; /* Number of bytes to transfer */ + }; + #include + #include + int gettimeofday(struct timeval* tv, struct timezone* tz); + #define PRId64 "lld" + typedef int socklen_t; + const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); + typedef int mode_t; + #define S_IRUSR 0 + #define S_IWUSR 0 + #define S_IRGRP 0 + #define S_IWGRP 0 + #define S_IROTH 0 + int open(const char *pathname, int flags); + int open(const char *pathname, int flags, mode_t mode); + int close(int fd); + off_t lseek(int fd, off_t offset, int whence); + ssize_t write(int fd, const void *buf, size_t count); + ssize_t read(int fd, void *buf, size_t count); + typedef int pid_t; + pid_t getpid(void); + #define snprintf _snprintf + ssize_t writev(int fd, const struct iovec *iov, int iovcnt); + typedef int64_t useconds_t; + int usleep(useconds_t usec); +#endif + /** * srs-librtmp is a librtmp like library, * used to play/publish rtmp stream from/to rtmp server. diff --git a/trunk/src/rtmp/srs_protocol_io.hpp b/trunk/src/rtmp/srs_protocol_io.hpp index 9112f70876..bc816309f4 100644 --- a/trunk/src/rtmp/srs_protocol_io.hpp +++ b/trunk/src/rtmp/srs_protocol_io.hpp @@ -30,7 +30,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include +// for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 +#ifndef _WIN32 #include +#endif #include diff --git a/trunk/src/rtmp/srs_protocol_rtmp.cpp b/trunk/src/rtmp/srs_protocol_rtmp.cpp index 9dec1f7a1b..1049de187c 100644 --- a/trunk/src/rtmp/srs_protocol_rtmp.cpp +++ b/trunk/src/rtmp/srs_protocol_rtmp.cpp @@ -31,7 +31,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +// for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 +#ifndef _WIN32 #include +#endif + using namespace std; /** @@ -260,7 +264,7 @@ int SrsHandshakeBytes::create_c0c1() return ret; } stream.write_1bytes(0x03); - stream.write_4bytes(::time(NULL)); + stream.write_4bytes((int32_t)::time(NULL)); stream.write_4bytes(0x00); return ret; @@ -283,7 +287,7 @@ int SrsHandshakeBytes::create_s0s1s2(const char* c1) return ret; } stream.write_1bytes(0x03); - stream.write_4bytes(::time(NULL)); + stream.write_4bytes((int32_t)::time(NULL)); // s2 time2 copy from c1 if (c0c1) { stream.write_bytes(c0c1 + 1, 4); @@ -314,7 +318,7 @@ int SrsHandshakeBytes::create_c2() if ((ret = stream.initialize(c2, 8)) != ERROR_SUCCESS) { return ret; } - stream.write_4bytes(::time(NULL)); + stream.write_4bytes((int32_t)::time(NULL)); // c2 time2 copy from s1 if (s0s1s2) { stream.write_bytes(s0s1s2 + 1, 4); diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp index 2e41e26753..ef8487983b 100644 --- a/trunk/src/rtmp/srs_protocol_stack.cpp +++ b/trunk/src/rtmp/srs_protocol_stack.cpp @@ -397,7 +397,8 @@ SrsMessage::~SrsMessage() SrsProtocol::AckWindowSize::AckWindowSize() { - ack_window_size = acked_size = 0; + ack_window_size = 0; + acked_size = 0; } SrsProtocol::SrsProtocol(ISrsProtocolReaderWriter* io) @@ -1427,7 +1428,7 @@ int SrsProtocol::read_message_header(SrsChunkStream* chunk, char fmt, int bh_siz * @remark, srs always send the extended-timestamp, to keep simple, * and compatible with adobe products. */ - u_int32_t chunk_timestamp = chunk->header.timestamp; + u_int32_t chunk_timestamp = (u_int32_t)chunk->header.timestamp; /** * if chunk_timestamp<=0, the chunk previous packet has no extended-timestamp, @@ -1695,7 +1696,8 @@ int SrsProtocol::response_acknowledgement_message() int ret = ERROR_SUCCESS; SrsAcknowledgementPacket* pkt = new SrsAcknowledgementPacket(); - in_ack_size.acked_size = pkt->sequence_number = skt->get_recv_bytes(); + in_ack_size.acked_size = skt->get_recv_bytes(); + pkt->sequence_number = (int32_t)in_ack_size.acked_size; if ((ret = send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { srs_error("send acknowledgement failed. ret=%d", ret); return ret; diff --git a/trunk/src/rtmp/srs_protocol_stack.hpp b/trunk/src/rtmp/srs_protocol_stack.hpp index 27f671151b..925391fdf8 100644 --- a/trunk/src/rtmp/srs_protocol_stack.hpp +++ b/trunk/src/rtmp/srs_protocol_stack.hpp @@ -32,7 +32,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include + +// for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 +#ifndef _WIN32 #include +#endif #include #include From 856cc0a51d5bdedbf04857b658603a2b5e93c06c Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 20 Nov 2014 10:45:03 +0800 Subject: [PATCH 195/800] update donations. --- DONATIONS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/DONATIONS.txt b/DONATIONS.txt index 6a350d929a..3ccfc41723 100644 --- a/DONATIONS.txt +++ b/DONATIONS.txt @@ -14,4 +14,5 @@ Donations ordered by first donation. * [2014-08-19 20:00] Matthew Matthew(1206651693) * [2014-08-20 20:13] 林瑞潮 甲子(459505921) * [2014-09-05 16:13] 于冰 秋雨☆ice(3373749) +* [2014-11-19 22:38] 夏江龙 From a058eeeb2063d39fa95c88874dbc602a4b7c1fb8 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 20 Nov 2014 13:36:14 +0800 Subject: [PATCH 196/800] fix #212, support publish audio raw frames. 2.0.27 --- README.md | 1 + trunk/research/librtmp/Makefile | 7 +- .../research/librtmp/srs_audio_raw_publish.c | 190 ++++++++++++++++++ trunk/src/core/srs_core.hpp | 2 +- trunk/src/libs/srs_librtmp.cpp | 44 +++- trunk/src/libs/srs_librtmp.hpp | 60 +++++- trunk/src/srs/srs.upp | 1 + 7 files changed, 301 insertions(+), 4 deletions(-) create mode 100644 trunk/research/librtmp/srs_audio_raw_publish.c diff --git a/README.md b/README.md index c7d5326887..25fd070333 100755 --- a/README.md +++ b/README.md @@ -482,6 +482,7 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History +* v2.0, 2014-11-20, fix [#212](https://github.com/winlinvip/simple-rtmp-server/issues/212), support publish audio raw frames. 2.0.27 * v2.0, 2014-11-19, fix [#213](https://github.com/winlinvip/simple-rtmp-server/issues/213), support compile [srs-librtmp on windows](https://github.com/winlinvip/srs.librtmp), [bug #213](https://github.com/winlinvip/simple-rtmp-server/issues/213). 2.0.26 * v2.0, 2014-11-18, all wiki translated to English. 2.0.23. * v2.0, 2014-11-15, fix [#204](https://github.com/winlinvip/simple-rtmp-server/issues/204), srs-librtmp drop duplicated sps/pps(sequence header). 2.0.22. diff --git a/trunk/research/librtmp/Makefile b/trunk/research/librtmp/Makefile index acc2cb3d1d..d5f19606e1 100755 --- a/trunk/research/librtmp/Makefile +++ b/trunk/research/librtmp/Makefile @@ -6,7 +6,8 @@ else ST_ALL = objs/srs_flv_parser \ objs/srs_flv_injecter objs/srs_publish objs/srs_play \ objs/srs_ingest_flv objs/srs_ingest_rtmp objs/srs_detect_rtmp \ - objs/srs_bandwidth_check objs/srs_h264_raw_publish + objs/srs_bandwidth_check objs/srs_h264_raw_publish \ + objs/srs_audio_raw_publish endif .PHONY: default clean help ssl nossl @@ -24,6 +25,7 @@ help: @echo " srs_flv_injecter inject keyframes information to metadata." @echo " srs_publish publish program using srs-librtmp" @echo " srs_h264_raw_publish publish raw h.264 stream to SSR by srs-librtmp" + @echo " srs_audio_raw_publish publish raw audio stream to SSR by srs-librtmp" @echo " srs_play play program using srs-librtmp" @echo " srs_ingest_flv ingest flv file and publish to RTMP server." @echo " srs_ingest_rtmp ingest RTMP and publish to RTMP server." @@ -85,6 +87,9 @@ objs/srs_publish: srs_publish.c $(SRS_RESEARCH_DEPS) $(SRS_LIBRTMP_I) $(SRS_LIBR objs/srs_h264_raw_publish: srs_h264_raw_publish.c $(SRS_RESEARCH_DEPS) $(SRS_LIBRTMP_I) $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(GCC) srs_h264_raw_publish.c $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(EXTRA_CXX_FLAG) -o objs/srs_h264_raw_publish +objs/srs_audio_raw_publish: srs_audio_raw_publish.c $(SRS_RESEARCH_DEPS) $(SRS_LIBRTMP_I) $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) + $(GCC) srs_audio_raw_publish.c $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(EXTRA_CXX_FLAG) -o objs/srs_audio_raw_publish + objs/srs_play: srs_play.c $(SRS_RESEARCH_DEPS) $(SRS_LIBRTMP_I) $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(GCC) srs_play.c $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(EXTRA_CXX_FLAG) -o objs/srs_play diff --git a/trunk/research/librtmp/srs_audio_raw_publish.c b/trunk/research/librtmp/srs_audio_raw_publish.c new file mode 100644 index 0000000000..4c3dbb3a9e --- /dev/null +++ b/trunk/research/librtmp/srs_audio_raw_publish.c @@ -0,0 +1,190 @@ +/* +The MIT License (MIT) + +Copyright (c) 2013-2014 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +/** +gcc srs_audio_raw_publish.c ../../objs/lib/srs_librtmp.a -g -O0 -lstdc++ -o srs_audio_raw_publish +*/ + +#include +#include +#include + +// for open audio raw file. +#include +#include +#include + +#include "../../objs/include/srs_librtmp.h" + +// https://github.com/winlinvip/simple-rtmp-server/issues/212#issuecomment-63648892 +// allspace: +// Take this file as an example: https://github.com/allspace/files/blob/master/srs.pcm +// It's captured using SDK callback method. I have filtered out h264 video, so it's audio only now. +// For every frame, it's a 8 bytes vendor specific header, following 160 bytes audio frame. +// The header part can be ignored. +int read_audio_frame(char* audio_raw, int file_size, char** pp, char** pdata, int* psize) +{ + char* p = *pp; + + if (file_size - (p - audio_raw) < 168) { + srs_lib_trace("audio must be 160+8 bytes. left %d bytes.", + file_size - (p - audio_raw)); + return - 1; + } + + // ignore 8bytes vendor specific header. + p += 8; + + // 160 bytes audio frame + *pdata = p; + *psize = 160; + + // next frame. + *pp = p + *psize; + + return 0; +} + +int main(int argc, char** argv) +{ + printf("publish raw audio as rtmp stream to server like FMLE/FFMPEG/Encoder\n"); + printf("SRS(simple-rtmp-server) client librtmp library.\n"); + printf("version: %d.%d.%d\n", srs_version_major(), srs_version_minor(), srs_version_revision()); + + if (argc <= 2) { + printf("Usage: %s \n", argv[0]); + printf(" audio_raw_file: the audio raw steam file.\n"); + printf(" rtmp_publish_url: the rtmp publish url.\n"); + printf("For example:\n"); + printf(" %s ./audio.raw.pcm rtmp://127.0.0.1:1935/live/livestream\n", argv[0]); + printf("Where the file: http://winlinvip.github.io/srs.release/3rdparty/audio.raw.pcm\n"); + printf("See: https://github.com/winlinvip/simple-rtmp-server/issues/212\n"); + exit(-1); + } + + const char* raw_file = argv[1]; + const char* rtmp_url = argv[2]; + srs_lib_trace("raw_file=%s, rtmp_url=%s", raw_file, rtmp_url); + + // open file + int raw_fd = open(raw_file, O_RDONLY); + if (raw_fd < 0) { + srs_lib_trace("open audio raw file %s failed.", raw_fd); + goto rtmp_destroy; + } + + off_t file_size = lseek(raw_fd, 0, SEEK_END); + if (file_size <= 0) { + srs_lib_trace("audio raw file %s empty.", raw_file); + goto rtmp_destroy; + } + srs_lib_trace("read entirely audio raw file, size=%dKB", (int)(file_size / 1024)); + + char* audio_raw = (char*)malloc(file_size); + if (!audio_raw) { + srs_lib_trace("alloc raw buffer failed for file %s.", raw_file); + goto rtmp_destroy; + } + + lseek(raw_fd, 0, SEEK_SET); + ssize_t nb_read = 0; + if ((nb_read = read(raw_fd, audio_raw, file_size)) != file_size) { + srs_lib_trace("buffer %s failed, expect=%dKB, actual=%dKB.", + raw_file, (int)(file_size / 1024), (int)(nb_read / 1024)); + goto rtmp_destroy; + } + + // connect rtmp context + srs_rtmp_t rtmp = srs_rtmp_create(rtmp_url); + + if (srs_simple_handshake(rtmp) != 0) { + srs_lib_trace("simple handshake failed."); + goto rtmp_destroy; + } + srs_lib_trace("simple handshake success"); + + if (srs_connect_app(rtmp) != 0) { + srs_lib_trace("connect vhost/app failed."); + goto rtmp_destroy; + } + srs_lib_trace("connect vhost/app success"); + + if (srs_publish_stream(rtmp) != 0) { + srs_lib_trace("publish stream failed."); + goto rtmp_destroy; + } + srs_lib_trace("publish stream success"); + + u_int32_t timestamp = 0; + u_int32_t time_delta = 17; + // @remark, to decode the file. + char* p = audio_raw; + for (;p < audio_raw + file_size;) { + // @remark, read a frame from file buffer. + char* data = NULL; + int size = 0; + if (read_audio_frame(audio_raw, file_size, &p, &data, &size) < 0) { + srs_lib_trace("read a frame from file buffer failed."); + goto rtmp_destroy; + } + + // 0 = Linear PCM, platform endian + // 1 = ADPCM + // 2 = MP3 + // 7 = G.711 A-law logarithmic PCM + // 8 = G.711 mu-law logarithmic PCM + // 10 = AAC + // 11 = Speex + char sound_format = 1; + // 3 = 44 kHz + char sound_rate = 3; + // 1 = 16-bit samples + char sound_size = 1; + // 1 = Stereo sound + char sound_type = 1; + + timestamp += time_delta; + + if (srs_audio_write_raw_frame(rtmp, + sound_format, sound_rate, sound_size, sound_type, + 0, data, size, timestamp) != 0 + ) { + srs_lib_trace("send audio raw data failed."); + goto rtmp_destroy; + } + + srs_lib_trace("sent packet: type=%s, time=%d, size=%d, codec=%d, rate=%d, sample=%d, channel=%d", + srs_type2string(SRS_RTMP_TYPE_AUDIO), timestamp, size, sound_format, sound_rate, sound_size, + sound_type); + + // @remark, when use encode device, it not need to sleep. + usleep(1000 * time_delta); + } + +rtmp_destroy: + srs_rtmp_destroy(rtmp); + close(raw_fd); + free(audio_raw); + + return 0; +} + diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 390e5427f5..c277518dfd 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 26 +#define VERSION_REVISION 27 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index f2be538f4e..e789d21a8b 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -1438,6 +1438,46 @@ char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize) return any->human_print(pdata, psize); } +/** +* write audio raw frame to SRS. +*/ +int srs_audio_write_raw_frame(srs_rtmp_t rtmp, + char sound_format, char sound_rate, char sound_size, char sound_type, + char aac_packet_type, char* frame, int frame_size, u_int32_t timestamp +) { + int ret = ERROR_SUCCESS; + + Context* context = (Context*)rtmp; + srs_assert(context); + + // TODO: FIXME: for aac, must send the sequence header first. + + // for audio frame, there is 1 or 2 bytes header: + // 1bytes, SoundFormat|SoundRate|SoundSize|SoundType + // 1bytes, AACPacketType for SoundFormat == 10 + int size = frame_size + 1; + if (aac_packet_type == SrsCodecAudioAAC) { + size += 1; + } + char* data = new char[size]; + char* p = data; + + u_int8_t audio_header = sound_type & 0x01; + audio_header |= (sound_size << 1) & 0x02; + audio_header |= (sound_rate << 2) & 0x0c; + audio_header |= (sound_format << 4) & 0xf0; + + *p++ = audio_header; + + if (aac_packet_type == SrsCodecAudioAAC) { + *p++ = aac_packet_type; + } + + memcpy(p, frame, frame_size); + + return srs_write_packet(context, SRS_RTMP_TYPE_AUDIO, timestamp, data, size); +} + /** * write h264 packet, with rtmp header. * @param frame_type, SrsCodecVideoAVCFrameKeyFrame or SrsCodecVideoAVCFrameInterFrame. @@ -1458,7 +1498,6 @@ int __srs_write_h264_packet(Context* context, // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78 int size = h264_raw_size + 5; char* data = new char[size]; - memcpy(data + 5, h264_raw_data, h264_raw_size); char* p = data; // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78 @@ -1480,6 +1519,9 @@ int __srs_write_h264_packet(Context* context, *p++ = pp[1]; *p++ = pp[0]; + // h.264 raw data. + memcpy(p, h264_raw_data, h264_raw_size); + return srs_write_packet(context, SRS_RTMP_TYPE_VIDEO, timestamp, data, size); } diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index 8a9d148c97..6df3be0ba3 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -461,6 +461,64 @@ extern void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value); */ extern char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize); +/************************************************************* +************************************************************** +* audio raw codec +************************************************************** +*************************************************************/ +/** +* write an audio raw frame to srs. +* not similar to h.264 video, the audio never aggregated, always +* encoded one frame by one, so this api is used to write a frame. +* +* @param sound_format Format of SoundData. The following values are defined: +* 0 = Linear PCM, platform endian +* 1 = ADPCM +* 2 = MP3 +* 3 = Linear PCM, little endian +* 4 = Nellymoser 16 kHz mono +* 5 = Nellymoser 8 kHz mono +* 6 = Nellymoser +* 7 = G.711 A-law logarithmic PCM +* 8 = G.711 mu-law logarithmic PCM +* 9 = reserved +* 10 = AAC +* 11 = Speex +* 14 = MP3 8 kHz +* 15 = Device-specific sound +* Formats 7, 8, 14, and 15 are reserved. +* AAC is supported in Flash Player 9,0,115,0 and higher. +* Speex is supported in Flash Player 10 and higher. +* @param sound_rate Sampling rate. The following values are defined: +* 0 = 5.5 kHz +* 1 = 11 kHz +* 2 = 22 kHz +* 3 = 44 kHz +* @param sound_size Size of each audio sample. This parameter only pertains to +* uncompressed formats. Compressed formats always decode +* to 16 bits internally. +* 0 = 8-bit samples +* 1 = 16-bit samples +* @param sound_type Mono or stereo sound +* 0 = Mono sound +* 1 = Stereo sound +* @param aac_packet_type The following values are defined: +* 0 = AAC sequence header +* 1 = AAC raw +* @param timestamp The timestamp of audio. +* +* @remark Ignore aac_packet_type if not aac(sound_format!=10). +* +* @see https://github.com/winlinvip/simple-rtmp-server/issues/212 +* @see E.4.2.1 AUDIODATA of video_file_format_spec_v10_1.pdf +* +* @return 0, success; otherswise, failed. +*/ +extern int srs_audio_write_raw_frame(srs_rtmp_t rtmp, + char sound_format, char sound_rate, char sound_size, char sound_type, + char aac_packet_type, char* frame, int frame_size, u_int32_t timestamp +); + /************************************************************* ************************************************************** * h264 raw codec @@ -474,7 +532,7 @@ typedef int srs_h264_bool; * each frame prefixed h.264 annexb header, by N[00] 00 00 01, where N>=0, * for instance, frame = header(00 00 00 01) + payload(67 42 80 29 95 A0 14 01 6E 40) * about annexb, @see H.264-AVC-ISO_IEC_14496-10.pdf, page 211. -* @paam frames_size the size of h264 raw data. +* @param frames_size the size of h264 raw data. * assert frames_size > 0, at least has 1 bytes header. * @param dts the dts of h.264 raw data. * @param pts the pts of h.264 raw data. diff --git a/trunk/src/srs/srs.upp b/trunk/src/srs/srs.upp index 5fa0bfbf97..da92f3ebf9 100755 --- a/trunk/src/srs/srs.upp +++ b/trunk/src/srs/srs.upp @@ -128,6 +128,7 @@ file ..\utest\srs_utest_reload.hpp, ..\utest\srs_utest_reload.cpp, research readonly separator, + ..\..\research\librtmp\srs_audio_raw_publish.c, ..\..\research\librtmp\srs_bandwidth_check.c, ..\..\research\librtmp\srs_detect_rtmp.c, ..\..\research\librtmp\srs_flv_injecter.c, From d9474d760076b1cd298c8317d82c8e266545ac6e Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 21 Nov 2014 13:48:57 +0800 Subject: [PATCH 197/800] refine examples of srs-librtmp, add srs_print_rtmp_packet. 2.0.28. --- README.md | 1 + trunk/research/librtmp/srs_flv_parser.c | 98 ++------------ trunk/research/librtmp/srs_ingest_flv.c | 9 +- trunk/research/librtmp/srs_ingest_rtmp.c | 7 +- trunk/research/librtmp/srs_play.c | 7 +- trunk/src/core/srs_core.hpp | 2 +- trunk/src/libs/srs_librtmp.cpp | 161 ++++++++++++++++++++++- trunk/src/libs/srs_librtmp.hpp | 100 ++++++++++++++ 8 files changed, 284 insertions(+), 101 deletions(-) diff --git a/README.md b/README.md index 25fd070333..e5027c2b8a 100755 --- a/README.md +++ b/README.md @@ -482,6 +482,7 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History +* v2.0, 2014-11-21, refine examples of srs-librtmp, add srs_print_rtmp_packet. 2.0.28. * v2.0, 2014-11-20, fix [#212](https://github.com/winlinvip/simple-rtmp-server/issues/212), support publish audio raw frames. 2.0.27 * v2.0, 2014-11-19, fix [#213](https://github.com/winlinvip/simple-rtmp-server/issues/213), support compile [srs-librtmp on windows](https://github.com/winlinvip/srs.librtmp), [bug #213](https://github.com/winlinvip/simple-rtmp-server/issues/213). 2.0.26 * v2.0, 2014-11-18, all wiki translated to English. 2.0.23. diff --git a/trunk/research/librtmp/srs_flv_parser.c b/trunk/research/librtmp/srs_flv_parser.c index 22108d5227..82a7470a07 100644 --- a/trunk/research/librtmp/srs_flv_parser.c +++ b/trunk/research/librtmp/srs_flv_parser.c @@ -124,86 +124,6 @@ int parse_bytes(char* data, int size, char* hbuf, int hsize, char* tbuf, int tsi } } -#define FLV_HEADER_SIZE 11 -int parse_script_data(u_int32_t timestamp, u_int32_t pts, char* data, int size, int64_t offset) -{ - int ret = 0; - - char hbuf[48]; - char tbuf[48]; - - int amf0_size = 0; - int nparsed = 0; - - srs_amf0_t amf0_name; - char* amf0_name_str = NULL; - - srs_amf0_t amf0_data; - char* amf0_data_str = NULL; - - // bytes - parse_bytes(data, size, hbuf, sizeof(hbuf), tbuf, sizeof(tbuf), 16); - - // amf0 - amf0_name = srs_amf0_parse(data, size, &nparsed); - if (amf0_name == NULL || nparsed >= size) { - srs_lib_trace("invalid amf0 name data."); - return -1; - } - amf0_data = srs_amf0_parse(data + nparsed, size - nparsed, &nparsed); - - srs_lib_trace("packet type=%s, dts=%d, pts=%d, size=%d, data-size=%d, \n" - "offset=%d\n[+00, +15] %s\n[-15, EOF] %s\n%s%s", - srs_type2string(SRS_RTMP_TYPE_SCRIPT), timestamp, pts, - size + FLV_HEADER_SIZE, size, (int)offset, hbuf, tbuf, - srs_amf0_human_print(amf0_name, &amf0_name_str, &amf0_size), - srs_amf0_human_print(amf0_data, &amf0_data_str, &amf0_size)); - - srs_amf0_free(amf0_name); - srs_amf0_free_bytes(amf0_name_str); - - srs_amf0_free(amf0_data); - srs_amf0_free_bytes(amf0_data_str); - - return ret; -} - -int parse_audio_data(u_int32_t timestamp, u_int32_t pts, char* data, int size, int64_t offset) -{ - int ret = 0; - - char hbuf[48]; - char tbuf[48]; - - // bytes - parse_bytes(data, size, hbuf, sizeof(hbuf), tbuf, sizeof(tbuf), 16); - - srs_lib_trace("packet type=%s, dts=%d, pts=%d, size=%d, data-size=%d, \n" - "offset=%d\n[+00, +15] %s\n[-15, EOF] %s\n", - srs_type2string(SRS_RTMP_TYPE_AUDIO), timestamp, pts, - size + FLV_HEADER_SIZE, size, (int)offset, hbuf, tbuf); - - return ret; -} - -int parse_video_data(u_int32_t timestamp, u_int32_t pts, char* data, int size, int64_t offset) -{ - int ret = 0; - - char hbuf[48]; - char tbuf[48]; - - // bytes - parse_bytes(data, size, hbuf, sizeof(hbuf), tbuf, sizeof(tbuf), 16); - - srs_lib_trace("packet type=%s, dts=%d, pts=%d, size=%d, data-size=%d, \n" - "offset=%d\n[+00, +15] %s\n[-15, EOF] %s\n", - srs_type2string(SRS_RTMP_TYPE_VIDEO), timestamp, pts, - size + FLV_HEADER_SIZE, size, (int)offset, hbuf, tbuf); - - return ret; -} - int parse_flv(srs_flv_t flv) { int ret = 0; @@ -240,19 +160,19 @@ int parse_flv(srs_flv_t flv) break; } - u_int32_t pts = 0; data = (char*)malloc(size); - if ((ret = srs_flv_read_tag_data(flv, data, size)) == 0 - && (ret = srs_parse_timestamp(timestamp, type, data, size, &pts)) == 0 - ) { - if (type == SRS_RTMP_TYPE_AUDIO) { - ret = parse_audio_data(timestamp, pts, data, size, offset); - } else if (type == SRS_RTMP_TYPE_VIDEO) { - ret = parse_video_data(timestamp, pts, data, size, offset); + if ((ret = srs_flv_read_tag_data(flv, data, size)) == 0) { + if ((ret = srs_print_rtmp_packet(type, timestamp, data, size)) == 0) { + char hbuf[48]; char tbuf[48]; + parse_bytes(data, size, hbuf, sizeof(hbuf), tbuf, sizeof(tbuf), 16); + srs_raw_trace("offset=%d, first and last 16 bytes:\n" + "[+00, +15] %s\n[-15, EOF] %s\n", (int)offset, hbuf, tbuf); } else { - ret = parse_script_data(timestamp, pts, data, size, offset); + srs_lib_trace("print packet failed. ret=%d", ret); } + } else { + srs_lib_trace("read flv failed. ret=%d", ret); } free(data); diff --git a/trunk/research/librtmp/srs_ingest_flv.c b/trunk/research/librtmp/srs_ingest_flv.c index baa9881cb3..141354933d 100644 --- a/trunk/research/librtmp/srs_ingest_flv.c +++ b/trunk/research/librtmp/srs_ingest_flv.c @@ -141,12 +141,17 @@ int do_proxy(srs_flv_t flv, srs_rtmp_t ortmp, int64_t re, int32_t* pstarttime, u return ret; } + u_int32_t timestamp = *ptimestamp; + + if ((ret = srs_print_rtmp_packet(type, timestamp, data, size)) != 0) { + srs_lib_trace("print packet failed. ret=%d", ret); + return ret; + } + if ((ret = srs_write_packet(ortmp, type, *ptimestamp, data, size)) != 0) { srs_lib_trace("irtmp get packet failed. ret=%d", ret); return ret; } - srs_lib_verbose("ortmp sent packet: type=%s, time=%d, size=%d", - srs_type2string(type), *ptimestamp, size); if (*pstarttime < 0) { *pstarttime = *ptimestamp; diff --git a/trunk/research/librtmp/srs_ingest_rtmp.c b/trunk/research/librtmp/srs_ingest_rtmp.c index 3bec458c6e..25357ce933 100644 --- a/trunk/research/librtmp/srs_ingest_rtmp.c +++ b/trunk/research/librtmp/srs_ingest_rtmp.c @@ -112,8 +112,11 @@ int proxy(srs_rtmp_t irtmp, srs_rtmp_t ortmp) srs_lib_trace("irtmp get packet failed. ret=%d", ret); return ret; } - srs_lib_verbose("irtmp got packet: type=%s, time=%d, size=%d", - srs_type2string(type), timestamp, size); + + if ((ret = srs_print_rtmp_packet(type, timestamp, data, size)) != 0) { + srs_lib_trace("print packet failed. ret=%d", ret); + return ret; + } if ((ret = srs_write_packet(ortmp, type, timestamp, data, size)) != 0) { srs_lib_trace("irtmp get packet failed. ret=%d", ret); diff --git a/trunk/research/librtmp/srs_play.c b/trunk/research/librtmp/srs_play.c index 0b04104748..4039ed3c2d 100644 --- a/trunk/research/librtmp/srs_play.c +++ b/trunk/research/librtmp/srs_play.c @@ -69,16 +69,15 @@ int main(int argc, char** argv) int size; char type; char* data; - u_int32_t timestamp, pts; + u_int32_t timestamp; if (srs_read_packet(rtmp, &type, ×tamp, &data, &size) != 0) { goto rtmp_destroy; } - if (srs_parse_timestamp(timestamp, type, data, size, &pts) != 0) { + + if (srs_print_rtmp_packet(type, timestamp, data, size) != 0) { goto rtmp_destroy; } - srs_lib_trace("got packet: type=%s, dts=%d, pts=%d, size=%d", - srs_type2string(type), timestamp, pts, size); free(data); } diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index c277518dfd..494556c551 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 27 +#define VERSION_REVISION 28 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index e789d21a8b..b0565c4934 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -936,6 +936,161 @@ int srs_parse_timestamp( return ret; } +char srs_get_codec_id(char* data, int size) +{ + if (size < 1) { + return 0; + } + + char codec_id = data[0]; + codec_id = codec_id & 0x0F; + + return codec_id; +} + +const char* srs_code_id2string(char codec_id) +{ + static const char* h263 = "H.263"; + static const char* screen = "Screen"; + static const char* vp6 = "VP6"; + static const char* vp6_alpha = "VP6Alpha"; + static const char* screen2 = "Screen2"; + static const char* h264 = "H.264"; + static const char* unknown = "Unknown"; + + switch (codec_id) { + case 2: return h263; + case 3: return screen; + case 4: return vp6; + case 5: return vp6_alpha; + case 6: return screen2; + case 7: return h264; + default: return unknown; + } + + return unknown; +} + +char srs_get_avc_packet_type(char* data, int size) +{ + if (size < 2) { + return -1; + } + + if (!SrsFlvCodec::video_is_h264(data, size)) { + return -1; + } + + u_int8_t avc_packet_type = data[1]; + + if (avc_packet_type > 2) { + return -1; + } + + return avc_packet_type; +} + +const char* srs_avc_packet2string(char avc_packet_type) +{ + static const char* sps_pps = "SpsPps"; + static const char* nalu = "Nalu"; + static const char* sps_pps_end = "SpsPpsEnd"; + static const char* unknown = "Unknown"; + + switch (avc_packet_type) { + case 0: return sps_pps; + case 1: return nalu; + case 2: return sps_pps_end; + default: return unknown; + } + + return unknown; +} + +char srs_get_frame_type(char* data, int size) +{ + if (size < 1) { + return -1; + } + + if (!SrsFlvCodec::video_is_h264(data, size)) { + return -1; + } + + u_int8_t frame_type = data[0]; + frame_type = (frame_type >> 4) & 0x0f; + if (frame_type < 1 || frame_type > 5) { + return -1; + } + + return frame_type; +} + +const char* srs_frame_type2string(char frame_type) +{ + static const char* keyframe = "I"; + static const char* interframe = "P/B"; + static const char* disposable_interframe = "DI"; + static const char* generated_keyframe = "GI"; + static const char* video_infoframe = "VI"; + static const char* unknown = "Unknown"; + + switch (frame_type) { + case 1: return keyframe; + case 2: return interframe; + case 3: return disposable_interframe; + case 4: return generated_keyframe; + case 5: return video_infoframe; + default: return unknown; + } + + return unknown; +} + +int srs_print_rtmp_packet(char type, u_int32_t timestamp, char* data, int size) +{ + int ret = ERROR_SUCCESS; + + u_int32_t pts; + if (srs_parse_timestamp(timestamp, type, data, size, &pts) != 0) { + return ret; + } + + if (type == SRS_RTMP_TYPE_VIDEO) { + srs_lib_trace("Video packet type=%s, dts=%d, pts=%d, size=%d, %s(%s,%s)", + srs_type2string(type), timestamp, pts, size, + srs_code_id2string(srs_get_codec_id(data, size)), + srs_avc_packet2string(srs_get_avc_packet_type(data, size)), + srs_frame_type2string(srs_get_frame_type(data, size)) + ); + } else if (type == SRS_RTMP_TYPE_AUDIO) { + srs_lib_trace("Audio packet type=%s, dts=%d, pts=%d, size=%d", + srs_type2string(type), timestamp, pts, size); + } else if (type == SRS_RTMP_TYPE_SCRIPT) { + srs_lib_verbose("Data packet type=%s, time=%d, size=%d", + srs_type2string(type), timestamp, size); + int nparsed = 0; + while (nparsed < size) { + int nb_parsed_this = 0; + srs_amf0_t amf0 = srs_amf0_parse(data + nparsed, size - nparsed, &nb_parsed_this); + if (amf0 == NULL) { + break; + } + + nparsed += nb_parsed_this; + + char* amf0_str = NULL; + srs_raw_trace("%s", srs_amf0_human_print(amf0, &amf0_str, NULL)); + srs_amf0_free_bytes(amf0_str); + } + } else { + srs_lib_trace("Unknown packet type=%s, dts=%d, pts=%d, size=%d", + srs_type2string(type), timestamp, pts, size); + } + + return ret; +} + const char* srs_format_time() { struct timeval tv; @@ -1171,7 +1326,9 @@ srs_amf0_t srs_amf0_parse(char* data, int size, int* nparsed) return amf0; } - *nparsed = stream.pos(); + if (nparsed) { + *nparsed = stream.pos(); + } amf0 = (srs_amf0_t)any; return amf0; @@ -1445,8 +1602,6 @@ int srs_audio_write_raw_frame(srs_rtmp_t rtmp, char sound_format, char sound_rate, char sound_size, char sound_type, char aac_packet_type, char* frame, int frame_size, u_int32_t timestamp ) { - int ret = ERROR_SUCCESS; - Context* context = (Context*)rtmp; srs_assert(context); diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index 6df3be0ba3..8b35f07424 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -273,9 +273,22 @@ extern int srs_version_revision(); * utilities ************************************************************** *************************************************************/ +/** +* get the current system time in ms. +* use gettimeofday() to get system time. +*/ extern int64_t srs_get_time_ms(); + +/** +* get the send bytes. +*/ extern int64_t srs_get_nsend_bytes(srs_rtmp_t rtmp); + +/** +* get the recv bytes. +*/ extern int64_t srs_get_nrecv_bytes(srs_rtmp_t rtmp); + /** * parse the dts and pts by time in header and data in tag, * or to parse the RTMP packet by srs_read_packet(). @@ -296,10 +309,92 @@ extern int srs_parse_timestamp( u_int32_t* ppts ); +/** +* get the CodecID of video tag. +* Codec Identifier. The following values are defined: +* 2 = Sorenson H.263 +* 3 = Screen video +* 4 = On2 VP6 +* 5 = On2 VP6 with alpha channel +* 6 = Screen video version 2 +* 7 = AVC +* @return the code id. 0 for error. +*/ +extern char srs_get_codec_id(char* data, int size); + +/** +* get the codec id string. +* H.263 = Sorenson H.263 +* Screen = Screen video +* VP6 = On2 VP6 +* VP6Alpha = On2 VP6 with alpha channel +* Screen2 = Screen video version 2 +* H.264 = AVC +* otherwise, "Unknown" +* @remark user never free the return char*, +* it's static shared const string. +*/ +extern const char* srs_code_id2string(char codec_id); + +/** +* get the AVCPacketType of video tag. +* The following values are defined: +* 0 = AVC sequence header +* 1 = AVC NALU +* 2 = AVC end of sequence (lower level NALU sequence ender is +* not required or supported) +* @return the avc packet type. -1(0xff) for error. +*/ +extern char srs_get_avc_packet_type(char* data, int size); + +/** +* get the avc packet type string. +* SpsPps = AVC sequence header +* Nalu = AVC NALU +* SpsPpsEnd = AVC end of sequence +* otherwise, "Unknown" +* @remark user never free the return char*, +* it's static shared const string. +*/ +extern const char* srs_avc_packet2string(char avc_packet_type); + +/** +* get the FrameType of video tag. +* Type of video frame. The following values are defined: +* 1 = key frame (for AVC, a seekable frame) +* 2 = inter frame (for AVC, a non-seekable frame) +* 3 = disposable inter frame (H.263 only) +* 4 = generated key frame (reserved for server use only) +* 5 = video info/command frame +* @return the frame type. 0 for error. +*/ +extern char srs_get_frame_type(char* data, int size); + +/** +* get the frame type string. +* I = key frame (for AVC, a seekable frame) +* P/B = inter frame (for AVC, a non-seekable frame) +* DI = disposable inter frame (H.263 only) +* GI = generated key frame (reserved for server use only) +* VI = video info/command frame +* otherwise, "Unknown" +* @remark user never free the return char*, +* it's static shared const string. +*/ +extern const char* srs_frame_type2string(char frame_type); + +/** +* print the rtmp packet, use srs_lib_trace/srs_lib_verbose for packet, +* and use srs_raw_trace for script data body. +* @return an error code for parse the timetstamp to dts and pts. +*/ +extern int srs_print_rtmp_packet(char type, u_int32_t timestamp, char* data, int size); + // log to console, for use srs-librtmp application. extern const char* srs_format_time(); #define srs_lib_trace(msg, ...) printf("[%s] ", srs_format_time());printf(msg, ##__VA_ARGS__);printf("\n") #define srs_lib_verbose(msg, ...) printf("[%s] ", srs_format_time());printf(msg, ##__VA_ARGS__);printf("\n") +#define srs_raw_trace(msg, ...) printf(msg, ##__VA_ARGS__) /************************************************************* ************************************************************** @@ -412,6 +507,11 @@ extern srs_flv_bool srs_flv_is_keyframe(char* data, int32_t size); typedef void* srs_amf0_t; typedef int srs_amf0_bool; typedef double srs_amf0_number; +/** +* parse amf0 from data. +* @param nparsed, the parsed size, NULL to ignore. +* @return the parsed amf0 object. NULL for error. +*/ extern srs_amf0_t srs_amf0_parse(char* data, int size, int* nparsed); extern srs_amf0_t srs_amf0_create_number(srs_amf0_number value); extern srs_amf0_t srs_amf0_create_ecma_array(); From 30e7c38a48c81e3565fed13e7d23846b645ebbfc Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 21 Nov 2014 13:56:09 +0800 Subject: [PATCH 198/800] refine code, add human readable section --- trunk/src/libs/srs_librtmp.cpp | 304 ++++++++++++++++----------------- trunk/src/libs/srs_librtmp.hpp | 128 +++++++------- 2 files changed, 219 insertions(+), 213 deletions(-) diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index b0565c4934..a3da335b39 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -701,23 +701,6 @@ int srs_publish_stream(srs_rtmp_t rtmp) return ret; } -const char* srs_type2string(char type) -{ - static const char* audio = "Audio"; - static const char* video = "Video"; - static const char* data = "Data"; - static const char* unknown = "Unknown"; - - switch (type) { - case SRS_RTMP_TYPE_AUDIO: return audio; - case SRS_RTMP_TYPE_VIDEO: return video; - case SRS_RTMP_TYPE_SCRIPT: return data; - default: return unknown; - } - - return unknown; -} - int srs_bandwidth_check(srs_rtmp_t rtmp, int64_t* start_time, int64_t* end_time, int* play_kbps, int* publish_kbps, @@ -948,29 +931,6 @@ char srs_get_codec_id(char* data, int size) return codec_id; } -const char* srs_code_id2string(char codec_id) -{ - static const char* h263 = "H.263"; - static const char* screen = "Screen"; - static const char* vp6 = "VP6"; - static const char* vp6_alpha = "VP6Alpha"; - static const char* screen2 = "Screen2"; - static const char* h264 = "H.264"; - static const char* unknown = "Unknown"; - - switch (codec_id) { - case 2: return h263; - case 3: return screen; - case 4: return vp6; - case 5: return vp6_alpha; - case 6: return screen2; - case 7: return h264; - default: return unknown; - } - - return unknown; -} - char srs_get_avc_packet_type(char* data, int size) { if (size < 2) { @@ -990,23 +950,6 @@ char srs_get_avc_packet_type(char* data, int size) return avc_packet_type; } -const char* srs_avc_packet2string(char avc_packet_type) -{ - static const char* sps_pps = "SpsPps"; - static const char* nalu = "Nalu"; - static const char* sps_pps_end = "SpsPpsEnd"; - static const char* unknown = "Unknown"; - - switch (avc_packet_type) { - case 0: return sps_pps; - case 1: return nalu; - case 2: return sps_pps_end; - default: return unknown; - } - - return unknown; -} - char srs_get_frame_type(char* data, int size) { if (size < 1) { @@ -1026,101 +969,6 @@ char srs_get_frame_type(char* data, int size) return frame_type; } -const char* srs_frame_type2string(char frame_type) -{ - static const char* keyframe = "I"; - static const char* interframe = "P/B"; - static const char* disposable_interframe = "DI"; - static const char* generated_keyframe = "GI"; - static const char* video_infoframe = "VI"; - static const char* unknown = "Unknown"; - - switch (frame_type) { - case 1: return keyframe; - case 2: return interframe; - case 3: return disposable_interframe; - case 4: return generated_keyframe; - case 5: return video_infoframe; - default: return unknown; - } - - return unknown; -} - -int srs_print_rtmp_packet(char type, u_int32_t timestamp, char* data, int size) -{ - int ret = ERROR_SUCCESS; - - u_int32_t pts; - if (srs_parse_timestamp(timestamp, type, data, size, &pts) != 0) { - return ret; - } - - if (type == SRS_RTMP_TYPE_VIDEO) { - srs_lib_trace("Video packet type=%s, dts=%d, pts=%d, size=%d, %s(%s,%s)", - srs_type2string(type), timestamp, pts, size, - srs_code_id2string(srs_get_codec_id(data, size)), - srs_avc_packet2string(srs_get_avc_packet_type(data, size)), - srs_frame_type2string(srs_get_frame_type(data, size)) - ); - } else if (type == SRS_RTMP_TYPE_AUDIO) { - srs_lib_trace("Audio packet type=%s, dts=%d, pts=%d, size=%d", - srs_type2string(type), timestamp, pts, size); - } else if (type == SRS_RTMP_TYPE_SCRIPT) { - srs_lib_verbose("Data packet type=%s, time=%d, size=%d", - srs_type2string(type), timestamp, size); - int nparsed = 0; - while (nparsed < size) { - int nb_parsed_this = 0; - srs_amf0_t amf0 = srs_amf0_parse(data + nparsed, size - nparsed, &nb_parsed_this); - if (amf0 == NULL) { - break; - } - - nparsed += nb_parsed_this; - - char* amf0_str = NULL; - srs_raw_trace("%s", srs_amf0_human_print(amf0, &amf0_str, NULL)); - srs_amf0_free_bytes(amf0_str); - } - } else { - srs_lib_trace("Unknown packet type=%s, dts=%d, pts=%d, size=%d", - srs_type2string(type), timestamp, pts, size); - } - - return ret; -} - -const char* srs_format_time() -{ - struct timeval tv; - static char buf[23]; - - memset(buf, 0, sizeof(buf)); - - // clock time - if (gettimeofday(&tv, NULL) == -1) { - return buf; - } - - // to calendar time - struct tm* tm; - if ((tm = localtime((const time_t*)&tv.tv_sec)) == NULL) { - return buf; - } - - snprintf(buf, sizeof(buf), - "%d-%02d-%02d %02d:%02d:%02d.%03d", - 1900 + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec, - (int)(tv.tv_usec / 1000)); - - // for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 - buf[sizeof(buf) - 1] = 0; - - return buf; -} - struct FlvContext { SrsFileReader reader; @@ -1595,6 +1443,158 @@ char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize) return any->human_print(pdata, psize); } +const char* srs_type2string(char type) +{ + static const char* audio = "Audio"; + static const char* video = "Video"; + static const char* data = "Data"; + static const char* unknown = "Unknown"; + + switch (type) { + case SRS_RTMP_TYPE_AUDIO: return audio; + case SRS_RTMP_TYPE_VIDEO: return video; + case SRS_RTMP_TYPE_SCRIPT: return data; + default: return unknown; + } + + return unknown; +} + +const char* srs_code_id2string(char codec_id) +{ + static const char* h263 = "H.263"; + static const char* screen = "Screen"; + static const char* vp6 = "VP6"; + static const char* vp6_alpha = "VP6Alpha"; + static const char* screen2 = "Screen2"; + static const char* h264 = "H.264"; + static const char* unknown = "Unknown"; + + switch (codec_id) { + case 2: return h263; + case 3: return screen; + case 4: return vp6; + case 5: return vp6_alpha; + case 6: return screen2; + case 7: return h264; + default: return unknown; + } + + return unknown; +} + +const char* srs_avc_packet2string(char avc_packet_type) +{ + static const char* sps_pps = "SpsPps"; + static const char* nalu = "Nalu"; + static const char* sps_pps_end = "SpsPpsEnd"; + static const char* unknown = "Unknown"; + + switch (avc_packet_type) { + case 0: return sps_pps; + case 1: return nalu; + case 2: return sps_pps_end; + default: return unknown; + } + + return unknown; +} + +const char* srs_frame_type2string(char frame_type) +{ + static const char* keyframe = "I"; + static const char* interframe = "P/B"; + static const char* disposable_interframe = "DI"; + static const char* generated_keyframe = "GI"; + static const char* video_infoframe = "VI"; + static const char* unknown = "Unknown"; + + switch (frame_type) { + case 1: return keyframe; + case 2: return interframe; + case 3: return disposable_interframe; + case 4: return generated_keyframe; + case 5: return video_infoframe; + default: return unknown; + } + + return unknown; +} + +int srs_print_rtmp_packet(char type, u_int32_t timestamp, char* data, int size) +{ + int ret = ERROR_SUCCESS; + + u_int32_t pts; + if (srs_parse_timestamp(timestamp, type, data, size, &pts) != 0) { + return ret; + } + + if (type == SRS_RTMP_TYPE_VIDEO) { + srs_lib_trace("Video packet type=%s, dts=%d, pts=%d, size=%d, %s(%s,%s)", + srs_type2string(type), timestamp, pts, size, + srs_code_id2string(srs_get_codec_id(data, size)), + srs_avc_packet2string(srs_get_avc_packet_type(data, size)), + srs_frame_type2string(srs_get_frame_type(data, size)) + ); + } else if (type == SRS_RTMP_TYPE_AUDIO) { + srs_lib_trace("Audio packet type=%s, dts=%d, pts=%d, size=%d", + srs_type2string(type), timestamp, pts, size); + } else if (type == SRS_RTMP_TYPE_SCRIPT) { + srs_lib_verbose("Data packet type=%s, time=%d, size=%d", + srs_type2string(type), timestamp, size); + int nparsed = 0; + while (nparsed < size) { + int nb_parsed_this = 0; + srs_amf0_t amf0 = srs_amf0_parse(data + nparsed, size - nparsed, &nb_parsed_this); + if (amf0 == NULL) { + break; + } + + nparsed += nb_parsed_this; + + char* amf0_str = NULL; + srs_raw_trace("%s", srs_amf0_human_print(amf0, &amf0_str, NULL)); + srs_amf0_free_bytes(amf0_str); + } + } else { + srs_lib_trace("Unknown packet type=%s, dts=%d, pts=%d, size=%d", + srs_type2string(type), timestamp, pts, size); + } + + return ret; +} + +const char* srs_format_time() +{ + struct timeval tv; + static char buf[23]; + + memset(buf, 0, sizeof(buf)); + + // clock time + if (gettimeofday(&tv, NULL) == -1) { + return buf; + } + + // to calendar time + struct tm* tm; + if ((tm = localtime((const time_t*)&tv.tv_sec)) == NULL) { + return buf; + } + + snprintf(buf, sizeof(buf), + "%d-%02d-%02d %02d:%02d:%02d.%03d", + 1900 + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec, + (int)(tv.tv_usec / 1000)); + + // for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 + buf[sizeof(buf) - 1] = 0; + + return buf; +} + /** * write audio raw frame to SRS. */ diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index 8b35f07424..89b5ac9fc8 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -226,16 +226,6 @@ extern int srs_bandwidth_check(srs_rtmp_t rtmp, // 18 = script data #define SRS_RTMP_TYPE_SCRIPT 18 /** -* convert the flv tag type to string. -* SRS_RTMP_TYPE_AUDIO to "Audio" -* SRS_RTMP_TYPE_VIDEO to "Video" -* SRS_RTMP_TYPE_SCRIPT to "Data" -* otherwise, "Unknown" -* @remark user never free the return char*, -* it's static shared const string. -*/ -extern const char* srs_type2string(char type); -/** * read a audio/video/script-data packet from rtmp stream. * @param type, output the packet type, macros: * SRS_RTMP_TYPE_AUDIO, FlvTagAudio @@ -322,20 +312,6 @@ extern int srs_parse_timestamp( */ extern char srs_get_codec_id(char* data, int size); -/** -* get the codec id string. -* H.263 = Sorenson H.263 -* Screen = Screen video -* VP6 = On2 VP6 -* VP6Alpha = On2 VP6 with alpha channel -* Screen2 = Screen video version 2 -* H.264 = AVC -* otherwise, "Unknown" -* @remark user never free the return char*, -* it's static shared const string. -*/ -extern const char* srs_code_id2string(char codec_id); - /** * get the AVCPacketType of video tag. * The following values are defined: @@ -347,17 +323,6 @@ extern const char* srs_code_id2string(char codec_id); */ extern char srs_get_avc_packet_type(char* data, int size); -/** -* get the avc packet type string. -* SpsPps = AVC sequence header -* Nalu = AVC NALU -* SpsPpsEnd = AVC end of sequence -* otherwise, "Unknown" -* @remark user never free the return char*, -* it's static shared const string. -*/ -extern const char* srs_avc_packet2string(char avc_packet_type); - /** * get the FrameType of video tag. * Type of video frame. The following values are defined: @@ -370,32 +335,6 @@ extern const char* srs_avc_packet2string(char avc_packet_type); */ extern char srs_get_frame_type(char* data, int size); -/** -* get the frame type string. -* I = key frame (for AVC, a seekable frame) -* P/B = inter frame (for AVC, a non-seekable frame) -* DI = disposable inter frame (H.263 only) -* GI = generated key frame (reserved for server use only) -* VI = video info/command frame -* otherwise, "Unknown" -* @remark user never free the return char*, -* it's static shared const string. -*/ -extern const char* srs_frame_type2string(char frame_type); - -/** -* print the rtmp packet, use srs_lib_trace/srs_lib_verbose for packet, -* and use srs_raw_trace for script data body. -* @return an error code for parse the timetstamp to dts and pts. -*/ -extern int srs_print_rtmp_packet(char type, u_int32_t timestamp, char* data, int size); - -// log to console, for use srs-librtmp application. -extern const char* srs_format_time(); -#define srs_lib_trace(msg, ...) printf("[%s] ", srs_format_time());printf(msg, ##__VA_ARGS__);printf("\n") -#define srs_lib_verbose(msg, ...) printf("[%s] ", srs_format_time());printf(msg, ##__VA_ARGS__);printf("\n") -#define srs_raw_trace(msg, ...) printf(msg, ##__VA_ARGS__) - /************************************************************* ************************************************************** * flv codec @@ -553,6 +492,12 @@ extern void srs_amf0_ecma_array_property_set(srs_amf0_t amf0, const char* name, extern int srs_amf0_strict_array_property_count(srs_amf0_t amf0); extern srs_amf0_t srs_amf0_strict_array_property_at(srs_amf0_t amf0, int index); extern void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value); + +/************************************************************* +************************************************************** +* human readable print. +************************************************************** +*************************************************************/ /** * human readable print * @param pdata, output the heap data, NULL to ignore. @@ -560,6 +505,67 @@ extern void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value); * @return return the *pdata for print. NULL to ignore. */ extern char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize); +/** +* convert the flv tag type to string. +* SRS_RTMP_TYPE_AUDIO to "Audio" +* SRS_RTMP_TYPE_VIDEO to "Video" +* SRS_RTMP_TYPE_SCRIPT to "Data" +* otherwise, "Unknown" +* @remark user never free the return char*, +* it's static shared const string. +*/ +extern const char* srs_type2string(char type); + +/** +* get the codec id string. +* H.263 = Sorenson H.263 +* Screen = Screen video +* VP6 = On2 VP6 +* VP6Alpha = On2 VP6 with alpha channel +* Screen2 = Screen video version 2 +* H.264 = AVC +* otherwise, "Unknown" +* @remark user never free the return char*, +* it's static shared const string. +*/ +extern const char* srs_code_id2string(char codec_id); + +/** +* get the avc packet type string. +* SpsPps = AVC sequence header +* Nalu = AVC NALU +* SpsPpsEnd = AVC end of sequence +* otherwise, "Unknown" +* @remark user never free the return char*, +* it's static shared const string. +*/ +extern const char* srs_avc_packet2string(char avc_packet_type); + +/** +* get the frame type string. +* I = key frame (for AVC, a seekable frame) +* P/B = inter frame (for AVC, a non-seekable frame) +* DI = disposable inter frame (H.263 only) +* GI = generated key frame (reserved for server use only) +* VI = video info/command frame +* otherwise, "Unknown" +* @remark user never free the return char*, +* it's static shared const string. +*/ +extern const char* srs_frame_type2string(char frame_type); + +/** +* print the rtmp packet, use srs_lib_trace/srs_lib_verbose for packet, +* and use srs_raw_trace for script data body. +* @return an error code for parse the timetstamp to dts and pts. +*/ +extern int srs_print_rtmp_packet(char type, u_int32_t timestamp, char* data, int size); + +// log to console, for use srs-librtmp application. +extern const char* srs_format_time(); +#define srs_lib_trace(msg, ...) printf("[%s] ", srs_format_time());printf(msg, ##__VA_ARGS__);printf("\n") +#define srs_lib_verbose(msg, ...) printf("[%s] ", srs_format_time());printf(msg, ##__VA_ARGS__);printf("\n") +#define srs_raw_trace(msg, ...) printf(msg, ##__VA_ARGS__) /************************************************************* ************************************************************** From b3bb2cdf2b0d2a461c85380d760b4d0dca331711 Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 21 Nov 2014 14:03:30 +0800 Subject: [PATCH 199/800] refine code, rename the human functions to prefixed with srs_human_ --- .../research/librtmp/srs_audio_raw_publish.c | 34 +++++++------- trunk/research/librtmp/srs_bandwidth_check.c | 20 ++++----- trunk/research/librtmp/srs_detect_rtmp.c | 40 ++++++++--------- trunk/research/librtmp/srs_flv_injecter.c | 36 +++++++-------- trunk/research/librtmp/srs_flv_parser.c | 22 +++++----- trunk/research/librtmp/srs_h264_raw_publish.c | 44 +++++++++---------- trunk/research/librtmp/srs_ingest_flv.c | 42 +++++++++--------- trunk/research/librtmp/srs_ingest_rtmp.c | 44 +++++++++---------- trunk/research/librtmp/srs_play.c | 16 +++---- trunk/research/librtmp/srs_publish.c | 20 ++++----- trunk/src/libs/srs_librtmp.cpp | 38 ++++++++-------- trunk/src/libs/srs_librtmp.hpp | 24 +++++----- 12 files changed, 190 insertions(+), 190 deletions(-) diff --git a/trunk/research/librtmp/srs_audio_raw_publish.c b/trunk/research/librtmp/srs_audio_raw_publish.c index 4c3dbb3a9e..04e228dd44 100644 --- a/trunk/research/librtmp/srs_audio_raw_publish.c +++ b/trunk/research/librtmp/srs_audio_raw_publish.c @@ -46,7 +46,7 @@ int read_audio_frame(char* audio_raw, int file_size, char** pp, char** pdata, in char* p = *pp; if (file_size - (p - audio_raw) < 168) { - srs_lib_trace("audio must be 160+8 bytes. left %d bytes.", + srs_human_trace("audio must be 160+8 bytes. left %d bytes.", file_size - (p - audio_raw)); return - 1; } @@ -83,32 +83,32 @@ int main(int argc, char** argv) const char* raw_file = argv[1]; const char* rtmp_url = argv[2]; - srs_lib_trace("raw_file=%s, rtmp_url=%s", raw_file, rtmp_url); + srs_human_trace("raw_file=%s, rtmp_url=%s", raw_file, rtmp_url); // open file int raw_fd = open(raw_file, O_RDONLY); if (raw_fd < 0) { - srs_lib_trace("open audio raw file %s failed.", raw_fd); + srs_human_trace("open audio raw file %s failed.", raw_fd); goto rtmp_destroy; } off_t file_size = lseek(raw_fd, 0, SEEK_END); if (file_size <= 0) { - srs_lib_trace("audio raw file %s empty.", raw_file); + srs_human_trace("audio raw file %s empty.", raw_file); goto rtmp_destroy; } - srs_lib_trace("read entirely audio raw file, size=%dKB", (int)(file_size / 1024)); + srs_human_trace("read entirely audio raw file, size=%dKB", (int)(file_size / 1024)); char* audio_raw = (char*)malloc(file_size); if (!audio_raw) { - srs_lib_trace("alloc raw buffer failed for file %s.", raw_file); + srs_human_trace("alloc raw buffer failed for file %s.", raw_file); goto rtmp_destroy; } lseek(raw_fd, 0, SEEK_SET); ssize_t nb_read = 0; if ((nb_read = read(raw_fd, audio_raw, file_size)) != file_size) { - srs_lib_trace("buffer %s failed, expect=%dKB, actual=%dKB.", + srs_human_trace("buffer %s failed, expect=%dKB, actual=%dKB.", raw_file, (int)(file_size / 1024), (int)(nb_read / 1024)); goto rtmp_destroy; } @@ -117,22 +117,22 @@ int main(int argc, char** argv) srs_rtmp_t rtmp = srs_rtmp_create(rtmp_url); if (srs_simple_handshake(rtmp) != 0) { - srs_lib_trace("simple handshake failed."); + srs_human_trace("simple handshake failed."); goto rtmp_destroy; } - srs_lib_trace("simple handshake success"); + srs_human_trace("simple handshake success"); if (srs_connect_app(rtmp) != 0) { - srs_lib_trace("connect vhost/app failed."); + srs_human_trace("connect vhost/app failed."); goto rtmp_destroy; } - srs_lib_trace("connect vhost/app success"); + srs_human_trace("connect vhost/app success"); if (srs_publish_stream(rtmp) != 0) { - srs_lib_trace("publish stream failed."); + srs_human_trace("publish stream failed."); goto rtmp_destroy; } - srs_lib_trace("publish stream success"); + srs_human_trace("publish stream success"); u_int32_t timestamp = 0; u_int32_t time_delta = 17; @@ -143,7 +143,7 @@ int main(int argc, char** argv) char* data = NULL; int size = 0; if (read_audio_frame(audio_raw, file_size, &p, &data, &size) < 0) { - srs_lib_trace("read a frame from file buffer failed."); + srs_human_trace("read a frame from file buffer failed."); goto rtmp_destroy; } @@ -168,12 +168,12 @@ int main(int argc, char** argv) sound_format, sound_rate, sound_size, sound_type, 0, data, size, timestamp) != 0 ) { - srs_lib_trace("send audio raw data failed."); + srs_human_trace("send audio raw data failed."); goto rtmp_destroy; } - srs_lib_trace("sent packet: type=%s, time=%d, size=%d, codec=%d, rate=%d, sample=%d, channel=%d", - srs_type2string(SRS_RTMP_TYPE_AUDIO), timestamp, size, sound_format, sound_rate, sound_size, + srs_human_trace("sent packet: type=%s, time=%d, size=%d, codec=%d, rate=%d, sample=%d, channel=%d", + srs_human_flv_tag_type2string(SRS_RTMP_TYPE_AUDIO), timestamp, size, sound_format, sound_rate, sound_size, sound_type); // @remark, when use encode device, it not need to sleep. diff --git a/trunk/research/librtmp/srs_bandwidth_check.c b/trunk/research/librtmp/srs_bandwidth_check.c index 84666a4ab2..29fe1223f4 100644 --- a/trunk/research/librtmp/srs_bandwidth_check.c +++ b/trunk/research/librtmp/srs_bandwidth_check.c @@ -81,31 +81,31 @@ int main(int argc, char** argv) rtmp = srs_rtmp_create2(argv[1]); - srs_lib_trace("bandwidth check/test url: %s", argv[1]); + srs_human_trace("bandwidth check/test url: %s", argv[1]); if ((ret = srs_simple_handshake(rtmp)) != 0) { - srs_lib_trace("simple handshake failed."); + srs_human_trace("simple handshake failed."); goto rtmp_destroy; } - srs_lib_trace("simple handshake success"); + srs_human_trace("simple handshake success"); if ((ret = srs_connect_app2(rtmp, srs_server_ip, srs_server, srs_primary_authors, srs_version, &srs_id, &srs_pid)) != 0) { - srs_lib_trace("connect vhost/app failed."); + srs_human_trace("connect vhost/app failed."); goto rtmp_destroy; } - srs_lib_trace("connect vhost/app success"); + srs_human_trace("connect vhost/app success"); if ((ret = srs_bandwidth_check(rtmp, &start_time, &end_time, &play_kbps, &publish_kbps, &play_bytes, &publish_bytes, &play_duration, &publish_duration)) != 0 ) { - srs_lib_trace("bandwidth check/test failed."); + srs_human_trace("bandwidth check/test failed."); goto rtmp_destroy; } - srs_lib_trace("bandwidth check/test success"); + srs_human_trace("bandwidth check/test success"); - srs_lib_trace("\n%s, %s\n" + srs_human_trace("\n%s, %s\n" "%s, %s, srs_pid=%d, srs_id=%d\n" "duration: %dms(%d+%d)\n" "play: %dkbps\n" @@ -137,8 +137,8 @@ int main(int argc, char** argv) (int)(end_time - start_time), play_duration, publish_duration, play_kbps, publish_kbps); - srs_lib_trace(""); - srs_lib_trace("completed"); + srs_human_trace(""); + srs_human_trace("completed"); return ret; } diff --git a/trunk/research/librtmp/srs_detect_rtmp.c b/trunk/research/librtmp/srs_detect_rtmp.c index 083392f264..a90cd604c6 100644 --- a/trunk/research/librtmp/srs_detect_rtmp.c +++ b/trunk/research/librtmp/srs_detect_rtmp.c @@ -80,56 +80,56 @@ int main(int argc, char** argv) duration = atoi(argv[2]); timeout = atoi(argv[3]); - srs_lib_trace("rtmp url: %s", rtmp_url); - srs_lib_trace("duration: %ds, timeout:%ds", duration, timeout); + srs_human_trace("rtmp url: %s", rtmp_url); + srs_human_trace("duration: %ds, timeout:%ds", duration, timeout); if (duration <= 0 || timeout <= 0) { - srs_lib_trace("duration and timeout must be positive."); + srs_human_trace("duration and timeout must be positive."); exit(-2); } rtmp = srs_rtmp_create(rtmp_url); if ((ret = __srs_dns_resolve(rtmp)) != 0) { - srs_lib_trace("dns resolve failed. ret=%d", ret); + srs_human_trace("dns resolve failed. ret=%d", ret); goto rtmp_destroy; } - srs_lib_trace("dns resolve success"); + srs_human_trace("dns resolve success"); time_dns_resolve = srs_get_time_ms(); if ((ret = __srs_connect_server(rtmp)) != 0) { - srs_lib_trace("socket connect failed. ret=%d", ret); + srs_human_trace("socket connect failed. ret=%d", ret); goto rtmp_destroy; } - srs_lib_trace("socket connect success"); + srs_human_trace("socket connect success"); time_socket_connect = srs_get_time_ms(); if ((ret = __srs_do_simple_handshake(rtmp)) != 0) { - srs_lib_trace("do simple handshake failed. ret=%d", ret); + srs_human_trace("do simple handshake failed. ret=%d", ret); goto rtmp_destroy; } - srs_lib_trace("do simple handshake success"); + srs_human_trace("do simple handshake success"); if ((ret = srs_connect_app(rtmp)) != 0) { - srs_lib_trace("connect vhost/app failed. ret=%d", ret); + srs_human_trace("connect vhost/app failed. ret=%d", ret); goto rtmp_destroy; } - srs_lib_trace("connect vhost/app success"); + srs_human_trace("connect vhost/app success"); if ((ret = srs_play_stream(rtmp)) != 0) { - srs_lib_trace("play stream failed. ret=%d", ret); + srs_human_trace("play stream failed. ret=%d", ret); goto rtmp_destroy; } - srs_lib_trace("play stream success"); + srs_human_trace("play stream success"); time_play_stream = srs_get_time_ms(); for (;;) { if ((ret = srs_read_packet(rtmp, &type, ×tamp, &data, &size)) != 0) { - srs_lib_trace("read packet failed. ret=%d", ret); + srs_human_trace("read packet failed. ret=%d", ret); goto rtmp_destroy; } - srs_lib_trace("got packet: type=%s, time=%d, size=%d", - srs_type2string(type), timestamp, size); + srs_human_trace("got packet: type=%s, time=%d, size=%d", + srs_human_flv_tag_type2string(type), timestamp, size); if (SRS_RTMP_TYPE_VIDEO == type || SRS_RTMP_TYPE_AUDIO == type) { if (time_first_packet <= 0) { @@ -143,12 +143,12 @@ int main(int argc, char** argv) free(data); if (srs_get_time_ms() - time_startup > timeout * 1000) { - srs_lib_trace("timeout, terminate."); + srs_human_trace("timeout, terminate."); goto rtmp_destroy; } if ((timestamp - basetime) > duration * 1000) { - srs_lib_trace("duration exceed, terminate."); + srs_human_trace("duration exceed, terminate."); goto rtmp_destroy; } } @@ -197,8 +197,8 @@ int main(int argc, char** argv) "\"remark2\": \"if code is not 0, user must ignore all data\"" ); - srs_lib_trace(""); - srs_lib_trace("completed"); + srs_human_trace(""); + srs_human_trace("completed"); return ret; } diff --git a/trunk/research/librtmp/srs_flv_injecter.c b/trunk/research/librtmp/srs_flv_injecter.c index 8d78ce2c15..f37562cd86 100644 --- a/trunk/research/librtmp/srs_flv_injecter.c +++ b/trunk/research/librtmp/srs_flv_injecter.c @@ -76,9 +76,9 @@ int main(int argc, char** argv) tmp_file = (char*)malloc(tmp_file_size); snprintf(tmp_file, tmp_file_size, "%s.tmp", out_flv_file); - srs_lib_trace("input: %s", in_flv_file); - srs_lib_trace("output: %s", out_flv_file); - srs_lib_trace("tmp_file: %s", tmp_file); + srs_human_trace("input: %s", in_flv_file); + srs_human_trace("output: %s", out_flv_file); + srs_human_trace("tmp_file: %s", tmp_file); ret = process(in_flv_file, tmp_file, &ic, &oc); @@ -89,13 +89,13 @@ int main(int argc, char** argv) unlink(tmp_file); if (ret == ERROR_INJECTED) { ret = 0; - srs_lib_trace("file already injected."); + srs_human_trace("file already injected."); } else { - srs_lib_trace("error, remove tmp file."); + srs_human_trace("error, remove tmp file."); } } else { rename(tmp_file, out_flv_file); - srs_lib_trace("completed, rename to %s", out_flv_file); + srs_human_trace("completed, rename to %s", out_flv_file); } free(tmp_file); @@ -123,14 +123,14 @@ int process(const char* in_flv_file, const char* out_flv_file, srs_flv_t* pic, s if ((ic = srs_flv_open_read(in_flv_file)) == NULL) { ret = 2; - srs_lib_trace("open input flv file failed. ret=%d", ret); + srs_human_trace("open input flv file failed. ret=%d", ret); return ret; } *pic = ic; if ((oc = srs_flv_open_write(out_flv_file)) == NULL) { ret = 2; - srs_lib_trace("open output flv file failed. ret=%d", ret); + srs_human_trace("open output flv file failed. ret=%d", ret); return ret; } *poc = oc; @@ -164,13 +164,13 @@ int parse_metadata(char* data, int size, srs_amf0_t* pname, srs_amf0_t* pdata) *pname = srs_amf0_parse(data, size, &nparsed); if (*pname == NULL || nparsed >= size) { - srs_lib_trace("invalid amf0 name data."); + srs_human_trace("invalid amf0 name data."); return -1; } *pdata = srs_amf0_parse(data + nparsed, size - nparsed, &nparsed); if (*pdata == NULL || nparsed > size) { - srs_lib_trace("invalid amf0 value data"); + srs_human_trace("invalid amf0 value data"); return -1; } @@ -206,22 +206,22 @@ int build_keyframes(srs_flv_t ic, srs_amf0_t *pname, srs_amf0_t* pdata, srs_amf0 return ret; } - srs_lib_trace("build keyframe infos from flv"); + srs_human_trace("build keyframe infos from flv"); for (;;) { offset = srs_flv_tellg(ic); // tag header if ((ret = srs_flv_read_tag_header(ic, &type, &size, ×tamp)) != 0) { if (srs_flv_is_eof(ret)) { - srs_lib_trace("parse completed."); + srs_human_trace("parse completed."); return 0; } - srs_lib_trace("flv get packet failed. ret=%d", ret); + srs_human_trace("flv get packet failed. ret=%d", ret); return ret; } if (size <= 0) { - srs_lib_trace("invalid size=%d", size); + srs_human_trace("invalid size=%d", size); return ret; } @@ -343,20 +343,20 @@ int do_inject_flv(srs_flv_t ic, srs_flv_t oc, srs_amf0_t amf0_name, srs_amf0_t a free(data); } - srs_lib_trace("build keyframe infos from flv"); + srs_human_trace("build keyframe infos from flv"); for (;;) { // tag header if ((ret = srs_flv_read_tag_header(ic, &type, &size, ×tamp)) != 0) { if (srs_flv_is_eof(ret)) { - srs_lib_trace("parse completed."); + srs_human_trace("parse completed."); return 0; } - srs_lib_trace("flv get packet failed. ret=%d", ret); + srs_human_trace("flv get packet failed. ret=%d", ret); return ret; } if (size <= 0) { - srs_lib_trace("invalid size=%d", size); + srs_human_trace("invalid size=%d", size); break; } diff --git a/trunk/research/librtmp/srs_flv_parser.c b/trunk/research/librtmp/srs_flv_parser.c index 82a7470a07..2009b5d47e 100644 --- a/trunk/research/librtmp/srs_flv_parser.c +++ b/trunk/research/librtmp/srs_flv_parser.c @@ -61,11 +61,11 @@ int main(int argc, char** argv) } in_flv_file = argv[1]; - srs_lib_trace("input: %s", in_flv_file); + srs_human_trace("input: %s", in_flv_file); if ((flv = srs_flv_open_read(in_flv_file)) == NULL) { ret = 2; - srs_lib_trace("open flv file failed. ret=%d", ret); + srs_human_trace("open flv file failed. ret=%d", ret); return ret; } @@ -141,44 +141,44 @@ int parse_flv(srs_flv_t flv) return ret; } - srs_lib_trace("start parse flv"); + srs_human_trace("start parse flv"); for (;;) { offset = srs_flv_tellg(flv); // tag header if ((ret = srs_flv_read_tag_header(flv, &type, &size, ×tamp)) != 0) { if (srs_flv_is_eof(ret)) { - srs_lib_trace("parse completed."); + srs_human_trace("parse completed."); return 0; } - srs_lib_trace("flv get packet failed. ret=%d", ret); + srs_human_trace("flv get packet failed. ret=%d", ret); return ret; } if (size <= 0) { - srs_lib_trace("invalid size=%d", size); + srs_human_trace("invalid size=%d", size); break; } data = (char*)malloc(size); if ((ret = srs_flv_read_tag_data(flv, data, size)) == 0) { - if ((ret = srs_print_rtmp_packet(type, timestamp, data, size)) == 0) { + if ((ret = srs_human_print_rtmp_packet(type, timestamp, data, size)) == 0) { char hbuf[48]; char tbuf[48]; parse_bytes(data, size, hbuf, sizeof(hbuf), tbuf, sizeof(tbuf), 16); - srs_raw_trace("offset=%d, first and last 16 bytes:\n" + srs_human_raw("offset=%d, first and last 16 bytes:\n" "[+00, +15] %s\n[-15, EOF] %s\n", (int)offset, hbuf, tbuf); } else { - srs_lib_trace("print packet failed. ret=%d", ret); + srs_human_trace("print packet failed. ret=%d", ret); } } else { - srs_lib_trace("read flv failed. ret=%d", ret); + srs_human_trace("read flv failed. ret=%d", ret); } free(data); if (ret != 0) { - srs_lib_trace("parse failed, ret=%d", ret); + srs_human_trace("parse failed, ret=%d", ret); return ret; } } diff --git a/trunk/research/librtmp/srs_h264_raw_publish.c b/trunk/research/librtmp/srs_h264_raw_publish.c index da8b9a483f..5e7afd399b 100644 --- a/trunk/research/librtmp/srs_h264_raw_publish.c +++ b/trunk/research/librtmp/srs_h264_raw_publish.c @@ -44,7 +44,7 @@ int read_h264_frame(char* data, int size, char** pp, int* pnb_start_code, int fp // we search the h264 frame from the buffer which cached the h264 data. // please get h264 raw data from device, it always a encoded frame. if (!srs_h264_startswith_annexb(p, size - (p - data), pnb_start_code)) { - srs_lib_trace("h264 raw data invalid."); + srs_human_trace("h264 raw data invalid."); return -1; } @@ -63,7 +63,7 @@ int read_h264_frame(char* data, int size, char** pp, int* pnb_start_code, int fp *pp = p; *frame_size = p - *frame; if (*frame_size <= 0) { - srs_lib_trace("h264 raw data invalid."); + srs_human_trace("h264 raw data invalid."); return -1; } @@ -95,32 +95,32 @@ int main(int argc, char** argv) const char* raw_file = argv[1]; const char* rtmp_url = argv[2]; - srs_lib_trace("raw_file=%s, rtmp_url=%s", raw_file, rtmp_url); + srs_human_trace("raw_file=%s, rtmp_url=%s", raw_file, rtmp_url); // open file int raw_fd = open(raw_file, O_RDONLY); if (raw_fd < 0) { - srs_lib_trace("open h264 raw file %s failed.", raw_fd); + srs_human_trace("open h264 raw file %s failed.", raw_fd); goto rtmp_destroy; } off_t file_size = lseek(raw_fd, 0, SEEK_END); if (file_size <= 0) { - srs_lib_trace("h264 raw file %s empty.", raw_file); + srs_human_trace("h264 raw file %s empty.", raw_file); goto rtmp_destroy; } - srs_lib_trace("read entirely h264 raw file, size=%dKB", (int)(file_size / 1024)); + srs_human_trace("read entirely h264 raw file, size=%dKB", (int)(file_size / 1024)); char* h264_raw = (char*)malloc(file_size); if (!h264_raw) { - srs_lib_trace("alloc raw buffer failed for file %s.", raw_file); + srs_human_trace("alloc raw buffer failed for file %s.", raw_file); goto rtmp_destroy; } lseek(raw_fd, 0, SEEK_SET); ssize_t nb_read = 0; if ((nb_read = read(raw_fd, h264_raw, file_size)) != file_size) { - srs_lib_trace("buffer %s failed, expect=%dKB, actual=%dKB.", + srs_human_trace("buffer %s failed, expect=%dKB, actual=%dKB.", raw_file, (int)(file_size / 1024), (int)(nb_read / 1024)); goto rtmp_destroy; } @@ -129,22 +129,22 @@ int main(int argc, char** argv) srs_rtmp_t rtmp = srs_rtmp_create(rtmp_url); if (srs_simple_handshake(rtmp) != 0) { - srs_lib_trace("simple handshake failed."); + srs_human_trace("simple handshake failed."); goto rtmp_destroy; } - srs_lib_trace("simple handshake success"); + srs_human_trace("simple handshake success"); if (srs_connect_app(rtmp) != 0) { - srs_lib_trace("connect vhost/app failed."); + srs_human_trace("connect vhost/app failed."); goto rtmp_destroy; } - srs_lib_trace("connect vhost/app success"); + srs_human_trace("connect vhost/app success"); if (srs_publish_stream(rtmp) != 0) { - srs_lib_trace("publish stream failed."); + srs_human_trace("publish stream failed."); goto rtmp_destroy; } - srs_lib_trace("publish stream success"); + srs_human_trace("publish stream success"); u_int32_t dts = 0; u_int32_t pts = 0; @@ -161,7 +161,7 @@ int main(int argc, char** argv) if (read_h264_frame(h264_raw, file_size, &p, &nb_start_code, fps, &data, &size, &dts, &pts) < 0 ) { - srs_lib_trace("read a frame from file buffer failed."); + srs_human_trace("read a frame from file buffer failed."); goto rtmp_destroy; } @@ -169,13 +169,13 @@ int main(int argc, char** argv) int error = srs_h264_write_raw_frames(rtmp, data, size, dts, pts); if (error != 0) { if (srs_h264_is_dvbsp_error(error)) { - srs_lib_trace("ignore drop video error, code=%d", error); + srs_human_trace("ignore drop video error, code=%d", error); } else if (srs_h264_is_duplicated_sps_error(error)) { - srs_lib_trace("ignore duplicated sps, code=%d", error); + srs_human_trace("ignore duplicated sps, code=%d", error); } else if (srs_h264_is_duplicated_pps_error(error)) { - srs_lib_trace("ignore duplicated pps, code=%d", error); + srs_human_trace("ignore duplicated pps, code=%d", error); } else { - srs_lib_trace("send h264 raw data failed."); + srs_human_trace("send h264 raw data failed."); goto rtmp_destroy; } } @@ -183,14 +183,14 @@ int main(int argc, char** argv) // 5bits, 7.3.1 NAL unit syntax, // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. u_int8_t nut = (char)data[nb_start_code] & 0x1f; - srs_lib_trace("sent packet: type=%s, time=%d, size=%d, fps=%d, b[%d]=%#x(%s)", - srs_type2string(SRS_RTMP_TYPE_VIDEO), dts, size, fps, nb_start_code, (char)data[nb_start_code], + srs_human_trace("sent packet: type=%s, time=%d, size=%d, fps=%d, b[%d]=%#x(%s)", + srs_human_flv_tag_type2string(SRS_RTMP_TYPE_VIDEO), dts, size, fps, nb_start_code, (char)data[nb_start_code], (nut == 7? "SPS":(nut == 8? "PPS":(nut == 5? "I":(nut == 1? "P":"Unknown"))))); // @remark, when use encode device, it not need to sleep. usleep(1000 / fps * 1000); } - srs_lib_trace("h264 raw data completed"); + srs_human_trace("h264 raw data completed"); rtmp_destroy: srs_rtmp_destroy(rtmp); diff --git a/trunk/research/librtmp/srs_ingest_flv.c b/trunk/research/librtmp/srs_ingest_flv.c index 141354933d..d73a017975 100644 --- a/trunk/research/librtmp/srs_ingest_flv.c +++ b/trunk/research/librtmp/srs_ingest_flv.c @@ -89,19 +89,19 @@ int main(int argc, char** argv) } } - srs_lib_trace("input: %s", in_flv_file); - srs_lib_trace("output: %s", out_rtmp_url); + srs_human_trace("input: %s", in_flv_file); + srs_human_trace("output: %s", out_rtmp_url); if ((flv = srs_flv_open_read(in_flv_file)) == NULL) { ret = 2; - srs_lib_trace("open flv file failed. ret=%d", ret); + srs_human_trace("open flv file failed. ret=%d", ret); return ret; } ortmp = srs_rtmp_create(out_rtmp_url); ret = proxy(flv, ortmp); - srs_lib_trace("ingest flv to RTMP completed"); + srs_human_trace("ingest flv to RTMP completed"); srs_rtmp_destroy(ortmp); srs_flv_close(flv); @@ -118,20 +118,20 @@ int do_proxy(srs_flv_t flv, srs_rtmp_t ortmp, int64_t re, int32_t* pstarttime, u int size; char* data = NULL; - srs_lib_trace("start ingest flv to RTMP stream"); + srs_human_trace("start ingest flv to RTMP stream"); for (;;) { // tag header if ((ret = srs_flv_read_tag_header(flv, &type, &size, ptimestamp)) != 0) { if (srs_flv_is_eof(ret)) { - srs_lib_trace("parse completed."); + srs_human_trace("parse completed."); return 0; } - srs_lib_trace("flv get packet failed. ret=%d", ret); + srs_human_trace("flv get packet failed. ret=%d", ret); return ret; } if (size <= 0) { - srs_lib_trace("invalid size=%d", size); + srs_human_trace("invalid size=%d", size); break; } @@ -143,13 +143,13 @@ int do_proxy(srs_flv_t flv, srs_rtmp_t ortmp, int64_t re, int32_t* pstarttime, u u_int32_t timestamp = *ptimestamp; - if ((ret = srs_print_rtmp_packet(type, timestamp, data, size)) != 0) { - srs_lib_trace("print packet failed. ret=%d", ret); + if ((ret = srs_human_print_rtmp_packet(type, timestamp, data, size)) != 0) { + srs_human_trace("print packet failed. ret=%d", ret); return ret; } if ((ret = srs_write_packet(ortmp, type, *ptimestamp, data, size)) != 0) { - srs_lib_trace("irtmp get packet failed. ret=%d", ret); + srs_human_trace("irtmp get packet failed. ret=%d", ret); return ret; } @@ -192,22 +192,22 @@ int connect_oc(srs_rtmp_t ortmp) int ret = 0; if ((ret = srs_simple_handshake(ortmp)) != 0) { - srs_lib_trace("ortmp simple handshake failed. ret=%d", ret); + srs_human_trace("ortmp simple handshake failed. ret=%d", ret); return ret; } - srs_lib_trace("ortmp simple handshake success"); + srs_human_trace("ortmp simple handshake success"); if ((ret = srs_connect_app(ortmp)) != 0) { - srs_lib_trace("ortmp connect vhost/app failed. ret=%d", ret); + srs_human_trace("ortmp connect vhost/app failed. ret=%d", ret); return ret; } - srs_lib_trace("ortmp connect vhost/app success"); + srs_human_trace("ortmp connect vhost/app success"); if ((ret = srs_publish_stream(ortmp)) != 0) { - srs_lib_trace("ortmp publish stream failed. ret=%d", ret); + srs_human_trace("ortmp publish stream failed. ret=%d", ret); return ret; } - srs_lib_trace("ortmp publish stream success"); + srs_human_trace("ortmp publish stream success"); return ret; } @@ -219,16 +219,16 @@ int64_t re_create() // use the starttime to get the deviation int64_t deviation = re - tools_main_entrance_startup_time; - srs_lib_trace("deviation is %d ms, pulse is %d ms", (int)(deviation), (int)(RE_PULSE_MS)); + srs_human_trace("deviation is %d ms, pulse is %d ms", (int)(deviation), (int)(RE_PULSE_MS)); // so, we adjust time to max(0, deviation) // because the last pulse, we already sleeped int adjust = (int)(deviation); if (adjust > 0) { - srs_lib_trace("adjust re time for %d ms", adjust); + srs_human_trace("adjust re time for %d ms", adjust); re -= adjust; } else { - srs_lib_trace("no need to adjust re time"); + srs_human_trace("no need to adjust re time"); } return re; @@ -249,7 +249,7 @@ void re_cleanup(int64_t re, int32_t starttime, u_int32_t time) int64_t now = srs_get_time_ms(); int64_t diff = time - starttime - (now -re); if (diff > 0) { - srs_lib_trace("re_cleanup, diff=%d, start=%d, last=%d ms", + srs_human_trace("re_cleanup, diff=%d, start=%d, last=%d ms", (int)diff, starttime, time); usleep(diff * 1000); } diff --git a/trunk/research/librtmp/srs_ingest_rtmp.c b/trunk/research/librtmp/srs_ingest_rtmp.c index 25357ce933..6918d1307c 100644 --- a/trunk/research/librtmp/srs_ingest_rtmp.c +++ b/trunk/research/librtmp/srs_ingest_rtmp.c @@ -74,14 +74,14 @@ int main(int argc, char** argv) } } - srs_lib_trace("input: %s", in_rtmp_url); - srs_lib_trace("output: %s", out_rtmp_url); + srs_human_trace("input: %s", in_rtmp_url); + srs_human_trace("output: %s", out_rtmp_url); irtmp = srs_rtmp_create(in_rtmp_url); ortmp = srs_rtmp_create(out_rtmp_url); ret = proxy(irtmp, ortmp); - srs_lib_trace("proxy completed"); + srs_human_trace("proxy completed"); srs_rtmp_destroy(irtmp); srs_rtmp_destroy(ortmp); @@ -106,24 +106,24 @@ int proxy(srs_rtmp_t irtmp, srs_rtmp_t ortmp) return ret; } - srs_lib_trace("start proxy RTMP stream"); + srs_human_trace("start proxy RTMP stream"); for (;;) { if ((ret = srs_read_packet(irtmp, &type, ×tamp, &data, &size)) != 0) { - srs_lib_trace("irtmp get packet failed. ret=%d", ret); + srs_human_trace("irtmp get packet failed. ret=%d", ret); return ret; } - if ((ret = srs_print_rtmp_packet(type, timestamp, data, size)) != 0) { - srs_lib_trace("print packet failed. ret=%d", ret); + if ((ret = srs_human_print_rtmp_packet(type, timestamp, data, size)) != 0) { + srs_human_trace("print packet failed. ret=%d", ret); return ret; } if ((ret = srs_write_packet(ortmp, type, timestamp, data, size)) != 0) { - srs_lib_trace("irtmp get packet failed. ret=%d", ret); + srs_human_trace("irtmp get packet failed. ret=%d", ret); return ret; } - srs_lib_verbose("ortmp sent packet: type=%s, time=%d, size=%d", - srs_type2string(type), timestamp, size); + srs_human_verbose("ortmp sent packet: type=%s, time=%d, size=%d", + srs_human_flv_tag_type2string(type), timestamp, size); } return ret; @@ -134,22 +134,22 @@ int connect_ic(srs_rtmp_t irtmp) int ret = 0; if ((ret = srs_simple_handshake(irtmp)) != 0) { - srs_lib_trace("irtmp simple handshake failed. ret=%d", ret); + srs_human_trace("irtmp simple handshake failed. ret=%d", ret); return ret; } - srs_lib_trace("irtmp simple handshake success"); + srs_human_trace("irtmp simple handshake success"); if ((ret = srs_connect_app(irtmp)) != 0) { - srs_lib_trace("irtmp connect vhost/app failed. ret=%d", ret); + srs_human_trace("irtmp connect vhost/app failed. ret=%d", ret); return ret; } - srs_lib_trace("irtmp connect vhost/app success"); + srs_human_trace("irtmp connect vhost/app success"); if ((ret = srs_play_stream(irtmp)) != 0) { - srs_lib_trace("irtmp play stream failed. ret=%d", ret); + srs_human_trace("irtmp play stream failed. ret=%d", ret); return ret; } - srs_lib_trace("irtmp play stream success"); + srs_human_trace("irtmp play stream success"); return ret; } @@ -159,22 +159,22 @@ int connect_oc(srs_rtmp_t ortmp) int ret = 0; if ((ret = srs_simple_handshake(ortmp)) != 0) { - srs_lib_trace("ortmp simple handshake failed. ret=%d", ret); + srs_human_trace("ortmp simple handshake failed. ret=%d", ret); return ret; } - srs_lib_trace("ortmp simple handshake success"); + srs_human_trace("ortmp simple handshake success"); if ((ret = srs_connect_app(ortmp)) != 0) { - srs_lib_trace("ortmp connect vhost/app failed. ret=%d", ret); + srs_human_trace("ortmp connect vhost/app failed. ret=%d", ret); return ret; } - srs_lib_trace("ortmp connect vhost/app success"); + srs_human_trace("ortmp connect vhost/app success"); if ((ret = srs_publish_stream(ortmp)) != 0) { - srs_lib_trace("ortmp publish stream failed. ret=%d", ret); + srs_human_trace("ortmp publish stream failed. ret=%d", ret); return ret; } - srs_lib_trace("ortmp publish stream success"); + srs_human_trace("ortmp publish stream success"); return ret; } diff --git a/trunk/research/librtmp/srs_play.c b/trunk/research/librtmp/srs_play.c index 4039ed3c2d..50583364ed 100644 --- a/trunk/research/librtmp/srs_play.c +++ b/trunk/research/librtmp/srs_play.c @@ -44,26 +44,26 @@ int main(int argc, char** argv) exit(-1); } - srs_lib_trace("rtmp url: %s", argv[1]); + srs_human_trace("rtmp url: %s", argv[1]); srs_rtmp_t rtmp = srs_rtmp_create(argv[1]); if (srs_simple_handshake(rtmp) != 0) { - srs_lib_trace("simple handshake failed."); + srs_human_trace("simple handshake failed."); goto rtmp_destroy; } - srs_lib_trace("simple handshake success"); + srs_human_trace("simple handshake success"); if (srs_connect_app(rtmp) != 0) { - srs_lib_trace("connect vhost/app failed."); + srs_human_trace("connect vhost/app failed."); goto rtmp_destroy; } - srs_lib_trace("connect vhost/app success"); + srs_human_trace("connect vhost/app success"); if (srs_play_stream(rtmp) != 0) { - srs_lib_trace("play stream failed."); + srs_human_trace("play stream failed."); goto rtmp_destroy; } - srs_lib_trace("play stream success"); + srs_human_trace("play stream success"); for (;;) { int size; @@ -75,7 +75,7 @@ int main(int argc, char** argv) goto rtmp_destroy; } - if (srs_print_rtmp_packet(type, timestamp, data, size) != 0) { + if (srs_human_print_rtmp_packet(type, timestamp, data, size) != 0) { goto rtmp_destroy; } diff --git a/trunk/research/librtmp/srs_publish.c b/trunk/research/librtmp/srs_publish.c index beea776927..077fa20d22 100644 --- a/trunk/research/librtmp/srs_publish.c +++ b/trunk/research/librtmp/srs_publish.c @@ -47,31 +47,31 @@ int main(int argc, char** argv) // warn it . // @see: https://github.com/winlinvip/simple-rtmp-server/issues/126 - srs_lib_trace("\033[33m%s\033[0m", + srs_human_trace("\033[33m%s\033[0m", "[warning] it's only a sample to use librtmp. " "please never use it to publish and test forward/transcode/edge/HLS whatever. " "you should refer to this tool to use the srs-librtmp to publish the real media stream." "read about: https://github.com/winlinvip/simple-rtmp-server/issues/126"); - srs_lib_trace("rtmp url: %s", argv[1]); + srs_human_trace("rtmp url: %s", argv[1]); srs_rtmp_t rtmp = srs_rtmp_create(argv[1]); if (srs_simple_handshake(rtmp) != 0) { - srs_lib_trace("simple handshake failed."); + srs_human_trace("simple handshake failed."); goto rtmp_destroy; } - srs_lib_trace("simple handshake success"); + srs_human_trace("simple handshake success"); if (srs_connect_app(rtmp) != 0) { - srs_lib_trace("connect vhost/app failed."); + srs_human_trace("connect vhost/app failed."); goto rtmp_destroy; } - srs_lib_trace("connect vhost/app success"); + srs_human_trace("connect vhost/app success"); if (srs_publish_stream(rtmp) != 0) { - srs_lib_trace("publish stream failed."); + srs_human_trace("publish stream failed."); goto rtmp_destroy; } - srs_lib_trace("publish stream success"); + srs_human_trace("publish stream success"); u_int32_t timestamp = 0; for (;;) { @@ -84,8 +84,8 @@ int main(int argc, char** argv) if (srs_write_packet(rtmp, type, timestamp, data, size) != 0) { goto rtmp_destroy; } - srs_lib_trace("sent packet: type=%s, time=%d, size=%d", - srs_type2string(type), timestamp, size); + srs_human_trace("sent packet: type=%s, time=%d, size=%d", + srs_human_flv_tag_type2string(type), timestamp, size); usleep(40 * 1000); } diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index a3da335b39..c435151434 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -1432,7 +1432,7 @@ void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value) obj->append(any); } -char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize) +char* srs_human_amf0_print(srs_amf0_t amf0, char** pdata, int* psize) { if (!amf0) { return NULL; @@ -1443,7 +1443,7 @@ char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize) return any->human_print(pdata, psize); } -const char* srs_type2string(char type) +const char* srs_human_flv_tag_type2string(char type) { static const char* audio = "Audio"; static const char* video = "Video"; @@ -1460,7 +1460,7 @@ const char* srs_type2string(char type) return unknown; } -const char* srs_code_id2string(char codec_id) +const char* srs_human_flv_video_codec_id2string(char codec_id) { static const char* h263 = "H.263"; static const char* screen = "Screen"; @@ -1483,7 +1483,7 @@ const char* srs_code_id2string(char codec_id) return unknown; } -const char* srs_avc_packet2string(char avc_packet_type) +const char* srs_human_flv_video_avc_packet_type2string(char avc_packet_type) { static const char* sps_pps = "SpsPps"; static const char* nalu = "Nalu"; @@ -1500,7 +1500,7 @@ const char* srs_avc_packet2string(char avc_packet_type) return unknown; } -const char* srs_frame_type2string(char frame_type) +const char* srs_human_flv_video_frame_type2string(char frame_type) { static const char* keyframe = "I"; static const char* interframe = "P/B"; @@ -1521,7 +1521,7 @@ const char* srs_frame_type2string(char frame_type) return unknown; } -int srs_print_rtmp_packet(char type, u_int32_t timestamp, char* data, int size) +int srs_human_print_rtmp_packet(char type, u_int32_t timestamp, char* data, int size) { int ret = ERROR_SUCCESS; @@ -1531,18 +1531,18 @@ int srs_print_rtmp_packet(char type, u_int32_t timestamp, char* data, int size) } if (type == SRS_RTMP_TYPE_VIDEO) { - srs_lib_trace("Video packet type=%s, dts=%d, pts=%d, size=%d, %s(%s,%s)", - srs_type2string(type), timestamp, pts, size, - srs_code_id2string(srs_get_codec_id(data, size)), - srs_avc_packet2string(srs_get_avc_packet_type(data, size)), - srs_frame_type2string(srs_get_frame_type(data, size)) + srs_human_trace("Video packet type=%s, dts=%d, pts=%d, size=%d, %s(%s,%s)", + srs_human_flv_tag_type2string(type), timestamp, pts, size, + srs_human_flv_video_codec_id2string(srs_get_codec_id(data, size)), + srs_human_flv_video_avc_packet_type2string(srs_get_avc_packet_type(data, size)), + srs_human_flv_video_frame_type2string(srs_get_frame_type(data, size)) ); } else if (type == SRS_RTMP_TYPE_AUDIO) { - srs_lib_trace("Audio packet type=%s, dts=%d, pts=%d, size=%d", - srs_type2string(type), timestamp, pts, size); + srs_human_trace("Audio packet type=%s, dts=%d, pts=%d, size=%d", + srs_human_flv_tag_type2string(type), timestamp, pts, size); } else if (type == SRS_RTMP_TYPE_SCRIPT) { - srs_lib_verbose("Data packet type=%s, time=%d, size=%d", - srs_type2string(type), timestamp, size); + srs_human_verbose("Data packet type=%s, time=%d, size=%d", + srs_human_flv_tag_type2string(type), timestamp, size); int nparsed = 0; while (nparsed < size) { int nb_parsed_this = 0; @@ -1554,18 +1554,18 @@ int srs_print_rtmp_packet(char type, u_int32_t timestamp, char* data, int size) nparsed += nb_parsed_this; char* amf0_str = NULL; - srs_raw_trace("%s", srs_amf0_human_print(amf0, &amf0_str, NULL)); + srs_human_raw("%s", srs_human_amf0_print(amf0, &amf0_str, NULL)); srs_amf0_free_bytes(amf0_str); } } else { - srs_lib_trace("Unknown packet type=%s, dts=%d, pts=%d, size=%d", - srs_type2string(type), timestamp, pts, size); + srs_human_trace("Unknown packet type=%s, dts=%d, pts=%d, size=%d", + srs_human_flv_tag_type2string(type), timestamp, pts, size); } return ret; } -const char* srs_format_time() +const char* srs_human_format_time() { struct timeval tv; static char buf[23]; diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index 89b5ac9fc8..4b47f4445d 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -504,7 +504,7 @@ extern void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value); * user must use srs_amf0_free_bytes to free it. * @return return the *pdata for print. NULL to ignore. */ -extern char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize); +extern char* srs_human_amf0_print(srs_amf0_t amf0, char** pdata, int* psize); /** * convert the flv tag type to string. * SRS_RTMP_TYPE_AUDIO to "Audio" @@ -514,7 +514,7 @@ extern char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize); * @remark user never free the return char*, * it's static shared const string. */ -extern const char* srs_type2string(char type); +extern const char* srs_human_flv_tag_type2string(char type); /** * get the codec id string. @@ -528,7 +528,7 @@ extern const char* srs_type2string(char type); * @remark user never free the return char*, * it's static shared const string. */ -extern const char* srs_code_id2string(char codec_id); +extern const char* srs_human_flv_video_codec_id2string(char codec_id); /** * get the avc packet type string. @@ -539,7 +539,7 @@ extern const char* srs_code_id2string(char codec_id); * @remark user never free the return char*, * it's static shared const string. */ -extern const char* srs_avc_packet2string(char avc_packet_type); +extern const char* srs_human_flv_video_avc_packet_type2string(char avc_packet_type); /** * get the frame type string. @@ -552,20 +552,20 @@ extern const char* srs_avc_packet2string(char avc_packet_type); * @remark user never free the return char*, * it's static shared const string. */ -extern const char* srs_frame_type2string(char frame_type); +extern const char* srs_human_flv_video_frame_type2string(char frame_type); /** -* print the rtmp packet, use srs_lib_trace/srs_lib_verbose for packet, -* and use srs_raw_trace for script data body. +* print the rtmp packet, use srs_human_trace/srs_human_verbose for packet, +* and use srs_human_raw for script data body. * @return an error code for parse the timetstamp to dts and pts. */ -extern int srs_print_rtmp_packet(char type, u_int32_t timestamp, char* data, int size); +extern int srs_human_print_rtmp_packet(char type, u_int32_t timestamp, char* data, int size); // log to console, for use srs-librtmp application. -extern const char* srs_format_time(); -#define srs_lib_trace(msg, ...) printf("[%s] ", srs_format_time());printf(msg, ##__VA_ARGS__);printf("\n") -#define srs_lib_verbose(msg, ...) printf("[%s] ", srs_format_time());printf(msg, ##__VA_ARGS__);printf("\n") -#define srs_raw_trace(msg, ...) printf(msg, ##__VA_ARGS__) +extern const char* srs_human_format_time(); +#define srs_human_trace(msg, ...) printf("[%s] ", srs_human_format_time());printf(msg, ##__VA_ARGS__);printf("\n") +#define srs_human_verbose(msg, ...) printf("[%s] ", srs_human_format_time());printf(msg, ##__VA_ARGS__);printf("\n") +#define srs_human_raw(msg, ...) printf(msg, ##__VA_ARGS__) /************************************************************* ************************************************************** From 10bc5399ecbd62d21d11d73d86a1cd90c2ea2c55 Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 21 Nov 2014 14:09:17 +0800 Subject: [PATCH 200/800] refine librtmp api, add prefix utils for utilities. --- trunk/research/librtmp/srs_detect_rtmp.c | 18 +++++++++--------- trunk/research/librtmp/srs_ingest_flv.c | 8 ++++---- trunk/src/libs/srs_librtmp.cpp | 22 +++++++++++----------- trunk/src/libs/srs_librtmp.hpp | 19 ++++++++++++------- 4 files changed, 36 insertions(+), 31 deletions(-) diff --git a/trunk/research/librtmp/srs_detect_rtmp.c b/trunk/research/librtmp/srs_detect_rtmp.c index a90cd604c6..3752153853 100644 --- a/trunk/research/librtmp/srs_detect_rtmp.c +++ b/trunk/research/librtmp/srs_detect_rtmp.c @@ -35,7 +35,7 @@ int main(int argc, char** argv) srs_rtmp_t rtmp; // time - int64_t time_startup = srs_get_time_ms(); + int64_t time_startup = srs_utils_get_time_ms(); int64_t time_dns_resolve = 0; int64_t time_socket_connect = 0; int64_t time_play_stream = 0; @@ -95,14 +95,14 @@ int main(int argc, char** argv) goto rtmp_destroy; } srs_human_trace("dns resolve success"); - time_dns_resolve = srs_get_time_ms(); + time_dns_resolve = srs_utils_get_time_ms(); if ((ret = __srs_connect_server(rtmp)) != 0) { srs_human_trace("socket connect failed. ret=%d", ret); goto rtmp_destroy; } srs_human_trace("socket connect success"); - time_socket_connect = srs_get_time_ms(); + time_socket_connect = srs_utils_get_time_ms(); if ((ret = __srs_do_simple_handshake(rtmp)) != 0) { srs_human_trace("do simple handshake failed. ret=%d", ret); @@ -121,7 +121,7 @@ int main(int argc, char** argv) goto rtmp_destroy; } srs_human_trace("play stream success"); - time_play_stream = srs_get_time_ms(); + time_play_stream = srs_utils_get_time_ms(); for (;;) { if ((ret = srs_read_packet(rtmp, &type, ×tamp, &data, &size)) != 0) { @@ -133,7 +133,7 @@ int main(int argc, char** argv) if (SRS_RTMP_TYPE_VIDEO == type || SRS_RTMP_TYPE_AUDIO == type) { if (time_first_packet <= 0) { - time_first_packet = srs_get_time_ms(); + time_first_packet = srs_utils_get_time_ms(); } if (basetime <= 0) { basetime = timestamp; @@ -142,7 +142,7 @@ int main(int argc, char** argv) free(data); - if (srs_get_time_ms() - time_startup > timeout * 1000) { + if (srs_utils_get_time_ms() - time_startup > timeout * 1000) { srs_human_trace("timeout, terminate."); goto rtmp_destroy; } @@ -154,11 +154,11 @@ int main(int argc, char** argv) } rtmp_destroy: - bytes_nsend = srs_get_nsend_bytes(rtmp); - bytes_nrecv = srs_get_nrecv_bytes(rtmp); + bytes_nsend = srs_utils_get_send_bytes(rtmp); + bytes_nrecv = srs_utils_get_recv_bytes(rtmp); srs_rtmp_destroy(rtmp); - time_cleanup = srs_get_time_ms(); + time_cleanup = srs_utils_get_time_ms(); time_duration = (int)(time_cleanup - time_startup); // print result to stderr. diff --git a/trunk/research/librtmp/srs_ingest_flv.c b/trunk/research/librtmp/srs_ingest_flv.c index d73a017975..a0a934816d 100644 --- a/trunk/research/librtmp/srs_ingest_flv.c +++ b/trunk/research/librtmp/srs_ingest_flv.c @@ -48,7 +48,7 @@ int main(int argc, char** argv) int ret = 0; // main function - tools_main_entrance_startup_time = srs_get_time_ms(); + tools_main_entrance_startup_time = srs_utils_get_time_ms(); // user option parse index. int opt = 0; @@ -215,7 +215,7 @@ int connect_oc(srs_rtmp_t ortmp) int64_t re_create() { // if not very precise, we can directly use this as re. - int64_t re = srs_get_time_ms(); + int64_t re = srs_utils_get_time_ms(); // use the starttime to get the deviation int64_t deviation = re - tools_main_entrance_startup_time; @@ -236,7 +236,7 @@ int64_t re_create() void re_update(int64_t re, int32_t starttime, u_int32_t time) { // send by pulse algorithm. - int64_t now = srs_get_time_ms(); + int64_t now = srs_utils_get_time_ms(); int64_t diff = time - starttime - (now -re); if (diff > RE_PULSE_MS) { usleep(diff * 1000); @@ -246,7 +246,7 @@ void re_cleanup(int64_t re, int32_t starttime, u_int32_t time) { // for the last pulse, always sleep. // for the virtual live encoder long time publishing. - int64_t now = srs_get_time_ms(); + int64_t now = srs_utils_get_time_ms(); int64_t diff = time - starttime - (now -re); if (diff > 0) { srs_human_trace("re_cleanup, diff=%d, start=%d, last=%d ms", diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index c435151434..73e575c40e 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -858,27 +858,27 @@ int srs_version_revision() return VERSION_REVISION; } -int64_t srs_get_time_ms() +int64_t srs_utils_get_time_ms() { srs_update_system_time_ms(); return srs_get_system_time_ms(); } -int64_t srs_get_nsend_bytes(srs_rtmp_t rtmp) +int64_t srs_utils_get_send_bytes(srs_rtmp_t rtmp) { srs_assert(rtmp != NULL); Context* context = (Context*)rtmp; return context->rtmp->get_send_bytes(); } -int64_t srs_get_nrecv_bytes(srs_rtmp_t rtmp) +int64_t srs_utils_get_recv_bytes(srs_rtmp_t rtmp) { srs_assert(rtmp != NULL); Context* context = (Context*)rtmp; return context->rtmp->get_recv_bytes(); } -int srs_parse_timestamp( +int srs_utils_parse_timestamp( u_int32_t time, char type, char* data, int size, u_int32_t* ppts ) { @@ -919,7 +919,7 @@ int srs_parse_timestamp( return ret; } -char srs_get_codec_id(char* data, int size) +char srs_utils_get_flv_video_codec_id(char* data, int size) { if (size < 1) { return 0; @@ -931,7 +931,7 @@ char srs_get_codec_id(char* data, int size) return codec_id; } -char srs_get_avc_packet_type(char* data, int size) +char srs_utils_get_flv_video_avc_packet_type(char* data, int size) { if (size < 2) { return -1; @@ -950,7 +950,7 @@ char srs_get_avc_packet_type(char* data, int size) return avc_packet_type; } -char srs_get_frame_type(char* data, int size) +char srs_utils_get_flv_video_frame_type(char* data, int size) { if (size < 1) { return -1; @@ -1526,16 +1526,16 @@ int srs_human_print_rtmp_packet(char type, u_int32_t timestamp, char* data, int int ret = ERROR_SUCCESS; u_int32_t pts; - if (srs_parse_timestamp(timestamp, type, data, size, &pts) != 0) { + if (srs_utils_parse_timestamp(timestamp, type, data, size, &pts) != 0) { return ret; } if (type == SRS_RTMP_TYPE_VIDEO) { srs_human_trace("Video packet type=%s, dts=%d, pts=%d, size=%d, %s(%s,%s)", srs_human_flv_tag_type2string(type), timestamp, pts, size, - srs_human_flv_video_codec_id2string(srs_get_codec_id(data, size)), - srs_human_flv_video_avc_packet_type2string(srs_get_avc_packet_type(data, size)), - srs_human_flv_video_frame_type2string(srs_get_frame_type(data, size)) + srs_human_flv_video_codec_id2string(srs_utils_get_flv_video_codec_id(data, size)), + srs_human_flv_video_avc_packet_type2string(srs_utils_get_flv_video_avc_packet_type(data, size)), + srs_human_flv_video_frame_type2string(srs_utils_get_flv_video_frame_type(data, size)) ); } else if (type == SRS_RTMP_TYPE_AUDIO) { srs_human_trace("Audio packet type=%s, dts=%d, pts=%d, size=%d", diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index 4b47f4445d..c02b5f281a 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -253,6 +253,11 @@ extern int srs_write_packet(srs_rtmp_t rtmp, char type, u_int32_t timestamp, char* data, int size ); +/************************************************************* +************************************************************** +* version apis +************************************************************** +*************************************************************/ // get protocol stack version extern int srs_version_major(); extern int srs_version_minor(); @@ -267,17 +272,17 @@ extern int srs_version_revision(); * get the current system time in ms. * use gettimeofday() to get system time. */ -extern int64_t srs_get_time_ms(); +extern int64_t srs_utils_get_time_ms(); /** * get the send bytes. */ -extern int64_t srs_get_nsend_bytes(srs_rtmp_t rtmp); +extern int64_t srs_utils_get_send_bytes(srs_rtmp_t rtmp); /** * get the recv bytes. */ -extern int64_t srs_get_nrecv_bytes(srs_rtmp_t rtmp); +extern int64_t srs_utils_get_recv_bytes(srs_rtmp_t rtmp); /** * parse the dts and pts by time in header and data in tag, @@ -294,7 +299,7 @@ extern int64_t srs_get_nrecv_bytes(srs_rtmp_t rtmp); * @remark, the pts=dts for audio or data. * @remark, video only support h.264. */ -extern int srs_parse_timestamp( +extern int srs_utils_parse_timestamp( u_int32_t time, char type, char* data, int size, u_int32_t* ppts ); @@ -310,7 +315,7 @@ extern int srs_parse_timestamp( * 7 = AVC * @return the code id. 0 for error. */ -extern char srs_get_codec_id(char* data, int size); +extern char srs_utils_get_flv_video_codec_id(char* data, int size); /** * get the AVCPacketType of video tag. @@ -321,7 +326,7 @@ extern char srs_get_codec_id(char* data, int size); * not required or supported) * @return the avc packet type. -1(0xff) for error. */ -extern char srs_get_avc_packet_type(char* data, int size); +extern char srs_utils_get_flv_video_avc_packet_type(char* data, int size); /** * get the FrameType of video tag. @@ -333,7 +338,7 @@ extern char srs_get_avc_packet_type(char* data, int size); * 5 = video info/command frame * @return the frame type. 0 for error. */ -extern char srs_get_frame_type(char* data, int size); +extern char srs_utils_get_flv_video_frame_type(char* data, int size); /************************************************************* ************************************************************** From 180106ce708e9c48610bcb3ab22e65babb8418af Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 21 Nov 2014 14:11:53 +0800 Subject: [PATCH 201/800] refine librtmp, move the utils and human to end section. --- trunk/src/libs/srs_librtmp.cpp | 704 ++++++++++++++++----------------- trunk/src/libs/srs_librtmp.hpp | 302 +++++++------- 2 files changed, 503 insertions(+), 503 deletions(-) diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index 73e575c40e..ca4e8ce321 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -858,117 +858,6 @@ int srs_version_revision() return VERSION_REVISION; } -int64_t srs_utils_get_time_ms() -{ - srs_update_system_time_ms(); - return srs_get_system_time_ms(); -} - -int64_t srs_utils_get_send_bytes(srs_rtmp_t rtmp) -{ - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; - return context->rtmp->get_send_bytes(); -} - -int64_t srs_utils_get_recv_bytes(srs_rtmp_t rtmp) -{ - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; - return context->rtmp->get_recv_bytes(); -} - -int srs_utils_parse_timestamp( - u_int32_t time, char type, char* data, int size, - u_int32_t* ppts -) { - int ret = ERROR_SUCCESS; - - if (type != SRS_RTMP_TYPE_VIDEO) { - *ppts = time; - return ret; - } - - if (!SrsFlvCodec::video_is_h264(data, size)) { - return ERROR_FLV_INVALID_VIDEO_TAG; - } - - if (SrsFlvCodec::video_is_sequence_header(data, size)) { - *ppts = time; - return ret; - } - - // 1bytes, frame type and codec id. - // 1bytes, avc packet type. - // 3bytes, cts, composition time, - // pts = dts + cts, or - // cts = pts - dts. - if (size < 5) { - return ERROR_FLV_INVALID_VIDEO_TAG; - } - - u_int32_t cts = 0; - char* p = data + 2; - char* pp = (char*)&cts; - pp[2] = *p++; - pp[1] = *p++; - pp[0] = *p++; - - *ppts = time + cts; - - return ret; -} - -char srs_utils_get_flv_video_codec_id(char* data, int size) -{ - if (size < 1) { - return 0; - } - - char codec_id = data[0]; - codec_id = codec_id & 0x0F; - - return codec_id; -} - -char srs_utils_get_flv_video_avc_packet_type(char* data, int size) -{ - if (size < 2) { - return -1; - } - - if (!SrsFlvCodec::video_is_h264(data, size)) { - return -1; - } - - u_int8_t avc_packet_type = data[1]; - - if (avc_packet_type > 2) { - return -1; - } - - return avc_packet_type; -} - -char srs_utils_get_flv_video_frame_type(char* data, int size) -{ - if (size < 1) { - return -1; - } - - if (!SrsFlvCodec::video_is_h264(data, size)) { - return -1; - } - - u_int8_t frame_type = data[0]; - frame_type = (frame_type >> 4) & 0x0f; - if (frame_type < 1 || frame_type > 5) { - return -1; - } - - return frame_type; -} - struct FlvContext { SrsFileReader reader; @@ -1432,264 +1321,101 @@ void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value) obj->append(any); } -char* srs_human_amf0_print(srs_amf0_t amf0, char** pdata, int* psize) -{ - if (!amf0) { - return NULL; +/** +* write audio raw frame to SRS. +*/ +int srs_audio_write_raw_frame(srs_rtmp_t rtmp, + char sound_format, char sound_rate, char sound_size, char sound_type, + char aac_packet_type, char* frame, int frame_size, u_int32_t timestamp +) { + Context* context = (Context*)rtmp; + srs_assert(context); + + // TODO: FIXME: for aac, must send the sequence header first. + + // for audio frame, there is 1 or 2 bytes header: + // 1bytes, SoundFormat|SoundRate|SoundSize|SoundType + // 1bytes, AACPacketType for SoundFormat == 10 + int size = frame_size + 1; + if (aac_packet_type == SrsCodecAudioAAC) { + size += 1; } + char* data = new char[size]; + char* p = data; - SrsAmf0Any* any = (SrsAmf0Any*)amf0; + u_int8_t audio_header = sound_type & 0x01; + audio_header |= (sound_size << 1) & 0x02; + audio_header |= (sound_rate << 2) & 0x0c; + audio_header |= (sound_format << 4) & 0xf0; - return any->human_print(pdata, psize); -} - -const char* srs_human_flv_tag_type2string(char type) -{ - static const char* audio = "Audio"; - static const char* video = "Video"; - static const char* data = "Data"; - static const char* unknown = "Unknown"; + *p++ = audio_header; - switch (type) { - case SRS_RTMP_TYPE_AUDIO: return audio; - case SRS_RTMP_TYPE_VIDEO: return video; - case SRS_RTMP_TYPE_SCRIPT: return data; - default: return unknown; + if (aac_packet_type == SrsCodecAudioAAC) { + *p++ = aac_packet_type; } - return unknown; + memcpy(p, frame, frame_size); + + return srs_write_packet(context, SRS_RTMP_TYPE_AUDIO, timestamp, data, size); } -const char* srs_human_flv_video_codec_id2string(char codec_id) -{ - static const char* h263 = "H.263"; - static const char* screen = "Screen"; - static const char* vp6 = "VP6"; - static const char* vp6_alpha = "VP6Alpha"; - static const char* screen2 = "Screen2"; - static const char* h264 = "H.264"; - static const char* unknown = "Unknown"; +/** +* write h264 packet, with rtmp header. +* @param frame_type, SrsCodecVideoAVCFrameKeyFrame or SrsCodecVideoAVCFrameInterFrame. +* @param avc_packet_type, SrsCodecVideoAVCTypeSequenceHeader or SrsCodecVideoAVCTypeNALU. +* @param h264_raw_data the h.264 raw data, user must free it. +*/ +int __srs_write_h264_packet(Context* context, + int8_t frame_type, int8_t avc_packet_type, + char* h264_raw_data, int h264_raw_size, u_int32_t dts, u_int32_t pts +) { + // the timestamp in rtmp message header is dts. + u_int32_t timestamp = dts; - switch (codec_id) { - case 2: return h263; - case 3: return screen; - case 4: return vp6; - case 5: return vp6_alpha; - case 6: return screen2; - case 7: return h264; - default: return unknown; - } + // for h264 in RTMP video payload, there is 5bytes header: + // 1bytes, FrameType | CodecID + // 1bytes, AVCPacketType + // 3bytes, CompositionTime, the cts. + // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78 + int size = h264_raw_size + 5; + char* data = new char[size]; + char* p = data; - return unknown; -} + // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78 + // Frame Type, Type of video frame. + // CodecID, Codec Identifier. + // set the rtmp header + *p++ = (frame_type << 4) | SrsCodecVideoAVC; + + // AVCPacketType + *p++ = avc_packet_type; -const char* srs_human_flv_video_avc_packet_type2string(char avc_packet_type) -{ - static const char* sps_pps = "SpsPps"; - static const char* nalu = "Nalu"; - static const char* sps_pps_end = "SpsPpsEnd"; - static const char* unknown = "Unknown"; + // CompositionTime + // pts = dts + cts, or + // cts = pts - dts. + // where cts is the header in rtmp video packet payload header. + u_int32_t cts = pts - dts; + char* pp = (char*)&cts; + *p++ = pp[2]; + *p++ = pp[1]; + *p++ = pp[0]; - switch (avc_packet_type) { - case 0: return sps_pps; - case 1: return nalu; - case 2: return sps_pps_end; - default: return unknown; - } + // h.264 raw data. + memcpy(p, h264_raw_data, h264_raw_size); - return unknown; + return srs_write_packet(context, SRS_RTMP_TYPE_VIDEO, timestamp, data, size); } -const char* srs_human_flv_video_frame_type2string(char frame_type) +/** +* write the h264 sps/pps in context over RTMP. +*/ +int __srs_write_h264_sps_pps(Context* context, u_int32_t dts, u_int32_t pts) { - static const char* keyframe = "I"; - static const char* interframe = "P/B"; - static const char* disposable_interframe = "DI"; - static const char* generated_keyframe = "GI"; - static const char* video_infoframe = "VI"; - static const char* unknown = "Unknown"; + int ret = ERROR_SUCCESS; - switch (frame_type) { - case 1: return keyframe; - case 2: return interframe; - case 3: return disposable_interframe; - case 4: return generated_keyframe; - case 5: return video_infoframe; - default: return unknown; - } - - return unknown; -} - -int srs_human_print_rtmp_packet(char type, u_int32_t timestamp, char* data, int size) -{ - int ret = ERROR_SUCCESS; - - u_int32_t pts; - if (srs_utils_parse_timestamp(timestamp, type, data, size, &pts) != 0) { - return ret; - } - - if (type == SRS_RTMP_TYPE_VIDEO) { - srs_human_trace("Video packet type=%s, dts=%d, pts=%d, size=%d, %s(%s,%s)", - srs_human_flv_tag_type2string(type), timestamp, pts, size, - srs_human_flv_video_codec_id2string(srs_utils_get_flv_video_codec_id(data, size)), - srs_human_flv_video_avc_packet_type2string(srs_utils_get_flv_video_avc_packet_type(data, size)), - srs_human_flv_video_frame_type2string(srs_utils_get_flv_video_frame_type(data, size)) - ); - } else if (type == SRS_RTMP_TYPE_AUDIO) { - srs_human_trace("Audio packet type=%s, dts=%d, pts=%d, size=%d", - srs_human_flv_tag_type2string(type), timestamp, pts, size); - } else if (type == SRS_RTMP_TYPE_SCRIPT) { - srs_human_verbose("Data packet type=%s, time=%d, size=%d", - srs_human_flv_tag_type2string(type), timestamp, size); - int nparsed = 0; - while (nparsed < size) { - int nb_parsed_this = 0; - srs_amf0_t amf0 = srs_amf0_parse(data + nparsed, size - nparsed, &nb_parsed_this); - if (amf0 == NULL) { - break; - } - - nparsed += nb_parsed_this; - - char* amf0_str = NULL; - srs_human_raw("%s", srs_human_amf0_print(amf0, &amf0_str, NULL)); - srs_amf0_free_bytes(amf0_str); - } - } else { - srs_human_trace("Unknown packet type=%s, dts=%d, pts=%d, size=%d", - srs_human_flv_tag_type2string(type), timestamp, pts, size); - } - - return ret; -} - -const char* srs_human_format_time() -{ - struct timeval tv; - static char buf[23]; - - memset(buf, 0, sizeof(buf)); - - // clock time - if (gettimeofday(&tv, NULL) == -1) { - return buf; - } - - // to calendar time - struct tm* tm; - if ((tm = localtime((const time_t*)&tv.tv_sec)) == NULL) { - return buf; - } - - snprintf(buf, sizeof(buf), - "%d-%02d-%02d %02d:%02d:%02d.%03d", - 1900 + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec, - (int)(tv.tv_usec / 1000)); - - // for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 - buf[sizeof(buf) - 1] = 0; - - return buf; -} - -/** -* write audio raw frame to SRS. -*/ -int srs_audio_write_raw_frame(srs_rtmp_t rtmp, - char sound_format, char sound_rate, char sound_size, char sound_type, - char aac_packet_type, char* frame, int frame_size, u_int32_t timestamp -) { - Context* context = (Context*)rtmp; - srs_assert(context); - - // TODO: FIXME: for aac, must send the sequence header first. - - // for audio frame, there is 1 or 2 bytes header: - // 1bytes, SoundFormat|SoundRate|SoundSize|SoundType - // 1bytes, AACPacketType for SoundFormat == 10 - int size = frame_size + 1; - if (aac_packet_type == SrsCodecAudioAAC) { - size += 1; - } - char* data = new char[size]; - char* p = data; - - u_int8_t audio_header = sound_type & 0x01; - audio_header |= (sound_size << 1) & 0x02; - audio_header |= (sound_rate << 2) & 0x0c; - audio_header |= (sound_format << 4) & 0xf0; - - *p++ = audio_header; - - if (aac_packet_type == SrsCodecAudioAAC) { - *p++ = aac_packet_type; - } - - memcpy(p, frame, frame_size); - - return srs_write_packet(context, SRS_RTMP_TYPE_AUDIO, timestamp, data, size); -} - -/** -* write h264 packet, with rtmp header. -* @param frame_type, SrsCodecVideoAVCFrameKeyFrame or SrsCodecVideoAVCFrameInterFrame. -* @param avc_packet_type, SrsCodecVideoAVCTypeSequenceHeader or SrsCodecVideoAVCTypeNALU. -* @param h264_raw_data the h.264 raw data, user must free it. -*/ -int __srs_write_h264_packet(Context* context, - int8_t frame_type, int8_t avc_packet_type, - char* h264_raw_data, int h264_raw_size, u_int32_t dts, u_int32_t pts -) { - // the timestamp in rtmp message header is dts. - u_int32_t timestamp = dts; - - // for h264 in RTMP video payload, there is 5bytes header: - // 1bytes, FrameType | CodecID - // 1bytes, AVCPacketType - // 3bytes, CompositionTime, the cts. - // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78 - int size = h264_raw_size + 5; - char* data = new char[size]; - char* p = data; - - // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78 - // Frame Type, Type of video frame. - // CodecID, Codec Identifier. - // set the rtmp header - *p++ = (frame_type << 4) | SrsCodecVideoAVC; - - // AVCPacketType - *p++ = avc_packet_type; - - // CompositionTime - // pts = dts + cts, or - // cts = pts - dts. - // where cts is the header in rtmp video packet payload header. - u_int32_t cts = pts - dts; - char* pp = (char*)&cts; - *p++ = pp[2]; - *p++ = pp[1]; - *p++ = pp[0]; - - // h.264 raw data. - memcpy(p, h264_raw_data, h264_raw_size); - - return srs_write_packet(context, SRS_RTMP_TYPE_VIDEO, timestamp, data, size); -} - -/** -* write the h264 sps/pps in context over RTMP. -*/ -int __srs_write_h264_sps_pps(Context* context, u_int32_t dts, u_int32_t pts) -{ - int ret = ERROR_SUCCESS; - - // only send when both sps and pps changed. - if (!context->h264_sps_changed || !context->h264_pps_changed) { - return ret; + // only send when both sps and pps changed. + if (!context->h264_sps_changed || !context->h264_pps_changed) { + return ret; } // 5bytes sps/pps header: @@ -1986,6 +1712,280 @@ int srs_h264_startswith_annexb(char* h264_raw_data, int h264_raw_size, int* pnb_ return srs_avc_startswith_annexb(&stream, pnb_start_code); } +int64_t srs_utils_get_time_ms() +{ + srs_update_system_time_ms(); + return srs_get_system_time_ms(); +} + +int64_t srs_utils_get_send_bytes(srs_rtmp_t rtmp) +{ + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + return context->rtmp->get_send_bytes(); +} + +int64_t srs_utils_get_recv_bytes(srs_rtmp_t rtmp) +{ + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + return context->rtmp->get_recv_bytes(); +} + +int srs_utils_parse_timestamp( + u_int32_t time, char type, char* data, int size, + u_int32_t* ppts +) { + int ret = ERROR_SUCCESS; + + if (type != SRS_RTMP_TYPE_VIDEO) { + *ppts = time; + return ret; + } + + if (!SrsFlvCodec::video_is_h264(data, size)) { + return ERROR_FLV_INVALID_VIDEO_TAG; + } + + if (SrsFlvCodec::video_is_sequence_header(data, size)) { + *ppts = time; + return ret; + } + + // 1bytes, frame type and codec id. + // 1bytes, avc packet type. + // 3bytes, cts, composition time, + // pts = dts + cts, or + // cts = pts - dts. + if (size < 5) { + return ERROR_FLV_INVALID_VIDEO_TAG; + } + + u_int32_t cts = 0; + char* p = data + 2; + char* pp = (char*)&cts; + pp[2] = *p++; + pp[1] = *p++; + pp[0] = *p++; + + *ppts = time + cts; + + return ret; +} + +char srs_utils_get_flv_video_codec_id(char* data, int size) +{ + if (size < 1) { + return 0; + } + + char codec_id = data[0]; + codec_id = codec_id & 0x0F; + + return codec_id; +} + +char srs_utils_get_flv_video_avc_packet_type(char* data, int size) +{ + if (size < 2) { + return -1; + } + + if (!SrsFlvCodec::video_is_h264(data, size)) { + return -1; + } + + u_int8_t avc_packet_type = data[1]; + + if (avc_packet_type > 2) { + return -1; + } + + return avc_packet_type; +} + +char srs_utils_get_flv_video_frame_type(char* data, int size) +{ + if (size < 1) { + return -1; + } + + if (!SrsFlvCodec::video_is_h264(data, size)) { + return -1; + } + + u_int8_t frame_type = data[0]; + frame_type = (frame_type >> 4) & 0x0f; + if (frame_type < 1 || frame_type > 5) { + return -1; + } + + return frame_type; +} + +char* srs_human_amf0_print(srs_amf0_t amf0, char** pdata, int* psize) +{ + if (!amf0) { + return NULL; + } + + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + + return any->human_print(pdata, psize); +} + +const char* srs_human_flv_tag_type2string(char type) +{ + static const char* audio = "Audio"; + static const char* video = "Video"; + static const char* data = "Data"; + static const char* unknown = "Unknown"; + + switch (type) { + case SRS_RTMP_TYPE_AUDIO: return audio; + case SRS_RTMP_TYPE_VIDEO: return video; + case SRS_RTMP_TYPE_SCRIPT: return data; + default: return unknown; + } + + return unknown; +} + +const char* srs_human_flv_video_codec_id2string(char codec_id) +{ + static const char* h263 = "H.263"; + static const char* screen = "Screen"; + static const char* vp6 = "VP6"; + static const char* vp6_alpha = "VP6Alpha"; + static const char* screen2 = "Screen2"; + static const char* h264 = "H.264"; + static const char* unknown = "Unknown"; + + switch (codec_id) { + case 2: return h263; + case 3: return screen; + case 4: return vp6; + case 5: return vp6_alpha; + case 6: return screen2; + case 7: return h264; + default: return unknown; + } + + return unknown; +} + +const char* srs_human_flv_video_avc_packet_type2string(char avc_packet_type) +{ + static const char* sps_pps = "SpsPps"; + static const char* nalu = "Nalu"; + static const char* sps_pps_end = "SpsPpsEnd"; + static const char* unknown = "Unknown"; + + switch (avc_packet_type) { + case 0: return sps_pps; + case 1: return nalu; + case 2: return sps_pps_end; + default: return unknown; + } + + return unknown; +} + +const char* srs_human_flv_video_frame_type2string(char frame_type) +{ + static const char* keyframe = "I"; + static const char* interframe = "P/B"; + static const char* disposable_interframe = "DI"; + static const char* generated_keyframe = "GI"; + static const char* video_infoframe = "VI"; + static const char* unknown = "Unknown"; + + switch (frame_type) { + case 1: return keyframe; + case 2: return interframe; + case 3: return disposable_interframe; + case 4: return generated_keyframe; + case 5: return video_infoframe; + default: return unknown; + } + + return unknown; +} + +int srs_human_print_rtmp_packet(char type, u_int32_t timestamp, char* data, int size) +{ + int ret = ERROR_SUCCESS; + + u_int32_t pts; + if (srs_utils_parse_timestamp(timestamp, type, data, size, &pts) != 0) { + return ret; + } + + if (type == SRS_RTMP_TYPE_VIDEO) { + srs_human_trace("Video packet type=%s, dts=%d, pts=%d, size=%d, %s(%s,%s)", + srs_human_flv_tag_type2string(type), timestamp, pts, size, + srs_human_flv_video_codec_id2string(srs_utils_get_flv_video_codec_id(data, size)), + srs_human_flv_video_avc_packet_type2string(srs_utils_get_flv_video_avc_packet_type(data, size)), + srs_human_flv_video_frame_type2string(srs_utils_get_flv_video_frame_type(data, size)) + ); + } else if (type == SRS_RTMP_TYPE_AUDIO) { + srs_human_trace("Audio packet type=%s, dts=%d, pts=%d, size=%d", + srs_human_flv_tag_type2string(type), timestamp, pts, size); + } else if (type == SRS_RTMP_TYPE_SCRIPT) { + srs_human_verbose("Data packet type=%s, time=%d, size=%d", + srs_human_flv_tag_type2string(type), timestamp, size); + int nparsed = 0; + while (nparsed < size) { + int nb_parsed_this = 0; + srs_amf0_t amf0 = srs_amf0_parse(data + nparsed, size - nparsed, &nb_parsed_this); + if (amf0 == NULL) { + break; + } + + nparsed += nb_parsed_this; + + char* amf0_str = NULL; + srs_human_raw("%s", srs_human_amf0_print(amf0, &amf0_str, NULL)); + srs_amf0_free_bytes(amf0_str); + } + } else { + srs_human_trace("Unknown packet type=%s, dts=%d, pts=%d, size=%d", + srs_human_flv_tag_type2string(type), timestamp, pts, size); + } + + return ret; +} + +const char* srs_human_format_time() +{ + struct timeval tv; + static char buf[23]; + + memset(buf, 0, sizeof(buf)); + + // clock time + if (gettimeofday(&tv, NULL) == -1) { + return buf; + } + + // to calendar time + struct tm* tm; + if ((tm = localtime((const time_t*)&tv.tv_sec)) == NULL) { + return buf; + } + + snprintf(buf, sizeof(buf), + "%d-%02d-%02d %02d:%02d:%02d.%03d", + 1900 + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec, + (int)(tv.tv_usec / 1000)); + + // for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 + buf[sizeof(buf) - 1] = 0; + + return buf; +} + #ifdef __cplusplus } #endif diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index c02b5f281a..d1e07e47ec 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -263,83 +263,6 @@ extern int srs_version_major(); extern int srs_version_minor(); extern int srs_version_revision(); -/************************************************************* -************************************************************** -* utilities -************************************************************** -*************************************************************/ -/** -* get the current system time in ms. -* use gettimeofday() to get system time. -*/ -extern int64_t srs_utils_get_time_ms(); - -/** -* get the send bytes. -*/ -extern int64_t srs_utils_get_send_bytes(srs_rtmp_t rtmp); - -/** -* get the recv bytes. -*/ -extern int64_t srs_utils_get_recv_bytes(srs_rtmp_t rtmp); - -/** -* parse the dts and pts by time in header and data in tag, -* or to parse the RTMP packet by srs_read_packet(). -* -* @param time, the timestamp of tag, read by srs_flv_read_tag_header(). -* @param type, the type of tag, read by srs_flv_read_tag_header(). -* @param data, the data of tag, read by srs_flv_read_tag_data(). -* @param size, the size of tag, read by srs_flv_read_tag_header(). -* @param ppts, output the pts in ms, -* -* @return 0, success; otherswise, failed. -* @remark, the dts always equals to @param time. -* @remark, the pts=dts for audio or data. -* @remark, video only support h.264. -*/ -extern int srs_utils_parse_timestamp( - u_int32_t time, char type, char* data, int size, - u_int32_t* ppts -); - -/** -* get the CodecID of video tag. -* Codec Identifier. The following values are defined: -* 2 = Sorenson H.263 -* 3 = Screen video -* 4 = On2 VP6 -* 5 = On2 VP6 with alpha channel -* 6 = Screen video version 2 -* 7 = AVC -* @return the code id. 0 for error. -*/ -extern char srs_utils_get_flv_video_codec_id(char* data, int size); - -/** -* get the AVCPacketType of video tag. -* The following values are defined: -* 0 = AVC sequence header -* 1 = AVC NALU -* 2 = AVC end of sequence (lower level NALU sequence ender is -* not required or supported) -* @return the avc packet type. -1(0xff) for error. -*/ -extern char srs_utils_get_flv_video_avc_packet_type(char* data, int size); - -/** -* get the FrameType of video tag. -* Type of video frame. The following values are defined: -* 1 = key frame (for AVC, a seekable frame) -* 2 = inter frame (for AVC, a non-seekable frame) -* 3 = disposable inter frame (H.263 only) -* 4 = generated key frame (reserved for server use only) -* 5 = video info/command frame -* @return the frame type. 0 for error. -*/ -extern char srs_utils_get_flv_video_frame_type(char* data, int size); - /************************************************************* ************************************************************** * flv codec @@ -498,80 +421,6 @@ extern int srs_amf0_strict_array_property_count(srs_amf0_t amf0); extern srs_amf0_t srs_amf0_strict_array_property_at(srs_amf0_t amf0, int index); extern void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value); -/************************************************************* -************************************************************** -* human readable print. -************************************************************** -*************************************************************/ -/** -* human readable print -* @param pdata, output the heap data, NULL to ignore. -* user must use srs_amf0_free_bytes to free it. -* @return return the *pdata for print. NULL to ignore. -*/ -extern char* srs_human_amf0_print(srs_amf0_t amf0, char** pdata, int* psize); -/** -* convert the flv tag type to string. -* SRS_RTMP_TYPE_AUDIO to "Audio" -* SRS_RTMP_TYPE_VIDEO to "Video" -* SRS_RTMP_TYPE_SCRIPT to "Data" -* otherwise, "Unknown" -* @remark user never free the return char*, -* it's static shared const string. -*/ -extern const char* srs_human_flv_tag_type2string(char type); - -/** -* get the codec id string. -* H.263 = Sorenson H.263 -* Screen = Screen video -* VP6 = On2 VP6 -* VP6Alpha = On2 VP6 with alpha channel -* Screen2 = Screen video version 2 -* H.264 = AVC -* otherwise, "Unknown" -* @remark user never free the return char*, -* it's static shared const string. -*/ -extern const char* srs_human_flv_video_codec_id2string(char codec_id); - -/** -* get the avc packet type string. -* SpsPps = AVC sequence header -* Nalu = AVC NALU -* SpsPpsEnd = AVC end of sequence -* otherwise, "Unknown" -* @remark user never free the return char*, -* it's static shared const string. -*/ -extern const char* srs_human_flv_video_avc_packet_type2string(char avc_packet_type); - -/** -* get the frame type string. -* I = key frame (for AVC, a seekable frame) -* P/B = inter frame (for AVC, a non-seekable frame) -* DI = disposable inter frame (H.263 only) -* GI = generated key frame (reserved for server use only) -* VI = video info/command frame -* otherwise, "Unknown" -* @remark user never free the return char*, -* it's static shared const string. -*/ -extern const char* srs_human_flv_video_frame_type2string(char frame_type); - -/** -* print the rtmp packet, use srs_human_trace/srs_human_verbose for packet, -* and use srs_human_raw for script data body. -* @return an error code for parse the timetstamp to dts and pts. -*/ -extern int srs_human_print_rtmp_packet(char type, u_int32_t timestamp, char* data, int size); - -// log to console, for use srs-librtmp application. -extern const char* srs_human_format_time(); -#define srs_human_trace(msg, ...) printf("[%s] ", srs_human_format_time());printf(msg, ##__VA_ARGS__);printf("\n") -#define srs_human_verbose(msg, ...) printf("[%s] ", srs_human_format_time());printf(msg, ##__VA_ARGS__);printf("\n") -#define srs_human_raw(msg, ...) printf(msg, ##__VA_ARGS__) - /************************************************************* ************************************************************** * audio raw codec @@ -735,6 +584,157 @@ extern int srs_h264_startswith_annexb( int* pnb_start_code ); +/************************************************************* +************************************************************** +* utilities +************************************************************** +*************************************************************/ +/** +* get the current system time in ms. +* use gettimeofday() to get system time. +*/ +extern int64_t srs_utils_get_time_ms(); + +/** +* get the send bytes. +*/ +extern int64_t srs_utils_get_send_bytes(srs_rtmp_t rtmp); + +/** +* get the recv bytes. +*/ +extern int64_t srs_utils_get_recv_bytes(srs_rtmp_t rtmp); + +/** +* parse the dts and pts by time in header and data in tag, +* or to parse the RTMP packet by srs_read_packet(). +* +* @param time, the timestamp of tag, read by srs_flv_read_tag_header(). +* @param type, the type of tag, read by srs_flv_read_tag_header(). +* @param data, the data of tag, read by srs_flv_read_tag_data(). +* @param size, the size of tag, read by srs_flv_read_tag_header(). +* @param ppts, output the pts in ms, +* +* @return 0, success; otherswise, failed. +* @remark, the dts always equals to @param time. +* @remark, the pts=dts for audio or data. +* @remark, video only support h.264. +*/ +extern int srs_utils_parse_timestamp( + u_int32_t time, char type, char* data, int size, + u_int32_t* ppts +); + +/** +* get the CodecID of video tag. +* Codec Identifier. The following values are defined: +* 2 = Sorenson H.263 +* 3 = Screen video +* 4 = On2 VP6 +* 5 = On2 VP6 with alpha channel +* 6 = Screen video version 2 +* 7 = AVC +* @return the code id. 0 for error. +*/ +extern char srs_utils_get_flv_video_codec_id(char* data, int size); + +/** +* get the AVCPacketType of video tag. +* The following values are defined: +* 0 = AVC sequence header +* 1 = AVC NALU +* 2 = AVC end of sequence (lower level NALU sequence ender is +* not required or supported) +* @return the avc packet type. -1(0xff) for error. +*/ +extern char srs_utils_get_flv_video_avc_packet_type(char* data, int size); + +/** +* get the FrameType of video tag. +* Type of video frame. The following values are defined: +* 1 = key frame (for AVC, a seekable frame) +* 2 = inter frame (for AVC, a non-seekable frame) +* 3 = disposable inter frame (H.263 only) +* 4 = generated key frame (reserved for server use only) +* 5 = video info/command frame +* @return the frame type. 0 for error. +*/ +extern char srs_utils_get_flv_video_frame_type(char* data, int size); + +/************************************************************* +************************************************************** +* human readable print. +************************************************************** +*************************************************************/ +/** +* human readable print +* @param pdata, output the heap data, NULL to ignore. +* user must use srs_amf0_free_bytes to free it. +* @return return the *pdata for print. NULL to ignore. +*/ +extern char* srs_human_amf0_print(srs_amf0_t amf0, char** pdata, int* psize); +/** +* convert the flv tag type to string. +* SRS_RTMP_TYPE_AUDIO to "Audio" +* SRS_RTMP_TYPE_VIDEO to "Video" +* SRS_RTMP_TYPE_SCRIPT to "Data" +* otherwise, "Unknown" +* @remark user never free the return char*, +* it's static shared const string. +*/ +extern const char* srs_human_flv_tag_type2string(char type); + +/** +* get the codec id string. +* H.263 = Sorenson H.263 +* Screen = Screen video +* VP6 = On2 VP6 +* VP6Alpha = On2 VP6 with alpha channel +* Screen2 = Screen video version 2 +* H.264 = AVC +* otherwise, "Unknown" +* @remark user never free the return char*, +* it's static shared const string. +*/ +extern const char* srs_human_flv_video_codec_id2string(char codec_id); + +/** +* get the avc packet type string. +* SpsPps = AVC sequence header +* Nalu = AVC NALU +* SpsPpsEnd = AVC end of sequence +* otherwise, "Unknown" +* @remark user never free the return char*, +* it's static shared const string. +*/ +extern const char* srs_human_flv_video_avc_packet_type2string(char avc_packet_type); + +/** +* get the frame type string. +* I = key frame (for AVC, a seekable frame) +* P/B = inter frame (for AVC, a non-seekable frame) +* DI = disposable inter frame (H.263 only) +* GI = generated key frame (reserved for server use only) +* VI = video info/command frame +* otherwise, "Unknown" +* @remark user never free the return char*, +* it's static shared const string. +*/ +extern const char* srs_human_flv_video_frame_type2string(char frame_type); + +/** +* print the rtmp packet, use srs_human_trace/srs_human_verbose for packet, +* and use srs_human_raw for script data body. +* @return an error code for parse the timetstamp to dts and pts. +*/ +extern int srs_human_print_rtmp_packet(char type, u_int32_t timestamp, char* data, int size); + +// log to console, for use srs-librtmp application. +extern const char* srs_human_format_time(); +#define srs_human_trace(msg, ...) printf("[%s] ", srs_human_format_time());printf(msg, ##__VA_ARGS__);printf("\n") +#define srs_human_verbose(msg, ...) printf("[%s] ", srs_human_format_time());printf(msg, ##__VA_ARGS__);printf("\n") +#define srs_human_raw(msg, ...) printf(msg, ##__VA_ARGS__) + #ifdef __cplusplus } #endif From aa69f6197a543194c9ff09ca8bb38013b2559399 Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 21 Nov 2014 14:18:50 +0800 Subject: [PATCH 202/800] srs-librtmp add rtmp prefix for rtmp apis. 2.0.29 --- README.md | 1 + .../research/librtmp/srs_audio_raw_publish.c | 6 +- trunk/research/librtmp/srs_bandwidth_check.c | 8 +-- trunk/research/librtmp/srs_detect_rtmp.c | 12 ++-- trunk/research/librtmp/srs_h264_raw_publish.c | 6 +- trunk/research/librtmp/srs_ingest_flv.c | 8 +-- trunk/research/librtmp/srs_ingest_rtmp.c | 16 ++--- trunk/research/librtmp/srs_play.c | 8 +-- trunk/research/librtmp/srs_publish.c | 8 +-- trunk/src/core/srs_core.hpp | 2 +- trunk/src/libs/srs_librtmp.cpp | 62 +++++++++---------- trunk/src/libs/srs_librtmp.hpp | 51 ++++++++------- trunk/src/srs/srs.upp | 2 +- 13 files changed, 95 insertions(+), 95 deletions(-) diff --git a/README.md b/README.md index e5027c2b8a..0178812c3b 100755 --- a/README.md +++ b/README.md @@ -482,6 +482,7 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History +* v2.0, 2014-11-21, srs-librtmp add rtmp prefix for rtmp/utils/human apis. 2.0.29. * v2.0, 2014-11-21, refine examples of srs-librtmp, add srs_print_rtmp_packet. 2.0.28. * v2.0, 2014-11-20, fix [#212](https://github.com/winlinvip/simple-rtmp-server/issues/212), support publish audio raw frames. 2.0.27 * v2.0, 2014-11-19, fix [#213](https://github.com/winlinvip/simple-rtmp-server/issues/213), support compile [srs-librtmp on windows](https://github.com/winlinvip/srs.librtmp), [bug #213](https://github.com/winlinvip/simple-rtmp-server/issues/213). 2.0.26 diff --git a/trunk/research/librtmp/srs_audio_raw_publish.c b/trunk/research/librtmp/srs_audio_raw_publish.c index 04e228dd44..9e11023ba1 100644 --- a/trunk/research/librtmp/srs_audio_raw_publish.c +++ b/trunk/research/librtmp/srs_audio_raw_publish.c @@ -116,19 +116,19 @@ int main(int argc, char** argv) // connect rtmp context srs_rtmp_t rtmp = srs_rtmp_create(rtmp_url); - if (srs_simple_handshake(rtmp) != 0) { + if (srs_rtmp_handshake(rtmp) != 0) { srs_human_trace("simple handshake failed."); goto rtmp_destroy; } srs_human_trace("simple handshake success"); - if (srs_connect_app(rtmp) != 0) { + if (srs_rtmp_connect_app(rtmp) != 0) { srs_human_trace("connect vhost/app failed."); goto rtmp_destroy; } srs_human_trace("connect vhost/app success"); - if (srs_publish_stream(rtmp) != 0) { + if (srs_rtmp_publish_stream(rtmp) != 0) { srs_human_trace("publish stream failed."); goto rtmp_destroy; } diff --git a/trunk/research/librtmp/srs_bandwidth_check.c b/trunk/research/librtmp/srs_bandwidth_check.c index 29fe1223f4..fa940480be 100644 --- a/trunk/research/librtmp/srs_bandwidth_check.c +++ b/trunk/research/librtmp/srs_bandwidth_check.c @@ -21,7 +21,7 @@ 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. */ /** -gcc srs_bandwidth_check.c ../../objs/lib/srs_librtmp.a -g -O0 -lstdc++ -o srs_bandwidth_check +gcc srs_rtmp_bandwidth_check.c ../../objs/lib/srs_librtmp.a -g -O0 -lstdc++ -o srs_rtmp_bandwidth_check */ #include @@ -83,20 +83,20 @@ int main(int argc, char** argv) srs_human_trace("bandwidth check/test url: %s", argv[1]); - if ((ret = srs_simple_handshake(rtmp)) != 0) { + if ((ret = srs_rtmp_handshake(rtmp)) != 0) { srs_human_trace("simple handshake failed."); goto rtmp_destroy; } srs_human_trace("simple handshake success"); - if ((ret = srs_connect_app2(rtmp, + if ((ret = srs_rtmp_connect_app2(rtmp, srs_server_ip, srs_server, srs_primary_authors, srs_version, &srs_id, &srs_pid)) != 0) { srs_human_trace("connect vhost/app failed."); goto rtmp_destroy; } srs_human_trace("connect vhost/app success"); - if ((ret = srs_bandwidth_check(rtmp, + if ((ret = srs_rtmp_bandwidth_check(rtmp, &start_time, &end_time, &play_kbps, &publish_kbps, &play_bytes, &publish_bytes, &play_duration, &publish_duration)) != 0 ) { diff --git a/trunk/research/librtmp/srs_detect_rtmp.c b/trunk/research/librtmp/srs_detect_rtmp.c index 3752153853..155d28bd0d 100644 --- a/trunk/research/librtmp/srs_detect_rtmp.c +++ b/trunk/research/librtmp/srs_detect_rtmp.c @@ -90,33 +90,33 @@ int main(int argc, char** argv) rtmp = srs_rtmp_create(rtmp_url); - if ((ret = __srs_dns_resolve(rtmp)) != 0) { + if ((ret = __srs_rtmp_dns_resolve(rtmp)) != 0) { srs_human_trace("dns resolve failed. ret=%d", ret); goto rtmp_destroy; } srs_human_trace("dns resolve success"); time_dns_resolve = srs_utils_get_time_ms(); - if ((ret = __srs_connect_server(rtmp)) != 0) { + if ((ret = __srs_rtmp_connect_server(rtmp)) != 0) { srs_human_trace("socket connect failed. ret=%d", ret); goto rtmp_destroy; } srs_human_trace("socket connect success"); time_socket_connect = srs_utils_get_time_ms(); - if ((ret = __srs_do_simple_handshake(rtmp)) != 0) { + if ((ret = __srs_rtmp_do_simple_handshake(rtmp)) != 0) { srs_human_trace("do simple handshake failed. ret=%d", ret); goto rtmp_destroy; } srs_human_trace("do simple handshake success"); - if ((ret = srs_connect_app(rtmp)) != 0) { + if ((ret = srs_rtmp_connect_app(rtmp)) != 0) { srs_human_trace("connect vhost/app failed. ret=%d", ret); goto rtmp_destroy; } srs_human_trace("connect vhost/app success"); - if ((ret = srs_play_stream(rtmp)) != 0) { + if ((ret = srs_rtmp_play_stream(rtmp)) != 0) { srs_human_trace("play stream failed. ret=%d", ret); goto rtmp_destroy; } @@ -124,7 +124,7 @@ int main(int argc, char** argv) time_play_stream = srs_utils_get_time_ms(); for (;;) { - if ((ret = srs_read_packet(rtmp, &type, ×tamp, &data, &size)) != 0) { + if ((ret = srs_rtmp_read_packet(rtmp, &type, ×tamp, &data, &size)) != 0) { srs_human_trace("read packet failed. ret=%d", ret); goto rtmp_destroy; } diff --git a/trunk/research/librtmp/srs_h264_raw_publish.c b/trunk/research/librtmp/srs_h264_raw_publish.c index 5e7afd399b..0b7bee1ad1 100644 --- a/trunk/research/librtmp/srs_h264_raw_publish.c +++ b/trunk/research/librtmp/srs_h264_raw_publish.c @@ -128,19 +128,19 @@ int main(int argc, char** argv) // connect rtmp context srs_rtmp_t rtmp = srs_rtmp_create(rtmp_url); - if (srs_simple_handshake(rtmp) != 0) { + if (srs_rtmp_handshake(rtmp) != 0) { srs_human_trace("simple handshake failed."); goto rtmp_destroy; } srs_human_trace("simple handshake success"); - if (srs_connect_app(rtmp) != 0) { + if (srs_rtmp_connect_app(rtmp) != 0) { srs_human_trace("connect vhost/app failed."); goto rtmp_destroy; } srs_human_trace("connect vhost/app success"); - if (srs_publish_stream(rtmp) != 0) { + if (srs_rtmp_publish_stream(rtmp) != 0) { srs_human_trace("publish stream failed."); goto rtmp_destroy; } diff --git a/trunk/research/librtmp/srs_ingest_flv.c b/trunk/research/librtmp/srs_ingest_flv.c index a0a934816d..8b21170f63 100644 --- a/trunk/research/librtmp/srs_ingest_flv.c +++ b/trunk/research/librtmp/srs_ingest_flv.c @@ -148,7 +148,7 @@ int do_proxy(srs_flv_t flv, srs_rtmp_t ortmp, int64_t re, int32_t* pstarttime, u return ret; } - if ((ret = srs_write_packet(ortmp, type, *ptimestamp, data, size)) != 0) { + if ((ret = srs_rtmp_write_packet(ortmp, type, *ptimestamp, data, size)) != 0) { srs_human_trace("irtmp get packet failed. ret=%d", ret); return ret; } @@ -191,19 +191,19 @@ int connect_oc(srs_rtmp_t ortmp) { int ret = 0; - if ((ret = srs_simple_handshake(ortmp)) != 0) { + if ((ret = srs_rtmp_handshake(ortmp)) != 0) { srs_human_trace("ortmp simple handshake failed. ret=%d", ret); return ret; } srs_human_trace("ortmp simple handshake success"); - if ((ret = srs_connect_app(ortmp)) != 0) { + if ((ret = srs_rtmp_connect_app(ortmp)) != 0) { srs_human_trace("ortmp connect vhost/app failed. ret=%d", ret); return ret; } srs_human_trace("ortmp connect vhost/app success"); - if ((ret = srs_publish_stream(ortmp)) != 0) { + if ((ret = srs_rtmp_publish_stream(ortmp)) != 0) { srs_human_trace("ortmp publish stream failed. ret=%d", ret); return ret; } diff --git a/trunk/research/librtmp/srs_ingest_rtmp.c b/trunk/research/librtmp/srs_ingest_rtmp.c index 6918d1307c..c688a5c1b2 100644 --- a/trunk/research/librtmp/srs_ingest_rtmp.c +++ b/trunk/research/librtmp/srs_ingest_rtmp.c @@ -108,7 +108,7 @@ int proxy(srs_rtmp_t irtmp, srs_rtmp_t ortmp) srs_human_trace("start proxy RTMP stream"); for (;;) { - if ((ret = srs_read_packet(irtmp, &type, ×tamp, &data, &size)) != 0) { + if ((ret = srs_rtmp_read_packet(irtmp, &type, ×tamp, &data, &size)) != 0) { srs_human_trace("irtmp get packet failed. ret=%d", ret); return ret; } @@ -118,7 +118,7 @@ int proxy(srs_rtmp_t irtmp, srs_rtmp_t ortmp) return ret; } - if ((ret = srs_write_packet(ortmp, type, timestamp, data, size)) != 0) { + if ((ret = srs_rtmp_write_packet(ortmp, type, timestamp, data, size)) != 0) { srs_human_trace("irtmp get packet failed. ret=%d", ret); return ret; } @@ -133,19 +133,19 @@ int connect_ic(srs_rtmp_t irtmp) { int ret = 0; - if ((ret = srs_simple_handshake(irtmp)) != 0) { + if ((ret = srs_rtmp_handshake(irtmp)) != 0) { srs_human_trace("irtmp simple handshake failed. ret=%d", ret); return ret; } srs_human_trace("irtmp simple handshake success"); - if ((ret = srs_connect_app(irtmp)) != 0) { + if ((ret = srs_rtmp_connect_app(irtmp)) != 0) { srs_human_trace("irtmp connect vhost/app failed. ret=%d", ret); return ret; } srs_human_trace("irtmp connect vhost/app success"); - if ((ret = srs_play_stream(irtmp)) != 0) { + if ((ret = srs_rtmp_play_stream(irtmp)) != 0) { srs_human_trace("irtmp play stream failed. ret=%d", ret); return ret; } @@ -158,19 +158,19 @@ int connect_oc(srs_rtmp_t ortmp) { int ret = 0; - if ((ret = srs_simple_handshake(ortmp)) != 0) { + if ((ret = srs_rtmp_handshake(ortmp)) != 0) { srs_human_trace("ortmp simple handshake failed. ret=%d", ret); return ret; } srs_human_trace("ortmp simple handshake success"); - if ((ret = srs_connect_app(ortmp)) != 0) { + if ((ret = srs_rtmp_connect_app(ortmp)) != 0) { srs_human_trace("ortmp connect vhost/app failed. ret=%d", ret); return ret; } srs_human_trace("ortmp connect vhost/app success"); - if ((ret = srs_publish_stream(ortmp)) != 0) { + if ((ret = srs_rtmp_publish_stream(ortmp)) != 0) { srs_human_trace("ortmp publish stream failed. ret=%d", ret); return ret; } diff --git a/trunk/research/librtmp/srs_play.c b/trunk/research/librtmp/srs_play.c index 50583364ed..c11214cc37 100644 --- a/trunk/research/librtmp/srs_play.c +++ b/trunk/research/librtmp/srs_play.c @@ -47,19 +47,19 @@ int main(int argc, char** argv) srs_human_trace("rtmp url: %s", argv[1]); srs_rtmp_t rtmp = srs_rtmp_create(argv[1]); - if (srs_simple_handshake(rtmp) != 0) { + if (srs_rtmp_handshake(rtmp) != 0) { srs_human_trace("simple handshake failed."); goto rtmp_destroy; } srs_human_trace("simple handshake success"); - if (srs_connect_app(rtmp) != 0) { + if (srs_rtmp_connect_app(rtmp) != 0) { srs_human_trace("connect vhost/app failed."); goto rtmp_destroy; } srs_human_trace("connect vhost/app success"); - if (srs_play_stream(rtmp) != 0) { + if (srs_rtmp_play_stream(rtmp) != 0) { srs_human_trace("play stream failed."); goto rtmp_destroy; } @@ -71,7 +71,7 @@ int main(int argc, char** argv) char* data; u_int32_t timestamp; - if (srs_read_packet(rtmp, &type, ×tamp, &data, &size) != 0) { + if (srs_rtmp_read_packet(rtmp, &type, ×tamp, &data, &size) != 0) { goto rtmp_destroy; } diff --git a/trunk/research/librtmp/srs_publish.c b/trunk/research/librtmp/srs_publish.c index 077fa20d22..1b8c4fb4e2 100644 --- a/trunk/research/librtmp/srs_publish.c +++ b/trunk/research/librtmp/srs_publish.c @@ -55,19 +55,19 @@ int main(int argc, char** argv) srs_human_trace("rtmp url: %s", argv[1]); srs_rtmp_t rtmp = srs_rtmp_create(argv[1]); - if (srs_simple_handshake(rtmp) != 0) { + if (srs_rtmp_handshake(rtmp) != 0) { srs_human_trace("simple handshake failed."); goto rtmp_destroy; } srs_human_trace("simple handshake success"); - if (srs_connect_app(rtmp) != 0) { + if (srs_rtmp_connect_app(rtmp) != 0) { srs_human_trace("connect vhost/app failed."); goto rtmp_destroy; } srs_human_trace("connect vhost/app success"); - if (srs_publish_stream(rtmp) != 0) { + if (srs_rtmp_publish_stream(rtmp) != 0) { srs_human_trace("publish stream failed."); goto rtmp_destroy; } @@ -81,7 +81,7 @@ int main(int argc, char** argv) timestamp += 40; - if (srs_write_packet(rtmp, type, timestamp, data, size) != 0) { + if (srs_rtmp_write_packet(rtmp, type, timestamp, data, size) != 0) { goto rtmp_destroy; } srs_human_trace("sent packet: type=%s, time=%d, size=%d", diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 494556c551..b647083813 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 28 +#define VERSION_REVISION 29 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index ca4e8ce321..219dcebf23 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -514,6 +514,21 @@ int srs_librtmp_context_connect(Context* context) extern "C"{ #endif +int srs_version_major() +{ + return VERSION_MAJOR; +} + +int srs_version_minor() +{ + return VERSION_MINOR; +} + +int srs_version_revision() +{ + return VERSION_REVISION; +} + srs_rtmp_t srs_rtmp_create(const char* url) { Context* context = new Context(); @@ -541,26 +556,26 @@ void srs_rtmp_destroy(srs_rtmp_t rtmp) srs_freep(context); } -int srs_simple_handshake(srs_rtmp_t rtmp) +int srs_rtmp_handshake(srs_rtmp_t rtmp) { int ret = ERROR_SUCCESS; - if ((ret = __srs_dns_resolve(rtmp)) != ERROR_SUCCESS) { + if ((ret = __srs_rtmp_dns_resolve(rtmp)) != ERROR_SUCCESS) { return ret; } - if ((ret = __srs_connect_server(rtmp)) != ERROR_SUCCESS) { + if ((ret = __srs_rtmp_connect_server(rtmp)) != ERROR_SUCCESS) { return ret; } - if ((ret = __srs_do_simple_handshake(rtmp)) != ERROR_SUCCESS) { + if ((ret = __srs_rtmp_do_simple_handshake(rtmp)) != ERROR_SUCCESS) { return ret; } return ret; } -int __srs_dns_resolve(srs_rtmp_t rtmp) +int __srs_rtmp_dns_resolve(srs_rtmp_t rtmp) { int ret = ERROR_SUCCESS; @@ -579,7 +594,7 @@ int __srs_dns_resolve(srs_rtmp_t rtmp) return ret; } -int __srs_connect_server(srs_rtmp_t rtmp) +int __srs_rtmp_connect_server(srs_rtmp_t rtmp) { int ret = ERROR_SUCCESS; @@ -593,7 +608,7 @@ int __srs_connect_server(srs_rtmp_t rtmp) return ret; } -int __srs_do_simple_handshake(srs_rtmp_t rtmp) +int __srs_rtmp_do_simple_handshake(srs_rtmp_t rtmp) { int ret = ERROR_SUCCESS; @@ -613,7 +628,7 @@ int __srs_do_simple_handshake(srs_rtmp_t rtmp) return ret; } -int srs_connect_app(srs_rtmp_t rtmp) +int srs_rtmp_connect_app(srs_rtmp_t rtmp) { int ret = ERROR_SUCCESS; @@ -634,7 +649,7 @@ int srs_connect_app(srs_rtmp_t rtmp) return ret; } -int srs_connect_app2(srs_rtmp_t rtmp, +int srs_rtmp_connect_app2(srs_rtmp_t rtmp, char srs_server_ip[128],char srs_server[128], char srs_primary_authors[128], char srs_version[32], int* srs_id, int* srs_pid ) { @@ -670,7 +685,7 @@ int srs_connect_app2(srs_rtmp_t rtmp, return ret; } -int srs_play_stream(srs_rtmp_t rtmp) +int srs_rtmp_play_stream(srs_rtmp_t rtmp) { int ret = ERROR_SUCCESS; @@ -687,7 +702,7 @@ int srs_play_stream(srs_rtmp_t rtmp) return ret; } -int srs_publish_stream(srs_rtmp_t rtmp) +int srs_rtmp_publish_stream(srs_rtmp_t rtmp) { int ret = ERROR_SUCCESS; @@ -701,7 +716,7 @@ int srs_publish_stream(srs_rtmp_t rtmp) return ret; } -int srs_bandwidth_check(srs_rtmp_t rtmp, +int srs_rtmp_bandwidth_check(srs_rtmp_t rtmp, int64_t* start_time, int64_t* end_time, int* play_kbps, int* publish_kbps, int* play_bytes, int* publish_bytes, @@ -737,7 +752,7 @@ int srs_bandwidth_check(srs_rtmp_t rtmp, return ret; } -int srs_read_packet(srs_rtmp_t rtmp, char* type, u_int32_t* timestamp, char** data, int* size) +int srs_rtmp_read_packet(srs_rtmp_t rtmp, char* type, u_int32_t* timestamp, char** data, int* size) { *type = 0; *timestamp = 0; @@ -792,7 +807,7 @@ int srs_read_packet(srs_rtmp_t rtmp, char* type, u_int32_t* timestamp, char** da return ret; } -int srs_write_packet(srs_rtmp_t rtmp, char type, u_int32_t timestamp, char* data, int size) +int srs_rtmp_write_packet(srs_rtmp_t rtmp, char type, u_int32_t timestamp, char* data, int size) { int ret = ERROR_SUCCESS; @@ -843,21 +858,6 @@ int srs_write_packet(srs_rtmp_t rtmp, char type, u_int32_t timestamp, char* data return ret; } -int srs_version_major() -{ - return VERSION_MAJOR; -} - -int srs_version_minor() -{ - return VERSION_MINOR; -} - -int srs_version_revision() -{ - return VERSION_REVISION; -} - struct FlvContext { SrsFileReader reader; @@ -1356,7 +1356,7 @@ int srs_audio_write_raw_frame(srs_rtmp_t rtmp, memcpy(p, frame, frame_size); - return srs_write_packet(context, SRS_RTMP_TYPE_AUDIO, timestamp, data, size); + return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_AUDIO, timestamp, data, size); } /** @@ -1403,7 +1403,7 @@ int __srs_write_h264_packet(Context* context, // h.264 raw data. memcpy(p, h264_raw_data, h264_raw_size); - return srs_write_packet(context, SRS_RTMP_TYPE_VIDEO, timestamp, data, size); + return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_VIDEO, timestamp, data, size); } /** diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index d1e07e47ec..d0638eb127 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -84,6 +84,15 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. extern "C"{ #endif +/************************************************************* +************************************************************** +* srs-librtmp version +************************************************************** +*************************************************************/ +extern int srs_version_major(); +extern int srs_version_minor(); +extern int srs_version_revision(); + /************************************************************* ************************************************************** * RTMP protocol context @@ -134,19 +143,19 @@ extern void srs_rtmp_destroy(srs_rtmp_t rtmp); * not depends on ssl. */ /** -* srs_simple_handshake equals to invoke: -* __srs_dns_resolve() -* __srs_connect_server() -* __srs_do_simple_handshake() +* srs_rtmp_handshake equals to invoke: +* __srs_rtmp_dns_resolve() +* __srs_rtmp_connect_server() +* __srs_rtmp_do_simple_handshake() * user can use these functions if needed. */ -extern int srs_simple_handshake(srs_rtmp_t rtmp); +extern int srs_rtmp_handshake(srs_rtmp_t rtmp); // parse uri, create socket, resolve host -extern int __srs_dns_resolve(srs_rtmp_t rtmp); +extern int __srs_rtmp_dns_resolve(srs_rtmp_t rtmp); // connect socket to server -extern int __srs_connect_server(srs_rtmp_t rtmp); +extern int __srs_rtmp_connect_server(srs_rtmp_t rtmp); // do simple handshake over socket. -extern int __srs_do_simple_handshake(srs_rtmp_t rtmp); +extern int __srs_rtmp_do_simple_handshake(srs_rtmp_t rtmp); /** * connect to rtmp vhost/app @@ -156,7 +165,7 @@ extern int __srs_do_simple_handshake(srs_rtmp_t rtmp); * * @return 0, success; otherswise, failed. */ -extern int srs_connect_app(srs_rtmp_t rtmp); +extern int srs_rtmp_connect_app(srs_rtmp_t rtmp); /** * connect to server, get the debug srs info. @@ -171,7 +180,7 @@ extern int srs_connect_app(srs_rtmp_t rtmp); * * @return 0, success; otherswise, failed. */ -extern int srs_connect_app2(srs_rtmp_t rtmp, +extern int srs_rtmp_connect_app2(srs_rtmp_t rtmp, char srs_server_ip[128], char srs_server[128], char srs_primary_authors[128], char srs_version[32], int* srs_id, int* srs_pid ); @@ -183,7 +192,7 @@ extern int srs_connect_app2(srs_rtmp_t rtmp, * next: destroy * @return 0, success; otherwise, failed. */ -extern int srs_play_stream(srs_rtmp_t rtmp); +extern int srs_rtmp_play_stream(srs_rtmp_t rtmp); /** * publish a live stream. @@ -192,7 +201,7 @@ extern int srs_play_stream(srs_rtmp_t rtmp); * next: destroy * @return 0, success; otherwise, failed. */ -extern int srs_publish_stream(srs_rtmp_t rtmp); +extern int srs_rtmp_publish_stream(srs_rtmp_t rtmp); /** * do bandwidth check with srs server. @@ -209,7 +218,7 @@ extern int srs_publish_stream(srs_rtmp_t rtmp); * * @return 0, success; otherswise, failed. */ -extern int srs_bandwidth_check(srs_rtmp_t rtmp, +extern int srs_rtmp_bandwidth_check(srs_rtmp_t rtmp, int64_t* start_time, int64_t* end_time, int* play_kbps, int* publish_kbps, int* play_bytes, int* publish_bytes, @@ -246,23 +255,13 @@ extern int srs_bandwidth_check(srs_rtmp_t rtmp, * * @return 0, success; otherswise, failed. */ -extern int srs_read_packet(srs_rtmp_t rtmp, +extern int srs_rtmp_read_packet(srs_rtmp_t rtmp, char* type, u_int32_t* timestamp, char** data, int* size ); -extern int srs_write_packet(srs_rtmp_t rtmp, +extern int srs_rtmp_write_packet(srs_rtmp_t rtmp, char type, u_int32_t timestamp, char* data, int size ); -/************************************************************* -************************************************************** -* version apis -************************************************************** -*************************************************************/ -// get protocol stack version -extern int srs_version_major(); -extern int srs_version_minor(); -extern int srs_version_revision(); - /************************************************************* ************************************************************** * flv codec @@ -607,7 +606,7 @@ extern int64_t srs_utils_get_recv_bytes(srs_rtmp_t rtmp); /** * parse the dts and pts by time in header and data in tag, -* or to parse the RTMP packet by srs_read_packet(). +* or to parse the RTMP packet by srs_rtmp_read_packet(). * * @param time, the timestamp of tag, read by srs_flv_read_tag_header(). * @param type, the type of tag, read by srs_flv_read_tag_header(). diff --git a/trunk/src/srs/srs.upp b/trunk/src/srs/srs.upp index da92f3ebf9..3320ab387b 100755 --- a/trunk/src/srs/srs.upp +++ b/trunk/src/srs/srs.upp @@ -129,7 +129,7 @@ file ..\utest\srs_utest_reload.cpp, research readonly separator, ..\..\research\librtmp\srs_audio_raw_publish.c, - ..\..\research\librtmp\srs_bandwidth_check.c, + ..\..\research\librtmp\srs_rtmp_bandwidth_check.c, ..\..\research\librtmp\srs_detect_rtmp.c, ..\..\research\librtmp\srs_flv_injecter.c, ..\..\research\librtmp\srs_flv_parser.c, From 2a9bec3d89d38dd1af9f420301526e47075fe559 Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 21 Nov 2014 14:26:47 +0800 Subject: [PATCH 203/800] adjust the api, move audio and h264 after rtmp --- trunk/src/libs/srs_librtmp.cpp | 1070 ++++++++++++++++---------------- trunk/src/libs/srs_librtmp.hpp | 316 +++++----- 2 files changed, 693 insertions(+), 693 deletions(-) diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index 219dcebf23..591faf819e 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -858,231 +858,622 @@ int srs_rtmp_write_packet(srs_rtmp_t rtmp, char type, u_int32_t timestamp, char* return ret; } -struct FlvContext -{ - SrsFileReader reader; - SrsFileWriter writer; - SrsFlvEncoder enc; - SrsFlvDecoder dec; -}; +/** +* write audio raw frame to SRS. +*/ +int srs_audio_write_raw_frame(srs_rtmp_t rtmp, + char sound_format, char sound_rate, char sound_size, char sound_type, + char aac_packet_type, char* frame, int frame_size, u_int32_t timestamp +) { + Context* context = (Context*)rtmp; + srs_assert(context); -srs_flv_t srs_flv_open_read(const char* file) -{ - int ret = ERROR_SUCCESS; - - FlvContext* flv = new FlvContext(); - - if ((ret = flv->reader.open(file)) != ERROR_SUCCESS) { - srs_freep(flv); - return NULL; - } + // TODO: FIXME: for aac, must send the sequence header first. - if ((ret = flv->dec.initialize(&flv->reader)) != ERROR_SUCCESS) { - srs_freep(flv); - return NULL; + // for audio frame, there is 1 or 2 bytes header: + // 1bytes, SoundFormat|SoundRate|SoundSize|SoundType + // 1bytes, AACPacketType for SoundFormat == 10 + int size = frame_size + 1; + if (aac_packet_type == SrsCodecAudioAAC) { + size += 1; } + char* data = new char[size]; + char* p = data; - return flv; -} - -srs_flv_t srs_flv_open_write(const char* file) -{ - int ret = ERROR_SUCCESS; + u_int8_t audio_header = sound_type & 0x01; + audio_header |= (sound_size << 1) & 0x02; + audio_header |= (sound_rate << 2) & 0x0c; + audio_header |= (sound_format << 4) & 0xf0; - FlvContext* flv = new FlvContext(); + *p++ = audio_header; - if ((ret = flv->writer.open(file)) != ERROR_SUCCESS) { - srs_freep(flv); - return NULL; + if (aac_packet_type == SrsCodecAudioAAC) { + *p++ = aac_packet_type; } - if ((ret = flv->enc.initialize(&flv->writer)) != ERROR_SUCCESS) { - srs_freep(flv); - return NULL; - } + memcpy(p, frame, frame_size); - return flv; -} - -void srs_flv_close(srs_flv_t flv) -{ - FlvContext* context = (FlvContext*)flv; - srs_freep(context); + return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_AUDIO, timestamp, data, size); } -int srs_flv_read_header(srs_flv_t flv, char header[9]) -{ - int ret = ERROR_SUCCESS; +/** +* write h264 packet, with rtmp header. +* @param frame_type, SrsCodecVideoAVCFrameKeyFrame or SrsCodecVideoAVCFrameInterFrame. +* @param avc_packet_type, SrsCodecVideoAVCTypeSequenceHeader or SrsCodecVideoAVCTypeNALU. +* @param h264_raw_data the h.264 raw data, user must free it. +*/ +int __srs_write_h264_packet(Context* context, + int8_t frame_type, int8_t avc_packet_type, + char* h264_raw_data, int h264_raw_size, u_int32_t dts, u_int32_t pts +) { + // the timestamp in rtmp message header is dts. + u_int32_t timestamp = dts; - FlvContext* context = (FlvContext*)flv; - - if (!context->reader.is_open()) { - return ERROR_SYSTEM_IO_INVALID; - } + // for h264 in RTMP video payload, there is 5bytes header: + // 1bytes, FrameType | CodecID + // 1bytes, AVCPacketType + // 3bytes, CompositionTime, the cts. + // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78 + int size = h264_raw_size + 5; + char* data = new char[size]; + char* p = data; - if ((ret = context->dec.read_header(header)) != ERROR_SUCCESS) { - return ret; - } + // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78 + // Frame Type, Type of video frame. + // CodecID, Codec Identifier. + // set the rtmp header + *p++ = (frame_type << 4) | SrsCodecVideoAVC; - char ts[4]; // tag size - if ((ret = context->dec.read_previous_tag_size(ts)) != ERROR_SUCCESS) { - return ret; - } + // AVCPacketType + *p++ = avc_packet_type; + + // CompositionTime + // pts = dts + cts, or + // cts = pts - dts. + // where cts is the header in rtmp video packet payload header. + u_int32_t cts = pts - dts; + char* pp = (char*)&cts; + *p++ = pp[2]; + *p++ = pp[1]; + *p++ = pp[0]; - return ret; + // h.264 raw data. + memcpy(p, h264_raw_data, h264_raw_size); + + return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_VIDEO, timestamp, data, size); } -int srs_flv_read_tag_header(srs_flv_t flv, char* ptype, int32_t* pdata_size, u_int32_t* ptime) +/** +* write the h264 sps/pps in context over RTMP. +*/ +int __srs_write_h264_sps_pps(Context* context, u_int32_t dts, u_int32_t pts) { int ret = ERROR_SUCCESS; - FlvContext* context = (FlvContext*)flv; - - if (!context->reader.is_open()) { - return ERROR_SYSTEM_IO_INVALID; + // only send when both sps and pps changed. + if (!context->h264_sps_changed || !context->h264_pps_changed) { + return ret; } - if ((ret = context->dec.read_tag_header(ptype, pdata_size, ptime)) != ERROR_SUCCESS) { + // 5bytes sps/pps header: + // configurationVersion, AVCProfileIndication, profile_compatibility, + // AVCLevelIndication, lengthSizeMinusOne + // 3bytes size of sps: + // numOfSequenceParameterSets, sequenceParameterSetLength(2B) + // Nbytes of sps. + // sequenceParameterSetNALUnit + // 3bytes size of pps: + // numOfPictureParameterSets, pictureParameterSetLength + // Nbytes of pps: + // pictureParameterSetNALUnit + int nb_packet = 5 + + 3 + (int)context->h264_sps.length() + + 3 + (int)context->h264_pps.length(); + char* packet = new char[nb_packet]; + SrsAutoFree(char, packet); + + // use stream to generate the h264 packet. + SrsStream stream; + if ((ret = stream.initialize(packet, nb_packet)) != ERROR_SUCCESS) { return ret; } - return ret; -} - -int srs_flv_read_tag_data(srs_flv_t flv, char* data, int32_t size) -{ - int ret = ERROR_SUCCESS; + // decode the SPS: + // @see: 7.3.2.1.1, H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 62 + if (true) { + srs_assert((int)context->h264_sps.length() >= 4); + char* frame = (char*)context->h264_sps.data(); - FlvContext* context = (FlvContext*)flv; - - if (!context->reader.is_open()) { - return ERROR_SYSTEM_IO_INVALID; + // @see: Annex A Profiles and levels, H.264-AVC-ISO_IEC_14496-10.pdf, page 205 + // Baseline profile profile_idc is 66(0x42). + // Main profile profile_idc is 77(0x4d). + // Extended profile profile_idc is 88(0x58). + u_int8_t profile_idc = frame[1]; + //u_int8_t constraint_set = frame[2]; + u_int8_t level_idc = frame[3]; + + // generate the sps/pps header + // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 + // configurationVersion + stream.write_1bytes(0x01); + // AVCProfileIndication + stream.write_1bytes(profile_idc); + // profile_compatibility + stream.write_1bytes(0x00); + // AVCLevelIndication + stream.write_1bytes(level_idc); + // lengthSizeMinusOne, or NAL_unit_length, always use 4bytes size, + // so we always set it to 0x03. + stream.write_1bytes(0x03); } - if ((ret = context->dec.read_tag_data(data, size)) != ERROR_SUCCESS) { - return ret; + // sps + if (true) { + // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 + // numOfSequenceParameterSets, always 1 + stream.write_1bytes(0x01); + // sequenceParameterSetLength + stream.write_2bytes(context->h264_sps.length()); + // sequenceParameterSetNALUnit + stream.write_string(context->h264_sps); } - char ts[4]; // tag size - if ((ret = context->dec.read_previous_tag_size(ts)) != ERROR_SUCCESS) { - return ret; + // pps + if (true) { + // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 + // numOfPictureParameterSets, always 1 + stream.write_1bytes(0x01); + // pictureParameterSetLength + stream.write_2bytes(context->h264_pps.length()); + // pictureParameterSetNALUnit + stream.write_string(context->h264_pps); } - return ret; -} - -int srs_flv_write_header(srs_flv_t flv, char header[9]) -{ - int ret = ERROR_SUCCESS; - - FlvContext* context = (FlvContext*)flv; - - if (!context->writer.is_open()) { - return ERROR_SYSTEM_IO_INVALID; - } + // reset sps and pps. + context->h264_sps_changed = false; + context->h264_pps_changed = false; + context->h264_sps_pps_sent = true; - if ((ret = context->enc.write_header(header)) != ERROR_SUCCESS) { - return ret; - } + // TODO: FIXME: for more profile. + // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 + // profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 144 - return ret; + // send out h264 packet. + int8_t frame_type = SrsCodecVideoAVCFrameKeyFrame; + int8_t avc_packet_type = SrsCodecVideoAVCTypeSequenceHeader; + return __srs_write_h264_packet( + context, frame_type, avc_packet_type, + packet, nb_packet, dts, pts + ); } -int srs_flv_write_tag(srs_flv_t flv, char type, int32_t time, char* data, int size) -{ +/** +* write h264 IPB-frame. +*/ +int __srs_write_h264_ipb_frame(Context* context, + char* data, int size, u_int32_t dts, u_int32_t pts +) { int ret = ERROR_SUCCESS; - FlvContext* context = (FlvContext*)flv; + // when sps or pps not sent, ignore the packet. + // @see https://github.com/winlinvip/simple-rtmp-server/issues/203 + if (!context->h264_sps_pps_sent) { + return ERROR_H264_DROP_BEFORE_SPS_PPS; + } + + // 5bits, 7.3.1 NAL unit syntax, + // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. + // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame + u_int8_t nal_unit_type = (char)data[0] & 0x1f; + + // 4bytes size of nalu: + // NALUnitLength + // Nbytes of nalu. + // NALUnit + int nb_packet = 4 + size; + char* packet = new char[nb_packet]; + SrsAutoFree(char, packet); + + // use stream to generate the h264 packet. + SrsStream stream; + if ((ret = stream.initialize(packet, nb_packet)) != ERROR_SUCCESS) { + return ret; + } - if (!context->writer.is_open()) { - return ERROR_SYSTEM_IO_INVALID; + // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 + // lengthSizeMinusOne, or NAL_unit_length, always use 4bytes size + u_int32_t NAL_unit_length = size; + + // mux the avc NALU in "ISO Base Media File Format" + // from H.264-AVC-ISO_IEC_14496-15.pdf, page 20 + // NALUnitLength + stream.write_4bytes(NAL_unit_length); + // NALUnit + stream.write_bytes(data, size); + + // send out h264 packet. + int8_t frame_type = SrsCodecVideoAVCFrameInterFrame; + if (nal_unit_type != 1) { + frame_type = SrsCodecVideoAVCFrameKeyFrame; } + int8_t avc_packet_type = SrsCodecVideoAVCTypeNALU; + return __srs_write_h264_packet( + context, frame_type, avc_packet_type, + packet, nb_packet, dts, pts + ); - if (type == SRS_RTMP_TYPE_AUDIO) { - return context->enc.write_audio(time, data, size); - } else if (type == SRS_RTMP_TYPE_VIDEO) { - return context->enc.write_video(time, data, size); + return ret; +} + +/** +* write h264 raw frame, maybe sps/pps/IPB-frame. +*/ +int __srs_write_h264_raw_frame(Context* context, + char* frame, int frame_size, u_int32_t dts, u_int32_t pts +) { + int ret = ERROR_SUCCESS; + + // ignore invalid frame, + // atleast 1bytes for SPS to decode the type + if (frame_size < 1) { + return ret; + } + + // 5bits, 7.3.1 NAL unit syntax, + // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. + // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame + u_int8_t nal_unit_type = (char)frame[0] & 0x1f; + + if (nal_unit_type == 7) { + // atleast 1bytes for SPS to decode the type, profile, constrain and level. + if (frame_size < 4) { + return ret; + } + + std::string sps; + sps.append(frame, frame_size); + + if (context->h264_sps == sps) { + return ERROR_H264_DUPLICATED_SPS; + } + context->h264_sps_changed = true; + context->h264_sps = sps; + + return __srs_write_h264_sps_pps(context, dts, pts); + } else if (nal_unit_type == 8) { + + std::string pps; + pps.append(frame, frame_size); + + if (context->h264_pps == pps) { + return ERROR_H264_DUPLICATED_PPS; + } + context->h264_pps_changed = true; + context->h264_pps = pps; + + return __srs_write_h264_sps_pps(context, dts, pts); } else { - return context->enc.write_metadata(data, size); + return __srs_write_h264_ipb_frame(context, frame, frame_size, dts, pts); } - + return ret; } -int srs_flv_size_tag(int data_size) -{ - return SrsFlvEncoder::size_tag(data_size); +/** +* write h264 multiple frames, in annexb format. +*/ +int srs_h264_write_raw_frames(srs_rtmp_t rtmp, + char* frames, int frames_size, u_int32_t dts, u_int32_t pts +) { + int ret = ERROR_SUCCESS; + + srs_assert(frames != NULL); + srs_assert(frames_size > 0); + + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + + if ((ret = context->h264_raw_stream.initialize(frames, frames_size)) != ERROR_SUCCESS) { + return ret; + } + + // use the last error + // @see https://github.com/winlinvip/simple-rtmp-server/issues/203 + // @see https://github.com/winlinvip/simple-rtmp-server/issues/204 + int error_code_return = ret; + + // send each frame. + while (!context->h264_raw_stream.empty()) { + // each frame must prefixed by annexb format. + // about annexb, @see H.264-AVC-ISO_IEC_14496-10.pdf, page 211. + int pnb_start_code = 0; + if (!srs_avc_startswith_annexb(&context->h264_raw_stream, &pnb_start_code)) { + return ERROR_H264_API_NO_PREFIXED; + } + int start = context->h264_raw_stream.pos() + pnb_start_code; + + // find the last frame prefixed by annexb format. + context->h264_raw_stream.skip(pnb_start_code); + while (!context->h264_raw_stream.empty()) { + if (srs_avc_startswith_annexb(&context->h264_raw_stream, NULL)) { + break; + } + context->h264_raw_stream.skip(1); + } + int size = context->h264_raw_stream.pos() - start; + + // send out the frame. + char* frame = context->h264_raw_stream.data() + start; + + // it may be return error, but we must process all packets. + if ((ret = __srs_write_h264_raw_frame(context, frame, size, dts, pts)) != ERROR_SUCCESS) { + error_code_return = ret; + + // ignore known error, process all packets. + if (srs_h264_is_dvbsp_error(ret) + || srs_h264_is_duplicated_sps_error(ret) + || srs_h264_is_duplicated_pps_error(ret) + ) { + continue; + } + + return ret; + } + } + + return error_code_return; } -int64_t srs_flv_tellg(srs_flv_t flv) +srs_h264_bool srs_h264_is_dvbsp_error(int error_code) { - FlvContext* context = (FlvContext*)flv; - return context->reader.tellg(); + return error_code == ERROR_H264_DROP_BEFORE_SPS_PPS; } -void srs_flv_lseek(srs_flv_t flv, int64_t offset) +srs_h264_bool srs_h264_is_duplicated_sps_error(int error_code) { - FlvContext* context = (FlvContext*)flv; - context->reader.lseek(offset); + return error_code == ERROR_H264_DUPLICATED_SPS; } -srs_flv_bool srs_flv_is_eof(int error_code) +srs_h264_bool srs_h264_is_duplicated_pps_error(int error_code) { - return error_code == ERROR_SYSTEM_FILE_EOF; + return error_code == ERROR_H264_DUPLICATED_PPS; } -srs_flv_bool srs_flv_is_sequence_header(char* data, int32_t size) +int srs_h264_startswith_annexb(char* h264_raw_data, int h264_raw_size, int* pnb_start_code) { - return SrsFlvCodec::video_is_sequence_header(data, (int)size); + SrsStream stream; + if (stream.initialize(h264_raw_data, h264_raw_size) != ERROR_SUCCESS) { + return false; + } + + return srs_avc_startswith_annexb(&stream, pnb_start_code); } -srs_flv_bool srs_flv_is_keyframe(char* data, int32_t size) +struct FlvContext { - return SrsFlvCodec::video_is_keyframe(data, (int)size); -} + SrsFileReader reader; + SrsFileWriter writer; + SrsFlvEncoder enc; + SrsFlvDecoder dec; +}; -srs_amf0_t srs_amf0_parse(char* data, int size, int* nparsed) +srs_flv_t srs_flv_open_read(const char* file) { int ret = ERROR_SUCCESS; - srs_amf0_t amf0 = NULL; + FlvContext* flv = new FlvContext(); - SrsStream stream; - if ((ret = stream.initialize(data, size)) != ERROR_SUCCESS) { - return amf0; + if ((ret = flv->reader.open(file)) != ERROR_SUCCESS) { + srs_freep(flv); + return NULL; } - SrsAmf0Any* any = NULL; - if ((ret = SrsAmf0Any::discovery(&stream, &any)) != ERROR_SUCCESS) { - return amf0; + if ((ret = flv->dec.initialize(&flv->reader)) != ERROR_SUCCESS) { + srs_freep(flv); + return NULL; } - stream.skip(-1 * stream.pos()); - if ((ret = any->read(&stream)) != ERROR_SUCCESS) { - srs_freep(any); - return amf0; + return flv; +} + +srs_flv_t srs_flv_open_write(const char* file) +{ + int ret = ERROR_SUCCESS; + + FlvContext* flv = new FlvContext(); + + if ((ret = flv->writer.open(file)) != ERROR_SUCCESS) { + srs_freep(flv); + return NULL; } - if (nparsed) { - *nparsed = stream.pos(); + if ((ret = flv->enc.initialize(&flv->writer)) != ERROR_SUCCESS) { + srs_freep(flv); + return NULL; } - amf0 = (srs_amf0_t)any; - return amf0; + return flv; } -srs_amf0_t srs_amf0_create_number(srs_amf0_number value) +void srs_flv_close(srs_flv_t flv) { - return SrsAmf0Any::number(value); + FlvContext* context = (FlvContext*)flv; + srs_freep(context); } -srs_amf0_t srs_amf0_create_ecma_array() +int srs_flv_read_header(srs_flv_t flv, char header[9]) { - return SrsAmf0Any::ecma_array(); -} + int ret = ERROR_SUCCESS; + + FlvContext* context = (FlvContext*)flv; -srs_amf0_t srs_amf0_create_strict_array() -{ + if (!context->reader.is_open()) { + return ERROR_SYSTEM_IO_INVALID; + } + + if ((ret = context->dec.read_header(header)) != ERROR_SUCCESS) { + return ret; + } + + char ts[4]; // tag size + if ((ret = context->dec.read_previous_tag_size(ts)) != ERROR_SUCCESS) { + return ret; + } + + return ret; +} + +int srs_flv_read_tag_header(srs_flv_t flv, char* ptype, int32_t* pdata_size, u_int32_t* ptime) +{ + int ret = ERROR_SUCCESS; + + FlvContext* context = (FlvContext*)flv; + + if (!context->reader.is_open()) { + return ERROR_SYSTEM_IO_INVALID; + } + + if ((ret = context->dec.read_tag_header(ptype, pdata_size, ptime)) != ERROR_SUCCESS) { + return ret; + } + + return ret; +} + +int srs_flv_read_tag_data(srs_flv_t flv, char* data, int32_t size) +{ + int ret = ERROR_SUCCESS; + + FlvContext* context = (FlvContext*)flv; + + if (!context->reader.is_open()) { + return ERROR_SYSTEM_IO_INVALID; + } + + if ((ret = context->dec.read_tag_data(data, size)) != ERROR_SUCCESS) { + return ret; + } + + char ts[4]; // tag size + if ((ret = context->dec.read_previous_tag_size(ts)) != ERROR_SUCCESS) { + return ret; + } + + return ret; +} + +int srs_flv_write_header(srs_flv_t flv, char header[9]) +{ + int ret = ERROR_SUCCESS; + + FlvContext* context = (FlvContext*)flv; + + if (!context->writer.is_open()) { + return ERROR_SYSTEM_IO_INVALID; + } + + if ((ret = context->enc.write_header(header)) != ERROR_SUCCESS) { + return ret; + } + + return ret; +} + +int srs_flv_write_tag(srs_flv_t flv, char type, int32_t time, char* data, int size) +{ + int ret = ERROR_SUCCESS; + + FlvContext* context = (FlvContext*)flv; + + if (!context->writer.is_open()) { + return ERROR_SYSTEM_IO_INVALID; + } + + if (type == SRS_RTMP_TYPE_AUDIO) { + return context->enc.write_audio(time, data, size); + } else if (type == SRS_RTMP_TYPE_VIDEO) { + return context->enc.write_video(time, data, size); + } else { + return context->enc.write_metadata(data, size); + } + + return ret; +} + +int srs_flv_size_tag(int data_size) +{ + return SrsFlvEncoder::size_tag(data_size); +} + +int64_t srs_flv_tellg(srs_flv_t flv) +{ + FlvContext* context = (FlvContext*)flv; + return context->reader.tellg(); +} + +void srs_flv_lseek(srs_flv_t flv, int64_t offset) +{ + FlvContext* context = (FlvContext*)flv; + context->reader.lseek(offset); +} + +srs_flv_bool srs_flv_is_eof(int error_code) +{ + return error_code == ERROR_SYSTEM_FILE_EOF; +} + +srs_flv_bool srs_flv_is_sequence_header(char* data, int32_t size) +{ + return SrsFlvCodec::video_is_sequence_header(data, (int)size); +} + +srs_flv_bool srs_flv_is_keyframe(char* data, int32_t size) +{ + return SrsFlvCodec::video_is_keyframe(data, (int)size); +} + +srs_amf0_t srs_amf0_parse(char* data, int size, int* nparsed) +{ + int ret = ERROR_SUCCESS; + + srs_amf0_t amf0 = NULL; + + SrsStream stream; + if ((ret = stream.initialize(data, size)) != ERROR_SUCCESS) { + return amf0; + } + + SrsAmf0Any* any = NULL; + if ((ret = SrsAmf0Any::discovery(&stream, &any)) != ERROR_SUCCESS) { + return amf0; + } + + stream.skip(-1 * stream.pos()); + if ((ret = any->read(&stream)) != ERROR_SUCCESS) { + srs_freep(any); + return amf0; + } + + if (nparsed) { + *nparsed = stream.pos(); + } + amf0 = (srs_amf0_t)any; + + return amf0; +} + +srs_amf0_t srs_amf0_create_number(srs_amf0_number value) +{ + return SrsAmf0Any::number(value); +} + +srs_amf0_t srs_amf0_create_ecma_array() +{ + return SrsAmf0Any::ecma_array(); +} + +srs_amf0_t srs_amf0_create_strict_array() +{ return SrsAmf0Any::strict_array(); } @@ -1321,397 +1712,6 @@ void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value) obj->append(any); } -/** -* write audio raw frame to SRS. -*/ -int srs_audio_write_raw_frame(srs_rtmp_t rtmp, - char sound_format, char sound_rate, char sound_size, char sound_type, - char aac_packet_type, char* frame, int frame_size, u_int32_t timestamp -) { - Context* context = (Context*)rtmp; - srs_assert(context); - - // TODO: FIXME: for aac, must send the sequence header first. - - // for audio frame, there is 1 or 2 bytes header: - // 1bytes, SoundFormat|SoundRate|SoundSize|SoundType - // 1bytes, AACPacketType for SoundFormat == 10 - int size = frame_size + 1; - if (aac_packet_type == SrsCodecAudioAAC) { - size += 1; - } - char* data = new char[size]; - char* p = data; - - u_int8_t audio_header = sound_type & 0x01; - audio_header |= (sound_size << 1) & 0x02; - audio_header |= (sound_rate << 2) & 0x0c; - audio_header |= (sound_format << 4) & 0xf0; - - *p++ = audio_header; - - if (aac_packet_type == SrsCodecAudioAAC) { - *p++ = aac_packet_type; - } - - memcpy(p, frame, frame_size); - - return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_AUDIO, timestamp, data, size); -} - -/** -* write h264 packet, with rtmp header. -* @param frame_type, SrsCodecVideoAVCFrameKeyFrame or SrsCodecVideoAVCFrameInterFrame. -* @param avc_packet_type, SrsCodecVideoAVCTypeSequenceHeader or SrsCodecVideoAVCTypeNALU. -* @param h264_raw_data the h.264 raw data, user must free it. -*/ -int __srs_write_h264_packet(Context* context, - int8_t frame_type, int8_t avc_packet_type, - char* h264_raw_data, int h264_raw_size, u_int32_t dts, u_int32_t pts -) { - // the timestamp in rtmp message header is dts. - u_int32_t timestamp = dts; - - // for h264 in RTMP video payload, there is 5bytes header: - // 1bytes, FrameType | CodecID - // 1bytes, AVCPacketType - // 3bytes, CompositionTime, the cts. - // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78 - int size = h264_raw_size + 5; - char* data = new char[size]; - char* p = data; - - // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78 - // Frame Type, Type of video frame. - // CodecID, Codec Identifier. - // set the rtmp header - *p++ = (frame_type << 4) | SrsCodecVideoAVC; - - // AVCPacketType - *p++ = avc_packet_type; - - // CompositionTime - // pts = dts + cts, or - // cts = pts - dts. - // where cts is the header in rtmp video packet payload header. - u_int32_t cts = pts - dts; - char* pp = (char*)&cts; - *p++ = pp[2]; - *p++ = pp[1]; - *p++ = pp[0]; - - // h.264 raw data. - memcpy(p, h264_raw_data, h264_raw_size); - - return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_VIDEO, timestamp, data, size); -} - -/** -* write the h264 sps/pps in context over RTMP. -*/ -int __srs_write_h264_sps_pps(Context* context, u_int32_t dts, u_int32_t pts) -{ - int ret = ERROR_SUCCESS; - - // only send when both sps and pps changed. - if (!context->h264_sps_changed || !context->h264_pps_changed) { - return ret; - } - - // 5bytes sps/pps header: - // configurationVersion, AVCProfileIndication, profile_compatibility, - // AVCLevelIndication, lengthSizeMinusOne - // 3bytes size of sps: - // numOfSequenceParameterSets, sequenceParameterSetLength(2B) - // Nbytes of sps. - // sequenceParameterSetNALUnit - // 3bytes size of pps: - // numOfPictureParameterSets, pictureParameterSetLength - // Nbytes of pps: - // pictureParameterSetNALUnit - int nb_packet = 5 - + 3 + (int)context->h264_sps.length() - + 3 + (int)context->h264_pps.length(); - char* packet = new char[nb_packet]; - SrsAutoFree(char, packet); - - // use stream to generate the h264 packet. - SrsStream stream; - if ((ret = stream.initialize(packet, nb_packet)) != ERROR_SUCCESS) { - return ret; - } - - // decode the SPS: - // @see: 7.3.2.1.1, H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 62 - if (true) { - srs_assert((int)context->h264_sps.length() >= 4); - char* frame = (char*)context->h264_sps.data(); - - // @see: Annex A Profiles and levels, H.264-AVC-ISO_IEC_14496-10.pdf, page 205 - // Baseline profile profile_idc is 66(0x42). - // Main profile profile_idc is 77(0x4d). - // Extended profile profile_idc is 88(0x58). - u_int8_t profile_idc = frame[1]; - //u_int8_t constraint_set = frame[2]; - u_int8_t level_idc = frame[3]; - - // generate the sps/pps header - // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 - // configurationVersion - stream.write_1bytes(0x01); - // AVCProfileIndication - stream.write_1bytes(profile_idc); - // profile_compatibility - stream.write_1bytes(0x00); - // AVCLevelIndication - stream.write_1bytes(level_idc); - // lengthSizeMinusOne, or NAL_unit_length, always use 4bytes size, - // so we always set it to 0x03. - stream.write_1bytes(0x03); - } - - // sps - if (true) { - // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 - // numOfSequenceParameterSets, always 1 - stream.write_1bytes(0x01); - // sequenceParameterSetLength - stream.write_2bytes(context->h264_sps.length()); - // sequenceParameterSetNALUnit - stream.write_string(context->h264_sps); - } - - // pps - if (true) { - // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 - // numOfPictureParameterSets, always 1 - stream.write_1bytes(0x01); - // pictureParameterSetLength - stream.write_2bytes(context->h264_pps.length()); - // pictureParameterSetNALUnit - stream.write_string(context->h264_pps); - } - - // reset sps and pps. - context->h264_sps_changed = false; - context->h264_pps_changed = false; - context->h264_sps_pps_sent = true; - - // TODO: FIXME: for more profile. - // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 - // profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 144 - - // send out h264 packet. - int8_t frame_type = SrsCodecVideoAVCFrameKeyFrame; - int8_t avc_packet_type = SrsCodecVideoAVCTypeSequenceHeader; - return __srs_write_h264_packet( - context, frame_type, avc_packet_type, - packet, nb_packet, dts, pts - ); -} - -/** -* write h264 IPB-frame. -*/ -int __srs_write_h264_ipb_frame(Context* context, - char* data, int size, u_int32_t dts, u_int32_t pts -) { - int ret = ERROR_SUCCESS; - - // when sps or pps not sent, ignore the packet. - // @see https://github.com/winlinvip/simple-rtmp-server/issues/203 - if (!context->h264_sps_pps_sent) { - return ERROR_H264_DROP_BEFORE_SPS_PPS; - } - - // 5bits, 7.3.1 NAL unit syntax, - // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. - // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame - u_int8_t nal_unit_type = (char)data[0] & 0x1f; - - // 4bytes size of nalu: - // NALUnitLength - // Nbytes of nalu. - // NALUnit - int nb_packet = 4 + size; - char* packet = new char[nb_packet]; - SrsAutoFree(char, packet); - - // use stream to generate the h264 packet. - SrsStream stream; - if ((ret = stream.initialize(packet, nb_packet)) != ERROR_SUCCESS) { - return ret; - } - - // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 - // lengthSizeMinusOne, or NAL_unit_length, always use 4bytes size - u_int32_t NAL_unit_length = size; - - // mux the avc NALU in "ISO Base Media File Format" - // from H.264-AVC-ISO_IEC_14496-15.pdf, page 20 - // NALUnitLength - stream.write_4bytes(NAL_unit_length); - // NALUnit - stream.write_bytes(data, size); - - // send out h264 packet. - int8_t frame_type = SrsCodecVideoAVCFrameInterFrame; - if (nal_unit_type != 1) { - frame_type = SrsCodecVideoAVCFrameKeyFrame; - } - int8_t avc_packet_type = SrsCodecVideoAVCTypeNALU; - return __srs_write_h264_packet( - context, frame_type, avc_packet_type, - packet, nb_packet, dts, pts - ); - - return ret; -} - -/** -* write h264 raw frame, maybe sps/pps/IPB-frame. -*/ -int __srs_write_h264_raw_frame(Context* context, - char* frame, int frame_size, u_int32_t dts, u_int32_t pts -) { - int ret = ERROR_SUCCESS; - - // ignore invalid frame, - // atleast 1bytes for SPS to decode the type - if (frame_size < 1) { - return ret; - } - - // 5bits, 7.3.1 NAL unit syntax, - // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. - // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame - u_int8_t nal_unit_type = (char)frame[0] & 0x1f; - - if (nal_unit_type == 7) { - // atleast 1bytes for SPS to decode the type, profile, constrain and level. - if (frame_size < 4) { - return ret; - } - - std::string sps; - sps.append(frame, frame_size); - - if (context->h264_sps == sps) { - return ERROR_H264_DUPLICATED_SPS; - } - context->h264_sps_changed = true; - context->h264_sps = sps; - - return __srs_write_h264_sps_pps(context, dts, pts); - } else if (nal_unit_type == 8) { - - std::string pps; - pps.append(frame, frame_size); - - if (context->h264_pps == pps) { - return ERROR_H264_DUPLICATED_PPS; - } - context->h264_pps_changed = true; - context->h264_pps = pps; - - return __srs_write_h264_sps_pps(context, dts, pts); - } else { - return __srs_write_h264_ipb_frame(context, frame, frame_size, dts, pts); - } - - return ret; -} - -/** -* write h264 multiple frames, in annexb format. -*/ -int srs_h264_write_raw_frames(srs_rtmp_t rtmp, - char* frames, int frames_size, u_int32_t dts, u_int32_t pts -) { - int ret = ERROR_SUCCESS; - - srs_assert(frames != NULL); - srs_assert(frames_size > 0); - - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; - - if ((ret = context->h264_raw_stream.initialize(frames, frames_size)) != ERROR_SUCCESS) { - return ret; - } - - // use the last error - // @see https://github.com/winlinvip/simple-rtmp-server/issues/203 - // @see https://github.com/winlinvip/simple-rtmp-server/issues/204 - int error_code_return = ret; - - // send each frame. - while (!context->h264_raw_stream.empty()) { - // each frame must prefixed by annexb format. - // about annexb, @see H.264-AVC-ISO_IEC_14496-10.pdf, page 211. - int pnb_start_code = 0; - if (!srs_avc_startswith_annexb(&context->h264_raw_stream, &pnb_start_code)) { - return ERROR_H264_API_NO_PREFIXED; - } - int start = context->h264_raw_stream.pos() + pnb_start_code; - - // find the last frame prefixed by annexb format. - context->h264_raw_stream.skip(pnb_start_code); - while (!context->h264_raw_stream.empty()) { - if (srs_avc_startswith_annexb(&context->h264_raw_stream, NULL)) { - break; - } - context->h264_raw_stream.skip(1); - } - int size = context->h264_raw_stream.pos() - start; - - // send out the frame. - char* frame = context->h264_raw_stream.data() + start; - - // it may be return error, but we must process all packets. - if ((ret = __srs_write_h264_raw_frame(context, frame, size, dts, pts)) != ERROR_SUCCESS) { - error_code_return = ret; - - // ignore known error, process all packets. - if (srs_h264_is_dvbsp_error(ret) - || srs_h264_is_duplicated_sps_error(ret) - || srs_h264_is_duplicated_pps_error(ret) - ) { - continue; - } - - return ret; - } - } - - return error_code_return; -} - -srs_h264_bool srs_h264_is_dvbsp_error(int error_code) -{ - return error_code == ERROR_H264_DROP_BEFORE_SPS_PPS; -} - -srs_h264_bool srs_h264_is_duplicated_sps_error(int error_code) -{ - return error_code == ERROR_H264_DUPLICATED_SPS; -} - -srs_h264_bool srs_h264_is_duplicated_pps_error(int error_code) -{ - return error_code == ERROR_H264_DUPLICATED_PPS; -} - -int srs_h264_startswith_annexb(char* h264_raw_data, int h264_raw_size, int* pnb_start_code) -{ - SrsStream stream; - if (stream.initialize(h264_raw_data, h264_raw_size) != ERROR_SUCCESS) { - return false; - } - - return srs_avc_startswith_annexb(&stream, pnb_start_code); -} - int64_t srs_utils_get_time_ms() { srs_update_system_time_ms(); diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index d0638eb127..8bed205965 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -262,164 +262,6 @@ extern int srs_rtmp_write_packet(srs_rtmp_t rtmp, char type, u_int32_t timestamp, char* data, int size ); -/************************************************************* -************************************************************** -* flv codec -* @example /trunk/research/librtmp/srs_flv_injecter.c -* @example /trunk/research/librtmp/srs_flv_parser.c -* @example /trunk/research/librtmp/srs_ingest_flv.c -* @example /trunk/research/librtmp/srs_ingest_rtmp.c -************************************************************** -*************************************************************/ -typedef void* srs_flv_t; -typedef int srs_flv_bool; -/* open flv file for both read/write. */ -extern srs_flv_t srs_flv_open_read(const char* file); -extern srs_flv_t srs_flv_open_write(const char* file); -extern void srs_flv_close(srs_flv_t flv); -/** -* read the flv header. 9bytes header. -* @param header, @see E.2 The FLV header, flv_v10_1.pdf in SRS doc. -* 3bytes, signature, "FLV", -* 1bytes, version, 0x01, -* 1bytes, flags, UB[5] 0, UB[1] audio present, UB[1] 0, UB[1] video present. -* 4bytes, dataoffset, 0x09, The length of this header in bytes -* -* @return 0, success; otherswise, failed. -* @remark, drop the 4bytes zero previous tag size. -*/ -extern int srs_flv_read_header(srs_flv_t flv, char header[9]); -/** -* read the flv tag header, 1bytes tag, 3bytes data_size, -* 4bytes time, 3bytes stream id. -* @param ptype, output the type of tag, macros: -* SRS_RTMP_TYPE_AUDIO, FlvTagAudio -* SRS_RTMP_TYPE_VIDEO, FlvTagVideo -* SRS_RTMP_TYPE_SCRIPT, FlvTagScript -* @param pdata_size, output the size of tag data. -* @param ptime, output the time of tag, the dts in ms. -* -* @return 0, success; otherswise, failed. -* @remark, user must ensure the next is a tag, srs never check it. -*/ -extern int srs_flv_read_tag_header(srs_flv_t flv, - char* ptype, int32_t* pdata_size, u_int32_t* ptime -); -/** -* read the tag data. drop the 4bytes previous tag size -* @param data, the data to read, user alloc and free it. -* @param size, the size of data to read, get by srs_flv_read_tag_header(). -* @remark, srs will ignore and drop the 4bytes previous tag size. -*/ -extern int srs_flv_read_tag_data(srs_flv_t flv, char* data, int32_t size); -/** -* write the flv header. 9bytes header. -* @param header, @see E.2 The FLV header, flv_v10_1.pdf in SRS doc. -* 3bytes, signature, "FLV", -* 1bytes, version, 0x01, -* 1bytes, flags, UB[5] 0, UB[1] audio present, UB[1] 0, UB[1] video present. -* 4bytes, dataoffset, 0x09, The length of this header in bytes -* -* @return 0, success; otherswise, failed. -* @remark, auto write the 4bytes zero previous tag size. -*/ -extern int srs_flv_write_header(srs_flv_t flv, char header[9]); -/** -* write the flv tag to file. -* -* @return 0, success; otherswise, failed. -* @remark, auto write the 4bytes zero previous tag size. -*/ -/* write flv tag to file, auto write the 4bytes previous tag size */ -extern int srs_flv_write_tag(srs_flv_t flv, - char type, int32_t time, char* data, int size -); -/** -* get the tag size, for flv injecter to adjust offset, -* size = tag_header(11B) + data_size + previous_tag(4B) -* @return the size of tag. -*/ -extern int srs_flv_size_tag(int data_size); -/* file stream */ -/* file stream tellg to get offset */ -extern int64_t srs_flv_tellg(srs_flv_t flv); -/* seek file stream, offset is form the start of file */ -extern void srs_flv_lseek(srs_flv_t flv, int64_t offset); -/* error code */ -/* whether the error code indicates EOF */ -extern srs_flv_bool srs_flv_is_eof(int error_code); -/* media codec */ -/** -* whether the video body is sequence header -* @param data, the data of tag, read by srs_flv_read_tag_data(). -* @param size, the size of tag, read by srs_flv_read_tag_data(). -*/ -extern srs_flv_bool srs_flv_is_sequence_header(char* data, int32_t size); -/** -* whether the video body is keyframe -* @param data, the data of tag, read by srs_flv_read_tag_data(). -* @param size, the size of tag, read by srs_flv_read_tag_data(). -*/ -extern srs_flv_bool srs_flv_is_keyframe(char* data, int32_t size); - -/************************************************************* -************************************************************** -* amf0 codec -* @example /trunk/research/librtmp/srs_ingest_flv.c -* @example /trunk/research/librtmp/srs_ingest_rtmp.c -************************************************************** -*************************************************************/ -/* the output handler. */ -typedef void* srs_amf0_t; -typedef int srs_amf0_bool; -typedef double srs_amf0_number; -/** -* parse amf0 from data. -* @param nparsed, the parsed size, NULL to ignore. -* @return the parsed amf0 object. NULL for error. -*/ -extern srs_amf0_t srs_amf0_parse(char* data, int size, int* nparsed); -extern srs_amf0_t srs_amf0_create_number(srs_amf0_number value); -extern srs_amf0_t srs_amf0_create_ecma_array(); -extern srs_amf0_t srs_amf0_create_strict_array(); -extern srs_amf0_t srs_amf0_create_object(); -extern void srs_amf0_free(srs_amf0_t amf0); -extern void srs_amf0_free_bytes(char* data); -/* size and to bytes */ -extern int srs_amf0_size(srs_amf0_t amf0); -extern int srs_amf0_serialize(srs_amf0_t amf0, char* data, int size); -/* type detecter */ -extern srs_amf0_bool srs_amf0_is_string(srs_amf0_t amf0); -extern srs_amf0_bool srs_amf0_is_boolean(srs_amf0_t amf0); -extern srs_amf0_bool srs_amf0_is_number(srs_amf0_t amf0); -extern srs_amf0_bool srs_amf0_is_null(srs_amf0_t amf0); -extern srs_amf0_bool srs_amf0_is_object(srs_amf0_t amf0); -extern srs_amf0_bool srs_amf0_is_ecma_array(srs_amf0_t amf0); -extern srs_amf0_bool srs_amf0_is_strict_array(srs_amf0_t amf0); -/* value converter */ -extern const char* srs_amf0_to_string(srs_amf0_t amf0); -extern srs_amf0_bool srs_amf0_to_boolean(srs_amf0_t amf0); -extern srs_amf0_number srs_amf0_to_number(srs_amf0_t amf0); -/* value setter */ -extern void srs_amf0_set_number(srs_amf0_t amf0, srs_amf0_number value); -/* object value converter */ -extern int srs_amf0_object_property_count(srs_amf0_t amf0); -extern const char* srs_amf0_object_property_name_at(srs_amf0_t amf0, int index); -extern srs_amf0_t srs_amf0_object_property_value_at(srs_amf0_t amf0, int index); -extern srs_amf0_t srs_amf0_object_property(srs_amf0_t amf0, const char* name); -extern void srs_amf0_object_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value); -extern void srs_amf0_object_clear(srs_amf0_t amf0); -/* ecma array value converter */ -extern int srs_amf0_ecma_array_property_count(srs_amf0_t amf0); -extern const char* srs_amf0_ecma_array_property_name_at(srs_amf0_t amf0, int index); -extern srs_amf0_t srs_amf0_ecma_array_property_value_at(srs_amf0_t amf0, int index); -extern srs_amf0_t srs_amf0_ecma_array_property(srs_amf0_t amf0, const char* name); -extern void srs_amf0_ecma_array_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value); -/* strict array value converter */ -extern int srs_amf0_strict_array_property_count(srs_amf0_t amf0); -extern srs_amf0_t srs_amf0_strict_array_property_at(srs_amf0_t amf0, int index); -extern void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value); - /************************************************************* ************************************************************** * audio raw codec @@ -583,6 +425,164 @@ extern int srs_h264_startswith_annexb( int* pnb_start_code ); +/************************************************************* +************************************************************** +* flv codec +* @example /trunk/research/librtmp/srs_flv_injecter.c +* @example /trunk/research/librtmp/srs_flv_parser.c +* @example /trunk/research/librtmp/srs_ingest_flv.c +* @example /trunk/research/librtmp/srs_ingest_rtmp.c +************************************************************** +*************************************************************/ +typedef void* srs_flv_t; +typedef int srs_flv_bool; +/* open flv file for both read/write. */ +extern srs_flv_t srs_flv_open_read(const char* file); +extern srs_flv_t srs_flv_open_write(const char* file); +extern void srs_flv_close(srs_flv_t flv); +/** +* read the flv header. 9bytes header. +* @param header, @see E.2 The FLV header, flv_v10_1.pdf in SRS doc. +* 3bytes, signature, "FLV", +* 1bytes, version, 0x01, +* 1bytes, flags, UB[5] 0, UB[1] audio present, UB[1] 0, UB[1] video present. +* 4bytes, dataoffset, 0x09, The length of this header in bytes +* +* @return 0, success; otherswise, failed. +* @remark, drop the 4bytes zero previous tag size. +*/ +extern int srs_flv_read_header(srs_flv_t flv, char header[9]); +/** +* read the flv tag header, 1bytes tag, 3bytes data_size, +* 4bytes time, 3bytes stream id. +* @param ptype, output the type of tag, macros: +* SRS_RTMP_TYPE_AUDIO, FlvTagAudio +* SRS_RTMP_TYPE_VIDEO, FlvTagVideo +* SRS_RTMP_TYPE_SCRIPT, FlvTagScript +* @param pdata_size, output the size of tag data. +* @param ptime, output the time of tag, the dts in ms. +* +* @return 0, success; otherswise, failed. +* @remark, user must ensure the next is a tag, srs never check it. +*/ +extern int srs_flv_read_tag_header(srs_flv_t flv, + char* ptype, int32_t* pdata_size, u_int32_t* ptime +); +/** +* read the tag data. drop the 4bytes previous tag size +* @param data, the data to read, user alloc and free it. +* @param size, the size of data to read, get by srs_flv_read_tag_header(). +* @remark, srs will ignore and drop the 4bytes previous tag size. +*/ +extern int srs_flv_read_tag_data(srs_flv_t flv, char* data, int32_t size); +/** +* write the flv header. 9bytes header. +* @param header, @see E.2 The FLV header, flv_v10_1.pdf in SRS doc. +* 3bytes, signature, "FLV", +* 1bytes, version, 0x01, +* 1bytes, flags, UB[5] 0, UB[1] audio present, UB[1] 0, UB[1] video present. +* 4bytes, dataoffset, 0x09, The length of this header in bytes +* +* @return 0, success; otherswise, failed. +* @remark, auto write the 4bytes zero previous tag size. +*/ +extern int srs_flv_write_header(srs_flv_t flv, char header[9]); +/** +* write the flv tag to file. +* +* @return 0, success; otherswise, failed. +* @remark, auto write the 4bytes zero previous tag size. +*/ +/* write flv tag to file, auto write the 4bytes previous tag size */ +extern int srs_flv_write_tag(srs_flv_t flv, + char type, int32_t time, char* data, int size +); +/** +* get the tag size, for flv injecter to adjust offset, +* size = tag_header(11B) + data_size + previous_tag(4B) +* @return the size of tag. +*/ +extern int srs_flv_size_tag(int data_size); +/* file stream */ +/* file stream tellg to get offset */ +extern int64_t srs_flv_tellg(srs_flv_t flv); +/* seek file stream, offset is form the start of file */ +extern void srs_flv_lseek(srs_flv_t flv, int64_t offset); +/* error code */ +/* whether the error code indicates EOF */ +extern srs_flv_bool srs_flv_is_eof(int error_code); +/* media codec */ +/** +* whether the video body is sequence header +* @param data, the data of tag, read by srs_flv_read_tag_data(). +* @param size, the size of tag, read by srs_flv_read_tag_data(). +*/ +extern srs_flv_bool srs_flv_is_sequence_header(char* data, int32_t size); +/** +* whether the video body is keyframe +* @param data, the data of tag, read by srs_flv_read_tag_data(). +* @param size, the size of tag, read by srs_flv_read_tag_data(). +*/ +extern srs_flv_bool srs_flv_is_keyframe(char* data, int32_t size); + +/************************************************************* +************************************************************** +* amf0 codec +* @example /trunk/research/librtmp/srs_ingest_flv.c +* @example /trunk/research/librtmp/srs_ingest_rtmp.c +************************************************************** +*************************************************************/ +/* the output handler. */ +typedef void* srs_amf0_t; +typedef int srs_amf0_bool; +typedef double srs_amf0_number; +/** +* parse amf0 from data. +* @param nparsed, the parsed size, NULL to ignore. +* @return the parsed amf0 object. NULL for error. +*/ +extern srs_amf0_t srs_amf0_parse(char* data, int size, int* nparsed); +extern srs_amf0_t srs_amf0_create_number(srs_amf0_number value); +extern srs_amf0_t srs_amf0_create_ecma_array(); +extern srs_amf0_t srs_amf0_create_strict_array(); +extern srs_amf0_t srs_amf0_create_object(); +extern void srs_amf0_free(srs_amf0_t amf0); +extern void srs_amf0_free_bytes(char* data); +/* size and to bytes */ +extern int srs_amf0_size(srs_amf0_t amf0); +extern int srs_amf0_serialize(srs_amf0_t amf0, char* data, int size); +/* type detecter */ +extern srs_amf0_bool srs_amf0_is_string(srs_amf0_t amf0); +extern srs_amf0_bool srs_amf0_is_boolean(srs_amf0_t amf0); +extern srs_amf0_bool srs_amf0_is_number(srs_amf0_t amf0); +extern srs_amf0_bool srs_amf0_is_null(srs_amf0_t amf0); +extern srs_amf0_bool srs_amf0_is_object(srs_amf0_t amf0); +extern srs_amf0_bool srs_amf0_is_ecma_array(srs_amf0_t amf0); +extern srs_amf0_bool srs_amf0_is_strict_array(srs_amf0_t amf0); +/* value converter */ +extern const char* srs_amf0_to_string(srs_amf0_t amf0); +extern srs_amf0_bool srs_amf0_to_boolean(srs_amf0_t amf0); +extern srs_amf0_number srs_amf0_to_number(srs_amf0_t amf0); +/* value setter */ +extern void srs_amf0_set_number(srs_amf0_t amf0, srs_amf0_number value); +/* object value converter */ +extern int srs_amf0_object_property_count(srs_amf0_t amf0); +extern const char* srs_amf0_object_property_name_at(srs_amf0_t amf0, int index); +extern srs_amf0_t srs_amf0_object_property_value_at(srs_amf0_t amf0, int index); +extern srs_amf0_t srs_amf0_object_property(srs_amf0_t amf0, const char* name); +extern void srs_amf0_object_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value); +extern void srs_amf0_object_clear(srs_amf0_t amf0); +/* ecma array value converter */ +extern int srs_amf0_ecma_array_property_count(srs_amf0_t amf0); +extern const char* srs_amf0_ecma_array_property_name_at(srs_amf0_t amf0, int index); +extern srs_amf0_t srs_amf0_ecma_array_property_value_at(srs_amf0_t amf0, int index); +extern srs_amf0_t srs_amf0_ecma_array_property(srs_amf0_t amf0, const char* name); +extern void srs_amf0_ecma_array_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value); +/* strict array value converter */ +extern int srs_amf0_strict_array_property_count(srs_amf0_t amf0); +extern srs_amf0_t srs_amf0_strict_array_property_at(srs_amf0_t amf0, int index); +extern void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value); + /************************************************************* ************************************************************** * utilities From bb7a19636a6efb835e3cf810b5e0c397f54ee5fe Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 21 Nov 2014 14:30:01 +0800 Subject: [PATCH 204/800] fix the bandwidth file name bug. --- trunk/research/librtmp/srs_bandwidth_check.c | 2 +- trunk/src/srs/srs.upp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/trunk/research/librtmp/srs_bandwidth_check.c b/trunk/research/librtmp/srs_bandwidth_check.c index fa940480be..46e8dc2069 100644 --- a/trunk/research/librtmp/srs_bandwidth_check.c +++ b/trunk/research/librtmp/srs_bandwidth_check.c @@ -21,7 +21,7 @@ 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. */ /** -gcc srs_rtmp_bandwidth_check.c ../../objs/lib/srs_librtmp.a -g -O0 -lstdc++ -o srs_rtmp_bandwidth_check +gcc srs_bandwidth_check.c ../../objs/lib/srs_librtmp.a -g -O0 -lstdc++ -o srs_bandwidth_check */ #include diff --git a/trunk/src/srs/srs.upp b/trunk/src/srs/srs.upp index 3320ab387b..da92f3ebf9 100755 --- a/trunk/src/srs/srs.upp +++ b/trunk/src/srs/srs.upp @@ -129,7 +129,7 @@ file ..\utest\srs_utest_reload.cpp, research readonly separator, ..\..\research\librtmp\srs_audio_raw_publish.c, - ..\..\research\librtmp\srs_rtmp_bandwidth_check.c, + ..\..\research\librtmp\srs_bandwidth_check.c, ..\..\research\librtmp\srs_detect_rtmp.c, ..\..\research\librtmp\srs_flv_injecter.c, ..\..\research\librtmp\srs_flv_parser.c, From d80b580c2fd69a8796d622f12c868995fce9a7fd Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 21 Nov 2014 16:08:29 +0800 Subject: [PATCH 205/800] refine librtmp, remove the _get_ for some attribute get. --- trunk/research/librtmp/srs_detect_rtmp.c | 18 +++++++++--------- trunk/research/librtmp/srs_ingest_flv.c | 8 ++++---- trunk/src/libs/srs_librtmp.cpp | 18 +++++++++--------- trunk/src/libs/srs_librtmp.hpp | 12 ++++++------ 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/trunk/research/librtmp/srs_detect_rtmp.c b/trunk/research/librtmp/srs_detect_rtmp.c index 155d28bd0d..84d18cc34d 100644 --- a/trunk/research/librtmp/srs_detect_rtmp.c +++ b/trunk/research/librtmp/srs_detect_rtmp.c @@ -35,7 +35,7 @@ int main(int argc, char** argv) srs_rtmp_t rtmp; // time - int64_t time_startup = srs_utils_get_time_ms(); + int64_t time_startup = srs_utils_time_ms(); int64_t time_dns_resolve = 0; int64_t time_socket_connect = 0; int64_t time_play_stream = 0; @@ -95,14 +95,14 @@ int main(int argc, char** argv) goto rtmp_destroy; } srs_human_trace("dns resolve success"); - time_dns_resolve = srs_utils_get_time_ms(); + time_dns_resolve = srs_utils_time_ms(); if ((ret = __srs_rtmp_connect_server(rtmp)) != 0) { srs_human_trace("socket connect failed. ret=%d", ret); goto rtmp_destroy; } srs_human_trace("socket connect success"); - time_socket_connect = srs_utils_get_time_ms(); + time_socket_connect = srs_utils_time_ms(); if ((ret = __srs_rtmp_do_simple_handshake(rtmp)) != 0) { srs_human_trace("do simple handshake failed. ret=%d", ret); @@ -121,7 +121,7 @@ int main(int argc, char** argv) goto rtmp_destroy; } srs_human_trace("play stream success"); - time_play_stream = srs_utils_get_time_ms(); + time_play_stream = srs_utils_time_ms(); for (;;) { if ((ret = srs_rtmp_read_packet(rtmp, &type, ×tamp, &data, &size)) != 0) { @@ -133,7 +133,7 @@ int main(int argc, char** argv) if (SRS_RTMP_TYPE_VIDEO == type || SRS_RTMP_TYPE_AUDIO == type) { if (time_first_packet <= 0) { - time_first_packet = srs_utils_get_time_ms(); + time_first_packet = srs_utils_time_ms(); } if (basetime <= 0) { basetime = timestamp; @@ -142,7 +142,7 @@ int main(int argc, char** argv) free(data); - if (srs_utils_get_time_ms() - time_startup > timeout * 1000) { + if (srs_utils_time_ms() - time_startup > timeout * 1000) { srs_human_trace("timeout, terminate."); goto rtmp_destroy; } @@ -154,11 +154,11 @@ int main(int argc, char** argv) } rtmp_destroy: - bytes_nsend = srs_utils_get_send_bytes(rtmp); - bytes_nrecv = srs_utils_get_recv_bytes(rtmp); + bytes_nsend = srs_utils_send_bytes(rtmp); + bytes_nrecv = srs_utils_recv_bytes(rtmp); srs_rtmp_destroy(rtmp); - time_cleanup = srs_utils_get_time_ms(); + time_cleanup = srs_utils_time_ms(); time_duration = (int)(time_cleanup - time_startup); // print result to stderr. diff --git a/trunk/research/librtmp/srs_ingest_flv.c b/trunk/research/librtmp/srs_ingest_flv.c index 8b21170f63..55466af4be 100644 --- a/trunk/research/librtmp/srs_ingest_flv.c +++ b/trunk/research/librtmp/srs_ingest_flv.c @@ -48,7 +48,7 @@ int main(int argc, char** argv) int ret = 0; // main function - tools_main_entrance_startup_time = srs_utils_get_time_ms(); + tools_main_entrance_startup_time = srs_utils_time_ms(); // user option parse index. int opt = 0; @@ -215,7 +215,7 @@ int connect_oc(srs_rtmp_t ortmp) int64_t re_create() { // if not very precise, we can directly use this as re. - int64_t re = srs_utils_get_time_ms(); + int64_t re = srs_utils_time_ms(); // use the starttime to get the deviation int64_t deviation = re - tools_main_entrance_startup_time; @@ -236,7 +236,7 @@ int64_t re_create() void re_update(int64_t re, int32_t starttime, u_int32_t time) { // send by pulse algorithm. - int64_t now = srs_utils_get_time_ms(); + int64_t now = srs_utils_time_ms(); int64_t diff = time - starttime - (now -re); if (diff > RE_PULSE_MS) { usleep(diff * 1000); @@ -246,7 +246,7 @@ void re_cleanup(int64_t re, int32_t starttime, u_int32_t time) { // for the last pulse, always sleep. // for the virtual live encoder long time publishing. - int64_t now = srs_utils_get_time_ms(); + int64_t now = srs_utils_time_ms(); int64_t diff = time - starttime - (now -re); if (diff > 0) { srs_human_trace("re_cleanup, diff=%d, start=%d, last=%d ms", diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index 591faf819e..cb826f9255 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -1712,20 +1712,20 @@ void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value) obj->append(any); } -int64_t srs_utils_get_time_ms() +int64_t srs_utils_time_ms() { srs_update_system_time_ms(); return srs_get_system_time_ms(); } -int64_t srs_utils_get_send_bytes(srs_rtmp_t rtmp) +int64_t srs_utils_send_bytes(srs_rtmp_t rtmp) { srs_assert(rtmp != NULL); Context* context = (Context*)rtmp; return context->rtmp->get_send_bytes(); } -int64_t srs_utils_get_recv_bytes(srs_rtmp_t rtmp) +int64_t srs_utils_recv_bytes(srs_rtmp_t rtmp) { srs_assert(rtmp != NULL); Context* context = (Context*)rtmp; @@ -1773,7 +1773,7 @@ int srs_utils_parse_timestamp( return ret; } -char srs_utils_get_flv_video_codec_id(char* data, int size) +char srs_utils_flv_video_codec_id(char* data, int size) { if (size < 1) { return 0; @@ -1785,7 +1785,7 @@ char srs_utils_get_flv_video_codec_id(char* data, int size) return codec_id; } -char srs_utils_get_flv_video_avc_packet_type(char* data, int size) +char srs_utils_flv_video_avc_packet_type(char* data, int size) { if (size < 2) { return -1; @@ -1804,7 +1804,7 @@ char srs_utils_get_flv_video_avc_packet_type(char* data, int size) return avc_packet_type; } -char srs_utils_get_flv_video_frame_type(char* data, int size) +char srs_utils_flv_video_frame_type(char* data, int size) { if (size < 1) { return -1; @@ -1924,9 +1924,9 @@ int srs_human_print_rtmp_packet(char type, u_int32_t timestamp, char* data, int if (type == SRS_RTMP_TYPE_VIDEO) { srs_human_trace("Video packet type=%s, dts=%d, pts=%d, size=%d, %s(%s,%s)", srs_human_flv_tag_type2string(type), timestamp, pts, size, - srs_human_flv_video_codec_id2string(srs_utils_get_flv_video_codec_id(data, size)), - srs_human_flv_video_avc_packet_type2string(srs_utils_get_flv_video_avc_packet_type(data, size)), - srs_human_flv_video_frame_type2string(srs_utils_get_flv_video_frame_type(data, size)) + srs_human_flv_video_codec_id2string(srs_utils_flv_video_codec_id(data, size)), + srs_human_flv_video_avc_packet_type2string(srs_utils_flv_video_avc_packet_type(data, size)), + srs_human_flv_video_frame_type2string(srs_utils_flv_video_frame_type(data, size)) ); } else if (type == SRS_RTMP_TYPE_AUDIO) { srs_human_trace("Audio packet type=%s, dts=%d, pts=%d, size=%d", diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index 8bed205965..d0ffd15674 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -592,17 +592,17 @@ extern void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value); * get the current system time in ms. * use gettimeofday() to get system time. */ -extern int64_t srs_utils_get_time_ms(); +extern int64_t srs_utils_time_ms(); /** * get the send bytes. */ -extern int64_t srs_utils_get_send_bytes(srs_rtmp_t rtmp); +extern int64_t srs_utils_send_bytes(srs_rtmp_t rtmp); /** * get the recv bytes. */ -extern int64_t srs_utils_get_recv_bytes(srs_rtmp_t rtmp); +extern int64_t srs_utils_recv_bytes(srs_rtmp_t rtmp); /** * parse the dts and pts by time in header and data in tag, @@ -635,7 +635,7 @@ extern int srs_utils_parse_timestamp( * 7 = AVC * @return the code id. 0 for error. */ -extern char srs_utils_get_flv_video_codec_id(char* data, int size); +extern char srs_utils_flv_video_codec_id(char* data, int size); /** * get the AVCPacketType of video tag. @@ -646,7 +646,7 @@ extern char srs_utils_get_flv_video_codec_id(char* data, int size); * not required or supported) * @return the avc packet type. -1(0xff) for error. */ -extern char srs_utils_get_flv_video_avc_packet_type(char* data, int size); +extern char srs_utils_flv_video_avc_packet_type(char* data, int size); /** * get the FrameType of video tag. @@ -658,7 +658,7 @@ extern char srs_utils_get_flv_video_avc_packet_type(char* data, int size); * 5 = video info/command frame * @return the frame type. 0 for error. */ -extern char srs_utils_get_flv_video_frame_type(char* data, int size); +extern char srs_utils_flv_video_frame_type(char* data, int size); /************************************************************* ************************************************************** From d3c770d252ad43e1d40c44a457e87d44e329df2a Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 21 Nov 2014 17:09:52 +0800 Subject: [PATCH 206/800] refine librtmp, add audio video detail. --- trunk/src/libs/srs_librtmp.cpp | 194 ++++++++++++++++++++++++++++++++- trunk/src/libs/srs_librtmp.hpp | 137 ++++++++++++++++++++++- 2 files changed, 327 insertions(+), 4 deletions(-) diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index cb826f9255..362963d57f 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -1823,6 +1823,85 @@ char srs_utils_flv_video_frame_type(char* data, int size) return frame_type; } +char srs_utils_flv_audio_sound_format(char* data, int size) +{ + if (size < 1) { + return -1; + } + + u_int8_t sound_format = data[0]; + sound_format = (sound_format >> 4) & 0x0f; + if (sound_format > 15 || sound_format == 12 || sound_format == 13) { + return -1; + } + + return sound_format; +} + +char srs_utils_flv_audio_sound_rate(char* data, int size) +{ + if (size < 1) { + return -1; + } + + u_int8_t sound_rate = data[0]; + sound_rate = (sound_rate >> 2) & 0x03; + if (sound_rate > 3) { + return -1; + } + + return sound_rate; +} + +char srs_utils_flv_audio_sound_size(char* data, int size) +{ + if (size < 1) { + return -1; + } + + u_int8_t sound_size = data[0]; + sound_size = (sound_size >> 1) & 0x01; + if (sound_size > 1) { + return -1; + } + + return sound_size; +} + +char srs_utils_flv_audio_sound_type(char* data, int size) +{ + if (size < 1) { + return -1; + } + + u_int8_t sound_type = data[0]; + sound_type = sound_type & 0x01; + if (sound_type > 1) { + return -1; + } + + return sound_type; +} + +char srs_utils_flv_audio_aac_packet_type(char* data, int size) +{ + if (size < 2) { + return -1; + } + + if (srs_utils_flv_audio_sound_format(data, size) != 10) { + return -1; + } + + u_int8_t aac_packet_type = data[1]; + aac_packet_type = aac_packet_type; + if (aac_packet_type > 1) { + return -1; + } + + return aac_packet_type; +} + char* srs_human_amf0_print(srs_amf0_t amf0, char** pdata, int* psize) { if (!amf0) { @@ -1876,7 +1955,7 @@ const char* srs_human_flv_video_codec_id2string(char codec_id) const char* srs_human_flv_video_avc_packet_type2string(char avc_packet_type) { - static const char* sps_pps = "SpsPps"; + static const char* sps_pps = "SH"; static const char* nalu = "Nalu"; static const char* sps_pps_end = "SpsPpsEnd"; static const char* unknown = "Unknown"; @@ -1912,6 +1991,109 @@ const char* srs_human_flv_video_frame_type2string(char frame_type) return unknown; } +const char* srs_human_flv_audio_sound_format2string(char sound_format) +{ + static const char* linear_pcm = "LinearPCM"; + static const char* ad_pcm = "ADPCM"; + static const char* mp3 = "MP3"; + static const char* linear_pcm_le = "LinearPCMLe"; + static const char* nellymoser_16khz = "NellymoserKHz16"; + static const char* nellymoser_8khz = "NellymoserKHz8"; + static const char* nellymoser = "Nellymoser"; + static const char* g711_a_pcm = "G711APCM"; + static const char* g711_mu_pcm = "G711MuPCM"; + static const char* reserved = "Reserved"; + static const char* aac = "AAC"; + static const char* speex = "Speex"; + static const char* mp3_8khz = "MP3KHz8"; + static const char* device_specific = "DeviceSpecific"; + static const char* unknown = "Unknown"; + + switch (sound_format) { + case 0: return linear_pcm; + case 1: return ad_pcm; + case 2: return mp3; + case 3: return linear_pcm_le; + case 4: return nellymoser_16khz; + case 5: return nellymoser_8khz; + case 6: return nellymoser; + case 7: return g711_a_pcm; + case 8: return g711_mu_pcm; + case 9: return reserved; + case 10: return aac; + case 11: return speex; + case 14: return mp3_8khz; + case 15: return device_specific; + default: return unknown; + } + + return unknown; +} + +const char* srs_human_flv_audio_sound_rate2string(char sound_rate) +{ + static const char* khz_5_5 = "5.5KHz"; + static const char* khz_11 = "11KHz"; + static const char* khz_22 = "22KHz"; + static const char* khz_44 = "44KHz"; + static const char* unknown = "Unknown"; + + switch (sound_rate) { + case 0: return khz_5_5; + case 1: return khz_11; + case 2: return khz_22; + case 3: return khz_44; + default: return unknown; + } + + return unknown; +} + +const char* srs_human_flv_audio_sound_size2string(char sound_size) +{ + static const char* bit_8 = "8bit"; + static const char* bit_16 = "16bit"; + static const char* unknown = "Unknown"; + + switch (sound_size) { + case 0: return bit_8; + case 1: return bit_16; + default: return unknown; + } + + return unknown; +} + +const char* srs_human_flv_audio_sound_type2string(char sound_type) +{ + static const char* mono = "Mono"; + static const char* stereo = "Stereo"; + static const char* unknown = "Unknown"; + + switch (sound_type) { + case 0: return mono; + case 1: return stereo; + default: return unknown; + } + + return unknown; +} + +const char* srs_human_flv_audio_aac_packet_type2string(char aac_packet_type) +{ + static const char* sps_pps = "SH"; + static const char* raw = "Raw"; + static const char* unknown = "Unknown"; + + switch (aac_packet_type) { + case 0: return sps_pps; + case 1: return raw; + default: return unknown; + } + + return unknown; +} + int srs_human_print_rtmp_packet(char type, u_int32_t timestamp, char* data, int size) { int ret = ERROR_SUCCESS; @@ -1929,8 +2111,14 @@ int srs_human_print_rtmp_packet(char type, u_int32_t timestamp, char* data, int srs_human_flv_video_frame_type2string(srs_utils_flv_video_frame_type(data, size)) ); } else if (type == SRS_RTMP_TYPE_AUDIO) { - srs_human_trace("Audio packet type=%s, dts=%d, pts=%d, size=%d", - srs_human_flv_tag_type2string(type), timestamp, pts, size); + srs_human_trace("Audio packet type=%s, dts=%d, pts=%d, size=%d, %s(%s,%s,%s,%s)", + srs_human_flv_tag_type2string(type), timestamp, pts, size, + srs_human_flv_audio_sound_format2string(srs_utils_flv_audio_sound_format(data, size)), + srs_human_flv_audio_sound_rate2string(srs_utils_flv_audio_sound_rate(data, size)), + srs_human_flv_audio_sound_size2string(srs_utils_flv_audio_sound_size(data, size)), + srs_human_flv_audio_sound_type2string(srs_utils_flv_audio_sound_type(data, size)), + srs_human_flv_audio_aac_packet_type2string(srs_utils_flv_audio_aac_packet_type(data, size)) + ); } else if (type == SRS_RTMP_TYPE_SCRIPT) { srs_human_verbose("Data packet type=%s, time=%d, size=%d", srs_human_flv_tag_type2string(type), timestamp, size); diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index d0ffd15674..371a2ca83d 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -660,6 +660,70 @@ extern char srs_utils_flv_video_avc_packet_type(char* data, int size); */ extern char srs_utils_flv_video_frame_type(char* data, int size); +/** +* get the SoundFormat of audio tag. +* Format of SoundData. The following values are defined: +* 0 = Linear PCM, platform endian +* 1 = ADPCM +* 2 = MP3 +* 3 = Linear PCM, little endian +* 4 = Nellymoser 16 kHz mono +* 5 = Nellymoser 8 kHz mono +* 6 = Nellymoser +* 7 = G.711 A-law logarithmic PCM +* 8 = G.711 mu-law logarithmic PCM +* 9 = reserved +* 10 = AAC +* 11 = Speex +* 14 = MP3 8 kHz +* 15 = Device-specific sound +* Formats 7, 8, 14, and 15 are reserved. +* AAC is supported in Flash Player 9,0,115,0 and higher. +* Speex is supported in Flash Player 10 and higher. +* @return the sound format. -1(0xff) for error. +*/ +extern char srs_utils_flv_audio_sound_format(char* data, int size); + +/** +* get the SoundRate of audio tag. +* Sampling rate. The following values are defined: +* 0 = 5.5 kHz +* 1 = 11 kHz +* 2 = 22 kHz +* 3 = 44 kHz +* @return the sound rate. -1(0xff) for error. +*/ +extern char srs_utils_flv_audio_sound_rate(char* data, int size); + +/** +* get the SoundSize of audio tag. +* Size of each audio sample. This parameter only pertains to +* uncompressed formats. Compressed formats always decode +* to 16 bits internally. +* 0 = 8-bit samples +* 1 = 16-bit samples +* @return the sound size. -1(0xff) for error. +*/ +extern char srs_utils_flv_audio_sound_size(char* data, int size); + +/** +* get the SoundType of audio tag. +* Mono or stereo sound +* 0 = Mono sound +* 1 = Stereo sound +* @return the sound type. -1(0xff) for error. +*/ +extern char srs_utils_flv_audio_sound_type(char* data, int size); + +/** +* get the AACPacketType of audio tag. +* The following values are defined: +* 0 = AAC sequence header +* 1 = AAC raw +* @return the aac packet type. -1(0xff) for error. +*/ +extern char srs_utils_flv_audio_aac_packet_type(char* data, int size); + /************************************************************* ************************************************************** * human readable print. @@ -699,7 +763,7 @@ extern const char* srs_human_flv_video_codec_id2string(char codec_id); /** * get the avc packet type string. -* SpsPps = AVC sequence header +* SH = AVC sequence header * Nalu = AVC NALU * SpsPpsEnd = AVC end of sequence * otherwise, "Unknown" @@ -721,6 +785,77 @@ extern const char* srs_human_flv_video_avc_packet_type2string(char avc_packet_ty */ extern const char* srs_human_flv_video_frame_type2string(char frame_type); +/** +* get the SoundFormat string. +* Format of SoundData. The following values are defined: +* LinearPCM = Linear PCM, platform endian +* ADPCM = ADPCM +* MP3 = MP3 +* LinearPCMLe = Linear PCM, little endian +* NellymoserKHz16 = Nellymoser 16 kHz mono +* NellymoserKHz8 = Nellymoser 8 kHz mono +* Nellymoser = Nellymoser +* G711APCM = G.711 A-law logarithmic PCM +* G711MuPCM = G.711 mu-law logarithmic PCM +* Reserved = reserved +* AAC = AAC +* Speex = Speex +* MP3KHz8 = MP3 8 kHz +* DeviceSpecific = Device-specific sound +* otherwise, "Unknown" +* @remark user never free the return char*, +* it's static shared const string. +*/ +extern const char* srs_human_flv_audio_sound_format2string(char sound_format); + +/** +* get the SoundRate of audio tag. +* Sampling rate. The following values are defined: +* 5.5KHz = 5.5 kHz +* 11KHz = 11 kHz +* 22KHz = 22 kHz +* 44KHz = 44 kHz +* otherwise, "Unknown" +* @remark user never free the return char*, +* it's static shared const string. +*/ +extern const char* srs_human_flv_audio_sound_rate2string(char sound_rate); + +/** +* get the SoundSize of audio tag. +* Size of each audio sample. This parameter only pertains to +* uncompressed formats. Compressed formats always decode +* to 16 bits internally. +* 8bit = 8-bit samples +* 16bit = 16-bit samples +* otherwise, "Unknown" +* @remark user never free the return char*, +* it's static shared const string. +*/ +extern const char* srs_human_flv_audio_sound_size2string(char sound_size); + +/** +* get the SoundType of audio tag. +* Mono or stereo sound +* Mono = Mono sound +* Stereo = Stereo sound +* otherwise, "Unknown" +* @remark user never free the return char*, +* it's static shared const string. +*/ +extern const char* srs_human_flv_audio_sound_type2string(char sound_type); + +/** +* get the AACPacketType of audio tag. +* The following values are defined: +* SH = AAC sequence header +* Raw = AAC raw +* otherwise, "Unknown" +* @remark user never free the return char*, +* it's static shared const string. +*/ +extern const char* srs_human_flv_audio_aac_packet_type2string(char aac_packet_type); + /** * print the rtmp packet, use srs_human_trace/srs_human_verbose for packet, * and use srs_human_raw for script data body. From 622218c4ddf02d52b6f999c61fe210fe3b000bba Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 22 Nov 2014 15:53:05 +0800 Subject: [PATCH 207/800] for bug #217, use isolate thread to improve 17% performance. --- trunk/src/app/srs_app_rtmp_conn.cpp | 152 +++++++++++++++++++++----- trunk/src/app/srs_app_rtmp_conn.hpp | 2 + trunk/src/app/srs_app_thread.cpp | 4 +- trunk/src/rtmp/srs_protocol_rtmp.cpp | 5 + trunk/src/rtmp/srs_protocol_rtmp.hpp | 6 + trunk/src/rtmp/srs_protocol_stack.cpp | 150 +++++++++++++++++++------ trunk/src/rtmp/srs_protocol_stack.hpp | 29 +++++ 7 files changed, 286 insertions(+), 62 deletions(-) diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index f2986c52eb..ae83a3e63b 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -493,10 +493,117 @@ int SrsRtmpConn::check_vhost() return ret; } +class IsolateRecvThread : public ISrsThreadHandler +{ +private: + SrsThread* trd; + SrsRtmpServer* rtmp; + std::vector queue; +public: + IsolateRecvThread(SrsRtmpServer* rtmp_sdk) + { + rtmp = rtmp_sdk; + trd = new SrsThread(this, 0, true); + } + virtual ~IsolateRecvThread() + { + // stop recv thread. + stop(); + + // destroy the thread. + srs_freep(trd); + + // clear all messages. + std::vector::iterator it; + for (it = queue.begin(); it != queue.end(); ++it) { + SrsMessage* msg = *it; + srs_freep(msg); + } + queue.clear(); + } +public: + virtual bool empty() + { + return queue.empty(); + } + virtual SrsMessage* pump() + { + SrsMessage* msg = *queue.begin(); + queue.erase(queue.begin()); + return msg; + } +public: + virtual int start() + { + return trd->start(); + } + virtual void stop() + { + trd->stop(); + } + virtual int cycle() + { + int ret = ERROR_SUCCESS; + + SrsMessage* msg = NULL; + + if ((ret = rtmp->recv_message(&msg)) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("recv client control message failed. ret=%d", ret); + } + + // we use no timeout to recv, should never got any error. + trd->stop_loop(); + + return ret; + } + srs_verbose("play loop recv message. ret=%d", ret); + + return ret; + } +}; + int SrsRtmpConn::playing(SrsSource* source) { int ret = ERROR_SUCCESS; + // the multiple messages writev improve performance large, + // but the timeout recv will cause 33% sys call performance, + // to use isolate thread to recv, can improve about 33% performance. + // @see https://github.com/winlinvip/simple-rtmp-server/issues/194 + // @see: https://github.com/winlinvip/simple-rtmp-server/issues/217 + //rtmp->set_recv_timeout(SRS_CONSTS_RTMP_PULSE_TIMEOUT_US); + rtmp->set_recv_timeout(ST_UTIME_NO_TIMEOUT); + + // disable the protocol auto response, + // for the isolate recv thread should never send any messages. + rtmp->set_auto_response(false); + + // use isolate thread to recv, + // start isolate recv thread. + IsolateRecvThread trd(rtmp); + if ((ret = trd.start()) != ERROR_SUCCESS) { + srs_error("start isolate recv thread failed. ret=%d", ret); + return ret; + } + + // delivery messages for clients playing stream. + ret = do_playing(source, &trd); + + // stop isolate recv thread + trd.stop(); + + // enable the protocol auto response, + // for the isolate recv thread terminated. + rtmp->set_auto_response(true); + + return ret; +} + +int SrsRtmpConn::do_playing(SrsSource* source, IsolateRecvThread* trd) +{ + int ret = ERROR_SUCCESS; + if ((ret = refer->check(req->pageUrl, _srs_config->get_refer_play(req->vhost))) != ERROR_SUCCESS) { srs_error("check play_refer failed. ret=%d", ret); return ret; @@ -519,38 +626,19 @@ int SrsRtmpConn::playing(SrsSource* source) bool user_specified_duration_to_stop = (req->duration > 0); int64_t starttime = -1; - // TODO: use isolate thread to recv, - // @see: https://github.com/winlinvip/simple-rtmp-server/issues/196 - // the performance bottleneck not in the timeout recv, but - // in the multiple messages send, so it's ok for timeout recv, - // @see https://github.com/winlinvip/simple-rtmp-server/issues/194 - rtmp->set_recv_timeout(SRS_CONSTS_RTMP_PULSE_TIMEOUT_US); - while (true) { - // TODO: to use isolate thread to recv, can improve about 5% performance. + // to use isolate thread to recv, can improve about 33% performance. // @see: https://github.com/winlinvip/simple-rtmp-server/issues/196 - // read from client. - if (true) { - SrsMessage* msg = NULL; - ret = rtmp->recv_message(&msg); - srs_verbose("play loop recv message. ret=%d", ret); + // @see: https://github.com/winlinvip/simple-rtmp-server/issues/217 + while (!trd->empty()) { + SrsMessage* msg = trd->pump(); + srs_warn("pump client message to process."); - if (ret == ERROR_SOCKET_TIMEOUT) { - // it's ok, do nothing. - ret = ERROR_SUCCESS; - srs_verbose("recv timeout, ignore. ret=%d", ret); - } else if (ret != ERROR_SUCCESS) { - if (!srs_is_client_gracefully_close(ret)) { - srs_error("recv client control message failed. ret=%d", ret); + if ((ret = process_play_control_msg(consumer, msg)) != ERROR_SUCCESS) { + if (!srs_is_system_control_error(ret)) { + srs_error("process play control message failed. ret=%d", ret); } return ret; - } else { - if ((ret = process_play_control_msg(consumer, msg)) != ERROR_SUCCESS) { - if (!srs_is_system_control_error(ret)) { - srs_error("process play control message failed. ret=%d", ret); - } - return ret; - } } } @@ -564,6 +652,12 @@ int SrsRtmpConn::playing(SrsSource* source) srs_error("get messages from consumer failed. ret=%d", ret); return ret; } + + // no message to send, sleep a while. + if (count <= 0) { + srs_verbose("sleep for no messages to send"); + st_usleep(SRS_CONSTS_RTMP_PULSE_TIMEOUT_US); + } // reportable if (pithy_print.can_print()) { @@ -596,7 +690,9 @@ int SrsRtmpConn::playing(SrsSource* source) if (count > 0) { // no need to assert msg, for the rtmp will assert it. if ((ret = rtmp->send_and_free_messages(msgs.msgs, count, res->stream_id)) != ERROR_SUCCESS) { - srs_error("send messages to client failed. ret=%d", ret); + if (!srs_is_client_gracefully_close(ret)) { + srs_error("send messages to client failed. ret=%d", ret); + } return ret; } } diff --git a/trunk/src/app/srs_app_rtmp_conn.hpp b/trunk/src/app/srs_app_rtmp_conn.hpp index 716d325882..4d96be7099 100644 --- a/trunk/src/app/srs_app_rtmp_conn.hpp +++ b/trunk/src/app/srs_app_rtmp_conn.hpp @@ -49,6 +49,7 @@ class SrsBandwidth; class SrsKbps; class SrsRtmpClient; class SrsSharedPtrMessage; +class IsolateRecvThread; /** * the client provides the main logic control for RTMP clients. @@ -88,6 +89,7 @@ class SrsRtmpConn : public virtual SrsConnection, public virtual ISrsReloadHandl virtual int stream_service_cycle(); virtual int check_vhost(); virtual int playing(SrsSource* source); + virtual int do_playing(SrsSource* source, IsolateRecvThread* trd); virtual int fmle_publishing(SrsSource* source); virtual int do_fmle_publishing(SrsSource* source); virtual int flash_publishing(SrsSource* source); diff --git a/trunk/src/app/srs_app_thread.cpp b/trunk/src/app/srs_app_thread.cpp index bc18333f07..856ff5fa0b 100644 --- a/trunk/src/app/srs_app_thread.cpp +++ b/trunk/src/app/srs_app_thread.cpp @@ -161,7 +161,9 @@ void SrsThread::thread_cycle() srs_info("thread on before cycle success"); if ((ret = handler->cycle()) != ERROR_SUCCESS) { - srs_warn("thread cycle failed, ignored and retry, ret=%d", ret); + if (!srs_is_client_gracefully_close(ret)) { + srs_warn("thread cycle failed, ignored and retry, ret=%d", ret); + } goto failed; } srs_info("thread cycle success"); diff --git a/trunk/src/rtmp/srs_protocol_rtmp.cpp b/trunk/src/rtmp/srs_protocol_rtmp.cpp index 1049de187c..a4ffd30778 100644 --- a/trunk/src/rtmp/srs_protocol_rtmp.cpp +++ b/trunk/src/rtmp/srs_protocol_rtmp.cpp @@ -735,6 +735,11 @@ SrsRtmpServer::~SrsRtmpServer() srs_freep(hs_bytes); } +void SrsRtmpServer::set_auto_response(bool v) +{ + protocol->set_auto_response(v); +} + void SrsRtmpServer::set_recv_timeout(int64_t timeout_us) { protocol->set_recv_timeout(timeout_us); diff --git a/trunk/src/rtmp/srs_protocol_rtmp.hpp b/trunk/src/rtmp/srs_protocol_rtmp.hpp index 9c8b85d3bb..77df8c8313 100644 --- a/trunk/src/rtmp/srs_protocol_rtmp.hpp +++ b/trunk/src/rtmp/srs_protocol_rtmp.hpp @@ -334,6 +334,12 @@ class SrsRtmpServer virtual ~SrsRtmpServer(); // protocol methods proxy public: + /** + * set the auto response message when recv for protocol stack. + * @param v, whether auto response message when recv message. + * @see: https://github.com/winlinvip/simple-rtmp-server/issues/217 + */ + virtual void set_auto_response(bool v); /** * set/get the recv timeout in us. * if timeout, recv/send message return ERROR_SOCKET_TIMEOUT. diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp index ef8487983b..6f79e202b6 100644 --- a/trunk/src/rtmp/srs_protocol_stack.cpp +++ b/trunk/src/rtmp/srs_protocol_stack.cpp @@ -415,6 +415,7 @@ SrsProtocol::SrsProtocol(ISrsProtocolReaderWriter* io) srs_assert(nb_out_iovs >= 2); warned_c0c3_cache_dry = false; + auto_response_when_recv = true; } SrsProtocol::~SrsProtocol() @@ -430,6 +431,15 @@ SrsProtocol::~SrsProtocol() chunk_streams.clear(); } + if (true) { + std::vector::iterator it; + for (it = manual_response_queue.begin(); it != manual_response_queue.end(); ++it) { + SrsPacket* pkt = *it; + srs_freep(pkt); + } + manual_response_queue.clear(); + } + srs_freep(in_buffer); // alloc by malloc, use free directly. @@ -439,6 +449,35 @@ SrsProtocol::~SrsProtocol() } } +void SrsProtocol::set_auto_response(bool v) +{ + auto_response_when_recv = v; +} + +int SrsProtocol::manual_response_flush() +{ + int ret = ERROR_SUCCESS; + + if (manual_response_queue.empty()) { + return ret; + } + + std::vector::iterator it; + for (it = manual_response_queue.begin(); it != manual_response_queue.end();) { + SrsPacket* pkt = *it; + + // erase this packet, the send api always free it. + it = manual_response_queue.erase(it); + + // use underlayer api to send, donot flush again. + if ((ret = do_send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { + return ret; + } + } + + return ret; +} + void SrsProtocol::set_recv_timeout(int64_t timeout_us) { return skt->set_recv_timeout(timeout_us); @@ -638,7 +677,9 @@ int SrsProtocol::do_send_messages(SrsMessage** msgs, int nb_msgs) // when c0c3 cache dry, // sendout all messages and reset the cache, then send again. if ((ret = skt->writev(out_iovs, iov_index, NULL)) != ERROR_SUCCESS) { - srs_error("send with writev failed. ret=%d", ret); + if (!srs_is_client_gracefully_close(ret)) { + srs_error("send with writev failed. ret=%d", ret); + } return ret; } @@ -663,13 +704,57 @@ int SrsProtocol::do_send_messages(SrsMessage** msgs, int nb_msgs) // sendout header and payload by writev. // decrease the sys invoke count to get higher performance. if ((ret = skt->writev(out_iovs, iov_index, NULL)) != ERROR_SUCCESS) { - srs_error("send with writev failed. ret=%d", ret); + if (!srs_is_client_gracefully_close(ret)) { + srs_error("send with writev failed. ret=%d", ret); + } return ret; } return ret; } +int SrsProtocol::do_send_and_free_packet(SrsPacket* packet, int stream_id) +{ + int ret = ERROR_SUCCESS; + + srs_assert(packet); + SrsAutoFree(SrsPacket, packet); + + int size = 0; + char* payload = NULL; + if ((ret = packet->encode(size, payload)) != ERROR_SUCCESS) { + srs_error("encode RTMP packet to bytes oriented RTMP message failed. ret=%d", ret); + return ret; + } + + // encode packet to payload and size. + if (size <= 0 || payload == NULL) { + srs_warn("packet is empty, ignore empty message."); + return ret; + } + + // to message + SrsMessage* msg = new SrsCommonMessage(); + + msg->payload = payload; + msg->size = size; + + msg->header.payload_length = size; + msg->header.message_type = packet->get_message_type(); + msg->header.stream_id = stream_id; + msg->header.perfer_cid = packet->get_prefer_cid(); + + // donot use the auto free to free the msg, + // for performance issue. + ret = do_send_messages(&msg, 1); + if (ret == ERROR_SUCCESS) { + ret = on_send_packet(msg, packet); + } + srs_freep(msg); + + return ret; +} + void SrsProtocol::generate_chunk_header(char* cache, SrsMessageHeader* mh, bool c0, int* pnbh, char** ph) { // to directly set the field. @@ -948,6 +1033,16 @@ int SrsProtocol::send_and_free_messages(SrsMessage** msgs, int nb_msgs, int stre srs_freep(msg); } + // donot flush when send failed + if (ret != ERROR_SUCCESS) { + return ret; + } + + // flush messages in manual queue + if ((ret = manual_response_flush()) != ERROR_SUCCESS) { + return ret; + } + return ret; } @@ -955,41 +1050,15 @@ int SrsProtocol::send_and_free_packet(SrsPacket* packet, int stream_id) { int ret = ERROR_SUCCESS; - srs_assert(packet); - SrsAutoFree(SrsPacket, packet); - - int size = 0; - char* payload = NULL; - if ((ret = packet->encode(size, payload)) != ERROR_SUCCESS) { - srs_error("encode RTMP packet to bytes oriented RTMP message failed. ret=%d", ret); + if ((ret = do_send_and_free_packet(packet, stream_id)) != ERROR_SUCCESS) { return ret; } - // encode packet to payload and size. - if (size <= 0 || payload == NULL) { - srs_warn("packet is empty, ignore empty message."); + // flush messages in manual queue + if ((ret = manual_response_flush()) != ERROR_SUCCESS) { return ret; } - // to message - SrsMessage* msg = new SrsCommonMessage(); - - msg->payload = payload; - msg->size = size; - - msg->header.payload_length = size; - msg->header.message_type = packet->get_message_type(); - msg->header.stream_id = stream_id; - msg->header.perfer_cid = packet->get_prefer_cid(); - - // donot use the auto free to free the msg, - // for performance issue. - ret = do_send_messages(&msg, 1); - if (ret == ERROR_SUCCESS) { - ret = on_send_packet(msg, packet); - } - srs_freep(msg); - return ret; } @@ -1698,7 +1767,15 @@ int SrsProtocol::response_acknowledgement_message() SrsAcknowledgementPacket* pkt = new SrsAcknowledgementPacket(); in_ack_size.acked_size = skt->get_recv_bytes(); pkt->sequence_number = (int32_t)in_ack_size.acked_size; - if ((ret = send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { + + // cache the message and use flush to send. + if (!auto_response_when_recv) { + manual_response_queue.push_back(pkt); + return ret; + } + + // use underlayer api to send, donot flush again. + if ((ret = do_send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { srs_error("send acknowledgement failed. ret=%d", ret); return ret; } @@ -1718,7 +1795,14 @@ int SrsProtocol::response_ping_message(int32_t timestamp) pkt->event_type = SrcPCUCPingResponse; pkt->event_data = timestamp; - if ((ret = send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { + // cache the message and use flush to send. + if (!auto_response_when_recv) { + manual_response_queue.push_back(pkt); + return ret; + } + + // use underlayer api to send, donot flush again. + if ((ret = do_send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { srs_error("send ping response failed. ret=%d", ret); return ret; } diff --git a/trunk/src/rtmp/srs_protocol_stack.hpp b/trunk/src/rtmp/srs_protocol_stack.hpp index 925391fdf8..d9ba0ee847 100644 --- a/trunk/src/rtmp/srs_protocol_stack.hpp +++ b/trunk/src/rtmp/srs_protocol_stack.hpp @@ -31,6 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +#include #include // for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 @@ -213,6 +214,16 @@ class SrsProtocol * input ack size, when to send the acked packet. */ AckWindowSize in_ack_size; + /** + * whether auto response when recv messages. + * default to true for it's very easy to use the protocol stack. + * @see: https://github.com/winlinvip/simple-rtmp-server/issues/217 + */ + bool auto_response_when_recv; + /** + * when not auto response message, manual flush the messages in queue. + */ + std::vector manual_response_queue; // peer out private: /** @@ -244,6 +255,20 @@ class SrsProtocol */ SrsProtocol(ISrsProtocolReaderWriter* io); virtual ~SrsProtocol(); +public: + /** + * set the auto response message when recv for protocol stack. + * @param v, whether auto response message when recv message. + * @see: https://github.com/winlinvip/simple-rtmp-server/issues/217 + */ + virtual void set_auto_response(bool v); + /** + * flush for manual response when the auto response is disabled + * by set_auto_response(false), we default use auto response, so donot + * need to call this api(the protocol sdk will auto send message). + * @see the auto_response_when_recv and manual_response_queue. + */ + virtual int manual_response_flush(); public: /** * set/get the recv timeout in us. @@ -371,6 +396,10 @@ class SrsProtocol */ virtual int do_send_messages(SrsMessage** msgs, int nb_msgs); /** + * underlayer api for send and free packet. + */ + virtual int do_send_and_free_packet(SrsPacket* packet, int stream_id); + /** * generate the chunk header for msg. * @param mh, the header of msg to send. * @param c0, whether the first chunk, the c0 chunk. From 58136ec178e3d47db6c90a59875d7e40946936e5 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 22 Nov 2014 17:58:02 +0800 Subject: [PATCH 208/800] fix #217, remove timeout recv, support 7.5k+ 250kbps clients. 2.0.30. --- README.md | 5 +- trunk/configure | 3 +- trunk/src/app/srs_app_recv_thread.cpp | 98 +++++++++++++++++++++++++++ trunk/src/app/srs_app_recv_thread.hpp | 65 ++++++++++++++++++ trunk/src/app/srs_app_rtmp_conn.cpp | 75 +------------------- trunk/src/app/srs_app_rtmp_conn.hpp | 4 +- trunk/src/core/srs_core.hpp | 2 +- trunk/src/srs/srs.upp | 2 + 8 files changed, 177 insertions(+), 77 deletions(-) create mode 100644 trunk/src/app/srs_app_recv_thread.cpp create mode 100644 trunk/src/app/srs_app_recv_thread.hpp diff --git a/README.md b/README.md index 0178812c3b..97e24d9bef 100755 --- a/README.md +++ b/README.md @@ -445,11 +445,12 @@ Supported operating systems and hardware: [CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_SrsLibrtmp#publish-h264-raw-data), [EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_SrsLibrtmp#publish-h264-raw-data) ) by srs-librtmp. -1. Support [6k+ clients](https://github.com/winlinvip/simple-rtmp-server/issues/194), 4Gbps per process. +1. Support [6k+ clients](https://github.com/winlinvip/simple-rtmp-server/issues/194), 3Gbps per process. 1. Suppport [English wiki](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Home). 1. Research and simplify st, [bug #182](https://github.com/winlinvip/simple-rtmp-server/issues/182). 1. Support compile [srs-librtmp on windows](https://github.com/winlinvip/srs.librtmp), [bug #213](https://github.com/winlinvip/simple-rtmp-server/issues/213). +1. Support [7.5k+ clients](https://github.com/winlinvip/simple-rtmp-server/issues/217), 4Gbps per process. 1. [no-plan] Support <500ms latency, FRSC(Fast RTMP-compatible Stream Channel tech). 1. [no-plan] Support RTMP 302 redirect [#92](https://github.com/winlinvip/simple-rtmp-server/issues/92). 1. [no-plan] Support multiple processes, for both origin and edge @@ -482,6 +483,7 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History +* v2.0, 2014-11-22, fix [#217](https://github.com/winlinvip/simple-rtmp-server/issues/217), remove timeout recv, support 7.5k+ 250kbps clients. 2.0.30. * v2.0, 2014-11-21, srs-librtmp add rtmp prefix for rtmp/utils/human apis. 2.0.29. * v2.0, 2014-11-21, refine examples of srs-librtmp, add srs_print_rtmp_packet. 2.0.28. * v2.0, 2014-11-20, fix [#212](https://github.com/winlinvip/simple-rtmp-server/issues/212), support publish audio raw frames. 2.0.27 @@ -700,6 +702,7 @@ Performance benchmark history, on virtual box: * 2014-11-12, SRS 2.0.14, 2700clients, 69%CPU, 59MB. * 2014-11-12, SRS 2.0.14, 3500clients, 95%CPU, 78MB. * 2014-11-13, SRS 2.0.15, 6000clients, 82%CPU, 203MB. (500 publishers). +* 2014-11-22, SRS 2.0.30, 7500clients, 87%CPU, 320MB. Latest benchmark(2014-07-12): diff --git a/trunk/configure b/trunk/configure index 219af140a4..b29d709cb4 100755 --- a/trunk/configure +++ b/trunk/configure @@ -388,7 +388,8 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then "srs_app_thread" "srs_app_bandwidth" "srs_app_st" "srs_app_log" "srs_app_config" "srs_app_pithy_print" "srs_app_reload" "srs_app_http_api" "srs_app_http_conn" "srs_app_http_hooks" "srs_app_json" "srs_app_ingest" "srs_app_ffmpeg" "srs_app_utility" "srs_app_dvr" "srs_app_edge" - "srs_app_kbps" "srs_app_heartbeat" "srs_app_empty" "srs_app_http_client" "srs_app_avc_aac") + "srs_app_kbps" "srs_app_heartbeat" "srs_app_empty" "srs_app_http_client" "srs_app_avc_aac" + "srs_app_recv_thread") APP_INCS="src/app"; MODULE_DIR=${APP_INCS} . auto/modules.sh APP_OBJS="${MODULE_OBJS[@]}" fi diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp new file mode 100644 index 0000000000..190adfb3ff --- /dev/null +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -0,0 +1,98 @@ +/* +The MIT License (MIT) + +Copyright (c) 2013-2014 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include +#include + +SrsRecvThread::SrsRecvThread(SrsRtmpServer* rtmp_sdk) +{ + rtmp = rtmp_sdk; + trd = new SrsThread(this, 0, true); +} + +SrsRecvThread::~SrsRecvThread() +{ + // stop recv thread. + stop(); + + // destroy the thread. + srs_freep(trd); + + // clear all messages. + std::vector::iterator it; + for (it = queue.begin(); it != queue.end(); ++it) { + SrsMessage* msg = *it; + srs_freep(msg); + } + queue.clear(); +} + +bool SrsRecvThread::empty() +{ + return queue.empty(); +} + +SrsMessage* SrsRecvThread::pump() +{ + srs_assert(!queue.empty()); + + SrsMessage* msg = *queue.begin(); + + queue.erase(queue.begin()); + + return msg; +} + +int SrsRecvThread::start() +{ + return trd->start(); +} + +void SrsRecvThread::stop() +{ + trd->stop(); +} + +int SrsRecvThread::cycle() +{ + int ret = ERROR_SUCCESS; + + SrsMessage* msg = NULL; + + if ((ret = rtmp->recv_message(&msg)) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("recv client control message failed. ret=%d", ret); + } + + // we use no timeout to recv, should never got any error. + trd->stop_loop(); + + return ret; + } + srs_verbose("play loop recv message. ret=%d", ret); + + return ret; +} + diff --git a/trunk/src/app/srs_app_recv_thread.hpp b/trunk/src/app/srs_app_recv_thread.hpp new file mode 100644 index 0000000000..9ba671dd60 --- /dev/null +++ b/trunk/src/app/srs_app_recv_thread.hpp @@ -0,0 +1,65 @@ +/* +The MIT License (MIT) + +Copyright (c) 2013-2014 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef SRS_APP_RECV_THREAD_HPP +#define SRS_APP_RECV_THREAD_HPP + +/* +#include +*/ + +#include + +#include + +#include + +class SrsRtmpServer; +class SrsMessage; + +/** +* the recv thread used to replace the timeout recv, +* which hurt performance for the epoll_ctrl is frequently used. +* @see: SrsRtmpConn::playing +* @see: https://github.com/winlinvip/simple-rtmp-server/issues/217 +*/ +class SrsRecvThread : public ISrsThreadHandler +{ +private: + SrsThread* trd; + SrsRtmpServer* rtmp; + std::vector queue; +public: + SrsRecvThread(SrsRtmpServer* rtmp_sdk); + virtual ~SrsRecvThread(); +public: + virtual bool empty(); + virtual SrsMessage* pump(); +public: + virtual int start(); + virtual void stop(); + virtual int cycle(); +}; + +#endif + diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index ae83a3e63b..3597e2d744 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -48,6 +48,7 @@ using namespace std; #include #include #include +#include // when stream is busy, for example, streaming is already // publishing, when a new client to request to publish, @@ -493,76 +494,6 @@ int SrsRtmpConn::check_vhost() return ret; } -class IsolateRecvThread : public ISrsThreadHandler -{ -private: - SrsThread* trd; - SrsRtmpServer* rtmp; - std::vector queue; -public: - IsolateRecvThread(SrsRtmpServer* rtmp_sdk) - { - rtmp = rtmp_sdk; - trd = new SrsThread(this, 0, true); - } - virtual ~IsolateRecvThread() - { - // stop recv thread. - stop(); - - // destroy the thread. - srs_freep(trd); - - // clear all messages. - std::vector::iterator it; - for (it = queue.begin(); it != queue.end(); ++it) { - SrsMessage* msg = *it; - srs_freep(msg); - } - queue.clear(); - } -public: - virtual bool empty() - { - return queue.empty(); - } - virtual SrsMessage* pump() - { - SrsMessage* msg = *queue.begin(); - queue.erase(queue.begin()); - return msg; - } -public: - virtual int start() - { - return trd->start(); - } - virtual void stop() - { - trd->stop(); - } - virtual int cycle() - { - int ret = ERROR_SUCCESS; - - SrsMessage* msg = NULL; - - if ((ret = rtmp->recv_message(&msg)) != ERROR_SUCCESS) { - if (!srs_is_client_gracefully_close(ret)) { - srs_error("recv client control message failed. ret=%d", ret); - } - - // we use no timeout to recv, should never got any error. - trd->stop_loop(); - - return ret; - } - srs_verbose("play loop recv message. ret=%d", ret); - - return ret; - } -}; - int SrsRtmpConn::playing(SrsSource* source) { int ret = ERROR_SUCCESS; @@ -581,7 +512,7 @@ int SrsRtmpConn::playing(SrsSource* source) // use isolate thread to recv, // start isolate recv thread. - IsolateRecvThread trd(rtmp); + SrsRecvThread trd(rtmp); if ((ret = trd.start()) != ERROR_SUCCESS) { srs_error("start isolate recv thread failed. ret=%d", ret); return ret; @@ -600,7 +531,7 @@ int SrsRtmpConn::playing(SrsSource* source) return ret; } -int SrsRtmpConn::do_playing(SrsSource* source, IsolateRecvThread* trd) +int SrsRtmpConn::do_playing(SrsSource* source, SrsRecvThread* trd) { int ret = ERROR_SUCCESS; diff --git a/trunk/src/app/srs_app_rtmp_conn.hpp b/trunk/src/app/srs_app_rtmp_conn.hpp index 4d96be7099..0374c1077b 100644 --- a/trunk/src/app/srs_app_rtmp_conn.hpp +++ b/trunk/src/app/srs_app_rtmp_conn.hpp @@ -49,7 +49,7 @@ class SrsBandwidth; class SrsKbps; class SrsRtmpClient; class SrsSharedPtrMessage; -class IsolateRecvThread; +class SrsRecvThread; /** * the client provides the main logic control for RTMP clients. @@ -89,7 +89,7 @@ class SrsRtmpConn : public virtual SrsConnection, public virtual ISrsReloadHandl virtual int stream_service_cycle(); virtual int check_vhost(); virtual int playing(SrsSource* source); - virtual int do_playing(SrsSource* source, IsolateRecvThread* trd); + virtual int do_playing(SrsSource* source, SrsRecvThread* trd); virtual int fmle_publishing(SrsSource* source); virtual int do_fmle_publishing(SrsSource* source); virtual int flash_publishing(SrsSource* source); diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index b647083813..b988a1fbb9 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 29 +#define VERSION_REVISION 30 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/srs/srs.upp b/trunk/src/srs/srs.upp index da92f3ebf9..e45a4738cd 100755 --- a/trunk/src/srs/srs.upp +++ b/trunk/src/srs/srs.upp @@ -92,6 +92,8 @@ file ..\app\srs_app_kbps.cpp, ..\app\srs_app_log.hpp, ..\app\srs_app_log.cpp, + ..\app\srs_app_recv_thread.hpp, + ..\app\srs_app_recv_thread.cpp, ..\app\srs_app_refer.hpp, ..\app\srs_app_refer.cpp, ..\app\srs_app_reload.hpp, From 5408169b6e2b159cbf9ed9e4f99657c53c6e57f3 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 22 Nov 2014 17:59:42 +0800 Subject: [PATCH 209/800] update readme --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 97e24d9bef..e621562c40 100755 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ HLS( [CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryHLS), [EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_DeliveryHLS) ), -high-performance(6k+ clients)( +high-performance(7.5k+ clients)( [CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Performance), [EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Performance) ), @@ -695,14 +695,14 @@ Supported operating systems and hardware: Performance benchmark history, on virtual box: -* 2013-11-28, SRS 0.5.0, 1800clients, 90%CPU, 41MB. [benchmark](https://github.com/winlinvip/simple-rtmp-server/commit/023e23bc8261bec15a70a7ae932098fb4f82b679) -* 2014-07-12, SRS 0.9.156, 1800clients, 68%CPU, 38MB. [benchmark](https://github.com/winlinvip/simple-rtmp-server/commit/e2d273f4939348374bf9644df9d54c4293b39c1a) -* 2014-07-12, SRS 0.9.156, 2700clients, 89%CPU, 61MB. [benchmark](https://github.com/winlinvip/simple-rtmp-server/commit/6d12280b7cc54c465b1caf8b1402149e77c4c7d9) -* 2014-11-11, SRS 1.0.5, 2700clients, 85%CPU, 66MB. (1.0 equals 2.0.12) -* 2014-11-12, SRS 2.0.14, 2700clients, 69%CPU, 59MB. -* 2014-11-12, SRS 2.0.14, 3500clients, 95%CPU, 78MB. -* 2014-11-13, SRS 2.0.15, 6000clients, 82%CPU, 203MB. (500 publishers). -* 2014-11-22, SRS 2.0.30, 7500clients, 87%CPU, 320MB. +* 2013-11-28, SRS 0.5.0, 1.8k(1800)clients, 90%CPU, 41MB. [benchmark](https://github.com/winlinvip/simple-rtmp-server/commit/023e23bc8261bec15a70a7ae932098fb4f82b679) +* 2014-07-12, SRS 0.9.156, 1.8k(1800)clients, 68%CPU, 38MB. [benchmark](https://github.com/winlinvip/simple-rtmp-server/commit/e2d273f4939348374bf9644df9d54c4293b39c1a) +* 2014-07-12, SRS 0.9.156, 2.7k(2700)clients, 89%CPU, 61MB. [benchmark](https://github.com/winlinvip/simple-rtmp-server/commit/6d12280b7cc54c465b1caf8b1402149e77c4c7d9) +* 2014-11-11, SRS 1.0.5, 2.7k(2700)clients, 85%CPU, 66MB. (1.0 equals 2.0.12) +* 2014-11-12, SRS 2.0.14, 2.7k(2700)clients, 69%CPU, 59MB. +* 2014-11-12, SRS 2.0.14, 3.5k(3500)clients, 95%CPU, 78MB. +* 2014-11-13, SRS 2.0.15, 6.0k(6000)clients, 82%CPU, 203MB. (500 publishers). +* 2014-11-22, SRS 2.0.30, 7.5k(7500)clients, 87%CPU, 320MB. Latest benchmark(2014-07-12): From 3e81e6e0f1d96305b0c4d2baebc1be007912f0a4 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 22 Nov 2014 18:08:45 +0800 Subject: [PATCH 210/800] refine code for bug #217, use recv thread to set the timeout. --- trunk/src/app/srs_app_recv_thread.cpp | 24 ++++++++++++++++++++++++ trunk/src/app/srs_app_recv_thread.hpp | 3 +++ trunk/src/app/srs_app_rtmp_conn.cpp | 18 ++---------------- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index 190adfb3ff..21e6cd3165 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -96,3 +96,27 @@ int SrsRecvThread::cycle() return ret; } +void SrsRecvThread::on_thread_start() +{ + // the multiple messages writev improve performance large, + // but the timeout recv will cause 33% sys call performance, + // to use isolate thread to recv, can improve about 33% performance. + // @see https://github.com/winlinvip/simple-rtmp-server/issues/194 + // @see: https://github.com/winlinvip/simple-rtmp-server/issues/217 + rtmp->set_recv_timeout(ST_UTIME_NO_TIMEOUT); + + // disable the protocol auto response, + // for the isolate recv thread should never send any messages. + rtmp->set_auto_response(false); +} + +void SrsRecvThread::on_thread_stop() +{ + // enable the protocol auto response, + // for the isolate recv thread terminated. + rtmp->set_auto_response(true); + + // reset the timeout to pulse mode. + rtmp->set_recv_timeout(SRS_CONSTS_RTMP_PULSE_TIMEOUT_US); +} + diff --git a/trunk/src/app/srs_app_recv_thread.hpp b/trunk/src/app/srs_app_recv_thread.hpp index 9ba671dd60..dd8886e150 100644 --- a/trunk/src/app/srs_app_recv_thread.hpp +++ b/trunk/src/app/srs_app_recv_thread.hpp @@ -59,6 +59,9 @@ class SrsRecvThread : public ISrsThreadHandler virtual int start(); virtual void stop(); virtual int cycle(); +public: + virtual void on_thread_start(); + virtual void on_thread_stop(); }; #endif diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index 3597e2d744..0d3d6a393b 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -498,21 +498,11 @@ int SrsRtmpConn::playing(SrsSource* source) { int ret = ERROR_SUCCESS; - // the multiple messages writev improve performance large, - // but the timeout recv will cause 33% sys call performance, - // to use isolate thread to recv, can improve about 33% performance. - // @see https://github.com/winlinvip/simple-rtmp-server/issues/194 + // use isolate thread to recv, // @see: https://github.com/winlinvip/simple-rtmp-server/issues/217 - //rtmp->set_recv_timeout(SRS_CONSTS_RTMP_PULSE_TIMEOUT_US); - rtmp->set_recv_timeout(ST_UTIME_NO_TIMEOUT); - - // disable the protocol auto response, - // for the isolate recv thread should never send any messages. - rtmp->set_auto_response(false); + SrsRecvThread trd(rtmp); - // use isolate thread to recv, // start isolate recv thread. - SrsRecvThread trd(rtmp); if ((ret = trd.start()) != ERROR_SUCCESS) { srs_error("start isolate recv thread failed. ret=%d", ret); return ret; @@ -524,10 +514,6 @@ int SrsRtmpConn::playing(SrsSource* source) // stop isolate recv thread trd.stop(); - // enable the protocol auto response, - // for the isolate recv thread terminated. - rtmp->set_auto_response(true); - return ret; } From 3d97048c3a846feebbd57169335df76972bf8b95 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 22 Nov 2014 19:15:40 +0800 Subject: [PATCH 211/800] fix bug #217, add reuse conn to play different player. --- .../srs_reuse_conn/.actionScriptProperties | 41 ++++++ .../research/players/srs_reuse_conn/.project | 17 +++ .../org.eclipse.core.resources.prefs | 3 + .../players/srs_reuse_conn/FlashCS5UI.swc | Bin 0 -> 121293 bytes .../srs_reuse_conn/release/srs_reuse_conn.swf | Bin 0 -> 27452 bytes .../srs_reuse_conn/src/srs_reuse_conn.as | 123 ++++++++++++++++++ trunk/src/app/srs_app_recv_thread.cpp | 18 +++ trunk/src/app/srs_app_recv_thread.hpp | 1 + trunk/src/app/srs_app_rtmp_conn.cpp | 7 +- 9 files changed, 209 insertions(+), 1 deletion(-) create mode 100755 trunk/research/players/srs_reuse_conn/.actionScriptProperties create mode 100755 trunk/research/players/srs_reuse_conn/.project create mode 100644 trunk/research/players/srs_reuse_conn/.settings/org.eclipse.core.resources.prefs create mode 100644 trunk/research/players/srs_reuse_conn/FlashCS5UI.swc create mode 100644 trunk/research/players/srs_reuse_conn/release/srs_reuse_conn.swf create mode 100644 trunk/research/players/srs_reuse_conn/src/srs_reuse_conn.as diff --git a/trunk/research/players/srs_reuse_conn/.actionScriptProperties b/trunk/research/players/srs_reuse_conn/.actionScriptProperties new file mode 100755 index 0000000000..ea33e282e2 --- /dev/null +++ b/trunk/research/players/srs_reuse_conn/.actionScriptProperties @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/trunk/research/players/srs_reuse_conn/.project b/trunk/research/players/srs_reuse_conn/.project new file mode 100755 index 0000000000..11dc3f9c2c --- /dev/null +++ b/trunk/research/players/srs_reuse_conn/.project @@ -0,0 +1,17 @@ + + + srs_reuse_conn + + + + + + com.adobe.flexbuilder.project.flexbuilder + + + + + + com.adobe.flexbuilder.project.actionscriptnature + + diff --git a/trunk/research/players/srs_reuse_conn/.settings/org.eclipse.core.resources.prefs b/trunk/research/players/srs_reuse_conn/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000000..0fc6bb311a --- /dev/null +++ b/trunk/research/players/srs_reuse_conn/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,3 @@ +#Sat Nov 22 18:40:22 CST 2014 +eclipse.preferences.version=1 +encoding/=utf-8 diff --git a/trunk/research/players/srs_reuse_conn/FlashCS5UI.swc b/trunk/research/players/srs_reuse_conn/FlashCS5UI.swc new file mode 100644 index 0000000000000000000000000000000000000000..ea19d9d5c559472563453f16d62aa627ffb02f7d GIT binary patch literal 121293 zcmV)9K*hgMO9KQH00;mG02fh_KmY&$000000000001E&B0BmVua$$0LE^~Kg04PA$ zze8732|khp0C=42y$5(yRoXay?wxz*PRnGHOnL$Xp(K!m=`+EWP6C01Bou-4%rL=_ zWYYl48loUoMF9~-0|-(UrAbu@h=M4ZfC?z=##NM6mStU6ch{2td(ORgN(!Rievf|t z=cf0NbiMyuu@yNJ5w8c3xf??S1Rf*+I&?pa?cy9Q_f+Xn430bMkD`|Y=#fJOI{NTc;+=Z+!B^y1xvwYQFJq0zN#*D$KxIY<+n zH7McYqMASL9}u}kC!oplXi5Md0l5bNO1+S~^jzWd8bA&;9qIl7t?LHll*g9U#YEZ0 zupNr|=|Rnd4qU%9we&li+U7ILxjtrAd;`E`ti%# zznfS5k1a2bPpo}!vnB4YXV(aI_=ya!;_K*^M*viN@z)~Uf*(>}Z}WWj_Zr~7zH7WF z?EC!=*+MLQKSAqc+R&A0uX$LxaX}ahR6MBR8S&@P`evx816%?{Tmuk#SgICqtwPBf z%FI*sZh%3cLiS`~6{1;uS9M@Y^?NmlXJ6o6z*wOR-`mlg+y3uUfWx z;(keJ=IQgz%g=N#`SQosQem95;y4BIrfO{14*t%p& zV6a4hA%NyG9@~yQ6qvZIUv$Y3u+ssCA{U~kBK9PtPA#c`(abKT{yJmff^~cAIwoFk zp)@i+!m&+(YIL#~I|e}6Pa%>+O;zOeR`yKJ67<1mpELPfoR zc{Ln(b+y2&eX5~j@~$Jc@+jG`FOOm*JRI$%-2Af34Idw}Crr>*%#UZG$kE@|9Jg!6 zpL^hAo>zx_+BUYU2d}=F02S9>lnwhr23)8ITp~cW!$8Uf+4hTV=j#8mVRP%+5w1Al zXQQ)qtyTCD`V}eiPJ}4qnD3*+-iBko2N{h-Zz0r<BTNrQrLN?uT|Cd<6j!##9lFl5 za?H;fnZY8mgL;q5>i}ciBjZIAZ)6Egj@PuFIfxbFy*ujj)St9=Y_I}rfSE<^CRQ0{RV`m2pgq0hKr2_GgR zJG4Iu#~2U)#Ta`zzW)VI!WhSj7!U6cV=U=$zd+JK=kvIF@r^s}f5(}X?dQq*s9z1jr_HAcD z03)yP8o%V8&(r1=8WS(9) zJ{bG_LZWKfy2Ym>t>Navdv)%#);`i664Qh>^`dV)O2kADM$;fVNnzN<7Eo7TAnS4^gOT-h&C;4I;oUyenfq#Q4yxzjW zf|^Ij974;FLgVNMbz#=LTjlo2A&RN0(HVlR2}5gICzP8IxUOOm%VIU1;M2S3X)_>j>DJ(3#5ycH~l;t3~arC2ecQC_`c%`!Mdx^b6^; zm5sCCT%Q>Jp7q5R)3pT$!WNGGW@5~+<2!Ftj>(IC_k`+;Tc)fD|LWs)$^2NG__3yO zjE}Ki5@bRyHgpY@d+_2_~qCCUxYLe^(m?^}AX zqagE-;fFIXDQ}5=WW8$y8!;jLwOwBGPO)KLEvN{2df1(fcIUEViJNg_&i5v3%r zv0Dr&qeYa6KL1gd5u7da8Wl)OI`F~popmkV@t{uLnY-Yz&fJum*FU;4d}FQZ(#9IV zJiB8{LHwrkhigL8b}yd#)WW*sxg?@I{E77Wsm~l$coduoy8c$&)uMf0HsGAi*^63u zgl`cM9xWn#3m|-MjfaM}_BX8k(a_zgpC(~KaVcxRQ+;Yk-8<1s4+u28O+@Kd5vAL_ zXt*6xZucG5n-F1doZJqVo6w$FA~&H)Rc=COcI=%*FCY4vy*crd1&?0ckoIxkWX5)6 z``hUGrh^yKmtPoOeog4o9iqv&UF^~w?k*i+*CCwxbEw>db$oTwTaQn(3VNsD&9VgM zdVuPwt3_{&U0ga^{$Sm%a(m*EyFH*#SU3w?Q486tEdMI4u8u@fooPISxnLP{#wBNCaJ#CUxvZ;Has4cSSeVH)(v z>)qG8zm9uo0wO6ah%req>#rn}6d6bnNzs>`Ya=O2sF0+@zjlx>r3S_oN_}yJqyzxp z2q}LJASqfPyO8p?6bh}tL4FFKVh?L{CW{lH=>XGE9a6L*G^dnk)F&wlOJ8?D{97-+ z_1pnw+6&*!h}pe<=d)d#)W;T{eyIAMf0m!3zgcIza`s+sL3Q9CspmxQO&6onG?7Z_ zs7|4nGe4~He7AfKq-5OmWWfqdFau7x
#rrQPi$QvuMKBrUnWn5RZHn0J!0Y)-}y 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 literal 0 HcmV?d00001 diff --git a/trunk/research/players/srs_reuse_conn/release/srs_reuse_conn.swf b/trunk/research/players/srs_reuse_conn/release/srs_reuse_conn.swf new file mode 100644 index 0000000000000000000000000000000000000000..c072720169cf448a8e73c144f60155172505c58f GIT binary patch literal 27452 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 LP0arSAZAdA6bDJv literal 0 HcmV?d00001 diff --git a/trunk/research/players/srs_reuse_conn/src/srs_reuse_conn.as b/trunk/research/players/srs_reuse_conn/src/srs_reuse_conn.as new file mode 100644 index 0000000000..c534b647bd --- /dev/null +++ b/trunk/research/players/srs_reuse_conn/src/srs_reuse_conn.as @@ -0,0 +1,123 @@ +package +{ + import fl.controls.Button; + import fl.controls.TextInput; + + import flash.display.Sprite; + import flash.display.StageAlign; + import flash.display.StageScaleMode; + import flash.events.Event; + import flash.events.MouseEvent; + import flash.events.NetStatusEvent; + import flash.media.Video; + import flash.net.NetConnection; + import flash.net.NetStream; + + [SWF(backgroundColor="0xEEEEEE",frameRate="30",width="1024",height="576")] + public class srs_reuse_conn extends Sprite + { + public function srs_reuse_conn() + { + if (stage) { + onAddedToStage(null); + } else { + addEventListener(Event.ADDED_TO_STAGE, onAddedToStage); + } + } + + private function onAddedToStage(evt:Event):void + { + stage.align = StageAlign.TOP_LEFT; + stage.scaleMode = StageScaleMode.NO_SCALE; + + var txtUrl:TextInput = new TextInput(); + var btnConn:Button = new Button(); + var btnPlay:Button = new Button(); + + txtUrl.x = 10; + txtUrl.y = 10; + txtUrl.width = 400; + txtUrl.text = "rtmp://dev/live/livestream"; + addChild(txtUrl); + + btnConn.label = "Connect"; + btnConn.x = txtUrl.x + txtUrl.width + 10; + btnConn.y = txtUrl.y; + btnConn.width = 100; + addChild(btnConn); + + btnPlay.label = "Play"; + btnPlay.x = btnConn.x + btnConn.width + 10; + btnPlay.y = btnConn.y; + btnPlay.width = 100; + addChild(btnPlay); + + var video:Video = new Video(); + video.x = txtUrl.x; + video.y = txtUrl.y + txtUrl.height + 10; + addChild(video); + + var conn:NetConnection = null; + var stream:NetStream = null; + + var tcUrl:Function = function():String { + var url:String = txtUrl.text; + return url.substr(0, url.lastIndexOf("/")); + } + var streamName:Function = function():String { + var url:String = txtUrl.text; + return url.substr(tcUrl().length + 1); + } + + var closeConnection:Function = function():void { + if (stream) { + stream.close(); + stream = null; + } + if (conn) { + conn.close(); + conn = null; + } + btnConn.label = "Connect"; + btnPlay.visible = false; + }; + + btnPlay.visible = false; + btnConn.addEventListener(MouseEvent.CLICK, function(e:MouseEvent):void { + if (btnConn.label == "Connect") { + conn = new NetConnection(); + conn.client = { + onBWDone: function():void{} + }; + conn.addEventListener(NetStatusEvent.NET_STATUS, function(ne:NetStatusEvent):void { + if (ne.info.code == "NetConnection.Connect.Success") { + btnPlay.visible = true; + } else if (ne.info.code == "NetConnection.Connect.Closed") { + closeConnection(); + } + trace(ne.info.code); + }); + conn.connect(tcUrl()); + btnConn.label = "Close"; + } else { + closeConnection(); + } + }); + btnPlay.addEventListener(MouseEvent.CLICK, function(e:MouseEvent):void { + if (stream) { + stream.close(); + stream = null; + } + stream = new NetStream(conn); + stream.client = { + onMetaData: function(metadata:Object):void { + video.width = metadata.width; + video.height = metadata.height; + } + }; + video.attachNetStream(stream); + stream.play(streamName()); + }); + } + } +} \ No newline at end of file diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index 21e6cd3165..0134af8de0 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -54,6 +54,11 @@ bool SrsRecvThread::empty() return queue.empty(); } +int SrsRecvThread::size() +{ + return (int)queue.size(); +} + SrsMessage* SrsRecvThread::pump() { srs_assert(!queue.empty()); @@ -79,6 +84,15 @@ int SrsRecvThread::cycle() { int ret = ERROR_SUCCESS; + // we only recv one message and then process it, + // for the message may cause the thread to stop, + // when stop, the thread is freed, so the messages + // are dropped. + if (!queue.empty()) { + st_usleep(SRS_CONSTS_RTMP_PULSE_TIMEOUT_US); + return ret; + } + SrsMessage* msg = NULL; if ((ret = rtmp->recv_message(&msg)) != ERROR_SUCCESS) { @@ -93,6 +107,10 @@ int SrsRecvThread::cycle() } srs_verbose("play loop recv message. ret=%d", ret); + // put into queue, the send thread will get and process it, + // @see SrsRtmpConn::process_play_control_msg + queue.push_back(msg); + return ret; } diff --git a/trunk/src/app/srs_app_recv_thread.hpp b/trunk/src/app/srs_app_recv_thread.hpp index dd8886e150..640decd1cd 100644 --- a/trunk/src/app/srs_app_recv_thread.hpp +++ b/trunk/src/app/srs_app_recv_thread.hpp @@ -54,6 +54,7 @@ class SrsRecvThread : public ISrsThreadHandler virtual ~SrsRecvThread(); public: virtual bool empty(); + virtual int size(); virtual SrsMessage* pump(); public: virtual int start(); diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index 0d3d6a393b..f280490e4b 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -514,6 +514,11 @@ int SrsRtmpConn::playing(SrsSource* source) // stop isolate recv thread trd.stop(); + // warn for the message is dropped. + if (!trd.empty()) { + srs_warn("drop the received %d messages", trd.size()); + } + return ret; } @@ -549,7 +554,7 @@ int SrsRtmpConn::do_playing(SrsSource* source, SrsRecvThread* trd) // @see: https://github.com/winlinvip/simple-rtmp-server/issues/217 while (!trd->empty()) { SrsMessage* msg = trd->pump(); - srs_warn("pump client message to process."); + srs_verbose("pump client message to process."); if ((ret = process_play_control_msg(consumer, msg)) != ERROR_SUCCESS) { if (!srs_is_system_control_error(ret)) { From e492fa53531ee8f7bc95642e495e064485dfa1ff Mon Sep 17 00:00:00 2001 From: winlin Date: Mon, 24 Nov 2014 16:28:52 +0800 Subject: [PATCH 212/800] fix #212, support publish aac adts raw stream. 2.0.31. --- README.md | 5 + trunk/research/librtmp/Makefile | 6 +- trunk/research/librtmp/srs_aac_raw_publish.c | 198 +++++++++++ .../research/librtmp/srs_audio_raw_publish.c | 2 +- trunk/research/librtmp/srs_h264_raw_publish.c | 18 +- trunk/src/app/srs_app_avc_aac.cpp | 3 - trunk/src/app/srs_app_avc_aac.hpp | 24 +- trunk/src/core/srs_core.hpp | 2 +- trunk/src/kernel/srs_kernel_codec.hpp | 19 ++ trunk/src/kernel/srs_kernel_error.hpp | 3 + trunk/src/libs/srs_librtmp.cpp | 323 ++++++++++++++++-- trunk/src/libs/srs_librtmp.hpp | 73 ++-- trunk/src/rtmp/srs_protocol_utility.cpp | 22 +- trunk/src/rtmp/srs_protocol_utility.hpp | 7 + trunk/src/srs/srs.upp | 1 + 15 files changed, 620 insertions(+), 86 deletions(-) create mode 100644 trunk/research/librtmp/srs_aac_raw_publish.c diff --git a/README.md b/README.md index e621562c40..462b4c45cc 100755 --- a/README.md +++ b/README.md @@ -451,6 +451,10 @@ Supported operating systems and hardware: 1. Support compile [srs-librtmp on windows](https://github.com/winlinvip/srs.librtmp), [bug #213](https://github.com/winlinvip/simple-rtmp-server/issues/213). 1. Support [7.5k+ clients](https://github.com/winlinvip/simple-rtmp-server/issues/217), 4Gbps per process. +1. Support publish aac adts raw stream( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_SrsLibrtmp#publish-audio-raw-stream), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_SrsLibrtmp#publish-audio-raw-stream) +) by srs-librtmp. 1. [no-plan] Support <500ms latency, FRSC(Fast RTMP-compatible Stream Channel tech). 1. [no-plan] Support RTMP 302 redirect [#92](https://github.com/winlinvip/simple-rtmp-server/issues/92). 1. [no-plan] Support multiple processes, for both origin and edge @@ -483,6 +487,7 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History +* v2.0, 2014-11-24, fix [#212](https://github.com/winlinvip/simple-rtmp-server/issues/212), support publish aac adts raw stream. 2.0.31. * v2.0, 2014-11-22, fix [#217](https://github.com/winlinvip/simple-rtmp-server/issues/217), remove timeout recv, support 7.5k+ 250kbps clients. 2.0.30. * v2.0, 2014-11-21, srs-librtmp add rtmp prefix for rtmp/utils/human apis. 2.0.29. * v2.0, 2014-11-21, refine examples of srs-librtmp, add srs_print_rtmp_packet. 2.0.28. diff --git a/trunk/research/librtmp/Makefile b/trunk/research/librtmp/Makefile index d5f19606e1..f8e9db4f9f 100755 --- a/trunk/research/librtmp/Makefile +++ b/trunk/research/librtmp/Makefile @@ -7,7 +7,7 @@ else objs/srs_flv_injecter objs/srs_publish objs/srs_play \ objs/srs_ingest_flv objs/srs_ingest_rtmp objs/srs_detect_rtmp \ objs/srs_bandwidth_check objs/srs_h264_raw_publish \ - objs/srs_audio_raw_publish + objs/srs_audio_raw_publish objs/srs_aac_raw_publish endif .PHONY: default clean help ssl nossl @@ -26,6 +26,7 @@ help: @echo " srs_publish publish program using srs-librtmp" @echo " srs_h264_raw_publish publish raw h.264 stream to SSR by srs-librtmp" @echo " srs_audio_raw_publish publish raw audio stream to SSR by srs-librtmp" + @echo " srs_aac_raw_publish publish raw aac stream to SSR by srs-librtmp" @echo " srs_play play program using srs-librtmp" @echo " srs_ingest_flv ingest flv file and publish to RTMP server." @echo " srs_ingest_rtmp ingest RTMP and publish to RTMP server." @@ -90,6 +91,9 @@ objs/srs_h264_raw_publish: srs_h264_raw_publish.c $(SRS_RESEARCH_DEPS) $(SRS_LIB objs/srs_audio_raw_publish: srs_audio_raw_publish.c $(SRS_RESEARCH_DEPS) $(SRS_LIBRTMP_I) $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(GCC) srs_audio_raw_publish.c $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(EXTRA_CXX_FLAG) -o objs/srs_audio_raw_publish +objs/srs_aac_raw_publish: srs_aac_raw_publish.c $(SRS_RESEARCH_DEPS) $(SRS_LIBRTMP_I) $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) + $(GCC) srs_aac_raw_publish.c $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(EXTRA_CXX_FLAG) -o objs/srs_aac_raw_publish + objs/srs_play: srs_play.c $(SRS_RESEARCH_DEPS) $(SRS_LIBRTMP_I) $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(GCC) srs_play.c $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(EXTRA_CXX_FLAG) -o objs/srs_play diff --git a/trunk/research/librtmp/srs_aac_raw_publish.c b/trunk/research/librtmp/srs_aac_raw_publish.c new file mode 100644 index 0000000000..b1e9511390 --- /dev/null +++ b/trunk/research/librtmp/srs_aac_raw_publish.c @@ -0,0 +1,198 @@ +/* +The MIT License (MIT) + +Copyright (c) 2013-2014 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +/** +gcc srs_aac_raw_publish.c ../../objs/lib/srs_librtmp.a -g -O0 -lstdc++ -o srs_aac_raw_publish +*/ + +#include +#include +#include + +// for open audio raw file. +#include +#include +#include + +#include "../../objs/include/srs_librtmp.h" + +// https://github.com/winlinvip/simple-rtmp-server/issues/212#issuecomment-63648892 +// allspace: +// Take this file as an example: https://github.com/allspace/files/blob/master/srs.pcm +// It's captured using SDK callback method. I have filtered out h264 video, so it's audio only now. +// For every frame, it's a 8 bytes vendor specific header, following 160 bytes audio frame. +// The header part can be ignored. +int read_audio_frame(char* data, int size, char** pp, char** frame, int* frame_size) +{ + char* p = *pp; + + // @remark, for this demo, to publish aac raw file to SRS, + // we search the adts frame from the buffer which cached the aac data. + // please get aac adts raw data from device, it always a encoded frame. + if (!srs_aac_is_adts(p, size - (p - data))) { + srs_human_trace("aac adts raw data invalid."); + return -1; + } + + // @see srs_audio_write_raw_frame + // each frame prefixed aac adts header, '1111 1111 1111'B, that is 0xFFF., + // for instance, frame = FF F1 5C 80 13 A0 FC 00 D0 33 83 E8 5B + *frame = p; + // skip some data. + // @remark, user donot need to do this. + p += srs_aac_adts_frame_size(p, size - (p - data)); + + *pp = p; + *frame_size = p - *frame; + if (*frame_size <= 0) { + srs_human_trace("aac adts raw data invalid."); + return -1; + } + + return 0; +} + +int main(int argc, char** argv) +{ + printf("publish raw audio as rtmp stream to server like FMLE/FFMPEG/Encoder\n"); + printf("SRS(simple-rtmp-server) client librtmp library.\n"); + printf("version: %d.%d.%d\n", srs_version_major(), srs_version_minor(), srs_version_revision()); + + if (argc <= 2) { + printf("Usage: %s \n", argv[0]); + printf(" audio_raw_file: the audio raw steam file.\n"); + printf(" rtmp_publish_url: the rtmp publish url.\n"); + printf("For example:\n"); + printf(" %s ./audio.raw.aac rtmp://127.0.0.1:1935/live/livestream\n", argv[0]); + printf("Where the file: http://winlinvip.github.io/srs.release/3rdparty/audio.raw.aac\n"); + printf("See: https://github.com/winlinvip/simple-rtmp-server/issues/212\n"); + exit(-1); + } + + const char* raw_file = argv[1]; + const char* rtmp_url = argv[2]; + srs_human_trace("raw_file=%s, rtmp_url=%s", raw_file, rtmp_url); + + // open file + int raw_fd = open(raw_file, O_RDONLY); + if (raw_fd < 0) { + srs_human_trace("open audio raw file %s failed.", raw_fd); + goto rtmp_destroy; + } + + off_t file_size = lseek(raw_fd, 0, SEEK_END); + if (file_size <= 0) { + srs_human_trace("audio raw file %s empty.", raw_file); + goto rtmp_destroy; + } + srs_human_trace("read entirely audio raw file, size=%dKB", (int)(file_size / 1024)); + + char* audio_raw = (char*)malloc(file_size); + if (!audio_raw) { + srs_human_trace("alloc raw buffer failed for file %s.", raw_file); + goto rtmp_destroy; + } + + lseek(raw_fd, 0, SEEK_SET); + ssize_t nb_read = 0; + if ((nb_read = read(raw_fd, audio_raw, file_size)) != file_size) { + srs_human_trace("buffer %s failed, expect=%dKB, actual=%dKB.", + raw_file, (int)(file_size / 1024), (int)(nb_read / 1024)); + goto rtmp_destroy; + } + + // connect rtmp context + srs_rtmp_t rtmp = srs_rtmp_create(rtmp_url); + + if (srs_rtmp_handshake(rtmp) != 0) { + srs_human_trace("simple handshake failed."); + goto rtmp_destroy; + } + srs_human_trace("simple handshake success"); + + if (srs_rtmp_connect_app(rtmp) != 0) { + srs_human_trace("connect vhost/app failed."); + goto rtmp_destroy; + } + srs_human_trace("connect vhost/app success"); + + if (srs_rtmp_publish_stream(rtmp) != 0) { + srs_human_trace("publish stream failed."); + goto rtmp_destroy; + } + srs_human_trace("publish stream success"); + + u_int32_t timestamp = 0; + u_int32_t time_delta = 45; + // @remark, to decode the file. + char* p = audio_raw; + for (;p < audio_raw + file_size;) { + // @remark, read a frame from file buffer. + char* data = NULL; + int size = 0; + if (read_audio_frame(audio_raw, file_size, &p, &data, &size) < 0) { + srs_human_trace("read a frame from file buffer failed."); + goto rtmp_destroy; + } + + // 0 = Linear PCM, platform endian + // 1 = ADPCM + // 2 = MP3 + // 7 = G.711 A-law logarithmic PCM + // 8 = G.711 mu-law logarithmic PCM + // 10 = AAC + // 11 = Speex + char sound_format = 10; + // 2 = 22 kHz + char sound_rate = 2; + // 1 = 16-bit samples + char sound_size = 1; + // 1 = Stereo sound + char sound_type = 1; + + timestamp += time_delta; + + int ret = 0; + if ((ret = srs_audio_write_raw_frame(rtmp, + sound_format, sound_rate, sound_size, sound_type, + data, size, timestamp)) != 0 + ) { + srs_human_trace("send audio raw data failed. ret=%d", ret); + goto rtmp_destroy; + } + + srs_human_trace("sent packet: type=%s, time=%d, size=%d, codec=%d, rate=%d, sample=%d, channel=%d", + srs_human_flv_tag_type2string(SRS_RTMP_TYPE_AUDIO), timestamp, size, sound_format, sound_rate, sound_size, + sound_type); + + // @remark, when use encode device, it not need to sleep. + usleep(1000 * time_delta); + } + +rtmp_destroy: + srs_rtmp_destroy(rtmp); + close(raw_fd); + free(audio_raw); + + return 0; +} + diff --git a/trunk/research/librtmp/srs_audio_raw_publish.c b/trunk/research/librtmp/srs_audio_raw_publish.c index 9e11023ba1..4c9c899142 100644 --- a/trunk/research/librtmp/srs_audio_raw_publish.c +++ b/trunk/research/librtmp/srs_audio_raw_publish.c @@ -166,7 +166,7 @@ int main(int argc, char** argv) if (srs_audio_write_raw_frame(rtmp, sound_format, sound_rate, sound_size, sound_type, - 0, data, size, timestamp) != 0 + data, size, timestamp) != 0 ) { srs_human_trace("send audio raw data failed."); goto rtmp_destroy; diff --git a/trunk/research/librtmp/srs_h264_raw_publish.c b/trunk/research/librtmp/srs_h264_raw_publish.c index 0b7bee1ad1..d85da46d17 100644 --- a/trunk/research/librtmp/srs_h264_raw_publish.c +++ b/trunk/research/librtmp/srs_h264_raw_publish.c @@ -166,16 +166,16 @@ int main(int argc, char** argv) } // send out the h264 packet over RTMP - int error = srs_h264_write_raw_frames(rtmp, data, size, dts, pts); - if (error != 0) { - if (srs_h264_is_dvbsp_error(error)) { - srs_human_trace("ignore drop video error, code=%d", error); - } else if (srs_h264_is_duplicated_sps_error(error)) { - srs_human_trace("ignore duplicated sps, code=%d", error); - } else if (srs_h264_is_duplicated_pps_error(error)) { - srs_human_trace("ignore duplicated pps, code=%d", error); + int ret = srs_h264_write_raw_frames(rtmp, data, size, dts, pts); + if (ret != 0) { + if (srs_h264_is_dvbsp_error(ret)) { + srs_human_trace("ignore drop video error, code=%d", ret); + } else if (srs_h264_is_duplicated_sps_error(ret)) { + srs_human_trace("ignore duplicated sps, code=%d", ret); + } else if (srs_h264_is_duplicated_pps_error(ret)) { + srs_human_trace("ignore duplicated pps, code=%d", ret); } else { - srs_human_trace("send h264 raw data failed."); + srs_human_trace("send h264 raw data failed. ret=%d", ret); goto rtmp_destroy; } } diff --git a/trunk/src/app/srs_app_avc_aac.cpp b/trunk/src/app/srs_app_avc_aac.cpp index 182da61aed..7c5c5cb450 100644 --- a/trunk/src/app/srs_app_avc_aac.cpp +++ b/trunk/src/app/srs_app_avc_aac.cpp @@ -249,9 +249,6 @@ int SrsAvcAacCodec::audio_aac_demux(char* data, int size, SrsCodecSample* sample return ret; } - // aac_profile = audioObjectType - 1 - aac_profile--; - // TODO: FIXME: to support aac he/he-v2, see: ngx_rtmp_codec_parse_aac_header // @see: https://github.com/winlinvip/nginx-rtmp-module/commit/3a5f9eea78fc8d11e8be922aea9ac349b9dcbfc2 // diff --git a/trunk/src/app/srs_app_avc_aac.hpp b/trunk/src/app/srs_app_avc_aac.hpp index 96b13e6f43..24a83b8d88 100644 --- a/trunk/src/app/srs_app_avc_aac.hpp +++ b/trunk/src/app/srs_app_avc_aac.hpp @@ -38,25 +38,6 @@ class SrsAmf0Object; #define __SRS_SRS_MAX_CODEC_SAMPLE 128 #define __SRS_AAC_SAMPLE_RATE_UNSET 15 -/** -* the FLV/RTMP supported audio sample rate. -* Sampling rate. The following values are defined: -* 0 = 5.5 kHz = 5512 Hz -* 1 = 11 kHz = 11025 Hz -* 2 = 22 kHz = 22050 Hz -* 3 = 44 kHz = 44100 Hz -*/ -enum SrsCodecAudioSampleRate -{ - // set to the max value to reserved, for array map. - SrsCodecAudioSampleRateReserved = 4, - - SrsCodecAudioSampleRate5512 = 0, - SrsCodecAudioSampleRate11025 = 1, - SrsCodecAudioSampleRate22050 = 2, - SrsCodecAudioSampleRate44100 = 3, -}; - /** * the FLV/RTMP supported audio sample size. * Size of each audio sample. This parameter only pertains to @@ -224,8 +205,9 @@ class SrsAvcAacCodec public: /** * audio specified - * 1.6.2.1 AudioSpecificConfig, in aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 33. - * audioObjectType, value defines in 7.1 Profiles, aac-iso-13818-7.pdf, page 40. + * audioObjectType, in 1.6.2.1 AudioSpecificConfig, page 33, + * 1.5.1.1 Audio object type definition, page 23, + * in aac-mp4a-format-ISO_IEC_14496-3+2001.pdf. */ u_int8_t aac_profile; /** diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index b988a1fbb9..b8b9474374 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 30 +#define VERSION_REVISION 31 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/kernel/srs_kernel_codec.hpp b/trunk/src/kernel/srs_kernel_codec.hpp index 32b019bd5d..507fc0b552 100644 --- a/trunk/src/kernel/srs_kernel_codec.hpp +++ b/trunk/src/kernel/srs_kernel_codec.hpp @@ -144,6 +144,25 @@ enum SrsCodecAudio SrsCodecAudioReservedDeviceSpecificSound = 15, }; +/** +* the FLV/RTMP supported audio sample rate. +* Sampling rate. The following values are defined: +* 0 = 5.5 kHz = 5512 Hz +* 1 = 11 kHz = 11025 Hz +* 2 = 22 kHz = 22050 Hz +* 3 = 44 kHz = 44100 Hz +*/ +enum SrsCodecAudioSampleRate +{ + // set to the max value to reserved, for array map. + SrsCodecAudioSampleRateReserved = 4, + + SrsCodecAudioSampleRate5512 = 0, + SrsCodecAudioSampleRate11025 = 1, + SrsCodecAudioSampleRate22050 = 2, + SrsCodecAudioSampleRate44100 = 3, +}; + /** * Annex E. The FLV File Format * @see SrsAvcAacCodec for the media stream codec. diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index f76f8129a2..14e94378b7 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -189,6 +189,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define ERROR_H264_DROP_BEFORE_SPS_PPS 3043 #define ERROR_H264_DUPLICATED_SPS 3044 #define ERROR_H264_DUPLICATED_PPS 3045 +#define ERROR_AAC_REQUIRED_ADTS 3046 +#define ERROR_AAC_ADTS_HEADER 3047 +#define ERROR_AAC_DATA_INVALID 3048 /** * whether the error code is an system control error. diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index 362963d57f..b6f4155679 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -75,7 +75,7 @@ struct Context int stream_id; // for h264 raw stream, - // see: https://github.com/winlinvip/simple-rtmp-server/issues/66#issuecomment-62240521 + // @see: https://github.com/winlinvip/simple-rtmp-server/issues/66#issuecomment-62240521 SrsStream h264_raw_stream; // about SPS, @see: 7.3.2.1.1, H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 62 std::string h264_sps; @@ -87,6 +87,11 @@ struct Context // @see https://github.com/winlinvip/simple-rtmp-server/issues/204 bool h264_sps_changed; bool h264_pps_changed; + // for aac raw stream, + // @see: https://github.com/winlinvip/simple-rtmp-server/issues/212#issuecomment-64146250 + SrsStream aac_raw_stream; + // the aac sequence header. + std::string aac_specific_config; Context() { rtmp = NULL; @@ -859,22 +864,18 @@ int srs_rtmp_write_packet(srs_rtmp_t rtmp, char type, u_int32_t timestamp, char* } /** -* write audio raw frame to SRS. +* directly write a audio frame. */ -int srs_audio_write_raw_frame(srs_rtmp_t rtmp, +int __srs_write_audio_raw_frame(Context* context, char sound_format, char sound_rate, char sound_size, char sound_type, char aac_packet_type, char* frame, int frame_size, u_int32_t timestamp ) { - Context* context = (Context*)rtmp; - srs_assert(context); - - // TODO: FIXME: for aac, must send the sequence header first. // for audio frame, there is 1 or 2 bytes header: // 1bytes, SoundFormat|SoundRate|SoundSize|SoundType - // 1bytes, AACPacketType for SoundFormat == 10 + // 1bytes, AACPacketType for SoundFormat == 10, 0 is sequence header. int size = frame_size + 1; - if (aac_packet_type == SrsCodecAudioAAC) { + if (sound_format == SrsCodecAudioAAC) { size += 1; } char* data = new char[size]; @@ -887,7 +888,7 @@ int srs_audio_write_raw_frame(srs_rtmp_t rtmp, *p++ = audio_header; - if (aac_packet_type == SrsCodecAudioAAC) { + if (sound_format == SrsCodecAudioAAC) { *p++ = aac_packet_type; } @@ -896,6 +897,278 @@ int srs_audio_write_raw_frame(srs_rtmp_t rtmp, return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_AUDIO, timestamp, data, size); } +/** +* write aac frame in adts. +*/ +int __srs_write_aac_adts_frame(Context* context, + char sound_format, char sound_rate, char sound_size, char sound_type, + char aac_profile, char aac_samplerate, char aac_channel, + char* frame, int frame_size, u_int32_t timestamp +) { + int ret = ERROR_SUCCESS; + + // override the aac samplerate by user specified. + // @see https://github.com/winlinvip/simple-rtmp-server/issues/212#issuecomment-64146899 + switch (sound_rate) { + case SrsCodecAudioSampleRate11025: + aac_samplerate = 0x0a; break; + case SrsCodecAudioSampleRate22050: + aac_samplerate = 0x07; break; + case SrsCodecAudioSampleRate44100: + aac_samplerate = 0x04; break; + default: + break; + } + + // send out aac sequence header if not sent. + if (context->aac_specific_config.empty()) { + char ch = 0; + // @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf + // AudioSpecificConfig (), page 33 + // 1.6.2.1 AudioSpecificConfig + // audioObjectType; 5 bslbf + ch = (aac_profile << 3) & 0xf8; + // 3bits left. + + // samplingFrequencyIndex; 4 bslbf + ch |= (aac_samplerate >> 1) & 0x07; + context->aac_specific_config += ch; + ch = (aac_samplerate << 7) & 0x80; + if (aac_samplerate == 0x0f) { + return ERROR_AAC_DATA_INVALID; + } + // 7bits left. + + // channelConfiguration; 4 bslbf + ch |= (aac_channel << 3) & 0x70; + // 3bits left. + + // only support aac profile 1-4. + if (aac_profile < 1 || aac_profile > 4) { + return ERROR_AAC_DATA_INVALID; + } + // GASpecificConfig(), page 451 + // 4.4.1 Decoder configuration (GASpecificConfig) + // frameLengthFlag; 1 bslbf + // dependsOnCoreCoder; 1 bslbf + // extensionFlag; 1 bslbf + context->aac_specific_config += ch; + + if ((ret = __srs_write_audio_raw_frame(context, + sound_format, sound_rate, sound_size, sound_type, + 0, (char*)context->aac_specific_config.data(), + context->aac_specific_config.length(), + timestamp)) != ERROR_SUCCESS + ) { + return ret; + } + } + + return __srs_write_audio_raw_frame(context, + sound_format, sound_rate, sound_size, sound_type, + 1, frame, frame_size, timestamp); +} + +/** +* write aac frames in adts. +*/ +int __srs_write_aac_adts_frames(Context* context, + char sound_format, char sound_rate, char sound_size, char sound_type, + char* frame, int frame_size, u_int32_t timestamp +) { + int ret = ERROR_SUCCESS; + + SrsStream* stream = &context->aac_raw_stream; + if ((ret = stream->initialize(frame, frame_size)) != ERROR_SUCCESS) { + return ret; + } + + while (!stream->empty()) { + int adts_header_start = stream->pos(); + + // decode the ADTS. + // @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 75, + // 1.A.2.2 Audio_Data_Transport_Stream frame, ADTS + // @see https://github.com/winlinvip/simple-rtmp-server/issues/212#issuecomment-64145885 + // byte_alignment() + + // adts_fixed_header: + // 12bits syncword, + // 16bits left. + // adts_variable_header: + // 28bits + // 12+16+28=56bits + // adts_error_check: + // 16bits if protection_absent + // 56+16=72bits + // if protection_absent: + // require(7bytes)=56bits + // else + // require(9bytes)=72bits + if (!stream->require(7)) { + return ERROR_AAC_ADTS_HEADER; + } + + // for aac, the frame must be ADTS format. + if (!srs_aac_startswith_adts(stream)) { + return ERROR_AAC_REQUIRED_ADTS; + } + + // Syncword 12 bslbf + stream->read_1bytes(); + // 4bits left. + // adts_fixed_header(), 1.A.2.2.1 Fixed Header of ADTS + // ID 1 bslbf + // Layer 2 uimsbf + // protection_absent 1 bslbf + int8_t fh0 = (stream->read_1bytes() & 0x0f); + /*int8_t fh_id = (fh0 >> 3) & 0x01;*/ + /*int8_t fh_layer = (fh0 >> 1) & 0x03;*/ + int8_t fh_protection_absent = fh0 & 0x01; + + int16_t fh1 = stream->read_2bytes(); + // Profile_ObjectType 2 uimsbf + // sampling_frequency_index 4 uimsbf + // private_bit 1 bslbf + // channel_configuration 3 uimsbf + // original/copy 1 bslbf + // home 1 bslbf + int8_t fh_Profile_ObjectType = (fh1 >> 14) & 0x03; + int8_t fh_sampling_frequency_index = (fh1 >> 10) & 0x0f; + /*int8_t fh_private_bit = (fh1 >> 9) & 0x01;*/ + int8_t fh_channel_configuration = (fh1 >> 6) & 0x07; + /*int8_t fh_original = (fh1 >> 5) & 0x01;*/ + /*int8_t fh_home = (fh1 >> 4) & 0x01;*/ + // @remark, Emphasis is removed, + // @see https://github.com/winlinvip/simple-rtmp-server/issues/212#issuecomment-64154736 + //int8_t fh_Emphasis = (fh1 >> 2) & 0x03; + // 4bits left. + // adts_variable_header(), 1.A.2.2.2 Variable Header of ADTS + // copyright_identification_bit 1 bslbf + // copyright_identification_start 1 bslbf + /*int8_t fh_copyright_identification_bit = (fh1 >> 3) & 0x01;*/ + /*int8_t fh_copyright_identification_start = (fh1 >> 2) & 0x01;*/ + // aac_frame_length 13 bslbf: Length of the frame including headers and error_check in bytes. + // use the left 2bits as the 13 and 12 bit, + // the aac_frame_length is 13bits, so we move 13-2=11. + int16_t fh_aac_frame_length = (fh1 << 11) & 0x0800; + + int32_t fh2 = stream->read_3bytes(); + // aac_frame_length 13 bslbf: consume the first 13-2=11bits + // the fh2 is 24bits, so we move right 24-11=13. + fh_aac_frame_length |= (fh2 >> 13) & 0x07ff; + // adts_buffer_fullness 11 bslbf + /*int16_t fh_adts_buffer_fullness = (fh2 >> 2) & 0x7ff;*/ + // no_raw_data_blocks_in_frame 2 uimsbf + /*int16_t fh_no_raw_data_blocks_in_frame = fh2 & 0x03;*/ + // adts_error_check(), 1.A.2.2.3 Error detection + if (!fh_protection_absent) { + if (!stream->require(2)) { + return ERROR_AAC_ADTS_HEADER; + } + // crc_check 16 Rpchof + /*int16_t crc_check = */stream->read_2bytes(); + } + + // TODO: check the fh_sampling_frequency_index + // TODO: check the fh_channel_configuration + + // raw_data_blocks + int adts_header_size = stream->pos() - adts_header_start; + int raw_data_size = fh_aac_frame_length - adts_header_size; + if (!stream->require(raw_data_size)) { + return ERROR_AAC_ADTS_HEADER; + } + + char* raw_data = stream->data() + stream->pos(); + if ((ret = __srs_write_aac_adts_frame(context, + sound_format, sound_rate, sound_size, sound_type, + fh_Profile_ObjectType, fh_sampling_frequency_index, fh_channel_configuration, + raw_data, raw_data_size, timestamp)) != ERROR_SUCCESS + ) { + return ret; + } + stream->skip(raw_data_size); + } + + return ret; +} + +/** +* write audio raw frame to SRS. +*/ +int srs_audio_write_raw_frame(srs_rtmp_t rtmp, + char sound_format, char sound_rate, char sound_size, char sound_type, + char* frame, int frame_size, u_int32_t timestamp +) { + int ret = ERROR_SUCCESS; + + Context* context = (Context*)rtmp; + srs_assert(context); + + if (sound_format == SrsCodecAudioAAC) { + // for aac, the frame must be ADTS format. + if (!srs_aac_is_adts(frame, frame_size)) { + return ERROR_AAC_REQUIRED_ADTS; + } + + // for aac, demux the ADTS to RTMP format. + return __srs_write_aac_adts_frames(context, + sound_format, sound_rate, sound_size, sound_type, + frame, frame_size, timestamp); + } else { + // for other data, directly write frame. + return __srs_write_audio_raw_frame(context, + sound_format, sound_rate, sound_size, sound_type, + 0, frame, frame_size, timestamp); + } + + + return ret; +} + +/** +* whether aac raw data is in adts format, +* which bytes sequence matches '1111 1111 1111'B, that is 0xFFF. +*/ +srs_bool srs_aac_is_adts(char* aac_raw_data, int ac_raw_size) +{ + SrsStream stream; + if (stream.initialize(aac_raw_data, ac_raw_size) != ERROR_SUCCESS) { + return false; + } + + return srs_aac_startswith_adts(&stream); +} + +/** +* parse the adts header to get the frame size. +*/ +int srs_aac_adts_frame_size(char* aac_raw_data, int ac_raw_size) +{ + int size = -1; + + if (!srs_aac_is_adts(aac_raw_data, ac_raw_size)) { + return size; + } + + // adts always 7bytes. + if (ac_raw_size <= 7) { + return size; + } + + // last 2bits + int16_t ch3 = aac_raw_data[3]; + // whole 8bits + int16_t ch4 = aac_raw_data[4]; + // first 3bits + int16_t ch5 = aac_raw_data[5]; + + size = ((ch3 << 11) & 0x1800) | ((ch4 << 3) & 0x07f8) | ((ch5 >> 5) & 0x0007); + + return size; +} + /** * write h264 packet, with rtmp header. * @param frame_type, SrsCodecVideoAVCFrameKeyFrame or SrsCodecVideoAVCFrameInterFrame. @@ -1224,22 +1497,22 @@ int srs_h264_write_raw_frames(srs_rtmp_t rtmp, return error_code_return; } -srs_h264_bool srs_h264_is_dvbsp_error(int error_code) +srs_bool srs_h264_is_dvbsp_error(int error_code) { return error_code == ERROR_H264_DROP_BEFORE_SPS_PPS; } -srs_h264_bool srs_h264_is_duplicated_sps_error(int error_code) +srs_bool srs_h264_is_duplicated_sps_error(int error_code) { return error_code == ERROR_H264_DUPLICATED_SPS; } -srs_h264_bool srs_h264_is_duplicated_pps_error(int error_code) +srs_bool srs_h264_is_duplicated_pps_error(int error_code) { return error_code == ERROR_H264_DUPLICATED_PPS; } -int srs_h264_startswith_annexb(char* h264_raw_data, int h264_raw_size, int* pnb_start_code) +srs_bool srs_h264_startswith_annexb(char* h264_raw_data, int h264_raw_size, int* pnb_start_code) { SrsStream stream; if (stream.initialize(h264_raw_data, h264_raw_size) != ERROR_SUCCESS) { @@ -1417,17 +1690,17 @@ void srs_flv_lseek(srs_flv_t flv, int64_t offset) context->reader.lseek(offset); } -srs_flv_bool srs_flv_is_eof(int error_code) +srs_bool srs_flv_is_eof(int error_code) { return error_code == ERROR_SYSTEM_FILE_EOF; } -srs_flv_bool srs_flv_is_sequence_header(char* data, int32_t size) +srs_bool srs_flv_is_sequence_header(char* data, int32_t size) { return SrsFlvCodec::video_is_sequence_header(data, (int)size); } -srs_flv_bool srs_flv_is_keyframe(char* data, int32_t size) +srs_bool srs_flv_is_keyframe(char* data, int32_t size) { return SrsFlvCodec::video_is_keyframe(data, (int)size); } @@ -1517,43 +1790,43 @@ int srs_amf0_serialize(srs_amf0_t amf0, char* data, int size) return ret; } -srs_amf0_bool srs_amf0_is_string(srs_amf0_t amf0) +srs_bool srs_amf0_is_string(srs_amf0_t amf0) { SrsAmf0Any* any = (SrsAmf0Any*)amf0; return any->is_string(); } -srs_amf0_bool srs_amf0_is_boolean(srs_amf0_t amf0) +srs_bool srs_amf0_is_boolean(srs_amf0_t amf0) { SrsAmf0Any* any = (SrsAmf0Any*)amf0; return any->is_boolean(); } -srs_amf0_bool srs_amf0_is_number(srs_amf0_t amf0) +srs_bool srs_amf0_is_number(srs_amf0_t amf0) { SrsAmf0Any* any = (SrsAmf0Any*)amf0; return any->is_number(); } -srs_amf0_bool srs_amf0_is_null(srs_amf0_t amf0) +srs_bool srs_amf0_is_null(srs_amf0_t amf0) { SrsAmf0Any* any = (SrsAmf0Any*)amf0; return any->is_null(); } -srs_amf0_bool srs_amf0_is_object(srs_amf0_t amf0) +srs_bool srs_amf0_is_object(srs_amf0_t amf0) { SrsAmf0Any* any = (SrsAmf0Any*)amf0; return any->is_object(); } -srs_amf0_bool srs_amf0_is_ecma_array(srs_amf0_t amf0) +srs_bool srs_amf0_is_ecma_array(srs_amf0_t amf0) { SrsAmf0Any* any = (SrsAmf0Any*)amf0; return any->is_ecma_array(); } -srs_amf0_bool srs_amf0_is_strict_array(srs_amf0_t amf0) +srs_bool srs_amf0_is_strict_array(srs_amf0_t amf0) { SrsAmf0Any* any = (SrsAmf0Any*)amf0; return any->is_strict_array(); @@ -1565,7 +1838,7 @@ const char* srs_amf0_to_string(srs_amf0_t amf0) return any->to_str_raw(); } -srs_amf0_bool srs_amf0_to_boolean(srs_amf0_t amf0) +srs_bool srs_amf0_to_boolean(srs_amf0_t amf0) { SrsAmf0Any* any = (SrsAmf0Any*)amf0; return any->to_boolean(); diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index 371a2ca83d..b941219dde 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -84,6 +84,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. extern "C"{ #endif +// typedefs +typedef int srs_bool; + /************************************************************* ************************************************************** * srs-librtmp version @@ -303,12 +306,15 @@ extern int srs_rtmp_write_packet(srs_rtmp_t rtmp, * @param sound_type Mono or stereo sound * 0 = Mono sound * 1 = Stereo sound -* @param aac_packet_type The following values are defined: -* 0 = AAC sequence header -* 1 = AAC raw * @param timestamp The timestamp of audio. * -* @remark Ignore aac_packet_type if not aac(sound_format!=10). +* @example /trunk/research/librtmp/srs_aac_raw_publish.c +* @example /trunk/research/librtmp/srs_audio_raw_publish.c +* +* @remark for aac, the frame must be in ADTS format. +* @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 75, 1.A.2.2 ADTS +* @remark for aac, only support profile 1-4, AAC main/LC/SSR/LTP, +* @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 23, 1.5.1.1 Audio object type * * @see https://github.com/winlinvip/simple-rtmp-server/issues/212 * @see E.4.2.1 AUDIODATA of video_file_format_spec_v10_1.pdf @@ -317,15 +323,38 @@ extern int srs_rtmp_write_packet(srs_rtmp_t rtmp, */ extern int srs_audio_write_raw_frame(srs_rtmp_t rtmp, char sound_format, char sound_rate, char sound_size, char sound_type, - char aac_packet_type, char* frame, int frame_size, u_int32_t timestamp + char* frame, int frame_size, u_int32_t timestamp ); +/** +* whether aac raw data is in adts format, +* which bytes sequence matches '1111 1111 1111'B, that is 0xFFF. +* @param aac_raw_data the input aac raw data, a encoded aac frame data. +* @param ac_raw_size the size of aac raw data. +* +* @reamrk used to check whether current frame is in adts format. +* @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 75, 1.A.2.2 ADTS +* @example /trunk/research/librtmp/srs_aac_raw_publish.c +* +* @return 0 false; otherwise, true. +*/ +extern srs_bool srs_aac_is_adts(char* aac_raw_data, int ac_raw_size); + +/** +* parse the adts header to get the frame size, +* which bytes sequence matches '1111 1111 1111'B, that is 0xFFF. +* @param aac_raw_data the input aac raw data, a encoded aac frame data. +* @param ac_raw_size the size of aac raw data. +* +* @return failed when <=0 failed; otherwise, ok. +*/ +extern int srs_aac_adts_frame_size(char* aac_raw_data, int ac_raw_size); + /************************************************************* ************************************************************** * h264 raw codec ************************************************************** *************************************************************/ -typedef int srs_h264_bool; /** * write h.264 raw frame over RTMP to rtmp server. * @param frames the input h264 raw data, encoded h.264 I/P/B frames data. @@ -392,21 +421,21 @@ extern int srs_h264_write_raw_frames(srs_rtmp_t rtmp, * so, when error and reconnect the rtmp, the first video is not sps/pps(sequence header), * this will cause SRS server to disable HLS. */ -extern srs_h264_bool srs_h264_is_dvbsp_error(int error_code); +extern srs_bool srs_h264_is_dvbsp_error(int error_code); /** * whether error_code is duplicated sps error. * * @see https://github.com/winlinvip/simple-rtmp-server/issues/204 * @example /trunk/research/librtmp/srs_h264_raw_publish.c */ -extern srs_h264_bool srs_h264_is_duplicated_sps_error(int error_code); +extern srs_bool srs_h264_is_duplicated_sps_error(int error_code); /** * whether error_code is duplicated pps error. * * @see https://github.com/winlinvip/simple-rtmp-server/issues/204 * @example /trunk/research/librtmp/srs_h264_raw_publish.c */ -extern srs_h264_bool srs_h264_is_duplicated_pps_error(int error_code); +extern srs_bool srs_h264_is_duplicated_pps_error(int error_code); /** * whether h264 raw data starts with the annexb, * which bytes sequence matches N[00] 00 00 01, where N>=0. @@ -420,7 +449,7 @@ extern srs_h264_bool srs_h264_is_duplicated_pps_error(int error_code); * * @return 0 false; otherwise, true. */ -extern int srs_h264_startswith_annexb( +extern srs_bool srs_h264_startswith_annexb( char* h264_raw_data, int h264_raw_size, int* pnb_start_code ); @@ -435,7 +464,6 @@ extern int srs_h264_startswith_annexb( ************************************************************** *************************************************************/ typedef void* srs_flv_t; -typedef int srs_flv_bool; /* open flv file for both read/write. */ extern srs_flv_t srs_flv_open_read(const char* file); extern srs_flv_t srs_flv_open_write(const char* file); @@ -510,20 +538,20 @@ extern int64_t srs_flv_tellg(srs_flv_t flv); extern void srs_flv_lseek(srs_flv_t flv, int64_t offset); /* error code */ /* whether the error code indicates EOF */ -extern srs_flv_bool srs_flv_is_eof(int error_code); +extern srs_bool srs_flv_is_eof(int error_code); /* media codec */ /** * whether the video body is sequence header * @param data, the data of tag, read by srs_flv_read_tag_data(). * @param size, the size of tag, read by srs_flv_read_tag_data(). */ -extern srs_flv_bool srs_flv_is_sequence_header(char* data, int32_t size); +extern srs_bool srs_flv_is_sequence_header(char* data, int32_t size); /** * whether the video body is keyframe * @param data, the data of tag, read by srs_flv_read_tag_data(). * @param size, the size of tag, read by srs_flv_read_tag_data(). */ -extern srs_flv_bool srs_flv_is_keyframe(char* data, int32_t size); +extern srs_bool srs_flv_is_keyframe(char* data, int32_t size); /************************************************************* ************************************************************** @@ -534,7 +562,6 @@ extern srs_flv_bool srs_flv_is_keyframe(char* data, int32_t size); *************************************************************/ /* the output handler. */ typedef void* srs_amf0_t; -typedef int srs_amf0_bool; typedef double srs_amf0_number; /** * parse amf0 from data. @@ -552,16 +579,16 @@ extern void srs_amf0_free_bytes(char* data); extern int srs_amf0_size(srs_amf0_t amf0); extern int srs_amf0_serialize(srs_amf0_t amf0, char* data, int size); /* type detecter */ -extern srs_amf0_bool srs_amf0_is_string(srs_amf0_t amf0); -extern srs_amf0_bool srs_amf0_is_boolean(srs_amf0_t amf0); -extern srs_amf0_bool srs_amf0_is_number(srs_amf0_t amf0); -extern srs_amf0_bool srs_amf0_is_null(srs_amf0_t amf0); -extern srs_amf0_bool srs_amf0_is_object(srs_amf0_t amf0); -extern srs_amf0_bool srs_amf0_is_ecma_array(srs_amf0_t amf0); -extern srs_amf0_bool srs_amf0_is_strict_array(srs_amf0_t amf0); +extern srs_bool srs_amf0_is_string(srs_amf0_t amf0); +extern srs_bool srs_amf0_is_boolean(srs_amf0_t amf0); +extern srs_bool srs_amf0_is_number(srs_amf0_t amf0); +extern srs_bool srs_amf0_is_null(srs_amf0_t amf0); +extern srs_bool srs_amf0_is_object(srs_amf0_t amf0); +extern srs_bool srs_amf0_is_ecma_array(srs_amf0_t amf0); +extern srs_bool srs_amf0_is_strict_array(srs_amf0_t amf0); /* value converter */ extern const char* srs_amf0_to_string(srs_amf0_t amf0); -extern srs_amf0_bool srs_amf0_to_boolean(srs_amf0_t amf0); +extern srs_bool srs_amf0_to_boolean(srs_amf0_t amf0); extern srs_amf0_number srs_amf0_to_number(srs_amf0_t amf0); /* value setter */ extern void srs_amf0_set_number(srs_amf0_t amf0, srs_amf0_number value); diff --git a/trunk/src/rtmp/srs_protocol_utility.cpp b/trunk/src/rtmp/srs_protocol_utility.cpp index a4ee30e565..b79e458291 100644 --- a/trunk/src/rtmp/srs_protocol_utility.cpp +++ b/trunk/src/rtmp/srs_protocol_utility.cpp @@ -167,12 +167,12 @@ bool srs_avc_startswith_annexb(SrsStream* stream, int* pnb_start_code) } // not match - if (p[0] != 0x00 || p[1] != 0x00) { + if (p[0] != (char)0x00 || p[1] != (char)0x00) { return false; } // match N[00] 00 00 01, where N>=0 - if (p[2] == 0x01) { + if (p[2] == (char)0x01) { if (pnb_start_code) { *pnb_start_code = (int)(p - bytes) + 3; } @@ -185,3 +185,21 @@ bool srs_avc_startswith_annexb(SrsStream* stream, int* pnb_start_code) return false; } +bool srs_aac_startswith_adts(SrsStream* stream) +{ + char* bytes = stream->data() + stream->pos(); + char* p = bytes; + + if (!stream->require(p - bytes + 2)) { + return false; + } + + // matched 12bits 0xFFF, + // @remark, we must cast the 0xff to char to compare. + if (p[0] != (char)0xff || (char)(p[1] & 0xf0) != (char)0xf0) { + return false; + } + + return true; +} + diff --git a/trunk/src/rtmp/srs_protocol_utility.hpp b/trunk/src/rtmp/srs_protocol_utility.hpp index b4293ab107..2ec909133f 100644 --- a/trunk/src/rtmp/srs_protocol_utility.hpp +++ b/trunk/src/rtmp/srs_protocol_utility.hpp @@ -96,5 +96,12 @@ extern bool srs_bytes_equals(void* pa, void* pb, int size); */ extern bool srs_avc_startswith_annexb(SrsStream* stream, int* pnb_start_code = NULL); +/** +* whether stream starts with the aac ADTS +* from aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 75, 1.A.2.2 ADTS. +* start code must be '1111 1111 1111'B, that is 0xFFF +*/ +extern bool srs_aac_startswith_adts(SrsStream* stream); + #endif diff --git a/trunk/src/srs/srs.upp b/trunk/src/srs/srs.upp index e45a4738cd..94f0fd3397 100755 --- a/trunk/src/srs/srs.upp +++ b/trunk/src/srs/srs.upp @@ -130,6 +130,7 @@ file ..\utest\srs_utest_reload.hpp, ..\utest\srs_utest_reload.cpp, research readonly separator, + ..\..\research\librtmp\srs_aac_raw_publish.c, ..\..\research\librtmp\srs_audio_raw_publish.c, ..\..\research\librtmp\srs_bandwidth_check.c, ..\..\research\librtmp\srs_detect_rtmp.c, From 5fde8906036e93e4b6d392495699517a038e289e Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 25 Nov 2014 09:50:57 +0800 Subject: [PATCH 213/800] update PRIMARY, AUTHORS, CONTRIBUTORS rule --- AUTHORS.txt | 7 +++++-- README.md | 24 +++++++++++------------- 2 files changed, 16 insertions(+), 15 deletions(-) mode change 100755 => 100644 AUTHORS.txt diff --git a/AUTHORS.txt b/AUTHORS.txt old mode 100755 new mode 100644 index b148da659d..23cd8718d1 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -1,7 +1,10 @@ -Authors ordered by first contribution. - +PRIMARY ordered by first contribution. * winlin + +AUTHORS ordered by first contribution. * wenjie.zhao<740936897@qq.com> + +CONTRIBUTORS ordered by first contribution. * xiangcheng.liu * naijia.liu * alcoholyi diff --git a/README.md b/README.md index 462b4c45cc..f524d1fae3 100755 --- a/README.md +++ b/README.md @@ -149,19 +149,17 @@ StreamServers:[BLS](https://github.com/wenjiegit/Bull-Live-Server)/[BLE](https [FMS/AMS](http://www.adobe.com/products/adobe-media-server-standard.html) ## AUTHORS -The PRIMARY AUTHORS are (and/or have been)(Authors ordered by first contribution): -* winlin([winterserver](#)): [http://blog.csdn.net/win_lin](http://blog.csdn.net/win_lin) -* wenjie([wenjiegit](https://github.com/wenjiegit/simple-rtmp-server)): [http://blog.chinaunix.net/uid/25006789.html](http://blog.chinaunix.net/uid/25006789.html) - -About the primary AUTHORS: -* Contribute important features to SRS. -* Names of all PRIMARY AUTHORS response in NetConnection.connect and metadata. -* Names of all CONTRIBUTORS response in api/v1/authors. - -And here is an inevitably incomplete list of MUCH-APPRECIATED CONTRIBUTORS -- -people who have submitted patches, reported bugs, added translations, helped -answer newbie questions, and generally made SRS that much better: -[AUTHORS.txt](https://github.com/winlinvip/simple-rtmp-server/blob/master/AUTHORS.txt) + +There are three types of people that have contributed to the SRS project: +* PRIMARY: Contribute important features and >10% code. Names of all +PRIMARY response in NetConnection.connect and metadata. +* AUTHORS: Contribute features and 1%~10% code. Names of all +PRIMARY response in NetConnection.connect and metadata. +* CONTRIBUTORS: Submit patches, report bugs, add translations, help answer +newbie questions, and generally make SRS that much better. + +About all PRIMARY, AUTHORS and CONTRIBUTORS, read +[AUTHORS.txt](https://github.com/winlinvip/simple-rtmp-server/blob/master/AUTHORS.txt). A big THANK YOU goes to: * [chnvideo](chnvideo.com) co-founders([wiseyoung](mailto:wiseyoung@chnvideo.com), [trueice](mailto:trueice@chnvideo.com), [leijian](mailto:leijian@chnvideo.com)) for [big supports](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Product#bigthanks). From 3c46bcbddc3e922f06b2cbb745d1b4292ebf0727 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 25 Nov 2014 09:58:11 +0800 Subject: [PATCH 214/800] update PRIMARY, AUTHORS, CONTRIBUTORS rule --- AUTHORS.txt | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/AUTHORS.txt b/AUTHORS.txt index 23cd8718d1..ac4806f57c 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -1,17 +1,17 @@ PRIMARY ordered by first contribution. -* winlin +* winlin "Plan, arch, implement SRS1.0 and SRS2.0" AUTHORS ordered by first contribution. -* wenjie.zhao<740936897@qq.com> +* wenjie.zhao<740936897@qq.com> "The bandwidth test module" CONTRIBUTORS ordered by first contribution. -* xiangcheng.liu -* naijia.liu -* alcoholyi -* byteman -* chad.wang -* suhetao -* Johnny -* karthikeyan -* StevenLiu -* zhengfl +* xiangcheng.liu "Bug fixed" +* naijia.liu "Performance benchmark" +* alcoholyi "Bug fixed" +* byteman "Bug fixed" +* chad.wang "Bug fixed" +* suhetao "Bug fixed" +* Johnny "Create domain ossrs.net" +* karthikeyan "Bug fixed" +* StevenLiu "Build SRS on Darwin OSX" +* zhengfl "Bug fixed" From e7ccbce47239a82ccbc9f48bbf1c0cf20a8dc17c Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 25 Nov 2014 10:41:55 +0800 Subject: [PATCH 215/800] update PRIMARY, AUTHORS, CONTRIBUTORS rule. 2.0.32. --- AUTHORS.txt | 5 ++++ README.md | 1 + trunk/research/librtmp/srs_bandwidth_check.c | 18 ++++++++----- .../players/srs_bwt/release/srs_bwt.swf | Bin 6474 -> 6547 bytes .../players/srs_bwt/src/SrsBandwidth.as | 21 +++++++++------ .../players/srs_bwt/src/srs.bandwidth.js | 10 ++++---- trunk/research/players/srs_bwt/src/srs_bwt.as | 13 ++++++---- .../players/srs_player/release/srs_player.swf | Bin 5573 -> 5635 bytes .../players/srs_player/src/srs_player.as | 24 +++++++++++++----- .../srs_publisher/release/srs_publisher.swf | Bin 5585 -> 5642 bytes .../srs_publisher/src/srs_publisher.as | 17 +++++++++---- trunk/src/app/srs_app_config.cpp | 3 ++- trunk/src/app/srs_app_edge.cpp | 6 +++-- trunk/src/app/srs_app_forward.cpp | 3 ++- trunk/src/app/srs_app_http_api.cpp | 3 ++- trunk/src/app/srs_app_source.cpp | 3 ++- trunk/src/core/srs_core.hpp | 5 ++-- trunk/src/libs/srs_librtmp.cpp | 13 ++++++---- trunk/src/libs/srs_librtmp.hpp | 6 +++-- trunk/src/main/srs_main_server.cpp | 3 ++- trunk/src/rtmp/srs_protocol_rtmp.cpp | 20 ++++++++++----- trunk/src/rtmp/srs_protocol_rtmp.hpp | 8 +++--- 22 files changed, 119 insertions(+), 63 deletions(-) diff --git a/AUTHORS.txt b/AUTHORS.txt index ac4806f57c..07f3b07d01 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -1,3 +1,8 @@ +There are three types of people that have contributed to the SRS project: +1. PRIMARY: Contribute important features and >10% code. Names of all PRIMARY response in NetConnection.connect and metadata. +2. AUTHORS: Contribute features and 1%~10% code. Names of all PRIMARY response in NetConnection.connect and metadata. +3. CONTRIBUTORS: Submit patches, report bugs, add translations, help answer newbie questions, and generally make SRS that much better. + PRIMARY ordered by first contribution. * winlin "Plan, arch, implement SRS1.0 and SRS2.0" diff --git a/README.md b/README.md index f524d1fae3..c674ad2b88 100755 --- a/README.md +++ b/README.md @@ -485,6 +485,7 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History +* v2.0, 2014-11-25, update PRIMARY, AUTHORS, CONTRIBUTORS rule. 2.0.32. * v2.0, 2014-11-24, fix [#212](https://github.com/winlinvip/simple-rtmp-server/issues/212), support publish aac adts raw stream. 2.0.31. * v2.0, 2014-11-22, fix [#217](https://github.com/winlinvip/simple-rtmp-server/issues/217), remove timeout recv, support 7.5k+ 250kbps clients. 2.0.30. * v2.0, 2014-11-21, srs-librtmp add rtmp prefix for rtmp/utils/human apis. 2.0.29. diff --git a/trunk/research/librtmp/srs_bandwidth_check.c b/trunk/research/librtmp/srs_bandwidth_check.c index 46e8dc2069..3a3fac75b0 100644 --- a/trunk/research/librtmp/srs_bandwidth_check.c +++ b/trunk/research/librtmp/srs_bandwidth_check.c @@ -43,7 +43,8 @@ int main(int argc, char** argv) // srs debug info. char srs_server_ip[128]; char srs_server[128]; - char srs_primary_authors[128]; + char srs_primary[128]; + char srs_authors[128]; char srs_version[32]; int srs_id = 0; int srs_pid = 0; @@ -60,7 +61,8 @@ int main(int argc, char** argv) // set to zero. srs_server_ip[0] = 0; srs_server[0] = 0; - srs_primary_authors[0] = 0; + srs_primary[0] = 0; + srs_authors[0] = 0; srs_version[0] = 0; printf("RTMP bandwidth check/test with server.\n"); @@ -90,7 +92,8 @@ int main(int argc, char** argv) srs_human_trace("simple handshake success"); if ((ret = srs_rtmp_connect_app2(rtmp, - srs_server_ip, srs_server, srs_primary_authors, srs_version, &srs_id, &srs_pid)) != 0) { + srs_server_ip, srs_server, srs_primary, srs_authors, srs_version, + &srs_id, &srs_pid)) != 0) { srs_human_trace("connect vhost/app failed."); goto rtmp_destroy; } @@ -105,12 +108,12 @@ int main(int argc, char** argv) } srs_human_trace("bandwidth check/test success"); - srs_human_trace("\n%s, %s\n" + srs_human_trace("\n%s, %s, %s\n" "%s, %s, srs_pid=%d, srs_id=%d\n" "duration: %dms(%d+%d)\n" "play: %dkbps\n" "publish: %dkbps", - (char*)srs_server, (char*)srs_primary_authors, + (char*)srs_server, (char*)srs_primary, (char*)srs_authors, (char*)srs_server_ip, (char*)srs_version, srs_pid, srs_id, (int)(end_time - start_time), play_duration, publish_duration, play_kbps, @@ -121,7 +124,8 @@ int main(int argc, char** argv) fprintf(stderr, "{\"code\":%d," "\"srs_server\":\"%s\", " - "\"srs_primary_authors\":\"%s\", " + "\"srs_primary\":\"%s\", " + "\"srs_authors\":\"%s\", " "\"srs_server_ip\":\"%s\", " "\"srs_version\":\"%s\", " "\"srs_pid\":%d, " @@ -132,7 +136,7 @@ int main(int argc, char** argv) "\"publish_kbps\":%d" "}", ret, - (char*)srs_server, (char*)srs_primary_authors, + (char*)srs_server, (char*)srs_primary, (char*)srs_authors, (char*)srs_server_ip, (char*)srs_version, srs_pid, srs_id, (int)(end_time - start_time), play_duration, publish_duration, play_kbps, publish_kbps); diff --git a/trunk/research/players/srs_bwt/release/srs_bwt.swf b/trunk/research/players/srs_bwt/release/srs_bwt.swf index 79d45e5855731230b642b36ee60b2ef7e16f9d7a..024d7771851252d961fb244dd6b49523ada56be4 100755 GIT binary patch literal 6547 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 FzeI;$3`_t3 literal 6474 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 delta 5543 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 diff --git a/trunk/research/players/srs_player/src/srs_player.as b/trunk/research/players/srs_player/src/srs_player.as index a8fa5ec2ce..e69f2c97ef 100755 --- a/trunk/research/players/srs_player/src/srs_player.as +++ b/trunk/research/players/srs_player/src/srs_player.as @@ -152,8 +152,11 @@ package if (srs_server != null) { obj.server = srs_server; } - if (srs_primary_authors != null) { - obj.contributor = srs_primary_authors; + if (srs_primary != null) { + obj.contributor = srs_primary; + } + if (srs_authors != null) { + obj.contributor = srs_authors; } var code:int = flash.external.ExternalInterface.call(js_on_player_metadata, js_id, obj); @@ -267,7 +270,8 @@ package // srs infos private var srs_server:String = null; - private var srs_primary_authors:String = null; + private var srs_primary:String = null; + private var srs_authors:String = null; private var srs_id:String = null; private var srs_pid:String = null; private var srs_server_ip:String = null; @@ -277,8 +281,11 @@ package if (srs_server != null) { customItems.push(new ContextMenuItem("Server: " + srs_server)); } - if (srs_primary_authors != null) { - customItems.push(new ContextMenuItem("PrimaryAuthors: " + srs_primary_authors)); + if (srs_primary != null) { + customItems.push(new ContextMenuItem("PrimaryAuthors: " + srs_primary)); + } + if (srs_authors != null) { + customItems.push(new ContextMenuItem("Authors: " + srs_authors)); } if (srs_server_ip != null) { customItems.push(new ContextMenuItem("SrsIp: " + srs_server_ip)); @@ -318,8 +325,11 @@ package if (evt.info.data.hasOwnProperty("srs_server")) { srs_server = evt.info.data.srs_server; } - if (evt.info.data.hasOwnProperty("srs_primary_authors")) { - srs_primary_authors = evt.info.data.srs_primary_authors; + if (evt.info.data.hasOwnProperty("srs_primary")) { + srs_primary = evt.info.data.srs_primary; + } + if (evt.info.data.hasOwnProperty("srs_authors")) { + srs_authors = evt.info.data.srs_authors; } if (evt.info.data.hasOwnProperty("srs_id")) { srs_id = evt.info.data.srs_id; diff --git a/trunk/research/players/srs_publisher/release/srs_publisher.swf b/trunk/research/players/srs_publisher/release/srs_publisher.swf index 1da99869f9aa06705fc6a418e098bc4a0c5859b4..785b53ea5c4ecc0302da1d2f93e34a7e9c940467 100755 GIT binary patch literal 5642 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 literal 5585 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 diff --git a/trunk/research/players/srs_publisher/src/srs_publisher.as b/trunk/research/players/srs_publisher/src/srs_publisher.as index 03c7d3918d..2669e708bc 100755 --- a/trunk/research/players/srs_publisher/src/srs_publisher.as +++ b/trunk/research/players/srs_publisher/src/srs_publisher.as @@ -136,7 +136,8 @@ package // srs infos private var srs_server:String = null; - private var srs_primary_authors:String = null; + private var srs_primary:String = null; + private var srs_authors:String = null; private var srs_id:String = null; private var srs_pid:String = null; private var srs_server_ip:String = null; @@ -146,8 +147,11 @@ package if (srs_server != null) { customItems.push(new ContextMenuItem("Server: " + srs_server)); } - if (srs_primary_authors != null) { - customItems.push(new ContextMenuItem("PrimaryAuthors: " + srs_primary_authors)); + if (srs_primary != null) { + customItems.push(new ContextMenuItem("PrimaryAuthors: " + srs_primary)); + } + if (srs_authors != null) { + customItems.push(new ContextMenuItem("Authors: " + srs_authors)); } if (srs_server_ip != null) { customItems.push(new ContextMenuItem("SrsIp: " + srs_server_ip)); @@ -217,8 +221,11 @@ package if (evt.info.data.hasOwnProperty("srs_server")) { srs_server = evt.info.data.srs_server; } - if (evt.info.data.hasOwnProperty("srs_primary_authors")) { - srs_primary_authors = evt.info.data.srs_primary_authors; + if (evt.info.data.hasOwnProperty("srs_primary")) { + srs_primary = evt.info.data.srs_primary; + } + if (evt.info.data.hasOwnProperty("srs_authors")) { + srs_authors = evt.info.data.srs_authors; } if (evt.info.data.hasOwnProperty("srs_id")) { srs_id = evt.info.data.srs_id; diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index 0513cfada4..58f0a3a86e 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -1167,7 +1167,8 @@ void SrsConfig::print_help(char** argv) printf( RTMP_SIG_SRS_NAME" "RTMP_SIG_SRS_VERSION" "RTMP_SIG_SRS_COPYRIGHT"\n" "License: "RTMP_SIG_SRS_LICENSE"\n" - "Primary Authors: "RTMP_SIG_SRS_PRIMARY_AUTHROS"\n" + "Primary: "RTMP_SIG_SRS_PRIMARY"\n" + "Authors: "RTMP_SIG_SRS_AUTHROS"\n" "Build: "SRS_AUTO_BUILD_DATE" Configuration:"SRS_AUTO_USER_CONFIGURE"\n" "Features:"SRS_AUTO_CONFIGURE"\n""\n" "Usage: %s [-h?vV] [[-t] -c ]\n" diff --git a/trunk/src/app/srs_app_edge.cpp b/trunk/src/app/srs_app_edge.cpp index 426bbbe4e0..78dd39cd82 100644 --- a/trunk/src/app/srs_app_edge.cpp +++ b/trunk/src/app/srs_app_edge.cpp @@ -227,7 +227,8 @@ int SrsEdgeIngester::connect_app(string ep_server, string ep_port) data->set("srs_site", SrsAmf0Any::str(RTMP_SIG_SRS_WEB)); data->set("srs_email", SrsAmf0Any::str(RTMP_SIG_SRS_EMAIL)); data->set("srs_copyright", SrsAmf0Any::str(RTMP_SIG_SRS_COPYRIGHT)); - data->set("srs_primary_authors", SrsAmf0Any::str(RTMP_SIG_SRS_PRIMARY_AUTHROS)); + data->set("srs_primary", SrsAmf0Any::str(RTMP_SIG_SRS_PRIMARY)); + data->set("srs_authors", SrsAmf0Any::str(RTMP_SIG_SRS_AUTHROS)); // for edge to directly get the id of client. data->set("srs_pid", SrsAmf0Any::number(getpid())); data->set("srs_id", SrsAmf0Any::number(_srs_context->get_id())); @@ -647,7 +648,8 @@ int SrsEdgeForwarder::connect_app(string ep_server, string ep_port) data->set("srs_site", SrsAmf0Any::str(RTMP_SIG_SRS_WEB)); data->set("srs_email", SrsAmf0Any::str(RTMP_SIG_SRS_EMAIL)); data->set("srs_copyright", SrsAmf0Any::str(RTMP_SIG_SRS_COPYRIGHT)); - data->set("srs_primary_authors", SrsAmf0Any::str(RTMP_SIG_SRS_PRIMARY_AUTHROS)); + data->set("srs_primary", SrsAmf0Any::str(RTMP_SIG_SRS_PRIMARY)); + data->set("srs_authors", SrsAmf0Any::str(RTMP_SIG_SRS_AUTHROS)); // for edge to directly get the id of client. data->set("srs_pid", SrsAmf0Any::number(getpid())); data->set("srs_id", SrsAmf0Any::number(_srs_context->get_id())); diff --git a/trunk/src/app/srs_app_forward.cpp b/trunk/src/app/srs_app_forward.cpp index 6afd57a646..1c1dbb2d5a 100644 --- a/trunk/src/app/srs_app_forward.cpp +++ b/trunk/src/app/srs_app_forward.cpp @@ -344,7 +344,8 @@ int SrsForwarder::connect_app(string ep_server, string ep_port) data->set("srs_site", SrsAmf0Any::str(RTMP_SIG_SRS_WEB)); data->set("srs_email", SrsAmf0Any::str(RTMP_SIG_SRS_EMAIL)); data->set("srs_copyright", SrsAmf0Any::str(RTMP_SIG_SRS_COPYRIGHT)); - data->set("srs_primary_authors", SrsAmf0Any::str(RTMP_SIG_SRS_PRIMARY_AUTHROS)); + data->set("srs_primary", SrsAmf0Any::str(RTMP_SIG_SRS_PRIMARY)); + data->set("srs_authors", SrsAmf0Any::str(RTMP_SIG_SRS_AUTHROS)); // for edge to directly get the id of client. data->set("srs_pid", SrsAmf0Any::number(getpid())); data->set("srs_id", SrsAmf0Any::number(_srs_context->get_id())); diff --git a/trunk/src/app/srs_app_http_api.cpp b/trunk/src/app/srs_app_http_api.cpp index bbbcfab53b..cd0ea5d17b 100644 --- a/trunk/src/app/srs_app_http_api.cpp +++ b/trunk/src/app/srs_app_http_api.cpp @@ -490,7 +490,8 @@ int SrsApiAuthors::do_process_request(SrsStSocket* skt, SrsHttpMessage* req) ss << __SRS_JOBJECT_START << __SRS_JFIELD_ERROR(ERROR_SUCCESS) << __SRS_JFIELD_CONT << __SRS_JFIELD_ORG("data", __SRS_JOBJECT_START) - << __SRS_JFIELD_STR("primary_authors", RTMP_SIG_SRS_PRIMARY_AUTHROS) << __SRS_JFIELD_CONT + << __SRS_JFIELD_STR("primary", RTMP_SIG_SRS_PRIMARY) << __SRS_JFIELD_CONT + << __SRS_JFIELD_STR("authors", RTMP_SIG_SRS_AUTHROS) << __SRS_JFIELD_CONT << __SRS_JFIELD_STR("contributors_link", RTMP_SIG_SRS_CONTRIBUTORS_URL) << __SRS_JFIELD_CONT << __SRS_JFIELD_STR("contributors", SRS_AUTO_CONSTRIBUTORS) << __SRS_JOBJECT_END diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index 4f578f0756..e5405ae1b4 100644 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -957,7 +957,8 @@ int SrsSource::on_meta_data(SrsMessage* msg, SrsOnMetaDataPacket* metadata) // add server info to metadata metadata->metadata->set("server", SrsAmf0Any::str(RTMP_SIG_SRS_KEY" "RTMP_SIG_SRS_VERSION" ("RTMP_SIG_SRS_URL_SHORT")")); - metadata->metadata->set("authors", SrsAmf0Any::str(RTMP_SIG_SRS_PRIMARY_AUTHROS)); + metadata->metadata->set("srs_primary", SrsAmf0Any::str(RTMP_SIG_SRS_PRIMARY)); + metadata->metadata->set("srs_authors", SrsAmf0Any::str(RTMP_SIG_SRS_AUTHROS)); // version, for example, 1.0.0 // add version to metadata, please donot remove it, for debug. diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index b8b9474374..3e2db170dd 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 31 +#define VERSION_REVISION 32 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" @@ -42,7 +42,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define RTMP_SIG_SRS_EMAIL "winlin@vip.126.com" #define RTMP_SIG_SRS_LICENSE "The MIT License (MIT)" #define RTMP_SIG_SRS_COPYRIGHT "Copyright (c) 2013-2014 winlin" -#define RTMP_SIG_SRS_PRIMARY_AUTHROS "winlin,wenjie.zhao" +#define RTMP_SIG_SRS_PRIMARY "winlin" +#define RTMP_SIG_SRS_AUTHROS "wenjie.zhao" #define RTMP_SIG_SRS_CONTRIBUTORS_URL RTMP_SIG_SRS_URL"/blob/master/AUTHORS.txt" #define RTMP_SIG_SRS_HANDSHAKE RTMP_SIG_SRS_KEY"("RTMP_SIG_SRS_VERSION")" #define RTMP_SIG_SRS_RELEASE "https://github.com/winlinvip/simple-rtmp-server/tree/1.0release" diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index b6f4155679..d1ad3513b6 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -655,12 +655,14 @@ int srs_rtmp_connect_app(srs_rtmp_t rtmp) } int srs_rtmp_connect_app2(srs_rtmp_t rtmp, - char srs_server_ip[128],char srs_server[128], char srs_primary_authors[128], + char srs_server_ip[128],char srs_server[128], + char srs_primary[128], char srs_authors[128], char srs_version[32], int* srs_id, int* srs_pid ) { srs_server_ip[0] = 0; srs_server[0] = 0; - srs_primary_authors[0] = 0; + srs_primary[0] = 0; + srs_authors[0] = 0; srs_version[0] = 0; *srs_id = 0; *srs_pid = 0; @@ -675,16 +677,17 @@ int srs_rtmp_connect_app2(srs_rtmp_t rtmp, context->param ); - std::string sip, sserver, sauthors, sversion; + std::string sip, sserver, sprimary, sauthors, sversion; if ((ret = context->rtmp->connect_app2(context->app, tcUrl, NULL, true, - sip, sserver, sauthors, sversion, *srs_id, *srs_pid)) != ERROR_SUCCESS) { + sip, sserver, sprimary, sauthors, sversion, *srs_id, *srs_pid)) != ERROR_SUCCESS) { return ret; } snprintf(srs_server_ip, 128, "%s", sip.c_str()); snprintf(srs_server, 128, "%s", sserver.c_str()); - snprintf(srs_primary_authors, 128, "%s", sauthors.c_str()); + snprintf(srs_primary, 128, "%s", sprimary.c_str()); + snprintf(srs_authors, 128, "%s", sauthors.c_str()); snprintf(srs_version, 32, "%s", sversion.c_str()); return ret; diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index b941219dde..58d2c4c8a8 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -176,7 +176,8 @@ extern int srs_rtmp_connect_app(srs_rtmp_t rtmp); * SRS debug info: * @param srs_server_ip, 128bytes, debug info, server ip client connected at. * @param srs_server, 128bytes, server info. -* @param srs_primary_authors, 128bytes, primary authors. +* @param srs_primary, 128bytes, primary authors. +* @param srs_authors, 128bytes, authors. * @param srs_version, 32bytes, server version. * @param srs_id, int, debug info, client id in server log. * @param srs_pid, int, debug info, server pid in log. @@ -184,7 +185,8 @@ extern int srs_rtmp_connect_app(srs_rtmp_t rtmp); * @return 0, success; otherswise, failed. */ extern int srs_rtmp_connect_app2(srs_rtmp_t rtmp, - char srs_server_ip[128], char srs_server[128], char srs_primary_authors[128], + char srs_server_ip[128], char srs_server[128], + char srs_primary[128], char srs_authors[128], char srs_version[32], int* srs_id, int* srs_pid ); diff --git a/trunk/src/main/srs_main_server.cpp b/trunk/src/main/srs_main_server.cpp index df489cffbd..8af0c6b245 100644 --- a/trunk/src/main/srs_main_server.cpp +++ b/trunk/src/main/srs_main_server.cpp @@ -188,7 +188,8 @@ int main(int argc, char** argv) srs_trace("srs(simple-rtmp-server) "RTMP_SIG_SRS_VERSION); srs_trace("license: "RTMP_SIG_SRS_LICENSE); - srs_trace("authors: "RTMP_SIG_SRS_PRIMARY_AUTHROS); + srs_trace("primary: "RTMP_SIG_SRS_PRIMARY); + srs_trace("authors: "RTMP_SIG_SRS_AUTHROS); srs_trace("contributors: "SRS_AUTO_CONSTRIBUTORS); srs_trace("uname: "SRS_AUTO_UNAME); srs_trace("build: %s, %s", SRS_AUTO_BUILD_DATE, srs_is_little_endian()? "little-endian":"big-endian"); diff --git a/trunk/src/rtmp/srs_protocol_rtmp.cpp b/trunk/src/rtmp/srs_protocol_rtmp.cpp index a4ffd30778..39ccb21319 100644 --- a/trunk/src/rtmp/srs_protocol_rtmp.cpp +++ b/trunk/src/rtmp/srs_protocol_rtmp.cpp @@ -444,20 +444,22 @@ int SrsRtmpClient::connect_app(string app, string tc_url, { std::string srs_server_ip; std::string srs_server; - std::string srs_primary_authors; + std::string srs_primary; + std::string srs_authors; std::string srs_version; int srs_id = 0; int srs_pid = 0; return connect_app2(app, tc_url, req, debug_srs_upnode, - srs_server_ip, srs_server, srs_primary_authors, + srs_server_ip, srs_server, srs_primary, srs_authors, srs_version, srs_id, srs_pid); } int SrsRtmpClient::connect_app2( string app, string tc_url, SrsRequest* req, bool debug_srs_upnode, - string& srs_server_ip, string& srs_server, string& srs_primary_authors, - string& srs_version, int& srs_id, int& srs_pid + string& srs_server_ip, string& srs_server, string& srs_primary, + string& srs_authors, string& srs_version, int& srs_id, + int& srs_pid ){ int ret = ERROR_SUCCESS; @@ -522,8 +524,11 @@ int SrsRtmpClient::connect_app2( SrsAmf0EcmaArray* arr = data->to_ecma_array(); SrsAmf0Any* prop = NULL; - if ((prop = arr->ensure_property_string("srs_primary_authors")) != NULL) { - srs_primary_authors = prop->to_str(); + if ((prop = arr->ensure_property_string("srs_primary")) != NULL) { + srs_primary = prop->to_str(); + } + if ((prop = arr->ensure_property_string("srs_authors")) != NULL) { + srs_authors = prop->to_str(); } if ((prop = arr->ensure_property_string("srs_version")) != NULL) { srs_version = prop->to_str(); @@ -927,7 +932,8 @@ int SrsRtmpServer::response_connect_app(SrsRequest *req, const char* server_ip) data->set("srs_site", SrsAmf0Any::str(RTMP_SIG_SRS_WEB)); data->set("srs_email", SrsAmf0Any::str(RTMP_SIG_SRS_EMAIL)); data->set("srs_copyright", SrsAmf0Any::str(RTMP_SIG_SRS_COPYRIGHT)); - data->set("srs_primary_authors", SrsAmf0Any::str(RTMP_SIG_SRS_PRIMARY_AUTHROS)); + data->set("srs_primary", SrsAmf0Any::str(RTMP_SIG_SRS_PRIMARY)); + data->set("srs_authors", SrsAmf0Any::str(RTMP_SIG_SRS_AUTHROS)); if (server_ip) { data->set("srs_server_ip", SrsAmf0Any::str(server_ip)); diff --git a/trunk/src/rtmp/srs_protocol_rtmp.hpp b/trunk/src/rtmp/srs_protocol_rtmp.hpp index 77df8c8313..9846d76191 100644 --- a/trunk/src/rtmp/srs_protocol_rtmp.hpp +++ b/trunk/src/rtmp/srs_protocol_rtmp.hpp @@ -268,14 +268,16 @@ class SrsRtmpClient * SRS debug info: * @param srs_server_ip, debug info, server ip client connected at. * @param srs_server, server info. - * @param srs_primary_authors, primary authors. + * @param srs_primary, primary authors. + * @param srs_authors, authors. * @param srs_id, int, debug info, client id in server log. * @param srs_pid, int, debug info, server pid in log. */ virtual int connect_app2( std::string app, std::string tc_url, SrsRequest* req, bool debug_srs_upnode, - std::string& srs_server_ip, std::string& srs_server, std::string& srs_primary_authors, - std::string& srs_version, int& srs_id, int& srs_pid + std::string& srs_server_ip, std::string& srs_server, std::string& srs_primary, + std::string& srs_authors, std::string& srs_version, int& srs_id, + int& srs_pid ); /** * create a stream, then play/publish data over this stream. From a2af051840b278c38b4326f56cebe1790d43c156 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 25 Nov 2014 10:59:38 +0800 Subject: [PATCH 216/800] udpate the srs_bwt. --- .../players/srs_bwt/release/srs_bwt.swf | Bin 6547 -> 6531 bytes trunk/research/players/srs_bwt/src/srs_bwt.as | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/trunk/research/players/srs_bwt/release/srs_bwt.swf b/trunk/research/players/srs_bwt/release/srs_bwt.swf index 024d7771851252d961fb244dd6b49523ada56be4..1fe102842afd256cc12f0af03c5e69d17b2a469a 100755 GIT binary patch literal 6531 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 FzeI;$3`_t3 diff --git a/trunk/research/players/srs_bwt/src/srs_bwt.as b/trunk/research/players/srs_bwt/src/srs_bwt.as index dc8eb95e75..c2d6aca264 100755 --- a/trunk/research/players/srs_bwt/src/srs_bwt.as +++ b/trunk/research/players/srs_bwt/src/srs_bwt.as @@ -101,7 +101,7 @@ package var as_on_progress_change:Function = function(percent:Number):void { self.on_progress(percent); }; - var as_on_srs_info:Function = function(srs_server:String, srs_primary, srs_authors:String, srs_id:String, srs_pid:String, srs_server_ip:String):void { + var as_on_srs_info:Function = function(srs_server:String, srs_primary:String, srs_authors:String, srs_id:String, srs_pid:String, srs_server_ip:String):void { self.update_context_items(srs_server, srs_primary, srs_authors, srs_id, srs_pid, srs_server_ip); }; var as_on_complete:Function = function(start_time:Number, end_time:Number, play_kbps:Number, publish_kbps:Number, play_bytes:Number, publish_bytes:Number, play_time:Number, publish_time:Number):void { From 02778dba4c744f52c6c83739cb21abd4230a1809 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 25 Nov 2014 11:05:45 +0800 Subject: [PATCH 217/800] udpate the srs_bwt. --- trunk/research/players/srs_bwt.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/trunk/research/players/srs_bwt.html b/trunk/research/players/srs_bwt.html index 78574addae..09f26aac31 100644 --- a/trunk/research/players/srs_bwt.html +++ b/trunk/research/players/srs_bwt.html @@ -63,9 +63,9 @@ bandwidth.on_update_status = function(status) { $("#check_status").text(status); } - bandwidth.on_srs_info = function(srs_server, srs_primary_authors, srs_id, srs_pid, srs_server_ip) { + bandwidth.on_srs_info = function(srs_server, srs_primary, srs_authors, srs_id, srs_pid, srs_server_ip) { $("#check_info").text( - "server:" + srs_server + ", authors:" + srs_primary_authors + + "server:" + srs_server + ", primary:" + srs_primary + ", authors:" + srs_authors + ", srs_id:" + srs_id + ", srs_pid:" + srs_pid + ", ip:" + srs_server_ip ); } From e11fa383ca534d7607117553266b72df5288afd8 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 25 Nov 2014 11:09:28 +0800 Subject: [PATCH 218/800] udpate the srs_bwt. --- .../players/srs_bwt/release/srs_bwt.swf | Bin 6531 -> 6528 bytes trunk/research/players/srs_bwt/src/srs_bwt.as | 2 +- .../players/srs_player/release/srs_player.swf | Bin 5635 -> 5638 bytes .../players/srs_player/src/srs_player.as | 2 +- .../srs_publisher/release/srs_publisher.swf | Bin 5642 -> 5641 bytes .../srs_publisher/src/srs_publisher.as | 2 +- 6 files changed, 3 insertions(+), 3 deletions(-) diff --git a/trunk/research/players/srs_bwt/release/srs_bwt.swf b/trunk/research/players/srs_bwt/release/srs_bwt.swf index 1fe102842afd256cc12f0af03c5e69d17b2a469a..6f5a4c5f9377631056275f5dcbff16f6551a0913 100755 GIT binary patch literal 6528 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 literal 6531 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 Qar}QdSpoL{049

CWpZo&W#< delta 5619 zcmVO(`xL{w+y8b_B{@pN?ih(2sAF_XF#U*1>%vN3kTP8yxT zSi0Zvg_6doZ>cX>7|+oH!oabDqn*>1sk6}y(ez;BP$nJAM&XkQQ+)9BA3wm+G$ z;zp)3o7$dAkEA%NAZ3_n#?bBbOvI#&>63bDAgd357@ghg`RFO0`>5MS=lb-pFVIr& z3pDwc3OAT^c_WMNv}org*uYMytRg#a{e~qQgT1|*H}rH#xpM6CjDKAfzf+b=tK{Ry zk6+?dpmL+B?uRcCDC-v!@2o*iyyCSoR%9rt?=Ujb^VOqgN)n{fG+~9*ASq*GA8HWa zD^G`ixAb&e2H<5G!!qYu4#1eXB(6IHGZtJeT#GsKW z=G)znwG&CJSO9D$clQou61Gt))UkxcrYMi5Q+6hujF?trP`9?bEF1B4bq^!KN(9ni zeXA_k)z#e<>Dv(L?F+8%uCaDlwlNqmA%r5xbSm-Lo zu!^Vtp-d^TMFqs%g74>b{7+KdH>T~IgE#sQrsGc+~Ovj9(_3&s_ za9y{@niQ3XvdLsG^2SJUo5i6`>(&9()7`y(Zq_m~%xms$!~_yY5Xd5UzABzb#=QN1 zS=&yhm~HQcX~4M+*Q#}ER$WjvIXa4SwmmYOh#BePRfwQ5YF}uivPJpOCyH5E^TLWa zVCQ8MNqbFd4Ps`gnRMFrCewP%$gD}3Y44Dp(Xod{#&U1y-)2N@IT0(1>(+*m)W%GD z$jI0`&d8s-!ifv^Ha7;EnwB;8Gt-HGl&d?FNoQscW-Z%i4H?mdnJ_G$9XEW5Sa~7z z$eP-w>KQrEArhl@rm#ySh;WFlQBPn$PoD`|gH>kqsGS%#1n`uZ`WZ$}6*`0GQwI+# z)9ZE=Rl&8*ioj9qjL7^5NnE=}?E$iP)@@CCc*`ky&S?2JHBmC-s4kEt*UtCEdSGjiMUNi9`mlDt_qsG9%F7uqrU-MpYR` zi5*F#l8F>Q{h5CR3Q0P{i@eYl^I0>AK|QmB_w}qDPiL$;78bT)zJE<^pNU*BVhuj# z!HSc|Zqj2ft7@YD)kGr3s1eQDMueBH{O+Cp5itrps$!uSEo@dV7+in1;3QK|tGX_p#l91OC-ad5 z+#-+9I0;T5lvH+5i5aO9zDy*11cCws&53ema;|(>l|;&RWelX5hN2xsR7<%z91AWj zpXBn|6RmZ+$^3D0_u836YM@wTaD=5NE-9AHedwNSZQ|Iu*JlU&@h%WYN?cmv%Hhff{K zTJegFBKw0P@jHB;sa`3YRGgDYVn~CTHA5Xfjk}VF6*04kSclKe1c?~egI&n=e7>pL zn;pVi$cW@`;0T^PgI2|Q!$uTQOFG0!QWLm%PoIC1HeAfE@`f(NGiBlVyef{DrZKJ9 zPoAwZK18>(7QZ!$^;9~wV-U}T+CVi_h(-z}0Tw3bVehi)6S;KS40uC+WJ7N@8Z|7- zGv%6iCpDxDyR0{zO~v{$ddgxC{`@%05d z1FnB>Q?G)*C{*^U&=RSbF}lH2*~LqZ#rR_ZFBL9wTiJfpUKfv!J2H}p*>M;DYLU2+ z7>L_$LCSU8)}!%))7Bu~IB{exs%9*sk3{;DC~y&0buoXq=)Onk`(kVkj zPO$B9+0jjz2{fhKfQV7Gui`0&dqW-kw#FNIpaO zJp6JAoN$^M#r&gH`$P|sj}-cRp;B3OQzNKqa%4n~#FdCOf)=My+HsRXjx=i&qHTm&rX;=OJmVoLHEN#W7ZK>*SZegExuuI!pRDa7-e3msg;ls48%^2I- z)~x#5ngeQ}soAdvTKs->X;b?$wIzQ5%Pno_Eo*CQS6c(E&1!4=(ss2i5b&#Qtu0Mz zdvjBZ+P=&mP}|%6Z7zS)(m)IQwg&KR2{f_qGA3W<_p@&c)3*8{5BOUkSr%w(cKKTa z&G@!0YlgPHt+mw^!1T+?Kk^PPo$+Lz^)C?`2G3$lw9M0rZ=0tb-ww~Y z_%7F6D>SuJqmOIqN=*)Gbe^VmX|uXDb+x9Ruh9jXx=y3@8g105N25NCF4ky^rhY=B zOEq=deHMT8Ike}|UVzOPL0^KrQuBNh?6=XrgZ5oa^$K}@z|8(9)ITK8 zGjQWM#^=z+(TDpxudfK*kSbGJe~bC6QlS zHC8G4FF|kd(3csx*d|_zfzL@Jw0PIcGZ8?vVT+0^)$AVb`kkE z@1wCjB>8W}FDU+p*hhvXQ)Z)0d1s{y;GWH!e?&XKN>9_Xw1=J`$v44(ud&ZhNOHq4 z`}a#4`zgInKchG3=kyC=^Zka#*dXR?{cnkWNB>2f{9ceh2=Yfk{#%fD1vw^&BglJ# z{7I1i5#-N;{6&9|_XYVtkiQD@H$na`$Ug-6P>_EL^1p%{7i3K44qhwBPC<4Fa-AT% z1-V|38wA-S$X-GA338(#pBCgML2ef07C~+mhVgnsCK2a zsicIY1%lE!g35~oRrZkR;OwlFdSr!M`|=(4MM9u$6tsV+S0Fuog7;p!Qo4~~ySQ_u zGz40*Wugu56rrGm? zl14RcL8*x5v*&Fu(EUYd7qP%5VR%Y#x0ozfbVO6k-t;l*j)f_^L{=;^wkzRLwY zBO>UT>7aj9MrXAIrAn#|2Bj)GdreTPrUmB*rP;LbP6|pjbk21kbEpnK2hK(0x=4~2 zEuJS46?v3g(`twn@_2jRQ=E=mlM~2olH%kv=fd*YkIQRiiYrcAF08IqrMSyEH@8M| zmgh|Ofy!!0a#rNRnyHyZInxvJ%5@b*kbOMo3weL2&I!V@tSBDudj!*~b4|ynQd)hC zs_0_p{AuHgVIJja}4&dr(gIHorjp1*d!q@UvS<;+t;r%H9s#X0jd^JC`eW4>3NEfe*N zLuY@`fl4p@{Y3c8`o^_qlHy#Jn=sGPYn?FY+4=%!E9gS~947)=r!R8!oT-Etn~Tl* zoVkS0(+^3g0oG&T#+=zyD}@^2p@EevET&&zE8mWwCc^N2OOg83wCIFj_*(V8`MtBGAfNo3no#a%LN6vz*1++c`VT+1{Ml!PybcZpxYG za(0xnn{(!J&R)UToVg%p)`nJKrYqsG>XJR)J~%u^Vz0)?xt4Nf=YdN6BJJ!XxP2LS zdl#*Cu4inRvo{d4=IjA>5e#C4`EmAH8Cs#%IeQ7VT`JD&XIM0D;l_387CJ`#v>$)p zsCx&%y3?IMw?;pP44zp(wo?ntLcADa^AwG z#5X#>q})XOBIj*l*?N9H$KPQ?i)9R5h@tQDp*u->7h`dMzDv$AHn`Vu*x)|rJq9mf z0a^g~2ON(3^#gMLDjZ`Q`#07d*DrSd!O*2L)_FP5V>0}|R?eAQhAUZ=cFL##TSA|} z0ooX4{#$@BzW#HBf;_y4XL4O0Gca7~V9synG3G#hA`~aFm*0`M zc1^;}M7+!SUIDdj3iYzQwR;M+E#B?CQb28=Lj6GA8k$0FkB6Ke7Elk+6zoUxR(%SV zjO)&L0d`*jHX(1ldYF@f_h~k~jTXMM5>nDm&yK^(e*>M9mP^d3B8 zIF9`~g|4JpIv9Vtim=nppEw*Vk{R6ji@bG9`0BM+OYtqv`*L`Txh4E5^HcgY&If`S zBXf-OYjOSskF|t$5(fT_^9%G{K~R-; zGr^d$T9U6PiA?DRymK~5l`hR&@&wE6!#LfW7e(F3&~JZ{>#%GPGSqDlGSr|6MxG^^kv*g^@&u`eW#{X1&Y&ded&qf`cV&GqIZyGf zqVFT;8!YQtk={cG>tCjWIdd;M-)EhDh#h+cJR|2qt=RYHBPSVuwRbZ=jDS-JxHzA}Ii@@&&ggH?wv2Q)! zywFuLBH&!lpfKR(XlD;X=pL15lk_P`?vchwQhFp?Qa4K($+cO!8GqSqlB!tIJjndH z6>msf<0FT@;;%wmGoQ5d5GJ&4W5^=fd`pxi+LL#c!=w6pVU1fbhqIomTM4SCAv#`N4ACeD2NMe^mmiiPs~d2=F^KT>i>CYUKWd#E<{2%A0MWwYJ^hRq$i zliYPq4!bGgw-Jx`K^BCgJP21yud(IRPw;>3mtR+$*D%3F)=}ptpc~=z>-lBB=DeYd z=iUFga)d4OsPiU7$N7rh0(>76y*(9-cYwkoUv+-NgTdnSTcGwb(eIVq1U7Fk9NdQ{ zsBrTS%*`6b&6Ct~DhBs1~`DUAG_R7QS5E=Hau_dkE} zw7_Ir_;%tyNz`GBo?u(c`dCMPWB-bc{ikwd;?VdbR#bETSLxUK)zZ0e^*9@S1XXHG zMWId zb2Hhs#D#Z}bCa6uclEnP6}wr*;^%+3@Rqs~Yu~T31NAgX`&G6m+OIly!o=N3@>&Yr z!>ZhYNnRSC2;EC~W#7dmq5BARM=c@HtQN{8_-ZF z*lHwFF)h79OD3a{phnHr4J|aoiQsjE+JL^$h-zkddEEq%j!8$-QGJCclnCgmH>wY* zO{!;NJVSE`1&1aaU2)zrH9ERBn26OSQ;AR}h&~x;iVZ%02sM$+1fmf$tfy9F;@8C! zgK>tMkWx%ArEBTLWJF#@^-(R}pV9jD6`fsd^f=Fa)YAHjuEc=a*z8st>l>Q58&tZy zjz;&qXy+ujfh&ZflFan3jSIUyJw01DZt4)S#kk9p{&kf6MqDneA|HJ4!FH#JDmymG zeq=jG{dCW}Z?46h_@UEGnf~z=312O*y;KlTAxxH+2(^MNgmj}nEC?q_;biTyO|3gn zvLmINx|RxtcO;|Qu#SRBFyw;EmQ51XYNjh=>M8#~FcH#&ysxpo%}is|Y@Gv`HU%sj zYIWK-i##12ogMzo8~r_-J*zt_&0#aG$NY)7Ukio&>4cvREJ22iT^~VpTu;rMG;!S| zPSgj|3Y%jcKRLgo?tNxp6Swok}G2RC;($F6Srm-P5zE zuCcy;ab19#j>K)9sZ=6W5zClq)lBNah!N3EH67N~NT_sDd~lnp>dPmsy0s^nilp_b z$-)jE67CaSrWV1_UosiA7FVCrg6YVB&Ve&0>n9nR#dUn^TiK3Ky_OE*v9#?p{pc%h z{dflZ@i>NtD^T%a>13eJR(~OoNvBL2JE*1NCNQJ?K{k=o)YF?IF&%4hZq7_P5;aRF4hJrPg~mN;XR!!I2C&?; zP5{q8Mbbbb5|a58rYjZ+VseEOaowEKIfR=S*P?7Q4K1h_)p#PUav#)mYFLfpys28npWnPVyrwApI1<* zKW=kwKu=-1QPQeP$&49xu?bRf6Okx;8@rvl6tsz-JK)8{YH@jS~UL%8k!T($FK35l5{U3wZr zp=+^L9!M#IMXrcRwRFa0r{1N-6Y=3#B4buHmZJ*S9LK_k?}q#!G^~|h%>JxN@Y>wS z+EvknsfP-3na_4*D*ICJa<=_J2Aai}uX2vn$O+6xnD=7->!A-@G?;u^ZoHaKs5#?Y zG5g}q_GE%V-82hWDZ#TqqouG2u^vBsaXszi0BYSHj7IRnbar)ardQ?W-X5$RwDyPm z19-tCaG7}L+WGOY^BZ^^p=Cmm9KtzqV(k2GF5#0*$X!}Gtp&p#Iw*_-El>AB0sjBMjepYT}KQFZ9c+IEMG<-P( z?J91SuS1pA^=#}?&>kMJh%t-`?z-w=%N`2T%Ct&yGfEIOe^;Y_VDB%4y>;X~8nUxW|Hq&~J%SE^# zjF$4x0C6C>cmXD}x}|%uTMZ@X?Mm4-wFnwN6#2;4$~h0E7{19|YY>AkMe4G&!Lzo@ zvC`AixqfX|r;Ryp@5;midP+2srr4iIN@nB+9foZ9-i@T?0C%lnVv|`pQNwiNYP#5( zi*L9i=K{Sb*)(m_-0F1~1g0A2t7)rTrZS_|JQ_7BJNs>TjQQzrv%6JvuFdIIbMDaY z^*j;VrqbnYp*60D70P_bit&|{TlIRnM`dh|$(S<4T)0^>%IIUBzOr&#@h{SY8Ri9+ z1P5KbUNWJJ|F&2a#yV_cJ}PDmpZ8%r9uE5Ap$ZZV!|+tUKa|o2{edVZq@TWm`cpKg z3(*UPVny^L9r@53(b1pMlHo|uR04W`B<_twqYgU3CcH*vJszThg&&pm7xe@^>`&02 zT=>H<5|7Z!7^`zMDJB!9*wVO2Y-wqh8tR)DNe%cA)^8Y*m1 zNh%$crIphim)hn{CDULA%(!gE{2A3VYGy2`TvEBL(o?ynayO6yAa>cII9&yWMa55o z43Z2s8D*j@6GaB6Ok6T4k)0L^_9;VT6r>as;q{ z6#K_OIe~q64BHdfK8Mpj1HlK;e{6rp_5rpLA`_6M$B8UHL1gJk zY@fyUIWk2M2>#utK!93l)kvXGd$mAnTUvc1+qP4xAr)#28k;Uy!5^t-FjR@>Ur zQc>}F*tH+-hr_TJ_JL6Q1rWsA%}Xl210&DEb0|Wts^a&QE8YuGTlIf1QjBI>ZX_dg z6rKCBT?B3-y9pzEIJudVTR6FuliN7i%gODWe1?;KoZP|5ot)go$=#gn=j0wv?&ahF zCkHt>#L0b}+|S8jP9EUoK~5gx%lafkGHhAH?v+VRi34SAS=DQY8tV$Q zL_|q(z7A-rGh2TS3Wd&dPy}7)pqTVo-tz`KNR71y&H5BWF`SyqT1%M%qrfQCimWTM zzT$Pof>vU!&l)A(DMF3al{HF@GNXK?;*8Zj=2pFxaIn;g(_iJA>aJQh6+~-ucFdTj zO}BcH&d_FBTaeDuW?Ng4eoVW>+KyD!=2+Jty;PfP_4%rdD(^gDr0T5YKW$uAT7u@T z_02ct`>KsWn(0{$S}0v=;X@xUcj!;P?a|A7bN^t1Xkl+_=0(?;^CAX6(kS(ZtvR>YC9!viK*~ zpl^w>gm%wZLuhUDsZn?D=-8-x%P6yZ109$(W@e4)r^dWXr5fww=$s^>bIZ`T5una( z6Gq@1Y=;rBZUi)P4I0^jMwo?Nz)j4iCcI0<8tW!N6SlmG-8mDxnTg$C?V%>FJ>SI5 zITM$Ro4AFVuuDX7zDh%M7qbx%_Jg$_e9PRSb<1E2(B<6&S!20cR))d47fzwdKz#Ye?avb$G^sO96tq-rXemjARkE7m!KF>I6L)c@zJAsOh zquztQ_Hk5KxZOGjzK!0ESQGw`hj+SfD4&H3{Ui9AyiKV26ZpD)R~c8iKk4np)AeUq zZM{DM{#5yF1pg%u{woDgcH+B+33nJfd_KeH zy#|H*h_BD+8z{y99m=|cn?kkMk7wX6;=9(kmaFdODm3TSP<1cS1z*4j(B{4eyg~4W zU{8!#2N({cM!Y)W4iVqw)Cj9&_hAAOLjbSPFOXKjX_K7=Cul03#6!AuLC}ubcpunK zf)47)l9-*6s+g<;CZ`T#F`~oin5aWQib?pj#cbzbKL{r=bMFVBqRmcdWMHmpo7Gb*}RR})8Rw3*V>`R1gf?OpG3XWF6Met3r4JSN8@KJFT z4fPC^(kLIJU6p0_qr6Kq`8e%*hw$j0AX!!Gu^vZ3FE3wD&@R(GNp-!}=V%En5bcU{ z7cH?*;|%BESrDGar~PvPl43E1114aYVsQX2*zCuO=wo&(&b9wp= zG8T}u8?5gjwP_!>zE2SE{!b{UuZbU!G2bVQPZ*!nK2@>?`5!Xn*SYdXIpwEOc{wVx zO!-YHr#1UjPB{XoEkwcToZvkJJ=p;V|ie2)OE2iqG+egO72k^B(kUnBVuINm~%1?6odr@;9Rk}+_-Ppp^op?d|R z^>gOuX}*YG&`|vt*pYn|m9L@lCUBpD@*+X-?FIw?+XH(_@T$sjH^ZJi80*&&cnj>o zXum;xx58=THt^mGkO|tYH;J$1w6PZg_M_ewFoUx7YvOA?ZQKstR&f6kyq^K65{!MQ za#(MDSOrX_SZ^~scK|cuwB8|Ay=Ks5y-Qtw9o%n#^&aje?&_Oh{f_VIufaNpJ$cmq z7FZVc9HZ{H!TLS+lu`F@!1@FB?4$1Ag7ru2IY-^^fb}Qrxp>ujpZM;Cb$5a=awh}} ztiO^#K|l)FymtW}*}suffdcJrux=B_0#TAdoA(>} zXu##YmkxVK#62#eFYD)Mx<7)C_Uf~^y}~2nIrsy>Q=&C0W{m@Q+t0y7Il}rXyC9B= zSV#_l_aL3>n24w609cQSS>GX8cL;qe01;{g^^$cR>P&Hx>Bj&7ilG#J4b{KB}#+=q3 zfRbtG<`)<{Mm5joU~^GteK9|VPL6htRFPxagIMSDASQo+%O3*kdAh_o=$-dr8mt#Z zAAQ;#zy$F=LIq!@(JQ8p_0<@>moRwro)TWd*c}FVNwmI!hDQ-z1>Pfcz&G=}fDh`U zfG_HAF^TsWV0rpJHCICL$CEc(j|QjX4P0SV)ca0=aRTankAd+RlE=Y#9LW=4Jb~mm j7{~MEYGF3}7k-pJjHjZt(y|Z#|3%vIFA4tuZg0q$0@wHA literal 5642 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 diff --git a/trunk/research/players/srs_publisher/src/srs_publisher.as b/trunk/research/players/srs_publisher/src/srs_publisher.as index 2669e708bc..40f02eae14 100755 --- a/trunk/research/players/srs_publisher/src/srs_publisher.as +++ b/trunk/research/players/srs_publisher/src/srs_publisher.as @@ -148,7 +148,7 @@ package customItems.push(new ContextMenuItem("Server: " + srs_server)); } if (srs_primary != null) { - customItems.push(new ContextMenuItem("PrimaryAuthors: " + srs_primary)); + customItems.push(new ContextMenuItem("Primary: " + srs_primary)); } if (srs_authors != null) { customItems.push(new ContextMenuItem("Authors: " + srs_authors)); From 6a5f405ba0b0ebe39bf4a1267a4321bd9626627d Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 27 Nov 2014 07:02:54 +0800 Subject: [PATCH 219/800] add github script --- trunk/scripts/github.commit.sh | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100755 trunk/scripts/github.commit.sh diff --git a/trunk/scripts/github.commit.sh b/trunk/scripts/github.commit.sh new file mode 100755 index 0000000000..de4b04d2b2 --- /dev/null +++ b/trunk/scripts/github.commit.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +echo "submit code to github.com" + +echo "argv[0]=$0" +if [[ ! -f $0 ]]; then + echo "directly execute the scripts on shell."; + work_dir=`pwd` +else + echo "execute scripts in file: $0"; + work_dir=`dirname $0`; work_dir=`(cd ${work_dir} && pwd)` +fi +work_dir=`(cd ${work_dir}/.. && pwd)` +product_dir=$work_dir + +# allow start script from any dir +cd $work_dir && git checkout master + +. ${product_dir}/scripts/_log.sh +ret=$?; if [[ $ret -ne 0 ]]; then exit $ret; fi +ok_msg "导入脚本成功" + +source $work_dir/scripts/_mirror.utils.sh + +git remote -v|grep github.com >/dev/null 2>&1 +ret=$?; if [[ 0 -ne $ret ]]; then + echo "current not under github.com branch" + exit -1; +fi + +sync_push "Github" + +exit 0 From 131f598ab5806d5f184f068b0d5b772d636a74e1 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 27 Nov 2014 07:47:39 +0800 Subject: [PATCH 220/800] use multiple remote to commit and sync with csdn+oschina. --- trunk/scripts/_log.sh | 2 +- trunk/scripts/_mirror.utils.sh | 95 --------------------------------- trunk/scripts/csdn.mirror.sh | 37 ------------- trunk/scripts/github.commit.sh | 46 +++++++++++++--- trunk/scripts/oschina.mirror.sh | 37 ------------- 5 files changed, 40 insertions(+), 177 deletions(-) delete mode 100755 trunk/scripts/_mirror.utils.sh delete mode 100755 trunk/scripts/csdn.mirror.sh delete mode 100755 trunk/scripts/oschina.mirror.sh diff --git a/trunk/scripts/_log.sh b/trunk/scripts/_log.sh index a91a2c8204..3c09982f3c 100755 --- a/trunk/scripts/_log.sh +++ b/trunk/scripts/_log.sh @@ -8,7 +8,7 @@ RED="\\e[31m" GREEN="\\e[32m" YELLOW="\\e[33m" BLACK="\\e[0m" -POS="\\e[110G" +POS="\\e[100G" # if need to log to file, change the log path. if [[ ! $log ]]; then diff --git a/trunk/scripts/_mirror.utils.sh b/trunk/scripts/_mirror.utils.sh deleted file mode 100755 index 01f961a5e8..0000000000 --- a/trunk/scripts/_mirror.utils.sh +++ /dev/null @@ -1,95 +0,0 @@ -#!/bin/bash - -############################################# -# help for the first checkout. -############################################# -function first_checkout() -{ - mirror_name=$1 - git_url=$2 - project_dir=$3 - sync_script=$4 - - failed_msg "当前分支不是${mirror_name}镜像"; - - cat </dev/null 2>&1 -ret=$?; if [[ 0 -ne $ret ]]; then - first_checkout "CSDN" \ - "git@code.csdn.net:winlinvip/srs-csdn.git" \ - "srs-csdn" "$work_dir/scripts/csdn.mirror.sh" - exit 0; -fi - -sync_master -sync_1_0_release -sync_push "CSDN" - -exit 0 diff --git a/trunk/scripts/github.commit.sh b/trunk/scripts/github.commit.sh index de4b04d2b2..e6c54739ca 100755 --- a/trunk/scripts/github.commit.sh +++ b/trunk/scripts/github.commit.sh @@ -20,14 +20,46 @@ cd $work_dir && git checkout master ret=$?; if [[ $ret -ne 0 ]]; then exit $ret; fi ok_msg "导入脚本成功" -source $work_dir/scripts/_mirror.utils.sh +function remote_check() +{ + remote=$1 + url=$2 + git remote -v| grep "$url" >/dev/null 2>&1 + ret=$?; if [[ 0 -ne $ret ]]; then + echo "remote $remote not found, add by:" + echo " git remote add $remote $url" + exit -1 + fi + ok_msg "remote $remote ok, url is $url" +} +remote_check origin git@github.com:winlinvip/simple-rtmp-server.git +remote_check srs.csdn git@code.csdn.net:winlinvip/srs-csdn.git +remote_check srs.oschina git@git.oschina.net:winlinvip/srs.oschina.git -git remote -v|grep github.com >/dev/null 2>&1 -ret=$?; if [[ 0 -ne $ret ]]; then - echo "current not under github.com branch" - exit -1; -fi +function sync_push() +{ + repository=$1 + branch=$2 + + for ((;;)); do + git push $repository $branch + ret=$?; if [[ 0 -ne $ret ]]; then + failed_msg "提交$repository/$branch分支失败,自动重试"; + continue + else + ok_msg "提交$repository/$branch分支成功" + fi + break + done + ok_msg "$repository/$branch同步git成功" +} -sync_push "Github" +sync_push origin master +sync_push origin 1.0release +sync_push srs.csdn master +sync_push srs.csdn 1.0release +sync_push srs.oschina master +sync_push srs.oschina 1.0release +ok_msg "sync push ok" exit 0 diff --git a/trunk/scripts/oschina.mirror.sh b/trunk/scripts/oschina.mirror.sh deleted file mode 100755 index 47841b5294..0000000000 --- a/trunk/scripts/oschina.mirror.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash - -echo "更新OSChina镜像的脚本" - -echo "argv[0]=$0" -if [[ ! -f $0 ]]; then - echo "directly execute the scripts on shell."; - work_dir=`pwd` -else - echo "execute scripts in file: $0"; - work_dir=`dirname $0`; work_dir=`(cd ${work_dir} && pwd)` -fi -work_dir=`(cd ${work_dir}/.. && pwd)` -product_dir=$work_dir - -# allow start script from any dir -cd $work_dir && git checkout master - -. ${product_dir}/scripts/_log.sh -ret=$?; if [[ $ret -ne 0 ]]; then exit $ret; fi -ok_msg "导入脚本成功" - -source $work_dir/scripts/_mirror.utils.sh - -git remote -v|grep git.oschina.net >/dev/null 2>&1 -ret=$?; if [[ 0 -ne $ret ]]; then - first_checkout "OSChina" \ - "git@git.oschina.net:winlinvip/srs.oschina.git" \ - "srs.oschina" "$work_dir/scripts/oschina.mirror.sh" - exit 0; -fi - -sync_master -sync_1_0_release -sync_push "OSChina" - -exit 0 From 55461e866f2ed23bf55a903b7c02446154a1e1e8 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 27 Nov 2014 07:52:12 +0800 Subject: [PATCH 221/800] sync tags with csdn and oschina --- trunk/scripts/github.commit.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/trunk/scripts/github.commit.sh b/trunk/scripts/github.commit.sh index e6c54739ca..26308ac615 100755 --- a/trunk/scripts/github.commit.sh +++ b/trunk/scripts/github.commit.sh @@ -62,4 +62,8 @@ sync_push srs.oschina master sync_push srs.oschina 1.0release ok_msg "sync push ok" +sync_push srs.csdn --tags +sync_push srs.oschina --tags +ok_msg "sync tags ok" + exit 0 From 93ee15a552aec6e98ec97639fbcca77803a0d75a Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 27 Nov 2014 08:00:11 +0800 Subject: [PATCH 222/800] udpate github commit to git commit --- .../{github.commit.sh => git.commit.sh} | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) rename trunk/scripts/{github.commit.sh => git.commit.sh} (69%) diff --git a/trunk/scripts/github.commit.sh b/trunk/scripts/git.commit.sh similarity index 69% rename from trunk/scripts/github.commit.sh rename to trunk/scripts/git.commit.sh index 26308ac615..7fda1958a9 100755 --- a/trunk/scripts/github.commit.sh +++ b/trunk/scripts/git.commit.sh @@ -35,35 +35,32 @@ function remote_check() remote_check origin git@github.com:winlinvip/simple-rtmp-server.git remote_check srs.csdn git@code.csdn.net:winlinvip/srs-csdn.git remote_check srs.oschina git@git.oschina.net:winlinvip/srs.oschina.git +ok_msg "remote check ok" function sync_push() { - repository=$1 - branch=$2 - for ((;;)); do - git push $repository $branch + git push $* ret=$?; if [[ 0 -ne $ret ]]; then - failed_msg "提交$repository/$branch分支失败,自动重试"; + failed_msg "Retry for failed: $*" continue else - ok_msg "提交$repository/$branch分支成功" + ok_msg "Success: $*" fi break done - ok_msg "$repository/$branch同步git成功" } -sync_push origin master -sync_push origin 1.0release -sync_push srs.csdn master -sync_push srs.csdn 1.0release -sync_push srs.oschina master -sync_push srs.oschina 1.0release -ok_msg "sync push ok" +sync_push --all origin master +sync_push --all origin 1.0release +sync_push --all srs.csdn master +sync_push --all srs.csdn 1.0release +sync_push --all srs.oschina master +sync_push --all srs.oschina 1.0release +ok_msg "push refs ok" -sync_push srs.csdn --tags -sync_push srs.oschina --tags -ok_msg "sync tags ok" +sync_push --tags srs.csdn +sync_push --tags srs.oschina +ok_msg "push tags ok" exit 0 From ff730880914babe401c93dcaf3ca50d73b103c31 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 27 Nov 2014 08:02:17 +0800 Subject: [PATCH 223/800] fix bug of git commit --- trunk/scripts/git.commit.sh | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/trunk/scripts/git.commit.sh b/trunk/scripts/git.commit.sh index 7fda1958a9..2ad3a83ea4 100755 --- a/trunk/scripts/git.commit.sh +++ b/trunk/scripts/git.commit.sh @@ -51,12 +51,9 @@ function sync_push() done } -sync_push --all origin master -sync_push --all origin 1.0release -sync_push --all srs.csdn master -sync_push --all srs.csdn 1.0release -sync_push --all srs.oschina master -sync_push --all srs.oschina 1.0release +sync_push --all origin +sync_push --all srs.csdn +sync_push --all srs.oschina ok_msg "push refs ok" sync_push --tags srs.csdn From e76c53dae83ccbc6b72aba1ad5ee2614753d52c8 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 27 Nov 2014 08:03:21 +0800 Subject: [PATCH 224/800] sleep when git commit failed. --- trunk/scripts/git.commit.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/trunk/scripts/git.commit.sh b/trunk/scripts/git.commit.sh index 2ad3a83ea4..ca85d80fb0 100755 --- a/trunk/scripts/git.commit.sh +++ b/trunk/scripts/git.commit.sh @@ -43,6 +43,7 @@ function sync_push() git push $* ret=$?; if [[ 0 -ne $ret ]]; then failed_msg "Retry for failed: $*" + sleep 3 continue else ok_msg "Success: $*" From 8766ab5de2a6b6754aa069aa364ea5386bcb1bd2 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 27 Nov 2014 12:45:34 +0800 Subject: [PATCH 225/800] add git2unix script --- trunk/scripts/git.commit.sh | 2 ++ trunk/scripts/git2unix.sh | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100755 trunk/scripts/git2unix.sh diff --git a/trunk/scripts/git.commit.sh b/trunk/scripts/git.commit.sh index ca85d80fb0..aa2792ab05 100755 --- a/trunk/scripts/git.commit.sh +++ b/trunk/scripts/git.commit.sh @@ -1,5 +1,7 @@ #!/bin/bash +# sudo ln -sf `pwd`/git.commit.sh /bin/git-ensure-commit + echo "submit code to github.com" echo "argv[0]=$0" diff --git a/trunk/scripts/git2unix.sh b/trunk/scripts/git2unix.sh new file mode 100755 index 0000000000..89c09d6bf1 --- /dev/null +++ b/trunk/scripts/git2unix.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# sudo ln -sf `pwd`/git2unix.sh /bin/git2unix + +dos2unix -V>/dev/null 2>&1 +ret=$?; if [[ 0 -ne $ret ]]; then + echo "dos2unix not found." + echo " sudo yum install -y dos2unix" + exit $ret +fi + +files=`git status|egrep "(modified|new file)"|awk -F ':' '{print $2}'|awk '{print $1}'|egrep "(.hpp$|.cpp$|.cc$|.h$|.c$|.txt$|.sh$)"`; +for file in $files; do + dos2unix $file; + echo $file|grep ".sh$" >/dev/null 2>&1; EOF_SH=$? + if [[ $EOF_SH -ne 0 && -f $file ]]; then + echo "chmod -x $file" + chmod -x $file; + fi +done From b825ea62f9b94fe3f33f99eeb8ff946693cee613 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 27 Nov 2014 12:50:04 +0800 Subject: [PATCH 226/800] refine comments --- trunk/scripts/git.commit.sh | 8 +++++++- trunk/scripts/git2unix.sh | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/trunk/scripts/git.commit.sh b/trunk/scripts/git.commit.sh index aa2792ab05..c42d04f669 100755 --- a/trunk/scripts/git.commit.sh +++ b/trunk/scripts/git.commit.sh @@ -1,6 +1,12 @@ #!/bin/bash -# sudo ln -sf `pwd`/git.commit.sh /bin/git-ensure-commit +cat <>/dev/null +touch git-ensure-commit && +echo "bash `pwd`/git.commit.sh" >git-ensure-commit && +chmod +x git-ensure-commit && +sudo rm -f /bin/git-ensure-commit && +sudo mv git-ensure-commit /bin/git-ensure-commit +END echo "submit code to github.com" diff --git a/trunk/scripts/git2unix.sh b/trunk/scripts/git2unix.sh index 89c09d6bf1..4f78ada6ff 100755 --- a/trunk/scripts/git2unix.sh +++ b/trunk/scripts/git2unix.sh @@ -1,6 +1,12 @@ #!/bin/bash -# sudo ln -sf `pwd`/git2unix.sh /bin/git2unix +cat <>/dev/null +touch git2unix && +echo "bash `pwd`/git2unix.sh" >git2unix && +chmod +x git2unix && +sudo rm -f /bin/git2unix && +sudo mv git2unix /bin/git2unix +END dos2unix -V>/dev/null 2>&1 ret=$?; if [[ 0 -ne $ret ]]; then From c49981ed6ff52658cb37bc8199de8887690c118e Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 27 Nov 2014 12:51:33 +0800 Subject: [PATCH 227/800] refine script --- trunk/scripts/git.commit.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/trunk/scripts/git.commit.sh b/trunk/scripts/git.commit.sh index c42d04f669..a39424be16 100755 --- a/trunk/scripts/git.commit.sh +++ b/trunk/scripts/git.commit.sh @@ -50,11 +50,11 @@ function sync_push() for ((;;)); do git push $* ret=$?; if [[ 0 -ne $ret ]]; then - failed_msg "Retry for failed: $*" + failed_msg "Retry for failed: git push $*" sleep 3 continue else - ok_msg "Success: $*" + ok_msg "Success: git push $*" fi break done From 1ce79980b1c1af1cf85919c3cd438c562c964c6c Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 27 Nov 2014 12:52:47 +0800 Subject: [PATCH 228/800] checkout master --- trunk/scripts/git.commit.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/trunk/scripts/git.commit.sh b/trunk/scripts/git.commit.sh index a39424be16..0bcaef6268 100755 --- a/trunk/scripts/git.commit.sh +++ b/trunk/scripts/git.commit.sh @@ -2,7 +2,8 @@ cat <>/dev/null touch git-ensure-commit && -echo "bash `pwd`/git.commit.sh" >git-ensure-commit && +echo "cd `pwd`/.. && git checkout master &&" >git-ensure-commit && +echo "bash `pwd`/git.commit.sh" >>git-ensure-commit && chmod +x git-ensure-commit && sudo rm -f /bin/git-ensure-commit && sudo mv git-ensure-commit /bin/git-ensure-commit From bb54194c2638071007fe0bd134c74bb3e638a574 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 27 Nov 2014 12:53:21 +0800 Subject: [PATCH 229/800] checkout master --- trunk/scripts/git.commit.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trunk/scripts/git.commit.sh b/trunk/scripts/git.commit.sh index 0bcaef6268..38932fdfff 100755 --- a/trunk/scripts/git.commit.sh +++ b/trunk/scripts/git.commit.sh @@ -2,7 +2,7 @@ cat <>/dev/null touch git-ensure-commit && -echo "cd `pwd`/.. && git checkout master &&" >git-ensure-commit && +echo "cd `pwd` && git checkout master &&" >git-ensure-commit && echo "bash `pwd`/git.commit.sh" >>git-ensure-commit && chmod +x git-ensure-commit && sudo rm -f /bin/git-ensure-commit && From 1855c9429cb292ec9f631ce1acfce7db46b9292f Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 27 Nov 2014 14:46:05 +0800 Subject: [PATCH 230/800] merge from allspace, to srs-librtmp, for vs2010 --- trunk/configure | 6 +- trunk/src/core/srs_core.hpp | 2 +- trunk/src/core/srs_platform.cpp | 407 ++++++++++++++++++ trunk/src/core/srs_platform.hpp | 80 ++++ trunk/src/kernel/srs_kernel_error.hpp | 4 + trunk/src/libs/srs_lib_simple_socket.cpp | 26 +- trunk/src/libs/srs_lib_simple_socket.hpp | 2 +- trunk/src/libs/srs_librtmp.cpp | 310 +------------ trunk/src/libs/srs_librtmp.hpp | 8 +- trunk/winbuild/srs-librtmp.def | 46 ++ trunk/winbuild/srs-librtmp_vs2010.sln | 26 ++ trunk/winbuild/srs-librtmp_vs2010.vcxproj | 137 ++++++ .../srs-librtmp_vs2010.vcxproj.filters | 156 +++++++ trunk/winbuild/srs_auto_headers.hpp | 56 +++ trunk/winbuild/srs_play.vcxproj | 85 ++++ trunk/winbuild/srs_play.vcxproj.filters | 22 + 16 files changed, 1053 insertions(+), 320 deletions(-) create mode 100644 trunk/src/core/srs_platform.cpp create mode 100644 trunk/src/core/srs_platform.hpp create mode 100644 trunk/winbuild/srs-librtmp.def create mode 100644 trunk/winbuild/srs-librtmp_vs2010.sln create mode 100644 trunk/winbuild/srs-librtmp_vs2010.vcxproj create mode 100644 trunk/winbuild/srs-librtmp_vs2010.vcxproj.filters create mode 100644 trunk/winbuild/srs_auto_headers.hpp create mode 100644 trunk/winbuild/srs_play.vcxproj create mode 100644 trunk/winbuild/srs_play.vcxproj.filters diff --git a/trunk/configure b/trunk/configure index b29d709cb4..285ff73819 100755 --- a/trunk/configure +++ b/trunk/configure @@ -300,7 +300,9 @@ GDBDebug=" -g -O0" # the warning level. WarnLevel=" -Wall" # the compile standard. -CppStd="-ansi" +if [ "$MSYSTEM" != "MINGW32" -a "$MSYSTEM" != "MINGW64" ]; then + CppStd="-ansi" +fi # for library compile LibraryCompile=" -fPIC" # performance of gprof @@ -355,7 +357,7 @@ if [ $SRS_MIPS_UBUNTU12 = YES ]; then SrsLinkOptions="${SrsLinkOptions} -lgcc_eh MODULE_ID="CORE" MODULE_DEPENDS=() ModuleLibIncs=(${SRS_OBJS_DIR}) -MODULE_FILES=("srs_core" "srs_core_autofree") +MODULE_FILES=("srs_core" "srs_core_autofree" "srs_platform") CORE_INCS="src/core"; MODULE_DIR=${CORE_INCS} . auto/modules.sh CORE_OBJS="${MODULE_OBJS[@]}" # diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 3e2db170dd..90802b2a0c 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -85,7 +85,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // generated by configure. #include - +#include // free the p and set to NULL. // p must be a T*. #define srs_freep(p) \ diff --git a/trunk/src/core/srs_platform.cpp b/trunk/src/core/srs_platform.cpp new file mode 100644 index 0000000000..c3940f4675 --- /dev/null +++ b/trunk/src/core/srs_platform.cpp @@ -0,0 +1,407 @@ +#include +#include +#include +#include "srs_platform.hpp" + + +#if defined(_WIN32) && !defined(__CYGWIN__) + +int socket_setup() +{ + WORD wVersionRequested; + WSADATA wsaData; + int err; + + /* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */ + wVersionRequested = MAKEWORD(2, 2); + + err = WSAStartup(wVersionRequested, &wsaData); + if (err != 0) { + /* Tell the user that we could not find a usable */ + /* Winsock DLL. */ + //printf("WSAStartup failed with error: %d\n", err); + return -1; + } + return 0; +} + +int socket_cleanup() +{ + WSACleanup(); + return 0; +} + + int gettimeofday(struct timeval* tv, struct timezone* tz) + { + time_t clock; + struct tm tm; + SYSTEMTIME win_time; + + GetLocalTime(&win_time); + + tm.tm_year = win_time.wYear - 1900; + tm.tm_mon = win_time.wMonth - 1; + tm.tm_mday = win_time.wDay; + tm.tm_hour = win_time.wHour; + tm.tm_min = win_time.wMinute; + tm.tm_sec = win_time.wSecond; + tm.tm_isdst = -1; + + clock = mktime(&tm); + + tv->tv_sec = (long)clock; + tv->tv_usec = win_time.wMilliseconds * 1000; + + return 0; + } + + pid_t getpid(void) + { + return (pid_t)GetCurrentProcessId(); + } + + int usleep(useconds_t usec) + { + Sleep((DWORD)(usec / 1000)); + return 0; + } + + ssize_t writev(int fd, const struct iovec *iov, int iovcnt) + { + ssize_t nwrite = 0; + for (int i = 0; i < iovcnt; i++) { + const struct iovec* current = iov + i; + + int nsent = ::send(fd, (char*)current->iov_base, current->iov_len, 0); + if (nsent < 0) { + return nsent; + } + + nwrite += nsent; + if (nsent == 0) { + return nwrite; + } + } + return nwrite; + } + + //////////////////////// strlcpy.c (modified) ////////////////////////// + + /* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */ + + /*- + * Copyright (c) 1998 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + //#include // **** + //#include // **** + // __FBSDID("$FreeBSD: stable/9/sys/libkern/strlcpy.c 243811 2012-12-03 18:08:44Z delphij $"); // **** + + // #include // **** + // #include // **** + + /* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ + + //#define __restrict // **** + + size_t strlcpy(char * __restrict dst, const char * __restrict src, size_t siz) + { + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0) { + while (--n != 0) { + if ((*d++ = *s++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ + } + + // http://www.cplusplus.com/forum/general/141779///////////////////////// inet_ntop.c (modified) ////////////////////////// + /* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + // #if defined(LIBC_SCCS) && !defined(lint) // **** + //static const char rcsid[] = "$Id: inet_ntop.c,v 1.3.18.2 2005/11/03 23:02:22 marka Exp $"; + // #endif /* LIBC_SCCS and not lint */ // **** + // #include // **** + // __FBSDID("$FreeBSD: stable/9/sys/libkern/inet_ntop.c 213103 2010-09-24 15:01:45Z attilio $"); // **** + + //#define _WIN32_WINNT _WIN32_WINNT_WIN8 // **** + //#include // **** + //#pragma comment(lib, "Ws2_32.lib") // **** + //#include // **** + + // #include // **** + // #include // **** + // #include // **** + + // #include // **** + + /*% + * WARNING: Don't even consider trying to compile this on a system where + * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. + */ + + static char *inet_ntop4(const u_char *src, char *dst, socklen_t size); + static char *inet_ntop6(const u_char *src, char *dst, socklen_t size); + + /* char * + * inet_ntop(af, src, dst, size) + * convert a network format address to presentation format. + * return: + * pointer to presentation format address (`dst'), or NULL (see errno). + * author: + * Paul Vixie, 1996. + */ + const char* inet_ntop(int af, const void *src, char *dst, socklen_t size) + { + switch (af) { + case AF_INET: + return (inet_ntop4( (unsigned char*)src, (char*)dst, size)); // **** + #ifdef AF_INET6 + //#error "IPv6 not supported" + //case AF_INET6: + // return (char*)(inet_ntop6( (unsigned char*)src, (char*)dst, size)); // **** + #endif + default: + // return (NULL); // **** + return 0 ; // **** + } + /* NOTREACHED */ + } + + /* const char * + * inet_ntop4(src, dst, size) + * format an IPv4 address + * return: + * `dst' (as a const) + * notes: + * (1) uses no statics + * (2) takes a u_char* not an in_addr as input + * author: + * Paul Vixie, 1996. + */ + static char * inet_ntop4(const u_char *src, char *dst, socklen_t size) + { + static const char fmt[128] = "%u.%u.%u.%u"; + char tmp[sizeof "255.255.255.255"]; + int l; + + l = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]); // **** + if (l <= 0 || (socklen_t) l >= size) { + return (NULL); + } + strlcpy(dst, tmp, size); + return (dst); + } + + /* const char * + * inet_ntop6(src, dst, size) + * convert IPv6 binary address into presentation (printable) format + * author: + * Paul Vixie, 1996. + */ + static char * inet_ntop6(const u_char *src, char *dst, socklen_t size) + { + /* + * Note that int32_t and int16_t need only be "at least" large enough + * to contain a value of the specified size. On some systems, like + * Crays, there is no such thing as an integer variable with 16 bits. + * Keep this in mind if you think this function should have been coded + * to use pointer overlays. All the world's not a VAX. + */ + char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp; + struct { int base, len; } best, cur; + #define NS_IN6ADDRSZ 16 + #define NS_INT16SZ 2 + u_int words[NS_IN6ADDRSZ / NS_INT16SZ]; + int i; + + /* + * Preprocess: + * Copy the input (bytewise) array into a wordwise array. + * Find the longest run of 0x00's in src[] for :: shorthanding. + */ + memset(words, '\0', sizeof words); + for (i = 0; i < NS_IN6ADDRSZ; i++) + words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); + best.base = -1; + best.len = 0; + cur.base = -1; + cur.len = 0; + for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { + if (words[i] == 0) { + if (cur.base == -1) + cur.base = i, cur.len = 1; + else + cur.len++; + } else { + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + cur.base = -1; + } + } + } + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + } + if (best.base != -1 && best.len < 2) + best.base = -1; + + /* + * Format the result. + */ + tp = tmp; + for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { + /* Are we inside the best run of 0x00's? */ + if (best.base != -1 && i >= best.base && + i < (best.base + best.len)) { + if (i == best.base) + *tp++ = ':'; + continue; + } + /* Are we following an initial run of 0x00s or any real hex? */ + if (i != 0) + *tp++ = ':'; + /* Is this address an encapsulated IPv4? */ + if (i == 6 && best.base == 0 && (best.len == 6 || + (best.len == 7 && words[7] != 0x0001) || + (best.len == 5 && words[5] == 0xffff))) { + if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp))) + return (NULL); + tp += strlen(tp); + break; + } + tp += sprintf(tp, "%x", words[i]); // **** + } + /* Was it a trailing run of 0x00's? */ + if (best.base != -1 && (best.base + best.len) == + (NS_IN6ADDRSZ / NS_INT16SZ)) + *tp++ = ':'; + *tp++ = '\0'; + + /* + * Check for overflow, copy, and we're done. + */ + if ((socklen_t)(tp - tmp) > size) { + return (NULL); + } + strcpy(dst, tmp); + return (dst); + } + +#define Set_errno(num) SetLastError((num)) + +/* INVALID_SOCKET == INVALID_HANDLE_VALUE == (unsigned int)(~0) */ +/* SOCKET_ERROR == -1 */ +//#define ENOTSOCK WSAENOTSOCK +//#define EINTR WSAEINTR +//#define ENOBUFS WSAENOBUFS +//#define EWOULDBLOCK WSAEWOULDBLOCK +#define EAFNOSUPPORT WSAEAFNOSUPPORT +/* from public\sdk\inc\crt\errno.h */ +#define ENOSPC 28 + +/* + * + */ +/* +#ifndef INET_ADDRSTRLEN +#define INET_ADDRSTRLEN 16 +#endif + +static const char * +inet_ntop_v4 (const void *src, char *dst, size_t size) +{ + const char digits[] = "0123456789"; + int i; + struct in_addr *addr = (struct in_addr *)src; + u_long a = ntohl(addr->s_addr); + const char *orig_dst = dst; + + if (size < INET_ADDRSTRLEN) { + Set_errno(ENOSPC); + return NULL; + } + for (i = 0; i < 4; ++i) { + int n = (a >> (24 - i * 8)) & 0xFF; + int non_zerop = 0; + + if (non_zerop || n / 100 > 0) { + *dst++ = digits[n / 100]; + n %= 100; + non_zerop = 1; + } + if (non_zerop || n / 10 > 0) { + *dst++ = digits[n / 10]; + n %= 10; + non_zerop = 1; + } + *dst++ = digits[n]; + if (i != 3) + *dst++ = '.'; + } + *dst++ = '\0'; + return orig_dst; +} + +const char * +inet_ntop(int af, const void *src, char *dst, size_t size) +{ + switch (af) { + case AF_INET : + return inet_ntop_v4 (src, dst, size); + default : + Set_errno(EAFNOSUPPORT); + return NULL; + } +} + +*/ +#endif + diff --git a/trunk/src/core/srs_platform.hpp b/trunk/src/core/srs_platform.hpp new file mode 100644 index 0000000000..2de4050569 --- /dev/null +++ b/trunk/src/core/srs_platform.hpp @@ -0,0 +1,80 @@ +#ifndef SRS_WIN_PORTING_H +#define SRS_WIN_PORTING_H + +#if !defined(_WIN32) || defined(__CYGWIN__) /*not on windows or it's cygwin. _WIN32 includes both 32-bit and 64-bit*/ + +#define SOCKET_ETIME ETIME +#define SOCKET_ECONNRESET ECONNRESET + +#define SOCKET int +#define SOCKET_ERRNO() errno +#define SOCKET_RESET(x) x=-1 +#define SOCKET_CLOSE(x) if(x>=0){::close(x);x=-1;} +#define SOCKET_VALID(x) (x>=0) +#define SOCKET_SETUP() {} +#define SOCKET_CLEANUP() {} + +#else /*on windows, but not on cygwin*/ + +#include +#include +#include +#include + +#ifdef _MSC_VER //for VS2010 +#include +#include +#define S_IRUSR _S_IREAD +#define S_IWUSR _S_IWRITE +#define open _open +#define close _close +#define lseek _lseek +#define write _write +#define read _read + +typedef int ssize_t; +typedef int pid_t; +typedef int mode_t; +typedef int64_t useconds_t; +#endif + +#define S_IRGRP 0 +#define S_IWGRP 0 +#define S_IXGRP 0 +#define S_IRWXG 0 +#define S_IROTH 0 +#define S_IWOTH 0 +#define S_IXOTH 0 +#define S_IRWXO 0 + +#define PRId64 "lld" + +#define SOCKET_ETIME WSAETIMEDOUT +#define SOCKET_ECONNRESET WSAECONNRESET +#define SOCKET_ERRNO() WSAGetLastError() +#define SOCKET_RESET(x) x=INVALID_SOCKET +#define SOCKET_CLOSE(x) if(x!=INVALID_SOCKET){::closesocket(x);x=INVALID_SOCKET;} +#define SOCKET_VALID(x) (x!=INVALID_SOCKET) +#define SOCKET_BUFF(x) ((char*)x) +#define SOCKET_SETUP() socket_setup() +#define SOCKET_CLEANUP() socket_cleanup() + +typedef uint32_t u_int32_t; +typedef uint8_t u_int8_t; +typedef int socklen_t; +struct iovec { + void* iov_base; /* Starting address */ + size_t iov_len; /* Length in bytes */ +}; + +#define snprintf _snprintf +ssize_t writev(int fd, const struct iovec *iov, int iovcnt); +const char* inet_ntop(int af, const void *src, char *dst, socklen_t size); +int gettimeofday(struct timeval* tv, struct timezone* tz); +pid_t getpid(void); +int usleep(useconds_t usec); +int socket_setup(); +int socket_cleanup(); +#endif + +#endif //SRS_WIN_PORTING_H diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index 14e94378b7..ed421b1187 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -31,7 +31,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include // success, ok +#if !defined(_WIN32) || defined(__CYGWIN__) //avoid redefine error on Windows #define ERROR_SUCCESS 0 +#else +#include +#endif /////////////////////////////////////////////////////// // system error. diff --git a/trunk/src/libs/srs_lib_simple_socket.cpp b/trunk/src/libs/srs_lib_simple_socket.cpp index e7346b6a06..9ee98e91d4 100644 --- a/trunk/src/libs/srs_lib_simple_socket.cpp +++ b/trunk/src/libs/srs_lib_simple_socket.cpp @@ -45,22 +45,28 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SimpleSocketStream::SimpleSocketStream() { - fd = -1; + //fd = -1; + SOCKET_RESET(fd); send_timeout = recv_timeout = ST_UTIME_NO_TIMEOUT; recv_bytes = send_bytes = 0; + SOCKET_SETUP(); } SimpleSocketStream::~SimpleSocketStream() { - if (fd != -1) { - ::close(fd); - fd = -1; - } + //if (fd != -1) { + // ::close(fd); + // fd = -1; + //} + SOCKET_CLOSE(fd); + SOCKET_CLEANUP(); } int SimpleSocketStream::create_socket() { - if((fd = ::socket(AF_INET, SOCK_STREAM, 0)) < 0){ + //if((fd = ::socket(AF_INET, SOCK_STREAM, 0)) < 0){ + fd = ::socket(AF_INET, SOCK_STREAM, 0); + if(!SOCKET_VALID(fd)){ return ERROR_SOCKET_CREATE; } @@ -95,12 +101,12 @@ int SimpleSocketStream::read(void* buf, size_t size, ssize_t* nread) // On success a non-negative integer indicating the number of bytes actually read is returned // (a value of 0 means the network connection is closed or end of file is reached). if (nb_read <= 0) { - if (nb_read < 0 && errno == ETIME) { + if (nb_read < 0 && SOCKET_ERRNO() == SOCKET_ETIME) { return ERROR_SOCKET_TIMEOUT; } if (nb_read == 0) { - errno = ECONNRESET; + errno = SOCKET_ECONNRESET; } return ERROR_SOCKET_READ; @@ -158,7 +164,7 @@ int SimpleSocketStream::writev(const iovec *iov, int iov_size, ssize_t* nwrite) // returned, and errno is set appropriately. if (nb_write <= 0) { // @see https://github.com/winlinvip/simple-rtmp-server/issues/200 - if (nb_write < 0 && errno == ETIME) { + if (nb_write < 0 && SOCKET_ERRNO() == SOCKET_ETIME) { return ERROR_SOCKET_TIMEOUT; } @@ -215,7 +221,7 @@ int SimpleSocketStream::write(void* buf, size_t size, ssize_t* nwrite) if (nb_write <= 0) { // @see https://github.com/winlinvip/simple-rtmp-server/issues/200 - if (nb_write < 0 && errno == ETIME) { + if (nb_write < 0 && SOCKET_ERRNO() == SOCKET_ETIME) { return ERROR_SOCKET_TIMEOUT; } diff --git a/trunk/src/libs/srs_lib_simple_socket.hpp b/trunk/src/libs/srs_lib_simple_socket.hpp index 2aa32bd391..90628a0930 100644 --- a/trunk/src/libs/srs_lib_simple_socket.hpp +++ b/trunk/src/libs/srs_lib_simple_socket.hpp @@ -43,7 +43,7 @@ class SimpleSocketStream : public ISrsProtocolReaderWriter int64_t send_timeout; int64_t recv_bytes; int64_t send_bytes; - int fd; + SOCKET fd; public: SimpleSocketStream(); virtual ~SimpleSocketStream(); diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index d1ad3513b6..d212e17793 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -22,7 +22,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include - +#include #include // for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 @@ -109,30 +109,8 @@ struct Context // for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 #ifdef _WIN32 - int gettimeofday(struct timeval* tv, struct timezone* tz) - { - time_t clock; - struct tm tm; - SYSTEMTIME win_time; - - GetLocalTime(&win_time); - - tm.tm_year = win_time.wYear - 1900; - tm.tm_mon = win_time.wMonth - 1; - tm.tm_mday = win_time.wDay; - tm.tm_hour = win_time.wHour; - tm.tm_min = win_time.wMinute; - tm.tm_sec = win_time.wSecond; - tm.tm_isdst = -1; - - clock = mktime(&tm); - - tv->tv_sec = (long)clock; - tv->tv_usec = win_time.wMilliseconds * 1000; - - return 0; - } - + +/* int open(const char *pathname, int flags) { return open(pathname, flags, 0); @@ -175,286 +153,8 @@ struct Context { return (ssize_t)fread(buf, count, 1, (FILE*)fd); } - - pid_t getpid(void) - { - return (pid_t)GetCurrentProcessId(); - } - - int usleep(useconds_t usec) - { - Sleep((DWORD)(usec / 1000)); - return 0; - } - - ssize_t writev(int fd, const struct iovec *iov, int iovcnt) - { - ssize_t nwrite = 0; - for (int i = 0; i < iovcnt; i++) { - const struct iovec* current = iov + i; - - int nsent = ::send(fd, (char*)current->iov_base, current->iov_len, 0); - if (nsent < 0) { - return nsent; - } - - nwrite += nsent; - if (nsent == 0) { - return nwrite; - } - } - return nwrite; - } - - //////////////////////// strlcpy.c (modified) ////////////////////////// - - /* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */ - - /*- - * Copyright (c) 1998 Todd C. Miller - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - - //#include // **** - //#include // **** - // __FBSDID("$FreeBSD: stable/9/sys/libkern/strlcpy.c 243811 2012-12-03 18:08:44Z delphij $"); // **** - - // #include // **** - // #include // **** - - /* - * Copy src to string dst of size siz. At most siz-1 characters - * will be copied. Always NUL terminates (unless siz == 0). - * Returns strlen(src); if retval >= siz, truncation occurred. - */ - - //#define __restrict // **** - - std::size_t strlcpy(char * __restrict dst, const char * __restrict src, size_t siz) - { - char *d = dst; - const char *s = src; - size_t n = siz; - - /* Copy as many bytes as will fit */ - if (n != 0) { - while (--n != 0) { - if ((*d++ = *s++) == '\0') - break; - } - } - - /* Not enough room in dst, add NUL and traverse rest of src */ - if (n == 0) { - if (siz != 0) - *d = '\0'; /* NUL-terminate dst */ - while (*s++) - ; - } - - return(s - src - 1); /* count does not include NUL */ - } - - // http://www.cplusplus.com/forum/general/141779///////////////////////// inet_ntop.c (modified) ////////////////////////// - /* - * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") - * Copyright (c) 1996-1999 by Internet Software Consortium. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT - * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - - // #if defined(LIBC_SCCS) && !defined(lint) // **** - //static const char rcsid[] = "$Id: inet_ntop.c,v 1.3.18.2 2005/11/03 23:02:22 marka Exp $"; - // #endif /* LIBC_SCCS and not lint */ // **** - // #include // **** - // __FBSDID("$FreeBSD: stable/9/sys/libkern/inet_ntop.c 213103 2010-09-24 15:01:45Z attilio $"); // **** - - //#define _WIN32_WINNT _WIN32_WINNT_WIN8 // **** - //#include // **** - #pragma comment(lib, "Ws2_32.lib") // **** - //#include // **** - - // #include // **** - // #include // **** - // #include // **** - - // #include // **** - - /*% - * WARNING: Don't even consider trying to compile this on a system where - * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. - */ - - static char *inet_ntop4(const u_char *src, char *dst, socklen_t size); - static char *inet_ntop6(const u_char *src, char *dst, socklen_t size); - - /* char * - * inet_ntop(af, src, dst, size) - * convert a network format address to presentation format. - * return: - * pointer to presentation format address (`dst'), or NULL (see errno). - * author: - * Paul Vixie, 1996. - */ - const char* inet_ntop(int af, const void *src, char *dst, socklen_t size) - { - switch (af) { - case AF_INET: - return (inet_ntop4( (unsigned char*)src, (char*)dst, size)); // **** - #ifdef AF_INET6 - #error "IPv6 not supported" - //case AF_INET6: - // return (char*)(inet_ntop6( (unsigned char*)src, (char*)dst, size)); // **** - #endif - default: - // return (NULL); // **** - return 0 ; // **** - } - /* NOTREACHED */ - } - - /* const char * - * inet_ntop4(src, dst, size) - * format an IPv4 address - * return: - * `dst' (as a const) - * notes: - * (1) uses no statics - * (2) takes a u_char* not an in_addr as input - * author: - * Paul Vixie, 1996. - */ - static char * inet_ntop4(const u_char *src, char *dst, socklen_t size) - { - static const char fmt[128] = "%u.%u.%u.%u"; - char tmp[sizeof "255.255.255.255"]; - int l; - - l = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]); // **** - if (l <= 0 || (socklen_t) l >= size) { - return (NULL); - } - strlcpy(dst, tmp, size); - return (dst); - } - - /* const char * - * inet_ntop6(src, dst, size) - * convert IPv6 binary address into presentation (printable) format - * author: - * Paul Vixie, 1996. - */ - static char * inet_ntop6(const u_char *src, char *dst, socklen_t size) - { - /* - * Note that int32_t and int16_t need only be "at least" large enough - * to contain a value of the specified size. On some systems, like - * Crays, there is no such thing as an integer variable with 16 bits. - * Keep this in mind if you think this function should have been coded - * to use pointer overlays. All the world's not a VAX. - */ - char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp; - struct { int base, len; } best, cur; - #define NS_IN6ADDRSZ 16 - #define NS_INT16SZ 2 - u_int words[NS_IN6ADDRSZ / NS_INT16SZ]; - int i; - - /* - * Preprocess: - * Copy the input (bytewise) array into a wordwise array. - * Find the longest run of 0x00's in src[] for :: shorthanding. - */ - memset(words, '\0', sizeof words); - for (i = 0; i < NS_IN6ADDRSZ; i++) - words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); - best.base = -1; - best.len = 0; - cur.base = -1; - cur.len = 0; - for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { - if (words[i] == 0) { - if (cur.base == -1) - cur.base = i, cur.len = 1; - else - cur.len++; - } else { - if (cur.base != -1) { - if (best.base == -1 || cur.len > best.len) - best = cur; - cur.base = -1; - } - } - } - if (cur.base != -1) { - if (best.base == -1 || cur.len > best.len) - best = cur; - } - if (best.base != -1 && best.len < 2) - best.base = -1; - - /* - * Format the result. - */ - tp = tmp; - for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { - /* Are we inside the best run of 0x00's? */ - if (best.base != -1 && i >= best.base && - i < (best.base + best.len)) { - if (i == best.base) - *tp++ = ':'; - continue; - } - /* Are we following an initial run of 0x00s or any real hex? */ - if (i != 0) - *tp++ = ':'; - /* Is this address an encapsulated IPv4? */ - if (i == 6 && best.base == 0 && (best.len == 6 || - (best.len == 7 && words[7] != 0x0001) || - (best.len == 5 && words[5] == 0xffff))) { - if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp))) - return (NULL); - tp += strlen(tp); - break; - } - tp += std::sprintf(tp, "%x", words[i]); // **** - } - /* Was it a trailing run of 0x00's? */ - if (best.base != -1 && (best.base + best.len) == - (NS_IN6ADDRSZ / NS_INT16SZ)) - *tp++ = ':'; - *tp++ = '\0'; - - /* - * Check for overflow, copy, and we're done. - */ - if ((socklen_t)(tp - tmp) > size) { - return (NULL); - } - strcpy(dst, tmp); - return (dst); - } +*/ + #endif int srs_librtmp_context_parse_uri(Context* context) diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index 58d2c4c8a8..0330b2da03 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -30,8 +30,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include + // for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 -#ifdef _WIN32 +#if 0 #define _CRT_SECURE_NO_WARNINGS typedef unsigned long long u_int64_t; typedef long long int64_t; @@ -72,6 +73,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. int usleep(useconds_t usec); #endif +#if defined(_WIN32) && !defined(__CYGWIN__) +#include +typedef uint32_t u_int32_t; +#endif + /** * srs-librtmp is a librtmp like library, * used to play/publish rtmp stream from/to rtmp server. diff --git a/trunk/winbuild/srs-librtmp.def b/trunk/winbuild/srs-librtmp.def new file mode 100644 index 0000000000..b35612186d --- /dev/null +++ b/trunk/winbuild/srs-librtmp.def @@ -0,0 +1,46 @@ +LIBRARY +EXPORTS +srs_rtmp_create +srs_rtmp_create2 +srs_rtmp_destroy +srs_rtmp_handshake +srs_rtmp_connect_app +srs_rtmp_connect_app2 +srs_rtmp_play_stream +srs_rtmp_publish_stream +srs_rtmp_read_packet +srs_rtmp_write_packet +srs_version_major +srs_version_minor +srs_version_revision +srs_utils_parse_timestamp +srs_human_format_time +srs_human_print_rtmp_packet +srs_rtmp_bandwidth_check +; +__srs_rtmp_do_simple_handshake +__srs_rtmp_connect_server +__srs_rtmp_dns_resolve + +;;;;;;;;for flv read/write;;;;;;;; +srs_flv_open_read +srs_flv_open_write +srs_flv_close +srs_flv_read_header +srs_flv_read_tag_header +srs_flv_read_tag_data +srs_flv_write_header +srs_flv_write_tag +srs_flv_size_tag +srs_flv_tellg +srs_flv_lseek +srs_flv_is_eof +srs_flv_is_sequence_header +srs_flv_is_keyframe + +;;;;;;;;for h264 read/write;;;;;;;; +srs_h264_write_raw_frames +srs_h264_is_dvbsp_error +srs_h264_is_duplicated_sps_error +srs_h264_is_duplicated_pps_error +srs_h264_startswith_annexb diff --git a/trunk/winbuild/srs-librtmp_vs2010.sln b/trunk/winbuild/srs-librtmp_vs2010.sln new file mode 100644 index 0000000000..204bf81ac7 --- /dev/null +++ b/trunk/winbuild/srs-librtmp_vs2010.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "srs-librtmp", "srs-librtmp_vs2010.vcxproj", "{051CC3D8-5A99-4534-90EE-AED40EDDEEB2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "srs_play", "srs_play.vcxproj", "{5149B9A9-5085-4A10-AD6F-23FBE6854390}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {051CC3D8-5A99-4534-90EE-AED40EDDEEB2}.Debug|Win32.ActiveCfg = Debug|Win32 + {051CC3D8-5A99-4534-90EE-AED40EDDEEB2}.Debug|Win32.Build.0 = Debug|Win32 + {051CC3D8-5A99-4534-90EE-AED40EDDEEB2}.Release|Win32.ActiveCfg = Release|Win32 + {051CC3D8-5A99-4534-90EE-AED40EDDEEB2}.Release|Win32.Build.0 = Release|Win32 + {5149B9A9-5085-4A10-AD6F-23FBE6854390}.Debug|Win32.ActiveCfg = Debug|Win32 + {5149B9A9-5085-4A10-AD6F-23FBE6854390}.Debug|Win32.Build.0 = Debug|Win32 + {5149B9A9-5085-4A10-AD6F-23FBE6854390}.Release|Win32.ActiveCfg = Release|Win32 + {5149B9A9-5085-4A10-AD6F-23FBE6854390}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/trunk/winbuild/srs-librtmp_vs2010.vcxproj b/trunk/winbuild/srs-librtmp_vs2010.vcxproj new file mode 100644 index 0000000000..08cc6ead06 --- /dev/null +++ b/trunk/winbuild/srs-librtmp_vs2010.vcxproj @@ -0,0 +1,137 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {051CC3D8-5A99-4534-90EE-AED40EDDEEB2} + Win32Proj + srslibrtmp + srs-librtmp + + + + DynamicLibrary + true + Unicode + + + DynamicLibrary + false + true + Unicode + + + + + + + + + + + + + true + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;SRSLIBRTMP_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + ..\src\rtmp;..\src\libs;..\src\kernel;..\src\core;.\;%(AdditionalIncludeDirectories) + + + Windows + true + ws2_32.lib;%(AdditionalDependencies) + srs-librtmp.def + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;SRSLIBRTMP_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + ..\src\rtmp;..\src\libs;..\src\kernel;..\src\core;.\;%(AdditionalIncludeDirectories) + + + Windows + true + true + true + srs-librtmp.def + ws2_32.lib;%(AdditionalDependencies) + + + + + + \ No newline at end of file diff --git a/trunk/winbuild/srs-librtmp_vs2010.vcxproj.filters b/trunk/winbuild/srs-librtmp_vs2010.vcxproj.filters new file mode 100644 index 0000000000..5c3da32199 --- /dev/null +++ b/trunk/winbuild/srs-librtmp_vs2010.vcxproj.filters @@ -0,0 +1,156 @@ + + + + + {8e33c6db-7a35-4d09-92d0-cc28491abbd6} + + + {eac81f26-3b4f-4905-85c5-2c0b915107b0} + + + {cbb45db3-c682-48bd-9a8c-1ae081aeddbf} + + + {6a284cec-b287-4aeb-b991-e11ebb4be6ec} + + + + + core + + + core + + + core + + + kernel + + + kernel + + + kernel + + + kernel + + + kernel + + + kernel + + + kernel + + + kernel + + + kernel + + + libs + + + libs + + + libs + + + rtmp + + + rtmp + + + rtmp + + + rtmp + + + rtmp + + + rtmp + + + rtmp + + + + + core + + + core + + + core + + + kernel + + + kernel + + + kernel + + + kernel + + + kernel + + + kernel + + + kernel + + + kernel + + + kernel + + + libs + + + libs + + + libs + + + rtmp + + + rtmp + + + rtmp + + + rtmp + + + rtmp + + + rtmp + + + rtmp + + + + + + \ No newline at end of file diff --git a/trunk/winbuild/srs_auto_headers.hpp b/trunk/winbuild/srs_auto_headers.hpp new file mode 100644 index 0000000000..c1845c9392 --- /dev/null +++ b/trunk/winbuild/srs_auto_headers.hpp @@ -0,0 +1,56 @@ +// auto generated by configure +#ifndef SRS_AUTO_HEADER_HPP +#define SRS_AUTO_HEADER_HPP + +#define SRS_AUTO_BUILD_TS "1416731672" +#define SRS_AUTO_BUILD_DATE "2014-11-23 16:34:32" +#define SRS_AUTO_UNAME "WIN_NT-6.1 WZY-PC2 1.0.18(0.48/3/2) 2012-11-21 22:34 i686 WINNT" +#define SRS_AUTO_USER_CONFIGURE "--x86-x64 --export-librtmp-project=../srs-librtmp" +#define SRS_AUTO_CONFIGURE "--prefix=/usr/local/srs --without-hls --without-dvr --without-nginx --without-ssl --without-ffmpeg --without-transcode --without-ingest --without-stat --without-http-callback --without-http-server --without-http-api --with-librtmp --with-research --without-utest --without-gperf --without-gmc --without-gmp --without-gcp --without-gprof --without-arm-ubuntu12 --without-mips-ubuntu12 --log-trace" + +#define SRS_AUTO_EMBEDED_TOOL_CHAIN "normal x86/x64 gcc" + +#undef SRS_AUTO_HTTP_PARSER +#undef SRS_AUTO_HTTP_SERVER +#undef SRS_AUTO_HTTP_API +#undef SRS_AUTO_NGINX +#undef SRS_AUTO_DVR +#undef SRS_AUTO_HLS +#undef SRS_AUTO_HTTP_CALLBACK +#undef SRS_AUTO_SSL +#undef SRS_AUTO_FFMPEG_TOOL +#define SRS_AUTO_FFMPEG_STUB +#undef SRS_AUTO_TRANSCODE +#undef SRS_AUTO_INGEST +#undef SRS_AUTO_STAT +#undef SRS_AUTO_GPERF +#undef SRS_AUTO_GPERF_MC +#undef SRS_AUTO_GPERF_MP +#undef SRS_AUTO_GPERF_CP +#undef SRS_AUTO_EMBEDED_CPU +#undef SRS_AUTO_ARM_UBUNTU12 +#undef SRS_AUTO_MIPS_UBUNTU12 + +#undef SRS_AUTO_VERBOSE +#undef SRS_AUTO_INFO +#define SRS_AUTO_TRACE + +#define SRS_AUTO_PREFIX "/usr/local/srs" + +#define SRS_AUTO_CONSTRIBUTORS "\ +winlin \ +wenjie.zhao<740936897@qq.com> \ +xiangcheng.liu \ +naijia.liu \ +alcoholyi \ +byteman \ +chad.wang \ +suhetao \ +Johnny \ +karthikeyan \ +StevenLiu \ +zhengfl \ +" + +#endif + diff --git a/trunk/winbuild/srs_play.vcxproj b/trunk/winbuild/srs_play.vcxproj new file mode 100644 index 0000000000..83c1b875b9 --- /dev/null +++ b/trunk/winbuild/srs_play.vcxproj @@ -0,0 +1,85 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {5149B9A9-5085-4A10-AD6F-23FBE6854390} + Win32Proj + srs_play + + + + Application + true + Unicode + + + Application + false + true + Unicode + + + + + + + + + + + + + true + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + ..\src\core;..\src\libs;%(AdditionalIncludeDirectories) + + + Console + true + srs-librtmp.lib;%(AdditionalDependencies) + ./debug;%(AdditionalLibraryDirectories) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + + \ No newline at end of file diff --git a/trunk/winbuild/srs_play.vcxproj.filters b/trunk/winbuild/srs_play.vcxproj.filters new file mode 100644 index 0000000000..53cbd3e870 --- /dev/null +++ b/trunk/winbuild/srs_play.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file From 18c308248eb0ff7db6e044082662bdf6d669b4ce Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 27 Nov 2014 15:22:39 +0800 Subject: [PATCH 231/800] refine code by winlin, for merged from allspace. --- trunk/configure | 7 +- trunk/src/core/srs_core.hpp | 6 +- trunk/src/core/srs_platform.cpp | 654 +++++++++++------------ trunk/src/core/srs_platform.hpp | 140 ++--- trunk/src/kernel/srs_kernel_error.hpp | 6 +- trunk/src/libs/srs_lib_simple_socket.cpp | 18 +- trunk/src/libs/srs_librtmp.cpp | 53 +- trunk/src/libs/srs_librtmp.hpp | 47 -- trunk/winbuild/srs_auto_headers.hpp | 2 +- trunk/winbuild/srs_play.vcxproj | 4 +- trunk/winbuild/srs_play.vcxproj.filters | 4 +- 11 files changed, 409 insertions(+), 532 deletions(-) diff --git a/trunk/configure b/trunk/configure index 285ff73819..1bd51abbc7 100755 --- a/trunk/configure +++ b/trunk/configure @@ -299,10 +299,9 @@ END GDBDebug=" -g -O0" # the warning level. WarnLevel=" -Wall" -# the compile standard. -if [ "$MSYSTEM" != "MINGW32" -a "$MSYSTEM" != "MINGW64" ]; then - CppStd="-ansi" -fi +# the compile in c++ standard. +# @remark, donot specifies it for mingw. +CppStd="-ansi" && echo $MSYSTEM|grep "MINGW">/dev/null && CppStd="" # for library compile LibraryCompile=" -fPIC" # performance of gprof diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 90802b2a0c..9025d504c5 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -85,7 +85,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // generated by configure. #include -#include + // free the p and set to NULL. // p must be a T*. #define srs_freep(p) \ @@ -110,5 +110,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. className(const className&); \ className& operator= (const className&) +// for windows to compile srs-librtmp +// @see: https://github.com/winlinvip/simple-rtmp-server/issues/213 +#include + #endif diff --git a/trunk/src/core/srs_platform.cpp b/trunk/src/core/srs_platform.cpp index c3940f4675..9e3f5fba9b 100644 --- a/trunk/src/core/srs_platform.cpp +++ b/trunk/src/core/srs_platform.cpp @@ -1,11 +1,33 @@ +/* +The MIT License (MIT) + +Copyright (c) 2014 allspace + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + #include #include #include -#include "srs_platform.hpp" - #if defined(_WIN32) && !defined(__CYGWIN__) - int socket_setup() { WORD wVersionRequested; @@ -31,309 +53,309 @@ int socket_cleanup() return 0; } - int gettimeofday(struct timeval* tv, struct timezone* tz) - { - time_t clock; - struct tm tm; - SYSTEMTIME win_time; - - GetLocalTime(&win_time); - - tm.tm_year = win_time.wYear - 1900; - tm.tm_mon = win_time.wMonth - 1; - tm.tm_mday = win_time.wDay; - tm.tm_hour = win_time.wHour; - tm.tm_min = win_time.wMinute; - tm.tm_sec = win_time.wSecond; - tm.tm_isdst = -1; - - clock = mktime(&tm); - - tv->tv_sec = (long)clock; - tv->tv_usec = win_time.wMilliseconds * 1000; - - return 0; +int gettimeofday(struct timeval* tv, struct timezone* tz) +{ + time_t clock; + struct tm tm; + SYSTEMTIME win_time; + + GetLocalTime(&win_time); + + tm.tm_year = win_time.wYear - 1900; + tm.tm_mon = win_time.wMonth - 1; + tm.tm_mday = win_time.wDay; + tm.tm_hour = win_time.wHour; + tm.tm_min = win_time.wMinute; + tm.tm_sec = win_time.wSecond; + tm.tm_isdst = -1; + + clock = mktime(&tm); + + tv->tv_sec = (long)clock; + tv->tv_usec = win_time.wMilliseconds * 1000; + + return 0; +} + +pid_t getpid(void) +{ + return (pid_t)GetCurrentProcessId(); +} + +int usleep(useconds_t usec) +{ + Sleep((DWORD)(usec / 1000)); + return 0; +} + +ssize_t writev(int fd, const struct iovec *iov, int iovcnt) +{ + ssize_t nwrite = 0; + for (int i = 0; i < iovcnt; i++) { + const struct iovec* current = iov + i; + + int nsent = ::send(fd, (char*)current->iov_base, current->iov_len, 0); + if (nsent < 0) { + return nsent; + } + + nwrite += nsent; + if (nsent == 0) { + return nwrite; + } } - - pid_t getpid(void) - { - return (pid_t)GetCurrentProcessId(); + return nwrite; +} + +//////////////////////// strlcpy.c (modified) ////////////////////////// + +/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */ + +/*- + * Copyright (c) 1998 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +//#include // **** +//#include // **** +// __FBSDID("$FreeBSD: stable/9/sys/libkern/strlcpy.c 243811 2012-12-03 18:08:44Z delphij $"); // **** + +// #include // **** +// #include // **** + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ + +//#define __restrict // **** + +size_t strlcpy(char * __restrict dst, const char * __restrict src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0) { + while (--n != 0) { + if ((*d++ = *s++) == '\0') + break; + } } - - int usleep(useconds_t usec) - { - Sleep((DWORD)(usec / 1000)); - return 0; + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; } - - ssize_t writev(int fd, const struct iovec *iov, int iovcnt) - { - ssize_t nwrite = 0; - for (int i = 0; i < iovcnt; i++) { - const struct iovec* current = iov + i; - - int nsent = ::send(fd, (char*)current->iov_base, current->iov_len, 0); - if (nsent < 0) { - return nsent; - } - - nwrite += nsent; - if (nsent == 0) { - return nwrite; - } - } - return nwrite; + + return(s - src - 1); /* count does not include NUL */ +} + +// http://www.cplusplus.com/forum/general/141779///////////////////////// inet_ntop.c (modified) ////////////////////////// +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +// #if defined(LIBC_SCCS) && !defined(lint) // **** +//static const char rcsid[] = "$Id: inet_ntop.c,v 1.3.18.2 2005/11/03 23:02:22 marka Exp $"; +// #endif /* LIBC_SCCS and not lint */ // **** +// #include // **** +// __FBSDID("$FreeBSD: stable/9/sys/libkern/inet_ntop.c 213103 2010-09-24 15:01:45Z attilio $"); // **** + +//#define _WIN32_WINNT _WIN32_WINNT_WIN8 // **** +//#include // **** +//#pragma comment(lib, "Ws2_32.lib") // **** +//#include // **** + +// #include // **** +// #include // **** +// #include // **** + +// #include // **** + +/*% + * WARNING: Don't even consider trying to compile this on a system where + * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. + */ + +static char *inet_ntop4(const u_char *src, char *dst, socklen_t size); +static char *inet_ntop6(const u_char *src, char *dst, socklen_t size); + +/* char * + * inet_ntop(af, src, dst, size) + * convert a network format address to presentation format. + * return: + * pointer to presentation format address (`dst'), or NULL (see errno). + * author: + * Paul Vixie, 1996. + */ +const char* inet_ntop(int af, const void *src, char *dst, socklen_t size) +{ + switch (af) { + case AF_INET: + return (inet_ntop4( (unsigned char*)src, (char*)dst, size)); // **** +#ifdef AF_INET6 + //#error "IPv6 not supported" + //case AF_INET6: + // return (char*)(inet_ntop6( (unsigned char*)src, (char*)dst, size)); // **** +#endif + default: + // return (NULL); // **** + return 0 ; // **** } + /* NOTREACHED */ +} - //////////////////////// strlcpy.c (modified) ////////////////////////// - - /* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */ - - /*- - * Copyright (c) 1998 Todd C. Miller - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - - //#include // **** - //#include // **** - // __FBSDID("$FreeBSD: stable/9/sys/libkern/strlcpy.c 243811 2012-12-03 18:08:44Z delphij $"); // **** - - // #include // **** - // #include // **** - - /* - * Copy src to string dst of size siz. At most siz-1 characters - * will be copied. Always NUL terminates (unless siz == 0). - * Returns strlen(src); if retval >= siz, truncation occurred. - */ - - //#define __restrict // **** - - size_t strlcpy(char * __restrict dst, const char * __restrict src, size_t siz) - { - char *d = dst; - const char *s = src; - size_t n = siz; - - /* Copy as many bytes as will fit */ - if (n != 0) { - while (--n != 0) { - if ((*d++ = *s++) == '\0') - break; - } - } - - /* Not enough room in dst, add NUL and traverse rest of src */ - if (n == 0) { - if (siz != 0) - *d = '\0'; /* NUL-terminate dst */ - while (*s++) - ; - } - - return(s - src - 1); /* count does not include NUL */ +/* const char * + * inet_ntop4(src, dst, size) + * format an IPv4 address + * return: + * `dst' (as a const) + * notes: + * (1) uses no statics + * (2) takes a u_char* not an in_addr as input + * author: + * Paul Vixie, 1996. + */ +static char * inet_ntop4(const u_char *src, char *dst, socklen_t size) +{ + static const char fmt[128] = "%u.%u.%u.%u"; + char tmp[sizeof "255.255.255.255"]; + int l; + + l = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]); // **** + if (l <= 0 || (socklen_t) l >= size) { + return (NULL); } - - // http://www.cplusplus.com/forum/general/141779///////////////////////// inet_ntop.c (modified) ////////////////////////// + strlcpy(dst, tmp, size); + return (dst); +} + +/* const char * + * inet_ntop6(src, dst, size) + * convert IPv6 binary address into presentation (printable) format + * author: + * Paul Vixie, 1996. + */ +static char * inet_ntop6(const u_char *src, char *dst, socklen_t size) +{ /* - * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") - * Copyright (c) 1996-1999 by Internet Software Consortium. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT - * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - - // #if defined(LIBC_SCCS) && !defined(lint) // **** - //static const char rcsid[] = "$Id: inet_ntop.c,v 1.3.18.2 2005/11/03 23:02:22 marka Exp $"; - // #endif /* LIBC_SCCS and not lint */ // **** - // #include // **** - // __FBSDID("$FreeBSD: stable/9/sys/libkern/inet_ntop.c 213103 2010-09-24 15:01:45Z attilio $"); // **** - - //#define _WIN32_WINNT _WIN32_WINNT_WIN8 // **** - //#include // **** - //#pragma comment(lib, "Ws2_32.lib") // **** - //#include // **** - - // #include // **** - // #include // **** - // #include // **** - - // #include // **** - - /*% - * WARNING: Don't even consider trying to compile this on a system where - * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. + * Note that int32_t and int16_t need only be "at least" large enough + * to contain a value of the specified size. On some systems, like + * Crays, there is no such thing as an integer variable with 16 bits. + * Keep this in mind if you think this function should have been coded + * to use pointer overlays. All the world's not a VAX. */ - - static char *inet_ntop4(const u_char *src, char *dst, socklen_t size); - static char *inet_ntop6(const u_char *src, char *dst, socklen_t size); - - /* char * - * inet_ntop(af, src, dst, size) - * convert a network format address to presentation format. - * return: - * pointer to presentation format address (`dst'), or NULL (see errno). - * author: - * Paul Vixie, 1996. + char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp; + struct { int base, len; } best, cur; +#define NS_IN6ADDRSZ 16 +#define NS_INT16SZ 2 + u_int words[NS_IN6ADDRSZ / NS_INT16SZ]; + int i; + + /* + * Preprocess: + * Copy the input (bytewise) array into a wordwise array. + * Find the longest run of 0x00's in src[] for :: shorthanding. */ - const char* inet_ntop(int af, const void *src, char *dst, socklen_t size) - { - switch (af) { - case AF_INET: - return (inet_ntop4( (unsigned char*)src, (char*)dst, size)); // **** - #ifdef AF_INET6 - //#error "IPv6 not supported" - //case AF_INET6: - // return (char*)(inet_ntop6( (unsigned char*)src, (char*)dst, size)); // **** - #endif - default: - // return (NULL); // **** - return 0 ; // **** + memset(words, '\0', sizeof words); + for (i = 0; i < NS_IN6ADDRSZ; i++) + words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); + best.base = -1; + best.len = 0; + cur.base = -1; + cur.len = 0; + for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { + if (words[i] == 0) { + if (cur.base == -1) + cur.base = i, cur.len = 1; + else + cur.len++; + } else { + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + cur.base = -1; + } } - /* NOTREACHED */ } - - /* const char * - * inet_ntop4(src, dst, size) - * format an IPv4 address - * return: - * `dst' (as a const) - * notes: - * (1) uses no statics - * (2) takes a u_char* not an in_addr as input - * author: - * Paul Vixie, 1996. - */ - static char * inet_ntop4(const u_char *src, char *dst, socklen_t size) - { - static const char fmt[128] = "%u.%u.%u.%u"; - char tmp[sizeof "255.255.255.255"]; - int l; - - l = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]); // **** - if (l <= 0 || (socklen_t) l >= size) { - return (NULL); - } - strlcpy(dst, tmp, size); - return (dst); + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; } - - /* const char * - * inet_ntop6(src, dst, size) - * convert IPv6 binary address into presentation (printable) format - * author: - * Paul Vixie, 1996. - */ - static char * inet_ntop6(const u_char *src, char *dst, socklen_t size) - { - /* - * Note that int32_t and int16_t need only be "at least" large enough - * to contain a value of the specified size. On some systems, like - * Crays, there is no such thing as an integer variable with 16 bits. - * Keep this in mind if you think this function should have been coded - * to use pointer overlays. All the world's not a VAX. - */ - char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp; - struct { int base, len; } best, cur; - #define NS_IN6ADDRSZ 16 - #define NS_INT16SZ 2 - u_int words[NS_IN6ADDRSZ / NS_INT16SZ]; - int i; - - /* - * Preprocess: - * Copy the input (bytewise) array into a wordwise array. - * Find the longest run of 0x00's in src[] for :: shorthanding. - */ - memset(words, '\0', sizeof words); - for (i = 0; i < NS_IN6ADDRSZ; i++) - words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); + if (best.base != -1 && best.len < 2) best.base = -1; - best.len = 0; - cur.base = -1; - cur.len = 0; - for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { - if (words[i] == 0) { - if (cur.base == -1) - cur.base = i, cur.len = 1; - else - cur.len++; - } else { - if (cur.base != -1) { - if (best.base == -1 || cur.len > best.len) - best = cur; - cur.base = -1; - } - } - } - if (cur.base != -1) { - if (best.base == -1 || cur.len > best.len) - best = cur; - } - if (best.base != -1 && best.len < 2) - best.base = -1; - - /* - * Format the result. - */ - tp = tmp; - for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { - /* Are we inside the best run of 0x00's? */ - if (best.base != -1 && i >= best.base && - i < (best.base + best.len)) { - if (i == best.base) - *tp++ = ':'; - continue; - } - /* Are we following an initial run of 0x00s or any real hex? */ - if (i != 0) + + /* + * Format the result. + */ + tp = tmp; + for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { + /* Are we inside the best run of 0x00's? */ + if (best.base != -1 && i >= best.base && + i < (best.base + best.len)) { + if (i == best.base) *tp++ = ':'; - /* Is this address an encapsulated IPv4? */ - if (i == 6 && best.base == 0 && (best.len == 6 || - (best.len == 7 && words[7] != 0x0001) || - (best.len == 5 && words[5] == 0xffff))) { - if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp))) - return (NULL); - tp += strlen(tp); - break; - } - tp += sprintf(tp, "%x", words[i]); // **** + continue; } - /* Was it a trailing run of 0x00's? */ - if (best.base != -1 && (best.base + best.len) == - (NS_IN6ADDRSZ / NS_INT16SZ)) + /* Are we following an initial run of 0x00s or any real hex? */ + if (i != 0) *tp++ = ':'; - *tp++ = '\0'; - - /* - * Check for overflow, copy, and we're done. - */ - if ((socklen_t)(tp - tmp) > size) { - return (NULL); + /* Is this address an encapsulated IPv4? */ + if (i == 6 && best.base == 0 && (best.len == 6 || + (best.len == 7 && words[7] != 0x0001) || + (best.len == 5 && words[5] == 0xffff))) { + if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp))) + return (NULL); + tp += strlen(tp); + break; } - strcpy(dst, tmp); - return (dst); + tp += sprintf(tp, "%x", words[i]); // **** + } + /* Was it a trailing run of 0x00's? */ + if (best.base != -1 && (best.base + best.len) == + (NS_IN6ADDRSZ / NS_INT16SZ)) + *tp++ = ':'; + *tp++ = '\0'; + + /* + * Check for overflow, copy, and we're done. + */ + if ((socklen_t)(tp - tmp) > size) { + return (NULL); } + strcpy(dst, tmp); + return (dst); +} #define Set_errno(num) SetLastError((num)) @@ -347,61 +369,5 @@ int socket_cleanup() /* from public\sdk\inc\crt\errno.h */ #define ENOSPC 28 -/* - * - */ -/* -#ifndef INET_ADDRSTRLEN -#define INET_ADDRSTRLEN 16 -#endif - -static const char * -inet_ntop_v4 (const void *src, char *dst, size_t size) -{ - const char digits[] = "0123456789"; - int i; - struct in_addr *addr = (struct in_addr *)src; - u_long a = ntohl(addr->s_addr); - const char *orig_dst = dst; - - if (size < INET_ADDRSTRLEN) { - Set_errno(ENOSPC); - return NULL; - } - for (i = 0; i < 4; ++i) { - int n = (a >> (24 - i * 8)) & 0xFF; - int non_zerop = 0; - - if (non_zerop || n / 100 > 0) { - *dst++ = digits[n / 100]; - n %= 100; - non_zerop = 1; - } - if (non_zerop || n / 10 > 0) { - *dst++ = digits[n / 10]; - n %= 10; - non_zerop = 1; - } - *dst++ = digits[n]; - if (i != 3) - *dst++ = '.'; - } - *dst++ = '\0'; - return orig_dst; -} - -const char * -inet_ntop(int af, const void *src, char *dst, size_t size) -{ - switch (af) { - case AF_INET : - return inet_ntop_v4 (src, dst, size); - default : - Set_errno(EAFNOSUPPORT); - return NULL; - } -} - -*/ #endif diff --git a/trunk/src/core/srs_platform.hpp b/trunk/src/core/srs_platform.hpp index 2de4050569..f1cf6479a0 100644 --- a/trunk/src/core/srs_platform.hpp +++ b/trunk/src/core/srs_platform.hpp @@ -1,80 +1,92 @@ #ifndef SRS_WIN_PORTING_H #define SRS_WIN_PORTING_H -#if !defined(_WIN32) || defined(__CYGWIN__) /*not on windows or it's cygwin. _WIN32 includes both 32-bit and 64-bit*/ - -#define SOCKET_ETIME ETIME -#define SOCKET_ECONNRESET ECONNRESET +// for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 +#if defined(_WIN32) + #include +#endif -#define SOCKET int -#define SOCKET_ERRNO() errno -#define SOCKET_RESET(x) x=-1 -#define SOCKET_CLOSE(x) if(x>=0){::close(x);x=-1;} -#define SOCKET_VALID(x) (x>=0) -#define SOCKET_SETUP() {} -#define SOCKET_CLEANUP() {} +/** +* for linux like, +* for example, not on windows or it's cygwin. +* while the _WIN32 includes both 32-bit and 64-bit +*/ +#if !defined(_WIN32) || defined(__CYGWIN__) + #define SOCKET_ETIME ETIME + #define SOCKET_ECONNRESET ECONNRESET + #define SOCKET int + #define SOCKET_ERRNO() errno + #define SOCKET_RESET(fd) fd = -1; (void)0 + #define SOCKET_CLOSE(fd) \ + if (fd > 0) {\ + ::close(fd); \ + fd = -1; \ + } \ + (void)0 + #define SOCKET_VALID(x) (x > 0) + #define SOCKET_SETUP() (void)0 + #define SOCKET_CLEANUP() (void)0 #else /*on windows, but not on cygwin*/ + #include + #include + #include + #include -#include -#include -#include -#include + #ifdef _MSC_VER //for VS2010 + #include + #include + #define S_IRUSR _S_IREAD + #define S_IWUSR _S_IWRITE + #define open _open + #define close _close + #define lseek _lseek + #define write _write + #define read _read -#ifdef _MSC_VER //for VS2010 -#include -#include -#define S_IRUSR _S_IREAD -#define S_IWUSR _S_IWRITE -#define open _open -#define close _close -#define lseek _lseek -#define write _write -#define read _read - -typedef int ssize_t; -typedef int pid_t; -typedef int mode_t; -typedef int64_t useconds_t; -#endif + typedef int ssize_t; + typedef int pid_t; + typedef int mode_t; + typedef int64_t useconds_t; + #endif -#define S_IRGRP 0 -#define S_IWGRP 0 -#define S_IXGRP 0 -#define S_IRWXG 0 -#define S_IROTH 0 -#define S_IWOTH 0 -#define S_IXOTH 0 -#define S_IRWXO 0 + #define S_IRGRP 0 + #define S_IWGRP 0 + #define S_IXGRP 0 + #define S_IRWXG 0 + #define S_IROTH 0 + #define S_IWOTH 0 + #define S_IXOTH 0 + #define S_IRWXO 0 -#define PRId64 "lld" + #define PRId64 "lld" -#define SOCKET_ETIME WSAETIMEDOUT -#define SOCKET_ECONNRESET WSAECONNRESET -#define SOCKET_ERRNO() WSAGetLastError() -#define SOCKET_RESET(x) x=INVALID_SOCKET -#define SOCKET_CLOSE(x) if(x!=INVALID_SOCKET){::closesocket(x);x=INVALID_SOCKET;} -#define SOCKET_VALID(x) (x!=INVALID_SOCKET) -#define SOCKET_BUFF(x) ((char*)x) -#define SOCKET_SETUP() socket_setup() -#define SOCKET_CLEANUP() socket_cleanup() + #define SOCKET_ETIME WSAETIMEDOUT + #define SOCKET_ECONNRESET WSAECONNRESET + #define SOCKET_ERRNO() WSAGetLastError() + #define SOCKET_RESET(x) x=INVALID_SOCKET + #define SOCKET_CLOSE(x) if(x!=INVALID_SOCKET){::closesocket(x);x=INVALID_SOCKET;} + #define SOCKET_VALID(x) (x!=INVALID_SOCKET) + #define SOCKET_BUFF(x) ((char*)x) + #define SOCKET_SETUP() socket_setup() + #define SOCKET_CLEANUP() socket_cleanup() -typedef uint32_t u_int32_t; -typedef uint8_t u_int8_t; -typedef int socklen_t; -struct iovec { - void* iov_base; /* Starting address */ - size_t iov_len; /* Length in bytes */ -}; + typedef uint32_t u_int32_t; + typedef uint8_t u_int8_t; + typedef int socklen_t; + struct iovec { + void* iov_base; /* Starting address */ + size_t iov_len; /* Length in bytes */ + }; -#define snprintf _snprintf -ssize_t writev(int fd, const struct iovec *iov, int iovcnt); -const char* inet_ntop(int af, const void *src, char *dst, socklen_t size); -int gettimeofday(struct timeval* tv, struct timezone* tz); -pid_t getpid(void); -int usleep(useconds_t usec); -int socket_setup(); -int socket_cleanup(); + #define snprintf _snprintf + ssize_t writev(int fd, const struct iovec *iov, int iovcnt); + const char* inet_ntop(int af, const void *src, char *dst, socklen_t size); + int gettimeofday(struct timeval* tv, struct timezone* tz); + pid_t getpid(void); + int usleep(useconds_t usec); + int socket_setup(); + int socket_cleanup(); #endif #endif //SRS_WIN_PORTING_H diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index ed421b1187..aa6aa41093 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -30,11 +30,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include -// success, ok -#if !defined(_WIN32) || defined(__CYGWIN__) //avoid redefine error on Windows +// for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 +#ifndef _WIN32 #define ERROR_SUCCESS 0 -#else -#include #endif /////////////////////////////////////////////////////// diff --git a/trunk/src/libs/srs_lib_simple_socket.cpp b/trunk/src/libs/srs_lib_simple_socket.cpp index 9ee98e91d4..08096e7147 100644 --- a/trunk/src/libs/srs_lib_simple_socket.cpp +++ b/trunk/src/libs/srs_lib_simple_socket.cpp @@ -45,28 +45,22 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SimpleSocketStream::SimpleSocketStream() { - //fd = -1; - SOCKET_RESET(fd); + SOCKET_RESET(fd); send_timeout = recv_timeout = ST_UTIME_NO_TIMEOUT; recv_bytes = send_bytes = 0; - SOCKET_SETUP(); + SOCKET_SETUP(); } SimpleSocketStream::~SimpleSocketStream() { - //if (fd != -1) { - // ::close(fd); - // fd = -1; - //} - SOCKET_CLOSE(fd); - SOCKET_CLEANUP(); + SOCKET_CLOSE(fd); + SOCKET_CLEANUP(); } int SimpleSocketStream::create_socket() { - //if((fd = ::socket(AF_INET, SOCK_STREAM, 0)) < 0){ - fd = ::socket(AF_INET, SOCK_STREAM, 0); - if(!SOCKET_VALID(fd)){ + fd = ::socket(AF_INET, SOCK_STREAM, 0); + if (!SOCKET_VALID(fd)) { return ERROR_SOCKET_CREATE; } diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index d212e17793..e2ca37a3b9 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -22,7 +22,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include -#include + #include // for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 @@ -34,6 +34,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include using namespace std; +#include #include #include #include @@ -107,56 +108,6 @@ struct Context } }; -// for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 -#ifdef _WIN32 - -/* - int open(const char *pathname, int flags) - { - return open(pathname, flags, 0); - } - - int open(const char *pathname, int flags, mode_t mode) - { - FILE* file = NULL; - - if ((flags & O_RDONLY) == O_RDONLY) { - file = fopen(pathname, "r"); - } else { - file = fopen(pathname, "w+"); - } - - if (file == NULL) { - return -1; - } - - return (int)file; - } - - int close(int fd) - { - FILE* file = (FILE*)fd; - return fclose(file); - } - - off_t lseek(int fd, off_t offset, int whence) - { - return (off_t)fseek((FILE*)fd, offset, whence); - } - - ssize_t write(int fd, const void *buf, size_t count) - { - return (ssize_t)fwrite(buf, count, 1, (FILE*)fd); - } - - ssize_t read(int fd, void *buf, size_t count) - { - return (ssize_t)fread(buf, count, 1, (FILE*)fd); - } -*/ - -#endif - int srs_librtmp_context_parse_uri(Context* context) { int ret = ERROR_SUCCESS; diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index 0330b2da03..fb39ddd325 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -31,53 +31,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include -// for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 -#if 0 - #define _CRT_SECURE_NO_WARNINGS - typedef unsigned long long u_int64_t; - typedef long long int64_t; - typedef unsigned int u_int32_t; - typedef int int32_t; - typedef unsigned char u_int8_t; - typedef char int8_t; - typedef unsigned short u_int16_t; - typedef short int16_t; - typedef int64_t ssize_t; - struct iovec { - void *iov_base; /* Starting address */ - size_t iov_len; /* Number of bytes to transfer */ - }; - #include - #include - int gettimeofday(struct timeval* tv, struct timezone* tz); - #define PRId64 "lld" - typedef int socklen_t; - const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); - typedef int mode_t; - #define S_IRUSR 0 - #define S_IWUSR 0 - #define S_IRGRP 0 - #define S_IWGRP 0 - #define S_IROTH 0 - int open(const char *pathname, int flags); - int open(const char *pathname, int flags, mode_t mode); - int close(int fd); - off_t lseek(int fd, off_t offset, int whence); - ssize_t write(int fd, const void *buf, size_t count); - ssize_t read(int fd, void *buf, size_t count); - typedef int pid_t; - pid_t getpid(void); - #define snprintf _snprintf - ssize_t writev(int fd, const struct iovec *iov, int iovcnt); - typedef int64_t useconds_t; - int usleep(useconds_t usec); -#endif - -#if defined(_WIN32) && !defined(__CYGWIN__) -#include -typedef uint32_t u_int32_t; -#endif - /** * srs-librtmp is a librtmp like library, * used to play/publish rtmp stream from/to rtmp server. diff --git a/trunk/winbuild/srs_auto_headers.hpp b/trunk/winbuild/srs_auto_headers.hpp index c1845c9392..4ebef350b4 100644 --- a/trunk/winbuild/srs_auto_headers.hpp +++ b/trunk/winbuild/srs_auto_headers.hpp @@ -19,7 +19,7 @@ #undef SRS_AUTO_HTTP_CALLBACK #undef SRS_AUTO_SSL #undef SRS_AUTO_FFMPEG_TOOL -#define SRS_AUTO_FFMPEG_STUB +#undef SRS_AUTO_FFMPEG_STUB #undef SRS_AUTO_TRANSCODE #undef SRS_AUTO_INGEST #undef SRS_AUTO_STAT diff --git a/trunk/winbuild/srs_play.vcxproj b/trunk/winbuild/srs_play.vcxproj index 83c1b875b9..0480153df8 100644 --- a/trunk/winbuild/srs_play.vcxproj +++ b/trunk/winbuild/srs_play.vcxproj @@ -77,9 +77,9 @@ - + - \ No newline at end of file + diff --git a/trunk/winbuild/srs_play.vcxproj.filters b/trunk/winbuild/srs_play.vcxproj.filters index 53cbd3e870..f6123616d0 100644 --- a/trunk/winbuild/srs_play.vcxproj.filters +++ b/trunk/winbuild/srs_play.vcxproj.filters @@ -15,8 +15,8 @@ - + Source Files - \ No newline at end of file + From 468941833ce8089f6bf2291dd7c078c75a6a20d4 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 27 Nov 2014 15:23:16 +0800 Subject: [PATCH 232/800] change srs version to 2.0.33 --- trunk/src/core/srs_core.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 9025d504c5..bffedbbb36 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 32 +#define VERSION_REVISION 33 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" From 4c0c3a9ada6e444a70a09715e9352f4901fa1cce Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 27 Nov 2014 15:29:24 +0800 Subject: [PATCH 233/800] refine merged code for allspace. --- trunk/src/core/srs_platform.hpp | 43 +++++++++++++++++++++++++-------- trunk/src/srs/srs.upp | 2 ++ 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/trunk/src/core/srs_platform.hpp b/trunk/src/core/srs_platform.hpp index f1cf6479a0..1bd61d1249 100644 --- a/trunk/src/core/srs_platform.hpp +++ b/trunk/src/core/srs_platform.hpp @@ -1,3 +1,26 @@ +/* +The MIT License (MIT) + +Copyright (c) 2014 allspace + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + #ifndef SRS_WIN_PORTING_H #define SRS_WIN_PORTING_H @@ -12,11 +35,11 @@ * while the _WIN32 includes both 32-bit and 64-bit */ #if !defined(_WIN32) || defined(__CYGWIN__) - #define SOCKET_ETIME ETIME - #define SOCKET_ECONNRESET ECONNRESET + #define SOCKET_ETIME ETIME + #define SOCKET_ECONNRESET ECONNRESET #define SOCKET int - #define SOCKET_ERRNO() errno + #define SOCKET_ERRNO() errno #define SOCKET_RESET(fd) fd = -1; (void)0 #define SOCKET_CLOSE(fd) \ if (fd > 0) {\ @@ -25,7 +48,7 @@ } \ (void)0 #define SOCKET_VALID(x) (x > 0) - #define SOCKET_SETUP() (void)0 + #define SOCKET_SETUP() (void)0 #define SOCKET_CLEANUP() (void)0 #else /*on windows, but not on cygwin*/ #include @@ -33,7 +56,7 @@ #include #include - #ifdef _MSC_VER //for VS2010 + #ifdef _MSC_VER //for VS2010 #include #include #define S_IRUSR _S_IREAD @@ -61,14 +84,14 @@ #define PRId64 "lld" - #define SOCKET_ETIME WSAETIMEDOUT - #define SOCKET_ECONNRESET WSAECONNRESET - #define SOCKET_ERRNO() WSAGetLastError() + #define SOCKET_ETIME WSAETIMEDOUT + #define SOCKET_ECONNRESET WSAECONNRESET + #define SOCKET_ERRNO() WSAGetLastError() #define SOCKET_RESET(x) x=INVALID_SOCKET #define SOCKET_CLOSE(x) if(x!=INVALID_SOCKET){::closesocket(x);x=INVALID_SOCKET;} #define SOCKET_VALID(x) (x!=INVALID_SOCKET) - #define SOCKET_BUFF(x) ((char*)x) - #define SOCKET_SETUP() socket_setup() + #define SOCKET_BUFF(x) ((char*)x) + #define SOCKET_SETUP() socket_setup() #define SOCKET_CLEANUP() socket_cleanup() typedef uint32_t u_int32_t; diff --git a/trunk/src/srs/srs.upp b/trunk/src/srs/srs.upp index 94f0fd3397..d2f18d7c0d 100755 --- a/trunk/src/srs/srs.upp +++ b/trunk/src/srs/srs.upp @@ -15,6 +15,8 @@ file ..\core\srs_core.cpp, ..\core\srs_core_autofree.hpp, ..\core\srs_core_autofree.cpp, + ..\core\srs_platform.hpp, + ..\core\srs_platform.cpp, kernel readonly separator, ..\kernel\srs_kernel_buffer.hpp, ..\kernel\srs_kernel_buffer.cpp, From f8a2c22f3a8b6859385e1907baa9b69d366f3738 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 27 Nov 2014 16:16:04 +0800 Subject: [PATCH 234/800] remove vs2010 files from master --- trunk/winbuild/srs-librtmp.def | 46 ------ trunk/winbuild/srs-librtmp_vs2010.sln | 26 --- trunk/winbuild/srs-librtmp_vs2010.vcxproj | 137 --------------- .../srs-librtmp_vs2010.vcxproj.filters | 156 ------------------ trunk/winbuild/srs_auto_headers.hpp | 56 ------- trunk/winbuild/srs_play.vcxproj | 85 ---------- trunk/winbuild/srs_play.vcxproj.filters | 22 --- 7 files changed, 528 deletions(-) delete mode 100644 trunk/winbuild/srs-librtmp.def delete mode 100644 trunk/winbuild/srs-librtmp_vs2010.sln delete mode 100644 trunk/winbuild/srs-librtmp_vs2010.vcxproj delete mode 100644 trunk/winbuild/srs-librtmp_vs2010.vcxproj.filters delete mode 100644 trunk/winbuild/srs_auto_headers.hpp delete mode 100644 trunk/winbuild/srs_play.vcxproj delete mode 100644 trunk/winbuild/srs_play.vcxproj.filters diff --git a/trunk/winbuild/srs-librtmp.def b/trunk/winbuild/srs-librtmp.def deleted file mode 100644 index b35612186d..0000000000 --- a/trunk/winbuild/srs-librtmp.def +++ /dev/null @@ -1,46 +0,0 @@ -LIBRARY -EXPORTS -srs_rtmp_create -srs_rtmp_create2 -srs_rtmp_destroy -srs_rtmp_handshake -srs_rtmp_connect_app -srs_rtmp_connect_app2 -srs_rtmp_play_stream -srs_rtmp_publish_stream -srs_rtmp_read_packet -srs_rtmp_write_packet -srs_version_major -srs_version_minor -srs_version_revision -srs_utils_parse_timestamp -srs_human_format_time -srs_human_print_rtmp_packet -srs_rtmp_bandwidth_check -; -__srs_rtmp_do_simple_handshake -__srs_rtmp_connect_server -__srs_rtmp_dns_resolve - -;;;;;;;;for flv read/write;;;;;;;; -srs_flv_open_read -srs_flv_open_write -srs_flv_close -srs_flv_read_header -srs_flv_read_tag_header -srs_flv_read_tag_data -srs_flv_write_header -srs_flv_write_tag -srs_flv_size_tag -srs_flv_tellg -srs_flv_lseek -srs_flv_is_eof -srs_flv_is_sequence_header -srs_flv_is_keyframe - -;;;;;;;;for h264 read/write;;;;;;;; -srs_h264_write_raw_frames -srs_h264_is_dvbsp_error -srs_h264_is_duplicated_sps_error -srs_h264_is_duplicated_pps_error -srs_h264_startswith_annexb diff --git a/trunk/winbuild/srs-librtmp_vs2010.sln b/trunk/winbuild/srs-librtmp_vs2010.sln deleted file mode 100644 index 204bf81ac7..0000000000 --- a/trunk/winbuild/srs-librtmp_vs2010.sln +++ /dev/null @@ -1,26 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "srs-librtmp", "srs-librtmp_vs2010.vcxproj", "{051CC3D8-5A99-4534-90EE-AED40EDDEEB2}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "srs_play", "srs_play.vcxproj", "{5149B9A9-5085-4A10-AD6F-23FBE6854390}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {051CC3D8-5A99-4534-90EE-AED40EDDEEB2}.Debug|Win32.ActiveCfg = Debug|Win32 - {051CC3D8-5A99-4534-90EE-AED40EDDEEB2}.Debug|Win32.Build.0 = Debug|Win32 - {051CC3D8-5A99-4534-90EE-AED40EDDEEB2}.Release|Win32.ActiveCfg = Release|Win32 - {051CC3D8-5A99-4534-90EE-AED40EDDEEB2}.Release|Win32.Build.0 = Release|Win32 - {5149B9A9-5085-4A10-AD6F-23FBE6854390}.Debug|Win32.ActiveCfg = Debug|Win32 - {5149B9A9-5085-4A10-AD6F-23FBE6854390}.Debug|Win32.Build.0 = Debug|Win32 - {5149B9A9-5085-4A10-AD6F-23FBE6854390}.Release|Win32.ActiveCfg = Release|Win32 - {5149B9A9-5085-4A10-AD6F-23FBE6854390}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/trunk/winbuild/srs-librtmp_vs2010.vcxproj b/trunk/winbuild/srs-librtmp_vs2010.vcxproj deleted file mode 100644 index 08cc6ead06..0000000000 --- a/trunk/winbuild/srs-librtmp_vs2010.vcxproj +++ /dev/null @@ -1,137 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {051CC3D8-5A99-4534-90EE-AED40EDDEEB2} - Win32Proj - srslibrtmp - srs-librtmp - - - - DynamicLibrary - true - Unicode - - - DynamicLibrary - false - true - Unicode - - - - - - - - - - - - - true - - - false - - - - - - Level3 - Disabled - WIN32;_DEBUG;_WINDOWS;_USRDLL;SRSLIBRTMP_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - ..\src\rtmp;..\src\libs;..\src\kernel;..\src\core;.\;%(AdditionalIncludeDirectories) - - - Windows - true - ws2_32.lib;%(AdditionalDependencies) - srs-librtmp.def - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_WINDOWS;_USRDLL;SRSLIBRTMP_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - ..\src\rtmp;..\src\libs;..\src\kernel;..\src\core;.\;%(AdditionalIncludeDirectories) - - - Windows - true - true - true - srs-librtmp.def - ws2_32.lib;%(AdditionalDependencies) - - - - - - \ No newline at end of file diff --git a/trunk/winbuild/srs-librtmp_vs2010.vcxproj.filters b/trunk/winbuild/srs-librtmp_vs2010.vcxproj.filters deleted file mode 100644 index 5c3da32199..0000000000 --- a/trunk/winbuild/srs-librtmp_vs2010.vcxproj.filters +++ /dev/null @@ -1,156 +0,0 @@ - - - - - {8e33c6db-7a35-4d09-92d0-cc28491abbd6} - - - {eac81f26-3b4f-4905-85c5-2c0b915107b0} - - - {cbb45db3-c682-48bd-9a8c-1ae081aeddbf} - - - {6a284cec-b287-4aeb-b991-e11ebb4be6ec} - - - - - core - - - core - - - core - - - kernel - - - kernel - - - kernel - - - kernel - - - kernel - - - kernel - - - kernel - - - kernel - - - kernel - - - libs - - - libs - - - libs - - - rtmp - - - rtmp - - - rtmp - - - rtmp - - - rtmp - - - rtmp - - - rtmp - - - - - core - - - core - - - core - - - kernel - - - kernel - - - kernel - - - kernel - - - kernel - - - kernel - - - kernel - - - kernel - - - kernel - - - libs - - - libs - - - libs - - - rtmp - - - rtmp - - - rtmp - - - rtmp - - - rtmp - - - rtmp - - - rtmp - - - - - - \ No newline at end of file diff --git a/trunk/winbuild/srs_auto_headers.hpp b/trunk/winbuild/srs_auto_headers.hpp deleted file mode 100644 index 4ebef350b4..0000000000 --- a/trunk/winbuild/srs_auto_headers.hpp +++ /dev/null @@ -1,56 +0,0 @@ -// auto generated by configure -#ifndef SRS_AUTO_HEADER_HPP -#define SRS_AUTO_HEADER_HPP - -#define SRS_AUTO_BUILD_TS "1416731672" -#define SRS_AUTO_BUILD_DATE "2014-11-23 16:34:32" -#define SRS_AUTO_UNAME "WIN_NT-6.1 WZY-PC2 1.0.18(0.48/3/2) 2012-11-21 22:34 i686 WINNT" -#define SRS_AUTO_USER_CONFIGURE "--x86-x64 --export-librtmp-project=../srs-librtmp" -#define SRS_AUTO_CONFIGURE "--prefix=/usr/local/srs --without-hls --without-dvr --without-nginx --without-ssl --without-ffmpeg --without-transcode --without-ingest --without-stat --without-http-callback --without-http-server --without-http-api --with-librtmp --with-research --without-utest --without-gperf --without-gmc --without-gmp --without-gcp --without-gprof --without-arm-ubuntu12 --without-mips-ubuntu12 --log-trace" - -#define SRS_AUTO_EMBEDED_TOOL_CHAIN "normal x86/x64 gcc" - -#undef SRS_AUTO_HTTP_PARSER -#undef SRS_AUTO_HTTP_SERVER -#undef SRS_AUTO_HTTP_API -#undef SRS_AUTO_NGINX -#undef SRS_AUTO_DVR -#undef SRS_AUTO_HLS -#undef SRS_AUTO_HTTP_CALLBACK -#undef SRS_AUTO_SSL -#undef SRS_AUTO_FFMPEG_TOOL -#undef SRS_AUTO_FFMPEG_STUB -#undef SRS_AUTO_TRANSCODE -#undef SRS_AUTO_INGEST -#undef SRS_AUTO_STAT -#undef SRS_AUTO_GPERF -#undef SRS_AUTO_GPERF_MC -#undef SRS_AUTO_GPERF_MP -#undef SRS_AUTO_GPERF_CP -#undef SRS_AUTO_EMBEDED_CPU -#undef SRS_AUTO_ARM_UBUNTU12 -#undef SRS_AUTO_MIPS_UBUNTU12 - -#undef SRS_AUTO_VERBOSE -#undef SRS_AUTO_INFO -#define SRS_AUTO_TRACE - -#define SRS_AUTO_PREFIX "/usr/local/srs" - -#define SRS_AUTO_CONSTRIBUTORS "\ -winlin \ -wenjie.zhao<740936897@qq.com> \ -xiangcheng.liu \ -naijia.liu \ -alcoholyi \ -byteman \ -chad.wang \ -suhetao \ -Johnny \ -karthikeyan \ -StevenLiu \ -zhengfl \ -" - -#endif - diff --git a/trunk/winbuild/srs_play.vcxproj b/trunk/winbuild/srs_play.vcxproj deleted file mode 100644 index 0480153df8..0000000000 --- a/trunk/winbuild/srs_play.vcxproj +++ /dev/null @@ -1,85 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {5149B9A9-5085-4A10-AD6F-23FBE6854390} - Win32Proj - srs_play - - - - Application - true - Unicode - - - Application - false - true - Unicode - - - - - - - - - - - - - true - - - false - - - - - - Level3 - Disabled - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - ..\src\core;..\src\libs;%(AdditionalIncludeDirectories) - - - Console - true - srs-librtmp.lib;%(AdditionalDependencies) - ./debug;%(AdditionalLibraryDirectories) - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - - - Console - true - true - true - - - - - - - - - diff --git a/trunk/winbuild/srs_play.vcxproj.filters b/trunk/winbuild/srs_play.vcxproj.filters deleted file mode 100644 index f6123616d0..0000000000 --- a/trunk/winbuild/srs_play.vcxproj.filters +++ /dev/null @@ -1,22 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Source Files - - - From 62b861718fd65387427df7a74cdb6ec4e54b7757 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 27 Nov 2014 17:02:30 +0800 Subject: [PATCH 235/800] Revert "remove vs2010 files from master" This reverts commit f8a2c22f3a8b6859385e1907baa9b69d366f3738. --- trunk/winbuild/srs-librtmp.def | 46 ++++++ trunk/winbuild/srs-librtmp_vs2010.sln | 26 +++ trunk/winbuild/srs-librtmp_vs2010.vcxproj | 137 +++++++++++++++ .../srs-librtmp_vs2010.vcxproj.filters | 156 ++++++++++++++++++ trunk/winbuild/srs_auto_headers.hpp | 56 +++++++ trunk/winbuild/srs_play.vcxproj | 85 ++++++++++ trunk/winbuild/srs_play.vcxproj.filters | 22 +++ 7 files changed, 528 insertions(+) create mode 100644 trunk/winbuild/srs-librtmp.def create mode 100644 trunk/winbuild/srs-librtmp_vs2010.sln create mode 100644 trunk/winbuild/srs-librtmp_vs2010.vcxproj create mode 100644 trunk/winbuild/srs-librtmp_vs2010.vcxproj.filters create mode 100644 trunk/winbuild/srs_auto_headers.hpp create mode 100644 trunk/winbuild/srs_play.vcxproj create mode 100644 trunk/winbuild/srs_play.vcxproj.filters diff --git a/trunk/winbuild/srs-librtmp.def b/trunk/winbuild/srs-librtmp.def new file mode 100644 index 0000000000..b35612186d --- /dev/null +++ b/trunk/winbuild/srs-librtmp.def @@ -0,0 +1,46 @@ +LIBRARY +EXPORTS +srs_rtmp_create +srs_rtmp_create2 +srs_rtmp_destroy +srs_rtmp_handshake +srs_rtmp_connect_app +srs_rtmp_connect_app2 +srs_rtmp_play_stream +srs_rtmp_publish_stream +srs_rtmp_read_packet +srs_rtmp_write_packet +srs_version_major +srs_version_minor +srs_version_revision +srs_utils_parse_timestamp +srs_human_format_time +srs_human_print_rtmp_packet +srs_rtmp_bandwidth_check +; +__srs_rtmp_do_simple_handshake +__srs_rtmp_connect_server +__srs_rtmp_dns_resolve + +;;;;;;;;for flv read/write;;;;;;;; +srs_flv_open_read +srs_flv_open_write +srs_flv_close +srs_flv_read_header +srs_flv_read_tag_header +srs_flv_read_tag_data +srs_flv_write_header +srs_flv_write_tag +srs_flv_size_tag +srs_flv_tellg +srs_flv_lseek +srs_flv_is_eof +srs_flv_is_sequence_header +srs_flv_is_keyframe + +;;;;;;;;for h264 read/write;;;;;;;; +srs_h264_write_raw_frames +srs_h264_is_dvbsp_error +srs_h264_is_duplicated_sps_error +srs_h264_is_duplicated_pps_error +srs_h264_startswith_annexb diff --git a/trunk/winbuild/srs-librtmp_vs2010.sln b/trunk/winbuild/srs-librtmp_vs2010.sln new file mode 100644 index 0000000000..204bf81ac7 --- /dev/null +++ b/trunk/winbuild/srs-librtmp_vs2010.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "srs-librtmp", "srs-librtmp_vs2010.vcxproj", "{051CC3D8-5A99-4534-90EE-AED40EDDEEB2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "srs_play", "srs_play.vcxproj", "{5149B9A9-5085-4A10-AD6F-23FBE6854390}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {051CC3D8-5A99-4534-90EE-AED40EDDEEB2}.Debug|Win32.ActiveCfg = Debug|Win32 + {051CC3D8-5A99-4534-90EE-AED40EDDEEB2}.Debug|Win32.Build.0 = Debug|Win32 + {051CC3D8-5A99-4534-90EE-AED40EDDEEB2}.Release|Win32.ActiveCfg = Release|Win32 + {051CC3D8-5A99-4534-90EE-AED40EDDEEB2}.Release|Win32.Build.0 = Release|Win32 + {5149B9A9-5085-4A10-AD6F-23FBE6854390}.Debug|Win32.ActiveCfg = Debug|Win32 + {5149B9A9-5085-4A10-AD6F-23FBE6854390}.Debug|Win32.Build.0 = Debug|Win32 + {5149B9A9-5085-4A10-AD6F-23FBE6854390}.Release|Win32.ActiveCfg = Release|Win32 + {5149B9A9-5085-4A10-AD6F-23FBE6854390}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/trunk/winbuild/srs-librtmp_vs2010.vcxproj b/trunk/winbuild/srs-librtmp_vs2010.vcxproj new file mode 100644 index 0000000000..08cc6ead06 --- /dev/null +++ b/trunk/winbuild/srs-librtmp_vs2010.vcxproj @@ -0,0 +1,137 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {051CC3D8-5A99-4534-90EE-AED40EDDEEB2} + Win32Proj + srslibrtmp + srs-librtmp + + + + DynamicLibrary + true + Unicode + + + DynamicLibrary + false + true + Unicode + + + + + + + + + + + + + true + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;SRSLIBRTMP_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + ..\src\rtmp;..\src\libs;..\src\kernel;..\src\core;.\;%(AdditionalIncludeDirectories) + + + Windows + true + ws2_32.lib;%(AdditionalDependencies) + srs-librtmp.def + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;SRSLIBRTMP_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + ..\src\rtmp;..\src\libs;..\src\kernel;..\src\core;.\;%(AdditionalIncludeDirectories) + + + Windows + true + true + true + srs-librtmp.def + ws2_32.lib;%(AdditionalDependencies) + + + + + + \ No newline at end of file diff --git a/trunk/winbuild/srs-librtmp_vs2010.vcxproj.filters b/trunk/winbuild/srs-librtmp_vs2010.vcxproj.filters new file mode 100644 index 0000000000..5c3da32199 --- /dev/null +++ b/trunk/winbuild/srs-librtmp_vs2010.vcxproj.filters @@ -0,0 +1,156 @@ + + + + + {8e33c6db-7a35-4d09-92d0-cc28491abbd6} + + + {eac81f26-3b4f-4905-85c5-2c0b915107b0} + + + {cbb45db3-c682-48bd-9a8c-1ae081aeddbf} + + + {6a284cec-b287-4aeb-b991-e11ebb4be6ec} + + + + + core + + + core + + + core + + + kernel + + + kernel + + + kernel + + + kernel + + + kernel + + + kernel + + + kernel + + + kernel + + + kernel + + + libs + + + libs + + + libs + + + rtmp + + + rtmp + + + rtmp + + + rtmp + + + rtmp + + + rtmp + + + rtmp + + + + + core + + + core + + + core + + + kernel + + + kernel + + + kernel + + + kernel + + + kernel + + + kernel + + + kernel + + + kernel + + + kernel + + + libs + + + libs + + + libs + + + rtmp + + + rtmp + + + rtmp + + + rtmp + + + rtmp + + + rtmp + + + rtmp + + + + + + \ No newline at end of file diff --git a/trunk/winbuild/srs_auto_headers.hpp b/trunk/winbuild/srs_auto_headers.hpp new file mode 100644 index 0000000000..4ebef350b4 --- /dev/null +++ b/trunk/winbuild/srs_auto_headers.hpp @@ -0,0 +1,56 @@ +// auto generated by configure +#ifndef SRS_AUTO_HEADER_HPP +#define SRS_AUTO_HEADER_HPP + +#define SRS_AUTO_BUILD_TS "1416731672" +#define SRS_AUTO_BUILD_DATE "2014-11-23 16:34:32" +#define SRS_AUTO_UNAME "WIN_NT-6.1 WZY-PC2 1.0.18(0.48/3/2) 2012-11-21 22:34 i686 WINNT" +#define SRS_AUTO_USER_CONFIGURE "--x86-x64 --export-librtmp-project=../srs-librtmp" +#define SRS_AUTO_CONFIGURE "--prefix=/usr/local/srs --without-hls --without-dvr --without-nginx --without-ssl --without-ffmpeg --without-transcode --without-ingest --without-stat --without-http-callback --without-http-server --without-http-api --with-librtmp --with-research --without-utest --without-gperf --without-gmc --without-gmp --without-gcp --without-gprof --without-arm-ubuntu12 --without-mips-ubuntu12 --log-trace" + +#define SRS_AUTO_EMBEDED_TOOL_CHAIN "normal x86/x64 gcc" + +#undef SRS_AUTO_HTTP_PARSER +#undef SRS_AUTO_HTTP_SERVER +#undef SRS_AUTO_HTTP_API +#undef SRS_AUTO_NGINX +#undef SRS_AUTO_DVR +#undef SRS_AUTO_HLS +#undef SRS_AUTO_HTTP_CALLBACK +#undef SRS_AUTO_SSL +#undef SRS_AUTO_FFMPEG_TOOL +#undef SRS_AUTO_FFMPEG_STUB +#undef SRS_AUTO_TRANSCODE +#undef SRS_AUTO_INGEST +#undef SRS_AUTO_STAT +#undef SRS_AUTO_GPERF +#undef SRS_AUTO_GPERF_MC +#undef SRS_AUTO_GPERF_MP +#undef SRS_AUTO_GPERF_CP +#undef SRS_AUTO_EMBEDED_CPU +#undef SRS_AUTO_ARM_UBUNTU12 +#undef SRS_AUTO_MIPS_UBUNTU12 + +#undef SRS_AUTO_VERBOSE +#undef SRS_AUTO_INFO +#define SRS_AUTO_TRACE + +#define SRS_AUTO_PREFIX "/usr/local/srs" + +#define SRS_AUTO_CONSTRIBUTORS "\ +winlin \ +wenjie.zhao<740936897@qq.com> \ +xiangcheng.liu \ +naijia.liu \ +alcoholyi \ +byteman \ +chad.wang \ +suhetao \ +Johnny \ +karthikeyan \ +StevenLiu \ +zhengfl \ +" + +#endif + diff --git a/trunk/winbuild/srs_play.vcxproj b/trunk/winbuild/srs_play.vcxproj new file mode 100644 index 0000000000..0480153df8 --- /dev/null +++ b/trunk/winbuild/srs_play.vcxproj @@ -0,0 +1,85 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {5149B9A9-5085-4A10-AD6F-23FBE6854390} + Win32Proj + srs_play + + + + Application + true + Unicode + + + Application + false + true + Unicode + + + + + + + + + + + + + true + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + ..\src\core;..\src\libs;%(AdditionalIncludeDirectories) + + + Console + true + srs-librtmp.lib;%(AdditionalDependencies) + ./debug;%(AdditionalLibraryDirectories) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + + diff --git a/trunk/winbuild/srs_play.vcxproj.filters b/trunk/winbuild/srs_play.vcxproj.filters new file mode 100644 index 0000000000..f6123616d0 --- /dev/null +++ b/trunk/winbuild/srs_play.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + From eb4e7aad69eab76c7a759ed69d8cf59fccf25f4a Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 27 Nov 2014 18:20:13 +0800 Subject: [PATCH 236/800] try to build srs-librtmp on vs2010, failed. --- trunk/src/core/srs_core.hpp | 5 + trunk/src/core/srs_platform.hpp | 5 - trunk/src/libs/srs_librtmp.hpp | 5 + trunk/winbuild/srs_play.cpp | 89 ++++++++++++ trunk/winbuild/srs_play.vcxproj | 173 ++++++++++++------------ trunk/winbuild/srs_play.vcxproj.filters | 49 ++++--- 6 files changed, 214 insertions(+), 112 deletions(-) create mode 100644 trunk/winbuild/srs_play.cpp mode change 100644 => 100755 trunk/winbuild/srs_play.vcxproj mode change 100644 => 100755 trunk/winbuild/srs_play.vcxproj.filters diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index bffedbbb36..785d12d502 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -72,6 +72,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define __STDC_FORMAT_MACROS #endif +// for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 +#if defined(_WIN32) + #include +#endif + // for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 #ifndef _WIN32 #include diff --git a/trunk/src/core/srs_platform.hpp b/trunk/src/core/srs_platform.hpp index 1bd61d1249..2604a0e88c 100644 --- a/trunk/src/core/srs_platform.hpp +++ b/trunk/src/core/srs_platform.hpp @@ -24,11 +24,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifndef SRS_WIN_PORTING_H #define SRS_WIN_PORTING_H -// for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 -#if defined(_WIN32) - #include -#endif - /** * for linux like, * for example, not on windows or it's cygwin. diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index fb39ddd325..206a9d9c49 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -30,6 +30,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include +#ifdef _WIN32 + typedef long int int64_t; + typedef unsigned int u_int32_t; + typedef int int32_t; +#endif /** * srs-librtmp is a librtmp like library, diff --git a/trunk/winbuild/srs_play.cpp b/trunk/winbuild/srs_play.cpp new file mode 100644 index 0000000000..2891e3a3e4 --- /dev/null +++ b/trunk/winbuild/srs_play.cpp @@ -0,0 +1,89 @@ +/* +The MIT License (MIT) + +Copyright (c) 2013-2014 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +/** +gcc srs_play.c ../../objs/lib/srs_librtmp.a -g -O0 -lstdc++ -o srs_play +*/ + +#include +#include + +#include "srs_librtmp.hpp" + +int main(int argc, char** argv) +{ + printf("suck rtmp stream like rtmpdump\n"); + printf("srs(simple-rtmp-server) client librtmp library.\n"); + printf("version: %d.%d.%d\n", srs_version_major(), srs_version_minor(), srs_version_revision()); + + if (argc <= 1) { + printf("Usage: %s \n" + " rtmp_url RTMP stream url to play\n" + "For example:\n" + " %s rtmp://127.0.0.1:1935/live/livestream\n", + argv[0], argv[0]); + exit(-1); + } + + srs_human_trace("rtmp url: %s", argv[1]); + srs_rtmp_t rtmp = srs_rtmp_create(argv[1]); + + if (srs_rtmp_handshake(rtmp) != 0) { + srs_human_trace("simple handshake failed."); + goto rtmp_destroy; + } + srs_human_trace("simple handshake success"); + + if (srs_rtmp_connect_app(rtmp) != 0) { + srs_human_trace("connect vhost/app failed."); + goto rtmp_destroy; + } + srs_human_trace("connect vhost/app success"); + + if (srs_rtmp_play_stream(rtmp) != 0) { + srs_human_trace("play stream failed."); + goto rtmp_destroy; + } + srs_human_trace("play stream success"); + + for (;;) { + int size; + char type; + char* data; + u_int32_t timestamp; + + if (srs_rtmp_read_packet(rtmp, &type, ×tamp, &data, &size) != 0) { + goto rtmp_destroy; + } + + if (srs_human_print_rtmp_packet(type, timestamp, data, size) != 0) { + goto rtmp_destroy; + } + + free(data); + } + +rtmp_destroy: + srs_rtmp_destroy(rtmp); + + return 0; +} diff --git a/trunk/winbuild/srs_play.vcxproj b/trunk/winbuild/srs_play.vcxproj old mode 100644 new mode 100755 index 0480153df8..22d653d2bd --- a/trunk/winbuild/srs_play.vcxproj +++ b/trunk/winbuild/srs_play.vcxproj @@ -1,85 +1,88 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {5149B9A9-5085-4A10-AD6F-23FBE6854390} - Win32Proj - srs_play - - - - Application - true - Unicode - - - Application - false - true - Unicode - - - - - - - - - - - - - true - - - false - - - - - - Level3 - Disabled - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - ..\src\core;..\src\libs;%(AdditionalIncludeDirectories) - - - Console - true - srs-librtmp.lib;%(AdditionalDependencies) - ./debug;%(AdditionalLibraryDirectories) - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - - - Console - true - true - true - - - - - - - - - + + + + + Debug + Win32 + + + Release + Win32 + + + + {5149B9A9-5085-4A10-AD6F-23FBE6854390} + Win32Proj + srs_play + + + + Application + true + Unicode + + + Application + false + true + Unicode + + + + + + + + + + + + + true + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + ..\src\core;..\src\libs;%(AdditionalIncludeDirectories) + + + Console + true + srs-librtmp.lib;%(AdditionalDependencies) + ./debug;%(AdditionalLibraryDirectories) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + + + + + \ No newline at end of file diff --git a/trunk/winbuild/srs_play.vcxproj.filters b/trunk/winbuild/srs_play.vcxproj.filters old mode 100644 new mode 100755 index f6123616d0..c66bb8bc03 --- a/trunk/winbuild/srs_play.vcxproj.filters +++ b/trunk/winbuild/srs_play.vcxproj.filters @@ -1,22 +1,27 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Source Files - - - + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + + + Source Files + + + \ No newline at end of file From bb85a0c856504b0e921e204b2c6a06d717e1e410 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 27 Nov 2014 18:31:25 +0800 Subject: [PATCH 237/800] for bug #229, remove the windows vs2010 build files for srs-librtmp build failed on windows. --- trunk/winbuild/srs-librtmp.def | 46 ------ trunk/winbuild/srs-librtmp_vs2010.sln | 26 --- trunk/winbuild/srs-librtmp_vs2010.vcxproj | 137 --------------- .../srs-librtmp_vs2010.vcxproj.filters | 156 ------------------ trunk/winbuild/srs_auto_headers.hpp | 56 ------- trunk/winbuild/srs_play.cpp | 89 ---------- trunk/winbuild/srs_play.vcxproj | 88 ---------- trunk/winbuild/srs_play.vcxproj.filters | 27 --- 8 files changed, 625 deletions(-) delete mode 100644 trunk/winbuild/srs-librtmp.def delete mode 100644 trunk/winbuild/srs-librtmp_vs2010.sln delete mode 100644 trunk/winbuild/srs-librtmp_vs2010.vcxproj delete mode 100644 trunk/winbuild/srs-librtmp_vs2010.vcxproj.filters delete mode 100644 trunk/winbuild/srs_auto_headers.hpp delete mode 100644 trunk/winbuild/srs_play.cpp delete mode 100755 trunk/winbuild/srs_play.vcxproj delete mode 100755 trunk/winbuild/srs_play.vcxproj.filters diff --git a/trunk/winbuild/srs-librtmp.def b/trunk/winbuild/srs-librtmp.def deleted file mode 100644 index b35612186d..0000000000 --- a/trunk/winbuild/srs-librtmp.def +++ /dev/null @@ -1,46 +0,0 @@ -LIBRARY -EXPORTS -srs_rtmp_create -srs_rtmp_create2 -srs_rtmp_destroy -srs_rtmp_handshake -srs_rtmp_connect_app -srs_rtmp_connect_app2 -srs_rtmp_play_stream -srs_rtmp_publish_stream -srs_rtmp_read_packet -srs_rtmp_write_packet -srs_version_major -srs_version_minor -srs_version_revision -srs_utils_parse_timestamp -srs_human_format_time -srs_human_print_rtmp_packet -srs_rtmp_bandwidth_check -; -__srs_rtmp_do_simple_handshake -__srs_rtmp_connect_server -__srs_rtmp_dns_resolve - -;;;;;;;;for flv read/write;;;;;;;; -srs_flv_open_read -srs_flv_open_write -srs_flv_close -srs_flv_read_header -srs_flv_read_tag_header -srs_flv_read_tag_data -srs_flv_write_header -srs_flv_write_tag -srs_flv_size_tag -srs_flv_tellg -srs_flv_lseek -srs_flv_is_eof -srs_flv_is_sequence_header -srs_flv_is_keyframe - -;;;;;;;;for h264 read/write;;;;;;;; -srs_h264_write_raw_frames -srs_h264_is_dvbsp_error -srs_h264_is_duplicated_sps_error -srs_h264_is_duplicated_pps_error -srs_h264_startswith_annexb diff --git a/trunk/winbuild/srs-librtmp_vs2010.sln b/trunk/winbuild/srs-librtmp_vs2010.sln deleted file mode 100644 index 204bf81ac7..0000000000 --- a/trunk/winbuild/srs-librtmp_vs2010.sln +++ /dev/null @@ -1,26 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "srs-librtmp", "srs-librtmp_vs2010.vcxproj", "{051CC3D8-5A99-4534-90EE-AED40EDDEEB2}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "srs_play", "srs_play.vcxproj", "{5149B9A9-5085-4A10-AD6F-23FBE6854390}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {051CC3D8-5A99-4534-90EE-AED40EDDEEB2}.Debug|Win32.ActiveCfg = Debug|Win32 - {051CC3D8-5A99-4534-90EE-AED40EDDEEB2}.Debug|Win32.Build.0 = Debug|Win32 - {051CC3D8-5A99-4534-90EE-AED40EDDEEB2}.Release|Win32.ActiveCfg = Release|Win32 - {051CC3D8-5A99-4534-90EE-AED40EDDEEB2}.Release|Win32.Build.0 = Release|Win32 - {5149B9A9-5085-4A10-AD6F-23FBE6854390}.Debug|Win32.ActiveCfg = Debug|Win32 - {5149B9A9-5085-4A10-AD6F-23FBE6854390}.Debug|Win32.Build.0 = Debug|Win32 - {5149B9A9-5085-4A10-AD6F-23FBE6854390}.Release|Win32.ActiveCfg = Release|Win32 - {5149B9A9-5085-4A10-AD6F-23FBE6854390}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/trunk/winbuild/srs-librtmp_vs2010.vcxproj b/trunk/winbuild/srs-librtmp_vs2010.vcxproj deleted file mode 100644 index 08cc6ead06..0000000000 --- a/trunk/winbuild/srs-librtmp_vs2010.vcxproj +++ /dev/null @@ -1,137 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {051CC3D8-5A99-4534-90EE-AED40EDDEEB2} - Win32Proj - srslibrtmp - srs-librtmp - - - - DynamicLibrary - true - Unicode - - - DynamicLibrary - false - true - Unicode - - - - - - - - - - - - - true - - - false - - - - - - Level3 - Disabled - WIN32;_DEBUG;_WINDOWS;_USRDLL;SRSLIBRTMP_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - ..\src\rtmp;..\src\libs;..\src\kernel;..\src\core;.\;%(AdditionalIncludeDirectories) - - - Windows - true - ws2_32.lib;%(AdditionalDependencies) - srs-librtmp.def - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_WINDOWS;_USRDLL;SRSLIBRTMP_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - ..\src\rtmp;..\src\libs;..\src\kernel;..\src\core;.\;%(AdditionalIncludeDirectories) - - - Windows - true - true - true - srs-librtmp.def - ws2_32.lib;%(AdditionalDependencies) - - - - - - \ No newline at end of file diff --git a/trunk/winbuild/srs-librtmp_vs2010.vcxproj.filters b/trunk/winbuild/srs-librtmp_vs2010.vcxproj.filters deleted file mode 100644 index 5c3da32199..0000000000 --- a/trunk/winbuild/srs-librtmp_vs2010.vcxproj.filters +++ /dev/null @@ -1,156 +0,0 @@ - - - - - {8e33c6db-7a35-4d09-92d0-cc28491abbd6} - - - {eac81f26-3b4f-4905-85c5-2c0b915107b0} - - - {cbb45db3-c682-48bd-9a8c-1ae081aeddbf} - - - {6a284cec-b287-4aeb-b991-e11ebb4be6ec} - - - - - core - - - core - - - core - - - kernel - - - kernel - - - kernel - - - kernel - - - kernel - - - kernel - - - kernel - - - kernel - - - kernel - - - libs - - - libs - - - libs - - - rtmp - - - rtmp - - - rtmp - - - rtmp - - - rtmp - - - rtmp - - - rtmp - - - - - core - - - core - - - core - - - kernel - - - kernel - - - kernel - - - kernel - - - kernel - - - kernel - - - kernel - - - kernel - - - kernel - - - libs - - - libs - - - libs - - - rtmp - - - rtmp - - - rtmp - - - rtmp - - - rtmp - - - rtmp - - - rtmp - - - - - - \ No newline at end of file diff --git a/trunk/winbuild/srs_auto_headers.hpp b/trunk/winbuild/srs_auto_headers.hpp deleted file mode 100644 index 4ebef350b4..0000000000 --- a/trunk/winbuild/srs_auto_headers.hpp +++ /dev/null @@ -1,56 +0,0 @@ -// auto generated by configure -#ifndef SRS_AUTO_HEADER_HPP -#define SRS_AUTO_HEADER_HPP - -#define SRS_AUTO_BUILD_TS "1416731672" -#define SRS_AUTO_BUILD_DATE "2014-11-23 16:34:32" -#define SRS_AUTO_UNAME "WIN_NT-6.1 WZY-PC2 1.0.18(0.48/3/2) 2012-11-21 22:34 i686 WINNT" -#define SRS_AUTO_USER_CONFIGURE "--x86-x64 --export-librtmp-project=../srs-librtmp" -#define SRS_AUTO_CONFIGURE "--prefix=/usr/local/srs --without-hls --without-dvr --without-nginx --without-ssl --without-ffmpeg --without-transcode --without-ingest --without-stat --without-http-callback --without-http-server --without-http-api --with-librtmp --with-research --without-utest --without-gperf --without-gmc --without-gmp --without-gcp --without-gprof --without-arm-ubuntu12 --without-mips-ubuntu12 --log-trace" - -#define SRS_AUTO_EMBEDED_TOOL_CHAIN "normal x86/x64 gcc" - -#undef SRS_AUTO_HTTP_PARSER -#undef SRS_AUTO_HTTP_SERVER -#undef SRS_AUTO_HTTP_API -#undef SRS_AUTO_NGINX -#undef SRS_AUTO_DVR -#undef SRS_AUTO_HLS -#undef SRS_AUTO_HTTP_CALLBACK -#undef SRS_AUTO_SSL -#undef SRS_AUTO_FFMPEG_TOOL -#undef SRS_AUTO_FFMPEG_STUB -#undef SRS_AUTO_TRANSCODE -#undef SRS_AUTO_INGEST -#undef SRS_AUTO_STAT -#undef SRS_AUTO_GPERF -#undef SRS_AUTO_GPERF_MC -#undef SRS_AUTO_GPERF_MP -#undef SRS_AUTO_GPERF_CP -#undef SRS_AUTO_EMBEDED_CPU -#undef SRS_AUTO_ARM_UBUNTU12 -#undef SRS_AUTO_MIPS_UBUNTU12 - -#undef SRS_AUTO_VERBOSE -#undef SRS_AUTO_INFO -#define SRS_AUTO_TRACE - -#define SRS_AUTO_PREFIX "/usr/local/srs" - -#define SRS_AUTO_CONSTRIBUTORS "\ -winlin \ -wenjie.zhao<740936897@qq.com> \ -xiangcheng.liu \ -naijia.liu \ -alcoholyi \ -byteman \ -chad.wang \ -suhetao \ -Johnny \ -karthikeyan \ -StevenLiu \ -zhengfl \ -" - -#endif - diff --git a/trunk/winbuild/srs_play.cpp b/trunk/winbuild/srs_play.cpp deleted file mode 100644 index 2891e3a3e4..0000000000 --- a/trunk/winbuild/srs_play.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013-2014 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ -/** -gcc srs_play.c ../../objs/lib/srs_librtmp.a -g -O0 -lstdc++ -o srs_play -*/ - -#include -#include - -#include "srs_librtmp.hpp" - -int main(int argc, char** argv) -{ - printf("suck rtmp stream like rtmpdump\n"); - printf("srs(simple-rtmp-server) client librtmp library.\n"); - printf("version: %d.%d.%d\n", srs_version_major(), srs_version_minor(), srs_version_revision()); - - if (argc <= 1) { - printf("Usage: %s \n" - " rtmp_url RTMP stream url to play\n" - "For example:\n" - " %s rtmp://127.0.0.1:1935/live/livestream\n", - argv[0], argv[0]); - exit(-1); - } - - srs_human_trace("rtmp url: %s", argv[1]); - srs_rtmp_t rtmp = srs_rtmp_create(argv[1]); - - if (srs_rtmp_handshake(rtmp) != 0) { - srs_human_trace("simple handshake failed."); - goto rtmp_destroy; - } - srs_human_trace("simple handshake success"); - - if (srs_rtmp_connect_app(rtmp) != 0) { - srs_human_trace("connect vhost/app failed."); - goto rtmp_destroy; - } - srs_human_trace("connect vhost/app success"); - - if (srs_rtmp_play_stream(rtmp) != 0) { - srs_human_trace("play stream failed."); - goto rtmp_destroy; - } - srs_human_trace("play stream success"); - - for (;;) { - int size; - char type; - char* data; - u_int32_t timestamp; - - if (srs_rtmp_read_packet(rtmp, &type, ×tamp, &data, &size) != 0) { - goto rtmp_destroy; - } - - if (srs_human_print_rtmp_packet(type, timestamp, data, size) != 0) { - goto rtmp_destroy; - } - - free(data); - } - -rtmp_destroy: - srs_rtmp_destroy(rtmp); - - return 0; -} diff --git a/trunk/winbuild/srs_play.vcxproj b/trunk/winbuild/srs_play.vcxproj deleted file mode 100755 index 22d653d2bd..0000000000 --- a/trunk/winbuild/srs_play.vcxproj +++ /dev/null @@ -1,88 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {5149B9A9-5085-4A10-AD6F-23FBE6854390} - Win32Proj - srs_play - - - - Application - true - Unicode - - - Application - false - true - Unicode - - - - - - - - - - - - - true - - - false - - - - - - Level3 - Disabled - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - ..\src\core;..\src\libs;%(AdditionalIncludeDirectories) - - - Console - true - srs-librtmp.lib;%(AdditionalDependencies) - ./debug;%(AdditionalLibraryDirectories) - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - - - Console - true - true - true - - - - - - - - - - - - \ No newline at end of file diff --git a/trunk/winbuild/srs_play.vcxproj.filters b/trunk/winbuild/srs_play.vcxproj.filters deleted file mode 100755 index c66bb8bc03..0000000000 --- a/trunk/winbuild/srs_play.vcxproj.filters +++ /dev/null @@ -1,27 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Header Files - - - - - Source Files - - - \ No newline at end of file From 03f72fb1a682f89d38bf50ae3863828a5cee7c88 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 27 Nov 2014 18:39:02 +0800 Subject: [PATCH 238/800] for bug #229, revert changes of allspace, for srs-librtmp branch build failed. 2.0.34 --- trunk/configure | 7 +- trunk/src/core/srs_core.hpp | 11 +- trunk/src/core/srs_platform.cpp | 373 ----------------------- trunk/src/core/srs_platform.hpp | 110 ------- trunk/src/libs/srs_lib_simple_socket.cpp | 20 +- trunk/src/libs/srs_lib_simple_socket.hpp | 2 +- trunk/src/libs/srs_librtmp.cpp | 351 ++++++++++++++++++++- trunk/src/libs/srs_librtmp.hpp | 38 ++- trunk/src/srs/srs.upp | 2 - 9 files changed, 402 insertions(+), 512 deletions(-) delete mode 100644 trunk/src/core/srs_platform.cpp delete mode 100644 trunk/src/core/srs_platform.hpp diff --git a/trunk/configure b/trunk/configure index 1bd51abbc7..b29d709cb4 100755 --- a/trunk/configure +++ b/trunk/configure @@ -299,9 +299,8 @@ END GDBDebug=" -g -O0" # the warning level. WarnLevel=" -Wall" -# the compile in c++ standard. -# @remark, donot specifies it for mingw. -CppStd="-ansi" && echo $MSYSTEM|grep "MINGW">/dev/null && CppStd="" +# the compile standard. +CppStd="-ansi" # for library compile LibraryCompile=" -fPIC" # performance of gprof @@ -356,7 +355,7 @@ if [ $SRS_MIPS_UBUNTU12 = YES ]; then SrsLinkOptions="${SrsLinkOptions} -lgcc_eh MODULE_ID="CORE" MODULE_DEPENDS=() ModuleLibIncs=(${SRS_OBJS_DIR}) -MODULE_FILES=("srs_core" "srs_core_autofree" "srs_platform") +MODULE_FILES=("srs_core" "srs_core_autofree") CORE_INCS="src/core"; MODULE_DIR=${CORE_INCS} . auto/modules.sh CORE_OBJS="${MODULE_OBJS[@]}" # diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 785d12d502..98fffae592 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 33 +#define VERSION_REVISION 34 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" @@ -72,11 +72,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define __STDC_FORMAT_MACROS #endif -// for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 -#if defined(_WIN32) - #include -#endif - // for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 #ifndef _WIN32 #include @@ -115,9 +110,5 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. className(const className&); \ className& operator= (const className&) -// for windows to compile srs-librtmp -// @see: https://github.com/winlinvip/simple-rtmp-server/issues/213 -#include - #endif diff --git a/trunk/src/core/srs_platform.cpp b/trunk/src/core/srs_platform.cpp deleted file mode 100644 index 9e3f5fba9b..0000000000 --- a/trunk/src/core/srs_platform.cpp +++ /dev/null @@ -1,373 +0,0 @@ -/* -The MIT License (MIT) - -Copyright (c) 2014 allspace - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include - -#include -#include -#include - -#if defined(_WIN32) && !defined(__CYGWIN__) -int socket_setup() -{ - WORD wVersionRequested; - WSADATA wsaData; - int err; - - /* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */ - wVersionRequested = MAKEWORD(2, 2); - - err = WSAStartup(wVersionRequested, &wsaData); - if (err != 0) { - /* Tell the user that we could not find a usable */ - /* Winsock DLL. */ - //printf("WSAStartup failed with error: %d\n", err); - return -1; - } - return 0; -} - -int socket_cleanup() -{ - WSACleanup(); - return 0; -} - -int gettimeofday(struct timeval* tv, struct timezone* tz) -{ - time_t clock; - struct tm tm; - SYSTEMTIME win_time; - - GetLocalTime(&win_time); - - tm.tm_year = win_time.wYear - 1900; - tm.tm_mon = win_time.wMonth - 1; - tm.tm_mday = win_time.wDay; - tm.tm_hour = win_time.wHour; - tm.tm_min = win_time.wMinute; - tm.tm_sec = win_time.wSecond; - tm.tm_isdst = -1; - - clock = mktime(&tm); - - tv->tv_sec = (long)clock; - tv->tv_usec = win_time.wMilliseconds * 1000; - - return 0; -} - -pid_t getpid(void) -{ - return (pid_t)GetCurrentProcessId(); -} - -int usleep(useconds_t usec) -{ - Sleep((DWORD)(usec / 1000)); - return 0; -} - -ssize_t writev(int fd, const struct iovec *iov, int iovcnt) -{ - ssize_t nwrite = 0; - for (int i = 0; i < iovcnt; i++) { - const struct iovec* current = iov + i; - - int nsent = ::send(fd, (char*)current->iov_base, current->iov_len, 0); - if (nsent < 0) { - return nsent; - } - - nwrite += nsent; - if (nsent == 0) { - return nwrite; - } - } - return nwrite; -} - -//////////////////////// strlcpy.c (modified) ////////////////////////// - -/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */ - -/*- - * Copyright (c) 1998 Todd C. Miller - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -//#include // **** -//#include // **** -// __FBSDID("$FreeBSD: stable/9/sys/libkern/strlcpy.c 243811 2012-12-03 18:08:44Z delphij $"); // **** - -// #include // **** -// #include // **** - -/* - * Copy src to string dst of size siz. At most siz-1 characters - * will be copied. Always NUL terminates (unless siz == 0). - * Returns strlen(src); if retval >= siz, truncation occurred. - */ - -//#define __restrict // **** - -size_t strlcpy(char * __restrict dst, const char * __restrict src, size_t siz) -{ - char *d = dst; - const char *s = src; - size_t n = siz; - - /* Copy as many bytes as will fit */ - if (n != 0) { - while (--n != 0) { - if ((*d++ = *s++) == '\0') - break; - } - } - - /* Not enough room in dst, add NUL and traverse rest of src */ - if (n == 0) { - if (siz != 0) - *d = '\0'; /* NUL-terminate dst */ - while (*s++) - ; - } - - return(s - src - 1); /* count does not include NUL */ -} - -// http://www.cplusplus.com/forum/general/141779///////////////////////// inet_ntop.c (modified) ////////////////////////// -/* - * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") - * Copyright (c) 1996-1999 by Internet Software Consortium. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT - * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -// #if defined(LIBC_SCCS) && !defined(lint) // **** -//static const char rcsid[] = "$Id: inet_ntop.c,v 1.3.18.2 2005/11/03 23:02:22 marka Exp $"; -// #endif /* LIBC_SCCS and not lint */ // **** -// #include // **** -// __FBSDID("$FreeBSD: stable/9/sys/libkern/inet_ntop.c 213103 2010-09-24 15:01:45Z attilio $"); // **** - -//#define _WIN32_WINNT _WIN32_WINNT_WIN8 // **** -//#include // **** -//#pragma comment(lib, "Ws2_32.lib") // **** -//#include // **** - -// #include // **** -// #include // **** -// #include // **** - -// #include // **** - -/*% - * WARNING: Don't even consider trying to compile this on a system where - * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. - */ - -static char *inet_ntop4(const u_char *src, char *dst, socklen_t size); -static char *inet_ntop6(const u_char *src, char *dst, socklen_t size); - -/* char * - * inet_ntop(af, src, dst, size) - * convert a network format address to presentation format. - * return: - * pointer to presentation format address (`dst'), or NULL (see errno). - * author: - * Paul Vixie, 1996. - */ -const char* inet_ntop(int af, const void *src, char *dst, socklen_t size) -{ - switch (af) { - case AF_INET: - return (inet_ntop4( (unsigned char*)src, (char*)dst, size)); // **** -#ifdef AF_INET6 - //#error "IPv6 not supported" - //case AF_INET6: - // return (char*)(inet_ntop6( (unsigned char*)src, (char*)dst, size)); // **** -#endif - default: - // return (NULL); // **** - return 0 ; // **** - } - /* NOTREACHED */ -} - -/* const char * - * inet_ntop4(src, dst, size) - * format an IPv4 address - * return: - * `dst' (as a const) - * notes: - * (1) uses no statics - * (2) takes a u_char* not an in_addr as input - * author: - * Paul Vixie, 1996. - */ -static char * inet_ntop4(const u_char *src, char *dst, socklen_t size) -{ - static const char fmt[128] = "%u.%u.%u.%u"; - char tmp[sizeof "255.255.255.255"]; - int l; - - l = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]); // **** - if (l <= 0 || (socklen_t) l >= size) { - return (NULL); - } - strlcpy(dst, tmp, size); - return (dst); -} - -/* const char * - * inet_ntop6(src, dst, size) - * convert IPv6 binary address into presentation (printable) format - * author: - * Paul Vixie, 1996. - */ -static char * inet_ntop6(const u_char *src, char *dst, socklen_t size) -{ - /* - * Note that int32_t and int16_t need only be "at least" large enough - * to contain a value of the specified size. On some systems, like - * Crays, there is no such thing as an integer variable with 16 bits. - * Keep this in mind if you think this function should have been coded - * to use pointer overlays. All the world's not a VAX. - */ - char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp; - struct { int base, len; } best, cur; -#define NS_IN6ADDRSZ 16 -#define NS_INT16SZ 2 - u_int words[NS_IN6ADDRSZ / NS_INT16SZ]; - int i; - - /* - * Preprocess: - * Copy the input (bytewise) array into a wordwise array. - * Find the longest run of 0x00's in src[] for :: shorthanding. - */ - memset(words, '\0', sizeof words); - for (i = 0; i < NS_IN6ADDRSZ; i++) - words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); - best.base = -1; - best.len = 0; - cur.base = -1; - cur.len = 0; - for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { - if (words[i] == 0) { - if (cur.base == -1) - cur.base = i, cur.len = 1; - else - cur.len++; - } else { - if (cur.base != -1) { - if (best.base == -1 || cur.len > best.len) - best = cur; - cur.base = -1; - } - } - } - if (cur.base != -1) { - if (best.base == -1 || cur.len > best.len) - best = cur; - } - if (best.base != -1 && best.len < 2) - best.base = -1; - - /* - * Format the result. - */ - tp = tmp; - for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { - /* Are we inside the best run of 0x00's? */ - if (best.base != -1 && i >= best.base && - i < (best.base + best.len)) { - if (i == best.base) - *tp++ = ':'; - continue; - } - /* Are we following an initial run of 0x00s or any real hex? */ - if (i != 0) - *tp++ = ':'; - /* Is this address an encapsulated IPv4? */ - if (i == 6 && best.base == 0 && (best.len == 6 || - (best.len == 7 && words[7] != 0x0001) || - (best.len == 5 && words[5] == 0xffff))) { - if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp))) - return (NULL); - tp += strlen(tp); - break; - } - tp += sprintf(tp, "%x", words[i]); // **** - } - /* Was it a trailing run of 0x00's? */ - if (best.base != -1 && (best.base + best.len) == - (NS_IN6ADDRSZ / NS_INT16SZ)) - *tp++ = ':'; - *tp++ = '\0'; - - /* - * Check for overflow, copy, and we're done. - */ - if ((socklen_t)(tp - tmp) > size) { - return (NULL); - } - strcpy(dst, tmp); - return (dst); -} - -#define Set_errno(num) SetLastError((num)) - -/* INVALID_SOCKET == INVALID_HANDLE_VALUE == (unsigned int)(~0) */ -/* SOCKET_ERROR == -1 */ -//#define ENOTSOCK WSAENOTSOCK -//#define EINTR WSAEINTR -//#define ENOBUFS WSAENOBUFS -//#define EWOULDBLOCK WSAEWOULDBLOCK -#define EAFNOSUPPORT WSAEAFNOSUPPORT -/* from public\sdk\inc\crt\errno.h */ -#define ENOSPC 28 - -#endif - diff --git a/trunk/src/core/srs_platform.hpp b/trunk/src/core/srs_platform.hpp deleted file mode 100644 index 2604a0e88c..0000000000 --- a/trunk/src/core/srs_platform.hpp +++ /dev/null @@ -1,110 +0,0 @@ -/* -The MIT License (MIT) - -Copyright (c) 2014 allspace - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#ifndef SRS_WIN_PORTING_H -#define SRS_WIN_PORTING_H - -/** -* for linux like, -* for example, not on windows or it's cygwin. -* while the _WIN32 includes both 32-bit and 64-bit -*/ -#if !defined(_WIN32) || defined(__CYGWIN__) - #define SOCKET_ETIME ETIME - #define SOCKET_ECONNRESET ECONNRESET - - #define SOCKET int - #define SOCKET_ERRNO() errno - #define SOCKET_RESET(fd) fd = -1; (void)0 - #define SOCKET_CLOSE(fd) \ - if (fd > 0) {\ - ::close(fd); \ - fd = -1; \ - } \ - (void)0 - #define SOCKET_VALID(x) (x > 0) - #define SOCKET_SETUP() (void)0 - #define SOCKET_CLEANUP() (void)0 -#else /*on windows, but not on cygwin*/ - #include - #include - #include - #include - - #ifdef _MSC_VER //for VS2010 - #include - #include - #define S_IRUSR _S_IREAD - #define S_IWUSR _S_IWRITE - #define open _open - #define close _close - #define lseek _lseek - #define write _write - #define read _read - - typedef int ssize_t; - typedef int pid_t; - typedef int mode_t; - typedef int64_t useconds_t; - #endif - - #define S_IRGRP 0 - #define S_IWGRP 0 - #define S_IXGRP 0 - #define S_IRWXG 0 - #define S_IROTH 0 - #define S_IWOTH 0 - #define S_IXOTH 0 - #define S_IRWXO 0 - - #define PRId64 "lld" - - #define SOCKET_ETIME WSAETIMEDOUT - #define SOCKET_ECONNRESET WSAECONNRESET - #define SOCKET_ERRNO() WSAGetLastError() - #define SOCKET_RESET(x) x=INVALID_SOCKET - #define SOCKET_CLOSE(x) if(x!=INVALID_SOCKET){::closesocket(x);x=INVALID_SOCKET;} - #define SOCKET_VALID(x) (x!=INVALID_SOCKET) - #define SOCKET_BUFF(x) ((char*)x) - #define SOCKET_SETUP() socket_setup() - #define SOCKET_CLEANUP() socket_cleanup() - - typedef uint32_t u_int32_t; - typedef uint8_t u_int8_t; - typedef int socklen_t; - struct iovec { - void* iov_base; /* Starting address */ - size_t iov_len; /* Length in bytes */ - }; - - #define snprintf _snprintf - ssize_t writev(int fd, const struct iovec *iov, int iovcnt); - const char* inet_ntop(int af, const void *src, char *dst, socklen_t size); - int gettimeofday(struct timeval* tv, struct timezone* tz); - pid_t getpid(void); - int usleep(useconds_t usec); - int socket_setup(); - int socket_cleanup(); -#endif - -#endif //SRS_WIN_PORTING_H diff --git a/trunk/src/libs/srs_lib_simple_socket.cpp b/trunk/src/libs/srs_lib_simple_socket.cpp index 08096e7147..e7346b6a06 100644 --- a/trunk/src/libs/srs_lib_simple_socket.cpp +++ b/trunk/src/libs/srs_lib_simple_socket.cpp @@ -45,22 +45,22 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SimpleSocketStream::SimpleSocketStream() { - SOCKET_RESET(fd); + fd = -1; send_timeout = recv_timeout = ST_UTIME_NO_TIMEOUT; recv_bytes = send_bytes = 0; - SOCKET_SETUP(); } SimpleSocketStream::~SimpleSocketStream() { - SOCKET_CLOSE(fd); - SOCKET_CLEANUP(); + if (fd != -1) { + ::close(fd); + fd = -1; + } } int SimpleSocketStream::create_socket() { - fd = ::socket(AF_INET, SOCK_STREAM, 0); - if (!SOCKET_VALID(fd)) { + if((fd = ::socket(AF_INET, SOCK_STREAM, 0)) < 0){ return ERROR_SOCKET_CREATE; } @@ -95,12 +95,12 @@ int SimpleSocketStream::read(void* buf, size_t size, ssize_t* nread) // On success a non-negative integer indicating the number of bytes actually read is returned // (a value of 0 means the network connection is closed or end of file is reached). if (nb_read <= 0) { - if (nb_read < 0 && SOCKET_ERRNO() == SOCKET_ETIME) { + if (nb_read < 0 && errno == ETIME) { return ERROR_SOCKET_TIMEOUT; } if (nb_read == 0) { - errno = SOCKET_ECONNRESET; + errno = ECONNRESET; } return ERROR_SOCKET_READ; @@ -158,7 +158,7 @@ int SimpleSocketStream::writev(const iovec *iov, int iov_size, ssize_t* nwrite) // returned, and errno is set appropriately. if (nb_write <= 0) { // @see https://github.com/winlinvip/simple-rtmp-server/issues/200 - if (nb_write < 0 && SOCKET_ERRNO() == SOCKET_ETIME) { + if (nb_write < 0 && errno == ETIME) { return ERROR_SOCKET_TIMEOUT; } @@ -215,7 +215,7 @@ int SimpleSocketStream::write(void* buf, size_t size, ssize_t* nwrite) if (nb_write <= 0) { // @see https://github.com/winlinvip/simple-rtmp-server/issues/200 - if (nb_write < 0 && SOCKET_ERRNO() == SOCKET_ETIME) { + if (nb_write < 0 && errno == ETIME) { return ERROR_SOCKET_TIMEOUT; } diff --git a/trunk/src/libs/srs_lib_simple_socket.hpp b/trunk/src/libs/srs_lib_simple_socket.hpp index 90628a0930..2aa32bd391 100644 --- a/trunk/src/libs/srs_lib_simple_socket.hpp +++ b/trunk/src/libs/srs_lib_simple_socket.hpp @@ -43,7 +43,7 @@ class SimpleSocketStream : public ISrsProtocolReaderWriter int64_t send_timeout; int64_t recv_bytes; int64_t send_bytes; - SOCKET fd; + int fd; public: SimpleSocketStream(); virtual ~SimpleSocketStream(); diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index e2ca37a3b9..d1ad3513b6 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -34,7 +34,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include using namespace std; -#include #include #include #include @@ -108,6 +107,356 @@ struct Context } }; +// for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 +#ifdef _WIN32 + int gettimeofday(struct timeval* tv, struct timezone* tz) + { + time_t clock; + struct tm tm; + SYSTEMTIME win_time; + + GetLocalTime(&win_time); + + tm.tm_year = win_time.wYear - 1900; + tm.tm_mon = win_time.wMonth - 1; + tm.tm_mday = win_time.wDay; + tm.tm_hour = win_time.wHour; + tm.tm_min = win_time.wMinute; + tm.tm_sec = win_time.wSecond; + tm.tm_isdst = -1; + + clock = mktime(&tm); + + tv->tv_sec = (long)clock; + tv->tv_usec = win_time.wMilliseconds * 1000; + + return 0; + } + + int open(const char *pathname, int flags) + { + return open(pathname, flags, 0); + } + + int open(const char *pathname, int flags, mode_t mode) + { + FILE* file = NULL; + + if ((flags & O_RDONLY) == O_RDONLY) { + file = fopen(pathname, "r"); + } else { + file = fopen(pathname, "w+"); + } + + if (file == NULL) { + return -1; + } + + return (int)file; + } + + int close(int fd) + { + FILE* file = (FILE*)fd; + return fclose(file); + } + + off_t lseek(int fd, off_t offset, int whence) + { + return (off_t)fseek((FILE*)fd, offset, whence); + } + + ssize_t write(int fd, const void *buf, size_t count) + { + return (ssize_t)fwrite(buf, count, 1, (FILE*)fd); + } + + ssize_t read(int fd, void *buf, size_t count) + { + return (ssize_t)fread(buf, count, 1, (FILE*)fd); + } + + pid_t getpid(void) + { + return (pid_t)GetCurrentProcessId(); + } + + int usleep(useconds_t usec) + { + Sleep((DWORD)(usec / 1000)); + return 0; + } + + ssize_t writev(int fd, const struct iovec *iov, int iovcnt) + { + ssize_t nwrite = 0; + for (int i = 0; i < iovcnt; i++) { + const struct iovec* current = iov + i; + + int nsent = ::send(fd, (char*)current->iov_base, current->iov_len, 0); + if (nsent < 0) { + return nsent; + } + + nwrite += nsent; + if (nsent == 0) { + return nwrite; + } + } + return nwrite; + } + + //////////////////////// strlcpy.c (modified) ////////////////////////// + + /* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */ + + /*- + * Copyright (c) 1998 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + //#include // **** + //#include // **** + // __FBSDID("$FreeBSD: stable/9/sys/libkern/strlcpy.c 243811 2012-12-03 18:08:44Z delphij $"); // **** + + // #include // **** + // #include // **** + + /* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ + + //#define __restrict // **** + + std::size_t strlcpy(char * __restrict dst, const char * __restrict src, size_t siz) + { + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0) { + while (--n != 0) { + if ((*d++ = *s++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ + } + + // http://www.cplusplus.com/forum/general/141779///////////////////////// inet_ntop.c (modified) ////////////////////////// + /* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + // #if defined(LIBC_SCCS) && !defined(lint) // **** + //static const char rcsid[] = "$Id: inet_ntop.c,v 1.3.18.2 2005/11/03 23:02:22 marka Exp $"; + // #endif /* LIBC_SCCS and not lint */ // **** + // #include // **** + // __FBSDID("$FreeBSD: stable/9/sys/libkern/inet_ntop.c 213103 2010-09-24 15:01:45Z attilio $"); // **** + + //#define _WIN32_WINNT _WIN32_WINNT_WIN8 // **** + //#include // **** + #pragma comment(lib, "Ws2_32.lib") // **** + //#include // **** + + // #include // **** + // #include // **** + // #include // **** + + // #include // **** + + /*% + * WARNING: Don't even consider trying to compile this on a system where + * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. + */ + + static char *inet_ntop4(const u_char *src, char *dst, socklen_t size); + static char *inet_ntop6(const u_char *src, char *dst, socklen_t size); + + /* char * + * inet_ntop(af, src, dst, size) + * convert a network format address to presentation format. + * return: + * pointer to presentation format address (`dst'), or NULL (see errno). + * author: + * Paul Vixie, 1996. + */ + const char* inet_ntop(int af, const void *src, char *dst, socklen_t size) + { + switch (af) { + case AF_INET: + return (inet_ntop4( (unsigned char*)src, (char*)dst, size)); // **** + #ifdef AF_INET6 + #error "IPv6 not supported" + //case AF_INET6: + // return (char*)(inet_ntop6( (unsigned char*)src, (char*)dst, size)); // **** + #endif + default: + // return (NULL); // **** + return 0 ; // **** + } + /* NOTREACHED */ + } + + /* const char * + * inet_ntop4(src, dst, size) + * format an IPv4 address + * return: + * `dst' (as a const) + * notes: + * (1) uses no statics + * (2) takes a u_char* not an in_addr as input + * author: + * Paul Vixie, 1996. + */ + static char * inet_ntop4(const u_char *src, char *dst, socklen_t size) + { + static const char fmt[128] = "%u.%u.%u.%u"; + char tmp[sizeof "255.255.255.255"]; + int l; + + l = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]); // **** + if (l <= 0 || (socklen_t) l >= size) { + return (NULL); + } + strlcpy(dst, tmp, size); + return (dst); + } + + /* const char * + * inet_ntop6(src, dst, size) + * convert IPv6 binary address into presentation (printable) format + * author: + * Paul Vixie, 1996. + */ + static char * inet_ntop6(const u_char *src, char *dst, socklen_t size) + { + /* + * Note that int32_t and int16_t need only be "at least" large enough + * to contain a value of the specified size. On some systems, like + * Crays, there is no such thing as an integer variable with 16 bits. + * Keep this in mind if you think this function should have been coded + * to use pointer overlays. All the world's not a VAX. + */ + char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp; + struct { int base, len; } best, cur; + #define NS_IN6ADDRSZ 16 + #define NS_INT16SZ 2 + u_int words[NS_IN6ADDRSZ / NS_INT16SZ]; + int i; + + /* + * Preprocess: + * Copy the input (bytewise) array into a wordwise array. + * Find the longest run of 0x00's in src[] for :: shorthanding. + */ + memset(words, '\0', sizeof words); + for (i = 0; i < NS_IN6ADDRSZ; i++) + words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); + best.base = -1; + best.len = 0; + cur.base = -1; + cur.len = 0; + for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { + if (words[i] == 0) { + if (cur.base == -1) + cur.base = i, cur.len = 1; + else + cur.len++; + } else { + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + cur.base = -1; + } + } + } + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + } + if (best.base != -1 && best.len < 2) + best.base = -1; + + /* + * Format the result. + */ + tp = tmp; + for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { + /* Are we inside the best run of 0x00's? */ + if (best.base != -1 && i >= best.base && + i < (best.base + best.len)) { + if (i == best.base) + *tp++ = ':'; + continue; + } + /* Are we following an initial run of 0x00s or any real hex? */ + if (i != 0) + *tp++ = ':'; + /* Is this address an encapsulated IPv4? */ + if (i == 6 && best.base == 0 && (best.len == 6 || + (best.len == 7 && words[7] != 0x0001) || + (best.len == 5 && words[5] == 0xffff))) { + if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp))) + return (NULL); + tp += strlen(tp); + break; + } + tp += std::sprintf(tp, "%x", words[i]); // **** + } + /* Was it a trailing run of 0x00's? */ + if (best.base != -1 && (best.base + best.len) == + (NS_IN6ADDRSZ / NS_INT16SZ)) + *tp++ = ':'; + *tp++ = '\0'; + + /* + * Check for overflow, copy, and we're done. + */ + if ((socklen_t)(tp - tmp) > size) { + return (NULL); + } + strcpy(dst, tmp); + return (dst); + } +#endif + int srs_librtmp_context_parse_uri(Context* context) { int ret = ERROR_SUCCESS; diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index 206a9d9c49..58d2c4c8a8 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -30,10 +30,46 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include +// for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 #ifdef _WIN32 - typedef long int int64_t; + #define _CRT_SECURE_NO_WARNINGS + typedef unsigned long long u_int64_t; + typedef long long int64_t; typedef unsigned int u_int32_t; typedef int int32_t; + typedef unsigned char u_int8_t; + typedef char int8_t; + typedef unsigned short u_int16_t; + typedef short int16_t; + typedef int64_t ssize_t; + struct iovec { + void *iov_base; /* Starting address */ + size_t iov_len; /* Number of bytes to transfer */ + }; + #include + #include + int gettimeofday(struct timeval* tv, struct timezone* tz); + #define PRId64 "lld" + typedef int socklen_t; + const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); + typedef int mode_t; + #define S_IRUSR 0 + #define S_IWUSR 0 + #define S_IRGRP 0 + #define S_IWGRP 0 + #define S_IROTH 0 + int open(const char *pathname, int flags); + int open(const char *pathname, int flags, mode_t mode); + int close(int fd); + off_t lseek(int fd, off_t offset, int whence); + ssize_t write(int fd, const void *buf, size_t count); + ssize_t read(int fd, void *buf, size_t count); + typedef int pid_t; + pid_t getpid(void); + #define snprintf _snprintf + ssize_t writev(int fd, const struct iovec *iov, int iovcnt); + typedef int64_t useconds_t; + int usleep(useconds_t usec); #endif /** diff --git a/trunk/src/srs/srs.upp b/trunk/src/srs/srs.upp index d2f18d7c0d..94f0fd3397 100755 --- a/trunk/src/srs/srs.upp +++ b/trunk/src/srs/srs.upp @@ -15,8 +15,6 @@ file ..\core\srs_core.cpp, ..\core\srs_core_autofree.hpp, ..\core\srs_core_autofree.cpp, - ..\core\srs_platform.hpp, - ..\core\srs_platform.cpp, kernel readonly separator, ..\kernel\srs_kernel_buffer.hpp, ..\kernel\srs_kernel_buffer.cpp, From 7f121efd7a7098287243f1501ea760435d87e7a5 Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 28 Nov 2014 10:33:36 +0800 Subject: [PATCH 239/800] merge from allspace srs-librtmp for win vs2010. 2.0.36 --- trunk/auto/generate-srs-librtmp-single.sh | 2 +- trunk/src/core/srs_core.hpp | 2 +- trunk/src/libs/srs_lib_simple_socket.cpp | 20 ++++----- trunk/src/libs/srs_lib_simple_socket.hpp | 3 +- trunk/src/libs/srs_librtmp.cpp | 52 ++++++++--------------- trunk/src/libs/srs_librtmp.hpp | 46 +++++++++++++++++--- 6 files changed, 70 insertions(+), 55 deletions(-) diff --git a/trunk/auto/generate-srs-librtmp-single.sh b/trunk/auto/generate-srs-librtmp-single.sh index 00035946fc..3246106c14 100755 --- a/trunk/auto/generate-srs-librtmp-single.sh +++ b/trunk/auto/generate-srs-librtmp-single.sh @@ -112,7 +112,7 @@ int main(int argc, char** argv) printf("version: %d.%d.%d\n", srs_version_major(), srs_version_minor(), srs_version_revision()); rtmp = srs_rtmp_create("rtmp://ossrs.net/live/livestream"); - srs_lib_trace("create rtmp success"); + srs_human_trace("create rtmp success"); srs_rtmp_destroy(rtmp); return 0; diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index e0fa759136..94d5287cde 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 35 +#define VERSION_REVISION 36 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/libs/srs_lib_simple_socket.cpp b/trunk/src/libs/srs_lib_simple_socket.cpp index e7346b6a06..08096e7147 100644 --- a/trunk/src/libs/srs_lib_simple_socket.cpp +++ b/trunk/src/libs/srs_lib_simple_socket.cpp @@ -45,22 +45,22 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SimpleSocketStream::SimpleSocketStream() { - fd = -1; + SOCKET_RESET(fd); send_timeout = recv_timeout = ST_UTIME_NO_TIMEOUT; recv_bytes = send_bytes = 0; + SOCKET_SETUP(); } SimpleSocketStream::~SimpleSocketStream() { - if (fd != -1) { - ::close(fd); - fd = -1; - } + SOCKET_CLOSE(fd); + SOCKET_CLEANUP(); } int SimpleSocketStream::create_socket() { - if((fd = ::socket(AF_INET, SOCK_STREAM, 0)) < 0){ + fd = ::socket(AF_INET, SOCK_STREAM, 0); + if (!SOCKET_VALID(fd)) { return ERROR_SOCKET_CREATE; } @@ -95,12 +95,12 @@ int SimpleSocketStream::read(void* buf, size_t size, ssize_t* nread) // On success a non-negative integer indicating the number of bytes actually read is returned // (a value of 0 means the network connection is closed or end of file is reached). if (nb_read <= 0) { - if (nb_read < 0 && errno == ETIME) { + if (nb_read < 0 && SOCKET_ERRNO() == SOCKET_ETIME) { return ERROR_SOCKET_TIMEOUT; } if (nb_read == 0) { - errno = ECONNRESET; + errno = SOCKET_ECONNRESET; } return ERROR_SOCKET_READ; @@ -158,7 +158,7 @@ int SimpleSocketStream::writev(const iovec *iov, int iov_size, ssize_t* nwrite) // returned, and errno is set appropriately. if (nb_write <= 0) { // @see https://github.com/winlinvip/simple-rtmp-server/issues/200 - if (nb_write < 0 && errno == ETIME) { + if (nb_write < 0 && SOCKET_ERRNO() == SOCKET_ETIME) { return ERROR_SOCKET_TIMEOUT; } @@ -215,7 +215,7 @@ int SimpleSocketStream::write(void* buf, size_t size, ssize_t* nwrite) if (nb_write <= 0) { // @see https://github.com/winlinvip/simple-rtmp-server/issues/200 - if (nb_write < 0 && errno == ETIME) { + if (nb_write < 0 && SOCKET_ERRNO() == SOCKET_ETIME) { return ERROR_SOCKET_TIMEOUT; } diff --git a/trunk/src/libs/srs_lib_simple_socket.hpp b/trunk/src/libs/srs_lib_simple_socket.hpp index 2aa32bd391..dca0c45b2b 100644 --- a/trunk/src/libs/srs_lib_simple_socket.hpp +++ b/trunk/src/libs/srs_lib_simple_socket.hpp @@ -31,6 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +#include /** * simple socket stream, @@ -43,7 +44,7 @@ class SimpleSocketStream : public ISrsProtocolReaderWriter int64_t send_timeout; int64_t recv_bytes; int64_t send_bytes; - int fd; + SOCKET fd; public: SimpleSocketStream(); virtual ~SimpleSocketStream(); diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index d1ad3513b6..04d307d9d7 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -133,47 +133,29 @@ struct Context return 0; } - int open(const char *pathname, int flags) + int socket_setup() { - return open(pathname, flags, 0); - } - - int open(const char *pathname, int flags, mode_t mode) - { - FILE* file = NULL; - - if ((flags & O_RDONLY) == O_RDONLY) { - file = fopen(pathname, "r"); - } else { - file = fopen(pathname, "w+"); - } - - if (file == NULL) { + WORD wVersionRequested; + WSADATA wsaData; + int err; + + /* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */ + wVersionRequested = MAKEWORD(2, 2); + + err = WSAStartup(wVersionRequested, &wsaData); + if (err != 0) { + /* Tell the user that we could not find a usable */ + /* Winsock DLL. */ + //printf("WSAStartup failed with error: %d\n", err); return -1; } - - return (int)file; - } - - int close(int fd) - { - FILE* file = (FILE*)fd; - return fclose(file); - } - - off_t lseek(int fd, off_t offset, int whence) - { - return (off_t)fseek((FILE*)fd, offset, whence); - } - - ssize_t write(int fd, const void *buf, size_t count) - { - return (ssize_t)fwrite(buf, count, 1, (FILE*)fd); + return 0; } - ssize_t read(int fd, void *buf, size_t count) + int socket_cleanup() { - return (ssize_t)fread(buf, count, 1, (FILE*)fd); + WSACleanup(); + return 0; } pid_t getpid(void) diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index 58d2c4c8a8..8cb20b5a1d 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -31,7 +31,23 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include // for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 -#ifdef _WIN32 +#ifndef _WIN32 + #define SOCKET_ETIME ETIME + #define SOCKET_ECONNRESET ECONNRESET + + #define SOCKET int + #define SOCKET_ERRNO() errno + #define SOCKET_RESET(fd) fd = -1; (void)0 + #define SOCKET_CLOSE(fd) \ + if (fd > 0) {\ + ::close(fd); \ + fd = -1; \ + } \ + (void)0 + #define SOCKET_VALID(x) (x > 0) + #define SOCKET_SETUP() (void)0 + #define SOCKET_CLEANUP() (void)0 +#else #define _CRT_SECURE_NO_WARNINGS typedef unsigned long long u_int64_t; typedef long long int64_t; @@ -50,6 +66,17 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include int gettimeofday(struct timeval* tv, struct timezone* tz); #define PRId64 "lld" + + #define SOCKET_ETIME WSAETIMEDOUT + #define SOCKET_ECONNRESET WSAECONNRESET + #define SOCKET_ERRNO() WSAGetLastError() + #define SOCKET_RESET(x) x=INVALID_SOCKET + #define SOCKET_CLOSE(x) if(x!=INVALID_SOCKET){::closesocket(x);x=INVALID_SOCKET;} + #define SOCKET_VALID(x) (x!=INVALID_SOCKET) + #define SOCKET_BUFF(x) ((char*)x) + #define SOCKET_SETUP() socket_setup() + #define SOCKET_CLEANUP() socket_cleanup() + typedef int socklen_t; const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); typedef int mode_t; @@ -58,18 +85,23 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define S_IRGRP 0 #define S_IWGRP 0 #define S_IROTH 0 - int open(const char *pathname, int flags); - int open(const char *pathname, int flags, mode_t mode); - int close(int fd); - off_t lseek(int fd, off_t offset, int whence); - ssize_t write(int fd, const void *buf, size_t count); - ssize_t read(int fd, void *buf, size_t count); + + #include + #include + #define open _open + #define close _close + #define lseek _lseek + #define write _write + #define read _read + typedef int pid_t; pid_t getpid(void); #define snprintf _snprintf ssize_t writev(int fd, const struct iovec *iov, int iovcnt); typedef int64_t useconds_t; int usleep(useconds_t usec); + int socket_setup(); + int socket_cleanup(); #endif /** From ff5cdb1cc1770de988156746d717ad0d5d70ddb1 Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 28 Nov 2014 12:26:36 +0800 Subject: [PATCH 240/800] for bug #215, add srs_rtmp_dump tool. 2.0.37. --- README.md | 1 + trunk/research/librtmp/Makefile | 7 +- trunk/research/librtmp/srs_rtmp_dump.c | 122 +++++++++++++++++++++++++ trunk/src/core/srs_core.hpp | 2 +- trunk/src/srs/srs.upp | 1 + 5 files changed, 131 insertions(+), 2 deletions(-) create mode 100644 trunk/research/librtmp/srs_rtmp_dump.c diff --git a/README.md b/README.md index c674ad2b88..c6285d058a 100755 --- a/README.md +++ b/README.md @@ -485,6 +485,7 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History +* v2.0, 2014-11-28, fix [#215](https://github.com/winlinvip/simple-rtmp-server/issues/215), for bug #215, add srs_rtmp_dump tool. 2.0.37. * v2.0, 2014-11-25, update PRIMARY, AUTHORS, CONTRIBUTORS rule. 2.0.32. * v2.0, 2014-11-24, fix [#212](https://github.com/winlinvip/simple-rtmp-server/issues/212), support publish aac adts raw stream. 2.0.31. * v2.0, 2014-11-22, fix [#217](https://github.com/winlinvip/simple-rtmp-server/issues/217), remove timeout recv, support 7.5k+ 250kbps clients. 2.0.30. diff --git a/trunk/research/librtmp/Makefile b/trunk/research/librtmp/Makefile index f8e9db4f9f..d9a470997d 100755 --- a/trunk/research/librtmp/Makefile +++ b/trunk/research/librtmp/Makefile @@ -7,7 +7,8 @@ else objs/srs_flv_injecter objs/srs_publish objs/srs_play \ objs/srs_ingest_flv objs/srs_ingest_rtmp objs/srs_detect_rtmp \ objs/srs_bandwidth_check objs/srs_h264_raw_publish \ - objs/srs_audio_raw_publish objs/srs_aac_raw_publish + objs/srs_audio_raw_publish objs/srs_aac_raw_publish \ + objs/srs_rtmp_dump endif .PHONY: default clean help ssl nossl @@ -32,6 +33,7 @@ help: @echo " srs_ingest_rtmp ingest RTMP and publish to RTMP server." @echo " srs_detect_rtmp detect RTMP stream info." @echo " srs_bandwidth_check bandwidth check/test tool." + @echo " srs_rtmp_dump dump rtmp stream to flv file." @echo "Remark: about simple/complex handshake, see: http://blog.csdn.net/win_lin/article/details/13006803" @echo "Remark: srs Makefile will auto invoke this by --with/without-ssl, " @echo " that is, if user specified ssl(by --with-ssl), srs will make this by 'make ssl'" @@ -108,3 +110,6 @@ objs/srs_detect_rtmp: srs_detect_rtmp.c $(SRS_RESEARCH_DEPS) $(SRS_LIBRTMP_I) $( objs/srs_bandwidth_check: srs_bandwidth_check.c $(SRS_RESEARCH_DEPS) $(SRS_LIBRTMP_I) $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(GCC) srs_bandwidth_check.c $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(EXTRA_CXX_FLAG) -o objs/srs_bandwidth_check + +objs/srs_rtmp_dump: srs_rtmp_dump.c $(SRS_RESEARCH_DEPS) $(SRS_LIBRTMP_I) $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) + $(GCC) srs_rtmp_dump.c $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(EXTRA_CXX_FLAG) -o objs/srs_rtmp_dump diff --git a/trunk/research/librtmp/srs_rtmp_dump.c b/trunk/research/librtmp/srs_rtmp_dump.c new file mode 100644 index 0000000000..00f8d1fd67 --- /dev/null +++ b/trunk/research/librtmp/srs_rtmp_dump.c @@ -0,0 +1,122 @@ +/* +The MIT License (MIT) + +Copyright (c) 2013-2014 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +/** +gcc srs_rtmp_dump.c ../../objs/lib/srs_librtmp.a -g -O0 -lstdc++ -o srs_rtmp_dump +*/ + +#include +#include + +#include "../../objs/include/srs_librtmp.h" + +int main(int argc, char** argv) +{ + printf("dump rtmp stream to flv file\n"); + printf("srs(simple-rtmp-server) client librtmp library.\n"); + printf("version: %d.%d.%d\n", srs_version_major(), srs_version_minor(), srs_version_revision()); + + if (argc <= 2) { + printf("Usage: %s \n" + " rtmp_url RTMP stream url to play\n" + " flv_path The flv file path to save\n" + "For example:\n" + " %s rtmp://127.0.0.1:1935/live/livestream output.flv\n", + argv[0], argv[0]); + exit(-1); + } + + srs_human_trace("rtmp url: %s", argv[1]); + srs_human_trace("flv path: %s", argv[2]); + srs_rtmp_t rtmp = srs_rtmp_create(argv[1]); + + if (srs_rtmp_handshake(rtmp) != 0) { + srs_human_trace("simple handshake failed."); + goto rtmp_destroy; + } + srs_human_trace("simple handshake success"); + + if (srs_rtmp_connect_app(rtmp) != 0) { + srs_human_trace("connect vhost/app failed."); + goto rtmp_destroy; + } + srs_human_trace("connect vhost/app success"); + + if (srs_rtmp_play_stream(rtmp) != 0) { + srs_human_trace("play stream failed."); + goto rtmp_destroy; + } + srs_human_trace("play stream success"); + + srs_flv_t flv = srs_flv_open_write(argv[2]); + + // flv header + char header[9]; + // 3bytes, signature, "FLV", + header[0] = 'F'; + header[1] = 'L'; + header[2] = 'V'; + // 1bytes, version, 0x01, + header[3] = 0x01; + // 1bytes, flags, UB[5] 0, UB[1] audio present, UB[1] 0, UB[1] video present. + header[4] = 0x03; // audio + video. + // 4bytes, dataoffset + header[5] = 0x00; + header[6] = 0x00; + header[7] = 0x00; + header[8] = 0x09; + if (srs_flv_write_header(flv, header) != 0) { + srs_human_trace("write flv header failed."); + goto rtmp_destroy; + } + + for (;;) { + int size; + char type; + char* data; + u_int32_t timestamp; + + if (srs_rtmp_read_packet(rtmp, &type, ×tamp, &data, &size) != 0) { + srs_human_trace("read rtmp packet failed."); + goto rtmp_destroy; + } + + if (srs_human_print_rtmp_packet(type, timestamp, data, size) != 0) { + srs_human_trace("print rtmp packet failed."); + goto rtmp_destroy; + } + + if (srs_flv_write_tag(flv, type, timestamp, data, size) != 0) { + srs_human_trace("dump rtmp packet failed."); + goto rtmp_destroy; + } + + free(data); + } + +rtmp_destroy: + srs_rtmp_destroy(rtmp); + srs_flv_close(flv); + srs_human_trace("completed"); + + return 0; +} diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 94d5287cde..e7333f98e9 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 36 +#define VERSION_REVISION 37 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/srs/srs.upp b/trunk/src/srs/srs.upp index 94f0fd3397..8e3d6ff113 100755 --- a/trunk/src/srs/srs.upp +++ b/trunk/src/srs/srs.upp @@ -141,6 +141,7 @@ file ..\..\research\librtmp\srs_ingest_rtmp.c, ..\..\research\librtmp\srs_play.c, ..\..\research\librtmp\srs_publish.c, + ..\..\research\librtmp\srs_rtmp_dump.c, ..\..\research\hls\ts_info.cc; mainconfig From 5d883e2e296b49ff4be088a76784ded7a5bf4806 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 29 Nov 2014 13:08:43 +0800 Subject: [PATCH 241/800] for bug #235, move functions of block and digest to struct. --- trunk/src/rtmp/srs_protocol_handshake.cpp | 132 ++++++++++-------- trunk/src/rtmp/srs_protocol_handshake.hpp | 159 +++++++++++----------- 2 files changed, 153 insertions(+), 138 deletions(-) diff --git a/trunk/src/rtmp/srs_protocol_handshake.cpp b/trunk/src/rtmp/srs_protocol_handshake.cpp index 89a5d3a326..143f966b92 100644 --- a/trunk/src/rtmp/srs_protocol_handshake.cpp +++ b/trunk/src/rtmp/srs_protocol_handshake.cpp @@ -312,31 +312,17 @@ namespace _srs_internal return stream.read_4bytes(); } - // calc the offset of key, - // the key->offset cannot be used as the offset of key. - int srs_key_block_get_offset(key_block* key) - { - int max_offset_size = 764 - 128 - 4; - - int offset = 0; - u_int8_t* pp = (u_int8_t*)&key->offset; - offset += *pp++; - offset += *pp++; - offset += *pp++; - offset += *pp++; - - return offset % max_offset_size; - } - // create new key block data. // if created, user must free it by srs_key_block_free - void srs_key_block_init(key_block* key) + void key_block::init() { + key_block* key = this; + key->offset = (int32_t)rand(); key->random0 = NULL; key->random1 = NULL; - int offset = srs_key_block_get_offset(key); + int offset = key->offsets(); srs_assert(offset >= 0); key->random0_size = offset; @@ -356,11 +342,31 @@ namespace _srs_internal } } + // calc the offset of key, + // the key->offset cannot be used as the offset of key. + int key_block::offsets() + { + key_block* key = this; + + int max_offset_size = 764 - 128 - 4; + + int offset = 0; + u_int8_t* pp = (u_int8_t*)&key->offset; + offset += *pp++; + offset += *pp++; + offset += *pp++; + offset += *pp++; + + return offset % max_offset_size; + } + // parse key block from c1s1. // if created, user must free it by srs_key_block_free // @c1s1_key_bytes the key start bytes, maybe c1s1 or c1s1+764 - int srs_key_block_parse(key_block* key, char* c1s1_key_bytes) + int key_block::parse(char* c1s1_key_bytes) { + key_block* key = this; + int ret = ERROR_SUCCESS; char* pp = c1s1_key_bytes + 764; @@ -371,7 +377,7 @@ namespace _srs_internal key->random0 = NULL; key->random1 = NULL; - int offset = srs_key_block_get_offset(key); + int offset = key->offsets(); srs_assert(offset >= 0); pp = c1s1_key_bytes; @@ -396,8 +402,10 @@ namespace _srs_internal // free the block data create by // srs_key_block_init or srs_key_block_parse - void srs_key_block_free(key_block* key) + void key_block::free() { + key_block* key = this; + if (key->random0) { srs_freep(key->random0); } @@ -406,31 +414,17 @@ namespace _srs_internal } } - // calc the offset of digest, - // the key->offset cannot be used as the offset of digest. - int srs_digest_block_get_offset(digest_block* digest) - { - int max_offset_size = 764 - 32 - 4; - - int offset = 0; - u_int8_t* pp = (u_int8_t*)&digest->offset; - offset += *pp++; - offset += *pp++; - offset += *pp++; - offset += *pp++; - - return offset % max_offset_size; - } - // create new digest block data. // if created, user must free it by srs_digest_block_free - void srs_digest_block_init(digest_block* digest) + void digest_block::init() { + digest_block* digest = this; + digest->offset = (int32_t)rand(); digest->random0 = NULL; digest->random1 = NULL; - int offset = srs_digest_block_get_offset(digest); + int offset = digest->offsets(); srs_assert(offset >= 0); digest->random0_size = offset; @@ -449,12 +443,32 @@ namespace _srs_internal snprintf(digest->random1, digest->random1_size, "%s", RTMP_SIG_SRS_HANDSHAKE); } } + + // calc the offset of digest, + // the key->offset cannot be used as the offset of digest. + int digest_block::offsets() + { + digest_block* digest = this; + + int max_offset_size = 764 - 32 - 4; + + int offset = 0; + u_int8_t* pp = (u_int8_t*)&digest->offset; + offset += *pp++; + offset += *pp++; + offset += *pp++; + offset += *pp++; + + return offset % max_offset_size; + } // parse digest block from c1s1. // if created, user must free it by srs_digest_block_free // @c1s1_digest_bytes the digest start bytes, maybe c1s1 or c1s1+764 - int srs_digest_block_parse(digest_block* digest, char* c1s1_digest_bytes) + int digest_block::parse(char* c1s1_digest_bytes) { + digest_block* digest = this; + int ret = ERROR_SUCCESS; char* pp = c1s1_digest_bytes; @@ -465,7 +479,7 @@ namespace _srs_internal digest->random0 = NULL; digest->random1 = NULL; - int offset = srs_digest_block_get_offset(digest); + int offset = digest->offsets(); srs_assert(offset >= 0); digest->random0_size = offset; @@ -489,8 +503,10 @@ namespace _srs_internal // free the block data create by // srs_digest_block_init or srs_digest_block_parse - void srs_digest_block_free(digest_block* digest) + void digest_block::free() { + digest_block* digest = this; + if (digest->random0) { srs_freep(digest->random0); } @@ -792,21 +808,21 @@ namespace _srs_internal version = __srs_stream_read_4bytes(_c1s1 + 4); // client c1 version if (_schema == srs_schema0) { - if ((ret = srs_key_block_parse(&block0.key, _c1s1 + 8)) != ERROR_SUCCESS) { + if ((ret = block0.key.parse(_c1s1 + 8)) != ERROR_SUCCESS) { srs_error("parse the c1 key failed. ret=%d", ret); return ret; } - if ((ret = srs_digest_block_parse(&block1.digest, _c1s1 + 8 + 764)) != ERROR_SUCCESS) { + if ((ret = block1.digest.parse(_c1s1 + 8 + 764)) != ERROR_SUCCESS) { srs_error("parse the c1 digest failed. ret=%d", ret); return ret; } srs_verbose("parse c1 key-digest success"); } else if (_schema == srs_schema1) { - if ((ret = srs_digest_block_parse(&block0.digest, _c1s1 + 8)) != ERROR_SUCCESS) { + if ((ret = block0.digest.parse(_c1s1 + 8)) != ERROR_SUCCESS) { srs_error("parse the c1 key failed. ret=%d", ret); return ret; } - if ((ret = srs_key_block_parse(&block1.key, _c1s1 + 8 + 764)) != ERROR_SUCCESS) { + if ((ret = block1.key.parse(_c1s1 + 8 + 764)) != ERROR_SUCCESS) { srs_error("parse the c1 digest failed. ret=%d", ret); return ret; } @@ -840,11 +856,11 @@ namespace _srs_internal // generate signature by schema if (_schema == srs_schema0) { - srs_key_block_init(&block0.key); - srs_digest_block_init(&block1.digest); + block0.key.init(); + block1.digest.init(); } else { - srs_digest_block_init(&block0.digest); - srs_key_block_init(&block1.key); + block0.digest.init(); + block1.key.init(); } schema = _schema; @@ -941,8 +957,8 @@ namespace _srs_internal } if (schema == srs_schema0) { - srs_key_block_init(&block0.key); - srs_digest_block_init(&block1.digest); + block0.key.init(); + block1.digest.init(); // directly generate the public key. // @see: https://github.com/winlinvip/simple-rtmp-server/issues/148 @@ -953,8 +969,8 @@ namespace _srs_internal } srs_assert(pkey_size == 128); } else { - srs_digest_block_init(&block0.digest); - srs_key_block_init(&block1.key); + block0.digest.init(); + block1.key.init(); // directly generate the public key. // @see: https://github.com/winlinvip/simple-rtmp-server/issues/148 @@ -1048,11 +1064,11 @@ namespace _srs_internal } if (schema == srs_schema0) { - srs_key_block_free(&block0.key); - srs_digest_block_free(&block1.digest); + block0.key.free(); + block1.digest.free(); } else { - srs_digest_block_free(&block0.digest); - srs_key_block_free(&block1.key); + block0.digest.free(); + block1.key.free(); } } } diff --git a/trunk/src/rtmp/srs_protocol_handshake.hpp b/trunk/src/rtmp/srs_protocol_handshake.hpp index 7dac4f8ca9..6ad671202a 100644 --- a/trunk/src/rtmp/srs_protocol_handshake.hpp +++ b/trunk/src/rtmp/srs_protocol_handshake.hpp @@ -41,6 +41,52 @@ class SrsHandshakeBytes; namespace _srs_internal { + // the digest key generate size. + #define __SRS_OpensslHashSize 512 + extern u_int8_t SrsGenuineFMSKey[]; + extern u_int8_t SrsGenuineFPKey[]; + int openssl_HMACsha256(const void* key, int key_size, const void* data, int data_size, void* digest); + int openssl_generate_key(char* public_key, int32_t size); + + /** + * the DH wrapper. + */ + class SrsDH + { + private: + DH* pdh; + public: + SrsDH(); + virtual ~SrsDH(); + public: + /** + * initialize dh, generate the public and private key. + * @param ensure_128bytes_public_key whether ensure public key is 128bytes, + * sometimes openssl generate 127bytes public key. + * default to false to donot ensure. + */ + virtual int initialize(bool ensure_128bytes_public_key = false); + /** + * copy the public key. + * @param pkey the bytes to copy the public key. + * @param pkey_size the max public key size, output the actual public key size. + * user should never ignore this size. + * @remark, when ensure_128bytes_public_key, the size always 128. + */ + virtual int copy_public_key(char* pkey, int32_t& pkey_size); + /** + * generate and copy the shared key. + * generate the shared key with peer public key. + * @param ppkey peer public key. + * @param ppkey_size the size of ppkey. + * @param skey the computed shared key. + * @param skey_size the max shared key size, output the actual shared key size. + * user should never ignore this size. + */ + virtual int copy_shared_key(const char* ppkey, int32_t ppkey_size, char* skey, int32_t& skey_size); + private: + virtual int do_initialize(); + }; /** * the schema type. */ @@ -85,6 +131,23 @@ namespace _srs_internal // 4bytes int32_t offset; + public: + // create new key block data. + // if created, user must free it by srs_key_block_free + void init(); + + // calc the offset of key, + // the key->offset cannot be used as the offset of key. + int offsets(); + + // parse key block from c1s1. + // if created, user must free it by srs_key_block_free + // @c1s1_key_bytes the key start bytes, maybe c1s1 or c1s1+764 + int parse(char* c1s1_key_bytes); + + // free the block data create by + // srs_key_block_init or srs_key_block_parse + void free(); }; /** @@ -111,88 +174,24 @@ namespace _srs_internal // (764-4-offset-32)bytes char* random1; int random1_size; - }; - - // the digest key generate size. - #define __SRS_OpensslHashSize 512 - extern u_int8_t SrsGenuineFMSKey[]; - extern u_int8_t SrsGenuineFPKey[]; - int openssl_HMACsha256(const void* key, int key_size, const void* data, int data_size, void* digest); - int openssl_generate_key(char* public_key, int32_t size); - - /** - * the DH wrapper. - */ - class SrsDH - { - private: - DH* pdh; public: - SrsDH(); - virtual ~SrsDH(); - public: - /** - * initialize dh, generate the public and private key. - * @param ensure_128bytes_public_key whether ensure public key is 128bytes, - * sometimes openssl generate 127bytes public key. - * default to false to donot ensure. - */ - virtual int initialize(bool ensure_128bytes_public_key = false); - /** - * copy the public key. - * @param pkey the bytes to copy the public key. - * @param pkey_size the max public key size, output the actual public key size. - * user should never ignore this size. - * @remark, when ensure_128bytes_public_key, the size always 128. - */ - virtual int copy_public_key(char* pkey, int32_t& pkey_size); - /** - * generate and copy the shared key. - * generate the shared key with peer public key. - * @param ppkey peer public key. - * @param ppkey_size the size of ppkey. - * @param skey the computed shared key. - * @param skey_size the max shared key size, output the actual shared key size. - * user should never ignore this size. - */ - virtual int copy_shared_key(const char* ppkey, int32_t ppkey_size, char* skey, int32_t& skey_size); - private: - virtual int do_initialize(); - }; - - // calc the offset of key, - // the key->offset cannot be used as the offset of key. - int srs_key_block_get_offset(key_block* key); - - // create new key block data. - // if created, user must free it by srs_key_block_free - void srs_key_block_init(key_block* key); - - // parse key block from c1s1. - // if created, user must free it by srs_key_block_free - // @c1s1_key_bytes the key start bytes, maybe c1s1 or c1s1+764 - int srs_key_block_parse(key_block* key, char* c1s1_key_bytes); - - // free the block data create by - // srs_key_block_init or srs_key_block_parse - void srs_key_block_free(key_block* key); - - // calc the offset of digest, - // the key->offset cannot be used as the offset of digest. - int srs_digest_block_get_offset(digest_block* digest); - - // create new digest block data. - // if created, user must free it by srs_digest_block_free - void srs_digest_block_init(digest_block* digest); - - // parse digest block from c1s1. - // if created, user must free it by srs_digest_block_free - // @c1s1_digest_bytes the digest start bytes, maybe c1s1 or c1s1+764 - int srs_digest_block_parse(digest_block* digest, char* c1s1_digest_bytes); + // create new digest block data. + // if created, user must free it by srs_digest_block_free + void init(); + + // calc the offset of digest, + // the key->offset cannot be used as the offset of digest. + int offsets(); - // free the block data create by - // srs_digest_block_init or srs_digest_block_parse - void srs_digest_block_free(digest_block* digest); + // parse digest block from c1s1. + // if created, user must free it by srs_digest_block_free + // @c1s1_digest_bytes the digest start bytes, maybe c1s1 or c1s1+764 + int parse(char* c1s1_digest_bytes); + + // free the block data create by + // srs_digest_block_init or srs_digest_block_parse + void free(); + }; /** * copy whole c1s1 to bytes. From 4b9875e279c73c5fac53f02e0a69abe7bbc67f47 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 29 Nov 2014 16:17:07 +0800 Subject: [PATCH 242/800] for bug #235, use strategy to implements the schema0 and schema1. --- trunk/src/rtmp/srs_protocol_handshake.cpp | 680 ++++++++++++++-------- trunk/src/rtmp/srs_protocol_handshake.hpp | 165 +++++- 2 files changed, 584 insertions(+), 261 deletions(-) diff --git a/trunk/src/rtmp/srs_protocol_handshake.cpp b/trunk/src/rtmp/srs_protocol_handshake.cpp index 143f966b92..a26ce2fa0b 100644 --- a/trunk/src/rtmp/srs_protocol_handshake.cpp +++ b/trunk/src/rtmp/srs_protocol_handshake.cpp @@ -638,6 +638,385 @@ namespace _srs_internal return bytes; } + c1s1_strategy::c1s1_strategy() + { + } + + c1s1_strategy::~c1s1_strategy() + { + } + + c1s1_strategy_schema0::c1s1_strategy_schema0() + { + key.init(); + digest.init(); + } + + c1s1_strategy_schema0::~c1s1_strategy_schema0() + { + key.free(); + digest.free(); + } + + srs_schema_type c1s1_strategy_schema0::schema() + { + return srs_schema0; + } + + char* c1s1_strategy_schema0::get_digest() + { + return digest.digest; + } + + void c1s1_strategy_schema0::dump(c1s1* owner, char* _c1s1) + { + srs_schema0_copy_to(_c1s1, true, owner->time, owner->version, &key, &digest); + } + + int c1s1_strategy_schema0::parse(char* _c1s1) + { + int ret = ERROR_SUCCESS; + + if ((ret = key.parse(_c1s1 + 8)) != ERROR_SUCCESS) { + srs_error("parse the c1 key failed. ret=%d", ret); + return ret; + } + + if ((ret = digest.parse(_c1s1 + 8 + 764)) != ERROR_SUCCESS) { + srs_error("parse the c1 digest failed. ret=%d", ret); + return ret; + } + + srs_verbose("parse c1 key-digest success"); + + return ret; + } + + int c1s1_strategy_schema0::c1_create(c1s1* owner) + { + int ret = ERROR_SUCCESS; + + // generate digest + char* c1_digest = NULL; + + if ((ret = calc_c1_digest(owner, c1_digest)) != ERROR_SUCCESS) { + srs_error("sign c1 error, failed to calc digest. ret=%d", ret); + return ret; + } + + srs_assert(c1_digest != NULL); + SrsAutoFree(char, c1_digest); + + memcpy(digest.digest, c1_digest, 32); + + return ret; + } + + int c1s1_strategy_schema0::c1_validate_digest(c1s1* owner, bool& is_valid) + { + int ret = ERROR_SUCCESS; + + char* c1_digest = NULL; + + if ((ret = calc_c1_digest(owner, c1_digest)) != ERROR_SUCCESS) { + srs_error("validate c1 error, failed to calc digest. ret=%d", ret); + return ret; + } + + srs_assert(c1_digest != NULL); + SrsAutoFree(char, c1_digest); + + is_valid = srs_bytes_equals(digest.digest, c1_digest, 32); + + return ret; + } + + int c1s1_strategy_schema0::s1_create(c1s1* owner) + { + int ret = ERROR_SUCCESS; + + SrsDH dh; + + // ensure generate 128bytes public key. + if ((ret = dh.initialize(true)) != ERROR_SUCCESS) { + return ret; + } + + // directly generate the public key. + // @see: https://github.com/winlinvip/simple-rtmp-server/issues/148 + int pkey_size = 128; + // TODO: FIXME: use c1 public key to calc the shared key. + if ((ret = dh.copy_public_key(key.key, pkey_size)) != ERROR_SUCCESS) { + srs_error("calc s1 key failed. ret=%d", ret); + return ret; + } + srs_assert(pkey_size == 128); + srs_verbose("calc s1 key success."); + + char* s1_digest = NULL; + if ((ret = calc_s1_digest(owner, s1_digest)) != ERROR_SUCCESS) { + srs_error("calc s1 digest failed. ret=%d", ret); + return ret; + } + srs_verbose("calc s1 digest success."); + + srs_assert(s1_digest != NULL); + SrsAutoFree(char, s1_digest); + + memcpy(digest.digest, s1_digest, 32); + srs_verbose("copy s1 key success."); + + return ret; + } + + int c1s1_strategy_schema0::s1_validate_digest(c1s1* owner, bool& is_valid) + { + int ret = ERROR_SUCCESS; + + char* s1_digest = NULL; + + if ((ret = calc_s1_digest(owner, s1_digest)) != ERROR_SUCCESS) { + srs_error("validate s1 error, failed to calc digest. ret=%d", ret); + return ret; + } + + srs_assert(s1_digest != NULL); + SrsAutoFree(char, s1_digest); + + is_valid = srs_bytes_equals(digest.digest, s1_digest, 32); + + return ret; + } + + int c1s1_strategy_schema0::calc_c1_digest(c1s1* owner, char*& c1_digest) + { + int ret = ERROR_SUCCESS; + + char* c1s1_joined_bytes = NULL; + + c1s1_joined_bytes = srs_bytes_join_schema0(owner->time, owner->version, &key, &digest); + + srs_assert(c1s1_joined_bytes != NULL); + SrsAutoFree(char, c1s1_joined_bytes); + + c1_digest = new char[__SRS_OpensslHashSize]; + if ((ret = openssl_HMACsha256(SrsGenuineFPKey, 30, c1s1_joined_bytes, 1536 - 32, c1_digest)) != ERROR_SUCCESS) { + srs_freep(c1_digest); + srs_error("calc digest for c1 failed. ret=%d", ret); + return ret; + } + srs_verbose("digest calculated for c1"); + + return ret; + } + + int c1s1_strategy_schema0::calc_s1_digest(c1s1* owner, char*& s1_digest) + { + int ret = ERROR_SUCCESS; + + char* c1s1_joined_bytes = NULL; + + c1s1_joined_bytes = srs_bytes_join_schema0(owner->time, owner->version, &key, &digest); + + srs_assert(c1s1_joined_bytes != NULL); + SrsAutoFree(char, c1s1_joined_bytes); + + s1_digest = new char[__SRS_OpensslHashSize]; + if ((ret = openssl_HMACsha256(SrsGenuineFMSKey, 36, c1s1_joined_bytes, 1536 - 32, s1_digest)) != ERROR_SUCCESS) { + srs_freep(s1_digest); + srs_error("calc digest for s1 failed. ret=%d", ret); + return ret; + } + srs_verbose("digest calculated for s1"); + + return ret; + } + + c1s1_strategy_schema1::c1s1_strategy_schema1() + { + key.init(); + digest.init(); + } + + c1s1_strategy_schema1::~c1s1_strategy_schema1() + { + key.free(); + digest.free(); + } + + srs_schema_type c1s1_strategy_schema1::schema() + { + return srs_schema1; + } + + char* c1s1_strategy_schema1::get_digest() + { + return digest.digest; + } + + void c1s1_strategy_schema1::dump(c1s1* owner, char* _c1s1) + { + srs_schema0_copy_to(_c1s1, true, owner->time, owner->version, &key, &digest); + } + + int c1s1_strategy_schema1::parse(char* _c1s1) + { + int ret = ERROR_SUCCESS; + + if ((ret = digest.parse(_c1s1 + 8)) != ERROR_SUCCESS) { + srs_error("parse the c1 digest failed. ret=%d", ret); + return ret; + } + + if ((ret = key.parse(_c1s1 + 8 + 764)) != ERROR_SUCCESS) { + srs_error("parse the c1 key failed. ret=%d", ret); + return ret; + } + + srs_verbose("parse c1 digest-key success"); + + return ret; + } + + int c1s1_strategy_schema1::c1_create(c1s1* owner) + { + int ret = ERROR_SUCCESS; + + // generate digest + char* c1_digest = NULL; + + if ((ret = calc_c1_digest(owner, c1_digest)) != ERROR_SUCCESS) { + srs_error("sign c1 error, failed to calc digest. ret=%d", ret); + return ret; + } + + srs_assert(c1_digest != NULL); + SrsAutoFree(char, c1_digest); + + memcpy(digest.digest, c1_digest, 32); + + return ret; + } + + int c1s1_strategy_schema1::c1_validate_digest(c1s1* owner, bool& is_valid) + { + int ret = ERROR_SUCCESS; + + char* c1_digest = NULL; + + if ((ret = calc_c1_digest(owner, c1_digest)) != ERROR_SUCCESS) { + srs_error("validate c1 error, failed to calc digest. ret=%d", ret); + return ret; + } + + srs_assert(c1_digest != NULL); + SrsAutoFree(char, c1_digest); + + is_valid = srs_bytes_equals(digest.digest, c1_digest, 32); + + return ret; + } + + int c1s1_strategy_schema1::s1_create(c1s1* owner) + { + int ret = ERROR_SUCCESS; + + SrsDH dh; + + // ensure generate 128bytes public key. + if ((ret = dh.initialize(true)) != ERROR_SUCCESS) { + return ret; + } + + // directly generate the public key. + // @see: https://github.com/winlinvip/simple-rtmp-server/issues/148 + int pkey_size = 128; + if ((ret = dh.copy_public_key(key.key, pkey_size)) != ERROR_SUCCESS) { + srs_error("calc s1 key failed. ret=%d", ret); + return ret; + } + srs_assert(pkey_size == 128); + srs_verbose("calc s1 key success."); + + char* s1_digest = NULL; + if ((ret = calc_s1_digest(owner, s1_digest)) != ERROR_SUCCESS) { + srs_error("calc s1 digest failed. ret=%d", ret); + return ret; + } + srs_verbose("calc s1 digest success."); + + srs_assert(s1_digest != NULL); + SrsAutoFree(char, s1_digest); + + memcpy(digest.digest, s1_digest, 32); + srs_verbose("copy s1 key success."); + + return ret; + } + + int c1s1_strategy_schema1::s1_validate_digest(c1s1* owner, bool& is_valid) + { + int ret = ERROR_SUCCESS; + + char* s1_digest = NULL; + + if ((ret = calc_s1_digest(owner, s1_digest)) != ERROR_SUCCESS) { + srs_error("validate s1 error, failed to calc digest. ret=%d", ret); + return ret; + } + + srs_assert(s1_digest != NULL); + SrsAutoFree(char, s1_digest); + + is_valid = srs_bytes_equals(digest.digest, s1_digest, 32); + + return ret; + } + + int c1s1_strategy_schema1::calc_c1_digest(c1s1* owner, char*& c1_digest) + { + int ret = ERROR_SUCCESS; + + char* c1s1_joined_bytes = NULL; + + c1s1_joined_bytes = srs_bytes_join_schema1(owner->time, owner->version, &digest, &key); + + srs_assert(c1s1_joined_bytes != NULL); + SrsAutoFree(char, c1s1_joined_bytes); + + c1_digest = new char[__SRS_OpensslHashSize]; + if ((ret = openssl_HMACsha256(SrsGenuineFPKey, 30, c1s1_joined_bytes, 1536 - 32, c1_digest)) != ERROR_SUCCESS) { + srs_freep(c1_digest); + srs_error("calc digest for c1 failed. ret=%d", ret); + return ret; + } + srs_verbose("digest calculated for c1"); + + return ret; + } + + int c1s1_strategy_schema1::calc_s1_digest(c1s1* owner, char*& s1_digest) + { + int ret = ERROR_SUCCESS; + + char* c1s1_joined_bytes = NULL; + + c1s1_joined_bytes = srs_bytes_join_schema1(owner->time, owner->version, &digest, &key); + + srs_assert(c1s1_joined_bytes != NULL); + SrsAutoFree(char, c1s1_joined_bytes); + + s1_digest = new char[__SRS_OpensslHashSize]; + if ((ret = openssl_HMACsha256(SrsGenuineFMSKey, 36, c1s1_joined_bytes, 1536 - 32, s1_digest)) != ERROR_SUCCESS) { + srs_freep(s1_digest); + srs_error("calc digest for s1 failed. ret=%d", ret); + return ret; + } + srs_verbose("digest calculated for s1"); + + return ret; + } + c2s2::c2s2() { srs_random_generate(random, 1504); @@ -762,314 +1141,129 @@ namespace _srs_internal // TODO: FIXME: move to the right position. c1s1::c1s1() { - schema = srs_schema_invalid; + payload = NULL; } c1s1::~c1s1() { - destroy_blocks(); + srs_freep(payload); + /* + void c1s1::destroy_blocks() + { + if (schema == srs_schema_invalid) { + return; + } + + if (schema == srs_schema0) { + block0.key.free(); + block1.digest.free(); + } else { + block0.digest.free(); + block1.key.free(); + } + }*/ + } + + srs_schema_type c1s1::schema() + { + srs_assert(payload != NULL); + return payload->schema(); } char* c1s1::get_digest() { - srs_assert(schema != srs_schema_invalid); - - if (schema == srs_schema0) { - return block1.digest.digest; - } else { - return block0.digest.digest; - } + srs_assert(payload != NULL); + return payload->get_digest(); } void c1s1::dump(char* _c1s1) { - srs_assert(schema != srs_schema_invalid); - - if (schema == srs_schema0) { - srs_schema0_copy_to(_c1s1, true, time, version, &block0.key, &block1.digest); - } else { - srs_schema1_copy_to(_c1s1, true, time, version, &block0.digest, &block1.key); - } + srs_assert(payload != NULL); + return payload->dump(this, _c1s1); } - int c1s1::parse(char* _c1s1, srs_schema_type _schema) + int c1s1::parse(char* _c1s1, srs_schema_type schema) { int ret = ERROR_SUCCESS; - if (_schema == srs_schema_invalid) { + if (schema != srs_schema0 && schema != srs_schema1) { ret = ERROR_RTMP_CH_SCHEMA; - srs_error("parse c1 failed. invalid schema=%d, ret=%d", _schema, ret); + srs_error("parse c1 failed. invalid schema=%d, ret=%d", schema, ret); return ret; } - destroy_blocks(); - - time = __srs_stream_read_4bytes(_c1s1); version = __srs_stream_read_4bytes(_c1s1 + 4); // client c1 version - if (_schema == srs_schema0) { - if ((ret = block0.key.parse(_c1s1 + 8)) != ERROR_SUCCESS) { - srs_error("parse the c1 key failed. ret=%d", ret); - return ret; - } - if ((ret = block1.digest.parse(_c1s1 + 8 + 764)) != ERROR_SUCCESS) { - srs_error("parse the c1 digest failed. ret=%d", ret); - return ret; - } - srs_verbose("parse c1 key-digest success"); - } else if (_schema == srs_schema1) { - if ((ret = block0.digest.parse(_c1s1 + 8)) != ERROR_SUCCESS) { - srs_error("parse the c1 key failed. ret=%d", ret); - return ret; - } - if ((ret = block1.key.parse(_c1s1 + 8 + 764)) != ERROR_SUCCESS) { - srs_error("parse the c1 digest failed. ret=%d", ret); - return ret; - } - srs_verbose("parse c1 digest-key success"); + srs_freep(payload); + if (schema == srs_schema0) { + payload = new c1s1_strategy_schema0(); } else { - ret = ERROR_RTMP_CH_SCHEMA; - srs_error("parse c1 failed. invalid schema=%d, ret=%d", _schema, ret); - return ret; + payload = new c1s1_strategy_schema1(); } - - schema = _schema; - - return ret; + + return payload->parse(_c1s1); } - int c1s1::c1_create(srs_schema_type _schema) + int c1s1::c1_create(srs_schema_type schema) { int ret = ERROR_SUCCESS; - if (_schema == srs_schema_invalid) { + if (schema != srs_schema0 && schema != srs_schema1) { ret = ERROR_RTMP_CH_SCHEMA; - srs_error("create c1 failed. invalid schema=%d, ret=%d", _schema, ret); + srs_error("create c1 failed. invalid schema=%d, ret=%d", schema, ret); return ret; } - destroy_blocks(); - // client c1 time and version time = ::time(NULL); version = 0x80000702; // client c1 version - + // generate signature by schema - if (_schema == srs_schema0) { - block0.key.init(); - block1.digest.init(); - } else { - block0.digest.init(); - block1.key.init(); - } - - schema = _schema; - - // generate digest - char* digest = NULL; - - if ((ret = calc_c1_digest(digest)) != ERROR_SUCCESS) { - srs_error("sign c1 error, failed to calc digest. ret=%d", ret); - return ret; - } - - srs_assert(digest != NULL); - SrsAutoFree(char, digest); - + srs_freep(payload); if (schema == srs_schema0) { - memcpy(block1.digest.digest, digest, 32); + payload = new c1s1_strategy_schema0(); } else { - memcpy(block0.digest.digest, digest, 32); + payload = new c1s1_strategy_schema1(); } - return ret; + return payload->c1_create(this); } int c1s1::c1_validate_digest(bool& is_valid) { is_valid = false; - int ret = ERROR_SUCCESS; - - char* c1_digest = NULL; - - if ((ret = calc_c1_digest(c1_digest)) != ERROR_SUCCESS) { - srs_error("validate c1 error, failed to calc digest. ret=%d", ret); - return ret; - } - - srs_assert(c1_digest != NULL); - SrsAutoFree(char, c1_digest); - - if (schema == srs_schema0) { - is_valid = srs_bytes_equals(block1.digest.digest, c1_digest, 32); - } else { - is_valid = srs_bytes_equals(block0.digest.digest, c1_digest, 32); - } - - return ret; + srs_assert(payload); + return payload->c1_validate_digest(this, is_valid); } int c1s1::s1_validate_digest(bool& is_valid) { is_valid = false; - int ret = ERROR_SUCCESS; - - char* s1_digest = NULL; - - if ((ret = calc_s1_digest(s1_digest)) != ERROR_SUCCESS) { - srs_error("validate s1 error, failed to calc digest. ret=%d", ret); - return ret; - } - - srs_assert(s1_digest != NULL); - SrsAutoFree(char, s1_digest); - - if (schema == srs_schema0) { - is_valid = srs_bytes_equals(block1.digest.digest, s1_digest, 32); - } else { - is_valid = srs_bytes_equals(block0.digest.digest, s1_digest, 32); - } - - return ret; + srs_assert(payload); + return payload->s1_validate_digest(this, is_valid); } int c1s1::s1_create(c1s1* c1) { int ret = ERROR_SUCCESS; - if (c1->schema == srs_schema_invalid) { + if (c1->schema() != srs_schema0 && c1->schema() != srs_schema1) { ret = ERROR_RTMP_CH_SCHEMA; - srs_error("create s1 failed. invalid schema=%d, ret=%d", c1->schema, ret); + srs_error("create s1 failed. invalid schema=%d, ret=%d", c1->schema(), ret); return ret; } - destroy_blocks(); - schema = c1->schema; - time = ::time(NULL); version = 0x01000504; // server s1 version - SrsDH dh; - - // ensure generate 128bytes public key. - if ((ret = dh.initialize(true)) != ERROR_SUCCESS) { - return ret; - } - - if (schema == srs_schema0) { - block0.key.init(); - block1.digest.init(); - - // directly generate the public key. - // @see: https://github.com/winlinvip/simple-rtmp-server/issues/148 - int pkey_size = 128; - if ((ret = dh.copy_public_key(block0.key.key, pkey_size)) != ERROR_SUCCESS) { - srs_error("calc s1 key failed. ret=%d", ret); - return ret; - } - srs_assert(pkey_size == 128); - } else { - block0.digest.init(); - block1.key.init(); - - // directly generate the public key. - // @see: https://github.com/winlinvip/simple-rtmp-server/issues/148 - int pkey_size = 128; - if ((ret = dh.copy_public_key(block1.key.key, pkey_size)) != ERROR_SUCCESS) { - srs_error("calc s1 key failed. ret=%d", ret); - return ret; - } - srs_assert(pkey_size == 128); - } - srs_verbose("calc s1 key success."); - - char* s1_digest = NULL; - if ((ret = calc_s1_digest(s1_digest)) != ERROR_SUCCESS) { - srs_error("calc s1 digest failed. ret=%d", ret); - return ret; - } - srs_verbose("calc s1 digest success."); - - srs_assert(s1_digest != NULL); - SrsAutoFree(char, s1_digest); - - if (schema == srs_schema0) { - memcpy(block1.digest.digest, s1_digest, 32); - } else { - memcpy(block0.digest.digest, s1_digest, 32); - } - srs_verbose("copy s1 key success."); - - return ret; - } - - int c1s1::calc_s1_digest(char*& digest) - { - int ret = ERROR_SUCCESS; - - srs_assert(schema == srs_schema0 || schema == srs_schema1); - - char* c1s1_joined_bytes = NULL; - - if (schema == srs_schema0) { - c1s1_joined_bytes = srs_bytes_join_schema0(time, version, &block0.key, &block1.digest); + srs_freep(payload); + if (c1->schema() == srs_schema0) { + payload = new c1s1_strategy_schema0(); } else { - c1s1_joined_bytes = srs_bytes_join_schema1(time, version, &block0.digest, &block1.key); - } - - srs_assert(c1s1_joined_bytes != NULL); - SrsAutoFree(char, c1s1_joined_bytes); - - digest = new char[__SRS_OpensslHashSize]; - if ((ret = openssl_HMACsha256(SrsGenuineFMSKey, 36, c1s1_joined_bytes, 1536 - 32, digest)) != ERROR_SUCCESS) { - srs_error("calc digest for s1 failed. ret=%d", ret); - return ret; + payload = new c1s1_strategy_schema1(); } - srs_verbose("digest calculated for s1"); - return ret; - } - - int c1s1::calc_c1_digest(char*& digest) - { - int ret = ERROR_SUCCESS; - - srs_assert(schema == srs_schema0 || schema == srs_schema1); - - char* c1s1_joined_bytes = NULL; - - if (schema == srs_schema0) { - c1s1_joined_bytes = srs_bytes_join_schema0(time, version, &block0.key, &block1.digest); - } else { - c1s1_joined_bytes = srs_bytes_join_schema1(time, version, &block0.digest, &block1.key); - } - - srs_assert(c1s1_joined_bytes != NULL); - SrsAutoFree(char, c1s1_joined_bytes); - - digest = new char[__SRS_OpensslHashSize]; - if ((ret = openssl_HMACsha256(SrsGenuineFPKey, 30, c1s1_joined_bytes, 1536 - 32, digest)) != ERROR_SUCCESS) { - srs_error("calc digest for c1 failed. ret=%d", ret); - return ret; - } - srs_verbose("digest calculated for c1"); - - return ret; - } - - void c1s1::destroy_blocks() - { - if (schema == srs_schema_invalid) { - return; - } - - if (schema == srs_schema0) { - block0.key.free(); - block1.digest.free(); - } else { - block0.digest.free(); - block1.key.free(); - } + return payload->s1_create(this); } } @@ -1190,6 +1384,7 @@ int SrsComplexHandshake::handshake_with_client(SrsHandshakeBytes* hs_bytes, ISrs // decode c1 c1s1 c1; // try schema0. + // @remark, use schema0 to make flash player happy. if ((ret = c1.parse(hs_bytes->c0c1 + 1, srs_schema0)) != ERROR_SUCCESS) { srs_error("parse c1 schema%d error. ret=%d", srs_schema0, ret); return ret; @@ -1197,6 +1392,7 @@ int SrsComplexHandshake::handshake_with_client(SrsHandshakeBytes* hs_bytes, ISrs // try schema1 bool is_valid = false; if ((ret = c1.c1_validate_digest(is_valid)) != ERROR_SUCCESS || !is_valid) { + srs_info("schema0 failed, try schema1."); if ((ret = c1.parse(hs_bytes->c0c1 + 1, srs_schema1)) != ERROR_SUCCESS) { srs_error("parse c1 schema%d error. ret=%d", srs_schema1, ret); return ret; @@ -1207,6 +1403,8 @@ int SrsComplexHandshake::handshake_with_client(SrsHandshakeBytes* hs_bytes, ISrs srs_info("all schema valid failed, try simple handshake. ret=%d", ret); return ret; } + } else { + srs_info("schema0 is ok."); } srs_verbose("decode c1 success."); @@ -1321,7 +1519,7 @@ int SrsComplexHandshake::handshake_with_server(SrsHandshakeBytes* hs_bytes, ISrs // verify s1s2 c1s1 s1; - if ((ret = s1.parse(hs_bytes->s0s1s2 + 1, c1.schema)) != ERROR_SUCCESS) { + if ((ret = s1.parse(hs_bytes->s0s1s2 + 1, c1.schema())) != ERROR_SUCCESS) { return ret; } diff --git a/trunk/src/rtmp/srs_protocol_handshake.hpp b/trunk/src/rtmp/srs_protocol_handshake.hpp index 6ad671202a..ad72486bff 100644 --- a/trunk/src/rtmp/srs_protocol_handshake.hpp +++ b/trunk/src/rtmp/srs_protocol_handshake.hpp @@ -219,6 +219,121 @@ namespace _srs_internal */ char* srs_bytes_join_schema1(int32_t time, int32_t version, digest_block* digest, key_block* key); + class c1s1; + + /** + * the c1s1 strategy, use schema0 or schema1. + * the template method class to defines common behaviors, + * while the concrete class to implements in schema0 or schema1. + */ + class c1s1_strategy + { + public: + c1s1_strategy(); + virtual ~c1s1_strategy(); + public: + /** + * get the scema. + */ + virtual srs_schema_type schema() = 0; + /** + * get the digest key. + */ + virtual char* get_digest() = 0; + /** + * copy to bytes. + */ + virtual void dump(c1s1* owner, char* _c1s1) = 0; + /** + * server: parse the c1s1, discovery the key and digest by schema. + * use the c1_validate_digest() to valid the digest of c1. + */ + virtual int parse(char* _c1s1) = 0; + public: + /** + * client: create and sign c1 by schema. + * sign the c1, generate the digest. + * calc_c1_digest(c1, schema) { + * get c1s1-joined from c1 by specified schema + * digest-data = HMACsha256(c1s1-joined, FPKey, 30) + * return digest-data; + * } + * random fill 1536bytes c1 // also fill the c1-128bytes-key + * time = time() // c1[0-3] + * version = [0x80, 0x00, 0x07, 0x02] // c1[4-7] + * schema = choose schema0 or schema1 + * digest-data = calc_c1_digest(c1, schema) + * copy digest-data to c1 + */ + virtual int c1_create(c1s1* owner) = 0; + /** + * server: validate the parsed c1 schema + */ + virtual int c1_validate_digest(c1s1* owner, bool& is_valid) = 0; + /** + * server: create and sign the s1 from c1. + */ + virtual int s1_create(c1s1* owner) = 0; + /** + * server: validate the parsed s1 schema + */ + virtual int s1_validate_digest(c1s1* owner, bool& is_valid) = 0; + }; + + /** + * c1s1 schema0 + * key: 764bytes + * digest: 764bytes + */ + class c1s1_strategy_schema0 : public c1s1_strategy + { + private: + key_block key; + digest_block digest; + public: + c1s1_strategy_schema0(); + virtual ~c1s1_strategy_schema0(); + public: + virtual srs_schema_type schema(); + virtual char* get_digest(); + virtual void dump(c1s1* owner, char* _c1s1); + virtual int parse(char* _c1s1); + virtual int c1_create(c1s1* owner); + virtual int c1_validate_digest(c1s1* owner, bool& is_valid); + virtual int s1_create(c1s1* owner); + virtual int s1_validate_digest(c1s1* owner, bool& is_valid); + private: + virtual int calc_c1_digest(c1s1* owner, char*& c1_digest); + virtual int calc_s1_digest(c1s1* owner, char*& s1_digest); + }; + + /** + * c1s1 schema1 + * digest: 764bytes + * key: 764bytes + */ + class c1s1_strategy_schema1 : public c1s1_strategy + { + private: + digest_block digest; + key_block key; + public: + c1s1_strategy_schema1(); + virtual ~c1s1_strategy_schema1(); + public: + virtual srs_schema_type schema(); + virtual char* get_digest(); + virtual void dump(c1s1* owner, char* _c1s1); + virtual int parse(char* _c1s1); + virtual int c1_create(c1s1* owner); + virtual int c1_validate_digest(c1s1* owner, bool& is_valid); + virtual int s1_create(c1s1* owner); + virtual int s1_validate_digest(c1s1* owner, bool& is_valid); + private: + virtual int calc_c1_digest(c1s1* owner, char*& c1_digest); + virtual int calc_s1_digest(c1s1* owner, char*& s1_digest); + }; + /** * c1s1 schema0 * time: 4bytes @@ -235,30 +350,20 @@ namespace _srs_internal class c1s1 { public: - union block { - key_block key; - digest_block digest; - }; - // 4bytes int32_t time; // 4bytes int32_t version; - // 764bytes - // if schema0, use key - // if schema1, use digest - block block0; - // 764bytes - // if schema0, use digest - // if schema1, use key - block block1; - - // the logic schema - srs_schema_type schema; + // 764bytes+764bytes + c1s1_strategy* payload; c1s1(); virtual ~c1s1(); /** + * get the scema. + */ + virtual srs_schema_type schema(); + /** * get the digest key. */ virtual char* get_digest(); @@ -269,6 +374,7 @@ namespace _srs_internal /** * server: parse the c1s1, discovery the key and digest by schema. * use the c1_validate_digest() to valid the digest of c1. + * use the s1_validate_digest() to valid the digest of s1. */ virtual int parse(char* _c1s1, srs_schema_type _schema); @@ -294,16 +400,35 @@ namespace _srs_internal virtual int c1_validate_digest(bool& is_valid); /** * server: create and sign the s1 from c1. + * // decode c1 try schema0 then schema1 + * c1-digest-data = get-c1-digest-data(schema0) + * if c1-digest-data equals to calc_c1_digest(c1, schema0) { + * c1-key-data = get-c1-key-data(schema0) + * schema = schema0 + * } else { + * c1-digest-data = get-c1-digest-data(schema1) + * if c1-digest-data not equals to calc_c1_digest(c1, schema1) { + * switch to simple handshake. + * return + * } + * c1-key-data = get-c1-key-data(schema1) + * schema = schema1 + * } + * + * // generate s1 + * random fill 1536bytes s1 + * time = time() // c1[0-3] + * version = [0x04, 0x05, 0x00, 0x01] // s1[4-7] + * s1-key-data=shared_key=DH_compute_key(peer_pub_key=c1-key-data) + * get c1s1-joined by specified schema + * s1-digest-data = HMACsha256(c1s1-joined, FMSKey, 36) + * copy s1-digest-data and s1-key-data to s1. */ virtual int s1_create(c1s1* c1); /** * server: validate the parsed s1 schema */ virtual int s1_validate_digest(bool& is_valid); - private: - virtual int calc_s1_digest(char*& digest); - virtual int calc_c1_digest(char*& digest); - virtual void destroy_blocks(); }; /** From 646d6f667365f7fc181f0c9f0715da30bc11787f Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 29 Nov 2014 17:04:24 +0800 Subject: [PATCH 243/800] for bug #235, refine schema0 and schema1, extract dup code. --- trunk/src/rtmp/srs_protocol_handshake.cpp | 424 ++++++---------------- trunk/src/rtmp/srs_protocol_handshake.hpp | 96 ++--- 2 files changed, 156 insertions(+), 364 deletions(-) diff --git a/trunk/src/rtmp/srs_protocol_handshake.cpp b/trunk/src/rtmp/srs_protocol_handshake.cpp index a26ce2fa0b..807208c99b 100644 --- a/trunk/src/rtmp/srs_protocol_handshake.cpp +++ b/trunk/src/rtmp/srs_protocol_handshake.cpp @@ -515,184 +515,29 @@ namespace _srs_internal } } - void __srs_time_copy_to(char*& pp, int32_t time) - { - // 4bytes time - __srs_stream_write_4bytes(pp, time); - pp += 4; - } - void __srs_version_copy_to(char*& pp, int32_t version) - { - // 4bytes version - __srs_stream_write_4bytes(pp, version); - pp += 4; - } - void __srs_key_copy_to(char*& pp, key_block* key) - { - // 764bytes key block - if (key->random0_size > 0) { - memcpy(pp, key->random0, key->random0_size); - } - pp += key->random0_size; - - memcpy(pp, key->key, sizeof(key->key)); - pp += sizeof(key->key); - - if (key->random1_size > 0) { - memcpy(pp, key->random1, key->random1_size); - } - pp += key->random1_size; - - __srs_stream_write_4bytes(pp, key->offset); - pp += 4; - } - void __srs_digest_copy_to(char*& pp, digest_block* digest, bool with_digest) - { - // 732bytes digest block without the 32bytes digest-data - // nbytes digest block part1 - __srs_stream_write_4bytes(pp, digest->offset); - pp += 4; - - // digest random padding. - if (digest->random0_size > 0) { - memcpy(pp, digest->random0, digest->random0_size); - } - pp += digest->random0_size; - - // digest - if (with_digest) { - memcpy(pp, digest->digest, 32); - pp += 32; - } - - // nbytes digest block part2 - if (digest->random1_size > 0) { - memcpy(pp, digest->random1, digest->random1_size); - } - pp += digest->random1_size; - } - - /** - * copy whole c1s1 to bytes. - */ - void srs_schema0_copy_to(char* bytes, bool with_digest, - int32_t time, int32_t version, key_block* key, digest_block* digest) - { - char* pp = bytes; - - __srs_time_copy_to(pp, time); - __srs_version_copy_to(pp, version); - __srs_key_copy_to(pp, key); - __srs_digest_copy_to(pp, digest, with_digest); - - if (with_digest) { - srs_assert(pp - bytes == 1536); - } else { - srs_assert(pp - bytes == 1536 - 32); - } - } - void srs_schema1_copy_to(char* bytes, bool with_digest, - int32_t time, int32_t version, digest_block* digest, key_block* key) - { - char* pp = bytes; - - __srs_time_copy_to(pp, time); - __srs_version_copy_to(pp, version); - __srs_digest_copy_to(pp, digest, with_digest); - __srs_key_copy_to(pp, key); - - if (with_digest) { - srs_assert(pp - bytes == 1536); - } else { - srs_assert(pp - bytes == 1536 - 32); - } - } - - /** - * c1s1 is splited by digest: - * c1s1-part1: n bytes (time, version, key and digest-part1). - * digest-data: 32bytes - * c1s1-part2: (1536-n-32)bytes (digest-part2) - */ - char* srs_bytes_join_schema0(int32_t time, int32_t version, key_block* key, digest_block* digest) - { - char* bytes = new char[1536 -32]; - - srs_schema0_copy_to(bytes, false, time, version, key, digest); - - return bytes; - } - - /** - * c1s1 is splited by digest: - * c1s1-part1: n bytes (time, version and digest-part1). - * digest-data: 32bytes - * c1s1-part2: (1536-n-32)bytes (digest-part2 and key) - */ - char* srs_bytes_join_schema1(int32_t time, int32_t version, digest_block* digest, key_block* key) - { - char* bytes = new char[1536 -32]; - - srs_schema1_copy_to(bytes, false, time, version, digest, key); - - return bytes; - } - c1s1_strategy::c1s1_strategy() - { - } - - c1s1_strategy::~c1s1_strategy() - { - } - - c1s1_strategy_schema0::c1s1_strategy_schema0() { key.init(); digest.init(); } - c1s1_strategy_schema0::~c1s1_strategy_schema0() + c1s1_strategy::~c1s1_strategy() { key.free(); digest.free(); } - srs_schema_type c1s1_strategy_schema0::schema() - { - return srs_schema0; - } - - char* c1s1_strategy_schema0::get_digest() + char* c1s1_strategy::get_digest() { return digest.digest; } - void c1s1_strategy_schema0::dump(c1s1* owner, char* _c1s1) + void c1s1_strategy::dump(c1s1* owner, char* _c1s1) { - srs_schema0_copy_to(_c1s1, true, owner->time, owner->version, &key, &digest); + copy_to(owner, _c1s1, true); } - int c1s1_strategy_schema0::parse(char* _c1s1) - { - int ret = ERROR_SUCCESS; - - if ((ret = key.parse(_c1s1 + 8)) != ERROR_SUCCESS) { - srs_error("parse the c1 key failed. ret=%d", ret); - return ret; - } - - if ((ret = digest.parse(_c1s1 + 8 + 764)) != ERROR_SUCCESS) { - srs_error("parse the c1 digest failed. ret=%d", ret); - return ret; - } - - srs_verbose("parse c1 key-digest success"); - - return ret; - } - - int c1s1_strategy_schema0::c1_create(c1s1* owner) + int c1s1_strategy::c1_create(c1s1* owner) { int ret = ERROR_SUCCESS; @@ -712,7 +557,7 @@ namespace _srs_internal return ret; } - int c1s1_strategy_schema0::c1_validate_digest(c1s1* owner, bool& is_valid) + int c1s1_strategy::c1_validate_digest(c1s1* owner, bool& is_valid) { int ret = ERROR_SUCCESS; @@ -731,7 +576,7 @@ namespace _srs_internal return ret; } - int c1s1_strategy_schema0::s1_create(c1s1* owner) + int c1s1_strategy::s1_create(c1s1* owner) { int ret = ERROR_SUCCESS; @@ -769,7 +614,7 @@ namespace _srs_internal return ret; } - int c1s1_strategy_schema0::s1_validate_digest(c1s1* owner, bool& is_valid) + int c1s1_strategy::s1_validate_digest(c1s1* owner, bool& is_valid) { int ret = ERROR_SUCCESS; @@ -788,16 +633,20 @@ namespace _srs_internal return ret; } - int c1s1_strategy_schema0::calc_c1_digest(c1s1* owner, char*& c1_digest) + int c1s1_strategy::calc_c1_digest(c1s1* owner, char*& c1_digest) { int ret = ERROR_SUCCESS; - char* c1s1_joined_bytes = NULL; - - c1s1_joined_bytes = srs_bytes_join_schema0(owner->time, owner->version, &key, &digest); - - srs_assert(c1s1_joined_bytes != NULL); + /** + * c1s1 is splited by digest: + * c1s1-part1: n bytes (time, version, key and digest-part1). + * digest-data: 32bytes + * c1s1-part2: (1536-n-32)bytes (digest-part2) + * @return a new allocated bytes, user must free it. + */ + char* c1s1_joined_bytes = new char[1536 -32]; SrsAutoFree(char, c1s1_joined_bytes); + copy_to(owner, c1s1_joined_bytes, false); c1_digest = new char[__SRS_OpensslHashSize]; if ((ret = openssl_HMACsha256(SrsGenuineFPKey, 30, c1s1_joined_bytes, 1536 - 32, c1_digest)) != ERROR_SUCCESS) { @@ -810,16 +659,20 @@ namespace _srs_internal return ret; } - int c1s1_strategy_schema0::calc_s1_digest(c1s1* owner, char*& s1_digest) + int c1s1_strategy::calc_s1_digest(c1s1* owner, char*& s1_digest) { int ret = ERROR_SUCCESS; - - char* c1s1_joined_bytes = NULL; - - c1s1_joined_bytes = srs_bytes_join_schema0(owner->time, owner->version, &key, &digest); - - srs_assert(c1s1_joined_bytes != NULL); + + /** + * c1s1 is splited by digest: + * c1s1-part1: n bytes (time, version, key and digest-part1). + * digest-data: 32bytes + * c1s1-part2: (1536-n-32)bytes (digest-part2) + * @return a new allocated bytes, user must free it. + */ + char* c1s1_joined_bytes = new char[1536 -32]; SrsAutoFree(char, c1s1_joined_bytes); + copy_to(owner, c1s1_joined_bytes, false); s1_digest = new char[__SRS_OpensslHashSize]; if ((ret = openssl_HMACsha256(SrsGenuineFMSKey, 36, c1s1_joined_bytes, 1536 - 32, s1_digest)) != ERROR_SUCCESS) { @@ -832,189 +685,152 @@ namespace _srs_internal return ret; } - c1s1_strategy_schema1::c1s1_strategy_schema1() + void c1s1_strategy::copy_time_version(char*& pp, c1s1* owner) { - key.init(); - digest.init(); + // 4bytes time + __srs_stream_write_4bytes(pp, owner->time); + pp += 4; + // 4bytes version + __srs_stream_write_4bytes(pp, owner->version); + pp += 4; } - - c1s1_strategy_schema1::~c1s1_strategy_schema1() + void c1s1_strategy::copy_key(char*& pp) { - key.free(); - digest.free(); + // 764bytes key block + if (key.random0_size > 0) { + memcpy(pp, key.random0, key.random0_size); + } + pp += key.random0_size; + + memcpy(pp, key.key, sizeof(key.key)); + pp += sizeof(key.key); + + if (key.random1_size > 0) { + memcpy(pp, key.random1, key.random1_size); + } + pp += key.random1_size; + + __srs_stream_write_4bytes(pp, key.offset); + pp += 4; + } + void c1s1_strategy::digest_key(char*& pp, bool with_digest) + { + // 732bytes digest block without the 32bytes digest-data + // nbytes digest block part1 + __srs_stream_write_4bytes(pp, digest.offset); + pp += 4; + + // digest random padding. + if (digest.random0_size > 0) { + memcpy(pp, digest.random0, digest.random0_size); + } + pp += digest.random0_size; + + // digest + if (with_digest) { + memcpy(pp, digest.digest, 32); + pp += 32; + } + + // nbytes digest block part2 + if (digest.random1_size > 0) { + memcpy(pp, digest.random1, digest.random1_size); + } + pp += digest.random1_size; } - srs_schema_type c1s1_strategy_schema1::schema() + c1s1_strategy_schema0::c1s1_strategy_schema0() { - return srs_schema1; } - char* c1s1_strategy_schema1::get_digest() + c1s1_strategy_schema0::~c1s1_strategy_schema0() { - return digest.digest; } - void c1s1_strategy_schema1::dump(c1s1* owner, char* _c1s1) + srs_schema_type c1s1_strategy_schema0::schema() { - srs_schema0_copy_to(_c1s1, true, owner->time, owner->version, &key, &digest); + return srs_schema0; } - int c1s1_strategy_schema1::parse(char* _c1s1) + int c1s1_strategy_schema0::parse(char* _c1s1) { int ret = ERROR_SUCCESS; - if ((ret = digest.parse(_c1s1 + 8)) != ERROR_SUCCESS) { - srs_error("parse the c1 digest failed. ret=%d", ret); + if ((ret = key.parse(_c1s1 + 8)) != ERROR_SUCCESS) { + srs_error("parse the c1 key failed. ret=%d", ret); return ret; } - if ((ret = key.parse(_c1s1 + 8 + 764)) != ERROR_SUCCESS) { - srs_error("parse the c1 key failed. ret=%d", ret); + if ((ret = digest.parse(_c1s1 + 8 + 764)) != ERROR_SUCCESS) { + srs_error("parse the c1 digest failed. ret=%d", ret); return ret; } - srs_verbose("parse c1 digest-key success"); + srs_verbose("parse c1 key-digest success"); return ret; } - int c1s1_strategy_schema1::c1_create(c1s1* owner) + void c1s1_strategy_schema0::copy_to(c1s1* owner, char* bytes, bool with_digest) { - int ret = ERROR_SUCCESS; - - // generate digest - char* c1_digest = NULL; + char* pp = bytes; + + copy_time_version(pp, owner); + copy_key(pp); + digest_key(pp, with_digest); - if ((ret = calc_c1_digest(owner, c1_digest)) != ERROR_SUCCESS) { - srs_error("sign c1 error, failed to calc digest. ret=%d", ret); - return ret; + if (with_digest) { + srs_assert(pp - bytes == 1536); + } else { + srs_assert(pp - bytes == 1536 - 32); } - - srs_assert(c1_digest != NULL); - SrsAutoFree(char, c1_digest); - - memcpy(digest.digest, c1_digest, 32); - - return ret; } - int c1s1_strategy_schema1::c1_validate_digest(c1s1* owner, bool& is_valid) + c1s1_strategy_schema1::c1s1_strategy_schema1() { - int ret = ERROR_SUCCESS; - - char* c1_digest = NULL; - - if ((ret = calc_c1_digest(owner, c1_digest)) != ERROR_SUCCESS) { - srs_error("validate c1 error, failed to calc digest. ret=%d", ret); - return ret; - } - - srs_assert(c1_digest != NULL); - SrsAutoFree(char, c1_digest); - - is_valid = srs_bytes_equals(digest.digest, c1_digest, 32); - - return ret; } - int c1s1_strategy_schema1::s1_create(c1s1* owner) + c1s1_strategy_schema1::~c1s1_strategy_schema1() { - int ret = ERROR_SUCCESS; - - SrsDH dh; - - // ensure generate 128bytes public key. - if ((ret = dh.initialize(true)) != ERROR_SUCCESS) { - return ret; - } - - // directly generate the public key. - // @see: https://github.com/winlinvip/simple-rtmp-server/issues/148 - int pkey_size = 128; - if ((ret = dh.copy_public_key(key.key, pkey_size)) != ERROR_SUCCESS) { - srs_error("calc s1 key failed. ret=%d", ret); - return ret; - } - srs_assert(pkey_size == 128); - srs_verbose("calc s1 key success."); - - char* s1_digest = NULL; - if ((ret = calc_s1_digest(owner, s1_digest)) != ERROR_SUCCESS) { - srs_error("calc s1 digest failed. ret=%d", ret); - return ret; - } - srs_verbose("calc s1 digest success."); - - srs_assert(s1_digest != NULL); - SrsAutoFree(char, s1_digest); - - memcpy(digest.digest, s1_digest, 32); - srs_verbose("copy s1 key success."); - - return ret; } - int c1s1_strategy_schema1::s1_validate_digest(c1s1* owner, bool& is_valid) + srs_schema_type c1s1_strategy_schema1::schema() { - int ret = ERROR_SUCCESS; - - char* s1_digest = NULL; - - if ((ret = calc_s1_digest(owner, s1_digest)) != ERROR_SUCCESS) { - srs_error("validate s1 error, failed to calc digest. ret=%d", ret); - return ret; - } - - srs_assert(s1_digest != NULL); - SrsAutoFree(char, s1_digest); - - is_valid = srs_bytes_equals(digest.digest, s1_digest, 32); - - return ret; + return srs_schema1; } - int c1s1_strategy_schema1::calc_c1_digest(c1s1* owner, char*& c1_digest) + int c1s1_strategy_schema1::parse(char* _c1s1) { int ret = ERROR_SUCCESS; - - char* c1s1_joined_bytes = NULL; - - c1s1_joined_bytes = srs_bytes_join_schema1(owner->time, owner->version, &digest, &key); - srs_assert(c1s1_joined_bytes != NULL); - SrsAutoFree(char, c1s1_joined_bytes); + if ((ret = digest.parse(_c1s1 + 8)) != ERROR_SUCCESS) { + srs_error("parse the c1 digest failed. ret=%d", ret); + return ret; + } - c1_digest = new char[__SRS_OpensslHashSize]; - if ((ret = openssl_HMACsha256(SrsGenuineFPKey, 30, c1s1_joined_bytes, 1536 - 32, c1_digest)) != ERROR_SUCCESS) { - srs_freep(c1_digest); - srs_error("calc digest for c1 failed. ret=%d", ret); + if ((ret = key.parse(_c1s1 + 8 + 764)) != ERROR_SUCCESS) { + srs_error("parse the c1 key failed. ret=%d", ret); return ret; } - srs_verbose("digest calculated for c1"); + + srs_verbose("parse c1 digest-key success"); return ret; } - int c1s1_strategy_schema1::calc_s1_digest(c1s1* owner, char*& s1_digest) + void c1s1_strategy_schema1::copy_to(c1s1* owner, char* bytes, bool with_digest) { - int ret = ERROR_SUCCESS; - - char* c1s1_joined_bytes = NULL; + char* pp = bytes; - c1s1_joined_bytes = srs_bytes_join_schema1(owner->time, owner->version, &digest, &key); + copy_time_version(pp, owner); + digest_key(pp, with_digest); + copy_key(pp); - srs_assert(c1s1_joined_bytes != NULL); - SrsAutoFree(char, c1s1_joined_bytes); - - s1_digest = new char[__SRS_OpensslHashSize]; - if ((ret = openssl_HMACsha256(SrsGenuineFMSKey, 36, c1s1_joined_bytes, 1536 - 32, s1_digest)) != ERROR_SUCCESS) { - srs_freep(s1_digest); - srs_error("calc digest for s1 failed. ret=%d", ret); - return ret; + if (with_digest) { + srs_assert(pp - bytes == 1536); + } else { + srs_assert(pp - bytes == 1536 - 32); } - srs_verbose("digest calculated for s1"); - - return ret; } c2s2::c2s2() diff --git a/trunk/src/rtmp/srs_protocol_handshake.hpp b/trunk/src/rtmp/srs_protocol_handshake.hpp index ad72486bff..871723298b 100644 --- a/trunk/src/rtmp/srs_protocol_handshake.hpp +++ b/trunk/src/rtmp/srs_protocol_handshake.hpp @@ -193,32 +193,6 @@ namespace _srs_internal void free(); }; - /** - * copy whole c1s1 to bytes. - */ - void srs_schema0_copy_to(char* bytes, bool with_digest, - int32_t time, int32_t version, key_block* key, digest_block* digest); - void srs_schema1_copy_to(char* bytes, bool with_digest, - int32_t time, int32_t version, digest_block* digest, key_block* key); - - /** - * c1s1 is splited by digest: - * c1s1-part1: n bytes (time, version, key and digest-part1). - * digest-data: 32bytes - * c1s1-part2: (1536-n-32)bytes (digest-part2) - * @return a new allocated bytes, user must free it. - */ - char* srs_bytes_join_schema0(int32_t time, int32_t version, key_block* key, digest_block* digest); - - /** - * c1s1 is splited by digest: - * c1s1-part1: n bytes (time, version and digest-part1). - * digest-data: 32bytes - * c1s1-part2: (1536-n-32)bytes (digest-part2 and key) - * @return a new allocated bytes, user must free it. - */ - char* srs_bytes_join_schema1(int32_t time, int32_t version, digest_block* digest, key_block* key); - class c1s1; /** @@ -228,6 +202,9 @@ namespace _srs_internal */ class c1s1_strategy { + protected: + key_block key; + digest_block digest; public: c1s1_strategy(); virtual ~c1s1_strategy(); @@ -239,11 +216,11 @@ namespace _srs_internal /** * get the digest key. */ - virtual char* get_digest() = 0; + virtual char* get_digest(); /** * copy to bytes. */ - virtual void dump(c1s1* owner, char* _c1s1) = 0; + virtual void dump(c1s1* owner, char* _c1s1); /** * server: parse the c1s1, discovery the key and digest by schema. * use the c1_validate_digest() to valid the digest of c1. @@ -265,19 +242,29 @@ namespace _srs_internal * digest-data = calc_c1_digest(c1, schema) * copy digest-data to c1 */ - virtual int c1_create(c1s1* owner) = 0; + virtual int c1_create(c1s1* owner); /** * server: validate the parsed c1 schema */ - virtual int c1_validate_digest(c1s1* owner, bool& is_valid) = 0; + virtual int c1_validate_digest(c1s1* owner, bool& is_valid); /** * server: create and sign the s1 from c1. */ - virtual int s1_create(c1s1* owner) = 0; + virtual int s1_create(c1s1* owner); /** * server: validate the parsed s1 schema */ - virtual int s1_validate_digest(c1s1* owner, bool& is_valid) = 0; + virtual int s1_validate_digest(c1s1* owner, bool& is_valid); + protected: + virtual int calc_c1_digest(c1s1* owner, char*& c1_digest); + virtual int calc_s1_digest(c1s1* owner, char*& s1_digest); + /** + * copy whole c1s1 to bytes. + */ + virtual void copy_to(c1s1* owner, char* bytes, bool with_digest) = 0; + virtual void copy_time_version(char*& pp, c1s1* owner); + virtual void copy_key(char*& pp); + virtual void digest_key(char*& pp, bool with_digest); }; /** @@ -287,24 +274,17 @@ namespace _srs_internal */ class c1s1_strategy_schema0 : public c1s1_strategy { - private: - key_block key; - digest_block digest; public: c1s1_strategy_schema0(); virtual ~c1s1_strategy_schema0(); public: virtual srs_schema_type schema(); - virtual char* get_digest(); - virtual void dump(c1s1* owner, char* _c1s1); virtual int parse(char* _c1s1); - virtual int c1_create(c1s1* owner); - virtual int c1_validate_digest(c1s1* owner, bool& is_valid); - virtual int s1_create(c1s1* owner); - virtual int s1_validate_digest(c1s1* owner, bool& is_valid); private: - virtual int calc_c1_digest(c1s1* owner, char*& c1_digest); - virtual int calc_s1_digest(c1s1* owner, char*& s1_digest); + /** + * copy whole c1s1 to bytes. + */ + virtual void copy_to(c1s1* owner, char* bytes, bool with_digest); }; /** @@ -314,24 +294,17 @@ namespace _srs_internal */ class c1s1_strategy_schema1 : public c1s1_strategy { - private: - digest_block digest; - key_block key; public: c1s1_strategy_schema1(); virtual ~c1s1_strategy_schema1(); public: virtual srs_schema_type schema(); - virtual char* get_digest(); - virtual void dump(c1s1* owner, char* _c1s1); virtual int parse(char* _c1s1); - virtual int c1_create(c1s1* owner); - virtual int c1_validate_digest(c1s1* owner, bool& is_valid); - virtual int s1_create(c1s1* owner); - virtual int s1_validate_digest(c1s1* owner, bool& is_valid); private: - virtual int calc_c1_digest(c1s1* owner, char*& c1_digest); - virtual int calc_s1_digest(c1s1* owner, char*& s1_digest); + /** + * copy whole c1s1 to bytes. + */ + virtual void copy_to(c1s1* owner, char* bytes, bool with_digest); }; /** @@ -356,9 +329,10 @@ namespace _srs_internal int32_t version; // 764bytes+764bytes c1s1_strategy* payload; - + public: c1s1(); virtual ~c1s1(); + public: /** * get the scema. */ @@ -367,6 +341,7 @@ namespace _srs_internal * get the digest key. */ virtual char* get_digest(); + public: /** * copy to bytes. */ @@ -377,7 +352,7 @@ namespace _srs_internal * use the s1_validate_digest() to valid the digest of s1. */ virtual int parse(char* _c1s1, srs_schema_type _schema); - + public: /** * client: create and sign c1 by schema. * sign the c1, generate the digest. @@ -398,6 +373,7 @@ namespace _srs_internal * server: validate the parsed c1 schema */ virtual int c1_validate_digest(bool& is_valid); + public: /** * server: create and sign the s1 from c1. * // decode c1 try schema0 then schema1 @@ -442,10 +418,10 @@ namespace _srs_internal public: char random[1504]; char digest[32]; - + public: c2s2(); virtual ~c2s2(); - + public: /** * copy to bytes. */ @@ -454,7 +430,7 @@ namespace _srs_internal * parse the c2s2 */ virtual void parse(char* _c2s2); - + public: /** * create c2. * random fill c2s2 1536 bytes @@ -469,7 +445,7 @@ namespace _srs_internal * validate the c2 from client. */ virtual int c2_validate(c1s1* s1, bool& is_valid); - + public: /** * create s2. * random fill c2s2 1536 bytes From 6d49cddc9deaaff5288be8ca629782001cdcaf91 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 29 Nov 2014 17:05:54 +0800 Subject: [PATCH 244/800] for bug #235, refine code order of c1s1 and c2s2 --- trunk/src/rtmp/srs_protocol_handshake.cpp | 256 +++++++++++----------- 1 file changed, 128 insertions(+), 128 deletions(-) diff --git a/trunk/src/rtmp/srs_protocol_handshake.cpp b/trunk/src/rtmp/srs_protocol_handshake.cpp index 807208c99b..a612bfa2c7 100644 --- a/trunk/src/rtmp/srs_protocol_handshake.cpp +++ b/trunk/src/rtmp/srs_protocol_handshake.cpp @@ -833,127 +833,6 @@ namespace _srs_internal } } - c2s2::c2s2() - { - srs_random_generate(random, 1504); - - int size = snprintf(random, 1504, "%s", RTMP_SIG_SRS_HANDSHAKE); - srs_assert(++size < 1504); - snprintf(random + 1504 - size, size, "%s", RTMP_SIG_SRS_HANDSHAKE); - - srs_random_generate(digest, 32); - } - - c2s2::~c2s2() - { - } - - void c2s2::dump(char* _c2s2) - { - memcpy(_c2s2, random, 1504); - memcpy(_c2s2 + 1504, digest, 32); - } - - void c2s2::parse(char* _c2s2) - { - memcpy(random, _c2s2, 1504); - memcpy(digest, _c2s2 + 1504, 32); - } - - int c2s2::c2_create(c1s1* s1) - { - int ret = ERROR_SUCCESS; - - char temp_key[__SRS_OpensslHashSize]; - if ((ret = openssl_HMACsha256(SrsGenuineFPKey, 62, s1->get_digest(), 32, temp_key)) != ERROR_SUCCESS) { - srs_error("create c2 temp key failed. ret=%d", ret); - return ret; - } - srs_verbose("generate c2 temp key success."); - - char _digest[__SRS_OpensslHashSize]; - if ((ret = openssl_HMACsha256(temp_key, 32, random, 1504, _digest)) != ERROR_SUCCESS) { - srs_error("create c2 digest failed. ret=%d", ret); - return ret; - } - srs_verbose("generate c2 digest success."); - - memcpy(digest, _digest, 32); - - return ret; - } - - int c2s2::c2_validate(c1s1* s1, bool& is_valid) - { - is_valid = false; - int ret = ERROR_SUCCESS; - - char temp_key[__SRS_OpensslHashSize]; - if ((ret = openssl_HMACsha256(SrsGenuineFPKey, 62, s1->get_digest(), 32, temp_key)) != ERROR_SUCCESS) { - srs_error("create c2 temp key failed. ret=%d", ret); - return ret; - } - srs_verbose("generate c2 temp key success."); - - char _digest[__SRS_OpensslHashSize]; - if ((ret = openssl_HMACsha256(temp_key, 32, random, 1504, _digest)) != ERROR_SUCCESS) { - srs_error("create c2 digest failed. ret=%d", ret); - return ret; - } - srs_verbose("generate c2 digest success."); - - is_valid = srs_bytes_equals(digest, _digest, 32); - - return ret; - } - - int c2s2::s2_create(c1s1* c1) - { - int ret = ERROR_SUCCESS; - - char temp_key[__SRS_OpensslHashSize]; - if ((ret = openssl_HMACsha256(SrsGenuineFMSKey, 68, c1->get_digest(), 32, temp_key)) != ERROR_SUCCESS) { - srs_error("create s2 temp key failed. ret=%d", ret); - return ret; - } - srs_verbose("generate s2 temp key success."); - - char _digest[__SRS_OpensslHashSize]; - if ((ret = openssl_HMACsha256(temp_key, 32, random, 1504, _digest)) != ERROR_SUCCESS) { - srs_error("create s2 digest failed. ret=%d", ret); - return ret; - } - srs_verbose("generate s2 digest success."); - - memcpy(digest, _digest, 32); - - return ret; - } - - int c2s2::s2_validate(c1s1* c1, bool& is_valid) - { - is_valid = false; - int ret = ERROR_SUCCESS; - - char temp_key[__SRS_OpensslHashSize]; - if ((ret = openssl_HMACsha256(SrsGenuineFMSKey, 68, c1->get_digest(), 32, temp_key)) != ERROR_SUCCESS) { - srs_error("create s2 temp key failed. ret=%d", ret); - return ret; - } - srs_verbose("generate s2 temp key success."); - - char _digest[__SRS_OpensslHashSize]; - if ((ret = openssl_HMACsha256(temp_key, 32, random, 1504, _digest)) != ERROR_SUCCESS) { - srs_error("create s2 digest failed. ret=%d", ret); - return ret; - } - srs_verbose("generate s2 digest success."); - - is_valid = srs_bytes_equals(digest, _digest, 32); - - return ret; - } - // TODO: FIXME: move to the right position. c1s1::c1s1() { @@ -1052,13 +931,6 @@ namespace _srs_internal return payload->c1_validate_digest(this, is_valid); } - int c1s1::s1_validate_digest(bool& is_valid) - { - is_valid = false; - srs_assert(payload); - return payload->s1_validate_digest(this, is_valid); - } - int c1s1::s1_create(c1s1* c1) { int ret = ERROR_SUCCESS; @@ -1081,6 +953,134 @@ namespace _srs_internal return payload->s1_create(this); } + + int c1s1::s1_validate_digest(bool& is_valid) + { + is_valid = false; + srs_assert(payload); + return payload->s1_validate_digest(this, is_valid); + } + + c2s2::c2s2() + { + srs_random_generate(random, 1504); + + int size = snprintf(random, 1504, "%s", RTMP_SIG_SRS_HANDSHAKE); + srs_assert(++size < 1504); + snprintf(random + 1504 - size, size, "%s", RTMP_SIG_SRS_HANDSHAKE); + + srs_random_generate(digest, 32); + } + + c2s2::~c2s2() + { + } + + void c2s2::dump(char* _c2s2) + { + memcpy(_c2s2, random, 1504); + memcpy(_c2s2 + 1504, digest, 32); + } + + void c2s2::parse(char* _c2s2) + { + memcpy(random, _c2s2, 1504); + memcpy(digest, _c2s2 + 1504, 32); + } + + int c2s2::c2_create(c1s1* s1) + { + int ret = ERROR_SUCCESS; + + char temp_key[__SRS_OpensslHashSize]; + if ((ret = openssl_HMACsha256(SrsGenuineFPKey, 62, s1->get_digest(), 32, temp_key)) != ERROR_SUCCESS) { + srs_error("create c2 temp key failed. ret=%d", ret); + return ret; + } + srs_verbose("generate c2 temp key success."); + + char _digest[__SRS_OpensslHashSize]; + if ((ret = openssl_HMACsha256(temp_key, 32, random, 1504, _digest)) != ERROR_SUCCESS) { + srs_error("create c2 digest failed. ret=%d", ret); + return ret; + } + srs_verbose("generate c2 digest success."); + + memcpy(digest, _digest, 32); + + return ret; + } + + int c2s2::c2_validate(c1s1* s1, bool& is_valid) + { + is_valid = false; + int ret = ERROR_SUCCESS; + + char temp_key[__SRS_OpensslHashSize]; + if ((ret = openssl_HMACsha256(SrsGenuineFPKey, 62, s1->get_digest(), 32, temp_key)) != ERROR_SUCCESS) { + srs_error("create c2 temp key failed. ret=%d", ret); + return ret; + } + srs_verbose("generate c2 temp key success."); + + char _digest[__SRS_OpensslHashSize]; + if ((ret = openssl_HMACsha256(temp_key, 32, random, 1504, _digest)) != ERROR_SUCCESS) { + srs_error("create c2 digest failed. ret=%d", ret); + return ret; + } + srs_verbose("generate c2 digest success."); + + is_valid = srs_bytes_equals(digest, _digest, 32); + + return ret; + } + + int c2s2::s2_create(c1s1* c1) + { + int ret = ERROR_SUCCESS; + + char temp_key[__SRS_OpensslHashSize]; + if ((ret = openssl_HMACsha256(SrsGenuineFMSKey, 68, c1->get_digest(), 32, temp_key)) != ERROR_SUCCESS) { + srs_error("create s2 temp key failed. ret=%d", ret); + return ret; + } + srs_verbose("generate s2 temp key success."); + + char _digest[__SRS_OpensslHashSize]; + if ((ret = openssl_HMACsha256(temp_key, 32, random, 1504, _digest)) != ERROR_SUCCESS) { + srs_error("create s2 digest failed. ret=%d", ret); + return ret; + } + srs_verbose("generate s2 digest success."); + + memcpy(digest, _digest, 32); + + return ret; + } + + int c2s2::s2_validate(c1s1* c1, bool& is_valid) + { + is_valid = false; + int ret = ERROR_SUCCESS; + + char temp_key[__SRS_OpensslHashSize]; + if ((ret = openssl_HMACsha256(SrsGenuineFMSKey, 68, c1->get_digest(), 32, temp_key)) != ERROR_SUCCESS) { + srs_error("create s2 temp key failed. ret=%d", ret); + return ret; + } + srs_verbose("generate s2 temp key success."); + + char _digest[__SRS_OpensslHashSize]; + if ((ret = openssl_HMACsha256(temp_key, 32, random, 1504, _digest)) != ERROR_SUCCESS) { + srs_error("create s2 digest failed. ret=%d", ret); + return ret; + } + srs_verbose("generate s2 digest success."); + + is_valid = srs_bytes_equals(digest, _digest, 32); + + return ret; + } } #endif From b32d3fb6aa1cc7239d5bccdabf2d76b567086609 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 29 Nov 2014 17:58:40 +0800 Subject: [PATCH 245/800] remove the read and write with SrsStream. --- trunk/src/rtmp/srs_protocol_handshake.cpp | 282 ++++++++++++---------- trunk/src/rtmp/srs_protocol_handshake.hpp | 43 ++-- 2 files changed, 174 insertions(+), 151 deletions(-) diff --git a/trunk/src/rtmp/srs_protocol_handshake.cpp b/trunk/src/rtmp/srs_protocol_handshake.cpp index a612bfa2c7..18b59aedbe 100644 --- a/trunk/src/rtmp/srs_protocol_handshake.cpp +++ b/trunk/src/rtmp/srs_protocol_handshake.cpp @@ -292,28 +292,6 @@ namespace _srs_internal return ret; } - // read/write stream using SrsStream. - void __srs_stream_write_4bytes(char* pp, int32_t value) - { - static SrsStream stream; - - int ret = stream.initialize(pp, 4); - srs_assert(ret == ERROR_SUCCESS); - - stream.write_4bytes(value); - } - int32_t __srs_stream_read_4bytes(char* pp) - { - static SrsStream stream; - - int ret = stream.initialize(pp, 4); - srs_assert(ret == ERROR_SUCCESS); - - return stream.read_4bytes(); - } - - // create new key block data. - // if created, user must free it by srs_key_block_free void key_block::init() { key_block* key = this; @@ -342,8 +320,6 @@ namespace _srs_internal } } - // calc the offset of key, - // the key->offset cannot be used as the offset of key. int key_block::offsets() { key_block* key = this; @@ -360,48 +336,46 @@ namespace _srs_internal return offset % max_offset_size; } - // parse key block from c1s1. - // if created, user must free it by srs_key_block_free - // @c1s1_key_bytes the key start bytes, maybe c1s1 or c1s1+764 - int key_block::parse(char* c1s1_key_bytes) + int key_block::parse(SrsStream* stream) { key_block* key = this; int ret = ERROR_SUCCESS; + + // the key must be 764 bytes. + srs_assert(stream->require(764)); - char* pp = c1s1_key_bytes + 764; + // read the last offset first, 760-763 + stream->skip(764 - sizeof(int32_t)); + key->offset = stream->read_4bytes(); - pp -= sizeof(int32_t); - key->offset = __srs_stream_read_4bytes(pp); + // reset stream to read others. + stream->skip(-764); + // TODO: FIXME: free it. key->random0 = NULL; key->random1 = NULL; int offset = key->offsets(); srs_assert(offset >= 0); - pp = c1s1_key_bytes; key->random0_size = offset; if (key->random0_size > 0) { key->random0 = new char[key->random0_size]; - memcpy(key->random0, pp, key->random0_size); + stream->read_bytes(key->random0, key->random0_size); } - pp += key->random0_size; - memcpy(key->key, pp, sizeof(key->key)); - pp += sizeof(key->key); + stream->read_bytes(key->key, 128); key->random1_size = 764 - offset - 128 - 4; if (key->random1_size > 0) { key->random1 = new char[key->random1_size]; - memcpy(key->random1, pp, key->random1_size); + stream->read_bytes(key->random1, key->random1_size); } return ret; } - // free the block data create by - // srs_key_block_init or srs_key_block_parse void key_block::free() { key_block* key = this; @@ -414,8 +388,6 @@ namespace _srs_internal } } - // create new digest block data. - // if created, user must free it by srs_digest_block_free void digest_block::init() { digest_block* digest = this; @@ -444,8 +416,6 @@ namespace _srs_internal } } - // calc the offset of digest, - // the key->offset cannot be used as the offset of digest. int digest_block::offsets() { digest_block* digest = this; @@ -462,20 +432,18 @@ namespace _srs_internal return offset % max_offset_size; } - // parse digest block from c1s1. - // if created, user must free it by srs_digest_block_free - // @c1s1_digest_bytes the digest start bytes, maybe c1s1 or c1s1+764 - int digest_block::parse(char* c1s1_digest_bytes) + int digest_block::parse(SrsStream* stream) { digest_block* digest = this; int ret = ERROR_SUCCESS; - - char* pp = c1s1_digest_bytes; - digest->offset = __srs_stream_read_4bytes(pp); - pp += sizeof(int32_t); + // the digest must be 764 bytes. + srs_assert(stream->require(764)); + + digest->offset = stream->read_4bytes(); + // TODO: FIXME: free it. digest->random0 = NULL; digest->random1 = NULL; @@ -485,24 +453,20 @@ namespace _srs_internal digest->random0_size = offset; if (digest->random0_size > 0) { digest->random0 = new char[digest->random0_size]; - memcpy(digest->random0, pp, digest->random0_size); + stream->read_bytes(digest->random0, digest->random0_size); } - pp += digest->random0_size; - memcpy(digest->digest, pp, sizeof(digest->digest)); - pp += sizeof(digest->digest); + stream->read_bytes(digest->digest, 32); digest->random1_size = 764 - 4 - offset - 32; if (digest->random1_size > 0) { digest->random1 = new char[digest->random1_size]; - memcpy(digest->random1, pp, digest->random1_size); + stream->read_bytes(digest->random1, digest->random1_size); } return ret; } - // free the block data create by - // srs_digest_block_init or srs_digest_block_parse void digest_block::free() { digest_block* digest = this; @@ -532,9 +496,9 @@ namespace _srs_internal return digest.digest; } - void c1s1_strategy::dump(c1s1* owner, char* _c1s1) + int c1s1_strategy::dump(c1s1* owner, char* _c1s1, int size) { - copy_to(owner, _c1s1, true); + return copy_to(owner, _c1s1, size, true); } int c1s1_strategy::c1_create(c1s1* owner) @@ -646,7 +610,9 @@ namespace _srs_internal */ char* c1s1_joined_bytes = new char[1536 -32]; SrsAutoFree(char, c1s1_joined_bytes); - copy_to(owner, c1s1_joined_bytes, false); + if ((ret = copy_to(owner, c1s1_joined_bytes, 1536 - 32, false)) != ERROR_SUCCESS) { + return ret; + } c1_digest = new char[__SRS_OpensslHashSize]; if ((ret = openssl_HMACsha256(SrsGenuineFPKey, 30, c1s1_joined_bytes, 1536 - 32, c1_digest)) != ERROR_SUCCESS) { @@ -672,7 +638,9 @@ namespace _srs_internal */ char* c1s1_joined_bytes = new char[1536 -32]; SrsAutoFree(char, c1s1_joined_bytes); - copy_to(owner, c1s1_joined_bytes, false); + if ((ret = copy_to(owner, c1s1_joined_bytes, 1536 - 32, false)) != ERROR_SUCCESS) { + return ret; + } s1_digest = new char[__SRS_OpensslHashSize]; if ((ret = openssl_HMACsha256(SrsGenuineFMSKey, 36, c1s1_joined_bytes, 1536 - 32, s1_digest)) != ERROR_SUCCESS) { @@ -685,58 +653,66 @@ namespace _srs_internal return ret; } - void c1s1_strategy::copy_time_version(char*& pp, c1s1* owner) + void c1s1_strategy::copy_time_version(SrsStream* stream, c1s1* owner) { + srs_assert(stream->require(8)); + // 4bytes time - __srs_stream_write_4bytes(pp, owner->time); - pp += 4; + stream->write_4bytes(owner->time); + // 4bytes version - __srs_stream_write_4bytes(pp, owner->version); - pp += 4; + stream->write_4bytes(owner->version); } - void c1s1_strategy::copy_key(char*& pp) + void c1s1_strategy::copy_key(SrsStream* stream) { + srs_assert(key.random0_size >= 0); + srs_assert(key.random1_size >= 0); + + int total = key.random0_size + 128 + key.random1_size + 4; + srs_assert(stream->require(total)); + // 764bytes key block if (key.random0_size > 0) { - memcpy(pp, key.random0, key.random0_size); + stream->write_bytes(key.random0, key.random0_size); } - pp += key.random0_size; - memcpy(pp, key.key, sizeof(key.key)); - pp += sizeof(key.key); + stream->write_bytes(key.key, 128); if (key.random1_size > 0) { - memcpy(pp, key.random1, key.random1_size); + stream->write_bytes(key.random1, key.random1_size); } - pp += key.random1_size; - __srs_stream_write_4bytes(pp, key.offset); - pp += 4; + stream->write_4bytes(key.offset); } - void c1s1_strategy::digest_key(char*& pp, bool with_digest) + void c1s1_strategy::copy_digest(SrsStream* stream, bool with_digest) { + srs_assert(key.random0_size >= 0); + srs_assert(key.random1_size >= 0); + + int total = 4 + digest.random0_size + digest.random1_size; + if (with_digest) { + total += 32; + } + srs_assert(stream->require(total)); + // 732bytes digest block without the 32bytes digest-data // nbytes digest block part1 - __srs_stream_write_4bytes(pp, digest.offset); - pp += 4; + stream->write_4bytes(digest.offset); // digest random padding. if (digest.random0_size > 0) { - memcpy(pp, digest.random0, digest.random0_size); + stream->write_bytes(digest.random0, digest.random0_size); } - pp += digest.random0_size; // digest if (with_digest) { - memcpy(pp, digest.digest, 32); - pp += 32; + stream->write_bytes(digest.digest, 32); } // nbytes digest block part2 if (digest.random1_size > 0) { - memcpy(pp, digest.random1, digest.random1_size); + stream->write_bytes(digest.random1, digest.random1_size); } - pp += digest.random1_size; } c1s1_strategy_schema0::c1s1_strategy_schema0() @@ -752,16 +728,28 @@ namespace _srs_internal return srs_schema0; } - int c1s1_strategy_schema0::parse(char* _c1s1) + int c1s1_strategy_schema0::parse(char* _c1s1, int size) { int ret = ERROR_SUCCESS; - if ((ret = key.parse(_c1s1 + 8)) != ERROR_SUCCESS) { + srs_assert(size == 1536); + + SrsStream stream; + + if ((ret = stream.initialize(_c1s1 + 8, 764)) != ERROR_SUCCESS) { + return ret; + } + + if ((ret = key.parse(&stream)) != ERROR_SUCCESS) { srs_error("parse the c1 key failed. ret=%d", ret); return ret; } - if ((ret = digest.parse(_c1s1 + 8 + 764)) != ERROR_SUCCESS) { + if ((ret = stream.initialize(_c1s1 + 8 + 764, 764)) != ERROR_SUCCESS) { + return ret; + } + + if ((ret = digest.parse(&stream)) != ERROR_SUCCESS) { srs_error("parse the c1 digest failed. ret=%d", ret); return ret; } @@ -771,19 +759,20 @@ namespace _srs_internal return ret; } - void c1s1_strategy_schema0::copy_to(c1s1* owner, char* bytes, bool with_digest) + int c1s1_strategy_schema0::copy_to(c1s1* owner, char* bytes, int size, bool with_digest) { - char* pp = bytes; - - copy_time_version(pp, owner); - copy_key(pp); - digest_key(pp, with_digest); + SrsStream stream; + int ret = ERROR_SUCCESS; - if (with_digest) { - srs_assert(pp - bytes == 1536); - } else { - srs_assert(pp - bytes == 1536 - 32); + if ((ret = stream.initialize(bytes, size)) != ERROR_SUCCESS) { + return ret; } + + copy_time_version(&stream, owner); + copy_key(&stream); + copy_digest(&stream, with_digest); + + srs_assert(stream.empty()); } c1s1_strategy_schema1::c1s1_strategy_schema1() @@ -799,16 +788,28 @@ namespace _srs_internal return srs_schema1; } - int c1s1_strategy_schema1::parse(char* _c1s1) + int c1s1_strategy_schema1::parse(char* _c1s1, int size) { int ret = ERROR_SUCCESS; - if ((ret = digest.parse(_c1s1 + 8)) != ERROR_SUCCESS) { + srs_assert(size == 1536); + + SrsStream stream; + + if ((ret = stream.initialize(_c1s1 + 8, 764)) != ERROR_SUCCESS) { + return ret; + } + + if ((ret = digest.parse(&stream)) != ERROR_SUCCESS) { srs_error("parse the c1 digest failed. ret=%d", ret); return ret; } - if ((ret = key.parse(_c1s1 + 8 + 764)) != ERROR_SUCCESS) { + if ((ret = stream.initialize(_c1s1 + 8 + 764, 764)) != ERROR_SUCCESS) { + return ret; + } + + if ((ret = key.parse(&stream)) != ERROR_SUCCESS) { srs_error("parse the c1 key failed. ret=%d", ret); return ret; } @@ -818,19 +819,20 @@ namespace _srs_internal return ret; } - void c1s1_strategy_schema1::copy_to(c1s1* owner, char* bytes, bool with_digest) + int c1s1_strategy_schema1::copy_to(c1s1* owner, char* bytes, int size, bool with_digest) { - char* pp = bytes; - - copy_time_version(pp, owner); - digest_key(pp, with_digest); - copy_key(pp); + SrsStream stream; + int ret = ERROR_SUCCESS; - if (with_digest) { - srs_assert(pp - bytes == 1536); - } else { - srs_assert(pp - bytes == 1536 - 32); + if ((ret = stream.initialize(bytes, size)) != ERROR_SUCCESS) { + return ret; } + + copy_time_version(&stream, owner); + copy_digest(&stream, with_digest); + copy_key(&stream); + + srs_assert(stream.empty()); } // TODO: FIXME: move to the right position. @@ -870,24 +872,32 @@ namespace _srs_internal return payload->get_digest(); } - void c1s1::dump(char* _c1s1) + int c1s1::dump(char* _c1s1, int size) { srs_assert(payload != NULL); - return payload->dump(this, _c1s1); + return payload->dump(this, _c1s1, size); } - int c1s1::parse(char* _c1s1, srs_schema_type schema) + int c1s1::parse(char* _c1s1, int size, srs_schema_type schema) { int ret = ERROR_SUCCESS; + srs_assert(size == 1536); + if (schema != srs_schema0 && schema != srs_schema1) { ret = ERROR_RTMP_CH_SCHEMA; srs_error("parse c1 failed. invalid schema=%d, ret=%d", schema, ret); return ret; } - time = __srs_stream_read_4bytes(_c1s1); - version = __srs_stream_read_4bytes(_c1s1 + 4); // client c1 version + SrsStream stream; + + if ((ret = stream.initialize(_c1s1, size)) != ERROR_SUCCESS) { + return ret; + } + + time = stream.read_4bytes(); + version = stream.read_4bytes(); // client c1 version srs_freep(payload); if (schema == srs_schema0) { @@ -896,7 +906,7 @@ namespace _srs_internal payload = new c1s1_strategy_schema1(); } - return payload->parse(_c1s1); + return payload->parse(_c1s1, size); } int c1s1::c1_create(srs_schema_type schema) @@ -976,16 +986,24 @@ namespace _srs_internal { } - void c2s2::dump(char* _c2s2) + int c2s2::dump(char* _c2s2, int size) { + srs_assert(size == 1536); + memcpy(_c2s2, random, 1504); memcpy(_c2s2 + 1504, digest, 32); + + return ERROR_SUCCESS; } - void c2s2::parse(char* _c2s2) + int c2s2::parse(char* _c2s2, int size) { + srs_assert(size == 1536); + memcpy(random, _c2s2, 1504); memcpy(digest, _c2s2 + 1504, 32); + + return ERROR_SUCCESS; } int c2s2::c2_create(c1s1* s1) @@ -1201,7 +1219,7 @@ int SrsComplexHandshake::handshake_with_client(SrsHandshakeBytes* hs_bytes, ISrs c1s1 c1; // try schema0. // @remark, use schema0 to make flash player happy. - if ((ret = c1.parse(hs_bytes->c0c1 + 1, srs_schema0)) != ERROR_SUCCESS) { + if ((ret = c1.parse(hs_bytes->c0c1 + 1, 1536, srs_schema0)) != ERROR_SUCCESS) { srs_error("parse c1 schema%d error. ret=%d", srs_schema0, ret); return ret; } @@ -1209,7 +1227,7 @@ int SrsComplexHandshake::handshake_with_client(SrsHandshakeBytes* hs_bytes, ISrs bool is_valid = false; if ((ret = c1.c1_validate_digest(is_valid)) != ERROR_SUCCESS || !is_valid) { srs_info("schema0 failed, try schema1."); - if ((ret = c1.parse(hs_bytes->c0c1 + 1, srs_schema1)) != ERROR_SUCCESS) { + if ((ret = c1.parse(hs_bytes->c0c1 + 1, 1536, srs_schema1)) != ERROR_SUCCESS) { srs_error("parse c1 schema%d error. ret=%d", srs_schema1, ret); return ret; } @@ -1257,8 +1275,12 @@ int SrsComplexHandshake::handshake_with_client(SrsHandshakeBytes* hs_bytes, ISrs if ((ret = hs_bytes->create_s0s1s2()) != ERROR_SUCCESS) { return ret; } - s1.dump(hs_bytes->s0s1s2 + 1); - s2.dump(hs_bytes->s0s1s2 + 1537); + if ((ret = s1.dump(hs_bytes->s0s1s2 + 1, 1536)) != ERROR_SUCCESS) { + return ret; + } + if ((ret = s2.dump(hs_bytes->s0s1s2 + 1537, 1536)) != ERROR_SUCCESS) { + return ret; + } if ((ret = io->write(hs_bytes->s0s1s2, 3073, &nsize)) != ERROR_SUCCESS) { srs_warn("complex handshake send s0s1s2 failed. ret=%d", ret); return ret; @@ -1270,7 +1292,9 @@ int SrsComplexHandshake::handshake_with_client(SrsHandshakeBytes* hs_bytes, ISrs return ret; } c2s2 c2; - c2.parse(hs_bytes->c2); + if ((ret = c2.parse(hs_bytes->c2, 1536)) != ERROR_SUCCESS) { + return ret; + } srs_verbose("complex handshake read c2 success."); // verify c2 @@ -1306,7 +1330,9 @@ int SrsComplexHandshake::handshake_with_server(SrsHandshakeBytes* hs_bytes, ISrs if ((ret = c1.c1_create(srs_schema1)) != ERROR_SUCCESS) { return ret; } - c1.dump(hs_bytes->c0c1 + 1); + if ((ret = c1.dump(hs_bytes->c0c1 + 1, 1536)) != ERROR_SUCCESS) { + return ret; + } // verify c1 bool is_valid; @@ -1335,7 +1361,7 @@ int SrsComplexHandshake::handshake_with_server(SrsHandshakeBytes* hs_bytes, ISrs // verify s1s2 c1s1 s1; - if ((ret = s1.parse(hs_bytes->s0s1s2 + 1, c1.schema())) != ERROR_SUCCESS) { + if ((ret = s1.parse(hs_bytes->s0s1s2 + 1, 1536, c1.schema())) != ERROR_SUCCESS) { return ret; } @@ -1353,7 +1379,9 @@ int SrsComplexHandshake::handshake_with_server(SrsHandshakeBytes* hs_bytes, ISrs return ret; } - c2.dump(hs_bytes->c2); + if ((ret = c2.dump(hs_bytes->c2, 1536)) != ERROR_SUCCESS) { + return ret; + } if ((ret = io->write(hs_bytes->c2, 1536, &nsize)) != ERROR_SUCCESS) { srs_warn("complex handshake write c2 failed. ret=%d", ret); return ret; diff --git a/trunk/src/rtmp/srs_protocol_handshake.hpp b/trunk/src/rtmp/srs_protocol_handshake.hpp index 871723298b..c02ceac012 100644 --- a/trunk/src/rtmp/srs_protocol_handshake.hpp +++ b/trunk/src/rtmp/srs_protocol_handshake.hpp @@ -33,6 +33,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. class ISrsProtocolReaderWriter; class SrsComplexHandshake; class SrsHandshakeBytes; +class SrsStream; #ifdef SRS_AUTO_SSL @@ -142,8 +143,8 @@ namespace _srs_internal // parse key block from c1s1. // if created, user must free it by srs_key_block_free - // @c1s1_key_bytes the key start bytes, maybe c1s1 or c1s1+764 - int parse(char* c1s1_key_bytes); + // @stream contains c1s1_key_bytes the key start bytes + int parse(SrsStream* stream); // free the block data create by // srs_key_block_init or srs_key_block_parse @@ -185,8 +186,8 @@ namespace _srs_internal // parse digest block from c1s1. // if created, user must free it by srs_digest_block_free - // @c1s1_digest_bytes the digest start bytes, maybe c1s1 or c1s1+764 - int parse(char* c1s1_digest_bytes); + // @stream contains c1s1_digest_bytes the digest start bytes + int parse(SrsStream* stream); // free the block data create by // srs_digest_block_init or srs_digest_block_parse @@ -220,12 +221,12 @@ namespace _srs_internal /** * copy to bytes. */ - virtual void dump(c1s1* owner, char* _c1s1); + virtual int dump(c1s1* owner, char* _c1s1, int size); /** * server: parse the c1s1, discovery the key and digest by schema. * use the c1_validate_digest() to valid the digest of c1. */ - virtual int parse(char* _c1s1) = 0; + virtual int parse(char* _c1s1, int size) = 0; public: /** * client: create and sign c1 by schema. @@ -261,10 +262,10 @@ namespace _srs_internal /** * copy whole c1s1 to bytes. */ - virtual void copy_to(c1s1* owner, char* bytes, bool with_digest) = 0; - virtual void copy_time_version(char*& pp, c1s1* owner); - virtual void copy_key(char*& pp); - virtual void digest_key(char*& pp, bool with_digest); + virtual int copy_to(c1s1* owner, char* bytes, int size, bool with_digest) = 0; + virtual void copy_time_version(SrsStream* stream, c1s1* owner); + virtual void copy_key(SrsStream* stream); + virtual void copy_digest(SrsStream* stream, bool with_digest); }; /** @@ -279,12 +280,9 @@ namespace _srs_internal virtual ~c1s1_strategy_schema0(); public: virtual srs_schema_type schema(); - virtual int parse(char* _c1s1); + virtual int parse(char* _c1s1, int size); private: - /** - * copy whole c1s1 to bytes. - */ - virtual void copy_to(c1s1* owner, char* bytes, bool with_digest); + virtual int copy_to(c1s1* owner, char* bytes, int size, bool with_digest); }; /** @@ -299,12 +297,9 @@ namespace _srs_internal virtual ~c1s1_strategy_schema1(); public: virtual srs_schema_type schema(); - virtual int parse(char* _c1s1); + virtual int parse(char* _c1s1, int size); private: - /** - * copy whole c1s1 to bytes. - */ - virtual void copy_to(c1s1* owner, char* bytes, bool with_digest); + virtual int copy_to(c1s1* owner, char* bytes, int size, bool with_digest); }; /** @@ -345,13 +340,13 @@ namespace _srs_internal /** * copy to bytes. */ - virtual void dump(char* _c1s1); + virtual int dump(char* _c1s1, int size); /** * server: parse the c1s1, discovery the key and digest by schema. * use the c1_validate_digest() to valid the digest of c1. * use the s1_validate_digest() to valid the digest of s1. */ - virtual int parse(char* _c1s1, srs_schema_type _schema); + virtual int parse(char* _c1s1, int size, srs_schema_type _schema); public: /** * client: create and sign c1 by schema. @@ -425,11 +420,11 @@ namespace _srs_internal /** * copy to bytes. */ - virtual void dump(char* _c2s2); + virtual int dump(char* _c2s2, int size); /** * parse the c2s2 */ - virtual void parse(char* _c2s2); + virtual int parse(char* _c2s2, int size); public: /** * create c2. From 818000dd18d9f75aae2f1b09ffd800107812a842 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 29 Nov 2014 18:10:33 +0800 Subject: [PATCH 246/800] for bug #235, use constructor and destructor for block and digest. --- trunk/src/rtmp/srs_protocol_handshake.cpp | 212 ++++++++++------------ trunk/src/rtmp/srs_protocol_handshake.hpp | 38 ++-- 2 files changed, 110 insertions(+), 140 deletions(-) diff --git a/trunk/src/rtmp/srs_protocol_handshake.cpp b/trunk/src/rtmp/srs_protocol_handshake.cpp index 18b59aedbe..6e8ff78d6b 100644 --- a/trunk/src/rtmp/srs_protocol_handshake.cpp +++ b/trunk/src/rtmp/srs_protocol_handshake.cpp @@ -292,54 +292,44 @@ namespace _srs_internal return ret; } - void key_block::init() + key_block::key_block() { - key_block* key = this; + offset = (int32_t)rand(); + random0 = NULL; + random1 = NULL; - key->offset = (int32_t)rand(); - key->random0 = NULL; - key->random1 = NULL; + int valid_offset = calc_valid_offset(); + srs_assert(valid_offset >= 0); - int offset = key->offsets(); - srs_assert(offset >= 0); - - key->random0_size = offset; - if (key->random0_size > 0) { - key->random0 = new char[key->random0_size]; - srs_random_generate(key->random0, key->random0_size); - snprintf(key->random0, key->random0_size, "%s", RTMP_SIG_SRS_HANDSHAKE); + random0_size = valid_offset; + if (random0_size > 0) { + random0 = new char[random0_size]; + srs_random_generate(random0, random0_size); + snprintf(random0, random0_size, "%s", RTMP_SIG_SRS_HANDSHAKE); } - srs_random_generate(key->key, sizeof(key->key)); + srs_random_generate(key, sizeof(key)); - key->random1_size = 764 - offset - 128 - 4; - if (key->random1_size > 0) { - key->random1 = new char[key->random1_size]; - srs_random_generate(key->random1, key->random1_size); - snprintf(key->random1, key->random1_size, "%s", RTMP_SIG_SRS_HANDSHAKE); + random1_size = 764 - valid_offset - 128 - 4; + if (random1_size > 0) { + random1 = new char[random1_size]; + srs_random_generate(random1, random1_size); + snprintf(random1, random1_size, "%s", RTMP_SIG_SRS_HANDSHAKE); } } - int key_block::offsets() + key_block::~key_block() { - key_block* key = this; - - int max_offset_size = 764 - 128 - 4; - - int offset = 0; - u_int8_t* pp = (u_int8_t*)&key->offset; - offset += *pp++; - offset += *pp++; - offset += *pp++; - offset += *pp++; - - return offset % max_offset_size; + if (random0) { + srs_freep(random0); + } + if (random1) { + srs_freep(random1); + } } int key_block::parse(SrsStream* stream) { - key_block* key = this; - int ret = ERROR_SUCCESS; // the key must be 764 bytes. @@ -347,148 +337,138 @@ namespace _srs_internal // read the last offset first, 760-763 stream->skip(764 - sizeof(int32_t)); - key->offset = stream->read_4bytes(); + offset = stream->read_4bytes(); // reset stream to read others. stream->skip(-764); // TODO: FIXME: free it. - key->random0 = NULL; - key->random1 = NULL; + random0 = NULL; + random1 = NULL; - int offset = key->offsets(); - srs_assert(offset >= 0); + int valid_offset = calc_valid_offset(); + srs_assert(valid_offset >= 0); - key->random0_size = offset; - if (key->random0_size > 0) { - key->random0 = new char[key->random0_size]; - stream->read_bytes(key->random0, key->random0_size); + random0_size = valid_offset; + if (random0_size > 0) { + random0 = new char[random0_size]; + stream->read_bytes(random0, random0_size); } - stream->read_bytes(key->key, 128); + stream->read_bytes(key, 128); - key->random1_size = 764 - offset - 128 - 4; - if (key->random1_size > 0) { - key->random1 = new char[key->random1_size]; - stream->read_bytes(key->random1, key->random1_size); + random1_size = 764 - valid_offset - 128 - 4; + if (random1_size > 0) { + random1 = new char[random1_size]; + stream->read_bytes(random1, random1_size); } return ret; } - void key_block::free() + int key_block::calc_valid_offset() { - key_block* key = this; + int max_offset_size = 764 - 128 - 4; - if (key->random0) { - srs_freep(key->random0); - } - if (key->random1) { - srs_freep(key->random1); - } + int valid_offset = 0; + u_int8_t* pp = (u_int8_t*)&offset; + valid_offset += *pp++; + valid_offset += *pp++; + valid_offset += *pp++; + valid_offset += *pp++; + + return valid_offset % max_offset_size; } - void digest_block::init() + digest_block::digest_block() { - digest_block* digest = this; + offset = (int32_t)rand(); + random0 = NULL; + random1 = NULL; - digest->offset = (int32_t)rand(); - digest->random0 = NULL; - digest->random1 = NULL; + int valid_offset = calc_valid_offset(); + srs_assert(valid_offset >= 0); - int offset = digest->offsets(); - srs_assert(offset >= 0); - - digest->random0_size = offset; - if (digest->random0_size > 0) { - digest->random0 = new char[digest->random0_size]; - srs_random_generate(digest->random0, digest->random0_size); - snprintf(digest->random0, digest->random0_size, "%s", RTMP_SIG_SRS_HANDSHAKE); + random0_size = valid_offset; + if (random0_size > 0) { + random0 = new char[random0_size]; + srs_random_generate(random0, random0_size); + snprintf(random0, random0_size, "%s", RTMP_SIG_SRS_HANDSHAKE); } - srs_random_generate(digest->digest, sizeof(digest->digest)); + srs_random_generate(digest, sizeof(digest)); - digest->random1_size = 764 - 4 - offset - 32; - if (digest->random1_size > 0) { - digest->random1 = new char[digest->random1_size]; - srs_random_generate(digest->random1, digest->random1_size); - snprintf(digest->random1, digest->random1_size, "%s", RTMP_SIG_SRS_HANDSHAKE); + random1_size = 764 - 4 - valid_offset - 32; + if (random1_size > 0) { + random1 = new char[random1_size]; + srs_random_generate(random1, random1_size); + snprintf(random1, random1_size, "%s", RTMP_SIG_SRS_HANDSHAKE); } } - int digest_block::offsets() + digest_block::~digest_block() { - digest_block* digest = this; - - int max_offset_size = 764 - 32 - 4; - - int offset = 0; - u_int8_t* pp = (u_int8_t*)&digest->offset; - offset += *pp++; - offset += *pp++; - offset += *pp++; - offset += *pp++; - - return offset % max_offset_size; + if (random0) { + srs_freep(random0); + } + if (random1) { + srs_freep(random1); + } } int digest_block::parse(SrsStream* stream) { - digest_block* digest = this; - int ret = ERROR_SUCCESS; // the digest must be 764 bytes. srs_assert(stream->require(764)); - digest->offset = stream->read_4bytes(); + offset = stream->read_4bytes(); // TODO: FIXME: free it. - digest->random0 = NULL; - digest->random1 = NULL; + random0 = NULL; + random1 = NULL; - int offset = digest->offsets(); - srs_assert(offset >= 0); + int valid_offset = calc_valid_offset(); + srs_assert(valid_offset >= 0); - digest->random0_size = offset; - if (digest->random0_size > 0) { - digest->random0 = new char[digest->random0_size]; - stream->read_bytes(digest->random0, digest->random0_size); + random0_size = valid_offset; + if (random0_size > 0) { + random0 = new char[random0_size]; + stream->read_bytes(random0, random0_size); } - stream->read_bytes(digest->digest, 32); + stream->read_bytes(digest, 32); - digest->random1_size = 764 - 4 - offset - 32; - if (digest->random1_size > 0) { - digest->random1 = new char[digest->random1_size]; - stream->read_bytes(digest->random1, digest->random1_size); + random1_size = 764 - 4 - valid_offset - 32; + if (random1_size > 0) { + random1 = new char[random1_size]; + stream->read_bytes(random1, random1_size); } return ret; } - void digest_block::free() + int digest_block::calc_valid_offset() { - digest_block* digest = this; + int max_offset_size = 764 - 32 - 4; - if (digest->random0) { - srs_freep(digest->random0); - } - if (digest->random1) { - srs_freep(digest->random1); - } + int valid_offset = 0; + u_int8_t* pp = (u_int8_t*)&offset; + valid_offset += *pp++; + valid_offset += *pp++; + valid_offset += *pp++; + valid_offset += *pp++; + + return valid_offset % max_offset_size; } c1s1_strategy::c1s1_strategy() { - key.init(); - digest.init(); } c1s1_strategy::~c1s1_strategy() { - key.free(); - digest.free(); } char* c1s1_strategy::get_digest() diff --git a/trunk/src/rtmp/srs_protocol_handshake.hpp b/trunk/src/rtmp/srs_protocol_handshake.hpp index c02ceac012..82a4137d8f 100644 --- a/trunk/src/rtmp/srs_protocol_handshake.hpp +++ b/trunk/src/rtmp/srs_protocol_handshake.hpp @@ -133,22 +133,17 @@ namespace _srs_internal // 4bytes int32_t offset; public: - // create new key block data. - // if created, user must free it by srs_key_block_free - void init(); - - // calc the offset of key, - // the key->offset cannot be used as the offset of key. - int offsets(); - + key_block(); + virtual ~key_block(); + public: // parse key block from c1s1. // if created, user must free it by srs_key_block_free // @stream contains c1s1_key_bytes the key start bytes int parse(SrsStream* stream); - - // free the block data create by - // srs_key_block_init or srs_key_block_parse - void free(); + private: + // calc the offset of key, + // the key->offset cannot be used as the offset of key. + int calc_valid_offset(); }; /** @@ -176,22 +171,17 @@ namespace _srs_internal char* random1; int random1_size; public: - // create new digest block data. - // if created, user must free it by srs_digest_block_free - void init(); - - // calc the offset of digest, - // the key->offset cannot be used as the offset of digest. - int offsets(); - + digest_block(); + virtual ~digest_block(); + public: // parse digest block from c1s1. // if created, user must free it by srs_digest_block_free // @stream contains c1s1_digest_bytes the digest start bytes int parse(SrsStream* stream); - - // free the block data create by - // srs_digest_block_init or srs_digest_block_parse - void free(); + private: + // calc the offset of digest, + // the key->offset cannot be used as the offset of digest. + int calc_valid_offset(); }; class c1s1; From e167c9778670a4940c92812b4033326733bf9098 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 29 Nov 2014 18:16:15 +0800 Subject: [PATCH 247/800] for bug #235, fix the return code bug. --- trunk/src/rtmp/srs_protocol_handshake.cpp | 38 ++++++++++------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/trunk/src/rtmp/srs_protocol_handshake.cpp b/trunk/src/rtmp/srs_protocol_handshake.cpp index 6e8ff78d6b..169587d975 100644 --- a/trunk/src/rtmp/srs_protocol_handshake.cpp +++ b/trunk/src/rtmp/srs_protocol_handshake.cpp @@ -320,12 +320,8 @@ namespace _srs_internal key_block::~key_block() { - if (random0) { - srs_freep(random0); - } - if (random1) { - srs_freep(random1); - } + srs_freep(random0); + srs_freep(random1); } int key_block::parse(SrsStream* stream) @@ -342,15 +338,12 @@ namespace _srs_internal // reset stream to read others. stream->skip(-764); - // TODO: FIXME: free it. - random0 = NULL; - random1 = NULL; - int valid_offset = calc_valid_offset(); srs_assert(valid_offset >= 0); random0_size = valid_offset; if (random0_size > 0) { + srs_freep(random0); random0 = new char[random0_size]; stream->read_bytes(random0, random0_size); } @@ -359,6 +352,7 @@ namespace _srs_internal random1_size = 764 - valid_offset - 128 - 4; if (random1_size > 0) { + srs_freep(random1); random1 = new char[random1_size]; stream->read_bytes(random1, random1_size); } @@ -408,12 +402,8 @@ namespace _srs_internal digest_block::~digest_block() { - if (random0) { - srs_freep(random0); - } - if (random1) { - srs_freep(random1); - } + srs_freep(random0); + srs_freep(random1); } int digest_block::parse(SrsStream* stream) @@ -425,15 +415,12 @@ namespace _srs_internal offset = stream->read_4bytes(); - // TODO: FIXME: free it. - random0 = NULL; - random1 = NULL; - int valid_offset = calc_valid_offset(); srs_assert(valid_offset >= 0); random0_size = valid_offset; if (random0_size > 0) { + srs_freep(random0); random0 = new char[random0_size]; stream->read_bytes(random0, random0_size); } @@ -442,6 +429,7 @@ namespace _srs_internal random1_size = 764 - 4 - valid_offset - 32; if (random1_size > 0) { + srs_freep(random1); random1 = new char[random1_size]; stream->read_bytes(random1, random1_size); } @@ -741,9 +729,10 @@ namespace _srs_internal int c1s1_strategy_schema0::copy_to(c1s1* owner, char* bytes, int size, bool with_digest) { - SrsStream stream; int ret = ERROR_SUCCESS; + SrsStream stream; + if ((ret = stream.initialize(bytes, size)) != ERROR_SUCCESS) { return ret; } @@ -753,6 +742,8 @@ namespace _srs_internal copy_digest(&stream, with_digest); srs_assert(stream.empty()); + + return ret; } c1s1_strategy_schema1::c1s1_strategy_schema1() @@ -801,9 +792,10 @@ namespace _srs_internal int c1s1_strategy_schema1::copy_to(c1s1* owner, char* bytes, int size, bool with_digest) { - SrsStream stream; int ret = ERROR_SUCCESS; + SrsStream stream; + if ((ret = stream.initialize(bytes, size)) != ERROR_SUCCESS) { return ret; } @@ -813,6 +805,8 @@ namespace _srs_internal copy_key(&stream); srs_assert(stream.empty()); + + return ret; } // TODO: FIXME: move to the right position. From 23f4aa5737879149abeeab77dacd738b6ffd2038 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 29 Nov 2014 18:33:43 +0800 Subject: [PATCH 248/800] for bug #235, fix bug of utest. --- trunk/src/rtmp/srs_protocol_handshake.cpp | 25 +++++++++++++++ trunk/src/rtmp/srs_protocol_handshake.hpp | 37 ++++++++++++++++++++--- trunk/src/utest/srs_utest_protocol.cpp | 17 ++++++----- 3 files changed, 67 insertions(+), 12 deletions(-) diff --git a/trunk/src/rtmp/srs_protocol_handshake.cpp b/trunk/src/rtmp/srs_protocol_handshake.cpp index 169587d975..a5b11ef136 100644 --- a/trunk/src/rtmp/srs_protocol_handshake.cpp +++ b/trunk/src/rtmp/srs_protocol_handshake.cpp @@ -464,8 +464,14 @@ namespace _srs_internal return digest.digest; } + char* c1s1_strategy::get_key() + { + return key.key; + } + int c1s1_strategy::dump(c1s1* owner, char* _c1s1, int size) { + srs_assert(size == 1536); return copy_to(owner, _c1s1, size, true); } @@ -731,6 +737,12 @@ namespace _srs_internal { int ret = ERROR_SUCCESS; + if (with_digest) { + srs_assert(size == 1536); + } else { + srs_assert(size == 1504); + } + SrsStream stream; if ((ret = stream.initialize(bytes, size)) != ERROR_SUCCESS) { @@ -794,6 +806,12 @@ namespace _srs_internal { int ret = ERROR_SUCCESS; + if (with_digest) { + srs_assert(size == 1536); + } else { + srs_assert(size == 1504); + } + SrsStream stream; if ((ret = stream.initialize(bytes, size)) != ERROR_SUCCESS) { @@ -846,8 +864,15 @@ namespace _srs_internal return payload->get_digest(); } + char* c1s1::get_key() + { + srs_assert(payload != NULL); + return payload->get_key(); + } + int c1s1::dump(char* _c1s1, int size) { + srs_assert(size == 1536); srs_assert(payload != NULL); return payload->dump(this, _c1s1, size); } diff --git a/trunk/src/rtmp/srs_protocol_handshake.hpp b/trunk/src/rtmp/srs_protocol_handshake.hpp index 82a4137d8f..1fd47e0134 100644 --- a/trunk/src/rtmp/srs_protocol_handshake.hpp +++ b/trunk/src/rtmp/srs_protocol_handshake.hpp @@ -205,11 +205,16 @@ namespace _srs_internal */ virtual srs_schema_type schema() = 0; /** - * get the digest key. + * get the digest. */ virtual char* get_digest(); /** + * get the key. + */ + virtual char* get_key(); + /** * copy to bytes. + * @param size must be 1536. */ virtual int dump(c1s1* owner, char* _c1s1, int size); /** @@ -246,15 +251,31 @@ namespace _srs_internal * server: validate the parsed s1 schema */ virtual int s1_validate_digest(c1s1* owner, bool& is_valid); - protected: + public: + /** + * calc the digest for c1 + */ virtual int calc_c1_digest(c1s1* owner, char*& c1_digest); + /** + * calc the digest for s1 + */ virtual int calc_s1_digest(c1s1* owner, char*& s1_digest); /** * copy whole c1s1 to bytes. + * @param size must always be 1536 with digest, and 1504 without digest. */ virtual int copy_to(c1s1* owner, char* bytes, int size, bool with_digest) = 0; + /** + * copy time and version to stream. + */ virtual void copy_time_version(SrsStream* stream, c1s1* owner); + /** + * copy key to stream. + */ virtual void copy_key(SrsStream* stream); + /** + * copy digest to stream. + */ virtual void copy_digest(SrsStream* stream, bool with_digest); }; @@ -271,7 +292,7 @@ namespace _srs_internal public: virtual srs_schema_type schema(); virtual int parse(char* _c1s1, int size); - private: + public: virtual int copy_to(c1s1* owner, char* bytes, int size, bool with_digest); }; @@ -288,7 +309,7 @@ namespace _srs_internal public: virtual srs_schema_type schema(); virtual int parse(char* _c1s1, int size); - private: + public: virtual int copy_to(c1s1* owner, char* bytes, int size, bool with_digest); }; @@ -326,13 +347,19 @@ namespace _srs_internal * get the digest key. */ virtual char* get_digest(); + /** + * get the key. + */ + virtual char* get_key(); public: /** * copy to bytes. + * @param size, must always be 1536. */ virtual int dump(char* _c1s1, int size); /** * server: parse the c1s1, discovery the key and digest by schema. + * @param size, must always be 1536. * use the c1_validate_digest() to valid the digest of c1. * use the s1_validate_digest() to valid the digest of s1. */ @@ -409,10 +436,12 @@ namespace _srs_internal public: /** * copy to bytes. + * @param size, must always be 1536. */ virtual int dump(char* _c2s2, int size); /** * parse the c2s2 + * @param size, must always be 1536. */ virtual int parse(char* _c2s2, int size); public: diff --git a/trunk/src/utest/srs_utest_protocol.cpp b/trunk/src/utest/srs_utest_protocol.cpp index c6aabc7a65..77384828d5 100644 --- a/trunk/src/utest/srs_utest_protocol.cpp +++ b/trunk/src/utest/srs_utest_protocol.cpp @@ -285,14 +285,15 @@ VOID TEST(ProtocolHandshakeTest, VerifyFPC0C1) c1s1 c1; // the schema of data must be schema0: key-digest. - ASSERT_EQ(ERROR_SUCCESS, c1.parse(c0c1 + 1, srs_schema0)); + ASSERT_EQ(ERROR_SUCCESS, c1.parse(c0c1 + 1, 1536, srs_schema0)); EXPECT_EQ((int32_t)0x000f64d0, c1.time); EXPECT_EQ((int32_t)0x80000702, c1.version); // manually validate the c1 // @see: calc_c1_digest - char* c1s1_joined_bytes = srs_bytes_join_schema0(c1.time, c1.version, &c1.block0.key, &c1.block1.digest); + char* c1s1_joined_bytes = new char[1536 -32]; SrsAutoFree(char, c1s1_joined_bytes); + ASSERT_EQ(ERROR_SUCCESS, c1.payload->copy_to(&c1, c1s1_joined_bytes, 1536 - 32, false)); bool is_valid; ASSERT_EQ(ERROR_SUCCESS, c1.c1_validate_digest(is_valid)); @@ -304,14 +305,14 @@ VOID TEST(ProtocolHandshakeTest, VerifyFPC0C1) (char)0xf4, (char)0x21, (char)0xa8, (char)0x65, (char)0xce, (char)0xf8, (char)0x8e, (char)0xcc, (char)0x16, (char)0x1e, (char)0xbb, (char)0xd8, (char)0x0e, (char)0xcb, (char)0xd2, (char)0x48, (char)0x37, (char)0xaf, (char)0x4e, (char)0x67, (char)0x45, (char)0xf1, (char)0x79, (char)0x69, (char)0xd2, (char)0xee, (char)0xa4, (char)0xb5, (char)0x01, (char)0xbf, (char)0x57, (char)0x0f, (char)0x68, (char)0x37, (char)0xbe, (char)0x4e, (char)0xff, (char)0xc9, (char)0xb9, (char)0x92, (char)0x23, (char)0x06, (char)0x75, (char)0xa0, (char)0x42, (char)0xe4, (char)0x0a, (char)0x30, (char)0xf0, (char)0xaf, (char)0xb0, (char)0x54, (char)0x88, (char)0x7c, (char)0xc0, (char)0xc1, (char)0x0c, (char)0x6d, (char)0x01, (char)0x36, (char)0x63, (char)0xf3, (char)0x3d, (char)0xbc, (char)0x72, (char)0xf6, (char)0x96, (char)0xc8, (char)0x87, (char)0xab, (char)0x8b, (char)0x0c, (char)0x91, (char)0x2f, (char)0x42, (char)0x2a, (char)0x11, (char)0xf6, (char)0x2d, (char)0x5e }; - EXPECT_TRUE(srs_bytes_equals(c1.block0.key.key, key, 128)); + EXPECT_TRUE(srs_bytes_equals(c1.get_key(), key, 128)); // 32bytes digest char digest[] = { (char)0x6c, (char)0x96, (char)0x9f, (char)0x26, (char)0xeb, (char)0xdc, (char)0x61, (char)0xc4, (char)0x8f, (char)0xd3, (char)0x2b, (char)0x81, (char)0x86, (char)0x6c, (char)0x9c, (char)0xc2, (char)0xb1, (char)0xb5, (char)0xbc, (char)0xa6, (char)0xd6, (char)0xd6, (char)0x1d, (char)0xce, (char)0x93, (char)0x78, (char)0xb3, (char)0xec, (char)0xa8, (char)0x64, (char)0x19, (char)0x13 }; - EXPECT_TRUE(srs_bytes_equals(c1.block1.digest.digest, digest, 32)); + EXPECT_TRUE(srs_bytes_equals(c1.get_digest(), digest, 32)); } VOID TEST(ProtocolHandshakeTest, ComplexHandshake) @@ -348,22 +349,22 @@ VOID TEST(ProtocolHandshakeTest, ComplexHandshake) bool is_valid; c1s1 c1; - ASSERT_EQ(ERROR_SUCCESS, c1.parse(hs_bytes->c0c1 + 1, srs_schema0)); + ASSERT_EQ(ERROR_SUCCESS, c1.parse(hs_bytes->c0c1 + 1, 1536, srs_schema0)); ASSERT_EQ(ERROR_SUCCESS, c1.c1_validate_digest(is_valid)); ASSERT_TRUE(is_valid); c1s1 s1; - ASSERT_EQ(ERROR_SUCCESS, s1.parse(hs_bytes->s0s1s2 + 1, c1.schema)); + ASSERT_EQ(ERROR_SUCCESS, s1.parse(hs_bytes->s0s1s2 + 1, 1536, c1.schema())); ASSERT_EQ(ERROR_SUCCESS, s1.s1_validate_digest(is_valid)); ASSERT_TRUE(is_valid); c2s2 c2; - c2.parse(hs_bytes->c2); + c2.parse(hs_bytes->c2, 1536); ASSERT_EQ(ERROR_SUCCESS, c2.c2_validate(&s1, is_valid)); ASSERT_TRUE(is_valid); c2s2 s2; - s2.parse(hs_bytes->s0s1s2 + 1 + 1536); + s2.parse(hs_bytes->s0s1s2 + 1 + 1536, 1536); ASSERT_EQ(ERROR_SUCCESS, s2.s2_validate(&c1, is_valid)); ASSERT_TRUE(is_valid); } From abb0fce3d8cea70675a01317ea54c494fe400173 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 29 Nov 2014 18:36:40 +0800 Subject: [PATCH 249/800] fix bug for utest of message array, it does not free messages now. --- trunk/src/utest/srs_utest_protocol.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/trunk/src/utest/srs_utest_protocol.cpp b/trunk/src/utest/srs_utest_protocol.cpp index 77384828d5..98907684b2 100644 --- a/trunk/src/utest/srs_utest_protocol.cpp +++ b/trunk/src/utest/srs_utest_protocol.cpp @@ -541,18 +541,18 @@ VOID TEST(ProtocolMsgArrayTest, MessageArray) arr.msgs[2] = msg.copy(); EXPECT_EQ(3, msg.count()); } - EXPECT_EQ(0, msg.count()); + EXPECT_EQ(3, msg.count()); if (true) { SrsMessageArray arr(3); arr.msgs[0] = msg.copy(); - EXPECT_EQ(1, msg.count()); + EXPECT_EQ(4, msg.count()); arr.msgs[2] = msg.copy(); - EXPECT_EQ(2, msg.count()); + EXPECT_EQ(5, msg.count()); } - EXPECT_EQ(0, msg.count()); + EXPECT_EQ(5, msg.count()); } /** From efb73c548793b6c6c5c75070d1a67c766d983f79 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 29 Nov 2014 18:41:14 +0800 Subject: [PATCH 250/800] fix #235, refine handshake, replace union with template method. 2.0.38. --- README.md | 3 ++- trunk/src/core/srs_core.hpp | 2 +- trunk/src/rtmp/srs_protocol_handshake.cpp | 23 +++----------------- trunk/src/rtmp/srs_protocol_handshake.hpp | 26 ++++++++++++++++++++++- 4 files changed, 31 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index c6285d058a..e1dff4fa87 100755 --- a/README.md +++ b/README.md @@ -485,7 +485,8 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History -* v2.0, 2014-11-28, fix [#215](https://github.com/winlinvip/simple-rtmp-server/issues/215), for bug #215, add srs_rtmp_dump tool. 2.0.37. +* v2.0, 2014-11-29, fix [#235](https://github.com/winlinvip/simple-rtmp-server/issues/235), refine handshake, replace union with template method. 2.0.38. +* v2.0, 2014-11-28, fix [#215](https://github.com/winlinvip/simple-rtmp-server/issues/215), add srs_rtmp_dump tool. 2.0.37. * v2.0, 2014-11-25, update PRIMARY, AUTHORS, CONTRIBUTORS rule. 2.0.32. * v2.0, 2014-11-24, fix [#212](https://github.com/winlinvip/simple-rtmp-server/issues/212), support publish aac adts raw stream. 2.0.31. * v2.0, 2014-11-22, fix [#217](https://github.com/winlinvip/simple-rtmp-server/issues/217), remove timeout recv, support 7.5k+ 250kbps clients. 2.0.30. diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index e7333f98e9..4915c69bab 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 37 +#define VERSION_REVISION 38 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/rtmp/srs_protocol_handshake.cpp b/trunk/src/rtmp/srs_protocol_handshake.cpp index a5b11ef136..e67c9e9ac7 100644 --- a/trunk/src/rtmp/srs_protocol_handshake.cpp +++ b/trunk/src/rtmp/srs_protocol_handshake.cpp @@ -514,7 +514,7 @@ namespace _srs_internal return ret; } - int c1s1_strategy::s1_create(c1s1* owner) + int c1s1_strategy::s1_create(c1s1* owner, c1s1* c1) { int ret = ERROR_SUCCESS; @@ -528,8 +528,7 @@ namespace _srs_internal // directly generate the public key. // @see: https://github.com/winlinvip/simple-rtmp-server/issues/148 int pkey_size = 128; - // TODO: FIXME: use c1 public key to calc the shared key. - if ((ret = dh.copy_public_key(key.key, pkey_size)) != ERROR_SUCCESS) { + if ((ret = dh.copy_shared_key(c1->get_key(), 128, key.key, pkey_size)) != ERROR_SUCCESS) { srs_error("calc s1 key failed. ret=%d", ret); return ret; } @@ -827,7 +826,6 @@ namespace _srs_internal return ret; } - // TODO: FIXME: move to the right position. c1s1::c1s1() { payload = NULL; @@ -835,21 +833,6 @@ namespace _srs_internal c1s1::~c1s1() { srs_freep(payload); - /* - void c1s1::destroy_blocks() - { - if (schema == srs_schema_invalid) { - return; - } - - if (schema == srs_schema0) { - block0.key.free(); - block1.digest.free(); - } else { - block0.digest.free(); - block1.key.free(); - } - }*/ } srs_schema_type c1s1::schema() @@ -960,7 +943,7 @@ namespace _srs_internal payload = new c1s1_strategy_schema1(); } - return payload->s1_create(this); + return payload->s1_create(this, c1); } int c1s1::s1_validate_digest(bool& is_valid) diff --git a/trunk/src/rtmp/srs_protocol_handshake.hpp b/trunk/src/rtmp/srs_protocol_handshake.hpp index 1fd47e0134..4b35a21ca7 100644 --- a/trunk/src/rtmp/srs_protocol_handshake.hpp +++ b/trunk/src/rtmp/srs_protocol_handshake.hpp @@ -245,8 +245,32 @@ namespace _srs_internal virtual int c1_validate_digest(c1s1* owner, bool& is_valid); /** * server: create and sign the s1 from c1. + * // decode c1 try schema0 then schema1 + * c1-digest-data = get-c1-digest-data(schema0) + * if c1-digest-data equals to calc_c1_digest(c1, schema0) { + * c1-key-data = get-c1-key-data(schema0) + * schema = schema0 + * } else { + * c1-digest-data = get-c1-digest-data(schema1) + * if c1-digest-data not equals to calc_c1_digest(c1, schema1) { + * switch to simple handshake. + * return + * } + * c1-key-data = get-c1-key-data(schema1) + * schema = schema1 + * } + * + * // generate s1 + * random fill 1536bytes s1 + * time = time() // c1[0-3] + * version = [0x04, 0x05, 0x00, 0x01] // s1[4-7] + * s1-key-data=shared_key=DH_compute_key(peer_pub_key=c1-key-data) + * get c1s1-joined by specified schema + * s1-digest-data = HMACsha256(c1s1-joined, FMSKey, 36) + * copy s1-digest-data and s1-key-data to s1. + * @param c1, to get the peer_pub_key of client. */ - virtual int s1_create(c1s1* owner); + virtual int s1_create(c1s1* owner, c1s1* c1); /** * server: validate the parsed s1 schema */ From b4057916b60716f7a2a19c5ab6f524a6a345eaec Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 30 Nov 2014 16:06:37 +0800 Subject: [PATCH 251/800] add qt project file. fix cmake file for clion. --- trunk/CMakeLists.txt | 3 +- trunk/src/app/srs_app_utility.cpp | 2 +- trunk/src/qt/srs/srs-qt.pro | 34 +++++ trunk/src/qt/srs/srs-qt.pro.user | 204 ++++++++++++++++++++++++++++++ trunk/src/{srs => upp}/init | 0 trunk/src/{srs => upp}/srs.upp | 0 6 files changed, 241 insertions(+), 2 deletions(-) create mode 100644 trunk/src/qt/srs/srs-qt.pro create mode 100644 trunk/src/qt/srs/srs-qt.pro.user rename trunk/src/{srs => upp}/init (100%) rename trunk/src/{srs => upp}/srs.upp (100%) diff --git a/trunk/CMakeLists.txt b/trunk/CMakeLists.txt index 5d7ea46cca..5bb58829b8 100644 --- a/trunk/CMakeLists.txt +++ b/trunk/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 2.6.4) project(srs CXX) -INCLUDE_DIRECTORIES(objs objs/st objs/hp objs/openssl src/core src/kernel src/rtmp src/app) +INCLUDE_DIRECTORIES(objs objs/st objs/hp objs/openssl/include src/core src/kernel src/rtmp src/app) set(SOURCE_FILES src/main/srs_main_server.cpp) AUX_SOURCE_DIRECTORY(src/core SOURCE_FILES) @@ -15,6 +15,7 @@ TARGET_LINK_LIBRARIES(srs ${PROJECT_SOURCE_DIR}/objs/st/libst.a) TARGET_LINK_LIBRARIES(srs ${PROJECT_SOURCE_DIR}/objs/openssl/lib/libssl.a) TARGET_LINK_LIBRARIES(srs ${PROJECT_SOURCE_DIR}/objs/openssl/lib/libcrypto.a) TARGET_LINK_LIBRARIES(srs ${PROJECT_SOURCE_DIR}/objs/hp/libhttp_parser.a) +TARGET_LINK_LIBRARIES(srs -ldl) IF(NOT EXISTS ${PROJECT_SOURCE_DIR}/objs/st/libst.a) MESSAGE("srs_libs not found") diff --git a/trunk/src/app/srs_app_utility.cpp b/trunk/src/app/srs_app_utility.cpp index f6253747ed..caae83e22f 100644 --- a/trunk/src/app/srs_app_utility.cpp +++ b/trunk/src/app/srs_app_utility.cpp @@ -440,7 +440,7 @@ bool srs_get_disk_diskstats_stat(SrsDiskStat& r) unsigned int nb_current = 0; unsigned int ticks = 0; unsigned int aveq = 0; - memset(name, sizeof(name), 0); + memset(name, 0, sizeof(name)); sscanf(buf, "%4d %4d %31s %u %u %llu %u %u %u %llu %u %u %u %u", &major, diff --git a/trunk/src/qt/srs/srs-qt.pro b/trunk/src/qt/srs/srs-qt.pro new file mode 100644 index 0000000000..5e2f7c8974 --- /dev/null +++ b/trunk/src/qt/srs/srs-qt.pro @@ -0,0 +1,34 @@ +TEMPLATE = app +CONFIG += console +CONFIG -= app_bundle +CONFIG -= qt + +HEADERS += \ + ../../core/*.hpp \ + ../../kernel/*.hpp \ + ../../app/*.hpp \ + ../../rtmp/*.hpp + +SOURCES += \ + ../../core/*.cpp \ + ../../kernel/*.cpp \ + ../../app/*.cpp \ + ../../rtmp/*.cpp \ + ../../main/*.cpp + +INCLUDEPATH += \ + ../../core \ + ../../kernel \ + ../../app \ + ../../rtmp \ + ../../../objs \ + ../../../objs/st \ + ../../../objs/hp \ + ../../../objs/openssl/include + +LIBS += \ + ../../../objs/st/libst.a \ + ../../../objs/hp/libhttp_parser.a \ + ../../../objs/openssl/lib/libssl.a \ + ../../../objs/openssl/lib/libcrypto.a \ + -ldl diff --git a/trunk/src/qt/srs/srs-qt.pro.user b/trunk/src/qt/srs/srs-qt.pro.user new file mode 100644 index 0000000000..644bccae0d --- /dev/null +++ b/trunk/src/qt/srs/srs-qt.pro.user @@ -0,0 +1,204 @@ + + + + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 80 + true + true + 1 + true + false + 0 + true + 0 + 8 + true + 1 + true + true + true + false + + + + ProjectExplorer.Project.PluginSettings + + + + ProjectExplorer.Project.Target.0 + + Desktop + Desktop + {fa2d28f9-85de-4a75-8e79-69d805f974bf} + 0 + 0 + 0 + + /home/winlin/git/simple-rtmp-server/trunk/src/qt/build-qt-Desktop-Debug + + + true + qmake + + QtProjectManager.QMakeBuildStep + false + true + + false + + + true + Make + + Qt4ProjectManager.MakeStep + + -w + -r + + false + + + + 2 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + Make + + Qt4ProjectManager.MakeStep + + -w + -r + + true + clean + + + 1 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Debug + + Qt4ProjectManager.Qt4BuildConfiguration + 2 + true + + 1 + + + 0 + Deploy + + ProjectExplorer.BuildSteps.Deploy + + 1 + Deploy locally + + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + + false + false + false + false + true + 0.01 + 10 + true + 1 + 25 + + 1 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + 2 + + srs-qt + + Qt4ProjectManager.Qt4RunConfiguration:/home/winlin/git/simple-rtmp-server/trunk/src/qt/srs/srs-qt.pro + -c conf/console.conf + srs-qt.pro + false + true + /home/winlin/git/simple-rtmp-server/trunk + 3768 + false + true + false + false + true + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.EnvironmentId + {67c55164-ec8b-40af-90ff-d8b0b02de43a} + + + ProjectExplorer.Project.Updater.FileVersion + 15 + + diff --git a/trunk/src/srs/init b/trunk/src/upp/init similarity index 100% rename from trunk/src/srs/init rename to trunk/src/upp/init diff --git a/trunk/src/srs/srs.upp b/trunk/src/upp/srs.upp similarity index 100% rename from trunk/src/srs/srs.upp rename to trunk/src/upp/srs.upp From 3e521d317db69fe0d1faa3c535d5b3984e2d6e3c Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 30 Nov 2014 16:54:57 +0800 Subject: [PATCH 252/800] update upp dir to srs. --- trunk/src/{upp => srs}/init | 0 trunk/src/{upp => srs}/srs.upp | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename trunk/src/{upp => srs}/init (100%) rename trunk/src/{upp => srs}/srs.upp (100%) diff --git a/trunk/src/upp/init b/trunk/src/srs/init similarity index 100% rename from trunk/src/upp/init rename to trunk/src/srs/init diff --git a/trunk/src/upp/srs.upp b/trunk/src/srs/srs.upp similarity index 100% rename from trunk/src/upp/srs.upp rename to trunk/src/srs/srs.upp From 47b4b694bea9bd6e620dd7c6d667ab24cad85bc0 Mon Sep 17 00:00:00 2001 From: winlin Date: Mon, 1 Dec 2014 10:58:17 +0800 Subject: [PATCH 253/800] add qtcreate project file trunk/src/qt/srs/srs-qt.pro. 2.0.39. --- README.md | 1 + trunk/src/core/srs_core.hpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e1dff4fa87..b087e85375 100755 --- a/README.md +++ b/README.md @@ -485,6 +485,7 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History +* v2.0, 2014-12-01, add qtcreate project file trunk/src/qt/srs/srs-qt.pro. 2.0.39. * v2.0, 2014-11-29, fix [#235](https://github.com/winlinvip/simple-rtmp-server/issues/235), refine handshake, replace union with template method. 2.0.38. * v2.0, 2014-11-28, fix [#215](https://github.com/winlinvip/simple-rtmp-server/issues/215), add srs_rtmp_dump tool. 2.0.37. * v2.0, 2014-11-25, update PRIMARY, AUTHORS, CONTRIBUTORS rule. 2.0.32. diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 4915c69bab..faf62502ea 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 38 +#define VERSION_REVISION 39 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" From c5dd334645c7db9b9ee324614c2b1667b3fbf032 Mon Sep 17 00:00:00 2001 From: winlin Date: Mon, 1 Dec 2014 20:54:19 +0800 Subject: [PATCH 254/800] fix the warning of ts_info --- trunk/research/hls/ts_info.cc | 6 +++--- trunk/src/qt/srs/srs-qt.pro | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/trunk/research/hls/ts_info.cc b/trunk/research/hls/ts_info.cc index 99ff200b5d..cc84a5072b 100644 --- a/trunk/research/hls/ts_info.cc +++ b/trunk/research/hls/ts_info.cc @@ -2255,9 +2255,9 @@ int main(int argc, char** argv) int64_t pcr = msg->pcr; static int64_t last_pcr_dts = 0; trace("demuxer+report id=%d, type=%s, size=%d, dts=%d, pts=%d, cts=%d, pcr=%d, dts-pcr=%d, ref=%d, unit=%d, dts(diff-pcr)=%d", - ctx.ts_packet_count, (msg->type == TSPidTypeVideo)? "video":"audio", - msg->parsed_packet_size, dts, pts, pts - dts, pcr, pcr? dts - pcr : 0, - msg->nal_ref_idc, msg->nal_unit_type, pcr? dts - last_pcr_dts: 0); + (int)ctx.ts_packet_count, (msg->type == TSPidTypeVideo)? "video":"audio", + (int)msg->parsed_packet_size, (int)dts, (int)pts, (int)(pts - dts), (int)pcr, (int)(pcr? dts - pcr : 0), + (int)msg->nal_ref_idc, (int)msg->nal_unit_type, (int)(pcr? dts - last_pcr_dts: 0)); if (pcr > 0) { last_pcr_dts = dts; } diff --git a/trunk/src/qt/srs/srs-qt.pro b/trunk/src/qt/srs/srs-qt.pro index 5e2f7c8974..ef19d2cd3e 100644 --- a/trunk/src/qt/srs/srs-qt.pro +++ b/trunk/src/qt/srs/srs-qt.pro @@ -32,3 +32,4 @@ LIBS += \ ../../../objs/openssl/lib/libssl.a \ ../../../objs/openssl/lib/libcrypto.a \ -ldl + From d171a2675832479cbd94a14e60cb58e5e7810cf0 Mon Sep 17 00:00:00 2001 From: winlin Date: Mon, 1 Dec 2014 21:34:35 +0800 Subject: [PATCH 255/800] disable gop cache when not h.264. fix some warning. --- trunk/research/librtmp/srs_aac_raw_publish.c | 2 +- trunk/research/librtmp/srs_audio_raw_publish.c | 4 ++-- trunk/research/librtmp/srs_bandwidth_check.c | 3 ++- trunk/research/librtmp/srs_detect_rtmp.c | 2 +- trunk/research/librtmp/srs_h264_raw_publish.c | 2 +- trunk/src/app/srs_app_source.cpp | 6 ++++++ trunk/src/qt/srs/srs-qt.pro.user | 2 +- 7 files changed, 14 insertions(+), 7 deletions(-) diff --git a/trunk/research/librtmp/srs_aac_raw_publish.c b/trunk/research/librtmp/srs_aac_raw_publish.c index b1e9511390..10ddfcfb84 100644 --- a/trunk/research/librtmp/srs_aac_raw_publish.c +++ b/trunk/research/librtmp/srs_aac_raw_publish.c @@ -95,7 +95,7 @@ int main(int argc, char** argv) // open file int raw_fd = open(raw_file, O_RDONLY); if (raw_fd < 0) { - srs_human_trace("open audio raw file %s failed.", raw_fd); + srs_human_trace("open audio raw file %s failed.", raw_file); goto rtmp_destroy; } diff --git a/trunk/research/librtmp/srs_audio_raw_publish.c b/trunk/research/librtmp/srs_audio_raw_publish.c index 4c9c899142..8c45984e3c 100644 --- a/trunk/research/librtmp/srs_audio_raw_publish.c +++ b/trunk/research/librtmp/srs_audio_raw_publish.c @@ -47,7 +47,7 @@ int read_audio_frame(char* audio_raw, int file_size, char** pp, char** pdata, in if (file_size - (p - audio_raw) < 168) { srs_human_trace("audio must be 160+8 bytes. left %d bytes.", - file_size - (p - audio_raw)); + (int)(file_size - (p - audio_raw))); return - 1; } @@ -88,7 +88,7 @@ int main(int argc, char** argv) // open file int raw_fd = open(raw_file, O_RDONLY); if (raw_fd < 0) { - srs_human_trace("open audio raw file %s failed.", raw_fd); + srs_human_trace("open audio raw file %s failed.", raw_file); goto rtmp_destroy; } diff --git a/trunk/research/librtmp/srs_bandwidth_check.c b/trunk/research/librtmp/srs_bandwidth_check.c index 3a3fac75b0..ddf3de9572 100644 --- a/trunk/research/librtmp/srs_bandwidth_check.c +++ b/trunk/research/librtmp/srs_bandwidth_check.c @@ -132,6 +132,7 @@ int main(int argc, char** argv) "\"srs_id\":%d, " "\"duration\":%d, " "\"play_duration\":%d, " + "\"publish_duration\":%d," "\"play_kbps\":%d, " "\"publish_kbps\":%d" "}", @@ -141,7 +142,7 @@ int main(int argc, char** argv) (int)(end_time - start_time), play_duration, publish_duration, play_kbps, publish_kbps); - srs_human_trace(""); + srs_human_trace(" "); srs_human_trace("completed"); return ret; diff --git a/trunk/research/librtmp/srs_detect_rtmp.c b/trunk/research/librtmp/srs_detect_rtmp.c index 84d18cc34d..df18a787f4 100644 --- a/trunk/research/librtmp/srs_detect_rtmp.c +++ b/trunk/research/librtmp/srs_detect_rtmp.c @@ -197,7 +197,7 @@ int main(int argc, char** argv) "\"remark2\": \"if code is not 0, user must ignore all data\"" ); - srs_human_trace(""); + srs_human_trace(" "); srs_human_trace("completed"); return ret; diff --git a/trunk/research/librtmp/srs_h264_raw_publish.c b/trunk/research/librtmp/srs_h264_raw_publish.c index d85da46d17..d111665dd4 100644 --- a/trunk/research/librtmp/srs_h264_raw_publish.c +++ b/trunk/research/librtmp/srs_h264_raw_publish.c @@ -100,7 +100,7 @@ int main(int argc, char** argv) // open file int raw_fd = open(raw_file, O_RDONLY); if (raw_fd < 0) { - srs_human_trace("open h264 raw file %s failed.", raw_fd); + srs_human_trace("open h264 raw file %s failed.", raw_file); goto rtmp_destroy; } diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index e5405ae1b4..d03b6dc08f 100644 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -392,6 +392,12 @@ int SrsGopCache::cache(SrsSharedPtrMessage* msg) srs_verbose("gop cache is disabled."); return ret; } + + // disable gop cache when not h.264 + if (!SrsFlvCodec::video_is_h264(msg->payload, msg->size)) { + srs_info("gop donot cache video for none h.264"); + return ret; + } // got video, update the video count if acceptable if (msg->header.is_video()) { diff --git a/trunk/src/qt/srs/srs-qt.pro.user b/trunk/src/qt/srs/srs-qt.pro.user index 644bccae0d..cf534d0dd8 100644 --- a/trunk/src/qt/srs/srs-qt.pro.user +++ b/trunk/src/qt/srs/srs-qt.pro.user @@ -1,6 +1,6 @@ - + ProjectExplorer.Project.ActiveTarget From 1847c5fef7dac200a5e3d5aea38950f3382e2625 Mon Sep 17 00:00:00 2001 From: winlin Date: Mon, 1 Dec 2014 22:09:09 +0800 Subject: [PATCH 256/800] ignore the actual length of share key. 2.0.40 --- trunk/src/core/srs_core.hpp | 2 +- trunk/src/rtmp/srs_protocol_handshake.cpp | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index faf62502ea..266a47f9d2 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 39 +#define VERSION_REVISION 40 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/rtmp/srs_protocol_handshake.cpp b/trunk/src/rtmp/srs_protocol_handshake.cpp index e67c9e9ac7..e5a69d7193 100644 --- a/trunk/src/rtmp/srs_protocol_handshake.cpp +++ b/trunk/src/rtmp/srs_protocol_handshake.cpp @@ -532,7 +532,11 @@ namespace _srs_internal srs_error("calc s1 key failed. ret=%d", ret); return ret; } - srs_assert(pkey_size == 128); + + // altough the public key is always 128bytes, but the share key maybe not. + // we just ignore the actual key size, but if need to use the key, must use the actual size. + // TODO: FIXME: use the actual key size. + //srs_assert(pkey_size == 128); srs_verbose("calc s1 key success."); char* s1_digest = NULL; From 22524f390a46b29fb432535fa818edcf6ee8f53e Mon Sep 17 00:00:00 2001 From: winlin Date: Mon, 1 Dec 2014 22:23:05 +0800 Subject: [PATCH 257/800] rename the recv thread to queue recv thread for bug #237. --- trunk/src/app/srs_app_recv_thread.cpp | 20 ++++++++++---------- trunk/src/app/srs_app_recv_thread.hpp | 6 +++--- trunk/src/app/srs_app_rtmp_conn.cpp | 4 ++-- trunk/src/app/srs_app_rtmp_conn.hpp | 4 ++-- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index 0134af8de0..2615784797 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -26,13 +26,13 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include -SrsRecvThread::SrsRecvThread(SrsRtmpServer* rtmp_sdk) +SrsQueueRecvThread::SrsQueueRecvThread(SrsRtmpServer* rtmp_sdk) { rtmp = rtmp_sdk; trd = new SrsThread(this, 0, true); } -SrsRecvThread::~SrsRecvThread() +SrsQueueRecvThread::~SrsQueueRecvThread() { // stop recv thread. stop(); @@ -49,17 +49,17 @@ SrsRecvThread::~SrsRecvThread() queue.clear(); } -bool SrsRecvThread::empty() +bool SrsQueueRecvThread::empty() { return queue.empty(); } -int SrsRecvThread::size() +int SrsQueueRecvThread::size() { return (int)queue.size(); } -SrsMessage* SrsRecvThread::pump() +SrsMessage* SrsQueueRecvThread::pump() { srs_assert(!queue.empty()); @@ -70,17 +70,17 @@ SrsMessage* SrsRecvThread::pump() return msg; } -int SrsRecvThread::start() +int SrsQueueRecvThread::start() { return trd->start(); } -void SrsRecvThread::stop() +void SrsQueueRecvThread::stop() { trd->stop(); } -int SrsRecvThread::cycle() +int SrsQueueRecvThread::cycle() { int ret = ERROR_SUCCESS; @@ -114,7 +114,7 @@ int SrsRecvThread::cycle() return ret; } -void SrsRecvThread::on_thread_start() +void SrsQueueRecvThread::on_thread_start() { // the multiple messages writev improve performance large, // but the timeout recv will cause 33% sys call performance, @@ -128,7 +128,7 @@ void SrsRecvThread::on_thread_start() rtmp->set_auto_response(false); } -void SrsRecvThread::on_thread_stop() +void SrsQueueRecvThread::on_thread_stop() { // enable the protocol auto response, // for the isolate recv thread terminated. diff --git a/trunk/src/app/srs_app_recv_thread.hpp b/trunk/src/app/srs_app_recv_thread.hpp index 640decd1cd..29cd74b27f 100644 --- a/trunk/src/app/srs_app_recv_thread.hpp +++ b/trunk/src/app/srs_app_recv_thread.hpp @@ -43,15 +43,15 @@ class SrsMessage; * @see: SrsRtmpConn::playing * @see: https://github.com/winlinvip/simple-rtmp-server/issues/217 */ -class SrsRecvThread : public ISrsThreadHandler +class SrsQueueRecvThread : public ISrsThreadHandler { private: SrsThread* trd; SrsRtmpServer* rtmp; std::vector queue; public: - SrsRecvThread(SrsRtmpServer* rtmp_sdk); - virtual ~SrsRecvThread(); + SrsQueueRecvThread(SrsRtmpServer* rtmp_sdk); + virtual ~SrsQueueRecvThread(); public: virtual bool empty(); virtual int size(); diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index f280490e4b..d5663bcfb5 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -500,7 +500,7 @@ int SrsRtmpConn::playing(SrsSource* source) // use isolate thread to recv, // @see: https://github.com/winlinvip/simple-rtmp-server/issues/217 - SrsRecvThread trd(rtmp); + SrsQueueRecvThread trd(rtmp); // start isolate recv thread. if ((ret = trd.start()) != ERROR_SUCCESS) { @@ -522,7 +522,7 @@ int SrsRtmpConn::playing(SrsSource* source) return ret; } -int SrsRtmpConn::do_playing(SrsSource* source, SrsRecvThread* trd) +int SrsRtmpConn::do_playing(SrsSource* source, SrsQueueRecvThread* trd) { int ret = ERROR_SUCCESS; diff --git a/trunk/src/app/srs_app_rtmp_conn.hpp b/trunk/src/app/srs_app_rtmp_conn.hpp index 0374c1077b..06dd917078 100644 --- a/trunk/src/app/srs_app_rtmp_conn.hpp +++ b/trunk/src/app/srs_app_rtmp_conn.hpp @@ -49,7 +49,7 @@ class SrsBandwidth; class SrsKbps; class SrsRtmpClient; class SrsSharedPtrMessage; -class SrsRecvThread; +class SrsQueueRecvThread; /** * the client provides the main logic control for RTMP clients. @@ -89,7 +89,7 @@ class SrsRtmpConn : public virtual SrsConnection, public virtual ISrsReloadHandl virtual int stream_service_cycle(); virtual int check_vhost(); virtual int playing(SrsSource* source); - virtual int do_playing(SrsSource* source, SrsRecvThread* trd); + virtual int do_playing(SrsSource* source, SrsQueueRecvThread* trd); virtual int fmle_publishing(SrsSource* source); virtual int do_fmle_publishing(SrsSource* source); virtual int flash_publishing(SrsSource* source); From 31eb9bf1c12415f95a4062c5447161ae8e6342f8 Mon Sep 17 00:00:00 2001 From: winlin Date: Mon, 1 Dec 2014 22:39:22 +0800 Subject: [PATCH 258/800] for bug #237, extract a queue recv thread. --- trunk/src/app/srs_app_recv_thread.cpp | 138 ++++++++++++++++---------- trunk/src/app/srs_app_recv_thread.hpp | 55 ++++++++-- 2 files changed, 130 insertions(+), 63 deletions(-) diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index 2615784797..ce8fa590ab 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -26,95 +26,69 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include -SrsQueueRecvThread::SrsQueueRecvThread(SrsRtmpServer* rtmp_sdk) +ISrsMessageHandler::ISrsMessageHandler() { - rtmp = rtmp_sdk; - trd = new SrsThread(this, 0, true); } -SrsQueueRecvThread::~SrsQueueRecvThread() +ISrsMessageHandler::~ISrsMessageHandler() { - // stop recv thread. - stop(); - - // destroy the thread. - srs_freep(trd); - - // clear all messages. - std::vector::iterator it; - for (it = queue.begin(); it != queue.end(); ++it) { - SrsMessage* msg = *it; - srs_freep(msg); - } - queue.clear(); } -bool SrsQueueRecvThread::empty() +SrsRecvThread::SrsRecvThread(ISrsMessageHandler* msg_handler, SrsRtmpServer* rtmp_sdk) { - return queue.empty(); + handler = msg_handler; + rtmp = rtmp_sdk; + trd = new SrsThread(this, 0, true); } -int SrsQueueRecvThread::size() +SrsRecvThread::~SrsRecvThread() { - return (int)queue.size(); -} + // stop recv thread. + stop(); -SrsMessage* SrsQueueRecvThread::pump() -{ - srs_assert(!queue.empty()); - - SrsMessage* msg = *queue.begin(); - - queue.erase(queue.begin()); - - return msg; + // destroy the thread. + srs_freep(trd); } -int SrsQueueRecvThread::start() +int SrsRecvThread::start() { return trd->start(); } -void SrsQueueRecvThread::stop() +void SrsRecvThread::stop() { trd->stop(); } -int SrsQueueRecvThread::cycle() +int SrsRecvThread::cycle() { int ret = ERROR_SUCCESS; - - // we only recv one message and then process it, - // for the message may cause the thread to stop, - // when stop, the thread is freed, so the messages - // are dropped. - if (!queue.empty()) { + + if (!handler->can_handle()) { st_usleep(SRS_CONSTS_RTMP_PULSE_TIMEOUT_US); return ret; } - + SrsMessage* msg = NULL; - + if ((ret = rtmp->recv_message(&msg)) != ERROR_SUCCESS) { if (!srs_is_client_gracefully_close(ret)) { srs_error("recv client control message failed. ret=%d", ret); } - + // we use no timeout to recv, should never got any error. trd->stop_loop(); - + return ret; } srs_verbose("play loop recv message. ret=%d", ret); - - // put into queue, the send thread will get and process it, - // @see SrsRtmpConn::process_play_control_msg - queue.push_back(msg); - + + handler->handle(msg); + return ret; } -void SrsQueueRecvThread::on_thread_start() +void SrsRecvThread::on_thread_start() { // the multiple messages writev improve performance large, // but the timeout recv will cause 33% sys call performance, @@ -122,19 +96,75 @@ void SrsQueueRecvThread::on_thread_start() // @see https://github.com/winlinvip/simple-rtmp-server/issues/194 // @see: https://github.com/winlinvip/simple-rtmp-server/issues/217 rtmp->set_recv_timeout(ST_UTIME_NO_TIMEOUT); - - // disable the protocol auto response, + + // disable the protocol auto response, // for the isolate recv thread should never send any messages. rtmp->set_auto_response(false); } -void SrsQueueRecvThread::on_thread_stop() +void SrsRecvThread::on_thread_stop() { // enable the protocol auto response, // for the isolate recv thread terminated. rtmp->set_auto_response(true); - + // reset the timeout to pulse mode. rtmp->set_recv_timeout(SRS_CONSTS_RTMP_PULSE_TIMEOUT_US); } +SrsQueueRecvThread::SrsQueueRecvThread(SrsRtmpServer* rtmp_sdk) + : SrsRecvThread(this, rtmp_sdk) +{ +} + +SrsQueueRecvThread::~SrsQueueRecvThread() +{ + stop(); + + // clear all messages. + std::vector::iterator it; + for (it = queue.begin(); it != queue.end(); ++it) { + SrsMessage* msg = *it; + srs_freep(msg); + } + queue.clear(); +} + +bool SrsQueueRecvThread::empty() +{ + return queue.empty(); +} + +int SrsQueueRecvThread::size() +{ + return (int)queue.size(); +} + +SrsMessage* SrsQueueRecvThread::pump() +{ + srs_assert(!queue.empty()); + + SrsMessage* msg = *queue.begin(); + + queue.erase(queue.begin()); + + return msg; +} + +bool SrsQueueRecvThread::can_handle() +{ + // we only recv one message and then process it, + // for the message may cause the thread to stop, + // when stop, the thread is freed, so the messages + // are dropped. + return empty(); +} + +int SrsQueueRecvThread::handle(SrsMessage* msg) +{ + // put into queue, the send thread will get and process it, + // @see SrsRtmpConn::process_play_control_msg + queue.push_back(msg); + + return ERROR_SUCCESS; +} diff --git a/trunk/src/app/srs_app_recv_thread.hpp b/trunk/src/app/srs_app_recv_thread.hpp index 29cd74b27f..914be09e3c 100644 --- a/trunk/src/app/srs_app_recv_thread.hpp +++ b/trunk/src/app/srs_app_recv_thread.hpp @@ -37,17 +37,58 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. class SrsRtmpServer; class SrsMessage; +/** + * for the recv thread to handle the message. + */ +class ISrsMessageHandler +{ +public: + ISrsMessageHandler(); + virtual ~ISrsMessageHandler(); +public: + /** + * whether the handler can handle, + * for example, when queue recv handler got an message, + * it wait the user to process it, then the recv thread + * never recv message util the handler is ok. + */ + virtual bool can_handle() = 0; + /** + * process the received message. + */ + virtual int handle(SrsMessage* msg) = 0; +}; + +/** + * the recv thread, use message handler to handle each received message. + */ +class SrsRecvThread : public ISrsThreadHandler +{ +protected: + SrsThread* trd; + ISrsMessageHandler* handler; + SrsRtmpServer* rtmp; +public: + SrsRecvThread(ISrsMessageHandler* msg_handler, SrsRtmpServer* rtmp_sdk); + virtual ~SrsRecvThread(); +public: + virtual int start(); + virtual void stop(); + virtual int cycle(); +public: + virtual void on_thread_start(); + virtual void on_thread_stop(); +}; + /** * the recv thread used to replace the timeout recv, * which hurt performance for the epoll_ctrl is frequently used. * @see: SrsRtmpConn::playing * @see: https://github.com/winlinvip/simple-rtmp-server/issues/217 */ -class SrsQueueRecvThread : public ISrsThreadHandler +class SrsQueueRecvThread : virtual public ISrsMessageHandler, virtual public SrsRecvThread { private: - SrsThread* trd; - SrsRtmpServer* rtmp; std::vector queue; public: SrsQueueRecvThread(SrsRtmpServer* rtmp_sdk); @@ -57,12 +98,8 @@ class SrsQueueRecvThread : public ISrsThreadHandler virtual int size(); virtual SrsMessage* pump(); public: - virtual int start(); - virtual void stop(); - virtual int cycle(); -public: - virtual void on_thread_start(); - virtual void on_thread_stop(); + virtual bool can_handle(); + virtual int handle(SrsMessage* msg); }; #endif From 0b631ee80df887c96b41528fd2d80c6235de7f71 Mon Sep 17 00:00:00 2001 From: winlin Date: Mon, 1 Dec 2014 22:45:45 +0800 Subject: [PATCH 259/800] for bug #235, pass timeout to recv thread. --- trunk/src/app/srs_app_recv_thread.cpp | 11 ++++++----- trunk/src/app/srs_app_recv_thread.hpp | 5 +++-- trunk/src/app/srs_app_rtmp_conn.cpp | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index ce8fa590ab..fe27a6c39b 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -34,8 +34,9 @@ ISrsMessageHandler::~ISrsMessageHandler() { } -SrsRecvThread::SrsRecvThread(ISrsMessageHandler* msg_handler, SrsRtmpServer* rtmp_sdk) +SrsRecvThread::SrsRecvThread(ISrsMessageHandler* msg_handler, SrsRtmpServer* rtmp_sdk, int timeout_ms) { + timeout = timeout_ms; handler = msg_handler; rtmp = rtmp_sdk; trd = new SrsThread(this, 0, true); @@ -65,7 +66,7 @@ int SrsRecvThread::cycle() int ret = ERROR_SUCCESS; if (!handler->can_handle()) { - st_usleep(SRS_CONSTS_RTMP_PULSE_TIMEOUT_US); + st_usleep(timeout * 1000); return ret; } @@ -109,11 +110,11 @@ void SrsRecvThread::on_thread_stop() rtmp->set_auto_response(true); // reset the timeout to pulse mode. - rtmp->set_recv_timeout(SRS_CONSTS_RTMP_PULSE_TIMEOUT_US); + rtmp->set_recv_timeout(timeout * 1000); } -SrsQueueRecvThread::SrsQueueRecvThread(SrsRtmpServer* rtmp_sdk) - : SrsRecvThread(this, rtmp_sdk) +SrsQueueRecvThread::SrsQueueRecvThread(SrsRtmpServer* rtmp_sdk, int timeout_ms) + : SrsRecvThread(this, rtmp_sdk, timeout_ms) { } diff --git a/trunk/src/app/srs_app_recv_thread.hpp b/trunk/src/app/srs_app_recv_thread.hpp index 914be09e3c..18be0df1e2 100644 --- a/trunk/src/app/srs_app_recv_thread.hpp +++ b/trunk/src/app/srs_app_recv_thread.hpp @@ -68,8 +68,9 @@ class SrsRecvThread : public ISrsThreadHandler SrsThread* trd; ISrsMessageHandler* handler; SrsRtmpServer* rtmp; + int timeout; public: - SrsRecvThread(ISrsMessageHandler* msg_handler, SrsRtmpServer* rtmp_sdk); + SrsRecvThread(ISrsMessageHandler* msg_handler, SrsRtmpServer* rtmp_sdk, int timeout_ms); virtual ~SrsRecvThread(); public: virtual int start(); @@ -91,7 +92,7 @@ class SrsQueueRecvThread : virtual public ISrsMessageHandler, virtual public Srs private: std::vector queue; public: - SrsQueueRecvThread(SrsRtmpServer* rtmp_sdk); + SrsQueueRecvThread(SrsRtmpServer* rtmp_sdk, int timeout_ms); virtual ~SrsQueueRecvThread(); public: virtual bool empty(); diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index d5663bcfb5..f1ac4b548c 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -500,7 +500,7 @@ int SrsRtmpConn::playing(SrsSource* source) // use isolate thread to recv, // @see: https://github.com/winlinvip/simple-rtmp-server/issues/217 - SrsQueueRecvThread trd(rtmp); + SrsQueueRecvThread trd(rtmp, SRS_CONSTS_RTMP_PULSE_TIMEOUT_US); // start isolate recv thread. if ((ret = trd.start()) != ERROR_SUCCESS) { From 0e7836868c480aa9ed0cf82db233965876d4dd9e Mon Sep 17 00:00:00 2001 From: winlin Date: Mon, 1 Dec 2014 22:53:03 +0800 Subject: [PATCH 260/800] for bug #235, refine the queue recv thread. --- trunk/src/app/srs_app_recv_thread.cpp | 14 ++++++++++++-- trunk/src/app/srs_app_recv_thread.hpp | 6 +++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index fe27a6c39b..317309627d 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -114,13 +114,13 @@ void SrsRecvThread::on_thread_stop() } SrsQueueRecvThread::SrsQueueRecvThread(SrsRtmpServer* rtmp_sdk, int timeout_ms) - : SrsRecvThread(this, rtmp_sdk, timeout_ms) + : trd(this, rtmp_sdk, timeout_ms) { } SrsQueueRecvThread::~SrsQueueRecvThread() { - stop(); + trd.stop(); // clear all messages. std::vector::iterator it; @@ -131,6 +131,16 @@ SrsQueueRecvThread::~SrsQueueRecvThread() queue.clear(); } +int SrsQueueRecvThread::start() +{ + return trd.start(); +} + +void SrsQueueRecvThread::stop() +{ + trd.stop(); +} + bool SrsQueueRecvThread::empty() { return queue.empty(); diff --git a/trunk/src/app/srs_app_recv_thread.hpp b/trunk/src/app/srs_app_recv_thread.hpp index 18be0df1e2..31852742e0 100644 --- a/trunk/src/app/srs_app_recv_thread.hpp +++ b/trunk/src/app/srs_app_recv_thread.hpp @@ -87,13 +87,17 @@ class SrsRecvThread : public ISrsThreadHandler * @see: SrsRtmpConn::playing * @see: https://github.com/winlinvip/simple-rtmp-server/issues/217 */ -class SrsQueueRecvThread : virtual public ISrsMessageHandler, virtual public SrsRecvThread +class SrsQueueRecvThread : virtual public ISrsMessageHandler { private: std::vector queue; + SrsRecvThread trd; public: SrsQueueRecvThread(SrsRtmpServer* rtmp_sdk, int timeout_ms); virtual ~SrsQueueRecvThread(); +public: + virtual int start(); + virtual void stop(); public: virtual bool empty(); virtual int size(); From 472b1742a2cf7b204a213933f8345f3dfcffd888 Mon Sep 17 00:00:00 2001 From: winlin Date: Mon, 1 Dec 2014 23:38:51 +0800 Subject: [PATCH 261/800] for bug #237, use isolate thread to recv message. 2.0.41 --- trunk/src/app/srs_app_recv_thread.cpp | 75 +++++++++ trunk/src/app/srs_app_recv_thread.hpp | 36 ++++- trunk/src/app/srs_app_rtmp_conn.cpp | 211 ++++++++++++-------------- trunk/src/app/srs_app_rtmp_conn.hpp | 7 +- trunk/src/core/srs_core.hpp | 2 +- 5 files changed, 209 insertions(+), 122 deletions(-) diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index 317309627d..c59be261f3 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -25,6 +25,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +#include ISrsMessageHandler::ISrsMessageHandler() { @@ -89,6 +90,11 @@ int SrsRecvThread::cycle() return ret; } +void SrsRecvThread::stop_loop() +{ + trd->stop_loop(); +} + void SrsRecvThread::on_thread_start() { // the multiple messages writev improve performance large, @@ -179,3 +185,72 @@ int SrsQueueRecvThread::handle(SrsMessage* msg) return ERROR_SUCCESS; } + +SrsPublishRecvThread::SrsPublishRecvThread( + SrsRtmpServer* rtmp_sdk, int timeout_ms, + SrsRtmpConn* conn, SrsSource* source, bool is_fmle, bool is_edge +): trd(this, rtmp_sdk, timeout_ms) +{ + _conn = conn; + _source = source; + _is_fmle = is_fmle; + _is_edge = is_edge; + + recv_error_code = ERROR_SUCCESS; + _nb_msgs = 0; +} + +SrsPublishRecvThread::~SrsPublishRecvThread() +{ + trd.stop(); +} + +int64_t SrsPublishRecvThread::nb_msgs() +{ + return _nb_msgs; +} + +int SrsPublishRecvThread::error_code() +{ + return recv_error_code; +} + +int SrsPublishRecvThread::start() +{ + return trd.start(); +} + +void SrsPublishRecvThread::stop() +{ + trd.stop(); +} + +bool SrsPublishRecvThread::can_handle() +{ + // publish thread always can handle message. + return true; +} + +int SrsPublishRecvThread::handle(SrsMessage* msg) +{ + int ret = ERROR_SUCCESS; + + _nb_msgs++; + + // the rtmp connection will handle this message, + // quit the thread loop when error. + recv_error_code = ret = _conn->handle_publish_message(_source, msg, _is_fmle, _is_edge); + + // when error, use stop loop to terminate the thread normally, + // for we are in the thread loop now, and should never use stop() to terminate it. + if (ret != ERROR_SUCCESS) { + trd.stop_loop(); + } + + // must always free it, + // the source will copy it if need to use. + srs_freep(msg); + + // TODO: FIXME: implements it. + return ret; +} diff --git a/trunk/src/app/srs_app_recv_thread.hpp b/trunk/src/app/srs_app_recv_thread.hpp index 31852742e0..b52b120a6a 100644 --- a/trunk/src/app/srs_app_recv_thread.hpp +++ b/trunk/src/app/srs_app_recv_thread.hpp @@ -36,6 +36,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. class SrsRtmpServer; class SrsMessage; +class SrsRtmpConn; +class SrsSource; /** * for the recv thread to handle the message. @@ -76,6 +78,7 @@ class SrsRecvThread : public ISrsThreadHandler virtual int start(); virtual void stop(); virtual int cycle(); + virtual void stop_loop(); public: virtual void on_thread_start(); virtual void on_thread_stop(); @@ -87,7 +90,7 @@ class SrsRecvThread : public ISrsThreadHandler * @see: SrsRtmpConn::playing * @see: https://github.com/winlinvip/simple-rtmp-server/issues/217 */ -class SrsQueueRecvThread : virtual public ISrsMessageHandler +class SrsQueueRecvThread : public ISrsMessageHandler { private: std::vector queue; @@ -107,5 +110,36 @@ class SrsQueueRecvThread : virtual public ISrsMessageHandler virtual int handle(SrsMessage* msg); }; +/** + * the publish recv thread got message and callback the source method to process message. +* @see: https://github.com/winlinvip/simple-rtmp-server/issues/237 + */ +class SrsPublishRecvThread : public ISrsMessageHandler +{ +private: + SrsRecvThread trd; + // the msgs already got. + int64_t _nb_msgs; + // the recv thread error code. + int recv_error_code; + SrsRtmpConn* _conn; + SrsSource* _source; + bool _is_fmle; + bool _is_edge; +public: + SrsPublishRecvThread(SrsRtmpServer* rtmp_sdk, int timeout_ms, + SrsRtmpConn* conn, SrsSource* source, bool is_fmle, bool is_edge); + virtual ~SrsPublishRecvThread(); +public: + virtual int64_t nb_msgs(); + virtual int error_code(); +public: + virtual int start(); + virtual void stop(); +public: + virtual bool can_handle(); + virtual int handle(SrsMessage* msg); +}; + #endif diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index f1ac4b548c..2282ba049b 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -644,8 +644,15 @@ int SrsRtmpConn::fmle_publishing(SrsSource* source) return ret; } + // use isolate thread to recv, + // @see: https://github.com/winlinvip/simple-rtmp-server/issues/237 + SrsPublishRecvThread trd(rtmp, SRS_CONSTS_RTMP_RECV_TIMEOUT_US, this, source, true, vhost_is_edge); + srs_info("start to publish stream %s success", req->stream.c_str()); - ret = do_fmle_publishing(source); + ret = do_publishing(source, &trd); + + // stop isolate recv thread + trd.stop(); // when edge, notice edge to change state. // when origin, notice all service to unpublish. @@ -660,95 +667,26 @@ int SrsRtmpConn::fmle_publishing(SrsSource* source) return ret; } -int SrsRtmpConn::do_fmle_publishing(SrsSource* source) -{ - int ret = ERROR_SUCCESS; - - if ((ret = refer->check(req->pageUrl, _srs_config->get_refer_publish(req->vhost))) != ERROR_SUCCESS) { - srs_error("fmle check publish_refer failed. ret=%d", ret); - return ret; - } - srs_verbose("fmle check publish_refer success."); - - SrsPithyPrint pithy_print(SRS_CONSTS_STAGE_PUBLISH_USER); - - bool vhost_is_edge = _srs_config->get_vhost_is_edge(req->vhost); - - // when edge, ignore the publish event, directly proxy it. - if (!vhost_is_edge) { - // notify the hls to prepare when publish start. - if ((ret = source->on_publish()) != ERROR_SUCCESS) { - srs_error("fmle hls on_publish failed. ret=%d", ret); - return ret; - } - srs_verbose("fmle hls on_publish success."); - } - - while (true) { - SrsMessage* msg = NULL; - if ((ret = rtmp->recv_message(&msg)) != ERROR_SUCCESS) { - srs_error("fmle recv identify client message failed. ret=%d", ret); - return ret; - } - - SrsAutoFree(SrsMessage, msg); - - pithy_print.elapse(); - - // reportable - if (pithy_print.can_print()) { - kbps->sample(); - srs_trace("<- "SRS_CONSTS_LOG_CLIENT_PUBLISH - " time=%"PRId64", okbps=%d,%d,%d, ikbps=%d,%d,%d", pithy_print.age(), - 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()); - } - - // process UnPublish event. - if (msg->header.is_amf0_command() || msg->header.is_amf3_command()) { - SrsPacket* pkt = NULL; - if ((ret = rtmp->decode_message(msg, &pkt)) != ERROR_SUCCESS) { - srs_error("fmle decode unpublish message failed. ret=%d", ret); - return ret; - } - - SrsAutoFree(SrsPacket, pkt); - - if (dynamic_cast(pkt)) { - SrsFMLEStartPacket* unpublish = dynamic_cast(pkt); - if ((ret = rtmp->fmle_unpublish(res->stream_id, unpublish->transaction_id)) != ERROR_SUCCESS) { - return ret; - } - return ERROR_CONTROL_REPUBLISH; - } - - srs_trace("fmle ignore AMF0/AMF3 command message."); - continue; - } - - // video, audio, data message - if ((ret = process_publish_message(source, msg, vhost_is_edge)) != ERROR_SUCCESS) { - srs_error("fmle process publish message failed. ret=%d", ret); - return ret; - } - } - - return ret; -} - int SrsRtmpConn::flash_publishing(SrsSource* source) { int ret = ERROR_SUCCESS; - + bool vhost_is_edge = _srs_config->get_vhost_is_edge(req->vhost); - + if ((ret = http_hooks_on_publish()) != ERROR_SUCCESS) { srs_error("http hook on_publish failed. ret=%d", ret); return ret; } - - srs_info("flash start to publish stream %s success", req->stream.c_str()); - ret = do_flash_publishing(source); + + // use isolate thread to recv, + // @see: https://github.com/winlinvip/simple-rtmp-server/issues/237 + SrsPublishRecvThread trd(rtmp, SRS_CONSTS_RTMP_RECV_TIMEOUT_US, this, source, false, vhost_is_edge); + + srs_info("start to publish stream %s success", req->stream.c_str()); + ret = do_publishing(source, &trd); + + // stop isolate recv thread + trd.stop(); // when edge, notice edge to change state. // when origin, notice all service to unpublish. @@ -757,80 +695,117 @@ int SrsRtmpConn::flash_publishing(SrsSource* source) } else { source->on_unpublish(); } - + http_hooks_on_unpublish(); - + return ret; } -int SrsRtmpConn::do_flash_publishing(SrsSource* source) +int SrsRtmpConn::do_publishing(SrsSource* source, SrsPublishRecvThread* trd) { int ret = ERROR_SUCCESS; - + if ((ret = refer->check(req->pageUrl, _srs_config->get_refer_publish(req->vhost))) != ERROR_SUCCESS) { - srs_error("flash check publish_refer failed. ret=%d", ret); + srs_error("check publish_refer failed. ret=%d", ret); return ret; } - srs_verbose("flash check publish_refer success."); - + srs_verbose("check publish_refer success."); + SrsPithyPrint pithy_print(SRS_CONSTS_STAGE_PUBLISH_USER); - + bool vhost_is_edge = _srs_config->get_vhost_is_edge(req->vhost); - + // when edge, ignore the publish event, directly proxy it. if (!vhost_is_edge) { // notify the hls to prepare when publish start. if ((ret = source->on_publish()) != ERROR_SUCCESS) { - srs_error("flash hls on_publish failed. ret=%d", ret); + srs_error("hls on_publish failed. ret=%d", ret); return ret; } - srs_verbose("flash hls on_publish success."); + srs_verbose("hls on_publish success."); } - + + // start isolate recv thread. + if ((ret = trd->start()) != ERROR_SUCCESS) { + srs_error("start isolate recv thread failed. ret=%d", ret); + return ret; + } + + int64_t nb_msgs = 0; while (true) { - SrsMessage* msg = NULL; - if ((ret = rtmp->recv_message(&msg)) != ERROR_SUCCESS) { - if (!srs_is_client_gracefully_close(ret)) { - srs_error("flash recv identify client message failed. ret=%d", ret); + // use small loop to check the error code, interval = 30s/100 = 300ms. + for (int i = 0; i < 100; i++) { + st_usleep(SRS_CONSTS_RTMP_RECV_TIMEOUT_US * 1000 / 100); + + // check the thread error code. + if ((ret = trd->error_code()) != ERROR_SUCCESS) { + return ret; } - return ret; } - SrsAutoFree(SrsMessage, msg); - + // when not got any messages, timeout. + if (trd->nb_msgs() <= nb_msgs) { + ret = ERROR_SOCKET_TIMEOUT; + srs_warn("publish timeout %"PRId64"us, nb_msgs=%"PRId64", ret=%d", + SRS_CONSTS_RTMP_RECV_TIMEOUT_US, nb_msgs, ret); + break; + } + nb_msgs = trd->nb_msgs(); + pithy_print.elapse(); // reportable if (pithy_print.can_print()) { kbps->sample(); - srs_trace("<- "SRS_CONSTS_LOG_WEB_PUBLISH - " time=%"PRId64", okbps=%d,%d,%d, ikbps=%d,%d,%d", - pithy_print.age(), + srs_trace("<- "SRS_CONSTS_LOG_CLIENT_PUBLISH + " time=%"PRId64", okbps=%d,%d,%d, ikbps=%d,%d,%d", pithy_print.age(), 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()); } + } + + return ret; +} + +int SrsRtmpConn::handle_publish_message(SrsSource* source, SrsMessage* msg, bool is_fmle, bool vhost_is_edge) +{ + int ret = ERROR_SUCCESS; - // process UnPublish event. - if (msg->header.is_amf0_command() || msg->header.is_amf3_command()) { - SrsPacket* pkt = NULL; - if ((ret = rtmp->decode_message(msg, &pkt)) != ERROR_SUCCESS) { - srs_error("flash decode unpublish message failed. ret=%d", ret); - return ret; - } - - SrsAutoFree(SrsPacket, pkt); - + // process publish event. + if (msg->header.is_amf0_command() || msg->header.is_amf3_command()) { + SrsPacket* pkt = NULL; + if ((ret = rtmp->decode_message(msg, &pkt)) != ERROR_SUCCESS) { + srs_error("fmle decode unpublish message failed. ret=%d", ret); + return ret; + } + + SrsAutoFree(SrsPacket, pkt); + + // for flash, any packet is republish. + if (!is_fmle) { // flash unpublish. // TODO: maybe need to support republish. srs_trace("flash flash publish finished."); return ERROR_CONTROL_REPUBLISH; } - // video, audio, data message - if ((ret = process_publish_message(source, msg, vhost_is_edge)) != ERROR_SUCCESS) { - srs_error("flash process publish message failed. ret=%d", ret); - return ret; + // for fmle, drop others except the fmle start packet. + if (dynamic_cast(pkt)) { + SrsFMLEStartPacket* unpublish = dynamic_cast(pkt); + if ((ret = rtmp->fmle_unpublish(res->stream_id, unpublish->transaction_id)) != ERROR_SUCCESS) { + return ret; + } + return ERROR_CONTROL_REPUBLISH; } + + srs_trace("fmle ignore AMF0/AMF3 command message."); + return ret; + } + + // video, audio, data message + if ((ret = process_publish_message(source, msg, vhost_is_edge)) != ERROR_SUCCESS) { + srs_error("fmle process publish message failed. ret=%d", ret); + return ret; } return ret; diff --git a/trunk/src/app/srs_app_rtmp_conn.hpp b/trunk/src/app/srs_app_rtmp_conn.hpp index 06dd917078..a10b9ecf65 100644 --- a/trunk/src/app/srs_app_rtmp_conn.hpp +++ b/trunk/src/app/srs_app_rtmp_conn.hpp @@ -50,12 +50,15 @@ class SrsKbps; class SrsRtmpClient; class SrsSharedPtrMessage; class SrsQueueRecvThread; +class SrsPublishRecvThread; /** * the client provides the main logic control for RTMP clients. */ class SrsRtmpConn : public virtual SrsConnection, public virtual ISrsReloadHandler { + // for the thread to directly access any field of connection. + friend class SrsPublishRecvThread; private: SrsRequest* req; SrsResponse* res; @@ -91,9 +94,9 @@ class SrsRtmpConn : public virtual SrsConnection, public virtual ISrsReloadHandl virtual int playing(SrsSource* source); virtual int do_playing(SrsSource* source, SrsQueueRecvThread* trd); virtual int fmle_publishing(SrsSource* source); - virtual int do_fmle_publishing(SrsSource* source); virtual int flash_publishing(SrsSource* source); - virtual int do_flash_publishing(SrsSource* source); + virtual int do_publishing(SrsSource* source, SrsPublishRecvThread* trd); + virtual int handle_publish_message(SrsSource* source, SrsMessage* msg, bool is_fmle, bool vhost_is_edge); virtual int process_publish_message(SrsSource* source, SrsMessage* msg, bool vhost_is_edge); virtual int process_play_control_msg(SrsConsumer* consumer, SrsMessage* msg); private: diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 266a47f9d2..cb506c2233 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 40 +#define VERSION_REVISION 41 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" From af83e18d7e7b0516bebc91e6cf239f14d64e0e5d Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 2 Dec 2014 07:27:20 +0800 Subject: [PATCH 262/800] update the README for 1.5k publishers. 2.0.41 --- README.md | 1 + trunk/src/qt/srs/srs-qt.pro.user | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b087e85375..ffa5e0a2f0 100755 --- a/README.md +++ b/README.md @@ -710,6 +710,7 @@ Performance benchmark history, on virtual box: * 2014-11-12, SRS 2.0.14, 3.5k(3500)clients, 95%CPU, 78MB. * 2014-11-13, SRS 2.0.15, 6.0k(6000)clients, 82%CPU, 203MB. (500 publishers). * 2014-11-22, SRS 2.0.30, 7.5k(7500)clients, 87%CPU, 320MB. +* 2014-12-01, SRS 2.0.41, 7.5k(7500)clients, 87%CPU, 320MB. (1500 publishers). Latest benchmark(2014-07-12): diff --git a/trunk/src/qt/srs/srs-qt.pro.user b/trunk/src/qt/srs/srs-qt.pro.user index cf534d0dd8..fb16e8d123 100644 --- a/trunk/src/qt/srs/srs-qt.pro.user +++ b/trunk/src/qt/srs/srs-qt.pro.user @@ -1,6 +1,6 @@ - + ProjectExplorer.Project.ActiveTarget From 29c6014192bdad814e9db6b4a2fa3b1d1c5edd2d Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 2 Dec 2014 10:40:21 +0800 Subject: [PATCH 263/800] refs #1670: fix the bug for bug #237, use us and ms timeout. --- trunk/src/app/srs_app_rtmp_conn.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index 2282ba049b..80025bf37b 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -500,7 +500,8 @@ int SrsRtmpConn::playing(SrsSource* source) // use isolate thread to recv, // @see: https://github.com/winlinvip/simple-rtmp-server/issues/217 - SrsQueueRecvThread trd(rtmp, SRS_CONSTS_RTMP_PULSE_TIMEOUT_US); + SrsQueueRecvThread trd(rtmp, + SRS_CONSTS_RTMP_PULSE_TIMEOUT_US / 1000); // start isolate recv thread. if ((ret = trd.start()) != ERROR_SUCCESS) { @@ -646,7 +647,9 @@ int SrsRtmpConn::fmle_publishing(SrsSource* source) // use isolate thread to recv, // @see: https://github.com/winlinvip/simple-rtmp-server/issues/237 - SrsPublishRecvThread trd(rtmp, SRS_CONSTS_RTMP_RECV_TIMEOUT_US, this, source, true, vhost_is_edge); + SrsPublishRecvThread trd(rtmp, + SRS_CONSTS_RTMP_RECV_TIMEOUT_US / 1000, + this, source, true, vhost_is_edge); srs_info("start to publish stream %s success", req->stream.c_str()); ret = do_publishing(source, &trd); @@ -680,7 +683,9 @@ int SrsRtmpConn::flash_publishing(SrsSource* source) // use isolate thread to recv, // @see: https://github.com/winlinvip/simple-rtmp-server/issues/237 - SrsPublishRecvThread trd(rtmp, SRS_CONSTS_RTMP_RECV_TIMEOUT_US, this, source, false, vhost_is_edge); + SrsPublishRecvThread trd(rtmp, + SRS_CONSTS_RTMP_RECV_TIMEOUT_US / 1000, + this, source, false, vhost_is_edge); srs_info("start to publish stream %s success", req->stream.c_str()); ret = do_publishing(source, &trd); @@ -735,7 +740,7 @@ int SrsRtmpConn::do_publishing(SrsSource* source, SrsPublishRecvThread* trd) while (true) { // use small loop to check the error code, interval = 30s/100 = 300ms. for (int i = 0; i < 100; i++) { - st_usleep(SRS_CONSTS_RTMP_RECV_TIMEOUT_US * 1000 / 100); + st_usleep(SRS_CONSTS_RTMP_RECV_TIMEOUT_US / 100); // check the thread error code. if ((ret = trd->error_code()) != ERROR_SUCCESS) { From 212a9aa78cfb632f79d1b9744af7ed6e6604c7a7 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 2 Dec 2014 12:18:33 +0800 Subject: [PATCH 264/800] for bug #237, refine syscall for recv, supports 1.5k clients. 2.0.41. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ffa5e0a2f0..25894e65ee 100755 --- a/README.md +++ b/README.md @@ -485,7 +485,8 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History -* v2.0, 2014-12-01, add qtcreate project file trunk/src/qt/srs/srs-qt.pro. 2.0.39. +* v2.0, 2014-12-01, for [#237](https://github.com/winlinvip/simple-rtmp-server/issues/237), refine syscall for recv, supports 1.5k clients. 2.0.41. +* v2.0, 2014-11-30, add qtcreate project file trunk/src/qt/srs/srs-qt.pro. 2.0.39. * v2.0, 2014-11-29, fix [#235](https://github.com/winlinvip/simple-rtmp-server/issues/235), refine handshake, replace union with template method. 2.0.38. * v2.0, 2014-11-28, fix [#215](https://github.com/winlinvip/simple-rtmp-server/issues/215), add srs_rtmp_dump tool. 2.0.37. * v2.0, 2014-11-25, update PRIMARY, AUTHORS, CONTRIBUTORS rule. 2.0.32. From 3584bdb7b66599ef1e2d19189cd0448eadc37027 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 2 Dec 2014 12:19:17 +0800 Subject: [PATCH 265/800] srs-librtmp support hijack io apis for st-load. 2.0.42. --- README.md | 1 + trunk/src/core/srs_core.hpp | 2 +- trunk/src/libs/srs_lib_simple_socket.cpp | 357 +++++++++++++++-------- trunk/src/libs/srs_lib_simple_socket.hpp | 8 +- trunk/src/libs/srs_librtmp.cpp | 2 +- trunk/src/libs/srs_librtmp.hpp | 87 ++++++ 6 files changed, 329 insertions(+), 128 deletions(-) diff --git a/README.md b/README.md index 25894e65ee..1fcdd58cf1 100755 --- a/README.md +++ b/README.md @@ -485,6 +485,7 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History +* v2.0, 2014-12-02, srs-librtmp support hijack io apis for st-load. 2.0.42. * v2.0, 2014-12-01, for [#237](https://github.com/winlinvip/simple-rtmp-server/issues/237), refine syscall for recv, supports 1.5k clients. 2.0.41. * v2.0, 2014-11-30, add qtcreate project file trunk/src/qt/srs/srs-qt.pro. 2.0.39. * v2.0, 2014-11-29, fix [#235](https://github.com/winlinvip/simple-rtmp-server/issues/235), refine handshake, replace union with template method. 2.0.38. diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index cb506c2233..83f4778e57 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 41 +#define VERSION_REVISION 42 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/libs/srs_lib_simple_socket.cpp b/trunk/src/libs/srs_lib_simple_socket.cpp index 08096e7147..1725b1505d 100644 --- a/trunk/src/libs/srs_lib_simple_socket.cpp +++ b/trunk/src/libs/srs_lib_simple_socket.cpp @@ -43,188 +43,305 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define ST_UTIME_NO_TIMEOUT -1 #endif +// when io not hijacked, use simple socket, the block sync stream. +#ifndef SRS_HIJACK_IO + struct SrsBlockSyncSocket + { + SOCKET fd; + int64_t recv_timeout; + int64_t send_timeout; + int64_t recv_bytes; + int64_t send_bytes; + + SrsBlockSyncSocket() { + send_timeout = recv_timeout = ST_UTIME_NO_TIMEOUT; + recv_bytes = send_bytes = 0; + + SOCKET_RESET(fd); + SOCKET_SETUP(); + } + + virtual ~SrsBlockSyncSocket() { + SOCKET_CLOSE(fd); + SOCKET_CLEANUP(); + } + }; + srs_hijack_io_t srs_hijack_io_create() + { + SrsBlockSyncSocket* skt = new SrsBlockSyncSocket(); + return skt; + } + void srs_hijack_io_destroy(srs_hijack_io_t ctx) + { + SrsBlockSyncSocket* skt = (SrsBlockSyncSocket*)ctx; + srs_freep(skt); + } + int srs_hijack_io_create_socket(srs_hijack_io_t ctx) + { + SrsBlockSyncSocket* skt = (SrsBlockSyncSocket*)ctx; + + skt->fd = ::socket(AF_INET, SOCK_STREAM, 0); + if (!SOCKET_VALID(skt->fd)) { + return ERROR_SOCKET_CREATE; + } + + return ERROR_SUCCESS; + } + int srs_hijack_io_connect(srs_hijack_io_t ctx, const char* server_ip, int port) + { + SrsBlockSyncSocket* skt = (SrsBlockSyncSocket*)ctx; + + sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = inet_addr(server_ip); + + if(::connect(skt->fd, (const struct sockaddr*)&addr, sizeof(sockaddr_in)) < 0){ + return ERROR_SOCKET_CONNECT; + } + + return ERROR_SUCCESS; + } + int srs_hijack_io_read(srs_hijack_io_t ctx, void* buf, size_t size, ssize_t* nread) + { + SrsBlockSyncSocket* skt = (SrsBlockSyncSocket*)ctx; + + int ret = ERROR_SUCCESS; + + ssize_t nb_read = ::recv(skt->fd, (char*)buf, size, 0); + + if (nread) { + *nread = nb_read; + } + + // On success a non-negative integer indicating the number of bytes actually read is returned + // (a value of 0 means the network connection is closed or end of file is reached). + if (nb_read <= 0) { + if (nb_read < 0 && SOCKET_ERRNO() == SOCKET_ETIME) { + return ERROR_SOCKET_TIMEOUT; + } + + if (nb_read == 0) { + errno = SOCKET_ECONNRESET; + } + + return ERROR_SOCKET_READ; + } + + skt->recv_bytes += nb_read; + + return ret; + } + void srs_hijack_io_set_recv_timeout(srs_hijack_io_t ctx, int64_t timeout_us) + { + SrsBlockSyncSocket* skt = (SrsBlockSyncSocket*)ctx; + skt->recv_timeout = timeout_us; + } + int64_t srs_hijack_io_get_recv_timeout(srs_hijack_io_t ctx) + { + SrsBlockSyncSocket* skt = (SrsBlockSyncSocket*)ctx; + return skt->recv_timeout; + } + int64_t srs_hijack_io_get_recv_bytes(srs_hijack_io_t ctx) + { + SrsBlockSyncSocket* skt = (SrsBlockSyncSocket*)ctx; + return skt->recv_bytes; + } + void srs_hijack_io_set_send_timeout(srs_hijack_io_t ctx, int64_t timeout_us) + { + SrsBlockSyncSocket* skt = (SrsBlockSyncSocket*)ctx; + skt->send_timeout = timeout_us; + } + int64_t srs_hijack_io_get_send_timeout(srs_hijack_io_t ctx) + { + SrsBlockSyncSocket* skt = (SrsBlockSyncSocket*)ctx; + return skt->send_timeout; + } + int64_t srs_hijack_io_get_send_bytes(srs_hijack_io_t ctx) + { + SrsBlockSyncSocket* skt = (SrsBlockSyncSocket*)ctx; + return skt->send_bytes; + } + int srs_hijack_io_writev(srs_hijack_io_t ctx, const iovec *iov, int iov_size, ssize_t* nwrite) + { + SrsBlockSyncSocket* skt = (SrsBlockSyncSocket*)ctx; + + int ret = ERROR_SUCCESS; + + ssize_t nb_write = ::writev(skt->fd, iov, iov_size); + + if (nwrite) { + *nwrite = nb_write; + } + + // On success, the readv() function returns the number of bytes read; + // the writev() function returns the number of bytes written. On error, -1 is + // returned, and errno is set appropriately. + if (nb_write <= 0) { + // @see https://github.com/winlinvip/simple-rtmp-server/issues/200 + if (nb_write < 0 && SOCKET_ERRNO() == SOCKET_ETIME) { + return ERROR_SOCKET_TIMEOUT; + } + + return ERROR_SOCKET_WRITE; + } + + skt->send_bytes += nb_write; + + return ret; + } + bool srs_hijack_io_is_never_timeout(srs_hijack_io_t ctx, int64_t timeout_us) + { + return timeout_us == (int64_t)ST_UTIME_NO_TIMEOUT; + } + int srs_hijack_io_read_fully(srs_hijack_io_t ctx, void* buf, size_t size, ssize_t* nread) + { + SrsBlockSyncSocket* skt = (SrsBlockSyncSocket*)ctx; + + int ret = ERROR_SUCCESS; + + size_t left = size; + ssize_t nb_read = 0; + + while (left > 0) { + char* this_buf = (char*)buf + nb_read; + ssize_t this_nread; + + if ((ret = srs_hijack_io_read(ctx, this_buf, left, &this_nread)) != ERROR_SUCCESS) { + return ret; + } + + nb_read += this_nread; + left -= (size_t)this_nread; + } + + if (nread) { + *nread = nb_read; + } + skt->recv_bytes += nb_read; + + return ret; + } + int srs_hijack_io_write(srs_hijack_io_t ctx, void* buf, size_t size, ssize_t* nwrite) + { + SrsBlockSyncSocket* skt = (SrsBlockSyncSocket*)ctx; + + int ret = ERROR_SUCCESS; + + ssize_t nb_write = ::send(skt->fd, (char*)buf, size, 0); + + if (nwrite) { + *nwrite = nb_write; + } + + if (nb_write <= 0) { + // @see https://github.com/winlinvip/simple-rtmp-server/issues/200 + if (nb_write < 0 && SOCKET_ERRNO() == SOCKET_ETIME) { + return ERROR_SOCKET_TIMEOUT; + } + + return ERROR_SOCKET_WRITE; + } + + skt->send_bytes += nb_write; + + return ret; + } +#endif + SimpleSocketStream::SimpleSocketStream() { - SOCKET_RESET(fd); - send_timeout = recv_timeout = ST_UTIME_NO_TIMEOUT; - recv_bytes = send_bytes = 0; - SOCKET_SETUP(); + io = srs_hijack_io_create(); } SimpleSocketStream::~SimpleSocketStream() { - SOCKET_CLOSE(fd); - SOCKET_CLEANUP(); + if (io) { + srs_hijack_io_destroy(io); + io = NULL; + } } int SimpleSocketStream::create_socket() { - fd = ::socket(AF_INET, SOCK_STREAM, 0); - if (!SOCKET_VALID(fd)) { - return ERROR_SOCKET_CREATE; - } - - return ERROR_SUCCESS; + srs_assert(io); + return srs_hijack_io_create_socket(io); } int SimpleSocketStream::connect(const char* server_ip, int port) { - sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = inet_addr(server_ip); - - if(::connect(fd, (const struct sockaddr*)&addr, sizeof(sockaddr_in)) < 0){ - return ERROR_SOCKET_CONNECT; - } - - return ERROR_SUCCESS; + srs_assert(io); + return srs_hijack_io_connect(io, server_ip, port); } // ISrsBufferReader int SimpleSocketStream::read(void* buf, size_t size, ssize_t* nread) { - int ret = ERROR_SUCCESS; - - ssize_t nb_read = ::recv(fd, (char*)buf, size, 0); - - if (nread) { - *nread = nb_read; - } - - // On success a non-negative integer indicating the number of bytes actually read is returned - // (a value of 0 means the network connection is closed or end of file is reached). - if (nb_read <= 0) { - if (nb_read < 0 && SOCKET_ERRNO() == SOCKET_ETIME) { - return ERROR_SOCKET_TIMEOUT; - } - - if (nb_read == 0) { - errno = SOCKET_ECONNRESET; - } - - return ERROR_SOCKET_READ; - } - - recv_bytes += nb_read; - - return ret; + srs_assert(io); + return srs_hijack_io_read(io, buf, size, nread); } // ISrsProtocolReader void SimpleSocketStream::set_recv_timeout(int64_t timeout_us) { - recv_timeout = timeout_us; + srs_assert(io); + srs_hijack_io_set_recv_timeout(io, timeout_us); } int64_t SimpleSocketStream::get_recv_timeout() { - return recv_timeout; + srs_assert(io); + return srs_hijack_io_get_recv_timeout(io); } int64_t SimpleSocketStream::get_recv_bytes() { - return recv_bytes; + srs_assert(io); + return srs_hijack_io_get_recv_bytes(io); } // ISrsProtocolWriter void SimpleSocketStream::set_send_timeout(int64_t timeout_us) { - send_timeout = timeout_us; + srs_assert(io); + srs_hijack_io_set_send_timeout(io, timeout_us); } int64_t SimpleSocketStream::get_send_timeout() { - return send_timeout; + srs_assert(io); + return srs_hijack_io_get_send_timeout(io); } int64_t SimpleSocketStream::get_send_bytes() { - return send_bytes; + srs_assert(io); + return srs_hijack_io_get_send_bytes(io); } int SimpleSocketStream::writev(const iovec *iov, int iov_size, ssize_t* nwrite) { - int ret = ERROR_SUCCESS; - - ssize_t nb_write = ::writev(fd, iov, iov_size); - - if (nwrite) { - *nwrite = nb_write; - } - - // On success, the readv() function returns the number of bytes read; - // the writev() function returns the number of bytes written. On error, -1 is - // returned, and errno is set appropriately. - if (nb_write <= 0) { - // @see https://github.com/winlinvip/simple-rtmp-server/issues/200 - if (nb_write < 0 && SOCKET_ERRNO() == SOCKET_ETIME) { - return ERROR_SOCKET_TIMEOUT; - } - - return ERROR_SOCKET_WRITE; - } - - send_bytes += nb_write; - - return ret; + srs_assert(io); + return srs_hijack_io_writev(io, iov, iov_size, nwrite); } // ISrsProtocolReaderWriter bool SimpleSocketStream::is_never_timeout(int64_t timeout_us) { - return timeout_us == (int64_t)ST_UTIME_NO_TIMEOUT; + srs_assert(io); + return srs_hijack_io_is_never_timeout(io, timeout_us); } int SimpleSocketStream::read_fully(void* buf, size_t size, ssize_t* nread) { - int ret = ERROR_SUCCESS; - - size_t left = size; - ssize_t nb_read = 0; - - while (left > 0) { - char* this_buf = (char*)buf + nb_read; - ssize_t this_nread; - - if ((ret = this->read(this_buf, left, &this_nread)) != ERROR_SUCCESS) { - return ret; - } - - nb_read += this_nread; - left -= (size_t)this_nread; - } - - if (nread) { - *nread = nb_read; - } - recv_bytes += nb_read; - - return ret; + srs_assert(io); + return srs_hijack_io_read_fully(io, buf, size, nread); } int SimpleSocketStream::write(void* buf, size_t size, ssize_t* nwrite) { - int ret = ERROR_SUCCESS; - - ssize_t nb_write = ::send(fd, (char*)buf, size, 0); - - if (nwrite) { - *nwrite = nb_write; - } - - if (nb_write <= 0) { - // @see https://github.com/winlinvip/simple-rtmp-server/issues/200 - if (nb_write < 0 && SOCKET_ERRNO() == SOCKET_ETIME) { - return ERROR_SOCKET_TIMEOUT; - } - - return ERROR_SOCKET_WRITE; - } - - send_bytes += nb_write; - - return ret; + srs_assert(io); + return srs_hijack_io_write(io, buf, size, nwrite); } diff --git a/trunk/src/libs/srs_lib_simple_socket.hpp b/trunk/src/libs/srs_lib_simple_socket.hpp index dca0c45b2b..438a27d324 100644 --- a/trunk/src/libs/srs_lib_simple_socket.hpp +++ b/trunk/src/libs/srs_lib_simple_socket.hpp @@ -32,7 +32,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include - + /** * simple socket stream, * use tcp socket, sync block mode, for client like srs-librtmp. @@ -40,11 +40,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. class SimpleSocketStream : public ISrsProtocolReaderWriter { private: - int64_t recv_timeout; - int64_t send_timeout; - int64_t recv_bytes; - int64_t send_bytes; - SOCKET fd; + srs_hijack_io_t io; public: SimpleSocketStream(); virtual ~SimpleSocketStream(); diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index 04d307d9d7..d552814f00 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -49,7 +49,7 @@ using namespace std; #include // if want to use your log, define the folowing macro. -#ifndef SRS_RTMP_USER_DEFINED_LOG +#ifndef SRS_HIJACK_LOG // kernel module. ISrsLog* _srs_log = new ISrsLog(); ISrsThreadContext* _srs_context = new ISrsThreadContext(); diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index 8cb20b5a1d..8b90d816f2 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -930,6 +930,93 @@ extern const char* srs_human_format_time(); #define srs_human_verbose(msg, ...) printf("[%s] ", srs_human_format_time());printf(msg, ##__VA_ARGS__);printf("\n") #define srs_human_raw(msg, ...) printf(msg, ##__VA_ARGS__) +/************************************************************* +************************************************************** +* IO hijack, use your specified io functions. +************************************************************** +*************************************************************/ +// the void* will convert to your handler for io hijack. +typedef void* srs_hijack_io_t; +// define the following macro and functions in your module to hijack the io. +// the example @see https://github.com/winlinvip/st-load +// which use librtmp but use its own io(use st also). +#ifdef SRS_HIJACK_IO + /** + * create hijack. + * @return NULL for error; otherwise, ok. + */ + extern srs_hijack_io_t srs_hijack_io_create(); + /** + * destroy the context, user must close the socket. + */ + extern void srs_hijack_io_destroy(srs_hijack_io_t ctx); + /** + * create socket, not connect yet. + * @return 0, success; otherswise, failed. + */ + extern int srs_hijack_io_create_socket(srs_hijack_io_t ctx); + /** + * connect socket at server_ip:port. + * @return 0, success; otherswise, failed. + */ + extern int srs_hijack_io_connect(srs_hijack_io_t ctx, const char* server_ip, int port); + /** + * read from socket. + * @return 0, success; otherswise, failed. + */ + extern int srs_hijack_io_read(srs_hijack_io_t ctx, void* buf, size_t size, ssize_t* nread); + /** + * set the socket recv timeout. + * @return 0, success; otherswise, failed. + */ + extern void srs_hijack_io_set_recv_timeout(srs_hijack_io_t ctx, int64_t timeout_us); + /** + * get the socket recv timeout. + * @return 0, success; otherswise, failed. + */ + extern int64_t srs_hijack_io_get_recv_timeout(srs_hijack_io_t ctx); + /** + * get the socket recv bytes. + * @return 0, success; otherswise, failed. + */ + extern int64_t srs_hijack_io_get_recv_bytes(srs_hijack_io_t ctx); + /** + * set the socket send timeout. + * @return 0, success; otherswise, failed. + */ + extern void srs_hijack_io_set_send_timeout(srs_hijack_io_t ctx, int64_t timeout_us); + /** + * get the socket send timeout. + * @return 0, success; otherswise, failed. + */ + extern int64_t srs_hijack_io_get_send_timeout(srs_hijack_io_t ctx); + /** + * get the socket send bytes. + * @return 0, success; otherswise, failed. + */ + extern int64_t srs_hijack_io_get_send_bytes(srs_hijack_io_t ctx); + /** + * writev of socket. + * @return 0, success; otherswise, failed. + */ + extern int srs_hijack_io_writev(srs_hijack_io_t ctx, const iovec *iov, int iov_size, ssize_t* nwrite); + /** + * whether the timeout is never timeout. + * @return 0, success; otherswise, failed. + */ + extern bool srs_hijack_io_is_never_timeout(srs_hijack_io_t ctx, int64_t timeout_us); + /** + * read fully, fill the buf exactly size bytes. + * @return 0, success; otherswise, failed. + */ + extern int srs_hijack_io_read_fully(srs_hijack_io_t ctx, void* buf, size_t size, ssize_t* nread); + /** + * write bytes to socket. + * @return 0, success; otherswise, failed. + */ + extern int srs_hijack_io_write(srs_hijack_io_t ctx, void* buf, size_t size, ssize_t* nwrite); +#endif + #ifdef __cplusplus } #endif From f6032ffe6b01384b4c88481611f55e059e8da8b3 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 2 Dec 2014 12:51:07 +0800 Subject: [PATCH 266/800] always use system log and context. allow hijack the human print macro. --- trunk/src/libs/srs_librtmp.cpp | 9 +++------ trunk/src/libs/srs_librtmp.hpp | 10 +++++++--- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index d552814f00..2877925b08 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -48,12 +48,9 @@ using namespace std; #include #include -// if want to use your log, define the folowing macro. -#ifndef SRS_HIJACK_LOG - // kernel module. - ISrsLog* _srs_log = new ISrsLog(); - ISrsThreadContext* _srs_context = new ISrsThreadContext(); -#endif +// kernel module. +ISrsLog* _srs_log = new ISrsLog(); +ISrsThreadContext* _srs_context = new ISrsThreadContext(); /** * export runtime context. diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index 8b90d816f2..7a8f3d41d4 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -926,9 +926,13 @@ extern int srs_human_print_rtmp_packet(char type, u_int32_t timestamp, char* dat // log to console, for use srs-librtmp application. extern const char* srs_human_format_time(); -#define srs_human_trace(msg, ...) printf("[%s] ", srs_human_format_time());printf(msg, ##__VA_ARGS__);printf("\n") -#define srs_human_verbose(msg, ...) printf("[%s] ", srs_human_format_time());printf(msg, ##__VA_ARGS__);printf("\n") -#define srs_human_raw(msg, ...) printf(msg, ##__VA_ARGS__) + +// when hijack the log, user will defines this macros. +#ifndef SRS_HIJACK_LOG + #define srs_human_trace(msg, ...) printf("[%s] ", srs_human_format_time());printf(msg, ##__VA_ARGS__);printf("\n") + #define srs_human_verbose(msg, ...) printf("[%s] ", srs_human_format_time());printf(msg, ##__VA_ARGS__);printf("\n") + #define srs_human_raw(msg, ...) printf(msg, ##__VA_ARGS__) +#endif /************************************************************* ************************************************************** From 07d55010fecb74bf1eca03a4eaa9380fecce4f88 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 2 Dec 2014 13:03:25 +0800 Subject: [PATCH 267/800] remove the free bytes api of srs-librtmp. use system default is ok. --- trunk/src/libs/srs_librtmp.cpp | 7 +------ trunk/src/libs/srs_librtmp.hpp | 1 - 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index 2877925b08..015f91bd17 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -1743,11 +1743,6 @@ void srs_amf0_free(srs_amf0_t amf0) srs_freep(any); } -void srs_amf0_free_bytes(char* data) -{ - srs_freep(data); -} - int srs_amf0_size(srs_amf0_t amf0) { SrsAmf0Any* any = (SrsAmf0Any*)amf0; @@ -2389,7 +2384,7 @@ int srs_human_print_rtmp_packet(char type, u_int32_t timestamp, char* data, int char* amf0_str = NULL; srs_human_raw("%s", srs_human_amf0_print(amf0, &amf0_str, NULL)); - srs_amf0_free_bytes(amf0_str); + srs_freep(amf0_str); } } else { srs_human_trace("Unknown packet type=%s, dts=%d, pts=%d, size=%d", diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index 7a8f3d41d4..272c2e85d9 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -608,7 +608,6 @@ extern srs_amf0_t srs_amf0_create_ecma_array(); extern srs_amf0_t srs_amf0_create_strict_array(); extern srs_amf0_t srs_amf0_create_object(); extern void srs_amf0_free(srs_amf0_t amf0); -extern void srs_amf0_free_bytes(char* data); /* size and to bytes */ extern int srs_amf0_size(srs_amf0_t amf0); extern int srs_amf0_serialize(srs_amf0_t amf0, char* data, int size); From 15ae4745f47857e78de44e4704c750fc3d3499b2 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 2 Dec 2014 13:21:26 +0800 Subject: [PATCH 268/800] add macro to disable log. --- trunk/src/libs/srs_librtmp.hpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index 272c2e85d9..8e5e75250c 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -926,8 +926,12 @@ extern int srs_human_print_rtmp_packet(char type, u_int32_t timestamp, char* dat // log to console, for use srs-librtmp application. extern const char* srs_human_format_time(); -// when hijack the log, user will defines this macros. -#ifndef SRS_HIJACK_LOG +// when disabled log, donot compile it. +#ifdef SRS_DISABLE_LOG + #define srs_human_trace(msg, ...) (void)0 + #define srs_human_verbose(msg, ...) (void)0 + #define srs_human_raw(msg, ...) (void)0 +#else #define srs_human_trace(msg, ...) printf("[%s] ", srs_human_format_time());printf(msg, ##__VA_ARGS__);printf("\n") #define srs_human_verbose(msg, ...) printf("[%s] ", srs_human_format_time());printf(msg, ##__VA_ARGS__);printf("\n") #define srs_human_raw(msg, ...) printf(msg, ##__VA_ARGS__) @@ -944,6 +948,10 @@ typedef void* srs_hijack_io_t; // the example @see https://github.com/winlinvip/st-load // which use librtmp but use its own io(use st also). #ifdef SRS_HIJACK_IO + #ifndef _WIN32 + // for iovec. + #include + #endif /** * create hijack. * @return NULL for error; otherwise, ok. From 9387d09f5ff7ea96efc7a6cd8fe7e740cbd12f55 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 2 Dec 2014 13:38:30 +0800 Subject: [PATCH 269/800] refine the macros for windows of srs-librtmp --- trunk/src/libs/srs_lib_simple_socket.cpp | 28 ++++++ trunk/src/libs/srs_lib_simple_socket.hpp | 5 ++ trunk/src/libs/srs_librtmp.hpp | 105 ++++++++++------------- 3 files changed, 78 insertions(+), 60 deletions(-) diff --git a/trunk/src/libs/srs_lib_simple_socket.cpp b/trunk/src/libs/srs_lib_simple_socket.cpp index 1725b1505d..70f97ebc78 100644 --- a/trunk/src/libs/srs_lib_simple_socket.cpp +++ b/trunk/src/libs/srs_lib_simple_socket.cpp @@ -25,6 +25,34 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include +// for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 +#ifndef _WIN32 + #define SOCKET_ETIME ETIME + #define SOCKET_ECONNRESET ECONNRESET + + #define SOCKET_ERRNO() errno + #define SOCKET_RESET(fd) fd = -1; (void)0 + #define SOCKET_CLOSE(fd) \ + if (fd > 0) {\ + ::close(fd); \ + fd = -1; \ + } \ + (void)0 + #define SOCKET_VALID(x) (x > 0) + #define SOCKET_SETUP() (void)0 + #define SOCKET_CLEANUP() (void)0 +#else + #define SOCKET_ETIME WSAETIMEDOUT + #define SOCKET_ECONNRESET WSAECONNRESET + #define SOCKET_ERRNO() WSAGetLastError() + #define SOCKET_RESET(x) x=INVALID_SOCKET + #define SOCKET_CLOSE(x) if(x!=INVALID_SOCKET){::closesocket(x);x=INVALID_SOCKET;} + #define SOCKET_VALID(x) (x!=INVALID_SOCKET) + #define SOCKET_BUFF(x) ((char*)x) + #define SOCKET_SETUP() socket_setup() + #define SOCKET_CLEANUP() socket_cleanup() +#endif + // for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 #ifndef _WIN32 #include diff --git a/trunk/src/libs/srs_lib_simple_socket.hpp b/trunk/src/libs/srs_lib_simple_socket.hpp index 438a27d324..a2f175788b 100644 --- a/trunk/src/libs/srs_lib_simple_socket.hpp +++ b/trunk/src/libs/srs_lib_simple_socket.hpp @@ -33,6 +33,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +// for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 +#ifndef _WIN32 + #define SOCKET int +#endif + /** * simple socket stream, * use tcp socket, sync block mode, for client like srs-librtmp. diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index 8e5e75250c..4c59a53805 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -28,27 +28,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include */ -#include - // for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 -#ifndef _WIN32 - #define SOCKET_ETIME ETIME - #define SOCKET_ECONNRESET ECONNRESET - - #define SOCKET int - #define SOCKET_ERRNO() errno - #define SOCKET_RESET(fd) fd = -1; (void)0 - #define SOCKET_CLOSE(fd) \ - if (fd > 0) {\ - ::close(fd); \ - fd = -1; \ - } \ - (void)0 - #define SOCKET_VALID(x) (x > 0) - #define SOCKET_SETUP() (void)0 - #define SOCKET_CLEANUP() (void)0 -#else - #define _CRT_SECURE_NO_WARNINGS +#ifdef _WIN32 + // include windows first. + #include + // the type used by this header for windows. typedef unsigned long long u_int64_t; typedef long long int64_t; typedef unsigned int u_int32_t; @@ -62,48 +46,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. void *iov_base; /* Starting address */ size_t iov_len; /* Number of bytes to transfer */ }; - #include - #include - int gettimeofday(struct timeval* tv, struct timezone* tz); - #define PRId64 "lld" - - #define SOCKET_ETIME WSAETIMEDOUT - #define SOCKET_ECONNRESET WSAECONNRESET - #define SOCKET_ERRNO() WSAGetLastError() - #define SOCKET_RESET(x) x=INVALID_SOCKET - #define SOCKET_CLOSE(x) if(x!=INVALID_SOCKET){::closesocket(x);x=INVALID_SOCKET;} - #define SOCKET_VALID(x) (x!=INVALID_SOCKET) - #define SOCKET_BUFF(x) ((char*)x) - #define SOCKET_SETUP() socket_setup() - #define SOCKET_CLEANUP() socket_cleanup() - - typedef int socklen_t; - const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); - typedef int mode_t; - #define S_IRUSR 0 - #define S_IWUSR 0 - #define S_IRGRP 0 - #define S_IWGRP 0 - #define S_IROTH 0 - - #include - #include - #define open _open - #define close _close - #define lseek _lseek - #define write _write - #define read _read - - typedef int pid_t; - pid_t getpid(void); - #define snprintf _snprintf - ssize_t writev(int fd, const struct iovec *iov, int iovcnt); - typedef int64_t useconds_t; - int usleep(useconds_t usec); - int socket_setup(); - int socket_cleanup(); #endif +#include + /** * srs-librtmp is a librtmp like library, * used to play/publish rtmp stream from/to rtmp server. @@ -1028,6 +974,45 @@ typedef void* srs_hijack_io_t; extern int srs_hijack_io_write(srs_hijack_io_t ctx, void* buf, size_t size, ssize_t* nwrite); #endif +/************************************************************* +************************************************************** +* Windows SRS-LIBRTMP solution +************************************************************** +*************************************************************/ +// for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 +#ifdef _WIN32 + #define _CRT_SECURE_NO_WARNINGS + #include + int gettimeofday(struct timeval* tv, struct timezone* tz); + #define PRId64 "lld" + + typedef int socklen_t; + const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); + typedef int mode_t; + #define S_IRUSR 0 + #define S_IWUSR 0 + #define S_IRGRP 0 + #define S_IWGRP 0 + #define S_IROTH 0 + + #include + #include + #define open _open + #define close _close + #define lseek _lseek + #define write _write + #define read _read + + typedef int pid_t; + pid_t getpid(void); + #define snprintf _snprintf + ssize_t writev(int fd, const struct iovec *iov, int iovcnt); + typedef int64_t useconds_t; + int usleep(useconds_t usec); + int socket_setup(); + int socket_cleanup(); +#endif + #ifdef __cplusplus } #endif From a94dbecdc409ad4a7b9f2c1cf2ff433316fe83d4 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 2 Dec 2014 13:41:41 +0800 Subject: [PATCH 270/800] refine macro of srs-librtmp. 2.0.43 --- trunk/src/core/srs_core.hpp | 2 +- trunk/src/libs/srs_lib_simple_socket.cpp | 10 +++++----- trunk/src/libs/srs_librtmp.hpp | 21 +++++++++++++-------- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 83f4778e57..f3f9a95af0 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 42 +#define VERSION_REVISION 43 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/libs/srs_lib_simple_socket.cpp b/trunk/src/libs/srs_lib_simple_socket.cpp index 70f97ebc78..75c7cf39cb 100644 --- a/trunk/src/libs/srs_lib_simple_socket.cpp +++ b/trunk/src/libs/srs_lib_simple_socket.cpp @@ -55,11 +55,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 #ifndef _WIN32 -#include -#include -#include -#include -#include + #include + #include + #include + #include + #include #endif #include diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index 4c59a53805..7e4d3806dc 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -28,6 +28,19 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include */ +/** +* srs-librtmp is a librtmp like library, +* used to play/publish rtmp stream from/to rtmp server. +* socket: use sync and block socket to connect/recv/send data with server. +* depends: no need other libraries; depends on ssl if use srs_complex_handshake. +* thread-safe: no +*/ + +/************************************************************* +************************************************************** +* Windows SRS-LIBRTMP pre-declare +************************************************************** +*************************************************************/ // for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 #ifdef _WIN32 // include windows first. @@ -50,14 +63,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include -/** -* srs-librtmp is a librtmp like library, -* used to play/publish rtmp stream from/to rtmp server. -* socket: use sync and block socket to connect/recv/send data with server. -* depends: no need other libraries; depends on ssl if use srs_complex_handshake. -* thread-safe: no -*/ - #ifdef __cplusplus extern "C"{ #endif From b28dc7364e850896a6ffd10fccdde7c0ea6623c5 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 2 Dec 2014 14:02:28 +0800 Subject: [PATCH 271/800] refine the srs-librtmp, add hijack get the object. --- trunk/src/libs/srs_lib_simple_socket.cpp | 5 +++++ trunk/src/libs/srs_lib_simple_socket.hpp | 1 + trunk/src/libs/srs_librtmp.cpp | 22 +++++++++++++++++++++- trunk/src/libs/srs_librtmp.hpp | 15 +++++++++++---- 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/trunk/src/libs/srs_lib_simple_socket.cpp b/trunk/src/libs/srs_lib_simple_socket.cpp index 75c7cf39cb..6a5b8cb72c 100644 --- a/trunk/src/libs/srs_lib_simple_socket.cpp +++ b/trunk/src/libs/srs_lib_simple_socket.cpp @@ -290,6 +290,11 @@ SimpleSocketStream::~SimpleSocketStream() } } +srs_hijack_io_t SimpleSocketStream::hijack_io() +{ + return io; +} + int SimpleSocketStream::create_socket() { srs_assert(io); diff --git a/trunk/src/libs/srs_lib_simple_socket.hpp b/trunk/src/libs/srs_lib_simple_socket.hpp index a2f175788b..2a76be19d5 100644 --- a/trunk/src/libs/srs_lib_simple_socket.hpp +++ b/trunk/src/libs/srs_lib_simple_socket.hpp @@ -50,6 +50,7 @@ class SimpleSocketStream : public ISrsProtocolReaderWriter SimpleSocketStream(); virtual ~SimpleSocketStream(); public: + virtual srs_hijack_io_t hijack_io(); virtual int create_socket(); virtual int connect(const char* server, int port); // ISrsBufferReader diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index 015f91bd17..17fdb269c8 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -534,7 +534,10 @@ srs_rtmp_t srs_rtmp_create2(const char* url) void srs_rtmp_destroy(srs_rtmp_t rtmp) { - srs_assert(rtmp != NULL); + if (!rtmp) { + return; + } + Context* context = (Context*)rtmp; srs_freep(context); @@ -2424,6 +2427,23 @@ const char* srs_human_format_time() return buf; } + +#ifdef SRS_HIJACK_IO +srs_hijack_io_t srs_hijack_io_get(srs_rtmp_t rtmp) +{ + if (!rtmp) { + return NULL; + } + + Context* context = (Context*)rtmp; + if (!context->skt) { + return NULL; + } + + return context->skt->hijack_io(); +} +#endif + #ifdef __cplusplus } #endif diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index 7e4d3806dc..ca9c018720 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -107,7 +107,7 @@ extern srs_rtmp_t srs_rtmp_create(const char* url); extern srs_rtmp_t srs_rtmp_create2(const char* url); /** * close and destroy the rtmp stack. -* @remark, user should use the rtmp again. +* @remark, user should never use the rtmp again. */ extern void srs_rtmp_destroy(srs_rtmp_t rtmp); @@ -895,14 +895,21 @@ extern const char* srs_human_format_time(); *************************************************************/ // the void* will convert to your handler for io hijack. typedef void* srs_hijack_io_t; -// define the following macro and functions in your module to hijack the io. -// the example @see https://github.com/winlinvip/st-load -// which use librtmp but use its own io(use st also). #ifdef SRS_HIJACK_IO #ifndef _WIN32 // for iovec. #include #endif + /** + * get the hijack io object in rtmp protocol sdk. + * @remark, user should never provides this method, srs-librtmp provides it. + */ + extern srs_hijack_io_t srs_hijack_io_get(srs_rtmp_t rtmp); +#endif +// define the following macro and functions in your module to hijack the io. +// the example @see https://github.com/winlinvip/st-load +// which use librtmp but use its own io(use st also). +#ifdef SRS_HIJACK_IO /** * create hijack. * @return NULL for error; otherwise, ok. From 757cffbabf44338b18e1e8231d8f05458e12b003 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 2 Dec 2014 15:21:08 +0800 Subject: [PATCH 272/800] for bug #237, when recv thread failed, quit the cycle. 2.0.44 --- trunk/src/app/srs_app_recv_thread.cpp | 19 +++++++++++++++++++ trunk/src/app/srs_app_recv_thread.hpp | 23 ++++++++++++++++------- trunk/src/app/srs_app_rtmp_conn.cpp | 13 ++++++++++++- trunk/src/core/srs_core.hpp | 2 +- 4 files changed, 48 insertions(+), 9 deletions(-) diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index c59be261f3..008ffe964b 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -80,6 +80,9 @@ int SrsRecvThread::cycle() // we use no timeout to recv, should never got any error. trd->stop_loop(); + + // notice the handler got a recv error. + handler->on_recv_error(ret); return ret; } @@ -122,6 +125,7 @@ void SrsRecvThread::on_thread_stop() SrsQueueRecvThread::SrsQueueRecvThread(SrsRtmpServer* rtmp_sdk, int timeout_ms) : trd(this, rtmp_sdk, timeout_ms) { + recv_error_code = ERROR_SUCCESS; } SrsQueueRecvThread::~SrsQueueRecvThread() @@ -168,6 +172,11 @@ SrsMessage* SrsQueueRecvThread::pump() return msg; } +int SrsQueueRecvThread::error_code() +{ + return recv_error_code; +} + bool SrsQueueRecvThread::can_handle() { // we only recv one message and then process it, @@ -186,6 +195,11 @@ int SrsQueueRecvThread::handle(SrsMessage* msg) return ERROR_SUCCESS; } +void SrsQueueRecvThread::on_recv_error(int ret) +{ + recv_error_code = ret; +} + SrsPublishRecvThread::SrsPublishRecvThread( SrsRtmpServer* rtmp_sdk, int timeout_ms, SrsRtmpConn* conn, SrsSource* source, bool is_fmle, bool is_edge @@ -254,3 +268,8 @@ int SrsPublishRecvThread::handle(SrsMessage* msg) // TODO: FIXME: implements it. return ret; } + +void SrsPublishRecvThread::on_recv_error(int ret) +{ + recv_error_code = ret; +} diff --git a/trunk/src/app/srs_app_recv_thread.hpp b/trunk/src/app/srs_app_recv_thread.hpp index b52b120a6a..df9ac8ef36 100644 --- a/trunk/src/app/srs_app_recv_thread.hpp +++ b/trunk/src/app/srs_app_recv_thread.hpp @@ -49,16 +49,20 @@ class ISrsMessageHandler virtual ~ISrsMessageHandler(); public: /** - * whether the handler can handle, - * for example, when queue recv handler got an message, - * it wait the user to process it, then the recv thread - * never recv message util the handler is ok. - */ + * whether the handler can handle, + * for example, when queue recv handler got an message, + * it wait the user to process it, then the recv thread + * never recv message util the handler is ok. + */ virtual bool can_handle() = 0; /** - * process the received message. - */ + * process the received message. + */ virtual int handle(SrsMessage* msg) = 0; + /** + * when recv message error. + */ + virtual void on_recv_error(int ret) = 0; }; /** @@ -95,6 +99,8 @@ class SrsQueueRecvThread : public ISrsMessageHandler private: std::vector queue; SrsRecvThread trd; + // the recv thread error code. + int recv_error_code; public: SrsQueueRecvThread(SrsRtmpServer* rtmp_sdk, int timeout_ms); virtual ~SrsQueueRecvThread(); @@ -105,9 +111,11 @@ class SrsQueueRecvThread : public ISrsMessageHandler virtual bool empty(); virtual int size(); virtual SrsMessage* pump(); + virtual int error_code(); public: virtual bool can_handle(); virtual int handle(SrsMessage* msg); + virtual void on_recv_error(int ret); }; /** @@ -139,6 +147,7 @@ class SrsPublishRecvThread : public ISrsMessageHandler public: virtual bool can_handle(); virtual int handle(SrsMessage* msg); + virtual void on_recv_error(int ret); }; #endif diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index 80025bf37b..83b2105dc8 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -558,13 +558,21 @@ int SrsRtmpConn::do_playing(SrsSource* source, SrsQueueRecvThread* trd) srs_verbose("pump client message to process."); if ((ret = process_play_control_msg(consumer, msg)) != ERROR_SUCCESS) { - if (!srs_is_system_control_error(ret)) { + if (!srs_is_system_control_error(ret) && !srs_is_client_gracefully_close(ret)) { srs_error("process play control message failed. ret=%d", ret); } return ret; } } + // quit when recv thread error. + if ((ret = trd->error_code()) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("recv thread failed. ret=%d", ret); + } + return ret; + } + // collect elapse for pithy print. pithy_print.elapse(); @@ -744,6 +752,9 @@ int SrsRtmpConn::do_publishing(SrsSource* source, SrsPublishRecvThread* trd) // check the thread error code. if ((ret = trd->error_code()) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("recv thread failed. ret=%d", ret); + } return ret; } } diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index f3f9a95af0..1408dd9b32 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 43 +#define VERSION_REVISION 44 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" From e9712cc627bb07f88a20b1c3d04fa6098fa47b63 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 2 Dec 2014 17:16:20 +0800 Subject: [PATCH 273/800] refine recv thread, donot set auto response for publish recv thread. 2.0.46 --- trunk/src/app/srs_app_recv_thread.cpp | 40 +++++++++++++++++++++------ trunk/src/app/srs_app_recv_thread.hpp | 14 ++++++++++ trunk/src/core/srs_core.hpp | 2 +- 3 files changed, 47 insertions(+), 9 deletions(-) diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index 008ffe964b..5a6a498074 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -106,25 +106,22 @@ void SrsRecvThread::on_thread_start() // @see https://github.com/winlinvip/simple-rtmp-server/issues/194 // @see: https://github.com/winlinvip/simple-rtmp-server/issues/217 rtmp->set_recv_timeout(ST_UTIME_NO_TIMEOUT); - - // disable the protocol auto response, - // for the isolate recv thread should never send any messages. - rtmp->set_auto_response(false); + + handler->on_thread_start(); } void SrsRecvThread::on_thread_stop() { - // enable the protocol auto response, - // for the isolate recv thread terminated. - rtmp->set_auto_response(true); - // reset the timeout to pulse mode. rtmp->set_recv_timeout(timeout * 1000); + + handler->on_thread_stop(); } SrsQueueRecvThread::SrsQueueRecvThread(SrsRtmpServer* rtmp_sdk, int timeout_ms) : trd(this, rtmp_sdk, timeout_ms) { + rtmp = rtmp_sdk; recv_error_code = ERROR_SUCCESS; } @@ -200,11 +197,26 @@ void SrsQueueRecvThread::on_recv_error(int ret) recv_error_code = ret; } +void SrsQueueRecvThread::on_thread_start() +{ + // disable the protocol auto response, + // for the isolate recv thread should never send any messages. + rtmp->set_auto_response(false); +} + +void SrsQueueRecvThread::on_thread_stop() +{ + // enable the protocol auto response, + // for the isolate recv thread terminated. + rtmp->set_auto_response(true); +} + SrsPublishRecvThread::SrsPublishRecvThread( SrsRtmpServer* rtmp_sdk, int timeout_ms, SrsRtmpConn* conn, SrsSource* source, bool is_fmle, bool is_edge ): trd(this, rtmp_sdk, timeout_ms) { + rtmp = rtmp_sdk; _conn = conn; _source = source; _is_fmle = is_fmle; @@ -273,3 +285,15 @@ void SrsPublishRecvThread::on_recv_error(int ret) { recv_error_code = ret; } + +void SrsPublishRecvThread::on_thread_start() +{ + // we donot set the auto response to false, + // for the main thread never send message. +} + +void SrsPublishRecvThread::on_thread_stop() +{ + // we donot set the auto response to true, + // for we donot set to false yet. +} diff --git a/trunk/src/app/srs_app_recv_thread.hpp b/trunk/src/app/srs_app_recv_thread.hpp index df9ac8ef36..88e020bc12 100644 --- a/trunk/src/app/srs_app_recv_thread.hpp +++ b/trunk/src/app/srs_app_recv_thread.hpp @@ -63,6 +63,12 @@ class ISrsMessageHandler * when recv message error. */ virtual void on_recv_error(int ret) = 0; + /** + * when thread start or stop, + * for example, the message handler can set whether auto response. + */ + virtual void on_thread_start() = 0; + virtual void on_thread_stop() = 0; }; /** @@ -99,6 +105,7 @@ class SrsQueueRecvThread : public ISrsMessageHandler private: std::vector queue; SrsRecvThread trd; + SrsRtmpServer* rtmp; // the recv thread error code. int recv_error_code; public: @@ -116,6 +123,9 @@ class SrsQueueRecvThread : public ISrsMessageHandler virtual bool can_handle(); virtual int handle(SrsMessage* msg); virtual void on_recv_error(int ret); +public: + virtual void on_thread_start(); + virtual void on_thread_stop(); }; /** @@ -126,6 +136,7 @@ class SrsPublishRecvThread : public ISrsMessageHandler { private: SrsRecvThread trd; + SrsRtmpServer* rtmp; // the msgs already got. int64_t _nb_msgs; // the recv thread error code. @@ -148,6 +159,9 @@ class SrsPublishRecvThread : public ISrsMessageHandler virtual bool can_handle(); virtual int handle(SrsMessage* msg); virtual void on_recv_error(int ret); +public: + virtual void on_thread_start(); + virtual void on_thread_stop(); }; #endif diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 44a1348666..6fa3d0f00c 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 45 +#define VERSION_REVISION 46 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" From 6f963b50f3e54426b76fd5e172baaea81597071e Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 2 Dec 2014 18:19:01 +0800 Subject: [PATCH 274/800] refine code, publish recv thread sleep 697ms --- trunk/src/app/srs_app_rtmp_conn.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index 1297c9459f..d9fac15a04 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -750,9 +750,9 @@ int SrsRtmpConn::do_publishing(SrsSource* source, SrsPublishRecvThread* trd) int64_t nb_msgs = 0; while (true) { - // use small loop to check the error code, interval = 30s/100 = 300ms. - for (int i = 0; i < 100; i++) { - st_usleep(SRS_CONSTS_RTMP_RECV_TIMEOUT_US / 100); + // use small loop to check the error code, interval = 30s/43 = 697ms. + for (int i = 0; i < 43; i++) { + st_usleep(SRS_CONSTS_RTMP_RECV_TIMEOUT_US / 43); // check the thread error code. if ((ret = trd->error_code()) != ERROR_SUCCESS) { From 24a6a723eebcf97ac4aa6fc283514db9ec99fec0 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 2 Dec 2014 19:22:06 +0800 Subject: [PATCH 275/800] for bug #237, never sleep when thread interval is 0. --- trunk/src/app/srs_app_recv_thread.hpp | 4 ++-- trunk/src/app/srs_app_thread.cpp | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/trunk/src/app/srs_app_recv_thread.hpp b/trunk/src/app/srs_app_recv_thread.hpp index 88e020bc12..95d46413b0 100644 --- a/trunk/src/app/srs_app_recv_thread.hpp +++ b/trunk/src/app/srs_app_recv_thread.hpp @@ -129,9 +129,9 @@ class SrsQueueRecvThread : public ISrsMessageHandler }; /** - * the publish recv thread got message and callback the source method to process message. +* the publish recv thread got message and callback the source method to process message. * @see: https://github.com/winlinvip/simple-rtmp-server/issues/237 - */ +*/ class SrsPublishRecvThread : public ISrsMessageHandler { private: diff --git a/trunk/src/app/srs_app_thread.cpp b/trunk/src/app/srs_app_thread.cpp index 856ff5fa0b..857f5fe8e7 100644 --- a/trunk/src/app/srs_app_thread.cpp +++ b/trunk/src/app/srs_app_thread.cpp @@ -179,7 +179,11 @@ void SrsThread::thread_cycle() break; } - st_usleep(cycle_interval_us); + // to improve performance, donot sleep when interval is zero. + // @see: https://github.com/winlinvip/simple-rtmp-server/issues/237 + if (cycle_interval_us > 0) { + st_usleep(cycle_interval_us); + } } handler->on_thread_stop(); From 463e1fbc4185405872c7814d22628bda3e572c73 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 2 Dec 2014 19:28:36 +0800 Subject: [PATCH 276/800] for bug #237, refine the thread loop for publish thread. 2.0.47 --- trunk/src/app/srs_app_recv_thread.cpp | 60 +++++++++++++-------------- trunk/src/core/srs_core.hpp | 2 +- 2 files changed, 30 insertions(+), 32 deletions(-) diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index 5a6a498074..40d888881c 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -66,29 +66,35 @@ int SrsRecvThread::cycle() { int ret = ERROR_SUCCESS; - if (!handler->can_handle()) { - st_usleep(timeout * 1000); - return ret; - } - - SrsMessage* msg = NULL; - - if ((ret = rtmp->recv_message(&msg)) != ERROR_SUCCESS) { - if (!srs_is_client_gracefully_close(ret)) { - srs_error("recv client control message failed. ret=%d", ret); + while (trd->can_loop()) { + if (!handler->can_handle()) { + st_usleep(timeout * 1000); + continue; } - - // we use no timeout to recv, should never got any error. - trd->stop_loop(); + + SrsMessage* msg = NULL; - // notice the handler got a recv error. - handler->on_recv_error(ret); - - return ret; + // recv and handle message + ret = rtmp->recv_message(&msg); + if (ret == ERROR_SUCCESS) { + ret = handler->handle(msg); + } + + if (ret != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("thread process message failed. ret=%d", ret); + } + + // we use no timeout to recv, should never got any error. + trd->stop_loop(); + + // notice the handler got a recv error. + handler->on_recv_error(ret); + + return ret; + } + srs_verbose("thread loop recv message. ret=%d", ret); } - srs_verbose("play loop recv message. ret=%d", ret); - - handler->handle(msg); return ret; } @@ -263,21 +269,13 @@ int SrsPublishRecvThread::handle(SrsMessage* msg) _nb_msgs++; - // the rtmp connection will handle this message, - // quit the thread loop when error. - recv_error_code = ret = _conn->handle_publish_message(_source, msg, _is_fmle, _is_edge); - - // when error, use stop loop to terminate the thread normally, - // for we are in the thread loop now, and should never use stop() to terminate it. - if (ret != ERROR_SUCCESS) { - trd.stop_loop(); - } + // the rtmp connection will handle this message + ret = _conn->handle_publish_message(_source, msg, _is_fmle, _is_edge); // must always free it, // the source will copy it if need to use. srs_freep(msg); - - // TODO: FIXME: implements it. + return ret; } diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 6fa3d0f00c..1bea57bce1 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 46 +#define VERSION_REVISION 47 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" From 6b57597718d8ea278e2612b35b84242a1e977656 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 2 Dec 2014 22:26:04 +0800 Subject: [PATCH 277/800] for bug #241, merge big chunks for publish, no use. --- trunk/src/app/srs_app_recv_thread.cpp | 10 ++++++++-- trunk/src/kernel/srs_kernel_buffer.cpp | 25 ++++++++++++++++++++----- trunk/src/kernel/srs_kernel_buffer.hpp | 26 ++++++++++++++++++++++++++ trunk/src/rtmp/srs_protocol_io.hpp | 20 ++++++-------------- trunk/src/rtmp/srs_protocol_rtmp.cpp | 5 +++++ trunk/src/rtmp/srs_protocol_rtmp.hpp | 7 +++++++ trunk/src/rtmp/srs_protocol_stack.cpp | 5 +++++ trunk/src/rtmp/srs_protocol_stack.hpp | 7 +++++++ trunk/src/utest/srs_utest_kernel.cpp | 5 +++++ trunk/src/utest/srs_utest_kernel.hpp | 1 + 10 files changed, 90 insertions(+), 21 deletions(-) diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index 40d888881c..5cecf27715 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -288,10 +288,16 @@ void SrsPublishRecvThread::on_thread_start() { // we donot set the auto response to false, // for the main thread never send message. + + // notice the protocol stack to merge chunks to big buffer. + // for example, the buffer is 64KB=512kb, it's 1s buffer for 500kbps video stream. + // so we can use read_fullly(64KB) to merge all chunks in 1s. + // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 + rtmp->set_merge_chunks(true); } void SrsPublishRecvThread::on_thread_stop() { - // we donot set the auto response to true, - // for we donot set to false yet. + // revert state + rtmp->set_merge_chunks(false); } diff --git a/trunk/src/kernel/srs_kernel_buffer.cpp b/trunk/src/kernel/srs_kernel_buffer.cpp index d60690de4f..fd49672d61 100644 --- a/trunk/src/kernel/srs_kernel_buffer.cpp +++ b/trunk/src/kernel/srs_kernel_buffer.cpp @@ -26,7 +26,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include -#define SOCKET_READ_SIZE 4096 +// 4096=4KB +// 16384=16KB +// 65536=64KB +#define SOCKET_READ_SIZE 16384 ISrsBufferReader::ISrsBufferReader() { @@ -38,10 +41,13 @@ ISrsBufferReader::~ISrsBufferReader() SrsBuffer::SrsBuffer() { + merge_chunks_in_big_buffer = false; + buffer = new char[SOCKET_READ_SIZE]; } SrsBuffer::~SrsBuffer() { + srs_freep(buffer); } int SrsBuffer::length() @@ -88,11 +94,15 @@ int SrsBuffer::grow(ISrsBufferReader* reader, int required_size) } while (length() < required_size) { - char buffer[SOCKET_READ_SIZE]; - ssize_t nread; - if ((ret = reader->read(buffer, SOCKET_READ_SIZE, &nread)) != ERROR_SUCCESS) { - return ret; + if (merge_chunks_in_big_buffer) { + if ((ret = reader->read_fully(buffer, SOCKET_READ_SIZE, &nread)) != ERROR_SUCCESS) { + return ret; + } + } else { + if ((ret = reader->read(buffer, SOCKET_READ_SIZE, &nread)) != ERROR_SUCCESS) { + return ret; + } } srs_assert((int)nread > 0); @@ -102,4 +112,9 @@ int SrsBuffer::grow(ISrsBufferReader* reader, int required_size) return ret; } +void SrsBuffer::set_merge_chunks(bool v) +{ + merge_chunks_in_big_buffer = v; +} + diff --git a/trunk/src/kernel/srs_kernel_buffer.hpp b/trunk/src/kernel/srs_kernel_buffer.hpp index 93d3a56832..9523377019 100644 --- a/trunk/src/kernel/srs_kernel_buffer.hpp +++ b/trunk/src/kernel/srs_kernel_buffer.hpp @@ -42,7 +42,16 @@ class ISrsBufferReader virtual ~ISrsBufferReader(); // for protocol/amf0/msg-codec public: + /** + * read some bytes of data. + * @param nread, the actually read size, NULL to ignore. + */ virtual int read(void* buf, size_t size, ssize_t* nread) = 0; + /** + * read specified size bytes of data + * @param nread, the actually read size, NULL to ignore. + */ + virtual int read_fully(void* buf, size_t size, ssize_t* nread) = 0; }; /** @@ -53,6 +62,15 @@ class SrsBuffer { private: std::vector data; + /** + * notice the protocol stack to merge chunks to big buffer. + * for example, the buffer is 64KB=512kb, it's 1s buffer for 500kbps video stream. + * so we can use read_fullly(64KB) to merge all chunks in 1s. + * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 + */ + bool merge_chunks_in_big_buffer; + // the socket recv buffer. + char* buffer; public: SrsBuffer(); virtual ~SrsBuffer(); @@ -89,6 +107,14 @@ class SrsBuffer * @remark, we actually maybe read more than required_size, maybe 4k for example. */ virtual int grow(ISrsBufferReader* reader, int required_size); +public: + /** + * notice the protocol stack to merge chunks to big buffer. + * for example, the buffer is 64KB=512kb, it's 1s buffer for 500kbps video stream. + * so we can use read_fullly(64KB) to merge all chunks in 1s. + * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 + */ + virtual void set_merge_chunks(bool v); }; #endif diff --git a/trunk/src/rtmp/srs_protocol_io.hpp b/trunk/src/rtmp/srs_protocol_io.hpp index bc816309f4..0de54ece32 100644 --- a/trunk/src/rtmp/srs_protocol_io.hpp +++ b/trunk/src/rtmp/srs_protocol_io.hpp @@ -43,17 +43,16 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | IBufferReader | | IStatistic | | IBufferWriter | +---------------+ +--------------------+ +---------------+ | + read() | | + get_recv_bytes() | | + write() | -+------+--------+ | + get_recv_bytes() | | + writev() | - / \ +---+--------------+-+ +-------+-------+ - | / \ / \ / \ +| + readfully() | | + get_recv_bytes() | | + writev() | ++------+--------+ +---+--------------+-+ +-------+-------+ + / \ / \ / \ / \ | | | | +------+------------------+-+ +-----+----------------+--+ | IProtocolReader | | IProtocolWriter | +---------------------------+ +-------------------------+ -| + readfully() | | + set_send_timeout() | -| + set_recv_timeout() | +-------+-----------------+ -+------------+--------------+ / \ - / \ | +| + set_recv_timeout() | | + set_send_timeout() | ++------------+--------------+ +-------+-----------------+ + / \ / \ | | +--+-----------------------------+-+ | IProtocolReaderWriter | @@ -123,13 +122,6 @@ class ISrsProtocolReader : public virtual ISrsBufferReader, public virtual ISrsP * get the recv timeout in us. */ virtual int64_t get_recv_timeout() = 0; -// for handshake. -public: - /** - * read specified size bytes of data - * @param nread, the actually read size, NULL to ignore. - */ - virtual int read_fully(void* buf, size_t size, ssize_t* nread) = 0; }; /** diff --git a/trunk/src/rtmp/srs_protocol_rtmp.cpp b/trunk/src/rtmp/srs_protocol_rtmp.cpp index 39ccb21319..b61b89c009 100644 --- a/trunk/src/rtmp/srs_protocol_rtmp.cpp +++ b/trunk/src/rtmp/srs_protocol_rtmp.cpp @@ -745,6 +745,11 @@ void SrsRtmpServer::set_auto_response(bool v) protocol->set_auto_response(v); } +void SrsRtmpServer::set_merge_chunks(bool v) +{ + protocol->set_merge_chunks(v); +} + void SrsRtmpServer::set_recv_timeout(int64_t timeout_us) { protocol->set_recv_timeout(timeout_us); diff --git a/trunk/src/rtmp/srs_protocol_rtmp.hpp b/trunk/src/rtmp/srs_protocol_rtmp.hpp index 9846d76191..217acd6e9f 100644 --- a/trunk/src/rtmp/srs_protocol_rtmp.hpp +++ b/trunk/src/rtmp/srs_protocol_rtmp.hpp @@ -343,6 +343,13 @@ class SrsRtmpServer */ virtual void set_auto_response(bool v); /** + * notice the protocol stack to merge chunks to big buffer. + * for example, the buffer is 64KB=512kb, it's 1s buffer for 500kbps video stream. + * so we can use read_fullly(64KB) to merge all chunks in 1s. + * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 + */ + virtual void set_merge_chunks(bool v); + /** * set/get the recv timeout in us. * if timeout, recv/send message return ERROR_SOCKET_TIMEOUT. */ diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp index 6f79e202b6..dd7aea0e8d 100644 --- a/trunk/src/rtmp/srs_protocol_stack.cpp +++ b/trunk/src/rtmp/srs_protocol_stack.cpp @@ -478,6 +478,11 @@ int SrsProtocol::manual_response_flush() return ret; } +void SrsProtocol::set_merge_chunks(bool v) +{ + in_buffer->set_merge_chunks(v); +} + void SrsProtocol::set_recv_timeout(int64_t timeout_us) { return skt->set_recv_timeout(timeout_us); diff --git a/trunk/src/rtmp/srs_protocol_stack.hpp b/trunk/src/rtmp/srs_protocol_stack.hpp index d9ba0ee847..328d1aec76 100644 --- a/trunk/src/rtmp/srs_protocol_stack.hpp +++ b/trunk/src/rtmp/srs_protocol_stack.hpp @@ -269,6 +269,13 @@ class SrsProtocol * @see the auto_response_when_recv and manual_response_queue. */ virtual int manual_response_flush(); + /** + * notice the protocol stack to merge chunks to big buffer. + * for example, the buffer is 64KB=512kb, it's 1s buffer for 500kbps video stream. + * so we can use read_fullly(64KB) to merge all chunks in 1s. + * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 + */ + virtual void set_merge_chunks(bool v); public: /** * set/get the recv timeout in us. diff --git a/trunk/src/utest/srs_utest_kernel.cpp b/trunk/src/utest/srs_utest_kernel.cpp index 166981cc84..445f41d244 100644 --- a/trunk/src/utest/srs_utest_kernel.cpp +++ b/trunk/src/utest/srs_utest_kernel.cpp @@ -199,6 +199,11 @@ int MockBufferReader::read(void* buf, size_t size, ssize_t* nread) return ERROR_SUCCESS; } +int MockBufferReader::read_fully(void* buf, size_t size, ssize_t* nread) +{ + return read(buf, size, nread); +} + #ifdef ENABLE_UTEST_KERNEL VOID TEST(KernelBufferTest, DefaultObject) diff --git a/trunk/src/utest/srs_utest_kernel.hpp b/trunk/src/utest/srs_utest_kernel.hpp index 44b7fd6765..5057e57fd5 100644 --- a/trunk/src/utest/srs_utest_kernel.hpp +++ b/trunk/src/utest/srs_utest_kernel.hpp @@ -42,6 +42,7 @@ class MockBufferReader: public ISrsBufferReader virtual ~MockBufferReader(); public: virtual int read(void* buf, size_t size, ssize_t* nread); + virtual int read_fully(void* buf, size_t size, ssize_t* nread); }; class MockSrsFileWriter : public SrsFileWriter From 565f29ed6c9d2de9b538ef1c3f2b1ad92ef19ba5 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 2 Dec 2014 22:26:20 +0800 Subject: [PATCH 278/800] Revert "for bug #241, merge big chunks for publish, no use." This reverts commit 6b57597718d8ea278e2612b35b84242a1e977656. --- trunk/src/app/srs_app_recv_thread.cpp | 10 ++-------- trunk/src/kernel/srs_kernel_buffer.cpp | 25 +++++-------------------- trunk/src/kernel/srs_kernel_buffer.hpp | 26 -------------------------- trunk/src/rtmp/srs_protocol_io.hpp | 20 ++++++++++++++------ trunk/src/rtmp/srs_protocol_rtmp.cpp | 5 ----- trunk/src/rtmp/srs_protocol_rtmp.hpp | 7 ------- trunk/src/rtmp/srs_protocol_stack.cpp | 5 ----- trunk/src/rtmp/srs_protocol_stack.hpp | 7 ------- trunk/src/utest/srs_utest_kernel.cpp | 5 ----- trunk/src/utest/srs_utest_kernel.hpp | 1 - 10 files changed, 21 insertions(+), 90 deletions(-) diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index 5cecf27715..40d888881c 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -288,16 +288,10 @@ void SrsPublishRecvThread::on_thread_start() { // we donot set the auto response to false, // for the main thread never send message. - - // notice the protocol stack to merge chunks to big buffer. - // for example, the buffer is 64KB=512kb, it's 1s buffer for 500kbps video stream. - // so we can use read_fullly(64KB) to merge all chunks in 1s. - // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 - rtmp->set_merge_chunks(true); } void SrsPublishRecvThread::on_thread_stop() { - // revert state - rtmp->set_merge_chunks(false); + // we donot set the auto response to true, + // for we donot set to false yet. } diff --git a/trunk/src/kernel/srs_kernel_buffer.cpp b/trunk/src/kernel/srs_kernel_buffer.cpp index fd49672d61..d60690de4f 100644 --- a/trunk/src/kernel/srs_kernel_buffer.cpp +++ b/trunk/src/kernel/srs_kernel_buffer.cpp @@ -26,10 +26,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include -// 4096=4KB -// 16384=16KB -// 65536=64KB -#define SOCKET_READ_SIZE 16384 +#define SOCKET_READ_SIZE 4096 ISrsBufferReader::ISrsBufferReader() { @@ -41,13 +38,10 @@ ISrsBufferReader::~ISrsBufferReader() SrsBuffer::SrsBuffer() { - merge_chunks_in_big_buffer = false; - buffer = new char[SOCKET_READ_SIZE]; } SrsBuffer::~SrsBuffer() { - srs_freep(buffer); } int SrsBuffer::length() @@ -94,15 +88,11 @@ int SrsBuffer::grow(ISrsBufferReader* reader, int required_size) } while (length() < required_size) { + char buffer[SOCKET_READ_SIZE]; + ssize_t nread; - if (merge_chunks_in_big_buffer) { - if ((ret = reader->read_fully(buffer, SOCKET_READ_SIZE, &nread)) != ERROR_SUCCESS) { - return ret; - } - } else { - if ((ret = reader->read(buffer, SOCKET_READ_SIZE, &nread)) != ERROR_SUCCESS) { - return ret; - } + if ((ret = reader->read(buffer, SOCKET_READ_SIZE, &nread)) != ERROR_SUCCESS) { + return ret; } srs_assert((int)nread > 0); @@ -112,9 +102,4 @@ int SrsBuffer::grow(ISrsBufferReader* reader, int required_size) return ret; } -void SrsBuffer::set_merge_chunks(bool v) -{ - merge_chunks_in_big_buffer = v; -} - diff --git a/trunk/src/kernel/srs_kernel_buffer.hpp b/trunk/src/kernel/srs_kernel_buffer.hpp index 9523377019..93d3a56832 100644 --- a/trunk/src/kernel/srs_kernel_buffer.hpp +++ b/trunk/src/kernel/srs_kernel_buffer.hpp @@ -42,16 +42,7 @@ class ISrsBufferReader virtual ~ISrsBufferReader(); // for protocol/amf0/msg-codec public: - /** - * read some bytes of data. - * @param nread, the actually read size, NULL to ignore. - */ virtual int read(void* buf, size_t size, ssize_t* nread) = 0; - /** - * read specified size bytes of data - * @param nread, the actually read size, NULL to ignore. - */ - virtual int read_fully(void* buf, size_t size, ssize_t* nread) = 0; }; /** @@ -62,15 +53,6 @@ class SrsBuffer { private: std::vector data; - /** - * notice the protocol stack to merge chunks to big buffer. - * for example, the buffer is 64KB=512kb, it's 1s buffer for 500kbps video stream. - * so we can use read_fullly(64KB) to merge all chunks in 1s. - * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 - */ - bool merge_chunks_in_big_buffer; - // the socket recv buffer. - char* buffer; public: SrsBuffer(); virtual ~SrsBuffer(); @@ -107,14 +89,6 @@ class SrsBuffer * @remark, we actually maybe read more than required_size, maybe 4k for example. */ virtual int grow(ISrsBufferReader* reader, int required_size); -public: - /** - * notice the protocol stack to merge chunks to big buffer. - * for example, the buffer is 64KB=512kb, it's 1s buffer for 500kbps video stream. - * so we can use read_fullly(64KB) to merge all chunks in 1s. - * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 - */ - virtual void set_merge_chunks(bool v); }; #endif diff --git a/trunk/src/rtmp/srs_protocol_io.hpp b/trunk/src/rtmp/srs_protocol_io.hpp index 0de54ece32..bc816309f4 100644 --- a/trunk/src/rtmp/srs_protocol_io.hpp +++ b/trunk/src/rtmp/srs_protocol_io.hpp @@ -43,16 +43,17 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | IBufferReader | | IStatistic | | IBufferWriter | +---------------+ +--------------------+ +---------------+ | + read() | | + get_recv_bytes() | | + write() | -| + readfully() | | + get_recv_bytes() | | + writev() | -+------+--------+ +---+--------------+-+ +-------+-------+ - / \ / \ / \ / \ ++------+--------+ | + get_recv_bytes() | | + writev() | + / \ +---+--------------+-+ +-------+-------+ + | / \ / \ / \ | | | | +------+------------------+-+ +-----+----------------+--+ | IProtocolReader | | IProtocolWriter | +---------------------------+ +-------------------------+ -| + set_recv_timeout() | | + set_send_timeout() | -+------------+--------------+ +-------+-----------------+ - / \ / \ +| + readfully() | | + set_send_timeout() | +| + set_recv_timeout() | +-------+-----------------+ ++------------+--------------+ / \ + / \ | | | +--+-----------------------------+-+ | IProtocolReaderWriter | @@ -122,6 +123,13 @@ class ISrsProtocolReader : public virtual ISrsBufferReader, public virtual ISrsP * get the recv timeout in us. */ virtual int64_t get_recv_timeout() = 0; +// for handshake. +public: + /** + * read specified size bytes of data + * @param nread, the actually read size, NULL to ignore. + */ + virtual int read_fully(void* buf, size_t size, ssize_t* nread) = 0; }; /** diff --git a/trunk/src/rtmp/srs_protocol_rtmp.cpp b/trunk/src/rtmp/srs_protocol_rtmp.cpp index b61b89c009..39ccb21319 100644 --- a/trunk/src/rtmp/srs_protocol_rtmp.cpp +++ b/trunk/src/rtmp/srs_protocol_rtmp.cpp @@ -745,11 +745,6 @@ void SrsRtmpServer::set_auto_response(bool v) protocol->set_auto_response(v); } -void SrsRtmpServer::set_merge_chunks(bool v) -{ - protocol->set_merge_chunks(v); -} - void SrsRtmpServer::set_recv_timeout(int64_t timeout_us) { protocol->set_recv_timeout(timeout_us); diff --git a/trunk/src/rtmp/srs_protocol_rtmp.hpp b/trunk/src/rtmp/srs_protocol_rtmp.hpp index 217acd6e9f..9846d76191 100644 --- a/trunk/src/rtmp/srs_protocol_rtmp.hpp +++ b/trunk/src/rtmp/srs_protocol_rtmp.hpp @@ -343,13 +343,6 @@ class SrsRtmpServer */ virtual void set_auto_response(bool v); /** - * notice the protocol stack to merge chunks to big buffer. - * for example, the buffer is 64KB=512kb, it's 1s buffer for 500kbps video stream. - * so we can use read_fullly(64KB) to merge all chunks in 1s. - * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 - */ - virtual void set_merge_chunks(bool v); - /** * set/get the recv timeout in us. * if timeout, recv/send message return ERROR_SOCKET_TIMEOUT. */ diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp index dd7aea0e8d..6f79e202b6 100644 --- a/trunk/src/rtmp/srs_protocol_stack.cpp +++ b/trunk/src/rtmp/srs_protocol_stack.cpp @@ -478,11 +478,6 @@ int SrsProtocol::manual_response_flush() return ret; } -void SrsProtocol::set_merge_chunks(bool v) -{ - in_buffer->set_merge_chunks(v); -} - void SrsProtocol::set_recv_timeout(int64_t timeout_us) { return skt->set_recv_timeout(timeout_us); diff --git a/trunk/src/rtmp/srs_protocol_stack.hpp b/trunk/src/rtmp/srs_protocol_stack.hpp index 328d1aec76..d9ba0ee847 100644 --- a/trunk/src/rtmp/srs_protocol_stack.hpp +++ b/trunk/src/rtmp/srs_protocol_stack.hpp @@ -269,13 +269,6 @@ class SrsProtocol * @see the auto_response_when_recv and manual_response_queue. */ virtual int manual_response_flush(); - /** - * notice the protocol stack to merge chunks to big buffer. - * for example, the buffer is 64KB=512kb, it's 1s buffer for 500kbps video stream. - * so we can use read_fullly(64KB) to merge all chunks in 1s. - * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 - */ - virtual void set_merge_chunks(bool v); public: /** * set/get the recv timeout in us. diff --git a/trunk/src/utest/srs_utest_kernel.cpp b/trunk/src/utest/srs_utest_kernel.cpp index 445f41d244..166981cc84 100644 --- a/trunk/src/utest/srs_utest_kernel.cpp +++ b/trunk/src/utest/srs_utest_kernel.cpp @@ -199,11 +199,6 @@ int MockBufferReader::read(void* buf, size_t size, ssize_t* nread) return ERROR_SUCCESS; } -int MockBufferReader::read_fully(void* buf, size_t size, ssize_t* nread) -{ - return read(buf, size, nread); -} - #ifdef ENABLE_UTEST_KERNEL VOID TEST(KernelBufferTest, DefaultObject) diff --git a/trunk/src/utest/srs_utest_kernel.hpp b/trunk/src/utest/srs_utest_kernel.hpp index 5057e57fd5..44b7fd6765 100644 --- a/trunk/src/utest/srs_utest_kernel.hpp +++ b/trunk/src/utest/srs_utest_kernel.hpp @@ -42,7 +42,6 @@ class MockBufferReader: public ISrsBufferReader virtual ~MockBufferReader(); public: virtual int read(void* buf, size_t size, ssize_t* nread); - virtual int read_fully(void* buf, size_t size, ssize_t* nread); }; class MockSrsFileWriter : public SrsFileWriter From 787ab674e38734ea8e0678101614fdcd84645dc8 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 3 Dec 2014 12:08:29 +0800 Subject: [PATCH 279/800] fix #244, conn thread use cond to wait for recv thread error. 2.0.47. --- README.md | 44 +++++++++------------------ trunk/src/app/srs_app_recv_thread.cpp | 22 ++++++++++++++ trunk/src/app/srs_app_recv_thread.hpp | 8 +++++ trunk/src/app/srs_app_rtmp_conn.cpp | 16 +++++----- 4 files changed, 51 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 0b9e39cdeb..9650f8139c 100755 --- a/README.md +++ b/README.md @@ -485,6 +485,7 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History +* v2.0, 2014-12-03, fix [#244](https://github.com/winlinvip/simple-rtmp-server/issues/244), conn thread use cond to wait for recv thread error. 2.0.47. * v2.0, 2014-12-02, merge [#239](https://github.com/winlinvip/simple-rtmp-server/pull/239), traverse the token before response connect. 2.0.45. * v2.0, 2014-12-02, srs-librtmp support hijack io apis for st-load. 2.0.42. * v2.0, 2014-12-01, for [#237](https://github.com/winlinvip/simple-rtmp-server/issues/237), refine syscall for recv, supports 1.5k clients. 2.0.41. @@ -703,47 +704,30 @@ Supported operating systems and hardware: ## Performance -Performance benchmark history, on virtual box: +Performance benchmark history, on virtual box. + +### Play benchmark + +The play benchmark by st-load: * 2013-11-28, SRS 0.5.0, 1.8k(1800)clients, 90%CPU, 41MB. [benchmark](https://github.com/winlinvip/simple-rtmp-server/commit/023e23bc8261bec15a70a7ae932098fb4f82b679) * 2014-07-12, SRS 0.9.156, 1.8k(1800)clients, 68%CPU, 38MB. [benchmark](https://github.com/winlinvip/simple-rtmp-server/commit/e2d273f4939348374bf9644df9d54c4293b39c1a) * 2014-07-12, SRS 0.9.156, 2.7k(2700)clients, 89%CPU, 61MB. [benchmark](https://github.com/winlinvip/simple-rtmp-server/commit/6d12280b7cc54c465b1caf8b1402149e77c4c7d9) -* 2014-11-11, SRS 1.0.5, 2.7k(2700)clients, 85%CPU, 66MB. (1.0 equals 2.0.12) +* 2014-11-11, SRS 1.0.5, 2.7k(2700)clients, 85%CPU, 66MB. +* 2014-11-11, SRS 2.0.12, 2.7k(2700)clients, 85%CPU, 66MB. * 2014-11-12, SRS 2.0.14, 2.7k(2700)clients, 69%CPU, 59MB. * 2014-11-12, SRS 2.0.14, 3.5k(3500)clients, 95%CPU, 78MB. -* 2014-11-13, SRS 2.0.15, 6.0k(6000)clients, 82%CPU, 203MB. (500 publishers). +* 2014-11-13, SRS 2.0.15, 6.0k(6000)clients, 82%CPU, 203MB. * 2014-11-22, SRS 2.0.30, 7.5k(7500)clients, 87%CPU, 320MB. -* 2014-12-01, SRS 2.0.41, 7.5k(7500)clients, 87%CPU, 320MB. (1500 publishers). -Latest benchmark(2014-07-12): +* See also: [Performance for x86/x64 Test Guide](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Performance) +* See also: [Performance for RaspberryPi](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RaspberryPi) -1. 300 connections, 150Mbps, 500kbps, CPU 5.7%, MEM 9208KB. -1. 600 connections, 300Mbps, 500kbps, CPU 18.3%, MEM 13MB. -1. 900 connections, 450Mbps, 500kbps, CPU 27.9%, MEM 20MB. -1. 1200 connections, 600Mbps, 500kbps, CPU 43.9%, MEM 26MB. -1. 1500 connections, 750Mbps, 500kbps, CPU 55.2%, MEM 32MB. -1. 1800 connections, 900Mbps, 500kbps, CPU 68.8%, MEM 38MB. -1. 2100 connections, 1050Mbps, 500kbps, CPU 75.7%, MEM 46MB. -1. 2400 connections, 1200Mbps, 500kbps, CPU 83.7%, MEM 54MB. -1. 2700 connections, 1350Mbps, 500kbps, CPU 89.9%, MEM 61MB. +### Publish benchmark -

-[winlin@dev6 srs]$ dstat
-----total-cpu-usage---- -dsk/total- ---net/lo-- ---paging-- ---system--
-usr sys idl wai hiq siq| read  writ| recv  send|  in   out | int   csw 
- 29  17  39   0   0  15|   0  5325B| 163M  163M|   0     0 |4331  3386 
- 30  16  38   0   0  16|   0  5325B| 160M  160M|   0     0 |4252  3332 
- 30  15  37   0   0  17|   0  7646B| 169M  169M|   0     0 |4015  2886 
- 30  17  36   0   0  17|   0  1638B| 197M  197M|   0     0 |4021  3037 
- 31  17  35   0   0  17|   0   410B| 204M  204M|   0     0 |4181  3243 
- 33  17  32   0   0  18|   0  2185B| 191M  191M|   0     0 |4305  3592 
- 31  15  36   0   0  18|   0  1229B| 127M  127M|   0     0 |4446  3822 
- 34  18  30   0   0  18|   0     0 | 231M  231M|   0     0 |4461  3691 
- 32  17  33   0   0  18|   0   410B| 169M  169M|   0     0 |4518  3788 
-
+The publish benchmark by st-load: -* See also: [Performance for x86/x64 Test Guide](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Performance) -* See also: [Performance for RaspberryPi](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RaspberryPi) +* 2014-12-03, SRS 1.0.10, 1k(1000) publishers, xx%CPU, xxMB. ## Architecture diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index 40d888881c..4a2b02723a 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -230,11 +230,25 @@ SrsPublishRecvThread::SrsPublishRecvThread( recv_error_code = ERROR_SUCCESS; _nb_msgs = 0; + error = st_cond_new(); } SrsPublishRecvThread::~SrsPublishRecvThread() { trd.stop(); + st_cond_destroy(error); +} + +int SrsPublishRecvThread::wait(int timeout_ms) +{ + if (recv_error_code != ERROR_SUCCESS) { + return recv_error_code; + } + + // ignore any return of cond wait. + st_cond_timedwait(error, timeout_ms * 1000); + + return ERROR_SUCCESS; } int64_t SrsPublishRecvThread::nb_msgs() @@ -282,6 +296,10 @@ int SrsPublishRecvThread::handle(SrsMessage* msg) void SrsPublishRecvThread::on_recv_error(int ret) { recv_error_code = ret; + + // when recv thread error, signal the conn thread to process it. + // @see https://github.com/winlinvip/simple-rtmp-server/issues/244 + st_cond_signal(error); } void SrsPublishRecvThread::on_thread_start() @@ -294,4 +312,8 @@ void SrsPublishRecvThread::on_thread_stop() { // we donot set the auto response to true, // for we donot set to false yet. + + // when thread stop, signal the conn thread which wait. + // @see https://github.com/winlinvip/simple-rtmp-server/issues/244 + st_cond_signal(error); } diff --git a/trunk/src/app/srs_app_recv_thread.hpp b/trunk/src/app/srs_app_recv_thread.hpp index 95d46413b0..5a579b6dcf 100644 --- a/trunk/src/app/srs_app_recv_thread.hpp +++ b/trunk/src/app/srs_app_recv_thread.hpp @@ -142,14 +142,22 @@ class SrsPublishRecvThread : public ISrsMessageHandler // the recv thread error code. int recv_error_code; SrsRtmpConn* _conn; + // the params for conn callback. SrsSource* _source; bool _is_fmle; bool _is_edge; + // the error timeout cond + // @see https://github.com/winlinvip/simple-rtmp-server/issues/244 + st_cond_t error; public: SrsPublishRecvThread(SrsRtmpServer* rtmp_sdk, int timeout_ms, SrsRtmpConn* conn, SrsSource* source, bool is_fmle, bool is_edge); virtual ~SrsPublishRecvThread(); public: + /** + * wait for error for some timeout. + */ + virtual int wait(int timeout_ms); virtual int64_t nb_msgs(); virtual int error_code(); public: diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index d9fac15a04..33e0bb5180 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -750,17 +750,15 @@ int SrsRtmpConn::do_publishing(SrsSource* source, SrsPublishRecvThread* trd) int64_t nb_msgs = 0; while (true) { - // use small loop to check the error code, interval = 30s/43 = 697ms. - for (int i = 0; i < 43; i++) { - st_usleep(SRS_CONSTS_RTMP_RECV_TIMEOUT_US / 43); + // cond wait for error. + trd->wait(SRS_CONSTS_RTMP_RECV_TIMEOUT_US / 1000); - // check the thread error code. - if ((ret = trd->error_code()) != ERROR_SUCCESS) { - if (!srs_is_client_gracefully_close(ret)) { - srs_error("recv thread failed. ret=%d", ret); - } - return ret; + // check the thread error code. + if ((ret = trd->error_code()) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("recv thread failed. ret=%d", ret); } + return ret; } // when not got any messages, timeout. From bf99069ed11254d16c0ed48bf0f668336fcb0490 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 3 Dec 2014 12:11:00 +0800 Subject: [PATCH 280/800] update readme. --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9650f8139c..eb1a0027d4 100755 --- a/README.md +++ b/README.md @@ -706,6 +706,9 @@ Supported operating systems and hardware: Performance benchmark history, on virtual box. +* See also: [Performance for x86/x64 Test Guide](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Performance) +* See also: [Performance for RaspberryPi](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RaspberryPi) + ### Play benchmark The play benchmark by st-load: @@ -718,10 +721,7 @@ The play benchmark by st-load: * 2014-11-12, SRS 2.0.14, 2.7k(2700)clients, 69%CPU, 59MB. * 2014-11-12, SRS 2.0.14, 3.5k(3500)clients, 95%CPU, 78MB. * 2014-11-13, SRS 2.0.15, 6.0k(6000)clients, 82%CPU, 203MB. -* 2014-11-22, SRS 2.0.30, 7.5k(7500)clients, 87%CPU, 320MB. - -* See also: [Performance for x86/x64 Test Guide](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Performance) -* See also: [Performance for RaspberryPi](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RaspberryPi) +* 2014-11-22, SRS 2.0.30, 7.5k(7500)clients, 87%CPU, 320MB. ### Publish benchmark From f70027cdebe17a0f37c6f5a29ac04f52d7be1495 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 3 Dec 2014 12:21:26 +0800 Subject: [PATCH 281/800] update readme. --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index eb1a0027d4..704bd1edda 100755 --- a/README.md +++ b/README.md @@ -711,21 +711,21 @@ Performance benchmark history, on virtual box. ### Play benchmark -The play benchmark by st-load: +The play benchmark by [st-load](https://github.com/winlinvip/st-load): -* 2013-11-28, SRS 0.5.0, 1.8k(1800)clients, 90%CPU, 41MB. [benchmark](https://github.com/winlinvip/simple-rtmp-server/commit/023e23bc8261bec15a70a7ae932098fb4f82b679) -* 2014-07-12, SRS 0.9.156, 1.8k(1800)clients, 68%CPU, 38MB. [benchmark](https://github.com/winlinvip/simple-rtmp-server/commit/e2d273f4939348374bf9644df9d54c4293b39c1a) -* 2014-07-12, SRS 0.9.156, 2.7k(2700)clients, 89%CPU, 61MB. [benchmark](https://github.com/winlinvip/simple-rtmp-server/commit/6d12280b7cc54c465b1caf8b1402149e77c4c7d9) -* 2014-11-11, SRS 1.0.5, 2.7k(2700)clients, 85%CPU, 66MB. +* 2013-11-28, SRS 0.5.0, 1.8k(1800)clients, 90%CPU, 41MB. +* 2014-07-12, SRS 0.9.156, 1.8k(1800)clients, 68%CPU, 38MB. +* 2014-07-12, SRS 0.9.156, 2.7k(2700)clients, 89%CPU, 61MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/1ae3e6c64cc5cee90e6050c26968ebc3c18281be) +* 2014-11-11, SRS 1.0.5, 2.7k(2700)clients, 85%CPU, 66MB. * 2014-11-11, SRS 2.0.12, 2.7k(2700)clients, 85%CPU, 66MB. * 2014-11-12, SRS 2.0.14, 2.7k(2700)clients, 69%CPU, 59MB. -* 2014-11-12, SRS 2.0.14, 3.5k(3500)clients, 95%CPU, 78MB. -* 2014-11-13, SRS 2.0.15, 6.0k(6000)clients, 82%CPU, 203MB. +* 2014-11-12, SRS 2.0.14, 3.5k(3500)clients, 95%CPU, 78MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/8acd143a7a152885b815999162660fd4e7a3f247) +* 2014-11-13, SRS 2.0.15, 6.0k(6000)clients, 82%CPU, 203MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/cc6aca9ad55342a06440ce7f3b38453776b2b2d1) * 2014-11-22, SRS 2.0.30, 7.5k(7500)clients, 87%CPU, 320MB. ### Publish benchmark -The publish benchmark by st-load: +The publish benchmark by [st-load](https://github.com/winlinvip/st-load): * 2014-12-03, SRS 1.0.10, 1k(1000) publishers, xx%CPU, xxMB. From a88962daf251e0a2c822efa7e31b3c806207603a Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 3 Dec 2014 12:24:29 +0800 Subject: [PATCH 282/800] update readme. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 704bd1edda..c809779025 100755 --- a/README.md +++ b/README.md @@ -721,7 +721,7 @@ The play benchmark by [st-load](https://github.com/winlinvip/st-load): * 2014-11-12, SRS 2.0.14, 2.7k(2700)clients, 69%CPU, 59MB. * 2014-11-12, SRS 2.0.14, 3.5k(3500)clients, 95%CPU, 78MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/8acd143a7a152885b815999162660fd4e7a3f247) * 2014-11-13, SRS 2.0.15, 6.0k(6000)clients, 82%CPU, 203MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/cc6aca9ad55342a06440ce7f3b38453776b2b2d1) -* 2014-11-22, SRS 2.0.30, 7.5k(7500)clients, 87%CPU, 320MB. +* 2014-11-22, SRS 2.0.30, 7.5k(7500)clients, 87%CPU, 320MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/58136ec178e3d47db6c90a59875d7e40946936e5) ### Publish benchmark From c083238760f1a367a0ba7cba94478bb48da4611b Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 3 Dec 2014 12:35:50 +0800 Subject: [PATCH 283/800] update readme. --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c809779025..facaad4259 100755 --- a/README.md +++ b/README.md @@ -727,7 +727,10 @@ The play benchmark by [st-load](https://github.com/winlinvip/st-load): The publish benchmark by [st-load](https://github.com/winlinvip/st-load): -* 2014-12-03, SRS 1.0.10, 1k(1000) publishers, xx%CPU, xxMB. +* 2014-12-03, SRS 1.0.10, 1k(1200) publishers, 96%CPU, 43MB. +* 2014-12-03, SRS 2.0.12, 1k(1200) publishers, 96%CPU, 43MB. +* 2014-12-03, SRS 2.0.47, 1k(1200) publishers, 84%CPU, 76MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/787ab674e38734ea8e0678101614fdcd84645dc8) +* 2014-12-03, SRS 2.0.47, 1k(1400) publishers, 95%CPU, 140MB. ## Architecture From c457364321d3e5e83296de77f49079df8c533a42 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 3 Dec 2014 14:05:15 +0800 Subject: [PATCH 284/800] for bug #237, add name for thread. 2.0.47 --- trunk/src/app/srs_app_conn.cpp | 2 +- trunk/src/app/srs_app_edge.cpp | 4 ++-- trunk/src/app/srs_app_encoder.cpp | 2 +- trunk/src/app/srs_app_forward.cpp | 2 +- trunk/src/app/srs_app_ingest.cpp | 4 ++-- trunk/src/app/srs_app_recv_thread.cpp | 2 +- trunk/src/app/srs_app_server.cpp | 14 +++++++------- trunk/src/app/srs_app_thread.cpp | 21 +++++++++++---------- trunk/src/app/srs_app_thread.hpp | 5 ++++- 9 files changed, 30 insertions(+), 26 deletions(-) diff --git a/trunk/src/app/srs_app_conn.cpp b/trunk/src/app/srs_app_conn.cpp index ecaed2168d..d42d4de18b 100644 --- a/trunk/src/app/srs_app_conn.cpp +++ b/trunk/src/app/srs_app_conn.cpp @@ -37,7 +37,7 @@ SrsConnection::SrsConnection(SrsServer* srs_server, st_netfd_t client_stfd) // so we never use joinable. // TODO: FIXME: maybe other thread need to stop it. // @see: https://github.com/winlinvip/simple-rtmp-server/issues/78 - pthread = new SrsThread(this, 0, false); + pthread = new SrsThread("conn", this, 0, false); } SrsConnection::~SrsConnection() diff --git a/trunk/src/app/srs_app_edge.cpp b/trunk/src/app/srs_app_edge.cpp index 78dd39cd82..37d9ef941e 100644 --- a/trunk/src/app/srs_app_edge.cpp +++ b/trunk/src/app/srs_app_edge.cpp @@ -69,7 +69,7 @@ SrsEdgeIngester::SrsEdgeIngester() origin_index = 0; stream_id = 0; stfd = NULL; - pthread = new SrsThread(this, SRS_EDGE_INGESTER_SLEEP_US, true); + pthread = new SrsThread("edge-igs", this, SRS_EDGE_INGESTER_SLEEP_US, true); } SrsEdgeIngester::~SrsEdgeIngester() @@ -386,7 +386,7 @@ SrsEdgeForwarder::SrsEdgeForwarder() origin_index = 0; stream_id = 0; stfd = NULL; - pthread = new SrsThread(this, SRS_EDGE_FORWARDER_SLEEP_US, true); + pthread = new SrsThread("edge-fwr", this, SRS_EDGE_FORWARDER_SLEEP_US, true); queue = new SrsMessageQueue(); send_error_code = ERROR_SUCCESS; } diff --git a/trunk/src/app/srs_app_encoder.cpp b/trunk/src/app/srs_app_encoder.cpp index 6aedcd1528..25d62063f8 100644 --- a/trunk/src/app/srs_app_encoder.cpp +++ b/trunk/src/app/srs_app_encoder.cpp @@ -44,7 +44,7 @@ static std::vector _transcoded_url; SrsEncoder::SrsEncoder() { - pthread = new SrsThread(this, SRS_RTMP_ENCODER_SLEEP_US, true); + pthread = new SrsThread("encoder", this, SRS_RTMP_ENCODER_SLEEP_US, true); pithy_print = new SrsPithyPrint(SRS_CONSTS_STAGE_ENCODER); } diff --git a/trunk/src/app/srs_app_forward.cpp b/trunk/src/app/srs_app_forward.cpp index 1c1dbb2d5a..52b62256b2 100644 --- a/trunk/src/app/srs_app_forward.cpp +++ b/trunk/src/app/srs_app_forward.cpp @@ -58,7 +58,7 @@ SrsForwarder::SrsForwarder(SrsSource* _source) kbps = new SrsKbps(); stream_id = 0; - pthread = new SrsThread(this, SRS_FORWARDER_SLEEP_US, true); + pthread = new SrsThread("forward", this, SRS_FORWARDER_SLEEP_US, true); queue = new SrsMessageQueue(); jitter = new SrsRtmpJitter(); diff --git a/trunk/src/app/srs_app_ingest.cpp b/trunk/src/app/srs_app_ingest.cpp index 7c5f6a1dee..6658dddc43 100644 --- a/trunk/src/app/srs_app_ingest.cpp +++ b/trunk/src/app/srs_app_ingest.cpp @@ -36,7 +36,7 @@ using namespace std; // when error, ingester sleep for a while and retry. // ingest never sleep a long time, for we must start the stream ASAP. -#define SRS_AUTO_INGESTER_SLEEP_US (int64_t)(6*100*1000LL) +#define SRS_AUTO_INGESTER_SLEEP_US (int64_t)(3*1000*1000LL) SrsIngesterFFMPEG::SrsIngesterFFMPEG(SrsFFMPEG* _ffmpeg, string _vhost, string _id) { @@ -54,7 +54,7 @@ SrsIngester::SrsIngester() { _srs_config->subscribe(this); - pthread = new SrsThread(this, SRS_AUTO_INGESTER_SLEEP_US, true); + pthread = new SrsThread("ingest", this, SRS_AUTO_INGESTER_SLEEP_US, true); pithy_print = new SrsPithyPrint(SRS_CONSTS_STAGE_INGESTER); } diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index 4a2b02723a..8ab29ebc35 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -40,7 +40,7 @@ SrsRecvThread::SrsRecvThread(ISrsMessageHandler* msg_handler, SrsRtmpServer* rtm timeout = timeout_ms; handler = msg_handler; rtmp = rtmp_sdk; - trd = new SrsThread(this, 0, true); + trd = new SrsThread("recv", this, 0, true); } SrsRecvThread::~SrsRecvThread() diff --git a/trunk/src/app/srs_app_server.cpp b/trunk/src/app/srs_app_server.cpp index bb1d29ed58..b1d4abd5c5 100644 --- a/trunk/src/app/srs_app_server.cpp +++ b/trunk/src/app/srs_app_server.cpp @@ -53,9 +53,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // system interval in ms, // all resolution times should be times togother, -// for example, system-time is 3(300ms), -// then rusage can be 3*x, for instance, 3*10=30(3s), -// the meminfo canbe 30*x, for instance, 30*2=60(6s) +// for example, system-interval is x=1s(1000ms), +// then rusage can be 3*x, for instance, 3*1=3s, +// the meminfo canbe 6*x, for instance, 6*1=6s, // for performance refine, @see: https://github.com/winlinvip/simple-rtmp-server/issues/194 // @remark, recomment to 1000ms. #define SRS_SYS_CYCLE_INTERVAL 1000 @@ -63,7 +63,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // update time interval: // SRS_SYS_CYCLE_INTERVAL * SRS_SYS_TIME_RESOLUTION_MS_TIMES // @see SYS_TIME_RESOLUTION_US -#define SRS_SYS_TIME_RESOLUTION_MS_TIMES 3 +#define SRS_SYS_TIME_RESOLUTION_MS_TIMES 1 // update rusage interval: // SRS_SYS_CYCLE_INTERVAL * SRS_SYS_RUSAGE_RESOLUTION_TIMES @@ -102,7 +102,7 @@ SrsListener::SrsListener(SrsServer* server, SrsListenerType type) _server = server; _type = type; - pthread = new SrsThread(this, 0, true); + pthread = new SrsThread("listen", this, 0, true); } SrsListener::~SrsListener() @@ -215,7 +215,7 @@ SrsSignalManager::SrsSignalManager(SrsServer* server) _server = server; sig_pipe[0] = sig_pipe[1] = -1; - pthread = new SrsThread(this, 0, true); + pthread = new SrsThread("signal", this, 0, true); signal_read_stfd = NULL; } @@ -714,7 +714,7 @@ int SrsServer::do_cycle() srs_trace("reload config success."); } - // update the cache time or rusage. + // update the cache time if ((i % SRS_SYS_TIME_RESOLUTION_MS_TIMES) == 0) { srs_info("update current time cache."); srs_update_system_time_ms(); diff --git a/trunk/src/app/srs_app_thread.cpp b/trunk/src/app/srs_app_thread.cpp index 857f5fe8e7..74dcfeeb41 100644 --- a/trunk/src/app/srs_app_thread.cpp +++ b/trunk/src/app/srs_app_thread.cpp @@ -54,8 +54,9 @@ void ISrsThreadHandler::on_thread_stop() { } -SrsThread::SrsThread(ISrsThreadHandler* thread_handler, int64_t interval_us, bool joinable) +SrsThread::SrsThread(const char* name, ISrsThreadHandler* thread_handler, int64_t interval_us, bool joinable) { + _name = name; handler = thread_handler; cycle_interval_us = interval_us; @@ -86,7 +87,7 @@ int SrsThread::start() int ret = ERROR_SUCCESS; if(tid) { - srs_info("thread already running."); + srs_info("thread %s already running.", _name); return ret; } @@ -141,7 +142,7 @@ void SrsThread::thread_cycle() int ret = ERROR_SUCCESS; _srs_context->generate_id(); - srs_info("thread cycle start"); + srs_info("thread %s cycle start", _name); _cid = _srs_context->get_id(); @@ -155,24 +156,24 @@ void SrsThread::thread_cycle() while (loop) { if ((ret = handler->on_before_cycle()) != ERROR_SUCCESS) { - srs_warn("thread on before cycle failed, ignored and retry, ret=%d", ret); + srs_warn("thread %s on before cycle failed, ignored and retry, ret=%d", _name, ret); goto failed; } - srs_info("thread on before cycle success"); + srs_info("thread %s on before cycle success"); if ((ret = handler->cycle()) != ERROR_SUCCESS) { if (!srs_is_client_gracefully_close(ret)) { - srs_warn("thread cycle failed, ignored and retry, ret=%d", ret); + srs_warn("thread cycle failed, ignored and retry, ret=%d", _name, ret); } goto failed; } - srs_info("thread cycle success"); + srs_info("thread %s cycle success", _name); if ((ret = handler->on_end_cycle()) != ERROR_SUCCESS) { - srs_warn("thread on end cycle failed, ignored and retry, ret=%d", ret); + srs_warn("thread %s on end cycle failed, ignored and retry, ret=%d", _name, ret); goto failed; } - srs_info("thread on end cycle success"); + srs_info("thread %s on end cycle success", _name); failed: if (!loop) { @@ -187,7 +188,7 @@ void SrsThread::thread_cycle() } handler->on_thread_stop(); - srs_info("thread cycle finished"); + srs_info("thread %s cycle finished", _name); } void* SrsThread::thread_fun(void* arg) diff --git a/trunk/src/app/srs_app_thread.hpp b/trunk/src/app/srs_app_thread.hpp index debfd216f5..347e6ceae1 100644 --- a/trunk/src/app/srs_app_thread.hpp +++ b/trunk/src/app/srs_app_thread.hpp @@ -88,23 +88,26 @@ class SrsThread bool loop; bool can_run; bool _joinable; + const char* _name; private: ISrsThreadHandler* handler; int64_t cycle_interval_us; public: /** * initialize the thread. + * @param name, human readable name for st debug. * @param thread_handler, the cycle handler for the thread. * @param interval_us, the sleep interval when cycle finished. * @param joinable, if joinable, other thread must stop the thread. * @remark if joinable, thread never quit itself, or memory leak. * @see: https://github.com/winlinvip/simple-rtmp-server/issues/78 + * @remark about st debug, see st-1.9/README, _st_iterate_threads_flag */ /** * TODO: FIXME: maybe all thread must be reap by others threads, * @see: https://github.com/winlinvip/simple-rtmp-server/issues/77 */ - SrsThread(ISrsThreadHandler* thread_handler, int64_t interval_us, bool joinable); + SrsThread(const char* name, ISrsThreadHandler* thread_handler, int64_t interval_us, bool joinable); virtual ~SrsThread(); public: /** From 6cbf732e1a2ed0a3473bb422207cae70a1fc3e55 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 3 Dec 2014 14:25:02 +0800 Subject: [PATCH 285/800] for bug #237, thread donot sleep when timeout is 0. --- trunk/src/app/srs_app_thread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trunk/src/app/srs_app_thread.cpp b/trunk/src/app/srs_app_thread.cpp index 74dcfeeb41..63110f1287 100644 --- a/trunk/src/app/srs_app_thread.cpp +++ b/trunk/src/app/srs_app_thread.cpp @@ -182,7 +182,7 @@ void SrsThread::thread_cycle() // to improve performance, donot sleep when interval is zero. // @see: https://github.com/winlinvip/simple-rtmp-server/issues/237 - if (cycle_interval_us > 0) { + if (cycle_interval_us != 0) { st_usleep(cycle_interval_us); } } From 2fdf99435044504990ce745897dd3334fa0d5e03 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 3 Dec 2014 14:27:36 +0800 Subject: [PATCH 286/800] for bug #241, use heap to alloc the socket buffer. --- trunk/src/kernel/srs_kernel_buffer.cpp | 10 ++++++++-- trunk/src/kernel/srs_kernel_buffer.hpp | 1 + 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/trunk/src/kernel/srs_kernel_buffer.cpp b/trunk/src/kernel/srs_kernel_buffer.cpp index d60690de4f..89c6f16720 100644 --- a/trunk/src/kernel/srs_kernel_buffer.cpp +++ b/trunk/src/kernel/srs_kernel_buffer.cpp @@ -26,6 +26,12 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +// 4KB=4096 +// 8KB=8192 +// 16KB=16384 +// 32KB=32768 +// 64KB=65536 +// @see https://github.com/winlinvip/simple-rtmp-server/issues/241 #define SOCKET_READ_SIZE 4096 ISrsBufferReader::ISrsBufferReader() @@ -38,10 +44,12 @@ ISrsBufferReader::~ISrsBufferReader() SrsBuffer::SrsBuffer() { + buffer = new char[SOCKET_READ_SIZE]; } SrsBuffer::~SrsBuffer() { + srs_freep(buffer); } int SrsBuffer::length() @@ -88,8 +96,6 @@ int SrsBuffer::grow(ISrsBufferReader* reader, int required_size) } while (length() < required_size) { - char buffer[SOCKET_READ_SIZE]; - ssize_t nread; if ((ret = reader->read(buffer, SOCKET_READ_SIZE, &nread)) != ERROR_SUCCESS) { return ret; diff --git a/trunk/src/kernel/srs_kernel_buffer.hpp b/trunk/src/kernel/srs_kernel_buffer.hpp index 93d3a56832..681b65a95d 100644 --- a/trunk/src/kernel/srs_kernel_buffer.hpp +++ b/trunk/src/kernel/srs_kernel_buffer.hpp @@ -53,6 +53,7 @@ class SrsBuffer { private: std::vector data; + char* buffer; public: SrsBuffer(); virtual ~SrsBuffer(); From ea0e837cd606aff0cd752083982091ea4776f8ad Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 3 Dec 2014 18:56:09 +0800 Subject: [PATCH 287/800] move the srs_kernel_buffer to srs_protocol_buffer. --- trunk/configure | 4 ++-- trunk/src/app/srs_app_hls.cpp | 2 +- .../srs_kernel_buffer.cpp => rtmp/srs_protocol_buffer.cpp} | 2 +- .../srs_kernel_buffer.hpp => rtmp/srs_protocol_buffer.hpp} | 2 +- trunk/src/rtmp/srs_protocol_io.hpp | 2 +- trunk/src/srs/srs.upp | 4 ++-- trunk/src/utest/srs_utest_kernel.hpp | 2 +- trunk/src/utest/srs_utest_protocol.hpp | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) rename trunk/src/{kernel/srs_kernel_buffer.cpp => rtmp/srs_protocol_buffer.cpp} (98%) rename trunk/src/{kernel/srs_kernel_buffer.hpp => rtmp/srs_protocol_buffer.hpp} (98%) diff --git a/trunk/configure b/trunk/configure index b29d709cb4..4b041e8a2d 100755 --- a/trunk/configure +++ b/trunk/configure @@ -363,7 +363,7 @@ CORE_OBJS="${MODULE_OBJS[@]}" MODULE_ID="KERNEL" MODULE_DEPENDS=("CORE") ModuleLibIncs=(${SRS_OBJS_DIR}) -MODULE_FILES=("srs_kernel_error" "srs_kernel_log" "srs_kernel_stream" "srs_kernel_buffer" +MODULE_FILES=("srs_kernel_error" "srs_kernel_log" "srs_kernel_stream" "srs_kernel_utility" "srs_kernel_flv" "srs_kernel_codec" "srs_kernel_file" "srs_kernel_consts") KERNEL_INCS="src/kernel"; MODULE_DIR=${KERNEL_INCS} . auto/modules.sh @@ -374,7 +374,7 @@ MODULE_ID="RTMP" MODULE_DEPENDS=("CORE" "KERNEL") ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSSLRoot}) MODULE_FILES=("srs_protocol_amf0" "srs_protocol_io" "srs_protocol_stack" "srs_protocol_rtmp" - "srs_protocol_handshake" "srs_protocol_utility" "srs_protocol_msg_array") + "srs_protocol_handshake" "srs_protocol_utility" "srs_protocol_msg_array" "srs_protocol_buffer") RTMP_INCS="src/rtmp"; MODULE_DIR=${RTMP_INCS} . auto/modules.sh RTMP_OBJS="${MODULE_OBJS[@]}" # diff --git a/trunk/src/app/srs_app_hls.cpp b/trunk/src/app/srs_app_hls.cpp index 8cd71c8e14..4915514e61 100644 --- a/trunk/src/app/srs_app_hls.cpp +++ b/trunk/src/app/srs_app_hls.cpp @@ -68,7 +68,7 @@ using namespace std; #include #include #include -#include +#include // max PES packets size to flush the video. #define SRS_AUTO_HLS_AUDIO_CACHE_SIZE 1024 * 1024 diff --git a/trunk/src/kernel/srs_kernel_buffer.cpp b/trunk/src/rtmp/srs_protocol_buffer.cpp similarity index 98% rename from trunk/src/kernel/srs_kernel_buffer.cpp rename to trunk/src/rtmp/srs_protocol_buffer.cpp index 89c6f16720..b371e2f043 100644 --- a/trunk/src/kernel/srs_kernel_buffer.cpp +++ b/trunk/src/rtmp/srs_protocol_buffer.cpp @@ -21,7 +21,7 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include +#include #include #include diff --git a/trunk/src/kernel/srs_kernel_buffer.hpp b/trunk/src/rtmp/srs_protocol_buffer.hpp similarity index 98% rename from trunk/src/kernel/srs_kernel_buffer.hpp rename to trunk/src/rtmp/srs_protocol_buffer.hpp index 681b65a95d..ff6ac02c7d 100644 --- a/trunk/src/kernel/srs_kernel_buffer.hpp +++ b/trunk/src/rtmp/srs_protocol_buffer.hpp @@ -25,7 +25,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define SRS_KERNEL_BUFFER_HPP /* -#include +#include */ #include diff --git a/trunk/src/rtmp/srs_protocol_io.hpp b/trunk/src/rtmp/srs_protocol_io.hpp index bc816309f4..f4b2d46039 100644 --- a/trunk/src/rtmp/srs_protocol_io.hpp +++ b/trunk/src/rtmp/srs_protocol_io.hpp @@ -35,7 +35,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #endif -#include +#include /** * the system io reader/writer architecture: diff --git a/trunk/src/srs/srs.upp b/trunk/src/srs/srs.upp index 8e3d6ff113..3e0b8a62a9 100755 --- a/trunk/src/srs/srs.upp +++ b/trunk/src/srs/srs.upp @@ -16,8 +16,6 @@ file ..\core\srs_core_autofree.hpp, ..\core\srs_core_autofree.cpp, kernel readonly separator, - ..\kernel\srs_kernel_buffer.hpp, - ..\kernel\srs_kernel_buffer.cpp, ..\kernel\srs_kernel_codec.hpp, ..\kernel\srs_kernel_codec.cpp, ..\kernel\srs_kernel_consts.hpp, @@ -37,6 +35,8 @@ file rtmp-protocol readonly separator, ..\rtmp\srs_protocol_amf0.hpp, ..\rtmp\srs_protocol_amf0.cpp, + ..\rtmp\srs_protocol_buffer.hpp, + ..\rtmp\srs_protocol_buffer.cpp, ..\rtmp\srs_protocol_handshake.hpp, ..\rtmp\srs_protocol_handshake.cpp, ..\rtmp\srs_protocol_io.hpp, diff --git a/trunk/src/utest/srs_utest_kernel.hpp b/trunk/src/utest/srs_utest_kernel.hpp index 44b7fd6765..11d220e95d 100644 --- a/trunk/src/utest/srs_utest_kernel.hpp +++ b/trunk/src/utest/srs_utest_kernel.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include -#include +#include class MockBufferReader: public ISrsBufferReader { diff --git a/trunk/src/utest/srs_utest_protocol.hpp b/trunk/src/utest/srs_utest_protocol.hpp index 8ffb69112a..b97d8fbe9c 100644 --- a/trunk/src/utest/srs_utest_protocol.hpp +++ b/trunk/src/utest/srs_utest_protocol.hpp @@ -34,7 +34,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include -#include +#include #ifdef SRS_AUTO_SSL using namespace _srs_internal; From adf95d239eb8d85d24dfd7e72e0bb1fa327492ac Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 3 Dec 2014 19:09:59 +0800 Subject: [PATCH 288/800] refine code, move the ISrsBufferReader to srs_protocol_io --- trunk/src/app/srs_app_http.cpp | 1 + trunk/src/rtmp/srs_protocol_buffer.cpp | 8 -------- trunk/src/rtmp/srs_protocol_buffer.hpp | 17 +++-------------- trunk/src/rtmp/srs_protocol_io.cpp | 8 ++++++++ trunk/src/rtmp/srs_protocol_io.hpp | 15 +++++++++++++-- trunk/src/rtmp/srs_protocol_stack.cpp | 1 + 6 files changed, 26 insertions(+), 24 deletions(-) diff --git a/trunk/src/app/srs_app_http.cpp b/trunk/src/app/srs_app_http.cpp index a1768130c5..9494b431dc 100644 --- a/trunk/src/app/srs_app_http.cpp +++ b/trunk/src/app/srs_app_http.cpp @@ -36,6 +36,7 @@ using namespace std; #include #include #include +#include #define SRS_DEFAULT_HTTP_PORT 80 diff --git a/trunk/src/rtmp/srs_protocol_buffer.cpp b/trunk/src/rtmp/srs_protocol_buffer.cpp index b371e2f043..b3e15bf3bc 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.cpp +++ b/trunk/src/rtmp/srs_protocol_buffer.cpp @@ -34,14 +34,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 #define SOCKET_READ_SIZE 4096 -ISrsBufferReader::ISrsBufferReader() -{ -} - -ISrsBufferReader::~ISrsBufferReader() -{ -} - SrsBuffer::SrsBuffer() { buffer = new char[SOCKET_READ_SIZE]; diff --git a/trunk/src/rtmp/srs_protocol_buffer.hpp b/trunk/src/rtmp/srs_protocol_buffer.hpp index ff6ac02c7d..13622d96e3 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.hpp +++ b/trunk/src/rtmp/srs_protocol_buffer.hpp @@ -21,8 +21,8 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#ifndef SRS_KERNEL_BUFFER_HPP -#define SRS_KERNEL_BUFFER_HPP +#ifndef SRS_PROTOCOL_BUFFER_HPP +#define SRS_PROTOCOL_BUFFER_HPP /* #include @@ -32,18 +32,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include -/** -* the reader for the buffer to read from whatever channel. -*/ -class ISrsBufferReader -{ -public: - ISrsBufferReader(); - virtual ~ISrsBufferReader(); -// for protocol/amf0/msg-codec -public: - virtual int read(void* buf, size_t size, ssize_t* nread) = 0; -}; +#include /** * the buffer provices bytes cache for protocol. generally, diff --git a/trunk/src/rtmp/srs_protocol_io.cpp b/trunk/src/rtmp/srs_protocol_io.cpp index a0dd8999c4..723e9d995e 100644 --- a/trunk/src/rtmp/srs_protocol_io.cpp +++ b/trunk/src/rtmp/srs_protocol_io.cpp @@ -23,6 +23,14 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include +ISrsBufferReader::ISrsBufferReader() +{ +} + +ISrsBufferReader::~ISrsBufferReader() +{ +} + ISrsBufferWriter::ISrsBufferWriter() { } diff --git a/trunk/src/rtmp/srs_protocol_io.hpp b/trunk/src/rtmp/srs_protocol_io.hpp index f4b2d46039..41215fd56b 100644 --- a/trunk/src/rtmp/srs_protocol_io.hpp +++ b/trunk/src/rtmp/srs_protocol_io.hpp @@ -35,8 +35,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #endif -#include - /** * the system io reader/writer architecture: +---------------+ +--------------------+ +---------------+ @@ -62,6 +60,19 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +----------------------------------+ */ +/** +* the reader for the buffer to read from whatever channel. +*/ +class ISrsBufferReader +{ +public: + ISrsBufferReader(); + virtual ~ISrsBufferReader(); +// for protocol/amf0/msg-codec +public: + virtual int read(void* buf, size_t size, ssize_t* nread) = 0; +}; + /** * the writer for the buffer to write to whatever channel. */ diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp index 6f79e202b6..94d75ced78 100644 --- a/trunk/src/rtmp/srs_protocol_stack.cpp +++ b/trunk/src/rtmp/srs_protocol_stack.cpp @@ -28,6 +28,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #include +#include #include using namespace std; From f35ec2155b1408d528a9f37da7904c9625186bcf Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 3 Dec 2014 19:27:27 +0800 Subject: [PATCH 289/800] for bug #241, support merged read. 2.0.48 --- trunk/src/app/srs_app_recv_thread.cpp | 57 ++++++++++++++++++++------ trunk/src/app/srs_app_recv_thread.hpp | 10 +++-- trunk/src/core/srs_core.hpp | 2 +- trunk/src/rtmp/srs_protocol_buffer.cpp | 33 +++++++++++---- trunk/src/rtmp/srs_protocol_buffer.hpp | 42 +++++++++++++++++++ trunk/src/rtmp/srs_protocol_rtmp.cpp | 5 +++ trunk/src/rtmp/srs_protocol_rtmp.hpp | 10 +++++ trunk/src/rtmp/srs_protocol_stack.cpp | 5 +++ trunk/src/rtmp/srs_protocol_stack.hpp | 11 +++++ 9 files changed, 152 insertions(+), 23 deletions(-) diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index 8ab29ebc35..e4bfaa8971 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -26,6 +26,14 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #include +#include + +// when we read from socket less than this value, +// sleep a while to merge read. +// @see https://github.com/winlinvip/simple-rtmp-server/issues/241 +#define SRS_MERGED_READ_SIZE (SOCKET_READ_SIZE / 10) +// the time to sleep to merge read, to read more bytes. +#define SRS_MERGED_READ_US (300 * 1000) ISrsMessageHandler::ISrsMessageHandler() { @@ -271,6 +279,30 @@ void SrsPublishRecvThread::stop() trd.stop(); } +void SrsPublishRecvThread::on_thread_start() +{ + // we donot set the auto response to false, + // for the main thread never send message. + + // enable the merge read + // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 + rtmp->set_merge_read(true, this); +} + +void SrsPublishRecvThread::on_thread_stop() +{ + // we donot set the auto response to true, + // for we donot set to false yet. + + // when thread stop, signal the conn thread which wait. + // @see https://github.com/winlinvip/simple-rtmp-server/issues/244 + st_cond_signal(error); + + // disable the merge read + // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 + rtmp->set_merge_read(false, NULL); +} + bool SrsPublishRecvThread::can_handle() { // publish thread always can handle message. @@ -302,18 +334,19 @@ void SrsPublishRecvThread::on_recv_error(int ret) st_cond_signal(error); } -void SrsPublishRecvThread::on_thread_start() -{ - // we donot set the auto response to false, - // for the main thread never send message. -} - -void SrsPublishRecvThread::on_thread_stop() +void SrsPublishRecvThread::on_read(ssize_t nread) { - // we donot set the auto response to true, - // for we donot set to false yet. + if (nread < 0) { + return; + } - // when thread stop, signal the conn thread which wait. - // @see https://github.com/winlinvip/simple-rtmp-server/issues/244 - st_cond_signal(error); + /** + * to improve read performance, merge some packets then read, + * when it on and read small bytes, we sleep to wait more data., + * that is, we merge some data to read together. + * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 + */ + if (nread < SRS_MERGED_READ_SIZE) { + st_usleep(SRS_MERGED_READ_US); + } } diff --git a/trunk/src/app/srs_app_recv_thread.hpp b/trunk/src/app/srs_app_recv_thread.hpp index 5a579b6dcf..f723374fd5 100644 --- a/trunk/src/app/srs_app_recv_thread.hpp +++ b/trunk/src/app/srs_app_recv_thread.hpp @@ -33,6 +33,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +#include class SrsRtmpServer; class SrsMessage; @@ -132,7 +133,7 @@ class SrsQueueRecvThread : public ISrsMessageHandler * the publish recv thread got message and callback the source method to process message. * @see: https://github.com/winlinvip/simple-rtmp-server/issues/237 */ -class SrsPublishRecvThread : public ISrsMessageHandler +class SrsPublishRecvThread : virtual public ISrsMessageHandler, virtual public IMergeReadHandler { private: SrsRecvThread trd; @@ -163,13 +164,16 @@ class SrsPublishRecvThread : public ISrsMessageHandler public: virtual int start(); virtual void stop(); + virtual void on_thread_start(); + virtual void on_thread_stop(); +// interface ISrsMessageHandler public: virtual bool can_handle(); virtual int handle(SrsMessage* msg); virtual void on_recv_error(int ret); +// interface IMergeReadHandler public: - virtual void on_thread_start(); - virtual void on_thread_stop(); + virtual void on_read(ssize_t nread); }; #endif diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 1bea57bce1..df7b42d152 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 47 +#define VERSION_REVISION 48 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/rtmp/srs_protocol_buffer.cpp b/trunk/src/rtmp/srs_protocol_buffer.cpp index b3e15bf3bc..1778b4efd0 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.cpp +++ b/trunk/src/rtmp/srs_protocol_buffer.cpp @@ -26,16 +26,19 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include -// 4KB=4096 -// 8KB=8192 -// 16KB=16384 -// 32KB=32768 -// 64KB=65536 -// @see https://github.com/winlinvip/simple-rtmp-server/issues/241 -#define SOCKET_READ_SIZE 4096 +IMergeReadHandler::IMergeReadHandler() +{ +} + +IMergeReadHandler::~IMergeReadHandler() +{ +} SrsBuffer::SrsBuffer() { + merged_read = false; + _handler = NULL; + buffer = new char[SOCKET_READ_SIZE]; } @@ -93,6 +96,16 @@ int SrsBuffer::grow(ISrsBufferReader* reader, int required_size) return ret; } + /** + * to improve read performance, merge some packets then read, + * when it on and read small bytes, we sleep to wait more data., + * that is, we merge some data to read together. + * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 + */ + if (merged_read && _handler) { + _handler->on_read(nread); + } + srs_assert((int)nread > 0); append(buffer, (int)nread); } @@ -100,4 +113,10 @@ int SrsBuffer::grow(ISrsBufferReader* reader, int required_size) return ret; } +void SrsBuffer::set_merge_read(bool v, IMergeReadHandler* handler) +{ + merged_read = v; + _handler = handler; +} + diff --git a/trunk/src/rtmp/srs_protocol_buffer.hpp b/trunk/src/rtmp/srs_protocol_buffer.hpp index 13622d96e3..b03f8ec4b1 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.hpp +++ b/trunk/src/rtmp/srs_protocol_buffer.hpp @@ -34,6 +34,34 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include +// 4KB=4096 +// 8KB=8192 +// 16KB=16384 +// 32KB=32768 +// 64KB=65536 +// @see https://github.com/winlinvip/simple-rtmp-server/issues/241 +#define SOCKET_READ_SIZE 4096 + +/** +* to improve read performance, merge some packets then read, +* when it on and read small bytes, we sleep to wait more data., +* that is, we merge some data to read together. +* @see https://github.com/winlinvip/simple-rtmp-server/issues/241 +*/ +class IMergeReadHandler +{ +public: + IMergeReadHandler(); + virtual ~IMergeReadHandler(); +public: + /** + * when read from channel, notice the merge handler to sleep for + * some small bytes. + * @remark, it only for server-side, client srs-librtmp just ignore. + */ + virtual void on_read(ssize_t nread) = 0; +}; + /** * the buffer provices bytes cache for protocol. generally, * protocol recv data from socket, put into buffer, decode to RTMP message. @@ -41,6 +69,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. class SrsBuffer { private: + // the merged handler + bool merged_read; + IMergeReadHandler* _handler; + // data and socket buffer std::vector data; char* buffer; public: @@ -79,6 +111,16 @@ class SrsBuffer * @remark, we actually maybe read more than required_size, maybe 4k for example. */ virtual int grow(ISrsBufferReader* reader, int required_size); +public: + /** + * to improve read performance, merge some packets then read, + * when it on and read small bytes, we sleep to wait more data., + * that is, we merge some data to read together. + * @param v true to ename merged read. + * @param handler the handler when merge read is enabled. + * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 + */ + virtual void set_merge_read(bool v, IMergeReadHandler* handler); }; #endif diff --git a/trunk/src/rtmp/srs_protocol_rtmp.cpp b/trunk/src/rtmp/srs_protocol_rtmp.cpp index 39ccb21319..80183dbbdc 100644 --- a/trunk/src/rtmp/srs_protocol_rtmp.cpp +++ b/trunk/src/rtmp/srs_protocol_rtmp.cpp @@ -745,6 +745,11 @@ void SrsRtmpServer::set_auto_response(bool v) protocol->set_auto_response(v); } +void SrsRtmpServer::set_merge_read(bool v, IMergeReadHandler* handler) +{ + protocol->set_merge_read(v, handler); +} + void SrsRtmpServer::set_recv_timeout(int64_t timeout_us) { protocol->set_recv_timeout(timeout_us); diff --git a/trunk/src/rtmp/srs_protocol_rtmp.hpp b/trunk/src/rtmp/srs_protocol_rtmp.hpp index 9846d76191..46eb92275d 100644 --- a/trunk/src/rtmp/srs_protocol_rtmp.hpp +++ b/trunk/src/rtmp/srs_protocol_rtmp.hpp @@ -46,6 +46,7 @@ class SrsPlayPacket; class SrsMessage; class SrsPacket; class SrsAmf0Object; +class IMergeReadHandler; /** * the original request from client. @@ -343,6 +344,15 @@ class SrsRtmpServer */ virtual void set_auto_response(bool v); /** + * to improve read performance, merge some packets then read, + * when it on and read small bytes, we sleep to wait more data., + * that is, we merge some data to read together. + * @param v true to ename merged read. + * @param handler the handler when merge read is enabled. + * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 + */ + virtual void set_merge_read(bool v, IMergeReadHandler* handler); + /** * set/get the recv timeout in us. * if timeout, recv/send message return ERROR_SOCKET_TIMEOUT. */ diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp index 94d75ced78..eb030f0073 100644 --- a/trunk/src/rtmp/srs_protocol_stack.cpp +++ b/trunk/src/rtmp/srs_protocol_stack.cpp @@ -479,6 +479,11 @@ int SrsProtocol::manual_response_flush() return ret; } +void SrsProtocol::set_merge_read(bool v, IMergeReadHandler* handler) +{ + in_buffer->set_merge_read(v, handler); +} + void SrsProtocol::set_recv_timeout(int64_t timeout_us) { return skt->set_recv_timeout(timeout_us); diff --git a/trunk/src/rtmp/srs_protocol_stack.hpp b/trunk/src/rtmp/srs_protocol_stack.hpp index d9ba0ee847..0f7dd69d0c 100644 --- a/trunk/src/rtmp/srs_protocol_stack.hpp +++ b/trunk/src/rtmp/srs_protocol_stack.hpp @@ -53,6 +53,7 @@ class SrsMessageHeader; class SrsMessage; class SrsChunkStream; class SrsSharedPtrMessage; +class IMergeReadHandler; /** * 4.1. Message Header @@ -269,6 +270,16 @@ class SrsProtocol * @see the auto_response_when_recv and manual_response_queue. */ virtual int manual_response_flush(); +public: + /** + * to improve read performance, merge some packets then read, + * when it on and read small bytes, we sleep to wait more data., + * that is, we merge some data to read together. + * @param v true to ename merged read. + * @param handler the handler when merge read is enabled. + * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 + */ + virtual void set_merge_read(bool v, IMergeReadHandler* handler); public: /** * set/get the recv timeout in us. From 19b1d750aa356a10671e032f0e17b9bd0af9de84 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 3 Dec 2014 19:51:04 +0800 Subject: [PATCH 290/800] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index facaad4259..83d8012d43 100755 --- a/README.md +++ b/README.md @@ -731,6 +731,7 @@ The publish benchmark by [st-load](https://github.com/winlinvip/st-load): * 2014-12-03, SRS 2.0.12, 1k(1200) publishers, 96%CPU, 43MB. * 2014-12-03, SRS 2.0.47, 1k(1200) publishers, 84%CPU, 76MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/787ab674e38734ea8e0678101614fdcd84645dc8) * 2014-12-03, SRS 2.0.47, 1k(1400) publishers, 95%CPU, 140MB. +* 2014-12-03, SRS 2.0.48, 1k(1400) publishers, 95%CPU, 140MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/f35ec2155b1408d528a9f37da7904c9625186bcf) ## Architecture From aee00877df9f4bb2c044dbc4151625f9c0dc8e52 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 3 Dec 2014 21:46:47 +0800 Subject: [PATCH 291/800] for bug #241, change buffer size when chunk size changed. --- trunk/src/app/srs_app_recv_thread.cpp | 6 ++--- trunk/src/app/srs_app_recv_thread.hpp | 2 +- trunk/src/qt/srs/srs-qt.pro.user | 1 - trunk/src/rtmp/srs_protocol_buffer.cpp | 32 +++++++++++++++++++++++--- trunk/src/rtmp/srs_protocol_buffer.hpp | 22 ++++++++++-------- trunk/src/rtmp/srs_protocol_stack.cpp | 9 +++++--- 6 files changed, 52 insertions(+), 20 deletions(-) diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index e4bfaa8971..dc1a26ea6e 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // when we read from socket less than this value, // sleep a while to merge read. // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 -#define SRS_MERGED_READ_SIZE (SOCKET_READ_SIZE / 10) +#define SRS_MERGED_READ_SIZE(buffer) (buffer / 10) // the time to sleep to merge read, to read more bytes. #define SRS_MERGED_READ_US (300 * 1000) @@ -334,7 +334,7 @@ void SrsPublishRecvThread::on_recv_error(int ret) st_cond_signal(error); } -void SrsPublishRecvThread::on_read(ssize_t nread) +void SrsPublishRecvThread::on_read(int nb_buffer, ssize_t nread) { if (nread < 0) { return; @@ -346,7 +346,7 @@ void SrsPublishRecvThread::on_read(ssize_t nread) * that is, we merge some data to read together. * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 */ - if (nread < SRS_MERGED_READ_SIZE) { + if (nread < SRS_MERGED_READ_SIZE(nb_buffer)) { st_usleep(SRS_MERGED_READ_US); } } diff --git a/trunk/src/app/srs_app_recv_thread.hpp b/trunk/src/app/srs_app_recv_thread.hpp index f723374fd5..534a051cc2 100644 --- a/trunk/src/app/srs_app_recv_thread.hpp +++ b/trunk/src/app/srs_app_recv_thread.hpp @@ -173,7 +173,7 @@ class SrsPublishRecvThread : virtual public ISrsMessageHandler, virtual public I virtual void on_recv_error(int ret); // interface IMergeReadHandler public: - virtual void on_read(ssize_t nread); + virtual void on_read(int nb_buffer, ssize_t nread); }; #endif diff --git a/trunk/src/qt/srs/srs-qt.pro.user b/trunk/src/qt/srs/srs-qt.pro.user index fb16e8d123..8672380b5b 100644 --- a/trunk/src/qt/srs/srs-qt.pro.user +++ b/trunk/src/qt/srs/srs-qt.pro.user @@ -1,6 +1,5 @@ - ProjectExplorer.Project.ActiveTarget diff --git a/trunk/src/rtmp/srs_protocol_buffer.cpp b/trunk/src/rtmp/srs_protocol_buffer.cpp index 1778b4efd0..f4d949cece 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.cpp +++ b/trunk/src/rtmp/srs_protocol_buffer.cpp @@ -26,6 +26,14 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +// 4KB=4096 +// 8KB=8192 +// 16KB=16384 +// 32KB=32768 +// 64KB=65536 +// @see https://github.com/winlinvip/simple-rtmp-server/issues/241 +#define SOCKET_READ_SIZE 4096 + IMergeReadHandler::IMergeReadHandler() { } @@ -39,7 +47,8 @@ SrsBuffer::SrsBuffer() merged_read = false; _handler = NULL; - buffer = new char[SOCKET_READ_SIZE]; + nb_buffer = SOCKET_READ_SIZE; + buffer = new char[nb_buffer]; } SrsBuffer::~SrsBuffer() @@ -92,7 +101,7 @@ int SrsBuffer::grow(ISrsBufferReader* reader, int required_size) while (length() < required_size) { ssize_t nread; - if ((ret = reader->read(buffer, SOCKET_READ_SIZE, &nread)) != ERROR_SUCCESS) { + if ((ret = reader->read(buffer, nb_buffer, &nread)) != ERROR_SUCCESS) { return ret; } @@ -103,7 +112,7 @@ int SrsBuffer::grow(ISrsBufferReader* reader, int required_size) * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 */ if (merged_read && _handler) { - _handler->on_read(nread); + _handler->on_read(nb_buffer, nread); } srs_assert((int)nread > 0); @@ -119,4 +128,21 @@ void SrsBuffer::set_merge_read(bool v, IMergeReadHandler* handler) _handler = handler; } +void SrsBuffer::on_chunk_size(int32_t chunk_size) +{ + if (nb_buffer >= chunk_size) { + return; + } + + srs_freep(buffer); + + nb_buffer = chunk_size; + buffer = new char[nb_buffer]; +} + +int SrsBuffer::buffer_size() +{ + return nb_buffer; +} + diff --git a/trunk/src/rtmp/srs_protocol_buffer.hpp b/trunk/src/rtmp/srs_protocol_buffer.hpp index b03f8ec4b1..35f9c4615b 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.hpp +++ b/trunk/src/rtmp/srs_protocol_buffer.hpp @@ -34,14 +34,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include -// 4KB=4096 -// 8KB=8192 -// 16KB=16384 -// 32KB=32768 -// 64KB=65536 -// @see https://github.com/winlinvip/simple-rtmp-server/issues/241 -#define SOCKET_READ_SIZE 4096 - /** * to improve read performance, merge some packets then read, * when it on and read small bytes, we sleep to wait more data., @@ -59,7 +51,7 @@ class IMergeReadHandler * some small bytes. * @remark, it only for server-side, client srs-librtmp just ignore. */ - virtual void on_read(ssize_t nread) = 0; + virtual void on_read(int nb_buffer, ssize_t nread) = 0; }; /** @@ -75,6 +67,7 @@ class SrsBuffer // data and socket buffer std::vector data; char* buffer; + int nb_buffer; public: SrsBuffer(); virtual ~SrsBuffer(); @@ -121,6 +114,17 @@ class SrsBuffer * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 */ virtual void set_merge_read(bool v, IMergeReadHandler* handler); +public: + /** + * when chunk size changed, the buffer should change the buffer also. + * to keep the socket buffer size always greater than chunk size. + * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 + */ + virtual void on_chunk_size(int32_t chunk_size); + /** + * get the size of socket buffer to read. + */ + virtual int buffer_size(); }; #endif diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp index eb030f0073..a58bc7c6ed 100644 --- a/trunk/src/rtmp/srs_protocol_stack.cpp +++ b/trunk/src/rtmp/srs_protocol_stack.cpp @@ -1688,10 +1688,13 @@ int SrsProtocol::on_recv_message(SrsMessage* msg) pkt->chunk_size, SRS_CONSTS_RTMP_MIN_CHUNK_SIZE, SRS_CONSTS_RTMP_MAX_CHUNK_SIZE); } - + + int sock_buffer = in_buffer->buffer_size(); in_chunk_size = pkt->chunk_size; - - srs_trace("input chunk size to %d", pkt->chunk_size); + in_buffer->on_chunk_size(pkt->chunk_size); + srs_trace("input chunk size to %d, sock buf %d=>%d", + pkt->chunk_size, sock_buffer, in_buffer->buffer_size()); + break; } case RTMP_MSG_UserControlMessage: { From 850946bb13ce68a1183b549bbce6a0324253c656 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 3 Dec 2014 22:39:25 +0800 Subject: [PATCH 292/800] for bug #241, calc the small and sleep for merged read. --- trunk/src/app/srs_app_recv_thread.cpp | 64 ++++++++++++++++++++++---- trunk/src/app/srs_app_recv_thread.hpp | 10 +++- trunk/src/app/srs_app_rtmp_conn.cpp | 4 +- trunk/src/rtmp/srs_protocol_buffer.cpp | 23 ++++++--- trunk/src/rtmp/srs_protocol_buffer.hpp | 13 ++++-- trunk/src/rtmp/srs_protocol_rtmp.cpp | 4 +- trunk/src/rtmp/srs_protocol_rtmp.hpp | 3 +- trunk/src/rtmp/srs_protocol_stack.cpp | 4 +- trunk/src/rtmp/srs_protocol_stack.hpp | 3 +- 9 files changed, 99 insertions(+), 29 deletions(-) diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index dc1a26ea6e..6550c7defc 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -27,13 +27,21 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #include +#include // when we read from socket less than this value, // sleep a while to merge read. // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 -#define SRS_MERGED_READ_SIZE(buffer) (buffer / 10) -// the time to sleep to merge read, to read more bytes. -#define SRS_MERGED_READ_US (300 * 1000) +// use the bitrate in kbps to calc the max sleep time. +#define SRS_MR_MAX_BITRATE_KBPS 10000 +#define SRS_MR_AVERAGE_BITRATE_KBPS 1000 +#define SRS_MR_MIN_BITRATE_KBPS 64 +// the max sleep time in ms +#define SRS_MR_MAX_SLEEP_MS 3000 +// the max small bytes to group +#define SRS_MR_SMALL_BYTES 64 +// the percent of buffer to set as small bytes +#define SRS_MR_SMALL_PERCENT 100 ISrsMessageHandler::ISrsMessageHandler() { @@ -226,7 +234,7 @@ void SrsQueueRecvThread::on_thread_stop() } SrsPublishRecvThread::SrsPublishRecvThread( - SrsRtmpServer* rtmp_sdk, int timeout_ms, + SrsRtmpServer* rtmp_sdk, int fd, int timeout_ms, SrsRtmpConn* conn, SrsSource* source, bool is_fmle, bool is_edge ): trd(this, rtmp_sdk, timeout_ms) { @@ -239,6 +247,10 @@ SrsPublishRecvThread::SrsPublishRecvThread( recv_error_code = ERROR_SUCCESS; _nb_msgs = 0; error = st_cond_new(); + + mr_fd = fd; + mr_small_bytes = 0; + mr_sleep_ms = 0; } SrsPublishRecvThread::~SrsPublishRecvThread() @@ -284,9 +296,17 @@ void SrsPublishRecvThread::on_thread_start() // we donot set the auto response to false, // for the main thread never send message. + // 128KB recv buffer. + int nb_rbuf = 128 * 1024; + socklen_t sock_buf_size = sizeof(int); + if (setsockopt(mr_fd, SOL_SOCKET, SO_RCVBUF, &nb_rbuf, sock_buf_size) < 0) { + srs_warn("set sock SO_RCVBUF=%d failed.", nb_rbuf); + } + getsockopt(mr_fd, SOL_SOCKET, SO_RCVBUF, &nb_rbuf, &sock_buf_size); + // enable the merge read // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 - rtmp->set_merge_read(true, this); + rtmp->set_merge_read(true, nb_rbuf, this); } void SrsPublishRecvThread::on_thread_stop() @@ -300,7 +320,7 @@ void SrsPublishRecvThread::on_thread_stop() // disable the merge read // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 - rtmp->set_merge_read(false, NULL); + rtmp->set_merge_read(false, 0, NULL); } bool SrsPublishRecvThread::can_handle() @@ -334,9 +354,9 @@ void SrsPublishRecvThread::on_recv_error(int ret) st_cond_signal(error); } -void SrsPublishRecvThread::on_read(int nb_buffer, ssize_t nread) +void SrsPublishRecvThread::on_read(ssize_t nread) { - if (nread < 0) { + if (nread < 0 || mr_sleep_ms <= 0) { return; } @@ -346,7 +366,31 @@ void SrsPublishRecvThread::on_read(int nb_buffer, ssize_t nread) * that is, we merge some data to read together. * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 */ - if (nread < SRS_MERGED_READ_SIZE(nb_buffer)) { - st_usleep(SRS_MERGED_READ_US); + if (nread < mr_small_bytes) { + st_usleep(mr_sleep_ms * 1000); } } + +void SrsPublishRecvThread::on_buffer_change(int nb_buffer) +{ + // set percent. + mr_small_bytes = (int)(nb_buffer / SRS_MR_SMALL_PERCENT); + // select the smaller + mr_small_bytes = srs_min(mr_small_bytes, SRS_MR_SMALL_BYTES); + + // the recv sleep is [buffer / max_kbps, buffer / min_kbps] + // for example, buffer is 256KB, max kbps is 10Mbps, min kbps is 10Kbps, + // the buffer is 256KB*8=2048Kb, which can provides sleep time in + // min: 2038Kb/10Mbps=2038Kb/10Kbpms=203.8ms + // max: 2038Kb/10Kbps=203.8s + // sleep = Xb * 8 / (N * 1000 b / 1000 ms) = (X * 8 / N) ms + // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 + int min_sleep = (int)(nb_buffer * 8.0 / SRS_MR_MAX_BITRATE_KBPS); + int average_sleep = (int)(nb_buffer * 8.0 / SRS_MR_AVERAGE_BITRATE_KBPS); + int max_sleep = (int)(nb_buffer * 8.0 / SRS_MR_MIN_BITRATE_KBPS); + // 80% min, 16% average, 4% max. + mr_sleep_ms = (int)(min_sleep * 0.8 + average_sleep * 0.16 + max_sleep * 0.04); + mr_sleep_ms = srs_min(mr_sleep_ms, SRS_MR_MAX_SLEEP_MS); + + srs_trace("merged read, buffer=%d, small=%d, sleep=%d", nb_buffer, mr_small_bytes, mr_sleep_ms); +} diff --git a/trunk/src/app/srs_app_recv_thread.hpp b/trunk/src/app/srs_app_recv_thread.hpp index 534a051cc2..7b947562c3 100644 --- a/trunk/src/app/srs_app_recv_thread.hpp +++ b/trunk/src/app/srs_app_recv_thread.hpp @@ -140,6 +140,11 @@ class SrsPublishRecvThread : virtual public ISrsMessageHandler, virtual public I SrsRtmpServer* rtmp; // the msgs already got. int64_t _nb_msgs; + // for mr(merged read), + // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 + int mr_fd; + int mr_small_bytes; + int mr_sleep_ms; // the recv thread error code. int recv_error_code; SrsRtmpConn* _conn; @@ -151,7 +156,7 @@ class SrsPublishRecvThread : virtual public ISrsMessageHandler, virtual public I // @see https://github.com/winlinvip/simple-rtmp-server/issues/244 st_cond_t error; public: - SrsPublishRecvThread(SrsRtmpServer* rtmp_sdk, int timeout_ms, + SrsPublishRecvThread(SrsRtmpServer* rtmp_sdk, int fd, int timeout_ms, SrsRtmpConn* conn, SrsSource* source, bool is_fmle, bool is_edge); virtual ~SrsPublishRecvThread(); public: @@ -173,7 +178,8 @@ class SrsPublishRecvThread : virtual public ISrsMessageHandler, virtual public I virtual void on_recv_error(int ret); // interface IMergeReadHandler public: - virtual void on_read(int nb_buffer, ssize_t nread); + virtual void on_read(ssize_t nread); + virtual void on_buffer_change(int nb_buffer); }; #endif diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index 33e0bb5180..3ba9168792 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -659,7 +659,7 @@ int SrsRtmpConn::fmle_publishing(SrsSource* source) // use isolate thread to recv, // @see: https://github.com/winlinvip/simple-rtmp-server/issues/237 - SrsPublishRecvThread trd(rtmp, + SrsPublishRecvThread trd(rtmp, st_netfd_fileno(stfd), SRS_CONSTS_RTMP_RECV_TIMEOUT_US / 1000, this, source, true, vhost_is_edge); @@ -695,7 +695,7 @@ int SrsRtmpConn::flash_publishing(SrsSource* source) // use isolate thread to recv, // @see: https://github.com/winlinvip/simple-rtmp-server/issues/237 - SrsPublishRecvThread trd(rtmp, + SrsPublishRecvThread trd(rtmp, st_netfd_fileno(stfd), SRS_CONSTS_RTMP_RECV_TIMEOUT_US / 1000, this, source, false, vhost_is_edge); diff --git a/trunk/src/rtmp/srs_protocol_buffer.cpp b/trunk/src/rtmp/srs_protocol_buffer.cpp index f4d949cece..72873ac4b3 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.cpp +++ b/trunk/src/rtmp/srs_protocol_buffer.cpp @@ -112,7 +112,7 @@ int SrsBuffer::grow(ISrsBufferReader* reader, int required_size) * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 */ if (merged_read && _handler) { - _handler->on_read(nb_buffer, nread); + _handler->on_read(nread); } srs_assert((int)nread > 0); @@ -122,10 +122,14 @@ int SrsBuffer::grow(ISrsBufferReader* reader, int required_size) return ret; } -void SrsBuffer::set_merge_read(bool v, IMergeReadHandler* handler) +void SrsBuffer::set_merge_read(bool v, int max_buffer, IMergeReadHandler* handler) { merged_read = v; _handler = handler; + + if (v && max_buffer != nb_buffer) { + reset_buffer(max_buffer); + } } void SrsBuffer::on_chunk_size(int32_t chunk_size) @@ -134,10 +138,7 @@ void SrsBuffer::on_chunk_size(int32_t chunk_size) return; } - srs_freep(buffer); - - nb_buffer = chunk_size; - buffer = new char[nb_buffer]; + reset_buffer(chunk_size); } int SrsBuffer::buffer_size() @@ -145,4 +146,14 @@ int SrsBuffer::buffer_size() return nb_buffer; } +void SrsBuffer::reset_buffer(int size) +{ + srs_freep(buffer); + + nb_buffer = size; + buffer = new char[nb_buffer]; + if (_handler) { + _handler->on_buffer_change(nb_buffer); + } +} diff --git a/trunk/src/rtmp/srs_protocol_buffer.hpp b/trunk/src/rtmp/srs_protocol_buffer.hpp index 35f9c4615b..be087f3da2 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.hpp +++ b/trunk/src/rtmp/srs_protocol_buffer.hpp @@ -51,7 +51,12 @@ class IMergeReadHandler * some small bytes. * @remark, it only for server-side, client srs-librtmp just ignore. */ - virtual void on_read(int nb_buffer, ssize_t nread) = 0; + virtual void on_read(ssize_t nread) = 0; + /** + * when buffer size changed. + * @param nb_buffer the new buffer size. + */ + virtual void on_buffer_change(int nb_buffer) = 0; }; /** @@ -110,11 +115,11 @@ class SrsBuffer * when it on and read small bytes, we sleep to wait more data., * that is, we merge some data to read together. * @param v true to ename merged read. + * @param max_buffer the max buffer size, the socket buffer. * @param handler the handler when merge read is enabled. * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 */ - virtual void set_merge_read(bool v, IMergeReadHandler* handler); -public: + virtual void set_merge_read(bool v, int max_buffer, IMergeReadHandler* handler); /** * when chunk size changed, the buffer should change the buffer also. * to keep the socket buffer size always greater than chunk size. @@ -125,6 +130,8 @@ class SrsBuffer * get the size of socket buffer to read. */ virtual int buffer_size(); +private: + virtual void reset_buffer(int size); }; #endif diff --git a/trunk/src/rtmp/srs_protocol_rtmp.cpp b/trunk/src/rtmp/srs_protocol_rtmp.cpp index 80183dbbdc..c6ccc1deeb 100644 --- a/trunk/src/rtmp/srs_protocol_rtmp.cpp +++ b/trunk/src/rtmp/srs_protocol_rtmp.cpp @@ -745,9 +745,9 @@ void SrsRtmpServer::set_auto_response(bool v) protocol->set_auto_response(v); } -void SrsRtmpServer::set_merge_read(bool v, IMergeReadHandler* handler) +void SrsRtmpServer::set_merge_read(bool v, int max_buffer, IMergeReadHandler* handler) { - protocol->set_merge_read(v, handler); + protocol->set_merge_read(v, max_buffer, handler); } void SrsRtmpServer::set_recv_timeout(int64_t timeout_us) diff --git a/trunk/src/rtmp/srs_protocol_rtmp.hpp b/trunk/src/rtmp/srs_protocol_rtmp.hpp index 46eb92275d..de2e4c47ed 100644 --- a/trunk/src/rtmp/srs_protocol_rtmp.hpp +++ b/trunk/src/rtmp/srs_protocol_rtmp.hpp @@ -348,10 +348,11 @@ class SrsRtmpServer * when it on and read small bytes, we sleep to wait more data., * that is, we merge some data to read together. * @param v true to ename merged read. + * @param max_buffer the max buffer size, the socket buffer. * @param handler the handler when merge read is enabled. * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 */ - virtual void set_merge_read(bool v, IMergeReadHandler* handler); + virtual void set_merge_read(bool v, int max_buffer, IMergeReadHandler* handler); /** * set/get the recv timeout in us. * if timeout, recv/send message return ERROR_SOCKET_TIMEOUT. diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp index a58bc7c6ed..e86e36b3a0 100644 --- a/trunk/src/rtmp/srs_protocol_stack.cpp +++ b/trunk/src/rtmp/srs_protocol_stack.cpp @@ -479,9 +479,9 @@ int SrsProtocol::manual_response_flush() return ret; } -void SrsProtocol::set_merge_read(bool v, IMergeReadHandler* handler) +void SrsProtocol::set_merge_read(bool v, int max_buffer, IMergeReadHandler* handler) { - in_buffer->set_merge_read(v, handler); + in_buffer->set_merge_read(v, max_buffer, handler); } void SrsProtocol::set_recv_timeout(int64_t timeout_us) diff --git a/trunk/src/rtmp/srs_protocol_stack.hpp b/trunk/src/rtmp/srs_protocol_stack.hpp index 0f7dd69d0c..461e14ceef 100644 --- a/trunk/src/rtmp/srs_protocol_stack.hpp +++ b/trunk/src/rtmp/srs_protocol_stack.hpp @@ -276,10 +276,11 @@ class SrsProtocol * when it on and read small bytes, we sleep to wait more data., * that is, we merge some data to read together. * @param v true to ename merged read. + * @param max_buffer the max buffer size, the socket buffer. * @param handler the handler when merge read is enabled. * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 */ - virtual void set_merge_read(bool v, IMergeReadHandler* handler); + virtual void set_merge_read(bool v, int max_buffer, IMergeReadHandler* handler); public: /** * set/get the recv timeout in us. From 4785f882d47b6ab6d7185207528ad4ba9ae2b02f Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 3 Dec 2014 22:51:19 +0800 Subject: [PATCH 293/800] for bug #241, refine the max buffer and kbps. --- trunk/src/app/srs_app_recv_thread.cpp | 2 +- trunk/src/rtmp/srs_protocol_buffer.cpp | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index 6550c7defc..04abe5e2b4 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -35,7 +35,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // use the bitrate in kbps to calc the max sleep time. #define SRS_MR_MAX_BITRATE_KBPS 10000 #define SRS_MR_AVERAGE_BITRATE_KBPS 1000 -#define SRS_MR_MIN_BITRATE_KBPS 64 +#define SRS_MR_MIN_BITRATE_KBPS 32 // the max sleep time in ms #define SRS_MR_MAX_SLEEP_MS 3000 // the max small bytes to group diff --git a/trunk/src/rtmp/srs_protocol_buffer.cpp b/trunk/src/rtmp/srs_protocol_buffer.cpp index 72873ac4b3..cf17aa1d24 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.cpp +++ b/trunk/src/rtmp/srs_protocol_buffer.cpp @@ -25,6 +25,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +#include // 4KB=4096 // 8KB=8192 @@ -33,6 +34,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // 64KB=65536 // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 #define SOCKET_READ_SIZE 4096 +// the max buffer for user space socket buffer. +#define SOCKET_MAX_BUF 65536 IMergeReadHandler::IMergeReadHandler() { @@ -127,8 +130,11 @@ void SrsBuffer::set_merge_read(bool v, int max_buffer, IMergeReadHandler* handle merged_read = v; _handler = handler; - if (v && max_buffer != nb_buffer) { - reset_buffer(max_buffer); + // limit the max buffer. + int buffer_size = srs_min(max_buffer, SOCKET_MAX_BUF); + + if (v && buffer_size != nb_buffer) { + reset_buffer(buffer_size); } } From 5791ed59fd2eba9f6a959e0357c466c667eda962 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 3 Dec 2014 23:05:42 +0800 Subject: [PATCH 294/800] for bug #241, refine the socket max buffer. --- trunk/src/app/srs_app_recv_thread.cpp | 9 +++++++-- trunk/src/rtmp/srs_protocol_buffer.cpp | 14 +++++++++----- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index 04abe5e2b4..16ef3bc1e4 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -42,6 +42,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define SRS_MR_SMALL_BYTES 64 // the percent of buffer to set as small bytes #define SRS_MR_SMALL_PERCENT 100 +// set the socket buffer to specified KB. +// the underlayer api will set to 2*SRS_MR_SOCKET_BUFFER KB. +#define SRS_MR_SOCKET_BUFFER 32 ISrsMessageHandler::ISrsMessageHandler() { @@ -296,14 +299,16 @@ void SrsPublishRecvThread::on_thread_start() // we donot set the auto response to false, // for the main thread never send message. - // 128KB recv buffer. - int nb_rbuf = 128 * 1024; + // socket recv buffer. + int nb_rbuf = SRS_MR_SOCKET_BUFFER * 1024; socklen_t sock_buf_size = sizeof(int); if (setsockopt(mr_fd, SOL_SOCKET, SO_RCVBUF, &nb_rbuf, sock_buf_size) < 0) { srs_warn("set sock SO_RCVBUF=%d failed.", nb_rbuf); } getsockopt(mr_fd, SOL_SOCKET, SO_RCVBUF, &nb_rbuf, &sock_buf_size); + srs_trace("set socket buffer to %d, actual %d KB", SRS_MR_SOCKET_BUFFER, nb_rbuf / 1024); + // enable the merge read // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 rtmp->set_merge_read(true, nb_rbuf, this); diff --git a/trunk/src/rtmp/srs_protocol_buffer.cpp b/trunk/src/rtmp/srs_protocol_buffer.cpp index cf17aa1d24..f78f6b49f8 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.cpp +++ b/trunk/src/rtmp/srs_protocol_buffer.cpp @@ -33,7 +33,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // 32KB=32768 // 64KB=65536 // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 -#define SOCKET_READ_SIZE 4096 +#define SOCKET_READ_SIZE 65536 // the max buffer for user space socket buffer. #define SOCKET_MAX_BUF 65536 @@ -136,6 +136,10 @@ void SrsBuffer::set_merge_read(bool v, int max_buffer, IMergeReadHandler* handle if (v && buffer_size != nb_buffer) { reset_buffer(buffer_size); } + + if (_handler) { + _handler->on_buffer_change(nb_buffer); + } } void SrsBuffer::on_chunk_size(int32_t chunk_size) @@ -145,6 +149,10 @@ void SrsBuffer::on_chunk_size(int32_t chunk_size) } reset_buffer(chunk_size); + + if (_handler) { + _handler->on_buffer_change(nb_buffer); + } } int SrsBuffer::buffer_size() @@ -158,8 +166,4 @@ void SrsBuffer::reset_buffer(int size) nb_buffer = size; buffer = new char[nb_buffer]; - - if (_handler) { - _handler->on_buffer_change(nb_buffer); - } } From d1d6023c70755313eb437d3607fd1be7566ea0fa Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 3 Dec 2014 23:11:13 +0800 Subject: [PATCH 295/800] for bug #241, limit the buffer for input chunk size. --- trunk/src/rtmp/srs_protocol_buffer.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/trunk/src/rtmp/srs_protocol_buffer.cpp b/trunk/src/rtmp/srs_protocol_buffer.cpp index f78f6b49f8..da0b1e6391 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.cpp +++ b/trunk/src/rtmp/srs_protocol_buffer.cpp @@ -148,7 +148,12 @@ void SrsBuffer::on_chunk_size(int32_t chunk_size) return; } - reset_buffer(chunk_size); + // limit the max buffer. + int buffer_size = srs_min(chunk_size, SOCKET_MAX_BUF); + + if (buffer_size != nb_buffer) { + reset_buffer(buffer_size); + } if (_handler) { _handler->on_buffer_change(nb_buffer); From 315f981821e08f8f8a3acbc0b254dc0230e58c22 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 4 Dec 2014 09:10:57 +0800 Subject: [PATCH 296/800] refine for bug #241, increase the small bytes for merged read. --- trunk/src/app/srs_app_recv_thread.cpp | 18 +++++++++--------- trunk/src/qt/srs/srs-qt.pro.user | 0 trunk/src/rtmp/srs_protocol_buffer.cpp | 10 ---------- trunk/src/rtmp/srs_protocol_buffer.hpp | 10 ++++++++++ 4 files changed, 19 insertions(+), 19 deletions(-) mode change 100644 => 100755 trunk/src/qt/srs/srs-qt.pro.user diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index 16ef3bc1e4..30939194bc 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -37,14 +37,14 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define SRS_MR_AVERAGE_BITRATE_KBPS 1000 #define SRS_MR_MIN_BITRATE_KBPS 32 // the max sleep time in ms -#define SRS_MR_MAX_SLEEP_MS 3000 +#define SRS_MR_MAX_SLEEP_MS 2500 // the max small bytes to group -#define SRS_MR_SMALL_BYTES 64 +#define SRS_MR_SMALL_BYTES 4096 // the percent of buffer to set as small bytes #define SRS_MR_SMALL_PERCENT 100 -// set the socket buffer to specified KB. -// the underlayer api will set to 2*SRS_MR_SOCKET_BUFFER KB. -#define SRS_MR_SOCKET_BUFFER 32 +// set the socket buffer to specified bytes. +// the underlayer api will set to SRS_MR_SOCKET_BUFFER bytes. +#define SRS_MR_SOCKET_BUFFER SOCKET_READ_SIZE ISrsMessageHandler::ISrsMessageHandler() { @@ -299,15 +299,15 @@ void SrsPublishRecvThread::on_thread_start() // we donot set the auto response to false, // for the main thread never send message. - // socket recv buffer. - int nb_rbuf = SRS_MR_SOCKET_BUFFER * 1024; + // socket recv buffer, system will double it. + int nb_rbuf = SRS_MR_SOCKET_BUFFER / 2; socklen_t sock_buf_size = sizeof(int); if (setsockopt(mr_fd, SOL_SOCKET, SO_RCVBUF, &nb_rbuf, sock_buf_size) < 0) { srs_warn("set sock SO_RCVBUF=%d failed.", nb_rbuf); } getsockopt(mr_fd, SOL_SOCKET, SO_RCVBUF, &nb_rbuf, &sock_buf_size); - srs_trace("set socket buffer to %d, actual %d KB", SRS_MR_SOCKET_BUFFER, nb_rbuf / 1024); + srs_trace("set socket buffer to %d, actual %d KB", SRS_MR_SOCKET_BUFFER / 1024, nb_rbuf / 1024); // enable the merge read // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 @@ -381,7 +381,7 @@ void SrsPublishRecvThread::on_buffer_change(int nb_buffer) // set percent. mr_small_bytes = (int)(nb_buffer / SRS_MR_SMALL_PERCENT); // select the smaller - mr_small_bytes = srs_min(mr_small_bytes, SRS_MR_SMALL_BYTES); + mr_small_bytes = srs_max(mr_small_bytes, SRS_MR_SMALL_BYTES); // the recv sleep is [buffer / max_kbps, buffer / min_kbps] // for example, buffer is 256KB, max kbps is 10Mbps, min kbps is 10Kbps, diff --git a/trunk/src/qt/srs/srs-qt.pro.user b/trunk/src/qt/srs/srs-qt.pro.user old mode 100644 new mode 100755 diff --git a/trunk/src/rtmp/srs_protocol_buffer.cpp b/trunk/src/rtmp/srs_protocol_buffer.cpp index da0b1e6391..68122c24a2 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.cpp +++ b/trunk/src/rtmp/srs_protocol_buffer.cpp @@ -27,16 +27,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include -// 4KB=4096 -// 8KB=8192 -// 16KB=16384 -// 32KB=32768 -// 64KB=65536 -// @see https://github.com/winlinvip/simple-rtmp-server/issues/241 -#define SOCKET_READ_SIZE 65536 -// the max buffer for user space socket buffer. -#define SOCKET_MAX_BUF 65536 - IMergeReadHandler::IMergeReadHandler() { } diff --git a/trunk/src/rtmp/srs_protocol_buffer.hpp b/trunk/src/rtmp/srs_protocol_buffer.hpp index be087f3da2..7ef91299fd 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.hpp +++ b/trunk/src/rtmp/srs_protocol_buffer.hpp @@ -34,6 +34,16 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include +// 4KB=4096 +// 8KB=8192 +// 16KB=16384 +// 32KB=32768 +// 64KB=65536 +// @see https://github.com/winlinvip/simple-rtmp-server/issues/241 +#define SOCKET_READ_SIZE 65536 +// the max buffer for user space socket buffer. +#define SOCKET_MAX_BUF SOCKET_READ_SIZE + /** * to improve read performance, merge some packets then read, * when it on and read small bytes, we sleep to wait more data., From 2cb8b7dd5286792f293a488e9588561b6d30f98b Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 4 Dec 2014 11:27:07 +0800 Subject: [PATCH 297/800] for bug #248, use simple buffer for hls. --- trunk/src/app/srs_app_hls.cpp | 73 +++++++++++++++++++++++++++-------- trunk/src/app/srs_app_hls.hpp | 50 ++++++++++++++++++++---- 2 files changed, 100 insertions(+), 23 deletions(-) diff --git a/trunk/src/app/srs_app_hls.cpp b/trunk/src/app/srs_app_hls.cpp index 4915514e61..801e72579c 100644 --- a/trunk/src/app/srs_app_hls.cpp +++ b/trunk/src/app/srs_app_hls.cpp @@ -198,7 +198,7 @@ class SrsMpegtsWriter return ret; } - static int write_frame(SrsFileWriter* writer, SrsMpegtsFrame* frame, SrsBuffer* buffer) + static int write_frame(SrsFileWriter* writer, SrsMpegtsFrame* frame, SrsSimpleBuffer* buffer) { int ret = ERROR_SUCCESS; @@ -396,6 +396,56 @@ class SrsMpegtsWriter } }; +SrsSimpleBuffer::SrsSimpleBuffer() +{ +} + +SrsSimpleBuffer::~SrsSimpleBuffer() +{ +} + +int SrsSimpleBuffer::length() +{ + int len = (int)data.size(); + srs_assert(len >= 0); + return len; +} + +char* SrsSimpleBuffer::bytes() +{ + return (length() == 0)? NULL : &data.at(0); +} + +void SrsSimpleBuffer::erase(int size) +{ + if (size <= 0) { + return; + } + + if (size >= length()) { + data.clear(); + return; + } + + data.erase(data.begin(), data.begin() + size); +} + +void SrsSimpleBuffer::append(const char* bytes, int size) +{ + srs_assert(size > 0); + + data.insert(data.end(), bytes, bytes + size); +} + +SrsHlsAacJitter::SrsHlsAacJitter() +{ + base_pts = 0; + nb_samples = 0; + + // TODO: config it, 0 means no adjust + sync_ms = SRS_CONF_DEFAULT_AAC_SYNC; +} + SrsHlsAacJitter::~SrsHlsAacJitter() { } @@ -481,7 +531,7 @@ int SrsTSMuxer::open(string _path) return ret; } -int SrsTSMuxer::write_audio(SrsMpegtsFrame* af, SrsBuffer* ab) +int SrsTSMuxer::write_audio(SrsMpegtsFrame* af, SrsSimpleBuffer* ab) { int ret = ERROR_SUCCESS; @@ -492,7 +542,7 @@ int SrsTSMuxer::write_audio(SrsMpegtsFrame* af, SrsBuffer* ab) return ret; } -int SrsTSMuxer::write_video(SrsMpegtsFrame* vf, SrsBuffer* vb) +int SrsTSMuxer::write_video(SrsMpegtsFrame* vf, SrsSimpleBuffer* vb) { int ret = ERROR_SUCCESS; @@ -538,15 +588,6 @@ void SrsHlsSegment::update_duration(int64_t current_frame_dts) return; } -SrsHlsAacJitter::SrsHlsAacJitter() -{ - base_pts = 0; - nb_samples = 0; - - // TODO: config it, 0 means no adjust - sync_ms = SRS_CONF_DEFAULT_AAC_SYNC; -} - SrsHlsMuxer::SrsHlsMuxer() { hls_fragment = hls_window = 0; @@ -654,7 +695,7 @@ bool SrsHlsMuxer::is_segment_overflow() return current->duration >= hls_fragment; } -int SrsHlsMuxer::flush_audio(SrsMpegtsFrame* af, SrsBuffer* ab) +int SrsHlsMuxer::flush_audio(SrsMpegtsFrame* af, SrsSimpleBuffer* ab) { int ret = ERROR_SUCCESS; @@ -681,7 +722,7 @@ int SrsHlsMuxer::flush_audio(SrsMpegtsFrame* af, SrsBuffer* ab) return ret; } -int SrsHlsMuxer::flush_video(SrsMpegtsFrame* /*af*/, SrsBuffer* /*ab*/, SrsMpegtsFrame* vf, SrsBuffer* vb) +int SrsHlsMuxer::flush_video(SrsMpegtsFrame* /*af*/, SrsSimpleBuffer* /*ab*/, SrsMpegtsFrame* vf, SrsSimpleBuffer* vb) { int ret = ERROR_SUCCESS; @@ -964,8 +1005,8 @@ SrsHlsCache::SrsHlsCache() { aac_jitter = new SrsHlsAacJitter(); - ab = new SrsBuffer(); - vb = new SrsBuffer(); + ab = new SrsSimpleBuffer(); + vb = new SrsSimpleBuffer(); af = new SrsMpegtsFrame(); vf = new SrsMpegtsFrame(); diff --git a/trunk/src/app/srs_app_hls.hpp b/trunk/src/app/srs_app_hls.hpp index d31820367a..923b568590 100644 --- a/trunk/src/app/srs_app_hls.hpp +++ b/trunk/src/app/srs_app_hls.hpp @@ -50,7 +50,6 @@ extern int aac_sample_rates[]; #include #include -class SrsBuffer; class SrsSharedPtrMessage; class SrsCodecSample; class SrsMpegtsFrame; @@ -63,6 +62,43 @@ class SrsPithyPrint; class SrsSource; class SrsFileWriter; +/** +* the simple buffer use vector to append bytes, +* it's for hls, and need to be refined in future. +*/ +class SrsSimpleBuffer +{ +private: + std::vector data; +public: + SrsSimpleBuffer(); + virtual ~SrsSimpleBuffer(); +public: + /** + * get the length of buffer. empty if zero. + * @remark assert length() is not negative. + */ + virtual int length(); + /** + * get the buffer bytes. + * @return the bytes, NULL if empty. + */ + virtual char* bytes(); + /** + * erase size of bytes from begin. + * @param size to erase size of bytes. + * clear if size greater than or equals to length() + * @remark ignore size is not positive. + */ + virtual void erase(int size); + /** + * append specified bytes to buffer. + * @param size the size of bytes + * @remark assert size is positive. + */ + virtual void append(const char* bytes, int size); +}; + /** * jitter correct for audio, * the sample rate 44100/32000 will lost precise, @@ -109,8 +145,8 @@ class SrsTSMuxer virtual ~SrsTSMuxer(); public: virtual int open(std::string _path); - virtual int write_audio(SrsMpegtsFrame* af, SrsBuffer* ab); - virtual int write_video(SrsMpegtsFrame* vf, SrsBuffer* vb); + virtual int write_audio(SrsMpegtsFrame* af, SrsSimpleBuffer* ab); + virtual int write_video(SrsMpegtsFrame* vf, SrsSimpleBuffer* vb); virtual void close(); }; @@ -196,8 +232,8 @@ class SrsHlsMuxer * that is whether the current segment duration >= the segment in config */ virtual bool is_segment_overflow(); - virtual int flush_audio(SrsMpegtsFrame* af, SrsBuffer* ab); - virtual int flush_video(SrsMpegtsFrame* af, SrsBuffer* ab, SrsMpegtsFrame* vf, SrsBuffer* vb); + virtual int flush_audio(SrsMpegtsFrame* af, SrsSimpleBuffer* ab); + virtual int flush_video(SrsMpegtsFrame* af, SrsSimpleBuffer* ab, SrsMpegtsFrame* vf, SrsSimpleBuffer* vb); /** * close segment(ts). * @param log_desc the description for log. @@ -231,9 +267,9 @@ class SrsHlsCache private: // current frame and buffer SrsMpegtsFrame* af; - SrsBuffer* ab; + SrsSimpleBuffer* ab; SrsMpegtsFrame* vf; - SrsBuffer* vb; + SrsSimpleBuffer* vb; private: // the audio cache buffer start pts, to flush audio if full. int64_t audio_buffer_start_pts; From 0ea8cd9e844bf07efe43ee233c445c76e9b12b91 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 4 Dec 2014 11:29:47 +0800 Subject: [PATCH 298/800] for bug #248, use simple buffer for http. --- trunk/src/app/srs_app_hls.cpp | 41 -------------------------- trunk/src/app/srs_app_hls.hpp | 38 +----------------------- trunk/src/app/srs_app_http.cpp | 2 +- trunk/src/app/srs_app_http.hpp | 4 +-- trunk/src/rtmp/srs_protocol_buffer.cpp | 41 ++++++++++++++++++++++++++ trunk/src/rtmp/srs_protocol_buffer.hpp | 39 ++++++++++++++++++++++++ 6 files changed, 84 insertions(+), 81 deletions(-) diff --git a/trunk/src/app/srs_app_hls.cpp b/trunk/src/app/srs_app_hls.cpp index 801e72579c..1f0c6bba0d 100644 --- a/trunk/src/app/srs_app_hls.cpp +++ b/trunk/src/app/srs_app_hls.cpp @@ -396,47 +396,6 @@ class SrsMpegtsWriter } }; -SrsSimpleBuffer::SrsSimpleBuffer() -{ -} - -SrsSimpleBuffer::~SrsSimpleBuffer() -{ -} - -int SrsSimpleBuffer::length() -{ - int len = (int)data.size(); - srs_assert(len >= 0); - return len; -} - -char* SrsSimpleBuffer::bytes() -{ - return (length() == 0)? NULL : &data.at(0); -} - -void SrsSimpleBuffer::erase(int size) -{ - if (size <= 0) { - return; - } - - if (size >= length()) { - data.clear(); - return; - } - - data.erase(data.begin(), data.begin() + size); -} - -void SrsSimpleBuffer::append(const char* bytes, int size) -{ - srs_assert(size > 0); - - data.insert(data.end(), bytes, bytes + size); -} - SrsHlsAacJitter::SrsHlsAacJitter() { base_pts = 0; diff --git a/trunk/src/app/srs_app_hls.hpp b/trunk/src/app/srs_app_hls.hpp index 923b568590..8b77945b61 100644 --- a/trunk/src/app/srs_app_hls.hpp +++ b/trunk/src/app/srs_app_hls.hpp @@ -61,43 +61,7 @@ class SrsRequest; class SrsPithyPrint; class SrsSource; class SrsFileWriter; - -/** -* the simple buffer use vector to append bytes, -* it's for hls, and need to be refined in future. -*/ -class SrsSimpleBuffer -{ -private: - std::vector data; -public: - SrsSimpleBuffer(); - virtual ~SrsSimpleBuffer(); -public: - /** - * get the length of buffer. empty if zero. - * @remark assert length() is not negative. - */ - virtual int length(); - /** - * get the buffer bytes. - * @return the bytes, NULL if empty. - */ - virtual char* bytes(); - /** - * erase size of bytes from begin. - * @param size to erase size of bytes. - * clear if size greater than or equals to length() - * @remark ignore size is not positive. - */ - virtual void erase(int size); - /** - * append specified bytes to buffer. - * @param size the size of bytes - * @remark assert size is positive. - */ - virtual void append(const char* bytes, int size); -}; +class SrsSimpleBuffer; /** * jitter correct for audio, diff --git a/trunk/src/app/srs_app_http.cpp b/trunk/src/app/srs_app_http.cpp index 9494b431dc..3cc17e5d7c 100644 --- a/trunk/src/app/srs_app_http.cpp +++ b/trunk/src/app/srs_app_http.cpp @@ -537,7 +537,7 @@ SrsHttpHandler* SrsHttpHandler::create_http_stream() SrsHttpMessage::SrsHttpMessage() { - _body = new SrsBuffer(); + _body = new SrsSimpleBuffer(); _state = SrsHttpParseStateInit; _uri = new SrsHttpUri(); _match = NULL; diff --git a/trunk/src/app/srs_app_http.hpp b/trunk/src/app/srs_app_http.hpp index a9dd80a597..27822ee0ea 100644 --- a/trunk/src/app/srs_app_http.hpp +++ b/trunk/src/app/srs_app_http.hpp @@ -39,7 +39,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include -class SrsBuffer; +class SrsSimpleBuffer; class SrsRequest; class SrsStSocket; class SrsHttpUri; @@ -214,7 +214,7 @@ class SrsHttpMessage * body object, in bytes. * @remark, user can get body in string by get_body(). */ - SrsBuffer* _body; + SrsSimpleBuffer* _body; /** * parser state * @remark, user can use is_complete() to determine the state. diff --git a/trunk/src/rtmp/srs_protocol_buffer.cpp b/trunk/src/rtmp/srs_protocol_buffer.cpp index 68122c24a2..4eb05edb3b 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.cpp +++ b/trunk/src/rtmp/srs_protocol_buffer.cpp @@ -27,6 +27,47 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +SrsSimpleBuffer::SrsSimpleBuffer() +{ +} + +SrsSimpleBuffer::~SrsSimpleBuffer() +{ +} + +int SrsSimpleBuffer::length() +{ + int len = (int)data.size(); + srs_assert(len >= 0); + return len; +} + +char* SrsSimpleBuffer::bytes() +{ + return (length() == 0)? NULL : &data.at(0); +} + +void SrsSimpleBuffer::erase(int size) +{ + if (size <= 0) { + return; + } + + if (size >= length()) { + data.clear(); + return; + } + + data.erase(data.begin(), data.begin() + size); +} + +void SrsSimpleBuffer::append(const char* bytes, int size) +{ + srs_assert(size > 0); + + data.insert(data.end(), bytes, bytes + size); +} + IMergeReadHandler::IMergeReadHandler() { } diff --git a/trunk/src/rtmp/srs_protocol_buffer.hpp b/trunk/src/rtmp/srs_protocol_buffer.hpp index 7ef91299fd..89d02182e4 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.hpp +++ b/trunk/src/rtmp/srs_protocol_buffer.hpp @@ -34,6 +34,43 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include +/** +* the simple buffer use vector to append bytes, +* it's for hls and http, and need to be refined in future. +*/ +class SrsSimpleBuffer +{ +private: + std::vector data; +public: + SrsSimpleBuffer(); + virtual ~SrsSimpleBuffer(); +public: + /** + * get the length of buffer. empty if zero. + * @remark assert length() is not negative. + */ + virtual int length(); + /** + * get the buffer bytes. + * @return the bytes, NULL if empty. + */ + virtual char* bytes(); + /** + * erase size of bytes from begin. + * @param size to erase size of bytes. + * clear if size greater than or equals to length() + * @remark ignore size is not positive. + */ + virtual void erase(int size); + /** + * append specified bytes to buffer. + * @param size the size of bytes + * @remark assert size is positive. + */ + virtual void append(const char* bytes, int size); +}; + // 4KB=4096 // 8KB=8192 // 16KB=16384 @@ -97,6 +134,7 @@ class SrsBuffer * @return the bytes, NULL if empty. */ virtual char* bytes(); +public: /** * erase size of bytes from begin. * @param size to erase size of bytes. @@ -104,6 +142,7 @@ class SrsBuffer * @remark ignore size is not positive. */ virtual void erase(int size); +private: /** * append specified bytes to buffer. * @param size the size of bytes From 8423974e3bc4c89b34df9b2172a3c167185b7ca1 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 4 Dec 2014 11:32:32 +0800 Subject: [PATCH 299/800] for bug #248, rename SrsBuffer to SrsFastBuffer. --- trunk/src/rtmp/srs_protocol_buffer.cpp | 22 +++++++++++----------- trunk/src/rtmp/srs_protocol_buffer.hpp | 6 +++--- trunk/src/rtmp/srs_protocol_stack.cpp | 2 +- trunk/src/rtmp/srs_protocol_stack.hpp | 4 ++-- trunk/src/utest/srs_utest_kernel.cpp | 8 ++++---- trunk/src/utest/srs_utest_protocol.hpp | 4 ++-- 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/trunk/src/rtmp/srs_protocol_buffer.cpp b/trunk/src/rtmp/srs_protocol_buffer.cpp index 4eb05edb3b..fa38f87779 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.cpp +++ b/trunk/src/rtmp/srs_protocol_buffer.cpp @@ -76,7 +76,7 @@ IMergeReadHandler::~IMergeReadHandler() { } -SrsBuffer::SrsBuffer() +SrsFastBuffer::SrsFastBuffer() { merged_read = false; _handler = NULL; @@ -85,24 +85,24 @@ SrsBuffer::SrsBuffer() buffer = new char[nb_buffer]; } -SrsBuffer::~SrsBuffer() +SrsFastBuffer::~SrsFastBuffer() { srs_freep(buffer); } -int SrsBuffer::length() +int SrsFastBuffer::length() { int len = (int)data.size(); srs_assert(len >= 0); return len; } -char* SrsBuffer::bytes() +char* SrsFastBuffer::bytes() { return (length() == 0)? NULL : &data.at(0); } -void SrsBuffer::erase(int size) +void SrsFastBuffer::erase(int size) { if (size <= 0) { return; @@ -116,14 +116,14 @@ void SrsBuffer::erase(int size) data.erase(data.begin(), data.begin() + size); } -void SrsBuffer::append(const char* bytes, int size) +void SrsFastBuffer::append(const char* bytes, int size) { srs_assert(size > 0); data.insert(data.end(), bytes, bytes + size); } -int SrsBuffer::grow(ISrsBufferReader* reader, int required_size) +int SrsFastBuffer::grow(ISrsBufferReader* reader, int required_size) { int ret = ERROR_SUCCESS; @@ -156,7 +156,7 @@ int SrsBuffer::grow(ISrsBufferReader* reader, int required_size) return ret; } -void SrsBuffer::set_merge_read(bool v, int max_buffer, IMergeReadHandler* handler) +void SrsFastBuffer::set_merge_read(bool v, int max_buffer, IMergeReadHandler* handler) { merged_read = v; _handler = handler; @@ -173,7 +173,7 @@ void SrsBuffer::set_merge_read(bool v, int max_buffer, IMergeReadHandler* handle } } -void SrsBuffer::on_chunk_size(int32_t chunk_size) +void SrsFastBuffer::on_chunk_size(int32_t chunk_size) { if (nb_buffer >= chunk_size) { return; @@ -191,12 +191,12 @@ void SrsBuffer::on_chunk_size(int32_t chunk_size) } } -int SrsBuffer::buffer_size() +int SrsFastBuffer::buffer_size() { return nb_buffer; } -void SrsBuffer::reset_buffer(int size) +void SrsFastBuffer::reset_buffer(int size) { srs_freep(buffer); diff --git a/trunk/src/rtmp/srs_protocol_buffer.hpp b/trunk/src/rtmp/srs_protocol_buffer.hpp index 89d02182e4..72047ac1ba 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.hpp +++ b/trunk/src/rtmp/srs_protocol_buffer.hpp @@ -110,7 +110,7 @@ class IMergeReadHandler * the buffer provices bytes cache for protocol. generally, * protocol recv data from socket, put into buffer, decode to RTMP message. */ -class SrsBuffer +class SrsFastBuffer { private: // the merged handler @@ -121,8 +121,8 @@ class SrsBuffer char* buffer; int nb_buffer; public: - SrsBuffer(); - virtual ~SrsBuffer(); + SrsFastBuffer(); + virtual ~SrsFastBuffer(); public: /** * get the length of buffer. empty if zero. diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp index e86e36b3a0..40a001e132 100644 --- a/trunk/src/rtmp/srs_protocol_stack.cpp +++ b/trunk/src/rtmp/srs_protocol_stack.cpp @@ -404,7 +404,7 @@ SrsProtocol::AckWindowSize::AckWindowSize() SrsProtocol::SrsProtocol(ISrsProtocolReaderWriter* io) { - in_buffer = new SrsBuffer(); + in_buffer = new SrsFastBuffer(); skt = io; in_chunk_size = SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE; diff --git a/trunk/src/rtmp/srs_protocol_stack.hpp b/trunk/src/rtmp/srs_protocol_stack.hpp index 461e14ceef..9acbecb010 100644 --- a/trunk/src/rtmp/srs_protocol_stack.hpp +++ b/trunk/src/rtmp/srs_protocol_stack.hpp @@ -44,7 +44,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include class ISrsProtocolReaderWriter; -class SrsBuffer; +class SrsFastBuffer; class SrsPacket; class SrsStream; class SrsAmf0Object; @@ -206,7 +206,7 @@ class SrsProtocol /** * bytes buffer cache, recv from skt, provide services for stream. */ - SrsBuffer* in_buffer; + SrsFastBuffer* in_buffer; /** * input chunk size, default to 128, set by peer packet. */ diff --git a/trunk/src/utest/srs_utest_kernel.cpp b/trunk/src/utest/srs_utest_kernel.cpp index 166981cc84..5c5e968d2c 100644 --- a/trunk/src/utest/srs_utest_kernel.cpp +++ b/trunk/src/utest/srs_utest_kernel.cpp @@ -203,7 +203,7 @@ int MockBufferReader::read(void* buf, size_t size, ssize_t* nread) VOID TEST(KernelBufferTest, DefaultObject) { - SrsBuffer b; + SrsFastBuffer b; EXPECT_EQ(0, b.length()); EXPECT_EQ(NULL, b.bytes()); @@ -211,7 +211,7 @@ VOID TEST(KernelBufferTest, DefaultObject) VOID TEST(KernelBufferTest, AppendBytes) { - SrsBuffer b; + SrsFastBuffer b; char winlin[] = "winlin"; b.append(winlin, strlen(winlin)); @@ -231,7 +231,7 @@ VOID TEST(KernelBufferTest, AppendBytes) VOID TEST(KernelBufferTest, EraseBytes) { - SrsBuffer b; + SrsFastBuffer b; b.erase(0); b.erase(-1); @@ -267,7 +267,7 @@ VOID TEST(KernelBufferTest, EraseBytes) VOID TEST(KernelBufferTest, Grow) { - SrsBuffer b; + SrsFastBuffer b; MockBufferReader r("winlin"); b.grow(&r, 1); diff --git a/trunk/src/utest/srs_utest_protocol.hpp b/trunk/src/utest/srs_utest_protocol.hpp index b97d8fbe9c..060f5a54ec 100644 --- a/trunk/src/utest/srs_utest_protocol.hpp +++ b/trunk/src/utest/srs_utest_protocol.hpp @@ -78,9 +78,9 @@ class MockBufferIO : public ISrsProtocolReaderWriter int64_t recv_bytes; int64_t send_bytes; // data source for socket read. - SrsBuffer in_buffer; + SrsFastBuffer in_buffer; // data buffer for socket send. - SrsBuffer out_buffer; + SrsFastBuffer out_buffer; public: MockBufferIO(); virtual ~MockBufferIO(); From 29324fab469e0f7cef9ad04ffdbce832ac7dd9ff Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 4 Dec 2014 13:05:13 +0800 Subject: [PATCH 300/800] fix #248, improve about 15% performance for fast buffer. 2.0.49 --- trunk/src/core/srs_core.hpp | 2 +- trunk/src/kernel/srs_kernel_error.hpp | 1 + trunk/src/rtmp/srs_protocol_buffer.cpp | 93 +++++++++++++++++--------- trunk/src/rtmp/srs_protocol_buffer.hpp | 43 ++++++------ trunk/src/rtmp/srs_protocol_stack.cpp | 91 +++++++++++-------------- trunk/src/rtmp/srs_protocol_stack.hpp | 9 +-- 6 files changed, 129 insertions(+), 110 deletions(-) diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index df7b42d152..2bc63f30e2 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 48 +#define VERSION_REVISION 49 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index aa6aa41093..75db04fafa 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -134,6 +134,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define ERROR_OpenSslSha256DigestSize 2037 #define ERROR_OpenSslGetPeerPublicKey 2038 #define ERROR_OpenSslComputeSharedKey 2039 +#define ERROR_RTMP_BUFFER_OVERFLOW 2040 // // system control message, // not an error, but special control logic. diff --git a/trunk/src/rtmp/srs_protocol_buffer.cpp b/trunk/src/rtmp/srs_protocol_buffer.cpp index fa38f87779..12cc48b305 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.cpp +++ b/trunk/src/rtmp/srs_protocol_buffer.cpp @@ -27,6 +27,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +// the max header size, +// @see SrsProtocol::read_message_header(). +#define SRS_RTMP_MAX_MESSAGE_HEADER 11 + SrsSimpleBuffer::SrsSimpleBuffer() { } @@ -81,8 +85,10 @@ SrsFastBuffer::SrsFastBuffer() merged_read = false; _handler = NULL; - nb_buffer = SOCKET_READ_SIZE; - buffer = new char[nb_buffer]; + p = end = buffer = NULL; + nb_buffer = 0; + + reset_buffer(SOCKET_READ_SIZE); } SrsFastBuffer::~SrsFastBuffer() @@ -90,37 +96,34 @@ SrsFastBuffer::~SrsFastBuffer() srs_freep(buffer); } -int SrsFastBuffer::length() +char SrsFastBuffer::read_1byte() { - int len = (int)data.size(); - srs_assert(len >= 0); - return len; -} - -char* SrsFastBuffer::bytes() -{ - return (length() == 0)? NULL : &data.at(0); + srs_assert(end - p >= 1); + return *p++; } -void SrsFastBuffer::erase(int size) +char* SrsFastBuffer::read_slice(int size) { - if (size <= 0) { - return; - } + srs_assert(end - p >= size); + srs_assert(p + size > buffer); - if (size >= length()) { - data.clear(); - return; - } + char* ptr = p; + p += size; - data.erase(data.begin(), data.begin() + size); + // reset when consumed all. + if (p == end) { + p = end = buffer; + srs_verbose("all consumed, reset fast buffer"); + } + + return ptr; } -void SrsFastBuffer::append(const char* bytes, int size) +void SrsFastBuffer::skip(int size) { - srs_assert(size > 0); - - data.insert(data.end(), bytes, bytes + size); + srs_assert(end - p >= size); + srs_assert(p + size > buffer); + p += size; } int SrsFastBuffer::grow(ISrsBufferReader* reader, int required_size) @@ -133,9 +136,27 @@ int SrsFastBuffer::grow(ISrsBufferReader* reader, int required_size) return ret; } - while (length() < required_size) { + // when read payload and need to grow, reset buffer. + if (end - p < required_size && required_size > SRS_RTMP_MAX_MESSAGE_HEADER) { + int nb_cap = end - p; + srs_verbose("move fast buffer %d bytes", nb_cap); + buffer = (char*)memmove(buffer, p, nb_cap); + p = buffer; + end = p + nb_cap; + } + + while (end - p < required_size) { + // the max to read is the left bytes. + size_t max_to_read = buffer + nb_buffer - end; + + if (max_to_read <= 0) { + ret = ERROR_RTMP_BUFFER_OVERFLOW; + srs_error("buffer overflow, required=%d, max=%d, ret=%d", required_size, nb_buffer, ret); + return ret; + } + ssize_t nread; - if ((ret = reader->read(buffer, nb_buffer, &nread)) != ERROR_SUCCESS) { + if ((ret = reader->read(end, max_to_read, &nread)) != ERROR_SUCCESS) { return ret; } @@ -149,8 +170,9 @@ int SrsFastBuffer::grow(ISrsBufferReader* reader, int required_size) _handler->on_read(nread); } + // we just move the ptr to next. srs_assert((int)nread > 0); - append(buffer, (int)nread); + end += nread; } return ret; @@ -198,8 +220,19 @@ int SrsFastBuffer::buffer_size() void SrsFastBuffer::reset_buffer(int size) { + // remember the cap. + int nb_cap = end - p; + + // atleast to put the old data. + nb_buffer = srs_max(nb_cap, size); + + // copy old data to buf. + char* buf = new char[nb_buffer]; + if (nb_cap > 0) { + memcpy(buf, p, nb_cap); + } + srs_freep(buffer); - - nb_buffer = size; - buffer = new char[nb_buffer]; + p = buffer = buf; + end = p + nb_cap; } diff --git a/trunk/src/rtmp/srs_protocol_buffer.hpp b/trunk/src/rtmp/srs_protocol_buffer.hpp index 72047ac1ba..6cae2915aa 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.hpp +++ b/trunk/src/rtmp/srs_protocol_buffer.hpp @@ -116,39 +116,40 @@ class SrsFastBuffer // the merged handler bool merged_read; IMergeReadHandler* _handler; - // data and socket buffer - std::vector data; + // the user-space buffer to fill by reader, + // which use fast index and reset when chunk body read ok. + // @see https://github.com/winlinvip/simple-rtmp-server/issues/248 + // ptr to the current read position. + char* p; + // ptr to the content end. + char* end; + // ptr to the buffer. + // buffer <= p <= end <= buffer+nb_buffer char* buffer; + // the max size of buffer. int nb_buffer; public: SrsFastBuffer(); virtual ~SrsFastBuffer(); public: /** - * get the length of buffer. empty if zero. - * @remark assert length() is not negative. - */ - virtual int length(); - /** - * get the buffer bytes. - * @return the bytes, NULL if empty. + * read 1byte from buffer, move to next bytes. + * @remark assert buffer already grow(1). */ - virtual char* bytes(); -public: + virtual char read_1byte(); /** - * erase size of bytes from begin. - * @param size to erase size of bytes. - * clear if size greater than or equals to length() - * @remark ignore size is not positive. + * read a slice in size bytes, move to next bytes. + * user can use this char* ptr directly, and should never free it. + * @remark assert buffer already grow(size). + * @remark the ptr returned maybe invalid after grow(x). */ - virtual void erase(int size); -private: + virtual char* read_slice(int size); /** - * append specified bytes to buffer. - * @param size the size of bytes - * @remark assert size is positive. + * skip some bytes in buffer. + * @param size the bytes to skip. positive to next; negative to previous. + * @remark assert buffer already grow(size). */ - virtual void append(const char* bytes, int size); + virtual void skip(int size); public: /** * grow buffer to the required size, loop to read from skt to fill. diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp index 40a001e132..609753992f 100644 --- a/trunk/src/rtmp/srs_protocol_stack.cpp +++ b/trunk/src/rtmp/srs_protocol_stack.cpp @@ -1075,14 +1075,13 @@ int SrsProtocol::recv_interlaced_message(SrsMessage** pmsg) // chunk stream basic header. char fmt = 0; int cid = 0; - int bh_size = 0; - if ((ret = read_basic_header(fmt, cid, bh_size)) != ERROR_SUCCESS) { + if ((ret = read_basic_header(fmt, cid)) != ERROR_SUCCESS) { if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) { srs_error("read basic header failed. ret=%d", ret); } return ret; } - srs_verbose("read basic header success. fmt=%d, cid=%d, bh_size=%d", fmt, cid, bh_size); + srs_verbose("read basic header success. fmt=%d, cid=%d", fmt, cid); // once we got the chunk message header, // that is there is a real message in cache, @@ -1115,8 +1114,7 @@ int SrsProtocol::recv_interlaced_message(SrsMessage** pmsg) } // chunk stream message header - int mh_size = 0; - if ((ret = read_message_header(chunk, fmt, bh_size, mh_size)) != ERROR_SUCCESS) { + if ((ret = read_message_header(chunk, fmt)) != ERROR_SUCCESS) { if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) { srs_error("read message header failed. ret=%d", ret); } @@ -1129,8 +1127,7 @@ int SrsProtocol::recv_interlaced_message(SrsMessage** pmsg) // read msg payload from chunk stream. SrsMessage* msg = NULL; - int payload_size = 0; - if ((ret = read_message_payload(chunk, bh_size, mh_size, payload_size, &msg)) != ERROR_SUCCESS) { + if ((ret = read_message_payload(chunk, &msg)) != ERROR_SUCCESS) { if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) { srs_error("read message payload failed. ret=%d", ret); } @@ -1203,59 +1200,52 @@ int SrsProtocol::recv_interlaced_message(SrsMessage** pmsg) * Chunk stream IDs with values 64-319 could be represented by both 2- * byte version and 3-byte version of this field. */ -int SrsProtocol::read_basic_header(char& fmt, int& cid, int& bh_size) +int SrsProtocol::read_basic_header(char& fmt, int& cid) { int ret = ERROR_SUCCESS; - int required_size = 1; - if ((ret = in_buffer->grow(skt, required_size)) != ERROR_SUCCESS) { + if ((ret = in_buffer->grow(skt, 1)) != ERROR_SUCCESS) { if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) { - srs_error("read 1bytes basic header failed. required_size=%d, ret=%d", required_size, ret); + srs_error("read 1bytes basic header failed. required_size=%d, ret=%d", 1, ret); } return ret; } - char* p = in_buffer->bytes(); - - fmt = (*p >> 6) & 0x03; - cid = *p & 0x3f; - bh_size = 1; + fmt = in_buffer->read_1byte(); + cid = fmt & 0x3f; + fmt = (fmt >> 6) & 0x03; // 2-63, 1B chunk header if (cid > 1) { - srs_verbose("%dbytes basic header parsed. fmt=%d, cid=%d", bh_size, fmt, cid); + srs_verbose("basic header parsed. fmt=%d, cid=%d", fmt, cid); return ret; } // 64-319, 2B chunk header if (cid == 0) { - required_size = 2; - if ((ret = in_buffer->grow(skt, required_size)) != ERROR_SUCCESS) { + if ((ret = in_buffer->grow(skt, 1)) != ERROR_SUCCESS) { if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) { - srs_error("read 2bytes basic header failed. required_size=%d, ret=%d", required_size, ret); + srs_error("read 2bytes basic header failed. required_size=%d, ret=%d", 1, ret); } return ret; } cid = 64; - cid += (u_int8_t)*(++p); - bh_size = 2; - srs_verbose("%dbytes basic header parsed. fmt=%d, cid=%d", bh_size, fmt, cid); + cid += (u_int8_t)in_buffer->read_1byte(); + srs_verbose("2bytes basic header parsed. fmt=%d, cid=%d", fmt, cid); // 64-65599, 3B chunk header } else if (cid == 1) { - required_size = 3; - if ((ret = in_buffer->grow(skt, 3)) != ERROR_SUCCESS) { + if ((ret = in_buffer->grow(skt, 2)) != ERROR_SUCCESS) { if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) { - srs_error("read 3bytes basic header failed. required_size=%d, ret=%d", required_size, ret); + srs_error("read 3bytes basic header failed. required_size=%d, ret=%d", 2, ret); } return ret; } cid = 64; - cid += (u_int8_t)*(++p); - cid += ((u_int8_t)*(++p)) * 256; - bh_size = 3; - srs_verbose("%dbytes basic header parsed. fmt=%d, cid=%d", bh_size, fmt, cid); + cid += (u_int8_t)in_buffer->read_1byte(); + cid += ((u_int8_t)in_buffer->read_1byte()) * 256; + srs_verbose("3bytes basic header parsed. fmt=%d, cid=%d", fmt, cid); } else { srs_error("invalid path, impossible basic header."); srs_assert(false); @@ -1276,7 +1266,7 @@ int SrsProtocol::read_basic_header(char& fmt, int& cid, int& bh_size) * fmt=2, 0x8X * fmt=3, 0xCX */ -int SrsProtocol::read_message_header(SrsChunkStream* chunk, char fmt, int bh_size, int& mh_size) +int SrsProtocol::read_message_header(SrsChunkStream* chunk, char fmt) { int ret = ERROR_SUCCESS; @@ -1344,17 +1334,15 @@ int SrsProtocol::read_message_header(SrsChunkStream* chunk, char fmt, int bh_siz // read message header from socket to buffer. static char mh_sizes[] = {11, 7, 3, 0}; - mh_size = mh_sizes[(int)fmt]; + int mh_size = mh_sizes[(int)fmt]; srs_verbose("calc chunk message header size. fmt=%d, mh_size=%d", fmt, mh_size); - int required_size = bh_size + mh_size; - if ((ret = in_buffer->grow(skt, required_size)) != ERROR_SUCCESS) { + if (mh_size > 0 && (ret = in_buffer->grow(skt, mh_size)) != ERROR_SUCCESS) { if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) { - srs_error("read %dbytes message header failed. required_size=%d, ret=%d", mh_size, required_size, ret); + srs_error("read %dbytes message header failed. ret=%d", mh_size, ret); } return ret; } - char* p = in_buffer->bytes() + bh_size; /** * parse the message header. @@ -1370,6 +1358,8 @@ int SrsProtocol::read_message_header(SrsChunkStream* chunk, char fmt, int bh_siz */ // see also: ngx_rtmp_recv if (fmt <= RTMP_FMT_TYPE2) { + char* p = in_buffer->read_slice(mh_size); + char* pp = (char*)&chunk->header.timestamp_delta; pp[2] = *p++; pp[1] = *p++; @@ -1466,14 +1456,16 @@ int SrsProtocol::read_message_header(SrsChunkStream* chunk, char fmt, int bh_siz // read extended-timestamp if (chunk->extended_timestamp) { mh_size += 4; - required_size = bh_size + mh_size; srs_verbose("read header ext time. fmt=%d, ext_time=%d, mh_size=%d", fmt, chunk->extended_timestamp, mh_size); - if ((ret = in_buffer->grow(skt, required_size)) != ERROR_SUCCESS) { + if ((ret = in_buffer->grow(skt, 4)) != ERROR_SUCCESS) { if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) { - srs_error("read %dbytes message header failed. required_size=%d, ret=%d", mh_size, required_size, ret); + srs_error("read %dbytes message header failed. required_size=%d, ret=%d", mh_size, 4, ret); } return ret; } + // the ptr to the slice maybe invalid when grow() + // reset the p to get 4bytes slice. + char* p = in_buffer->read_slice(4); u_int32_t timestamp = 0x00; char* pp = (char*)×tamp; @@ -1515,6 +1507,7 @@ int SrsProtocol::read_message_header(SrsChunkStream* chunk, char fmt, int bh_siz */ if (!is_first_chunk_of_msg && chunk_timestamp > 0 && chunk_timestamp != timestamp) { mh_size -= 4; + in_buffer->skip(-4); srs_info("no 4bytes extended timestamp in the continued chunk"); } else { chunk->header.timestamp = timestamp; @@ -1557,15 +1550,12 @@ int SrsProtocol::read_message_header(SrsChunkStream* chunk, char fmt, int bh_siz return ret; } -int SrsProtocol::read_message_payload(SrsChunkStream* chunk, int bh_size, int mh_size, int& payload_size, SrsMessage** pmsg) +int SrsProtocol::read_message_payload(SrsChunkStream* chunk, SrsMessage** pmsg) { int ret = ERROR_SUCCESS; // empty message if (chunk->header.payload_length <= 0) { - // need erase the header in buffer. - in_buffer->erase(bh_size + mh_size); - srs_trace("get an empty RTMP " "message(type=%d, size=%d, time=%"PRId64", sid=%d)", chunk->header.message_type, chunk->header.payload_length, chunk->header.timestamp, chunk->header.stream_id); @@ -1578,7 +1568,7 @@ int SrsProtocol::read_message_payload(SrsChunkStream* chunk, int bh_size, int mh srs_assert(chunk->header.payload_length > 0); // the chunk payload size. - payload_size = chunk->header.payload_length - chunk->msg->size; + int payload_size = chunk->header.payload_length - chunk->msg->size; payload_size = srs_min(payload_size, in_chunk_size); srs_verbose("chunk payload size is %d, message_size=%d, received_size=%d, in_chunk_size=%d", payload_size, chunk->header.payload_length, chunk->msg->size, in_chunk_size); @@ -1586,23 +1576,20 @@ int SrsProtocol::read_message_payload(SrsChunkStream* chunk, int bh_size, int mh // create msg payload if not initialized if (!chunk->msg->payload) { chunk->msg->payload = new char[chunk->header.payload_length]; - memset(chunk->msg->payload, 0, chunk->header.payload_length); - srs_verbose("create empty payload for RTMP message. size=%d", chunk->header.payload_length); + srs_verbose("create payload for RTMP message. size=%d", chunk->header.payload_length); } // read payload to buffer - int required_size = bh_size + mh_size + payload_size; - if ((ret = in_buffer->grow(skt, required_size)) != ERROR_SUCCESS) { + if ((ret = in_buffer->grow(skt, payload_size)) != ERROR_SUCCESS) { if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) { - srs_error("read payload failed. required_size=%d, ret=%d", required_size, ret); + srs_error("read payload failed. required_size=%d, ret=%d", payload_size, ret); } return ret; } - memcpy(chunk->msg->payload + chunk->msg->size, in_buffer->bytes() + bh_size + mh_size, payload_size); - in_buffer->erase(bh_size + mh_size + payload_size); + memcpy(chunk->msg->payload + chunk->msg->size, in_buffer->read_slice(payload_size), payload_size); chunk->msg->size += payload_size; - srs_verbose("chunk payload read completed. bh_size=%d, mh_size=%d, payload_size=%d", bh_size, mh_size, payload_size); + srs_verbose("chunk payload read completed. payload_size=%d", payload_size); // got entire RTMP message? if (chunk->header.payload_length == chunk->msg->size) { diff --git a/trunk/src/rtmp/srs_protocol_stack.hpp b/trunk/src/rtmp/srs_protocol_stack.hpp index 9acbecb010..18531224f7 100644 --- a/trunk/src/rtmp/srs_protocol_stack.hpp +++ b/trunk/src/rtmp/srs_protocol_stack.hpp @@ -434,21 +434,18 @@ class SrsProtocol /** * read the chunk basic header(fmt, cid) from chunk stream. * user can discovery a SrsChunkStream by cid. - * @bh_size return the chunk basic header size, to remove the used bytes when finished. */ - virtual int read_basic_header(char& fmt, int& cid, int& bh_size); + virtual int read_basic_header(char& fmt, int& cid); /** * read the chunk message header(timestamp, payload_length, message_type, stream_id) * from chunk stream and save to SrsChunkStream. - * @mh_size return the chunk message header size, to remove the used bytes when finished. */ - virtual int read_message_header(SrsChunkStream* chunk, char fmt, int bh_size, int& mh_size); + virtual int read_message_header(SrsChunkStream* chunk, char fmt); /** * read the chunk payload, remove the used bytes in buffer, * if got entire message, set the pmsg. - * @payload_size read size in this roundtrip, generally a chunk size or left message size. */ - virtual int read_message_payload(SrsChunkStream* chunk, int bh_size, int mh_size, int& payload_size, SrsMessage** pmsg); + virtual int read_message_payload(SrsChunkStream* chunk, SrsMessage** pmsg); /** * when recv message, update the context. */ From fd5ef89030c81eab9d20caf896db8faf0fc46c47 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 4 Dec 2014 13:06:21 +0800 Subject: [PATCH 301/800] update readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 83d8012d43..e09e8fc7f8 100755 --- a/README.md +++ b/README.md @@ -485,6 +485,7 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History +* v2.0, 2014-12-04, fix [#248](https://github.com/winlinvip/simple-rtmp-server/issues/248), improve about 15% performance for fast buffer. 2.0.49 * v2.0, 2014-12-03, fix [#244](https://github.com/winlinvip/simple-rtmp-server/issues/244), conn thread use cond to wait for recv thread error. 2.0.47. * v2.0, 2014-12-02, merge [#239](https://github.com/winlinvip/simple-rtmp-server/pull/239), traverse the token before response connect. 2.0.45. * v2.0, 2014-12-02, srs-librtmp support hijack io apis for st-load. 2.0.42. @@ -732,6 +733,7 @@ The publish benchmark by [st-load](https://github.com/winlinvip/st-load): * 2014-12-03, SRS 2.0.47, 1k(1200) publishers, 84%CPU, 76MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/787ab674e38734ea8e0678101614fdcd84645dc8) * 2014-12-03, SRS 2.0.47, 1k(1400) publishers, 95%CPU, 140MB. * 2014-12-03, SRS 2.0.48, 1k(1400) publishers, 95%CPU, 140MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/f35ec2155b1408d528a9f37da7904c9625186bcf) +* 2014-12-04, SRS 2.0.49, 1k(1400) publishers, 68%CPU, 144MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/29324fab469e0f7cef9ad04ffdbce832ac7dd9ff) ## Architecture From 60bc9c2aa0b1c95b350814affffd528d23ecc60b Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 4 Dec 2014 13:43:55 +0800 Subject: [PATCH 302/800] add performance header to control options for performance. 2.0.50 --- trunk/configure | 2 +- trunk/src/app/srs_app_recv_thread.cpp | 9 ++++ trunk/src/app/srs_app_recv_thread.hpp | 8 ++- trunk/src/core/srs_core.hpp | 2 +- trunk/src/core/srs_core_performance.cpp | 25 +++++++++ trunk/src/core/srs_core_performance.hpp | 71 +++++++++++++++++++++++++ trunk/src/rtmp/srs_protocol_buffer.cpp | 11 ++++ trunk/src/rtmp/srs_protocol_buffer.hpp | 29 ++++++---- trunk/src/rtmp/srs_protocol_rtmp.cpp | 2 + trunk/src/rtmp/srs_protocol_rtmp.hpp | 3 ++ trunk/src/rtmp/srs_protocol_stack.cpp | 2 + trunk/src/rtmp/srs_protocol_stack.hpp | 3 ++ trunk/src/srs/srs.upp | 2 + trunk/src/utest/srs_utest_kernel.cpp | 19 ++++--- trunk/src/utest/srs_utest_protocol.hpp | 4 +- 15 files changed, 167 insertions(+), 25 deletions(-) create mode 100644 trunk/src/core/srs_core_performance.cpp create mode 100644 trunk/src/core/srs_core_performance.hpp diff --git a/trunk/configure b/trunk/configure index 4b041e8a2d..dd6d5f4e35 100755 --- a/trunk/configure +++ b/trunk/configure @@ -355,7 +355,7 @@ if [ $SRS_MIPS_UBUNTU12 = YES ]; then SrsLinkOptions="${SrsLinkOptions} -lgcc_eh MODULE_ID="CORE" MODULE_DEPENDS=() ModuleLibIncs=(${SRS_OBJS_DIR}) -MODULE_FILES=("srs_core" "srs_core_autofree") +MODULE_FILES=("srs_core" "srs_core_autofree" "srs_core_performance") CORE_INCS="src/core"; MODULE_DIR=${CORE_INCS} . auto/modules.sh CORE_OBJS="${MODULE_OBJS[@]}" # diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index 30939194bc..c296e24344 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -28,6 +28,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #include +#include // when we read from socket less than this value, // sleep a while to merge read. @@ -299,6 +300,7 @@ void SrsPublishRecvThread::on_thread_start() // we donot set the auto response to false, // for the main thread never send message. +#ifdef SRS_PERF_MERGED_READ // socket recv buffer, system will double it. int nb_rbuf = SRS_MR_SOCKET_BUFFER / 2; socklen_t sock_buf_size = sizeof(int); @@ -312,6 +314,7 @@ void SrsPublishRecvThread::on_thread_start() // enable the merge read // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 rtmp->set_merge_read(true, nb_rbuf, this); +#endif } void SrsPublishRecvThread::on_thread_stop() @@ -323,9 +326,11 @@ void SrsPublishRecvThread::on_thread_stop() // @see https://github.com/winlinvip/simple-rtmp-server/issues/244 st_cond_signal(error); +#ifdef SRS_PERF_MERGED_READ // disable the merge read // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 rtmp->set_merge_read(false, 0, NULL); +#endif } bool SrsPublishRecvThread::can_handle() @@ -359,6 +364,7 @@ void SrsPublishRecvThread::on_recv_error(int ret) st_cond_signal(error); } +#ifdef SRS_PERF_MERGED_READ void SrsPublishRecvThread::on_read(ssize_t nread) { if (nread < 0 || mr_sleep_ms <= 0) { @@ -378,6 +384,8 @@ void SrsPublishRecvThread::on_read(ssize_t nread) void SrsPublishRecvThread::on_buffer_change(int nb_buffer) { + srs_assert(nb_buffer > 0); + // set percent. mr_small_bytes = (int)(nb_buffer / SRS_MR_SMALL_PERCENT); // select the smaller @@ -399,3 +407,4 @@ void SrsPublishRecvThread::on_buffer_change(int nb_buffer) srs_trace("merged read, buffer=%d, small=%d, sleep=%d", nb_buffer, mr_small_bytes, mr_sleep_ms); } +#endif diff --git a/trunk/src/app/srs_app_recv_thread.hpp b/trunk/src/app/srs_app_recv_thread.hpp index 7b947562c3..cdaf0c1d54 100644 --- a/trunk/src/app/srs_app_recv_thread.hpp +++ b/trunk/src/app/srs_app_recv_thread.hpp @@ -34,6 +34,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +#include class SrsRtmpServer; class SrsMessage; @@ -133,7 +134,10 @@ class SrsQueueRecvThread : public ISrsMessageHandler * the publish recv thread got message and callback the source method to process message. * @see: https://github.com/winlinvip/simple-rtmp-server/issues/237 */ -class SrsPublishRecvThread : virtual public ISrsMessageHandler, virtual public IMergeReadHandler +class SrsPublishRecvThread : virtual public ISrsMessageHandler +#ifdef SRS_PERF_MERGED_READ + , virtual public IMergeReadHandler +#endif { private: SrsRecvThread trd; @@ -178,8 +182,10 @@ class SrsPublishRecvThread : virtual public ISrsMessageHandler, virtual public I virtual void on_recv_error(int ret); // interface IMergeReadHandler public: +#ifdef SRS_PERF_MERGED_READ virtual void on_read(ssize_t nread); virtual void on_buffer_change(int nb_buffer); +#endif }; #endif diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 2bc63f30e2..0a67aa8f9a 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 49 +#define VERSION_REVISION 50 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/core/srs_core_performance.cpp b/trunk/src/core/srs_core_performance.cpp new file mode 100644 index 0000000000..5da7069dad --- /dev/null +++ b/trunk/src/core/srs_core_performance.cpp @@ -0,0 +1,25 @@ +/* +The MIT License (MIT) + +Copyright (c) 2013-2014 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + diff --git a/trunk/src/core/srs_core_performance.hpp b/trunk/src/core/srs_core_performance.hpp new file mode 100644 index 0000000000..ef207c3d53 --- /dev/null +++ b/trunk/src/core/srs_core_performance.hpp @@ -0,0 +1,71 @@ +/* +The MIT License (MIT) + +Copyright (c) 2013-2014 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef SRS_CORE_PERFORMANCE_HPP +#define SRS_CORE_PERFORMANCE_HPP + +/* +#include +*/ + +#include + +/** +* this file defines the perfromance options. +*/ + +/** +* to improve read performance, merge some packets then read, +* when it on and read small bytes, we sleep to wait more data., +* that is, we merge some data to read together. +* @see https://github.com/winlinvip/simple-rtmp-server/issues/241 +* @remark other macros: +* SOCKET_MAX_BUF, the max size of user-space buffer. +* SOCKET_READ_SIZE, the user space buffer for socket. +* SRS_MR_MAX_BITRATE_KBPS, the kbps of stream for system to guess the sleep time. +* SRS_MR_AVERAGE_BITRATE_KBPS, the average kbps of system. +* SRS_MR_MIN_BITRATE_KBPS, the min kbps of system. +* SRS_MR_MAX_SLEEP_MS, the max sleep time, the latency+ when sleep+. +* SRS_MR_SMALL_BYTES, sleep when got small bytes, the latency+ when small+. +* SRS_MR_SMALL_PERCENT, to calc the small bytes = SRS_MR_SOCKET_BUFFER/percent. +* SRS_MR_SOCKET_BUFFER, the socket buffer to set fd. +* @remark the actual socket buffer used to set the buffer user-space size. +* buffer = min(SOCKET_MAX_BUF, SRS_MR_SOCKET_BUFFER, SOCKET_READ_SIZE) +* small bytes = max(buffer/SRS_MR_SMALL_PERCENT, SRS_MR_SMALL_BYTES) +* sleep = calc the sleep by kbps and buffer. +* @remark the main merged-read algorithm: +* while true: +* nread = read from socket. +* sleep if nread < small bytes +* process bytes. +* @example, for the default settings, this algorithm will use: +* socket buffer set to 64KB, +* user space buffer set to 64KB, +* buffer=65536B, small=4096B, sleep=780ms +* that is, when got nread bytes smaller than 4KB, sleep(780ms). +*/ +#define SRS_PERF_MERGED_READ +#undef SRS_PERF_MERGED_READ + +#endif + diff --git a/trunk/src/rtmp/srs_protocol_buffer.cpp b/trunk/src/rtmp/srs_protocol_buffer.cpp index 12cc48b305..a54428fd7c 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.cpp +++ b/trunk/src/rtmp/srs_protocol_buffer.cpp @@ -26,6 +26,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #include +#include // the max header size, // @see SrsProtocol::read_message_header(). @@ -72,6 +73,7 @@ void SrsSimpleBuffer::append(const char* bytes, int size) data.insert(data.end(), bytes, bytes + size); } +#ifdef SRS_PERF_MERGED_READ IMergeReadHandler::IMergeReadHandler() { } @@ -79,11 +81,14 @@ IMergeReadHandler::IMergeReadHandler() IMergeReadHandler::~IMergeReadHandler() { } +#endif SrsFastBuffer::SrsFastBuffer() { +#ifdef SRS_PERF_MERGED_READ merged_read = false; _handler = NULL; +#endif p = end = buffer = NULL; nb_buffer = 0; @@ -160,6 +165,7 @@ int SrsFastBuffer::grow(ISrsBufferReader* reader, int required_size) return ret; } +#ifdef SRS_PERF_MERGED_READ /** * to improve read performance, merge some packets then read, * when it on and read small bytes, we sleep to wait more data., @@ -169,6 +175,7 @@ int SrsFastBuffer::grow(ISrsBufferReader* reader, int required_size) if (merged_read && _handler) { _handler->on_read(nread); } +#endif // we just move the ptr to next. srs_assert((int)nread > 0); @@ -178,6 +185,7 @@ int SrsFastBuffer::grow(ISrsBufferReader* reader, int required_size) return ret; } +#ifdef SRS_PERF_MERGED_READ void SrsFastBuffer::set_merge_read(bool v, int max_buffer, IMergeReadHandler* handler) { merged_read = v; @@ -194,6 +202,7 @@ void SrsFastBuffer::set_merge_read(bool v, int max_buffer, IMergeReadHandler* ha _handler->on_buffer_change(nb_buffer); } } +#endif void SrsFastBuffer::on_chunk_size(int32_t chunk_size) { @@ -208,9 +217,11 @@ void SrsFastBuffer::on_chunk_size(int32_t chunk_size) reset_buffer(buffer_size); } +#ifdef SRS_PERF_MERGED_READ if (_handler) { _handler->on_buffer_change(nb_buffer); } +#endif } int SrsFastBuffer::buffer_size() diff --git a/trunk/src/rtmp/srs_protocol_buffer.hpp b/trunk/src/rtmp/srs_protocol_buffer.hpp index 6cae2915aa..396aa04ee2 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.hpp +++ b/trunk/src/rtmp/srs_protocol_buffer.hpp @@ -33,6 +33,17 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +#include + +// 4KB=4096 +// 8KB=8192 +// 16KB=16384 +// 32KB=32768 +// 64KB=65536 +// @see https://github.com/winlinvip/simple-rtmp-server/issues/241 +#define SOCKET_READ_SIZE 65536 +// the max buffer for user space socket buffer. +#define SOCKET_MAX_BUF SOCKET_READ_SIZE /** * the simple buffer use vector to append bytes, @@ -71,16 +82,7 @@ class SrsSimpleBuffer virtual void append(const char* bytes, int size); }; -// 4KB=4096 -// 8KB=8192 -// 16KB=16384 -// 32KB=32768 -// 64KB=65536 -// @see https://github.com/winlinvip/simple-rtmp-server/issues/241 -#define SOCKET_READ_SIZE 65536 -// the max buffer for user space socket buffer. -#define SOCKET_MAX_BUF SOCKET_READ_SIZE - +#ifdef SRS_PERF_MERGED_READ /** * to improve read performance, merge some packets then read, * when it on and read small bytes, we sleep to wait more data., @@ -105,17 +107,21 @@ class IMergeReadHandler */ virtual void on_buffer_change(int nb_buffer) = 0; }; +#endif /** * the buffer provices bytes cache for protocol. generally, * protocol recv data from socket, put into buffer, decode to RTMP message. */ +// TODO: FIXME: add utest for it. class SrsFastBuffer { private: +#ifdef SRS_PERF_MERGED_READ // the merged handler bool merged_read; IMergeReadHandler* _handler; +#endif // the user-space buffer to fill by reader, // which use fast index and reset when chunk body read ok. // @see https://github.com/winlinvip/simple-rtmp-server/issues/248 @@ -160,6 +166,7 @@ class SrsFastBuffer */ virtual int grow(ISrsBufferReader* reader, int required_size); public: +#ifdef SRS_PERF_MERGED_READ /** * to improve read performance, merge some packets then read, * when it on and read small bytes, we sleep to wait more data., @@ -170,6 +177,8 @@ class SrsFastBuffer * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 */ virtual void set_merge_read(bool v, int max_buffer, IMergeReadHandler* handler); +#endif +public: /** * when chunk size changed, the buffer should change the buffer also. * to keep the socket buffer size always greater than chunk size. diff --git a/trunk/src/rtmp/srs_protocol_rtmp.cpp b/trunk/src/rtmp/srs_protocol_rtmp.cpp index c6ccc1deeb..2c098a6d2c 100644 --- a/trunk/src/rtmp/srs_protocol_rtmp.cpp +++ b/trunk/src/rtmp/srs_protocol_rtmp.cpp @@ -745,10 +745,12 @@ void SrsRtmpServer::set_auto_response(bool v) protocol->set_auto_response(v); } +#ifdef SRS_PERF_MERGED_READ void SrsRtmpServer::set_merge_read(bool v, int max_buffer, IMergeReadHandler* handler) { protocol->set_merge_read(v, max_buffer, handler); } +#endif void SrsRtmpServer::set_recv_timeout(int64_t timeout_us) { diff --git a/trunk/src/rtmp/srs_protocol_rtmp.hpp b/trunk/src/rtmp/srs_protocol_rtmp.hpp index de2e4c47ed..cab5b3065a 100644 --- a/trunk/src/rtmp/srs_protocol_rtmp.hpp +++ b/trunk/src/rtmp/srs_protocol_rtmp.hpp @@ -33,6 +33,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +#include class SrsProtocol; class ISrsProtocolReaderWriter; @@ -343,6 +344,7 @@ class SrsRtmpServer * @see: https://github.com/winlinvip/simple-rtmp-server/issues/217 */ virtual void set_auto_response(bool v); +#ifdef SRS_PERF_MERGED_READ /** * to improve read performance, merge some packets then read, * when it on and read small bytes, we sleep to wait more data., @@ -353,6 +355,7 @@ class SrsRtmpServer * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 */ virtual void set_merge_read(bool v, int max_buffer, IMergeReadHandler* handler); +#endif /** * set/get the recv timeout in us. * if timeout, recv/send message return ERROR_SOCKET_TIMEOUT. diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp index 609753992f..23fdfee926 100644 --- a/trunk/src/rtmp/srs_protocol_stack.cpp +++ b/trunk/src/rtmp/srs_protocol_stack.cpp @@ -479,10 +479,12 @@ int SrsProtocol::manual_response_flush() return ret; } +#ifdef SRS_PERF_MERGED_READ void SrsProtocol::set_merge_read(bool v, int max_buffer, IMergeReadHandler* handler) { in_buffer->set_merge_read(v, max_buffer, handler); } +#endif void SrsProtocol::set_recv_timeout(int64_t timeout_us) { diff --git a/trunk/src/rtmp/srs_protocol_stack.hpp b/trunk/src/rtmp/srs_protocol_stack.hpp index 18531224f7..49f332b005 100644 --- a/trunk/src/rtmp/srs_protocol_stack.hpp +++ b/trunk/src/rtmp/srs_protocol_stack.hpp @@ -42,6 +42,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #include +#include class ISrsProtocolReaderWriter; class SrsFastBuffer; @@ -271,6 +272,7 @@ class SrsProtocol */ virtual int manual_response_flush(); public: +#ifdef SRS_PERF_MERGED_READ /** * to improve read performance, merge some packets then read, * when it on and read small bytes, we sleep to wait more data., @@ -281,6 +283,7 @@ class SrsProtocol * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 */ virtual void set_merge_read(bool v, int max_buffer, IMergeReadHandler* handler); +#endif public: /** * set/get the recv timeout in us. diff --git a/trunk/src/srs/srs.upp b/trunk/src/srs/srs.upp index 3e0b8a62a9..c400d51a73 100755 --- a/trunk/src/srs/srs.upp +++ b/trunk/src/srs/srs.upp @@ -15,6 +15,8 @@ file ..\core\srs_core.cpp, ..\core\srs_core_autofree.hpp, ..\core\srs_core_autofree.cpp, + ..\core\srs_core_performance.hpp, + ..\core\srs_core_performance.cpp, kernel readonly separator, ..\kernel\srs_kernel_codec.hpp, ..\kernel\srs_kernel_codec.cpp, diff --git a/trunk/src/utest/srs_utest_kernel.cpp b/trunk/src/utest/srs_utest_kernel.cpp index 5c5e968d2c..45bcbdcd09 100644 --- a/trunk/src/utest/srs_utest_kernel.cpp +++ b/trunk/src/utest/srs_utest_kernel.cpp @@ -203,7 +203,7 @@ int MockBufferReader::read(void* buf, size_t size, ssize_t* nread) VOID TEST(KernelBufferTest, DefaultObject) { - SrsFastBuffer b; + SrsSimpleBuffer b; EXPECT_EQ(0, b.length()); EXPECT_EQ(NULL, b.bytes()); @@ -211,7 +211,7 @@ VOID TEST(KernelBufferTest, DefaultObject) VOID TEST(KernelBufferTest, AppendBytes) { - SrsFastBuffer b; + SrsSimpleBuffer b; char winlin[] = "winlin"; b.append(winlin, strlen(winlin)); @@ -231,7 +231,7 @@ VOID TEST(KernelBufferTest, AppendBytes) VOID TEST(KernelBufferTest, EraseBytes) { - SrsFastBuffer b; + SrsSimpleBuffer b; b.erase(0); b.erase(-1); @@ -265,22 +265,21 @@ VOID TEST(KernelBufferTest, EraseBytes) EXPECT_EQ(0, b.length()); } -VOID TEST(KernelBufferTest, Grow) +VOID TEST(KernelFastBufferTest, Grow) { SrsFastBuffer b; MockBufferReader r("winlin"); b.grow(&r, 1); - EXPECT_EQ(6, b.length()); - EXPECT_EQ('w', b.bytes()[0]); + EXPECT_EQ('w', b.read_1byte()); b.grow(&r, 3); - EXPECT_EQ(6, b.length()); - EXPECT_EQ('n', b.bytes()[2]); + b.skip(1); + EXPECT_EQ('n', b.read_1byte()); b.grow(&r, 100); - EXPECT_EQ(102, b.length()); - EXPECT_EQ('l', b.bytes()[99]); + b.skip(99); + EXPECT_EQ('w', b.read_1byte()); } /** diff --git a/trunk/src/utest/srs_utest_protocol.hpp b/trunk/src/utest/srs_utest_protocol.hpp index 060f5a54ec..351975987e 100644 --- a/trunk/src/utest/srs_utest_protocol.hpp +++ b/trunk/src/utest/srs_utest_protocol.hpp @@ -78,9 +78,9 @@ class MockBufferIO : public ISrsProtocolReaderWriter int64_t recv_bytes; int64_t send_bytes; // data source for socket read. - SrsFastBuffer in_buffer; + SrsSimpleBuffer in_buffer; // data buffer for socket send. - SrsFastBuffer out_buffer; + SrsSimpleBuffer out_buffer; public: MockBufferIO(); virtual ~MockBufferIO(); From 90992b31a3cd9621243a746251f8ab719c9b100e Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 4 Dec 2014 13:54:02 +0800 Subject: [PATCH 303/800] for bug #194, add the send macro to performance. --- trunk/src/app/srs_app_rtmp_conn.cpp | 3 ++- trunk/src/core/srs_core_performance.hpp | 14 +++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index 3ba9168792..9c60430e2f 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -49,6 +49,7 @@ using namespace std; #include #include #include +#include // when stream is busy, for example, streaming is already // publishing, when a new client to request to publish, @@ -591,7 +592,7 @@ int SrsRtmpConn::do_playing(SrsSource* source, SrsQueueRecvThread* trd) // no message to send, sleep a while. if (count <= 0) { srs_verbose("sleep for no messages to send"); - st_usleep(SRS_CONSTS_RTMP_PULSE_TIMEOUT_US); + st_usleep(SRS_PERF_SEND_MSGS_CACHE * 1000); } // reportable diff --git a/trunk/src/core/srs_core_performance.hpp b/trunk/src/core/srs_core_performance.hpp index ef207c3d53..f07e2bd375 100644 --- a/trunk/src/core/srs_core_performance.hpp +++ b/trunk/src/core/srs_core_performance.hpp @@ -64,8 +64,20 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * buffer=65536B, small=4096B, sleep=780ms * that is, when got nread bytes smaller than 4KB, sleep(780ms). */ -#define SRS_PERF_MERGED_READ #undef SRS_PERF_MERGED_READ +#define SRS_PERF_MERGED_READ + +/** +* the send cache time in ms. +* to improve send performance, cache msgs and send in a time. +* for example, cache 500ms videos and audios, then convert all these +* msgs to iovecs, finally use writev to send. +* @remark this largely improve performance, from 3.5k+ to 7.5k+. +* the latency+ when cache+. +* @remark the socket send buffer default to 185KB, it large enough. +* @see https://github.com/winlinvip/simple-rtmp-server/issues/194 +*/ +#define SRS_PERF_SEND_MSGS_CACHE 500 #endif From 511a482fba29c74a10096ef600dcbdfdc331778d Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 4 Dec 2014 14:05:35 +0800 Subject: [PATCH 304/800] for bug #241 and #248, improve about 25% performance. 2.0.50 --- README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index e09e8fc7f8..bef0e4dc5f 100755 --- a/README.md +++ b/README.md @@ -485,6 +485,7 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History +* v2.0, 2014-12-04, enable [#241](https://github.com/winlinvip/simple-rtmp-server/issues/241) and [#248](https://github.com/winlinvip/simple-rtmp-server/issues/248), improve about 25% performance. 2.0.50 * v2.0, 2014-12-04, fix [#248](https://github.com/winlinvip/simple-rtmp-server/issues/248), improve about 15% performance for fast buffer. 2.0.49 * v2.0, 2014-12-03, fix [#244](https://github.com/winlinvip/simple-rtmp-server/issues/244), conn thread use cond to wait for recv thread error. 2.0.47. * v2.0, 2014-12-02, merge [#239](https://github.com/winlinvip/simple-rtmp-server/pull/239), traverse the token before response connect. 2.0.45. @@ -728,12 +729,13 @@ The play benchmark by [st-load](https://github.com/winlinvip/st-load): The publish benchmark by [st-load](https://github.com/winlinvip/st-load): -* 2014-12-03, SRS 1.0.10, 1k(1200) publishers, 96%CPU, 43MB. -* 2014-12-03, SRS 2.0.12, 1k(1200) publishers, 96%CPU, 43MB. -* 2014-12-03, SRS 2.0.47, 1k(1200) publishers, 84%CPU, 76MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/787ab674e38734ea8e0678101614fdcd84645dc8) -* 2014-12-03, SRS 2.0.47, 1k(1400) publishers, 95%CPU, 140MB. -* 2014-12-03, SRS 2.0.48, 1k(1400) publishers, 95%CPU, 140MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/f35ec2155b1408d528a9f37da7904c9625186bcf) -* 2014-12-04, SRS 2.0.49, 1k(1400) publishers, 68%CPU, 144MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/29324fab469e0f7cef9ad04ffdbce832ac7dd9ff) +* 2014-12-03, SRS 1.0.10, 1.2k(1200) publishers, 96%CPU, 43MB. +* 2014-12-03, SRS 2.0.12, 1.2k(1200) publishers, 96%CPU, 43MB. +* 2014-12-03, SRS 2.0.47, 1.2k(1200) publishers, 84%CPU, 76MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/787ab674e38734ea8e0678101614fdcd84645dc8) +* 2014-12-03, SRS 2.0.47, 1.4k(1400) publishers, 95%CPU, 140MB. +* 2014-12-03, SRS 2.0.48, 1.4k(1400) publishers, 95%CPU, 140MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/f35ec2155b1408d528a9f37da7904c9625186bcf) +* 2014-12-04, SRS 2.0.49, 1.4k(1400) publishers, 68%CPU, 144MB. +* 2014-12-04, SRS 2.0.49, 2.5k(2500) publishers, 95%CPU, 404MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/29324fab469e0f7cef9ad04ffdbce832ac7dd9ff) ## Architecture From b84e87845e1acfc5f46a41e5ffd5249484bdb456 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 4 Dec 2014 14:14:29 +0800 Subject: [PATCH 305/800] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bef0e4dc5f..3c32d00fa8 100755 --- a/README.md +++ b/README.md @@ -485,7 +485,7 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History -* v2.0, 2014-12-04, enable [#241](https://github.com/winlinvip/simple-rtmp-server/issues/241) and [#248](https://github.com/winlinvip/simple-rtmp-server/issues/248), improve about 25% performance. 2.0.50 +* v2.0, 2014-12-04, enable [#241](https://github.com/winlinvip/simple-rtmp-server/issues/241) and [#248](https://github.com/winlinvip/simple-rtmp-server/issues/248), +25% performance, 2.5k publisher. 2.0.50 * v2.0, 2014-12-04, fix [#248](https://github.com/winlinvip/simple-rtmp-server/issues/248), improve about 15% performance for fast buffer. 2.0.49 * v2.0, 2014-12-03, fix [#244](https://github.com/winlinvip/simple-rtmp-server/issues/244), conn thread use cond to wait for recv thread error. 2.0.47. * v2.0, 2014-12-02, merge [#239](https://github.com/winlinvip/simple-rtmp-server/pull/239), traverse the token before response connect. 2.0.45. From f57801eb46c16755b173984b915a4166922df6a6 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 4 Dec 2014 14:58:40 +0800 Subject: [PATCH 306/800] fix #249, cache the chunk headers info to +5% or +10% performance. 2.0.51 --- trunk/src/core/srs_core.hpp | 2 +- trunk/src/core/srs_core_performance.hpp | 7 ++++ trunk/src/rtmp/srs_protocol_stack.cpp | 46 ++++++++++++++++++++----- trunk/src/rtmp/srs_protocol_stack.hpp | 6 ++++ 4 files changed, 52 insertions(+), 9 deletions(-) diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 0a67aa8f9a..28bc0c07eb 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 50 +#define VERSION_REVISION 51 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/core/srs_core_performance.hpp b/trunk/src/core/srs_core_performance.hpp index f07e2bd375..c963a61fb0 100644 --- a/trunk/src/core/srs_core_performance.hpp +++ b/trunk/src/core/srs_core_performance.hpp @@ -79,5 +79,12 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define SRS_PERF_SEND_MSGS_CACHE 500 +/** +* how many chunk stream to cache, [0, N]. +* to imporove about 10% performance when chunk size small, and 5% for large chunk. +* @see https://github.com/winlinvip/simple-rtmp-server/issues/249 +*/ +#define SRS_PERF_CHUNK_STREAM_CACHE 16 + #endif diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp index 23fdfee926..03d9e5e7de 100644 --- a/trunk/src/rtmp/srs_protocol_stack.cpp +++ b/trunk/src/rtmp/srs_protocol_stack.cpp @@ -417,6 +417,16 @@ SrsProtocol::SrsProtocol(ISrsProtocolReaderWriter* io) warned_c0c3_cache_dry = false; auto_response_when_recv = true; + + cs_cache = new SrsChunkStream*[SRS_PERF_CHUNK_STREAM_CACHE]; + for (int cid = 0; cid < SRS_PERF_CHUNK_STREAM_CACHE; cid++) { + SrsChunkStream* cs = new SrsChunkStream(cid); + // set the perfer cid of chunk, + // which will copy to the message received. + cs->header.perfer_cid = cid; + + cs_cache[cid] = cs; + } } SrsProtocol::~SrsProtocol() @@ -448,6 +458,13 @@ SrsProtocol::~SrsProtocol() free(out_iovs); out_iovs = NULL; } + + // free all chunk stream cache. + for (int i = 0; i < SRS_PERF_CHUNK_STREAM_CACHE; i++) { + SrsChunkStream* cs = cs_cache[i]; + srs_freep(cs); + } + srs_freep(cs_cache); } void SrsProtocol::set_auto_response(bool v) @@ -1102,17 +1119,30 @@ int SrsProtocol::recv_interlaced_message(SrsMessage** pmsg) // get the cached chunk stream. SrsChunkStream* chunk = NULL; - if (chunk_streams.find(cid) == chunk_streams.end()) { - chunk = chunk_streams[cid] = new SrsChunkStream(cid); - // set the perfer cid of chunk, - // which will copy to the message received. - chunk->header.perfer_cid = cid; - srs_verbose("cache new chunk stream: fmt=%d, cid=%d", fmt, cid); - } else { - chunk = chunk_streams[cid]; + // use chunk stream cache to get the chunk info. + // @see https://github.com/winlinvip/simple-rtmp-server/issues/249 + if (cid < SRS_PERF_CHUNK_STREAM_CACHE) { + // chunk stream cache hit. + srs_verbose("cs-cache hit, cid=%d", cid); + // already init, use it direclty + chunk = cs_cache[cid]; srs_verbose("cached chunk stream: fmt=%d, cid=%d, size=%d, message(type=%d, size=%d, time=%"PRId64", sid=%d)", chunk->fmt, chunk->cid, (chunk->msg? chunk->msg->size : 0), chunk->header.message_type, chunk->header.payload_length, chunk->header.timestamp, chunk->header.stream_id); + } else { + // chunk stream cache miss, use map. + if (chunk_streams.find(cid) == chunk_streams.end()) { + chunk = chunk_streams[cid] = new SrsChunkStream(cid); + // set the perfer cid of chunk, + // which will copy to the message received. + chunk->header.perfer_cid = cid; + srs_verbose("cache new chunk stream: fmt=%d, cid=%d", fmt, cid); + } else { + chunk = chunk_streams[cid]; + srs_verbose("cached chunk stream: fmt=%d, cid=%d, size=%d, message(type=%d, size=%d, time=%"PRId64", sid=%d)", + chunk->fmt, chunk->cid, (chunk->msg? chunk->msg->size : 0), chunk->header.message_type, chunk->header.payload_length, + chunk->header.timestamp, chunk->header.stream_id); + } } // chunk stream message header diff --git a/trunk/src/rtmp/srs_protocol_stack.hpp b/trunk/src/rtmp/srs_protocol_stack.hpp index 49f332b005..026c755dc6 100644 --- a/trunk/src/rtmp/srs_protocol_stack.hpp +++ b/trunk/src/rtmp/srs_protocol_stack.hpp @@ -205,6 +205,12 @@ class SrsProtocol */ std::map chunk_streams; /** + * cache some frequently used chunk header. + * cs_cache, the chunk stream cache. + * @see https://github.com/winlinvip/simple-rtmp-server/issues/249 + */ + SrsChunkStream** cs_cache; + /** * bytes buffer cache, recv from skt, provide services for stream. */ SrsFastBuffer* in_buffer; From 310f51e6a1ad787192b9d220b11bf72a913d5676 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 4 Dec 2014 15:00:22 +0800 Subject: [PATCH 307/800] update readme. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3c32d00fa8..b1a2d267d7 100755 --- a/README.md +++ b/README.md @@ -736,6 +736,7 @@ The publish benchmark by [st-load](https://github.com/winlinvip/st-load): * 2014-12-03, SRS 2.0.48, 1.4k(1400) publishers, 95%CPU, 140MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/f35ec2155b1408d528a9f37da7904c9625186bcf) * 2014-12-04, SRS 2.0.49, 1.4k(1400) publishers, 68%CPU, 144MB. * 2014-12-04, SRS 2.0.49, 2.5k(2500) publishers, 95%CPU, 404MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/29324fab469e0f7cef9ad04ffdbce832ac7dd9ff) +* 2014-12-04, SRS 2.0.51, 2.5k(2500) publishers, 92%CPU, 259MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/f57801eb46c16755b173984b915a4166922df6a6) ## Architecture From 4b09531e2fb9b7ac9b892aaf2f6aec8b0200300c Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 4 Dec 2014 15:33:17 +0800 Subject: [PATCH 308/800] for bug #241, simplify the merged read config macros. --- README.md | 2 +- trunk/src/app/srs_app_recv_thread.cpp | 54 +++---------------------- trunk/src/app/srs_app_recv_thread.hpp | 3 -- trunk/src/core/srs_core_performance.hpp | 13 +++++- trunk/src/rtmp/srs_protocol_buffer.cpp | 16 ++------ trunk/src/rtmp/srs_protocol_buffer.hpp | 15 ------- 6 files changed, 20 insertions(+), 83 deletions(-) diff --git a/README.md b/README.md index b1a2d267d7..ae0f54778c 100755 --- a/README.md +++ b/README.md @@ -736,7 +736,7 @@ The publish benchmark by [st-load](https://github.com/winlinvip/st-load): * 2014-12-03, SRS 2.0.48, 1.4k(1400) publishers, 95%CPU, 140MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/f35ec2155b1408d528a9f37da7904c9625186bcf) * 2014-12-04, SRS 2.0.49, 1.4k(1400) publishers, 68%CPU, 144MB. * 2014-12-04, SRS 2.0.49, 2.5k(2500) publishers, 95%CPU, 404MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/29324fab469e0f7cef9ad04ffdbce832ac7dd9ff) -* 2014-12-04, SRS 2.0.51, 2.5k(2500) publishers, 92%CPU, 259MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/f57801eb46c16755b173984b915a4166922df6a6) +* 2014-12-04, SRS 2.0.51, 2.5k(2500) publishers, 91%CPU, 259MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/f57801eb46c16755b173984b915a4166922df6a6) ## Architecture diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index c296e24344..b9dc9818e0 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -30,23 +30,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include -// when we read from socket less than this value, -// sleep a while to merge read. -// @see https://github.com/winlinvip/simple-rtmp-server/issues/241 -// use the bitrate in kbps to calc the max sleep time. -#define SRS_MR_MAX_BITRATE_KBPS 10000 -#define SRS_MR_AVERAGE_BITRATE_KBPS 1000 -#define SRS_MR_MIN_BITRATE_KBPS 32 -// the max sleep time in ms -#define SRS_MR_MAX_SLEEP_MS 2500 -// the max small bytes to group -#define SRS_MR_SMALL_BYTES 4096 -// the percent of buffer to set as small bytes -#define SRS_MR_SMALL_PERCENT 100 -// set the socket buffer to specified bytes. -// the underlayer api will set to SRS_MR_SOCKET_BUFFER bytes. -#define SRS_MR_SOCKET_BUFFER SOCKET_READ_SIZE - ISrsMessageHandler::ISrsMessageHandler() { } @@ -253,8 +236,6 @@ SrsPublishRecvThread::SrsPublishRecvThread( error = st_cond_new(); mr_fd = fd; - mr_small_bytes = 0; - mr_sleep_ms = 0; } SrsPublishRecvThread::~SrsPublishRecvThread() @@ -309,7 +290,8 @@ void SrsPublishRecvThread::on_thread_start() } getsockopt(mr_fd, SOL_SOCKET, SO_RCVBUF, &nb_rbuf, &sock_buf_size); - srs_trace("set socket buffer to %d, actual %d KB", SRS_MR_SOCKET_BUFFER / 1024, nb_rbuf / 1024); + srs_trace("merged read sockbuf=%d, actual=%d, sleep %d when nread<=%d", + SRS_MR_SOCKET_BUFFER, nb_rbuf, SRS_MR_MAX_SLEEP_MS, SRS_MR_SMALL_BYTES); // enable the merge read // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 @@ -367,7 +349,7 @@ void SrsPublishRecvThread::on_recv_error(int ret) #ifdef SRS_PERF_MERGED_READ void SrsPublishRecvThread::on_read(ssize_t nread) { - if (nread < 0 || mr_sleep_ms <= 0) { + if (nread < 0 || SRS_MR_MAX_SLEEP_MS <= 0) { return; } @@ -377,34 +359,8 @@ void SrsPublishRecvThread::on_read(ssize_t nread) * that is, we merge some data to read together. * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 */ - if (nread < mr_small_bytes) { - st_usleep(mr_sleep_ms * 1000); + if (nread < SRS_MR_SMALL_BYTES) { + st_usleep(SRS_MR_MAX_SLEEP_MS * 1000); } } - -void SrsPublishRecvThread::on_buffer_change(int nb_buffer) -{ - srs_assert(nb_buffer > 0); - - // set percent. - mr_small_bytes = (int)(nb_buffer / SRS_MR_SMALL_PERCENT); - // select the smaller - mr_small_bytes = srs_max(mr_small_bytes, SRS_MR_SMALL_BYTES); - - // the recv sleep is [buffer / max_kbps, buffer / min_kbps] - // for example, buffer is 256KB, max kbps is 10Mbps, min kbps is 10Kbps, - // the buffer is 256KB*8=2048Kb, which can provides sleep time in - // min: 2038Kb/10Mbps=2038Kb/10Kbpms=203.8ms - // max: 2038Kb/10Kbps=203.8s - // sleep = Xb * 8 / (N * 1000 b / 1000 ms) = (X * 8 / N) ms - // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 - int min_sleep = (int)(nb_buffer * 8.0 / SRS_MR_MAX_BITRATE_KBPS); - int average_sleep = (int)(nb_buffer * 8.0 / SRS_MR_AVERAGE_BITRATE_KBPS); - int max_sleep = (int)(nb_buffer * 8.0 / SRS_MR_MIN_BITRATE_KBPS); - // 80% min, 16% average, 4% max. - mr_sleep_ms = (int)(min_sleep * 0.8 + average_sleep * 0.16 + max_sleep * 0.04); - mr_sleep_ms = srs_min(mr_sleep_ms, SRS_MR_MAX_SLEEP_MS); - - srs_trace("merged read, buffer=%d, small=%d, sleep=%d", nb_buffer, mr_small_bytes, mr_sleep_ms); -} #endif diff --git a/trunk/src/app/srs_app_recv_thread.hpp b/trunk/src/app/srs_app_recv_thread.hpp index cdaf0c1d54..6c219a62aa 100644 --- a/trunk/src/app/srs_app_recv_thread.hpp +++ b/trunk/src/app/srs_app_recv_thread.hpp @@ -147,8 +147,6 @@ class SrsPublishRecvThread : virtual public ISrsMessageHandler // for mr(merged read), // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 int mr_fd; - int mr_small_bytes; - int mr_sleep_ms; // the recv thread error code. int recv_error_code; SrsRtmpConn* _conn; @@ -184,7 +182,6 @@ class SrsPublishRecvThread : virtual public ISrsMessageHandler public: #ifdef SRS_PERF_MERGED_READ virtual void on_read(ssize_t nread); - virtual void on_buffer_change(int nb_buffer); #endif }; diff --git a/trunk/src/core/srs_core_performance.hpp b/trunk/src/core/srs_core_performance.hpp index c963a61fb0..0b4853978f 100644 --- a/trunk/src/core/srs_core_performance.hpp +++ b/trunk/src/core/srs_core_performance.hpp @@ -64,8 +64,17 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * buffer=65536B, small=4096B, sleep=780ms * that is, when got nread bytes smaller than 4KB, sleep(780ms). */ -#undef SRS_PERF_MERGED_READ -#define SRS_PERF_MERGED_READ +#if 1 + // to enable merged read. + #define SRS_PERF_MERGED_READ + // the max sleep time in ms + #define SRS_MR_MAX_SLEEP_MS 1000 + // the max small bytes to group + #define SRS_MR_SMALL_BYTES 4096 + // the underlayer api will set to SRS_MR_SOCKET_BUFFER bytes. + // 4KB=4096, 8KB=8192, 16KB=16384, 32KB=32768, 64KB=65536 + #define SRS_MR_SOCKET_BUFFER 65536 +#endif /** * the send cache time in ms. diff --git a/trunk/src/rtmp/srs_protocol_buffer.cpp b/trunk/src/rtmp/srs_protocol_buffer.cpp index a54428fd7c..82ef77f37f 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.cpp +++ b/trunk/src/rtmp/srs_protocol_buffer.cpp @@ -93,7 +93,7 @@ SrsFastBuffer::SrsFastBuffer() p = end = buffer = NULL; nb_buffer = 0; - reset_buffer(SOCKET_READ_SIZE); + reset_buffer(SRS_MR_SOCKET_BUFFER); } SrsFastBuffer::~SrsFastBuffer() @@ -192,15 +192,11 @@ void SrsFastBuffer::set_merge_read(bool v, int max_buffer, IMergeReadHandler* ha _handler = handler; // limit the max buffer. - int buffer_size = srs_min(max_buffer, SOCKET_MAX_BUF); + int buffer_size = srs_min(max_buffer, SRS_MR_SOCKET_BUFFER); if (v && buffer_size != nb_buffer) { reset_buffer(buffer_size); } - - if (_handler) { - _handler->on_buffer_change(nb_buffer); - } } #endif @@ -211,17 +207,11 @@ void SrsFastBuffer::on_chunk_size(int32_t chunk_size) } // limit the max buffer. - int buffer_size = srs_min(chunk_size, SOCKET_MAX_BUF); + int buffer_size = srs_min(chunk_size, SRS_MR_SOCKET_BUFFER); if (buffer_size != nb_buffer) { reset_buffer(buffer_size); } - -#ifdef SRS_PERF_MERGED_READ - if (_handler) { - _handler->on_buffer_change(nb_buffer); - } -#endif } int SrsFastBuffer::buffer_size() diff --git a/trunk/src/rtmp/srs_protocol_buffer.hpp b/trunk/src/rtmp/srs_protocol_buffer.hpp index 396aa04ee2..1e4a8375aa 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.hpp +++ b/trunk/src/rtmp/srs_protocol_buffer.hpp @@ -35,16 +35,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include -// 4KB=4096 -// 8KB=8192 -// 16KB=16384 -// 32KB=32768 -// 64KB=65536 -// @see https://github.com/winlinvip/simple-rtmp-server/issues/241 -#define SOCKET_READ_SIZE 65536 -// the max buffer for user space socket buffer. -#define SOCKET_MAX_BUF SOCKET_READ_SIZE - /** * the simple buffer use vector to append bytes, * it's for hls and http, and need to be refined in future. @@ -101,11 +91,6 @@ class IMergeReadHandler * @remark, it only for server-side, client srs-librtmp just ignore. */ virtual void on_read(ssize_t nread) = 0; - /** - * when buffer size changed. - * @param nb_buffer the new buffer size. - */ - virtual void on_buffer_change(int nb_buffer) = 0; }; #endif From d026861e239eb674a076175342477be454ce0208 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 4 Dec 2014 15:35:39 +0800 Subject: [PATCH 309/800] for bug #241, simplify the buffer, donot reset when chunk size change. --- trunk/src/rtmp/srs_protocol_buffer.cpp | 19 ------------------- trunk/src/rtmp/srs_protocol_buffer.hpp | 11 ----------- trunk/src/rtmp/srs_protocol_stack.cpp | 5 +---- 3 files changed, 1 insertion(+), 34 deletions(-) diff --git a/trunk/src/rtmp/srs_protocol_buffer.cpp b/trunk/src/rtmp/srs_protocol_buffer.cpp index 82ef77f37f..d222e62c96 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.cpp +++ b/trunk/src/rtmp/srs_protocol_buffer.cpp @@ -200,25 +200,6 @@ void SrsFastBuffer::set_merge_read(bool v, int max_buffer, IMergeReadHandler* ha } #endif -void SrsFastBuffer::on_chunk_size(int32_t chunk_size) -{ - if (nb_buffer >= chunk_size) { - return; - } - - // limit the max buffer. - int buffer_size = srs_min(chunk_size, SRS_MR_SOCKET_BUFFER); - - if (buffer_size != nb_buffer) { - reset_buffer(buffer_size); - } -} - -int SrsFastBuffer::buffer_size() -{ - return nb_buffer; -} - void SrsFastBuffer::reset_buffer(int size) { // remember the cap. diff --git a/trunk/src/rtmp/srs_protocol_buffer.hpp b/trunk/src/rtmp/srs_protocol_buffer.hpp index 1e4a8375aa..bb9f69cc18 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.hpp +++ b/trunk/src/rtmp/srs_protocol_buffer.hpp @@ -163,17 +163,6 @@ class SrsFastBuffer */ virtual void set_merge_read(bool v, int max_buffer, IMergeReadHandler* handler); #endif -public: - /** - * when chunk size changed, the buffer should change the buffer also. - * to keep the socket buffer size always greater than chunk size. - * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 - */ - virtual void on_chunk_size(int32_t chunk_size); - /** - * get the size of socket buffer to read. - */ - virtual int buffer_size(); private: virtual void reset_buffer(int size); }; diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp index 03d9e5e7de..10ece0e0dc 100644 --- a/trunk/src/rtmp/srs_protocol_stack.cpp +++ b/trunk/src/rtmp/srs_protocol_stack.cpp @@ -1708,11 +1708,8 @@ int SrsProtocol::on_recv_message(SrsMessage* msg) SRS_CONSTS_RTMP_MAX_CHUNK_SIZE); } - int sock_buffer = in_buffer->buffer_size(); in_chunk_size = pkt->chunk_size; - in_buffer->on_chunk_size(pkt->chunk_size); - srs_trace("input chunk size to %d, sock buf %d=>%d", - pkt->chunk_size, sock_buffer, in_buffer->buffer_size()); + srs_trace("input chunk size to %d", pkt->chunk_size); break; } From a23b5b9a4be78460f7c6ad683b66cf7af2741148 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 4 Dec 2014 15:43:37 +0800 Subject: [PATCH 310/800] for bug#241, simplify the buffer, donot change its size. --- trunk/src/app/srs_app_recv_thread.cpp | 4 ++-- trunk/src/core/srs_core_performance.hpp | 31 ++++++------------------- trunk/src/rtmp/srs_protocol_buffer.cpp | 9 +------ trunk/src/rtmp/srs_protocol_buffer.hpp | 3 +-- trunk/src/rtmp/srs_protocol_rtmp.cpp | 4 ++-- trunk/src/rtmp/srs_protocol_rtmp.hpp | 3 +-- trunk/src/rtmp/srs_protocol_stack.cpp | 4 ++-- trunk/src/rtmp/srs_protocol_stack.hpp | 3 +-- 8 files changed, 17 insertions(+), 44 deletions(-) diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index b9dc9818e0..74de0016b7 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -295,7 +295,7 @@ void SrsPublishRecvThread::on_thread_start() // enable the merge read // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 - rtmp->set_merge_read(true, nb_rbuf, this); + rtmp->set_merge_read(true, this); #endif } @@ -311,7 +311,7 @@ void SrsPublishRecvThread::on_thread_stop() #ifdef SRS_PERF_MERGED_READ // disable the merge read // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 - rtmp->set_merge_read(false, 0, NULL); + rtmp->set_merge_read(false, NULL); #endif } diff --git a/trunk/src/core/srs_core_performance.hpp b/trunk/src/core/srs_core_performance.hpp index 0b4853978f..bc82fd1099 100644 --- a/trunk/src/core/srs_core_performance.hpp +++ b/trunk/src/core/srs_core_performance.hpp @@ -39,40 +39,23 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * when it on and read small bytes, we sleep to wait more data., * that is, we merge some data to read together. * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 -* @remark other macros: -* SOCKET_MAX_BUF, the max size of user-space buffer. -* SOCKET_READ_SIZE, the user space buffer for socket. -* SRS_MR_MAX_BITRATE_KBPS, the kbps of stream for system to guess the sleep time. -* SRS_MR_AVERAGE_BITRATE_KBPS, the average kbps of system. -* SRS_MR_MIN_BITRATE_KBPS, the min kbps of system. -* SRS_MR_MAX_SLEEP_MS, the max sleep time, the latency+ when sleep+. -* SRS_MR_SMALL_BYTES, sleep when got small bytes, the latency+ when small+. -* SRS_MR_SMALL_PERCENT, to calc the small bytes = SRS_MR_SOCKET_BUFFER/percent. -* SRS_MR_SOCKET_BUFFER, the socket buffer to set fd. -* @remark the actual socket buffer used to set the buffer user-space size. -* buffer = min(SOCKET_MAX_BUF, SRS_MR_SOCKET_BUFFER, SOCKET_READ_SIZE) -* small bytes = max(buffer/SRS_MR_SMALL_PERCENT, SRS_MR_SMALL_BYTES) -* sleep = calc the sleep by kbps and buffer. -* @remark the main merged-read algorithm: -* while true: -* nread = read from socket. -* sleep if nread < small bytes -* process bytes. * @example, for the default settings, this algorithm will use: -* socket buffer set to 64KB, -* user space buffer set to 64KB, -* buffer=65536B, small=4096B, sleep=780ms * that is, when got nread bytes smaller than 4KB, sleep(780ms). */ #if 1 // to enable merged read. #define SRS_PERF_MERGED_READ // the max sleep time in ms - #define SRS_MR_MAX_SLEEP_MS 1000 + #define SRS_MR_MAX_SLEEP_MS 780 // the max small bytes to group #define SRS_MR_SMALL_BYTES 4096 // the underlayer api will set to SRS_MR_SOCKET_BUFFER bytes. - // 4KB=4096, 8KB=8192, 16KB=16384, 32KB=32768, 64KB=65536 + // 4KB=4096, 8KB=8192, 16KB=16384, 32KB=32768, 64KB=65536, + // 128KB=131072, 256KB=262144, 512KB=524288 + // the buffer should set to SRS_MR_MAX_SLEEP_MS*kbps/8, + // for example, your system delivery stream in 1000kbps, + // sleep 800ms for small bytes, the buffer should set to: + // 800*1000/8=100000B(about 128KB). #define SRS_MR_SOCKET_BUFFER 65536 #endif diff --git a/trunk/src/rtmp/srs_protocol_buffer.cpp b/trunk/src/rtmp/srs_protocol_buffer.cpp index d222e62c96..c2ab1ce299 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.cpp +++ b/trunk/src/rtmp/srs_protocol_buffer.cpp @@ -186,17 +186,10 @@ int SrsFastBuffer::grow(ISrsBufferReader* reader, int required_size) } #ifdef SRS_PERF_MERGED_READ -void SrsFastBuffer::set_merge_read(bool v, int max_buffer, IMergeReadHandler* handler) +void SrsFastBuffer::set_merge_read(bool v, IMergeReadHandler* handler) { merged_read = v; _handler = handler; - - // limit the max buffer. - int buffer_size = srs_min(max_buffer, SRS_MR_SOCKET_BUFFER); - - if (v && buffer_size != nb_buffer) { - reset_buffer(buffer_size); - } } #endif diff --git a/trunk/src/rtmp/srs_protocol_buffer.hpp b/trunk/src/rtmp/srs_protocol_buffer.hpp index bb9f69cc18..061b06721f 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.hpp +++ b/trunk/src/rtmp/srs_protocol_buffer.hpp @@ -157,11 +157,10 @@ class SrsFastBuffer * when it on and read small bytes, we sleep to wait more data., * that is, we merge some data to read together. * @param v true to ename merged read. - * @param max_buffer the max buffer size, the socket buffer. * @param handler the handler when merge read is enabled. * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 */ - virtual void set_merge_read(bool v, int max_buffer, IMergeReadHandler* handler); + virtual void set_merge_read(bool v, IMergeReadHandler* handler); #endif private: virtual void reset_buffer(int size); diff --git a/trunk/src/rtmp/srs_protocol_rtmp.cpp b/trunk/src/rtmp/srs_protocol_rtmp.cpp index 2c098a6d2c..7c14bac490 100644 --- a/trunk/src/rtmp/srs_protocol_rtmp.cpp +++ b/trunk/src/rtmp/srs_protocol_rtmp.cpp @@ -746,9 +746,9 @@ void SrsRtmpServer::set_auto_response(bool v) } #ifdef SRS_PERF_MERGED_READ -void SrsRtmpServer::set_merge_read(bool v, int max_buffer, IMergeReadHandler* handler) +void SrsRtmpServer::set_merge_read(bool v, IMergeReadHandler* handler) { - protocol->set_merge_read(v, max_buffer, handler); + protocol->set_merge_read(v, handler); } #endif diff --git a/trunk/src/rtmp/srs_protocol_rtmp.hpp b/trunk/src/rtmp/srs_protocol_rtmp.hpp index cab5b3065a..9b5a355ca5 100644 --- a/trunk/src/rtmp/srs_protocol_rtmp.hpp +++ b/trunk/src/rtmp/srs_protocol_rtmp.hpp @@ -350,11 +350,10 @@ class SrsRtmpServer * when it on and read small bytes, we sleep to wait more data., * that is, we merge some data to read together. * @param v true to ename merged read. - * @param max_buffer the max buffer size, the socket buffer. * @param handler the handler when merge read is enabled. * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 */ - virtual void set_merge_read(bool v, int max_buffer, IMergeReadHandler* handler); + virtual void set_merge_read(bool v, IMergeReadHandler* handler); #endif /** * set/get the recv timeout in us. diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp index 10ece0e0dc..a154d8131b 100644 --- a/trunk/src/rtmp/srs_protocol_stack.cpp +++ b/trunk/src/rtmp/srs_protocol_stack.cpp @@ -497,9 +497,9 @@ int SrsProtocol::manual_response_flush() } #ifdef SRS_PERF_MERGED_READ -void SrsProtocol::set_merge_read(bool v, int max_buffer, IMergeReadHandler* handler) +void SrsProtocol::set_merge_read(bool v, IMergeReadHandler* handler) { - in_buffer->set_merge_read(v, max_buffer, handler); + in_buffer->set_merge_read(v, handler); } #endif diff --git a/trunk/src/rtmp/srs_protocol_stack.hpp b/trunk/src/rtmp/srs_protocol_stack.hpp index 026c755dc6..9c909c87b8 100644 --- a/trunk/src/rtmp/srs_protocol_stack.hpp +++ b/trunk/src/rtmp/srs_protocol_stack.hpp @@ -284,11 +284,10 @@ class SrsProtocol * when it on and read small bytes, we sleep to wait more data., * that is, we merge some data to read together. * @param v true to ename merged read. - * @param max_buffer the max buffer size, the socket buffer. * @param handler the handler when merge read is enabled. * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 */ - virtual void set_merge_read(bool v, int max_buffer, IMergeReadHandler* handler); + virtual void set_merge_read(bool v, IMergeReadHandler* handler); #endif public: /** From 159ef3823c367c73c74e554a00aa2508c82fa76e Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 4 Dec 2014 15:45:25 +0800 Subject: [PATCH 311/800] for bug#241, simplify the buffer, donot realloc it. --- trunk/src/rtmp/srs_protocol_buffer.cpp | 25 +++---------------------- trunk/src/rtmp/srs_protocol_buffer.hpp | 2 -- 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/trunk/src/rtmp/srs_protocol_buffer.cpp b/trunk/src/rtmp/srs_protocol_buffer.cpp index c2ab1ce299..617ce148e3 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.cpp +++ b/trunk/src/rtmp/srs_protocol_buffer.cpp @@ -90,10 +90,9 @@ SrsFastBuffer::SrsFastBuffer() _handler = NULL; #endif - p = end = buffer = NULL; - nb_buffer = 0; - - reset_buffer(SRS_MR_SOCKET_BUFFER); + nb_buffer = SRS_MR_SOCKET_BUFFER; + buffer = new char[nb_buffer]; + p = end = buffer; } SrsFastBuffer::~SrsFastBuffer() @@ -193,21 +192,3 @@ void SrsFastBuffer::set_merge_read(bool v, IMergeReadHandler* handler) } #endif -void SrsFastBuffer::reset_buffer(int size) -{ - // remember the cap. - int nb_cap = end - p; - - // atleast to put the old data. - nb_buffer = srs_max(nb_cap, size); - - // copy old data to buf. - char* buf = new char[nb_buffer]; - if (nb_cap > 0) { - memcpy(buf, p, nb_cap); - } - - srs_freep(buffer); - p = buffer = buf; - end = p + nb_cap; -} diff --git a/trunk/src/rtmp/srs_protocol_buffer.hpp b/trunk/src/rtmp/srs_protocol_buffer.hpp index 061b06721f..e2ec16a457 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.hpp +++ b/trunk/src/rtmp/srs_protocol_buffer.hpp @@ -162,8 +162,6 @@ class SrsFastBuffer */ virtual void set_merge_read(bool v, IMergeReadHandler* handler); #endif -private: - virtual void reset_buffer(int size); }; #endif From 57f844b6364a9920901f198d52dfc9cc5b81d4bd Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 4 Dec 2014 15:49:24 +0800 Subject: [PATCH 312/800] for bug #249, simplify the macro, 0 to disable chunk stream cache. --- trunk/src/core/srs_core_performance.hpp | 1 + trunk/src/rtmp/srs_protocol_stack.cpp | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/trunk/src/core/srs_core_performance.hpp b/trunk/src/core/srs_core_performance.hpp index bc82fd1099..855693feab 100644 --- a/trunk/src/core/srs_core_performance.hpp +++ b/trunk/src/core/srs_core_performance.hpp @@ -75,6 +75,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * how many chunk stream to cache, [0, N]. * to imporove about 10% performance when chunk size small, and 5% for large chunk. * @see https://github.com/winlinvip/simple-rtmp-server/issues/249 +* @remark 0 to disable the chunk stream cache. */ #define SRS_PERF_CHUNK_STREAM_CACHE 16 diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp index a154d8131b..98502f7b75 100644 --- a/trunk/src/rtmp/srs_protocol_stack.cpp +++ b/trunk/src/rtmp/srs_protocol_stack.cpp @@ -418,7 +418,10 @@ SrsProtocol::SrsProtocol(ISrsProtocolReaderWriter* io) warned_c0c3_cache_dry = false; auto_response_when_recv = true; - cs_cache = new SrsChunkStream*[SRS_PERF_CHUNK_STREAM_CACHE]; + cs_cache = NULL; + if (SRS_PERF_CHUNK_STREAM_CACHE > 0) { + cs_cache = new SrsChunkStream*[SRS_PERF_CHUNK_STREAM_CACHE]; + } for (int cid = 0; cid < SRS_PERF_CHUNK_STREAM_CACHE; cid++) { SrsChunkStream* cs = new SrsChunkStream(cid); // set the perfer cid of chunk, @@ -1102,6 +1105,9 @@ int SrsProtocol::recv_interlaced_message(SrsMessage** pmsg) } srs_verbose("read basic header success. fmt=%d, cid=%d", fmt, cid); + // the cid must not negative. + srs_assert(cid >= 0); + // once we got the chunk message header, // that is there is a real message in cache, // increase the timeout to got it. From 5589b13d2e216b91f97afb78ee0c011b2fccf7da Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 4 Dec 2014 18:21:04 +0800 Subject: [PATCH 313/800] for bug #241, support mr(merged-read) config and reload. 2.0.52. --- README.md | 1 + trunk/conf/full.conf | 20 +++++ trunk/src/app/srs_app_config.cpp | 68 ++++++++++++++- trunk/src/app/srs_app_config.hpp | 10 +++ trunk/src/app/srs_app_recv_thread.cpp | 108 ++++++++++++++++++++---- trunk/src/app/srs_app_recv_thread.hpp | 14 ++- trunk/src/app/srs_app_reload.cpp | 5 ++ trunk/src/app/srs_app_reload.hpp | 1 + trunk/src/app/srs_app_rtmp_conn.cpp | 10 +-- trunk/src/core/srs_core.hpp | 2 +- trunk/src/core/srs_core_performance.hpp | 33 ++++---- trunk/src/kernel/srs_kernel_consts.hpp | 4 + trunk/src/rtmp/srs_protocol_buffer.cpp | 26 +++++- trunk/src/rtmp/srs_protocol_buffer.hpp | 9 ++ trunk/src/rtmp/srs_protocol_rtmp.cpp | 5 ++ trunk/src/rtmp/srs_protocol_rtmp.hpp | 8 ++ trunk/src/rtmp/srs_protocol_stack.cpp | 5 ++ trunk/src/rtmp/srs_protocol_stack.hpp | 12 ++- 18 files changed, 293 insertions(+), 48 deletions(-) mode change 100644 => 100755 trunk/conf/full.conf diff --git a/README.md b/README.md index ae0f54778c..750ade9a8c 100755 --- a/README.md +++ b/README.md @@ -485,6 +485,7 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History +* v2.0, 2014-12-04, for [#241](https://github.com/winlinvip/simple-rtmp-server/issues/241), support mr(merged-read) config and reload. 2.0.52. * v2.0, 2014-12-04, enable [#241](https://github.com/winlinvip/simple-rtmp-server/issues/241) and [#248](https://github.com/winlinvip/simple-rtmp-server/issues/248), +25% performance, 2.5k publisher. 2.0.50 * v2.0, 2014-12-04, fix [#248](https://github.com/winlinvip/simple-rtmp-server/issues/248), improve about 15% performance for fast buffer. 2.0.49 * v2.0, 2014-12-03, fix [#244](https://github.com/winlinvip/simple-rtmp-server/issues/244), conn thread use cond to wait for recv thread error. 2.0.47. diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf old mode 100644 new mode 100755 index 61c8b3d497..6edc5a0bb2 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -142,6 +142,26 @@ http_stream { vhost __defaultVhost__ { } +# the MR(merged-read) setting for publisher. +vhost mr.srs.com { + # about MR, read https://github.com/winlinvip/simple-rtmp-server/issues/241 + mr { + # whether enable the MR(merged-read) + # default: off + enabled on; + # the latency in ms for MR(merged-read), + # the performance+ when latency+, and memory+, + # memory(buffer) = latency * kbps / 8 + # for example, latency=500ms, kbps=3000kbps, each publish connection will consume + # memory = 500 * 3000 / 8 = 187500B = 183KB + # when there are 2500 publisher, the total memory of SRS atleast: + # 183KB * 2500 = 446MB + # the value recomment is [300, 2000] + # default: 500 + latency 500; + } +} + # vhost for edge, edge and origin is the same vhost vhost same.edge.srs.com { # the mode of vhost, local or remote. diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index 58f0a3a86e..b804e3239a 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -816,7 +816,18 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root) return ret; } } - srs_trace("vhost %s reload hls success.", vhost.c_str()); + srs_trace("vhost %s reload hlsdvrsuccess.", vhost.c_str()); + } + // mr, only one per vhost + if (!srs_directive_equals(new_vhost->get("mr"), old_vhost->get("mr"))) { + for (it = subscribes.begin(); it != subscribes.end(); ++it) { + ISrsReloadHandler* subscribe = *it; + if ((ret = subscribe->on_reload_vhost_mr(vhost)) != ERROR_SUCCESS) { + srs_error("vhost %s notify subscribes mr failed. ret=%d", vhost.c_str(), ret); + return ret; + } + } + srs_trace("vhost %s reload mr success.", vhost.c_str()); } // http, only one per vhost. if (!srs_directive_equals(new_vhost->get("http"), old_vhost->get("http"))) { @@ -1316,6 +1327,7 @@ int SrsConfig::check_config() && n != "time_jitter" && n != "atc" && n != "atc_auto" && n != "debug_srs_upnode" + && n != "mr" ) { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("unsupported vhost directive %s, ret=%d", n.c_str(), ret); @@ -1333,6 +1345,16 @@ int SrsConfig::check_config() return ret; } } + } else if (n == "mr") { + for (int j = 0; j < (int)conf->directives.size(); j++) { + string m = conf->at(j)->name.c_str(); + if (m != "enabled" && m != "latency" + ) { + ret = ERROR_SYSTEM_CONFIG_INVALID; + srs_error("unsupported vhost mr directive %s, ret=%d", m.c_str(), ret); + return ret; + } + } } else if (n == "ingest") { for (int j = 0; j < (int)conf->directives.size(); j++) { string m = conf->at(j)->name.c_str(); @@ -2078,6 +2100,50 @@ int SrsConfig::get_chunk_size(string vhost) return ::atoi(conf->arg0().c_str()); } +bool SrsConfig::get_mr_enabled(string vhost) +{ + + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return SRS_CONSTS_RTMP_MR; + } + + conf = conf->get("mr"); + if (!conf) { + return SRS_CONSTS_RTMP_MR; + } + + conf = conf->get("enabled"); + if (!conf || conf->arg0() != "on") { + return SRS_CONSTS_RTMP_MR; + } + + return true; +} + +int SrsConfig::get_mr_sleep_ms(string vhost) +{ + + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return SRS_CONSTS_RTMP_MR_SLEEP; + } + + conf = conf->get("mr"); + if (!conf) { + return SRS_CONSTS_RTMP_MR_SLEEP; + } + + conf = conf->get("latency"); + if (!conf || conf->arg0().empty()) { + return SRS_CONSTS_RTMP_MR_SLEEP; + } + + return ::atoi(conf->arg0().c_str()); +} + int SrsConfig::get_global_chunk_size() { SrsConfDirective* conf = root->get("chunk_size"); diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index 675036e2f2..34315406ce 100644 --- a/trunk/src/app/srs_app_config.hpp +++ b/trunk/src/app/srs_app_config.hpp @@ -530,6 +530,16 @@ class SrsConfig * @remark, default 60000. */ virtual int get_chunk_size(std::string vhost); + /** + * whether mr is enabled for vhost. + * @param vhost, the vhost to get the mr. + */ + virtual bool get_mr_enabled(std::string vhost); + /** + * get the mr sleep time in ms for vhost. + * @param vhost, the vhost to get the mr sleep time. + */ + virtual int get_mr_sleep_ms(std::string vhost); private: /** * get the global chunk size. diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index 74de0016b7..0226090c2d 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -29,6 +29,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #include +#include + +using namespace std; ISrsMessageHandler::ISrsMessageHandler() { @@ -221,11 +224,13 @@ void SrsQueueRecvThread::on_thread_stop() } SrsPublishRecvThread::SrsPublishRecvThread( - SrsRtmpServer* rtmp_sdk, int fd, int timeout_ms, + SrsRtmpServer* rtmp_sdk, + SrsRequest* _req, int mr_sock_fd, int timeout_ms, SrsRtmpConn* conn, SrsSource* source, bool is_fmle, bool is_edge ): trd(this, rtmp_sdk, timeout_ms) { rtmp = rtmp_sdk; + _conn = conn; _source = source; _is_fmle = is_fmle; @@ -234,12 +239,22 @@ SrsPublishRecvThread::SrsPublishRecvThread( recv_error_code = ERROR_SUCCESS; _nb_msgs = 0; error = st_cond_new(); + + req = _req; + mr_fd = mr_sock_fd; - mr_fd = fd; + // the mr settings, + // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 + mr = _srs_config->get_mr_enabled(req->vhost); + mr_sleep = _srs_config->get_mr_sleep_ms(req->vhost); + + _srs_config->subscribe(this); } SrsPublishRecvThread::~SrsPublishRecvThread() { + _srs_config->unsubscribe(this); + trd.stop(); st_cond_destroy(error); } @@ -282,20 +297,8 @@ void SrsPublishRecvThread::on_thread_start() // for the main thread never send message. #ifdef SRS_PERF_MERGED_READ - // socket recv buffer, system will double it. - int nb_rbuf = SRS_MR_SOCKET_BUFFER / 2; - socklen_t sock_buf_size = sizeof(int); - if (setsockopt(mr_fd, SOL_SOCKET, SO_RCVBUF, &nb_rbuf, sock_buf_size) < 0) { - srs_warn("set sock SO_RCVBUF=%d failed.", nb_rbuf); - } - getsockopt(mr_fd, SOL_SOCKET, SO_RCVBUF, &nb_rbuf, &sock_buf_size); - - srs_trace("merged read sockbuf=%d, actual=%d, sleep %d when nread<=%d", - SRS_MR_SOCKET_BUFFER, nb_rbuf, SRS_MR_MAX_SLEEP_MS, SRS_MR_SMALL_BYTES); - - // enable the merge read - // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 - rtmp->set_merge_read(true, this); + // for mr. + update_buffer(mr, mr_sleep); #endif } @@ -349,7 +352,11 @@ void SrsPublishRecvThread::on_recv_error(int ret) #ifdef SRS_PERF_MERGED_READ void SrsPublishRecvThread::on_read(ssize_t nread) { - if (nread < 0 || SRS_MR_MAX_SLEEP_MS <= 0) { + if (!mr) { + return; + } + + if (nread < 0 || mr_sleep <= 0) { return; } @@ -360,7 +367,72 @@ void SrsPublishRecvThread::on_read(ssize_t nread) * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 */ if (nread < SRS_MR_SMALL_BYTES) { - st_usleep(SRS_MR_MAX_SLEEP_MS * 1000); + st_usleep(mr_sleep * 1000); } } #endif + +int SrsPublishRecvThread::on_reload_vhost_mr(string vhost) +{ + int ret = ERROR_SUCCESS; + + // the mr settings, + // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 + bool mr_enabled = _srs_config->get_mr_enabled(req->vhost); + int sleep_ms = _srs_config->get_mr_sleep_ms(req->vhost); + update_buffer(mr_enabled, sleep_ms); + + return ret; +} + +void SrsPublishRecvThread::update_buffer(bool mr_enabled, int sleep_ms) +{ + // TODO: FIXME: refine it. + +#ifdef SRS_PERF_MERGED_READ + // previous enabled mr, update the buffer. + if (mr && mr_sleep != sleep_ms) { + // the underlayer api will set to SRS_MR_SOCKET_BUFFER bytes. + // 4KB=4096, 8KB=8192, 16KB=16384, 32KB=32768, 64KB=65536, + // 128KB=131072, 256KB=262144, 512KB=524288 + // the buffer should set to SRS_MR_MAX_SLEEP_MS*kbps/8, + // for example, your system delivery stream in 1000kbps, + // sleep 800ms for small bytes, the buffer should set to: + // 800*1000/8=100000B(about 128KB). + // 2000*3000/8=750000B(about 732KB). + int kbps = 3000; + int socket_buffer_size = mr_sleep * kbps / 8; + + // socket recv buffer, system will double it. + int nb_rbuf = socket_buffer_size / 2; + socklen_t sock_buf_size = sizeof(int); + if (setsockopt(mr_fd, SOL_SOCKET, SO_RCVBUF, &nb_rbuf, sock_buf_size) < 0) { + srs_warn("set sock SO_RCVBUF=%d failed.", nb_rbuf); + } + getsockopt(mr_fd, SOL_SOCKET, SO_RCVBUF, &nb_rbuf, &sock_buf_size); + + srs_trace("merged read sockbuf=%d, actual=%d, sleep %d when nread<=%d", + socket_buffer_size, nb_rbuf, mr_sleep, SRS_MR_SMALL_BYTES); + + rtmp->set_recv_buffer(nb_rbuf); + } +#endif + + // update to new state + mr = mr_enabled; + mr_sleep = sleep_ms; + +#ifdef SRS_PERF_MERGED_READ + // apply new state. + if (mr) { + // enable the merge read + // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 + rtmp->set_merge_read(true, this); + } else { + // disable the merge read + // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 + rtmp->set_merge_read(false, NULL); + } +#endif +} + diff --git a/trunk/src/app/srs_app_recv_thread.hpp b/trunk/src/app/srs_app_recv_thread.hpp index 6c219a62aa..c39ec51ca0 100644 --- a/trunk/src/app/srs_app_recv_thread.hpp +++ b/trunk/src/app/srs_app_recv_thread.hpp @@ -35,11 +35,13 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #include +#include class SrsRtmpServer; class SrsMessage; class SrsRtmpConn; class SrsSource; +class SrsRequest; /** * for the recv thread to handle the message. @@ -138,15 +140,19 @@ class SrsPublishRecvThread : virtual public ISrsMessageHandler #ifdef SRS_PERF_MERGED_READ , virtual public IMergeReadHandler #endif + , virtual public ISrsReloadHandler { private: SrsRecvThread trd; SrsRtmpServer* rtmp; + SrsRequest* req; // the msgs already got. int64_t _nb_msgs; // for mr(merged read), // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 + bool mr; int mr_fd; + int mr_sleep; // the recv thread error code. int recv_error_code; SrsRtmpConn* _conn; @@ -158,7 +164,8 @@ class SrsPublishRecvThread : virtual public ISrsMessageHandler // @see https://github.com/winlinvip/simple-rtmp-server/issues/244 st_cond_t error; public: - SrsPublishRecvThread(SrsRtmpServer* rtmp_sdk, int fd, int timeout_ms, + SrsPublishRecvThread(SrsRtmpServer* rtmp_sdk, + SrsRequest* _req, int mr_sock_fd, int timeout_ms, SrsRtmpConn* conn, SrsSource* source, bool is_fmle, bool is_edge); virtual ~SrsPublishRecvThread(); public: @@ -183,6 +190,11 @@ class SrsPublishRecvThread : virtual public ISrsMessageHandler #ifdef SRS_PERF_MERGED_READ virtual void on_read(ssize_t nread); #endif +// interface ISrsReloadHandler +public: + virtual int on_reload_vhost_mr(std::string vhost); +private: + virtual void update_buffer(bool mr_enabled, int sleep_ms); }; #endif diff --git a/trunk/src/app/srs_app_reload.cpp b/trunk/src/app/srs_app_reload.cpp index 8bed3f19ba..9581df1d17 100644 --- a/trunk/src/app/srs_app_reload.cpp +++ b/trunk/src/app/srs_app_reload.cpp @@ -140,6 +140,11 @@ int ISrsReloadHandler::on_reload_vhost_dvr(string /*vhost*/) return ERROR_SUCCESS; } +int ISrsReloadHandler::on_reload_vhost_mr(string /*vhost*/) +{ + return ERROR_SUCCESS; +} + int ISrsReloadHandler::on_reload_vhost_transcode(string /*vhost*/) { return ERROR_SUCCESS; diff --git a/trunk/src/app/srs_app_reload.hpp b/trunk/src/app/srs_app_reload.hpp index 85d5be016e..a133c52d8d 100644 --- a/trunk/src/app/srs_app_reload.hpp +++ b/trunk/src/app/srs_app_reload.hpp @@ -65,6 +65,7 @@ class ISrsReloadHandler virtual int on_reload_vhost_forward(std::string vhost); virtual int on_reload_vhost_hls(std::string vhost); virtual int on_reload_vhost_dvr(std::string vhost); + virtual int on_reload_vhost_mr(std::string vhost); virtual int on_reload_vhost_transcode(std::string vhost); virtual int on_reload_ingest_removed(std::string vhost, std::string ingest_id); virtual int on_reload_ingest_added(std::string vhost, std::string ingest_id); diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index 9c60430e2f..958c92396c 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -660,9 +660,8 @@ int SrsRtmpConn::fmle_publishing(SrsSource* source) // use isolate thread to recv, // @see: https://github.com/winlinvip/simple-rtmp-server/issues/237 - SrsPublishRecvThread trd(rtmp, st_netfd_fileno(stfd), - SRS_CONSTS_RTMP_RECV_TIMEOUT_US / 1000, - this, source, true, vhost_is_edge); + SrsPublishRecvThread trd(rtmp, req, + st_netfd_fileno(stfd), 0, this, source, true, vhost_is_edge); srs_info("start to publish stream %s success", req->stream.c_str()); ret = do_publishing(source, &trd); @@ -696,9 +695,8 @@ int SrsRtmpConn::flash_publishing(SrsSource* source) // use isolate thread to recv, // @see: https://github.com/winlinvip/simple-rtmp-server/issues/237 - SrsPublishRecvThread trd(rtmp, st_netfd_fileno(stfd), - SRS_CONSTS_RTMP_RECV_TIMEOUT_US / 1000, - this, source, false, vhost_is_edge); + SrsPublishRecvThread trd(rtmp, req, + st_netfd_fileno(stfd), 0, this, source, true, vhost_is_edge); srs_info("start to publish stream %s success", req->stream.c_str()); ret = do_publishing(source, &trd); diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 28bc0c07eb..b5926e0383 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 51 +#define VERSION_REVISION 52 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/core/srs_core_performance.hpp b/trunk/src/core/srs_core_performance.hpp index 855693feab..29abcbbb76 100644 --- a/trunk/src/core/srs_core_performance.hpp +++ b/trunk/src/core/srs_core_performance.hpp @@ -42,22 +42,23 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * @example, for the default settings, this algorithm will use: * that is, when got nread bytes smaller than 4KB, sleep(780ms). */ -#if 1 - // to enable merged read. - #define SRS_PERF_MERGED_READ - // the max sleep time in ms - #define SRS_MR_MAX_SLEEP_MS 780 - // the max small bytes to group - #define SRS_MR_SMALL_BYTES 4096 - // the underlayer api will set to SRS_MR_SOCKET_BUFFER bytes. - // 4KB=4096, 8KB=8192, 16KB=16384, 32KB=32768, 64KB=65536, - // 128KB=131072, 256KB=262144, 512KB=524288 - // the buffer should set to SRS_MR_MAX_SLEEP_MS*kbps/8, - // for example, your system delivery stream in 1000kbps, - // sleep 800ms for small bytes, the buffer should set to: - // 800*1000/8=100000B(about 128KB). - #define SRS_MR_SOCKET_BUFFER 65536 -#endif +/** +* https://github.com/winlinvip/simple-rtmp-server/issues/241#issuecomment-65554690 +* The merged read algorithm is ok and can be simplified for: +* 1. Suppose the client network is ok. All algorithm go wrong when netowrk is not ok. +* 2. Suppose the client send each packet one by one. Although send some together, it's same. +* 3. SRS MR algorithm will read all data then sleep. +* So, the MR algorithm is: +* while true: +* read all data from socket. +* sleep a while +* For example, sleep 120ms. Then there is, and always 120ms data in buffer. +* That is, the latency is 120ms(the sleep time). +*/ +// to enable merged read. +#undef SRS_PERF_MERGED_READ +// the max sleep time in ms +#define SRS_MR_MAX_SLEEP_MS 800 /** * the send cache time in ms. diff --git a/trunk/src/kernel/srs_kernel_consts.hpp b/trunk/src/kernel/srs_kernel_consts.hpp index d30fd07d11..194c9f9d20 100644 --- a/trunk/src/kernel/srs_kernel_consts.hpp +++ b/trunk/src/kernel/srs_kernel_consts.hpp @@ -50,6 +50,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // 6. Chunking, RTMP protocol default chunk size. #define SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE 128 +// the default setting of mr. +#define SRS_CONSTS_RTMP_MR false +#define SRS_CONSTS_RTMP_MR_SLEEP 500 + /** * 6. Chunking * The chunk size is configurable. It can be set using a control diff --git a/trunk/src/rtmp/srs_protocol_buffer.cpp b/trunk/src/rtmp/srs_protocol_buffer.cpp index 617ce148e3..4a8234dba7 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.cpp +++ b/trunk/src/rtmp/srs_protocol_buffer.cpp @@ -28,6 +28,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +// the max small bytes to group +#define SRS_MR_SMALL_BYTES 4096 +// the default recv buffer size +#define SRS_DEFAULT_RECV_BUFFER_SIZE 8192 + // the max header size, // @see SrsProtocol::read_message_header(). #define SRS_RTMP_MAX_MESSAGE_HEADER 11 @@ -90,11 +95,30 @@ SrsFastBuffer::SrsFastBuffer() _handler = NULL; #endif - nb_buffer = SRS_MR_SOCKET_BUFFER; + nb_buffer = SRS_DEFAULT_RECV_BUFFER_SIZE; buffer = new char[nb_buffer]; p = end = buffer; } +void SrsFastBuffer::set_buffer(int buffer_size) +{ + // only realloc when buffer changed bigger + if (buffer_size <= nb_buffer) { + return; + } + + int start = p - buffer; + int cap = end - p; + + char* buf = new char[buffer_size]; + memcpy(buf, buffer, nb_buffer); + srs_freep(buffer); + + buffer = buf; + p = buffer + start; + end = p + cap; +} + SrsFastBuffer::~SrsFastBuffer() { srs_freep(buffer); diff --git a/trunk/src/rtmp/srs_protocol_buffer.hpp b/trunk/src/rtmp/srs_protocol_buffer.hpp index e2ec16a457..36444a08ae 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.hpp +++ b/trunk/src/rtmp/srs_protocol_buffer.hpp @@ -122,6 +122,15 @@ class SrsFastBuffer public: SrsFastBuffer(); virtual ~SrsFastBuffer(); +public: + /** + * create buffer with specifeid size. + * @param buffer the size of buffer. + * @remark when MR(SRS_PERF_MERGED_READ) disabled, always set to 8K. + * @remark when buffer changed, the previous ptr maybe invalid. + * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 + */ + virtual void set_buffer(int buffer_size); public: /** * read 1byte from buffer, move to next bytes. diff --git a/trunk/src/rtmp/srs_protocol_rtmp.cpp b/trunk/src/rtmp/srs_protocol_rtmp.cpp index 7c14bac490..2b6525dbd1 100644 --- a/trunk/src/rtmp/srs_protocol_rtmp.cpp +++ b/trunk/src/rtmp/srs_protocol_rtmp.cpp @@ -750,6 +750,11 @@ void SrsRtmpServer::set_merge_read(bool v, IMergeReadHandler* handler) { protocol->set_merge_read(v, handler); } + +void SrsRtmpServer::set_recv_buffer(int buffer_size) +{ + protocol->set_recv_buffer(buffer_size); +} #endif void SrsRtmpServer::set_recv_timeout(int64_t timeout_us) diff --git a/trunk/src/rtmp/srs_protocol_rtmp.hpp b/trunk/src/rtmp/srs_protocol_rtmp.hpp index 9b5a355ca5..f4fb0aea96 100644 --- a/trunk/src/rtmp/srs_protocol_rtmp.hpp +++ b/trunk/src/rtmp/srs_protocol_rtmp.hpp @@ -354,6 +354,14 @@ class SrsRtmpServer * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 */ virtual void set_merge_read(bool v, IMergeReadHandler* handler); + /** + * create buffer with specifeid size. + * @param buffer the size of buffer. + * @remark when MR(SRS_PERF_MERGED_READ) disabled, always set to 8K. + * @remark when buffer changed, the previous ptr maybe invalid. + * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 + */ + virtual void set_recv_buffer(int buffer_size); #endif /** * set/get the recv timeout in us. diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp index 98502f7b75..1eee9d9e87 100644 --- a/trunk/src/rtmp/srs_protocol_stack.cpp +++ b/trunk/src/rtmp/srs_protocol_stack.cpp @@ -504,6 +504,11 @@ void SrsProtocol::set_merge_read(bool v, IMergeReadHandler* handler) { in_buffer->set_merge_read(v, handler); } + +void SrsProtocol::set_recv_buffer(int buffer_size) +{ + in_buffer->set_buffer(buffer_size); +} #endif void SrsProtocol::set_recv_timeout(int64_t timeout_us) diff --git a/trunk/src/rtmp/srs_protocol_stack.hpp b/trunk/src/rtmp/srs_protocol_stack.hpp index 9c909c87b8..72d30a11db 100644 --- a/trunk/src/rtmp/srs_protocol_stack.hpp +++ b/trunk/src/rtmp/srs_protocol_stack.hpp @@ -257,10 +257,6 @@ class SrsProtocol */ int32_t out_chunk_size; public: - /** - * use io to create the protocol stack, - * @param io, provides io interfaces, user must free it. - */ SrsProtocol(ISrsProtocolReaderWriter* io); virtual ~SrsProtocol(); public: @@ -288,6 +284,14 @@ class SrsProtocol * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 */ virtual void set_merge_read(bool v, IMergeReadHandler* handler); + /** + * create buffer with specifeid size. + * @param buffer the size of buffer. + * @remark when MR(SRS_PERF_MERGED_READ) disabled, always set to 8K. + * @remark when buffer changed, the previous ptr maybe invalid. + * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 + */ + virtual void set_recv_buffer(int buffer_size); #endif public: /** From f1192a830243a3f5ce1618b347a945429644ffe4 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 4 Dec 2014 18:35:50 +0800 Subject: [PATCH 314/800] fix bug for bug #241, default to 32k for socket buffer. --- trunk/src/app/srs_app_config.hpp | 1 + trunk/src/app/srs_app_recv_thread.cpp | 7 +++++-- trunk/src/core/srs_core_performance.hpp | 4 +--- trunk/src/rtmp/srs_protocol_buffer.cpp | 8 ++++---- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index 34315406ce..1ec0e24119 100644 --- a/trunk/src/app/srs_app_config.hpp +++ b/trunk/src/app/srs_app_config.hpp @@ -539,6 +539,7 @@ class SrsConfig * get the mr sleep time in ms for vhost. * @param vhost, the vhost to get the mr sleep time. */ + // TODO: FIXME: add utest for mr config. virtual int get_mr_sleep_ms(std::string vhost); private: /** diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index 0226090c2d..578bc842eb 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -33,6 +33,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using namespace std; +// the max small bytes to group +#define SRS_MR_SMALL_BYTES 4096 + ISrsMessageHandler::ISrsMessageHandler() { } @@ -390,8 +393,8 @@ void SrsPublishRecvThread::update_buffer(bool mr_enabled, int sleep_ms) // TODO: FIXME: refine it. #ifdef SRS_PERF_MERGED_READ - // previous enabled mr, update the buffer. - if (mr && mr_sleep != sleep_ms) { + // update the buffer. + if (true) { // the underlayer api will set to SRS_MR_SOCKET_BUFFER bytes. // 4KB=4096, 8KB=8192, 16KB=16384, 32KB=32768, 64KB=65536, // 128KB=131072, 256KB=262144, 512KB=524288 diff --git a/trunk/src/core/srs_core_performance.hpp b/trunk/src/core/srs_core_performance.hpp index 29abcbbb76..6407ae8df5 100644 --- a/trunk/src/core/srs_core_performance.hpp +++ b/trunk/src/core/srs_core_performance.hpp @@ -56,9 +56,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * That is, the latency is 120ms(the sleep time). */ // to enable merged read. -#undef SRS_PERF_MERGED_READ -// the max sleep time in ms -#define SRS_MR_MAX_SLEEP_MS 800 +#define SRS_PERF_MERGED_READ /** * the send cache time in ms. diff --git a/trunk/src/rtmp/srs_protocol_buffer.cpp b/trunk/src/rtmp/srs_protocol_buffer.cpp index 4a8234dba7..ab0cc7bbdb 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.cpp +++ b/trunk/src/rtmp/srs_protocol_buffer.cpp @@ -28,10 +28,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include -// the max small bytes to group -#define SRS_MR_SMALL_BYTES 4096 // the default recv buffer size -#define SRS_DEFAULT_RECV_BUFFER_SIZE 8192 +#define SRS_DEFAULT_RECV_BUFFER_SIZE 32768 // the max header size, // @see SrsProtocol::read_message_header(). @@ -111,7 +109,9 @@ void SrsFastBuffer::set_buffer(int buffer_size) int cap = end - p; char* buf = new char[buffer_size]; - memcpy(buf, buffer, nb_buffer); + if (cap > 0) { + memcpy(buf, buffer, nb_buffer); + } srs_freep(buffer); buffer = buf; From 72fa33d9fb5bdfd6dce84a8d00f61fcf2ab63765 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 4 Dec 2014 18:46:08 +0800 Subject: [PATCH 315/800] update performance to 4k, when mr_sleep to 2000ms. 2.0.52 --- README.md | 1 + trunk/src/rtmp/srs_protocol_buffer.cpp | 16 +++++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 750ade9a8c..19fd4d5a89 100755 --- a/README.md +++ b/README.md @@ -738,6 +738,7 @@ The publish benchmark by [st-load](https://github.com/winlinvip/st-load): * 2014-12-04, SRS 2.0.49, 1.4k(1400) publishers, 68%CPU, 144MB. * 2014-12-04, SRS 2.0.49, 2.5k(2500) publishers, 95%CPU, 404MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/29324fab469e0f7cef9ad04ffdbce832ac7dd9ff) * 2014-12-04, SRS 2.0.51, 2.5k(2500) publishers, 91%CPU, 259MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/f57801eb46c16755b173984b915a4166922df6a6) +* 2014-12-04, SRS 2.0.52, 4.0k(4000) publishers, 80%CPU, 331MB. (mr_sleep=2000) [commit](https://github.com/winlinvip/simple-rtmp-server/commit/5589b13d2e216b91f97afb78ee0c011b2fccf7da) ## Architecture diff --git a/trunk/src/rtmp/srs_protocol_buffer.cpp b/trunk/src/rtmp/srs_protocol_buffer.cpp index ab0cc7bbdb..bc5d217845 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.cpp +++ b/trunk/src/rtmp/srs_protocol_buffer.cpp @@ -165,17 +165,23 @@ int SrsFastBuffer::grow(ISrsBufferReader* reader, int required_size) } // when read payload and need to grow, reset buffer. - if (end - p < required_size && required_size > SRS_RTMP_MAX_MESSAGE_HEADER) { + // or there is no space to read. + int max_to_read = buffer + nb_buffer - end; + if (end - p < required_size + && (required_size > SRS_RTMP_MAX_MESSAGE_HEADER || max_to_read < required_size) + ) { int nb_cap = end - p; srs_verbose("move fast buffer %d bytes", nb_cap); - buffer = (char*)memmove(buffer, p, nb_cap); - p = buffer; - end = p + nb_cap; + if (nb_cap < nb_buffer) { + buffer = (char*)memmove(buffer, p, nb_cap); + p = buffer; + end = p + nb_cap; + } } while (end - p < required_size) { // the max to read is the left bytes. - size_t max_to_read = buffer + nb_buffer - end; + max_to_read = buffer + nb_buffer - end; if (max_to_read <= 0) { ret = ERROR_RTMP_BUFFER_OVERFLOW; From b1d7fbe668b9a01a104fd435b6863024e509f3a2 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 4 Dec 2014 21:35:13 +0800 Subject: [PATCH 316/800] fix #241, add mw(merged-write) config. 2.0.53 --- README.md | 1 + trunk/conf/full.conf | 8 +++- trunk/src/app/srs_app_config.cpp | 51 +++++++++++++++++++------ trunk/src/app/srs_app_config.hpp | 8 +++- trunk/src/app/srs_app_reload.cpp | 5 +++ trunk/src/app/srs_app_reload.hpp | 1 + trunk/src/app/srs_app_rtmp_conn.cpp | 25 +++++++++--- trunk/src/app/srs_app_rtmp_conn.hpp | 3 ++ trunk/src/core/srs_core.hpp | 3 +- trunk/src/core/srs_core_performance.hpp | 21 ++++++++-- trunk/src/kernel/srs_kernel_consts.hpp | 4 -- trunk/src/main/srs_main_server.cpp | 23 +++++++++++ 12 files changed, 125 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 19fd4d5a89..3fc55843c6 100755 --- a/README.md +++ b/README.md @@ -485,6 +485,7 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History +* v2.0, 2014-12-04, fix [#241](https://github.com/winlinvip/simple-rtmp-server/issues/241), add mw(merged-write) config. 2.0.53 * v2.0, 2014-12-04, for [#241](https://github.com/winlinvip/simple-rtmp-server/issues/241), support mr(merged-read) config and reload. 2.0.52. * v2.0, 2014-12-04, enable [#241](https://github.com/winlinvip/simple-rtmp-server/issues/241) and [#248](https://github.com/winlinvip/simple-rtmp-server/issues/248), +25% performance, 2.5k publisher. 2.0.50 * v2.0, 2014-12-04, fix [#248](https://github.com/winlinvip/simple-rtmp-server/issues/248), improve about 15% performance for fast buffer. 2.0.49 diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index 6edc5a0bb2..ddcbcb0eed 100755 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -143,7 +143,8 @@ vhost __defaultVhost__ { } # the MR(merged-read) setting for publisher. -vhost mr.srs.com { +# the MW(merged-write) settings for player. +vhost mrw.srs.com { # about MR, read https://github.com/winlinvip/simple-rtmp-server/issues/241 mr { # whether enable the MR(merged-read) @@ -160,6 +161,11 @@ vhost mr.srs.com { # default: 500 latency 500; } + # set the MW(merged-write) latency in ms. + # SRS always set mw on, so we just set the latency value. + # the latency of stream >= mw_latency + mr_latency + # default: 500 + mw_latency 500; } # vhost for edge, edge and origin is the same vhost diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index b804e3239a..cd4603b052 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -45,6 +45,7 @@ using namespace std; #include #include #include +#include using namespace _srs_internal; @@ -829,6 +830,17 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root) } srs_trace("vhost %s reload mr success.", vhost.c_str()); } + // mw, only one per vhost + if (!srs_directive_equals(new_vhost->get("mw_latency"), old_vhost->get("mw_latency"))) { + for (it = subscribes.begin(); it != subscribes.end(); ++it) { + ISrsReloadHandler* subscribe = *it; + if ((ret = subscribe->on_reload_vhost_mw(vhost)) != ERROR_SUCCESS) { + srs_error("vhost %s notify subscribes mw failed. ret=%d", vhost.c_str(), ret); + return ret; + } + } + srs_trace("vhost %s reload mw success.", vhost.c_str()); + } // http, only one per vhost. if (!srs_directive_equals(new_vhost->get("http"), old_vhost->get("http"))) { for (it = subscribes.begin(); it != subscribes.end(); ++it) { @@ -1327,7 +1339,7 @@ int SrsConfig::check_config() && n != "time_jitter" && n != "atc" && n != "atc_auto" && n != "debug_srs_upnode" - && n != "mr" + && n != "mr" && n != "mw_latency" ) { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("unsupported vhost directive %s, ret=%d", n.c_str(), ret); @@ -1951,7 +1963,7 @@ bool SrsConfig::get_gop_cache(string vhost) SrsConfDirective* conf = get_vhost(vhost); if (!conf) { - return true; + return SRS_PERF_GOP_CACHE; } conf = conf->get("gop_cache"); @@ -1959,7 +1971,7 @@ bool SrsConfig::get_gop_cache(string vhost) return false; } - return true; + return SRS_PERF_GOP_CACHE; } bool SrsConfig::get_debug_srs_upnode(string vhost) @@ -2032,12 +2044,12 @@ double SrsConfig::get_queue_length(string vhost) SrsConfDirective* conf = get_vhost(vhost); if (!conf) { - return SRS_CONF_DEFAULT_QUEUE_LENGTH; + return SRS_PERF_PLAY_QUEUE; } conf = conf->get("queue_length"); if (!conf || conf->arg0().empty()) { - return SRS_CONF_DEFAULT_QUEUE_LENGTH; + return SRS_PERF_PLAY_QUEUE; } return ::atoi(conf->arg0().c_str()); @@ -2106,17 +2118,17 @@ bool SrsConfig::get_mr_enabled(string vhost) SrsConfDirective* conf = get_vhost(vhost); if (!conf) { - return SRS_CONSTS_RTMP_MR; + return SRS_PERF_MR_ENABLED; } conf = conf->get("mr"); if (!conf) { - return SRS_CONSTS_RTMP_MR; + return SRS_PERF_MR_ENABLED; } conf = conf->get("enabled"); if (!conf || conf->arg0() != "on") { - return SRS_CONSTS_RTMP_MR; + return SRS_PERF_MR_ENABLED; } return true; @@ -2128,17 +2140,34 @@ int SrsConfig::get_mr_sleep_ms(string vhost) SrsConfDirective* conf = get_vhost(vhost); if (!conf) { - return SRS_CONSTS_RTMP_MR_SLEEP; + return SRS_PERF_MR_SLEEP; } conf = conf->get("mr"); if (!conf) { - return SRS_CONSTS_RTMP_MR_SLEEP; + return SRS_PERF_MR_SLEEP; } conf = conf->get("latency"); if (!conf || conf->arg0().empty()) { - return SRS_CONSTS_RTMP_MR_SLEEP; + return SRS_PERF_MR_SLEEP; + } + + return ::atoi(conf->arg0().c_str()); +} + +int SrsConfig::get_mw_sleep_ms(string vhost) +{ + + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return SRS_PERF_MW_SLEEP; + } + + conf = conf->get("mw_latency"); + if (!conf || conf->arg0().empty()) { + return SRS_PERF_MW_SLEEP; } return ::atoi(conf->arg0().c_str()); diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index 1ec0e24119..2c01d02a45 100644 --- a/trunk/src/app/srs_app_config.hpp +++ b/trunk/src/app/srs_app_config.hpp @@ -54,8 +54,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define SRS_CONF_DEFAULT_DVR_PLAN SRS_CONF_DEFAULT_DVR_PLAN_SESSION #define SRS_CONF_DEFAULT_DVR_DURATION 30 #define SRS_CONF_DEFAULT_TIME_JITTER "full" -// in seconds, the live queue length. -#define SRS_CONF_DEFAULT_QUEUE_LENGTH 30 // in seconds, the paused queue length. #define SRS_CONF_DEFAULT_PAUSED_LENGTH 10 // the interval in seconds for bandwidth check @@ -541,6 +539,12 @@ class SrsConfig */ // TODO: FIXME: add utest for mr config. virtual int get_mr_sleep_ms(std::string vhost); + /** + * get the mw sleep time in ms for vhost. + * @param vhost, the vhost to get the mw sleep time. + */ + // TODO: FIXME: add utest for mw config. + virtual int get_mw_sleep_ms(std::string vhost); private: /** * get the global chunk size. diff --git a/trunk/src/app/srs_app_reload.cpp b/trunk/src/app/srs_app_reload.cpp index 9581df1d17..cc69a9c448 100644 --- a/trunk/src/app/srs_app_reload.cpp +++ b/trunk/src/app/srs_app_reload.cpp @@ -145,6 +145,11 @@ int ISrsReloadHandler::on_reload_vhost_mr(string /*vhost*/) return ERROR_SUCCESS; } +int ISrsReloadHandler::on_reload_vhost_mw(string /*vhost*/) +{ + return ERROR_SUCCESS; +} + int ISrsReloadHandler::on_reload_vhost_transcode(string /*vhost*/) { return ERROR_SUCCESS; diff --git a/trunk/src/app/srs_app_reload.hpp b/trunk/src/app/srs_app_reload.hpp index a133c52d8d..878518b092 100644 --- a/trunk/src/app/srs_app_reload.hpp +++ b/trunk/src/app/srs_app_reload.hpp @@ -66,6 +66,7 @@ class ISrsReloadHandler virtual int on_reload_vhost_hls(std::string vhost); virtual int on_reload_vhost_dvr(std::string vhost); virtual int on_reload_vhost_mr(std::string vhost); + virtual int on_reload_vhost_mw(std::string vhost); virtual int on_reload_vhost_transcode(std::string vhost); virtual int on_reload_ingest_removed(std::string vhost, std::string ingest_id); virtual int on_reload_ingest_added(std::string vhost, std::string ingest_id); diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index 958c92396c..ff1b8696b1 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -83,6 +83,7 @@ SrsRtmpConn::SrsRtmpConn(SrsServer* srs_server, st_netfd_t client_stfd) duration = 0; kbps = new SrsKbps(); kbps->set_io(skt, skt); + mw_sleep = SRS_PERF_MW_SLEEP; _srs_config->subscribe(this); } @@ -209,6 +210,13 @@ int SrsRtmpConn::on_reload_vhost_removed(string vhost) return ret; } +int SrsRtmpConn::on_reload_vhost_mw(string /*vhost*/) +{ + mw_sleep = _srs_config->get_mw_sleep_ms(req->vhost); + + return ERROR_SUCCESS; +} + int64_t SrsRtmpConn::get_send_bytes_delta() { return kbps->get_send_bytes_delta(); @@ -361,7 +369,7 @@ int SrsRtmpConn::stream_service_cycle() } bool enabled_cache = _srs_config->get_gop_cache(req->vhost); - srs_trace("source url=%s, ip=%s, cache=%d, is_edge=%d, source_id=%d[%d]", + srs_trace("source url=%s, ip=%s, cache=%d, is_edge=%d, source_id=%d[%d]", req->get_stream_url().c_str(), ip.c_str(), enabled_cache, vhost_is_edge, source->source_id(), source->source_id()); source->set_cache(enabled_cache); @@ -592,17 +600,18 @@ int SrsRtmpConn::do_playing(SrsSource* source, SrsQueueRecvThread* trd) // no message to send, sleep a while. if (count <= 0) { srs_verbose("sleep for no messages to send"); - st_usleep(SRS_PERF_SEND_MSGS_CACHE * 1000); + st_usleep(mw_sleep * 1000); } // reportable if (pithy_print.can_print()) { kbps->sample(); srs_trace("-> "SRS_CONSTS_LOG_PLAY - " time=%"PRId64", msgs=%d, okbps=%d,%d,%d, ikbps=%d,%d,%d", + " time=%"PRId64", msgs=%d, okbps=%d,%d,%d, ikbps=%d,%d,%d, mw=%d", pithy_print.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() + kbps->get_recv_kbps(), kbps->get_recv_kbps_30s(), kbps->get_recv_kbps_5m(), + mw_sleep ); } @@ -774,10 +783,14 @@ int SrsRtmpConn::do_publishing(SrsSource* source, SrsPublishRecvThread* trd) // reportable if (pithy_print.can_print()) { kbps->sample(); + bool mr = _srs_config->get_mr_enabled(req->vhost); + int mr_sleep = _srs_config->get_mr_sleep_ms(req->vhost); srs_trace("<- "SRS_CONSTS_LOG_CLIENT_PUBLISH - " time=%"PRId64", okbps=%d,%d,%d, ikbps=%d,%d,%d", pithy_print.age(), + " time=%"PRId64", okbps=%d,%d,%d, ikbps=%d,%d,%d, mr=%d/%d", pithy_print.age(), 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()); + kbps->get_recv_kbps(), kbps->get_recv_kbps_30s(), kbps->get_recv_kbps_5m(), + mr, mr_sleep + ); } } diff --git a/trunk/src/app/srs_app_rtmp_conn.hpp b/trunk/src/app/srs_app_rtmp_conn.hpp index a10b9ecf65..a5a8885a20 100644 --- a/trunk/src/app/srs_app_rtmp_conn.hpp +++ b/trunk/src/app/srs_app_rtmp_conn.hpp @@ -71,6 +71,8 @@ class SrsRtmpConn : public virtual SrsConnection, public virtual ISrsReloadHandl // @see https://github.com/winlinvip/simple-rtmp-server/issues/47 int64_t duration; SrsKbps* kbps; + // the MR(merged-write) sleep time in ms. + int mw_sleep; public: SrsRtmpConn(SrsServer* srs_server, st_netfd_t client_stfd); virtual ~SrsRtmpConn(); @@ -81,6 +83,7 @@ class SrsRtmpConn : public virtual SrsConnection, public virtual ISrsReloadHandl // interface ISrsReloadHandler public: virtual int on_reload_vhost_removed(std::string vhost); + virtual int on_reload_vhost_mw(std::string vhost); // interface IKbpsDelta public: virtual int64_t get_send_bytes_delta(); diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index b5926e0383..da11a8ed69 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 52 +#define VERSION_REVISION 53 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" @@ -48,6 +48,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define RTMP_SIG_SRS_HANDSHAKE RTMP_SIG_SRS_KEY"("RTMP_SIG_SRS_VERSION")" #define RTMP_SIG_SRS_RELEASE "https://github.com/winlinvip/simple-rtmp-server/tree/1.0release" #define RTMP_SIG_SRS_HTTP_SERVER "https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPServer#feature" +#define RTMP_SIG_SRS_ISSUES(id) "https://github.com/winlinvip/simple-rtmp-server/issues/"#id #define RTMP_SIG_SRS_VERSION __SRS_XSTR(VERSION_MAJOR)"."__SRS_XSTR(VERSION_MINOR)"."__SRS_XSTR(VERSION_REVISION) // internal macros, covert macro values to str, diff --git a/trunk/src/core/srs_core_performance.hpp b/trunk/src/core/srs_core_performance.hpp index 6407ae8df5..d2e1489497 100644 --- a/trunk/src/core/srs_core_performance.hpp +++ b/trunk/src/core/srs_core_performance.hpp @@ -38,6 +38,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * to improve read performance, merge some packets then read, * when it on and read small bytes, we sleep to wait more data., * that is, we merge some data to read together. +* @see SrsConfig::get_mr_enabled() +* @see SrsConfig::get_mr_sleep_ms() * @see https://github.com/winlinvip/simple-rtmp-server/issues/241 * @example, for the default settings, this algorithm will use: * that is, when got nread bytes smaller than 4KB, sleep(780ms). @@ -55,11 +57,14 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * For example, sleep 120ms. Then there is, and always 120ms data in buffer. * That is, the latency is 120ms(the sleep time). */ -// to enable merged read. #define SRS_PERF_MERGED_READ +// the default config of mr. +#define SRS_PERF_MR_ENABLED false +#define SRS_PERF_MR_SLEEP 500 /** -* the send cache time in ms. +* the MW(merged-write) send cache time in ms. +* the default value, user can override it in config. * to improve send performance, cache msgs and send in a time. * for example, cache 500ms videos and audios, then convert all these * msgs to iovecs, finally use writev to send. @@ -67,8 +72,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * the latency+ when cache+. * @remark the socket send buffer default to 185KB, it large enough. * @see https://github.com/winlinvip/simple-rtmp-server/issues/194 +* @see SrsConfig::get_mw_sleep_ms() */ -#define SRS_PERF_SEND_MSGS_CACHE 500 +// the default config of mw. +#define SRS_PERF_MW_SLEEP 500 /** * how many chunk stream to cache, [0, N]. @@ -78,5 +85,13 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define SRS_PERF_CHUNK_STREAM_CACHE 16 +/** +* the gop cache and play cache queue. +*/ +// whether gop cache is on. +#define SRS_PERF_GOP_CACHE true +// in seconds, the live queue length. +#define SRS_PERF_PLAY_QUEUE 30 + #endif diff --git a/trunk/src/kernel/srs_kernel_consts.hpp b/trunk/src/kernel/srs_kernel_consts.hpp index 194c9f9d20..d30fd07d11 100644 --- a/trunk/src/kernel/srs_kernel_consts.hpp +++ b/trunk/src/kernel/srs_kernel_consts.hpp @@ -50,10 +50,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // 6. Chunking, RTMP protocol default chunk size. #define SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE 128 -// the default setting of mr. -#define SRS_CONSTS_RTMP_MR false -#define SRS_CONSTS_RTMP_MR_SLEEP 500 - /** * 6. Chunking * The chunk size is configurable. It can be set using a control diff --git a/trunk/src/main/srs_main_server.cpp b/trunk/src/main/srs_main_server.cpp index dcb3b5448c..26a2d585d8 100644 --- a/trunk/src/main/srs_main_server.cpp +++ b/trunk/src/main/srs_main_server.cpp @@ -39,6 +39,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #include +#include // pre-declare int run(); @@ -130,6 +131,24 @@ void show_macro_features() #else srs_warn("check feature compile ffmpeg: off"); #endif + +#ifdef SRS_PERF_MERGED_READ + srs_trace("MR(merged-read): on, @see %s", RTMP_SIG_SRS_ISSUES(241)); +#else + srs_warn("MR(merged-read): off, @see %s", RTMP_SIG_SRS_ISSUES(241)); +#endif + + srs_trace("MR(merged-read) default %d sleep %d", SRS_PERF_MR_ENABLED, SRS_PERF_MR_SLEEP); + srs_trace("MW(merged-write) default sleep %d", SRS_PERF_MW_SLEEP); + srs_trace("read chunk stream cache cid [0, %d)", SRS_PERF_CHUNK_STREAM_CACHE); + srs_trace("default gop cache %d, play queue %ds", SRS_PERF_GOP_CACHE, SRS_PERF_PLAY_QUEUE); + + int possible_mr_latency = 0; +#ifdef SRS_PERF_MERGED_READ + possible_mr_latency = SRS_PERF_MR_SLEEP; +#endif + srs_trace("system default latency in ms: mw(0-%d) + mr(0-%d) + play-queue(0-%d)", + SRS_PERF_MW_SLEEP, possible_mr_latency, SRS_PERF_PLAY_QUEUE*1000); } void check_macro_features() @@ -139,6 +158,10 @@ void check_macro_features() srs_warn("http server is dev feature, @see %s", RTMP_SIG_SRS_HTTP_SERVER); #endif +#ifndef SRS_PERF_MERGED_READ + srs_warn("MR(merged-read) is disabled, hurts read performance. @see %s", RTMP_SIG_SRS_ISSUES(241)); +#endif + #if VERSION_MAJOR > 1 #warning "using develop SRS, please use release instead." srs_warn("SRS %s is develop branch, please use %s instead", RTMP_SIG_SRS_VERSION, RTMP_SIG_SRS_RELEASE); From 279b4ff4c91942ba729204f8a0f3750a4d585d41 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 4 Dec 2014 21:37:04 +0800 Subject: [PATCH 317/800] refine macros --- trunk/src/core/srs_core.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index da11a8ed69..8e11138aec 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -46,9 +46,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define RTMP_SIG_SRS_AUTHROS "wenjie.zhao" #define RTMP_SIG_SRS_CONTRIBUTORS_URL RTMP_SIG_SRS_URL"/blob/master/AUTHORS.txt" #define RTMP_SIG_SRS_HANDSHAKE RTMP_SIG_SRS_KEY"("RTMP_SIG_SRS_VERSION")" -#define RTMP_SIG_SRS_RELEASE "https://github.com/winlinvip/simple-rtmp-server/tree/1.0release" -#define RTMP_SIG_SRS_HTTP_SERVER "https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_HTTPServer#feature" -#define RTMP_SIG_SRS_ISSUES(id) "https://github.com/winlinvip/simple-rtmp-server/issues/"#id +#define RTMP_SIG_SRS_RELEASE RTMP_SIG_SRS_URL"/tree/1.0release" +#define RTMP_SIG_SRS_HTTP_SERVER RTMP_SIG_SRS_URL"/wiki/v1_CN_HTTPServer#feature" +#define RTMP_SIG_SRS_ISSUES(id) RTMP_SIG_SRS_URL"/issues/"#id #define RTMP_SIG_SRS_VERSION __SRS_XSTR(VERSION_MAJOR)"."__SRS_XSTR(VERSION_MINOR)"."__SRS_XSTR(VERSION_REVISION) // internal macros, covert macro values to str, From 09101b8d5e3e487c40f8c8f4c8a1a204cd040978 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 4 Dec 2014 21:39:42 +0800 Subject: [PATCH 318/800] remove the increase timeout for read, for there is no small timeout. --- trunk/src/rtmp/srs_protocol_stack.cpp | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp index 1eee9d9e87..058793c456 100644 --- a/trunk/src/rtmp/srs_protocol_stack.cpp +++ b/trunk/src/rtmp/srs_protocol_stack.cpp @@ -1113,20 +1113,6 @@ int SrsProtocol::recv_interlaced_message(SrsMessage** pmsg) // the cid must not negative. srs_assert(cid >= 0); - // once we got the chunk message header, - // that is there is a real message in cache, - // increase the timeout to got it. - // For example, in the play loop, we set timeout to 100ms, - // when we got a chunk header, we should increase the timeout, - // or we maybe timeout and disconnect the client. - int64_t timeout_us = skt->get_recv_timeout(); - if (!skt->is_never_timeout(timeout_us)) { - int64_t pkt_timeout_us = srs_max(timeout_us, SRS_MIN_RECV_TIMEOUT_US); - skt->set_recv_timeout(pkt_timeout_us); - srs_verbose("change recv timeout_us " - "from %"PRId64" to %"PRId64"", timeout_us, pkt_timeout_us); - } - // get the cached chunk stream. SrsChunkStream* chunk = NULL; @@ -1177,12 +1163,6 @@ int SrsProtocol::recv_interlaced_message(SrsMessage** pmsg) return ret; } - // reset the recv timeout - if (!skt->is_never_timeout(timeout_us)) { - skt->set_recv_timeout(timeout_us); - srs_verbose("reset recv timeout_us to %"PRId64"", timeout_us); - } - // not got an entire RTMP message, try next chunk. if (!msg) { srs_verbose("get partial message success. chunk_payload_size=%d, size=%d, message(type=%d, size=%d, time=%"PRId64", sid=%d)", From 76af04c55d8311ac13de82fa6a4c6db24441b0b2 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 4 Dec 2014 21:50:23 +0800 Subject: [PATCH 319/800] refine the recv buffer for mr. --- trunk/src/app/srs_app_recv_thread.cpp | 105 ++++++++++++++------------ trunk/src/app/srs_app_recv_thread.hpp | 2 +- 2 files changed, 57 insertions(+), 50 deletions(-) diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index 578bc842eb..877ed14a65 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -300,8 +300,14 @@ void SrsPublishRecvThread::on_thread_start() // for the main thread never send message. #ifdef SRS_PERF_MERGED_READ - // for mr. - update_buffer(mr, mr_sleep); + if (mr) { + // set underlayer buffer size + set_socket_buffer(mr_sleep); + + // disable the merge read + // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 + rtmp->set_merge_read(true, this); + } #endif } @@ -315,9 +321,11 @@ void SrsPublishRecvThread::on_thread_stop() st_cond_signal(error); #ifdef SRS_PERF_MERGED_READ - // disable the merge read - // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 - rtmp->set_merge_read(false, NULL); + if (mr) { + // disable the merge read + // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 + rtmp->set_merge_read(false, NULL); + } #endif } @@ -383,41 +391,24 @@ int SrsPublishRecvThread::on_reload_vhost_mr(string vhost) // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 bool mr_enabled = _srs_config->get_mr_enabled(req->vhost); int sleep_ms = _srs_config->get_mr_sleep_ms(req->vhost); - update_buffer(mr_enabled, sleep_ms); - - return ret; -} -void SrsPublishRecvThread::update_buffer(bool mr_enabled, int sleep_ms) -{ - // TODO: FIXME: refine it. - + // update buffer when sleep ms changed. + if (mr_sleep != sleep_ms) { + set_socket_buffer(sleep_ms); + } + #ifdef SRS_PERF_MERGED_READ - // update the buffer. - if (true) { - // the underlayer api will set to SRS_MR_SOCKET_BUFFER bytes. - // 4KB=4096, 8KB=8192, 16KB=16384, 32KB=32768, 64KB=65536, - // 128KB=131072, 256KB=262144, 512KB=524288 - // the buffer should set to SRS_MR_MAX_SLEEP_MS*kbps/8, - // for example, your system delivery stream in 1000kbps, - // sleep 800ms for small bytes, the buffer should set to: - // 800*1000/8=100000B(about 128KB). - // 2000*3000/8=750000B(about 732KB). - int kbps = 3000; - int socket_buffer_size = mr_sleep * kbps / 8; - - // socket recv buffer, system will double it. - int nb_rbuf = socket_buffer_size / 2; - socklen_t sock_buf_size = sizeof(int); - if (setsockopt(mr_fd, SOL_SOCKET, SO_RCVBUF, &nb_rbuf, sock_buf_size) < 0) { - srs_warn("set sock SO_RCVBUF=%d failed.", nb_rbuf); - } - getsockopt(mr_fd, SOL_SOCKET, SO_RCVBUF, &nb_rbuf, &sock_buf_size); - - srs_trace("merged read sockbuf=%d, actual=%d, sleep %d when nread<=%d", - socket_buffer_size, nb_rbuf, mr_sleep, SRS_MR_SMALL_BYTES); - - rtmp->set_recv_buffer(nb_rbuf); + // mr enabled=>disabled + if (mr && !mr_enabled) { + // disable the merge read + // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 + rtmp->set_merge_read(false, NULL); + } + // mr disabled=>enabled + if (!mr && mr_enabled) { + // enable the merge read + // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 + rtmp->set_merge_read(true, this); } #endif @@ -425,17 +416,33 @@ void SrsPublishRecvThread::update_buffer(bool mr_enabled, int sleep_ms) mr = mr_enabled; mr_sleep = sleep_ms; -#ifdef SRS_PERF_MERGED_READ - // apply new state. - if (mr) { - // enable the merge read - // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 - rtmp->set_merge_read(true, this); - } else { - // disable the merge read - // @see https://github.com/winlinvip/simple-rtmp-server/issues/241 - rtmp->set_merge_read(false, NULL); + return ret; +} + +void SrsPublishRecvThread::set_socket_buffer(int sleep_ms) +{ + // the underlayer api will set to SRS_MR_SOCKET_BUFFER bytes. + // 4KB=4096, 8KB=8192, 16KB=16384, 32KB=32768, 64KB=65536, + // 128KB=131072, 256KB=262144, 512KB=524288 + // the buffer should set to SRS_MR_MAX_SLEEP_MS*kbps/8, + // for example, your system delivery stream in 1000kbps, + // sleep 800ms for small bytes, the buffer should set to: + // 800*1000/8=100000B(about 128KB). + // 2000*3000/8=750000B(about 732KB). + int kbps = 3000; + int socket_buffer_size = sleep_ms * kbps / 8; + + // socket recv buffer, system will double it. + int nb_rbuf = socket_buffer_size / 2; + socklen_t sock_buf_size = sizeof(int); + if (setsockopt(mr_fd, SOL_SOCKET, SO_RCVBUF, &nb_rbuf, sock_buf_size) < 0) { + srs_warn("set sock SO_RCVBUF=%d failed.", nb_rbuf); } -#endif + getsockopt(mr_fd, SOL_SOCKET, SO_RCVBUF, &nb_rbuf, &sock_buf_size); + + srs_trace("merged read sockbuf=%d, actual=%d, sleep %d when nread<=%d", + socket_buffer_size, nb_rbuf, sleep_ms, SRS_MR_SMALL_BYTES); + + rtmp->set_recv_buffer(nb_rbuf); } diff --git a/trunk/src/app/srs_app_recv_thread.hpp b/trunk/src/app/srs_app_recv_thread.hpp index c39ec51ca0..125a16e671 100644 --- a/trunk/src/app/srs_app_recv_thread.hpp +++ b/trunk/src/app/srs_app_recv_thread.hpp @@ -194,7 +194,7 @@ class SrsPublishRecvThread : virtual public ISrsMessageHandler public: virtual int on_reload_vhost_mr(std::string vhost); private: - virtual void update_buffer(bool mr_enabled, int sleep_ms); + virtual void set_socket_buffer(int sleep_ms); }; #endif From 98647d6e6730edfd437b791b1df58f32427eb344 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 4 Dec 2014 22:00:09 +0800 Subject: [PATCH 320/800] limit the user-space buffer size to 128KB, 128MB for 1k publishers. --- trunk/src/app/srs_app_recv_thread.cpp | 3 ++- trunk/src/rtmp/srs_protocol_buffer.cpp | 19 +++++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index 877ed14a65..e17f9737ee 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -429,7 +429,8 @@ void SrsPublishRecvThread::set_socket_buffer(int sleep_ms) // sleep 800ms for small bytes, the buffer should set to: // 800*1000/8=100000B(about 128KB). // 2000*3000/8=750000B(about 732KB). - int kbps = 3000; + // 2000*5000/8=1250000B(about 1220KB). + int kbps = 5000; int socket_buffer_size = sleep_ms * kbps / 8; // socket recv buffer, system will double it. diff --git a/trunk/src/rtmp/srs_protocol_buffer.cpp b/trunk/src/rtmp/srs_protocol_buffer.cpp index bc5d217845..f801380c0d 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.cpp +++ b/trunk/src/rtmp/srs_protocol_buffer.cpp @@ -28,8 +28,13 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include -// the default recv buffer size -#define SRS_DEFAULT_RECV_BUFFER_SIZE 32768 +// the default recv buffer size, 128KB. +#define SRS_DEFAULT_RECV_BUFFER_SIZE 131072 + +// limit user-space buffer to 256KB, for 3Mbps stream delivery. +// 800*2000/8=200000B(about 195KB). +// @remark it's ok for higher stream, the buffer is ok for one chunk is 256KB. +#define SRS_MAX_SOCKET_BUFFER 262144 // the max header size, // @see SrsProtocol::read_message_header(). @@ -100,15 +105,21 @@ SrsFastBuffer::SrsFastBuffer() void SrsFastBuffer::set_buffer(int buffer_size) { + // the user-space buffer size limit to a max value. + int nb_max_buf = srs_min(buffer_size, SRS_MAX_SOCKET_BUFFER); + if (nb_max_buf < buffer_size) { + srs_warn("limit the user-space buffer from %d to %d", buffer_size, nb_max_buf); + } + // only realloc when buffer changed bigger - if (buffer_size <= nb_buffer) { + if (nb_max_buf <= nb_buffer) { return; } int start = p - buffer; int cap = end - p; - char* buf = new char[buffer_size]; + char* buf = new char[nb_max_buf]; if (cap > 0) { memcpy(buf, buffer, nb_buffer); } From 4c6cf959c155221f8e8e6115b43178068da98375 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 4 Dec 2014 22:10:05 +0800 Subject: [PATCH 321/800] refine fast buffer, check available bytes to read. --- trunk/src/kernel/srs_kernel_error.hpp | 3 +-- trunk/src/rtmp/srs_protocol_buffer.cpp | 34 +++++++++++++------------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index 75db04fafa..51141b107b 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -60,7 +60,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define ERROR_SYSTEM_PACKET_INVALID 1019 #define ERROR_SYSTEM_CLIENT_INVALID 1020 #define ERROR_SYSTEM_ASSERT_FAILED 1021 -#define ERROR_SYSTEM_SIZE_NEGATIVE 1022 +#define ERROR_READER_BUFFER_OVERFLOW 1022 #define ERROR_SYSTEM_CONFIG_INVALID 1023 #define ERROR_SYSTEM_CONFIG_DIRECTIVE 1024 #define ERROR_SYSTEM_CONFIG_BLOCK_START 1025 @@ -134,7 +134,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define ERROR_OpenSslSha256DigestSize 2037 #define ERROR_OpenSslGetPeerPublicKey 2038 #define ERROR_OpenSslComputeSharedKey 2039 -#define ERROR_RTMP_BUFFER_OVERFLOW 2040 // // system control message, // not an error, but special control logic. diff --git a/trunk/src/rtmp/srs_protocol_buffer.cpp b/trunk/src/rtmp/srs_protocol_buffer.cpp index f801380c0d..20b9d2be3f 100644 --- a/trunk/src/rtmp/srs_protocol_buffer.cpp +++ b/trunk/src/rtmp/srs_protocol_buffer.cpp @@ -169,18 +169,18 @@ int SrsFastBuffer::grow(ISrsBufferReader* reader, int required_size) { int ret = ERROR_SUCCESS; - if (required_size < 0) { - ret = ERROR_SYSTEM_SIZE_NEGATIVE; - srs_error("size is negative. size=%d, ret=%d", required_size, ret); + // generally the required size is ok. + if (end - p >= required_size) { return ret; } - // when read payload and need to grow, reset buffer. - // or there is no space to read. + // must be positive. + srs_assert(required_size > 0); + + // when read payload or there is no space to read, + // reset the buffer with exists bytes. int max_to_read = buffer + nb_buffer - end; - if (end - p < required_size - && (required_size > SRS_RTMP_MAX_MESSAGE_HEADER || max_to_read < required_size) - ) { + if (required_size > SRS_RTMP_MAX_MESSAGE_HEADER || max_to_read < required_size) { int nb_cap = end - p; srs_verbose("move fast buffer %d bytes", nb_cap); if (nb_cap < nb_buffer) { @@ -190,16 +190,16 @@ int SrsFastBuffer::grow(ISrsBufferReader* reader, int required_size) } } + // directly check the available bytes to read in buffer. + max_to_read = buffer + nb_buffer - end; + if (max_to_read < required_size) { + ret = ERROR_READER_BUFFER_OVERFLOW; + srs_error("buffer overflow, required=%d, max=%d, ret=%d", required_size, nb_buffer, ret); + return ret; + } + + // buffer is ok, read required size of bytes. while (end - p < required_size) { - // the max to read is the left bytes. - max_to_read = buffer + nb_buffer - end; - - if (max_to_read <= 0) { - ret = ERROR_RTMP_BUFFER_OVERFLOW; - srs_error("buffer overflow, required=%d, max=%d, ret=%d", required_size, nb_buffer, ret); - return ret; - } - ssize_t nread; if ((ret = reader->read(end, max_to_read, &nread)) != ERROR_SUCCESS) { return ret; From 4d1c162ebd6b9dfc400fcc47eaa657640249f721 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 4 Dec 2014 22:28:33 +0800 Subject: [PATCH 322/800] fix utest failed bug. --- trunk/src/qt/srs/srs-qt.pro.user | 1 + trunk/src/utest/srs_utest_config.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/trunk/src/qt/srs/srs-qt.pro.user b/trunk/src/qt/srs/srs-qt.pro.user index 8672380b5b..ffd773f849 100755 --- a/trunk/src/qt/srs/srs-qt.pro.user +++ b/trunk/src/qt/srs/srs-qt.pro.user @@ -1,5 +1,6 @@ + ProjectExplorer.Project.ActiveTarget diff --git a/trunk/src/utest/srs_utest_config.cpp b/trunk/src/utest/srs_utest_config.cpp index 072a16a7aa..797da573d2 100644 --- a/trunk/src/utest/srs_utest_config.cpp +++ b/trunk/src/utest/srs_utest_config.cpp @@ -28,6 +28,7 @@ using namespace std; #include #include #include +#include MockSrsConfigBuffer::MockSrsConfigBuffer(string buf) { @@ -1092,7 +1093,7 @@ VOID TEST(ConfigTest, CheckMacros) #ifndef SRS_CONF_DEFAULT_TIME_JITTER EXPECT_TRUE(false); #endif -#ifndef SRS_CONF_DEFAULT_QUEUE_LENGTH +#ifndef SRS_PERF_PLAY_QUEUE EXPECT_TRUE(false); #endif #ifndef SRS_CONF_DEFAULT_PAUSED_LENGTH From fad6074a255b75889028bd76020a6df3119191ca Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 5 Dec 2014 11:24:05 +0800 Subject: [PATCH 323/800] set send socket buf. mw/mr sleep default to 350. --- README.md | 3 +- trunk/conf/full.conf | 9 ++-- trunk/src/app/srs_app_recv_thread.cpp | 5 ++- trunk/src/app/srs_app_rtmp_conn.cpp | 58 +++++++++++++++++++++++-- trunk/src/app/srs_app_rtmp_conn.hpp | 3 ++ trunk/src/app/srs_app_source.cpp | 2 +- trunk/src/core/srs_core.hpp | 2 +- trunk/src/core/srs_core_performance.hpp | 21 ++++++++- trunk/src/kernel/srs_kernel_consts.hpp | 26 +++-------- trunk/src/rtmp/srs_protocol_stack.cpp | 8 +++- 10 files changed, 101 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 3fc55843c6..9681f71508 100755 --- a/README.md +++ b/README.md @@ -726,6 +726,7 @@ The play benchmark by [st-load](https://github.com/winlinvip/st-load): * 2014-11-12, SRS 2.0.14, 3.5k(3500)clients, 95%CPU, 78MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/8acd143a7a152885b815999162660fd4e7a3f247) * 2014-11-13, SRS 2.0.15, 6.0k(6000)clients, 82%CPU, 203MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/cc6aca9ad55342a06440ce7f3b38453776b2b2d1) * 2014-11-22, SRS 2.0.30, 7.5k(7500)clients, 87%CPU, 320MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/58136ec178e3d47db6c90a59875d7e40946936e5) +* 2014-12-05, SRS 2.0.55, 8.0k(8000)clients, 89%CPU, 360MB. (mw_sleep=1800)[commit](https://github.com/winlinvip/simple-rtmp-server/commit/58136ec178e3d47db6c90a59875d7e40946936e5) ### Publish benchmark @@ -739,7 +740,7 @@ The publish benchmark by [st-load](https://github.com/winlinvip/st-load): * 2014-12-04, SRS 2.0.49, 1.4k(1400) publishers, 68%CPU, 144MB. * 2014-12-04, SRS 2.0.49, 2.5k(2500) publishers, 95%CPU, 404MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/29324fab469e0f7cef9ad04ffdbce832ac7dd9ff) * 2014-12-04, SRS 2.0.51, 2.5k(2500) publishers, 91%CPU, 259MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/f57801eb46c16755b173984b915a4166922df6a6) -* 2014-12-04, SRS 2.0.52, 4.0k(4000) publishers, 80%CPU, 331MB. (mr_sleep=2000) [commit](https://github.com/winlinvip/simple-rtmp-server/commit/5589b13d2e216b91f97afb78ee0c011b2fccf7da) +* 2014-12-04, SRS 2.0.52, 4.0k(4000) publishers, 80%CPU, 331MB. (mr_sleep=2000)[commit](https://github.com/winlinvip/simple-rtmp-server/commit/5589b13d2e216b91f97afb78ee0c011b2fccf7da) ## Architecture diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index ddcbcb0eed..de0ca04987 100755 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -158,14 +158,15 @@ vhost mrw.srs.com { # when there are 2500 publisher, the total memory of SRS atleast: # 183KB * 2500 = 446MB # the value recomment is [300, 2000] - # default: 500 - latency 500; + # default: 350 + latency 350; } # set the MW(merged-write) latency in ms. # SRS always set mw on, so we just set the latency value. # the latency of stream >= mw_latency + mr_latency - # default: 500 - mw_latency 500; + # the value recomment is [300, 1800] + # default: 350 + mw_latency 350; } # vhost for edge, edge and origin is the same vhost diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index e17f9737ee..6ee404a023 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -421,13 +421,14 @@ int SrsPublishRecvThread::on_reload_vhost_mr(string vhost) void SrsPublishRecvThread::set_socket_buffer(int sleep_ms) { - // the underlayer api will set to SRS_MR_SOCKET_BUFFER bytes. + // the bytes: // 4KB=4096, 8KB=8192, 16KB=16384, 32KB=32768, 64KB=65536, // 128KB=131072, 256KB=262144, 512KB=524288 - // the buffer should set to SRS_MR_MAX_SLEEP_MS*kbps/8, + // the buffer should set to sleep*kbps/8, // for example, your system delivery stream in 1000kbps, // sleep 800ms for small bytes, the buffer should set to: // 800*1000/8=100000B(about 128KB). + // other examples: // 2000*3000/8=750000B(about 732KB). // 2000*5000/8=1250000B(about 1220KB). int kbps = 5000; diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index ff1b8696b1..aa5a86942a 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -83,7 +83,9 @@ SrsRtmpConn::SrsRtmpConn(SrsServer* srs_server, st_netfd_t client_stfd) duration = 0; kbps = new SrsKbps(); kbps->set_io(skt, skt); + mw_sleep = SRS_PERF_MW_SLEEP; + mw_enabled = false; _srs_config->subscribe(this); } @@ -212,7 +214,10 @@ int SrsRtmpConn::on_reload_vhost_removed(string vhost) int SrsRtmpConn::on_reload_vhost_mw(string /*vhost*/) { - mw_sleep = _srs_config->get_mw_sleep_ms(req->vhost); + int sleep_ms = _srs_config->get_mw_sleep_ms(req->vhost); + + // when mw_sleep changed, resize the socket send buffer. + change_mw_sleep(sleep_ms); return ERROR_SUCCESS; } @@ -513,8 +518,7 @@ int SrsRtmpConn::playing(SrsSource* source) // use isolate thread to recv, // @see: https://github.com/winlinvip/simple-rtmp-server/issues/217 - SrsQueueRecvThread trd(rtmp, - SRS_CONSTS_RTMP_PULSE_TIMEOUT_US / 1000); + SrsQueueRecvThread trd(rtmp, SRS_PERF_MW_SLEEP); // start isolate recv thread. if ((ret = trd.start()) != ERROR_SUCCESS) { @@ -558,10 +562,15 @@ int SrsRtmpConn::do_playing(SrsSource* source, SrsQueueRecvThread* trd) // initialize other components SrsPithyPrint pithy_print(SRS_CONSTS_STAGE_PLAY_USER); - SrsMessageArray msgs(SYS_CONSTS_MAX_PLAY_SEND_MSGS); + SrsMessageArray msgs(SRS_PERF_MW_MSGS); bool user_specified_duration_to_stop = (req->duration > 0); int64_t starttime = -1; + // setup the mw config. + // when mw_sleep changed, resize the socket send buffer. + mw_enabled = true; + change_mw_sleep(_srs_config->get_mw_sleep_ms(req->vhost)); + while (true) { // to use isolate thread to recv, can improve about 33% performance. // @see: https://github.com/winlinvip/simple-rtmp-server/issues/196 @@ -602,6 +611,7 @@ int SrsRtmpConn::do_playing(SrsSource* source, SrsQueueRecvThread* trd) srs_verbose("sleep for no messages to send"); st_usleep(mw_sleep * 1000); } + srs_info("got %d msgs, mw=%d", count, mw_sleep); // reportable if (pithy_print.can_print()) { @@ -980,6 +990,46 @@ int SrsRtmpConn::process_play_control_msg(SrsConsumer* consumer, SrsMessage* msg return ret; } +void SrsRtmpConn::change_mw_sleep(int sleep_ms) +{ + if (!mw_enabled) { + return; + } + + // the bytes: + // 4KB=4096, 8KB=8192, 16KB=16384, 32KB=32768, 64KB=65536, + // 128KB=131072, 256KB=262144, 512KB=524288 + // the buffer should set to sleep*kbps/8, + // for example, your system delivery stream in 1000kbps, + // sleep 800ms for small bytes, the buffer should set to: + // 800*1000/8=100000B(about 128KB). + // other examples: + // 2000*3000/8=750000B(about 732KB). + // 2000*5000/8=1250000B(about 1220KB). + int kbps = 5000; + int socket_buffer_size = sleep_ms * kbps / 8; + + int fd = st_netfd_fileno(stfd); + int onb_sbuf = 0; + socklen_t sock_buf_size = sizeof(int); + getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &onb_sbuf, &sock_buf_size); + + // socket send buffer, system will double it. + int nb_sbuf = socket_buffer_size / 2; + + // set the socket send buffer when required larger buffer + if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &nb_sbuf, sock_buf_size) < 0) { + srs_warn("set sock SO_SENDBUF=%d failed.", nb_sbuf); + } + getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &nb_sbuf, &sock_buf_size); + + srs_trace("mw changed %d=>%d, max_msgs=%d, esbuf=%d, sbuf %d=>%d", + mw_sleep, sleep_ms, SRS_PERF_MW_MSGS, socket_buffer_size, + onb_sbuf, nb_sbuf); + + mw_sleep = sleep_ms; +} + int SrsRtmpConn::check_edge_token_traverse_auth() { int ret = ERROR_SUCCESS; diff --git a/trunk/src/app/srs_app_rtmp_conn.hpp b/trunk/src/app/srs_app_rtmp_conn.hpp index a5a8885a20..3ecaf82cab 100644 --- a/trunk/src/app/srs_app_rtmp_conn.hpp +++ b/trunk/src/app/srs_app_rtmp_conn.hpp @@ -73,6 +73,8 @@ class SrsRtmpConn : public virtual SrsConnection, public virtual ISrsReloadHandl SrsKbps* kbps; // the MR(merged-write) sleep time in ms. int mw_sleep; + // the MR(merged-write) only enabled for play. + int mw_enabled; public: SrsRtmpConn(SrsServer* srs_server, st_netfd_t client_stfd); virtual ~SrsRtmpConn(); @@ -102,6 +104,7 @@ class SrsRtmpConn : public virtual SrsConnection, public virtual ISrsReloadHandl virtual int handle_publish_message(SrsSource* source, SrsMessage* msg, bool is_fmle, bool vhost_is_edge); virtual int process_publish_message(SrsSource* source, SrsMessage* msg, bool vhost_is_edge); virtual int process_play_control_msg(SrsConsumer* consumer, SrsMessage* msg); + virtual void change_mw_sleep(int sleep_ms); private: virtual int check_edge_token_traverse_auth(); virtual int connect_server(int origin_index, st_netfd_t* pstsock); diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index d03b6dc08f..baa3e50192 100644 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -216,7 +216,7 @@ int SrsMessageQueue::dump_packets(int max_count, SrsMessage** pmsgs, int& count) } else { // erase some vector elements may cause memory copy, // maybe can use more efficient vector.swap to avoid copy. - // @remark for the pmsgs is big enough, for instance, SYS_CONSTS_MAX_PLAY_SEND_MSGS 128, + // @remark for the pmsgs is big enough, for instance, SRS_PERF_MW_MSGS 128, // the rtmp play client will get 128msgs once, so this branch rarely execute. msgs.erase(msgs.begin(), msgs.begin() + count); } diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 8e11138aec..13a24da78b 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 53 +#define VERSION_REVISION 55 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/core/srs_core_performance.hpp b/trunk/src/core/srs_core_performance.hpp index d2e1489497..e54f4ef0f4 100644 --- a/trunk/src/core/srs_core_performance.hpp +++ b/trunk/src/core/srs_core_performance.hpp @@ -60,7 +60,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define SRS_PERF_MERGED_READ // the default config of mr. #define SRS_PERF_MR_ENABLED false -#define SRS_PERF_MR_SLEEP 500 +#define SRS_PERF_MR_SLEEP 350 /** * the MW(merged-write) send cache time in ms. @@ -73,9 +73,26 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * @remark the socket send buffer default to 185KB, it large enough. * @see https://github.com/winlinvip/simple-rtmp-server/issues/194 * @see SrsConfig::get_mw_sleep_ms() +* @remark the mw sleep and msgs to send, maybe: +* mw_sleep msgs iovs +* 350 24/48 48/84 +* 500 24/48 48/84 +* 800 42/64 84/128 +* 1000 64/85 128/170 +* 1200 65/86 130/172 +* 1500 87/110 174/220 +* 1800 106/128 212/256 +* 2000 134/142 268/284 */ // the default config of mw. -#define SRS_PERF_MW_SLEEP 500 +#define SRS_PERF_MW_SLEEP 350 +/** +* how many msgs can be send entirely. +* for play clients to get msgs then totally send out. +* for the mw sleep set to 1800, the msgs is about 128. +* @remark, recomment to 156. +*/ +#define SRS_PERF_MW_MSGS 156 /** * how many chunk stream to cache, [0, N]. diff --git a/trunk/src/kernel/srs_kernel_consts.hpp b/trunk/src/kernel/srs_kernel_consts.hpp index d30fd07d11..5b5860c0c9 100644 --- a/trunk/src/kernel/srs_kernel_consts.hpp +++ b/trunk/src/kernel/srs_kernel_consts.hpp @@ -98,42 +98,28 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // always use fmt0 as cache. //#define SRS_CONSTS_RTMP_MAX_FMT3_HEADER_SIZE 5 -/** -* how many msgs can be send entirely. -* for play clients to get msgs then totally send out. -* for example, 25fps video, 40ms per video packet, -* while audio is 20ms per audio packet where 2/3 is audios, -* when SYS_CONSTS_MAX_PLAY_SEND_MSGS is 128, then -* we will send all 128*40ms/3=1706ms packets in a time, -* which should greater than the SRS_CONSTS_RTMP_PULSE_TIMEOUT_US -* (for example, 500ms), that is, we should: -* SYS_CONSTS_MAX_PLAY_SEND_MSGS * 40 / 3 >= SRS_CONSTS_RTMP_PULSE_TIMEOUT_US -* @remark, recomment to 128. -*/ -#define SYS_CONSTS_MAX_PLAY_SEND_MSGS 128 /** * for performance issue, * the iovs cache, @see https://github.com/winlinvip/simple-rtmp-server/issues/194 * iovs cache for multiple messages for each connections. -* each iovc is 16bytes, sizeof(iovec)=16, suppose the chunk size is 64k, -* each message send in a chunk which needs only 2 iovec, -* so the iovs max should be (SYS_CONSTS_MAX_PLAY_SEND_MSGS * 16 * 2) +* suppose the chunk size is 64k, each message send in a chunk which needs only 2 iovec, +* so the iovs max should be (SRS_PERF_MW_MSGS * 2) * * @remark, SRS will realloc when the iovs not enough. */ -#define SRS_CONSTS_IOVS_MAX (SYS_CONSTS_MAX_PLAY_SEND_MSGS * 32) +#define SRS_CONSTS_IOVS_MAX (SRS_PERF_MW_MSGS * 2) /** * for performance issue, * the c0c3 cache, @see https://github.com/winlinvip/simple-rtmp-server/issues/194 * c0c3 cache for multiple messages for each connections. * each c0 <= 16byes, suppose the chunk size is 64k, * each message send in a chunk which needs only a c0 header, -* so the c0c3 cache should be (SYS_CONSTS_MAX_PLAY_SEND_MSGS * 16) +* so the c0c3 cache should be (SRS_PERF_MW_MSGS * 16) * * @remark, SRS will try another loop when c0c3 cache dry, for we cannot realloc it. -* so we use larger c0c3 cache, that is (SYS_CONSTS_MAX_PLAY_SEND_MSGS * 32) +* so we use larger c0c3 cache, that is (SRS_PERF_MW_MSGS * 32) */ -#define SRS_CONSTS_C0C3_HEADERS_MAX (SYS_CONSTS_MAX_PLAY_SEND_MSGS * 32) +#define SRS_CONSTS_C0C3_HEADERS_MAX (SRS_PERF_MW_MSGS * 32) /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp index 058793c456..bc84046fe9 100644 --- a/trunk/src/rtmp/srs_protocol_stack.cpp +++ b/trunk/src/rtmp/srs_protocol_stack.cpp @@ -645,7 +645,7 @@ int SrsProtocol::do_send_messages(SrsMessage** msgs, int nb_msgs) // we donot use the complex basic header, // ensure the basic header is 1bytes. if (msg->header.perfer_cid < 2) { - srs_warn("change the chunk_id=%d to default=%d", + srs_info("change the chunk_id=%d to default=%d", msg->header.perfer_cid, RTMP_CID_ProtocolControl); msg->header.perfer_cid = RTMP_CID_ProtocolControl; } @@ -682,6 +682,10 @@ int SrsProtocol::do_send_messages(SrsMessage** msgs, int nb_msgs) // for we donot know how many messges maybe to send entirely, // we just alloc the iovs, it's ok. if (iov_index >= nb_out_iovs - 2) { + srs_warn("resize iovs %d => %d, max_msgs=%d", + nb_out_iovs, nb_out_iovs + SRS_CONSTS_IOVS_MAX, + SRS_PERF_MW_MSGS); + nb_out_iovs += SRS_CONSTS_IOVS_MAX; int realloc_size = sizeof(iovec) * nb_out_iovs; out_iovs = (iovec*)realloc(out_iovs, realloc_size); @@ -732,6 +736,8 @@ int SrsProtocol::do_send_messages(SrsMessage** msgs, int nb_msgs) if (iov_index <= 0) { return ret; } + srs_info("mw %d msgs in %d iovs, max_msgs=%d, nb_out_iovs=%d", + nb_msgs, iov_index, SRS_PERF_MW_MSGS, nb_out_iovs); // send by writev // sendout header and payload by writev. From 4c1d5c0d1e8d93679e5c042579810fe66c109976 Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 5 Dec 2014 11:31:06 +0800 Subject: [PATCH 324/800] refine the log for mw and mr. 2.0.55 --- trunk/src/app/srs_app_recv_thread.cpp | 14 +++++++++----- trunk/src/app/srs_app_rtmp_conn.cpp | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index 6ee404a023..5ff6de5280 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -433,17 +433,21 @@ void SrsPublishRecvThread::set_socket_buffer(int sleep_ms) // 2000*5000/8=1250000B(about 1220KB). int kbps = 5000; int socket_buffer_size = sleep_ms * kbps / 8; + + int fd = mr_fd; + int onb_rbuf = 0; + socklen_t sock_buf_size = sizeof(int); + getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &onb_rbuf, &sock_buf_size); // socket recv buffer, system will double it. int nb_rbuf = socket_buffer_size / 2; - socklen_t sock_buf_size = sizeof(int); - if (setsockopt(mr_fd, SOL_SOCKET, SO_RCVBUF, &nb_rbuf, sock_buf_size) < 0) { + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &nb_rbuf, sock_buf_size) < 0) { srs_warn("set sock SO_RCVBUF=%d failed.", nb_rbuf); } - getsockopt(mr_fd, SOL_SOCKET, SO_RCVBUF, &nb_rbuf, &sock_buf_size); + getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &nb_rbuf, &sock_buf_size); - srs_trace("merged read sockbuf=%d, actual=%d, sleep %d when nread<=%d", - socket_buffer_size, nb_rbuf, sleep_ms, SRS_MR_SMALL_BYTES); + srs_trace("mr change sleep %d=>%d, erbuf=%d, rbuf %d=>%d, sbytes=%d", + mr_sleep, sleep_ms, socket_buffer_size, onb_rbuf, nb_rbuf, SRS_MR_SMALL_BYTES); rtmp->set_recv_buffer(nb_rbuf); } diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index aa5a86942a..347f8d7a26 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -1023,7 +1023,7 @@ void SrsRtmpConn::change_mw_sleep(int sleep_ms) } getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &nb_sbuf, &sock_buf_size); - srs_trace("mw changed %d=>%d, max_msgs=%d, esbuf=%d, sbuf %d=>%d", + srs_trace("mw change sleep %d=>%d, max_msgs=%d, esbuf=%d, sbuf %d=>%d", mw_sleep, sleep_ms, SRS_PERF_MW_MSGS, socket_buffer_size, onb_sbuf, nb_sbuf); From dde05c63155b705e6337e9a633886432cec32f2c Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 5 Dec 2014 14:38:43 +0800 Subject: [PATCH 325/800] for bug #251, refine the send use cond wait. --- trunk/src/app/srs_app_rtmp_conn.cpp | 13 +++++---- trunk/src/app/srs_app_source.cpp | 38 +++++++++++++++++++++++++ trunk/src/app/srs_app_source.hpp | 20 +++++++++++++ trunk/src/core/srs_core.hpp | 2 +- trunk/src/core/srs_core_performance.hpp | 5 ++++ 5 files changed, 71 insertions(+), 7 deletions(-) diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index 347f8d7a26..0de7e9ba6f 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -598,6 +598,10 @@ int SrsRtmpConn::do_playing(SrsSource* source, SrsQueueRecvThread* trd) // collect elapse for pithy print. pithy_print.elapse(); + // wait for message to incoming. + // @see https://github.com/winlinvip/simple-rtmp-server/issues/251 + consumer->wait(SRS_PERF_MW_MIN_MSGS, mw_sleep); + // get messages from consumer. // each msg in msgs.msgs must be free, for the SrsMessageArray never free them. int count = 0; @@ -606,12 +610,9 @@ int SrsRtmpConn::do_playing(SrsSource* source, SrsQueueRecvThread* trd) return ret; } - // no message to send, sleep a while. - if (count <= 0) { - srs_verbose("sleep for no messages to send"); - st_usleep(mw_sleep * 1000); - } - srs_info("got %d msgs, mw=%d", count, mw_sleep); + // we use wait to get messages, so the count must be positive. + srs_assert(count > 0); + srs_info("got %d msgs, min=%d, mw=%d", count, SRS_PERF_MW_MIN_MSGS, mw_sleep); // reportable if (pithy_print.can_print()) { diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index baa3e50192..595dfbe2e7 100644 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -166,6 +166,16 @@ SrsMessageQueue::~SrsMessageQueue() clear(); } +int SrsMessageQueue::count() +{ + return (int)msgs.size(); +} + +int SrsMessageQueue::duration() +{ + return (int)(av_end_time - av_start_time); +} + void SrsMessageQueue::set_queue_size(double queue_size) { queue_size_ms = (int)(queue_size * 1000); @@ -290,6 +300,11 @@ SrsConsumer::SrsConsumer(SrsSource* _source) jitter = new SrsRtmpJitter(); queue = new SrsMessageQueue(); should_update_source_id = false; + + mw_wait = st_cond_new(); + mw_min_msgs = 0; + mw_duration = 0; + mw_waiting = false; } SrsConsumer::~SrsConsumer() @@ -297,6 +312,7 @@ SrsConsumer::~SrsConsumer() source->on_consumer_destroy(this); srs_freep(jitter); srs_freep(queue); + st_cond_destroy(mw_wait); } void SrsConsumer::set_queue_size(double queue_size) @@ -329,6 +345,12 @@ int SrsConsumer::enqueue(SrsSharedPtrMessage* msg, bool atc, int tba, int tbv, S return ret; } + // fire the mw when msgs is enough. + if (mw_waiting && queue->count() > mw_min_msgs && queue->duration() > mw_duration) { + st_cond_signal(mw_wait); + mw_waiting = false; + } + return ret; } @@ -349,6 +371,22 @@ int SrsConsumer::dump_packets(int max_count, SrsMessage** pmsgs, int& count) return queue->dump_packets(max_count, pmsgs, count); } +void SrsConsumer::wait(int nb_msgs, int duration) +{ + mw_min_msgs = nb_msgs; + mw_duration = duration; + + // already ok, donot wait. + if (queue->count() > mw_min_msgs && queue->duration() > mw_duration) { + return; + } + + // the enqueue will notify this cond. + mw_waiting = true; + + st_cond_wait(mw_wait); +} + int SrsConsumer::on_play_client_pause(bool is_pause) { int ret = ERROR_SUCCESS; diff --git a/trunk/src/app/srs_app_source.hpp b/trunk/src/app/srs_app_source.hpp index 2ac9b7f9e6..63795b66c0 100644 --- a/trunk/src/app/srs_app_source.hpp +++ b/trunk/src/app/srs_app_source.hpp @@ -115,6 +115,14 @@ class SrsMessageQueue SrsMessageQueue(); virtual ~SrsMessageQueue(); public: + /** + * get the count of queue. + */ + virtual int count(); + /** + * get duration of queue. + */ + virtual int duration(); /** * set the queue size * @param queue_size the queue size in seconds. @@ -154,6 +162,12 @@ class SrsConsumer bool paused; // when source id changed, notice all consumers bool should_update_source_id; + // the cond wait for mw. + // @see https://github.com/winlinvip/simple-rtmp-server/issues/251 + st_cond_t mw_wait; + bool mw_waiting; + int mw_min_msgs; + int mw_duration; public: SrsConsumer(SrsSource* _source); virtual ~SrsConsumer(); @@ -189,6 +203,12 @@ class SrsConsumer */ virtual int dump_packets(int max_count, SrsMessage** pmsgs, int& count); /** + * wait for messages incomming, atleast nb_msgs and in duration. + * @param nb_msgs the messages count to wait. + * @param duration the messgae duration to wait. + */ + virtual void wait(int nb_msgs, int duration); + /** * when client send the pause message. */ virtual int on_play_client_pause(bool is_pause); diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 13a24da78b..7ac574fc2c 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 55 +#define VERSION_REVISION 56 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/core/srs_core_performance.hpp b/trunk/src/core/srs_core_performance.hpp index e54f4ef0f4..97c5191ba6 100644 --- a/trunk/src/core/srs_core_performance.hpp +++ b/trunk/src/core/srs_core_performance.hpp @@ -93,6 +93,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * @remark, recomment to 156. */ #define SRS_PERF_MW_MSGS 156 +/** +* how many msgs atleast to send. +* @remark, recomment to 8. +*/ +#define SRS_PERF_MW_MIN_MSGS 8 /** * how many chunk stream to cache, [0, N]. From 9ee138746f83adc26f0e236ec017f4d68a300004 Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 5 Dec 2014 16:44:11 +0800 Subject: [PATCH 326/800] for bug #251, 9k+ clients, use fast cache for msgs queue. 2.0.57 --- README.md | 1 + trunk/src/app/srs_app_rtmp_conn.cpp | 2 +- trunk/src/app/srs_app_source.cpp | 123 ++++++++++++++++++---- trunk/src/app/srs_app_source.hpp | 32 +++--- trunk/src/core/srs_core.hpp | 2 +- trunk/src/core/srs_core_performance.hpp | 26 +++-- trunk/src/rtmp/srs_protocol_msg_array.cpp | 24 ++++- trunk/src/rtmp/srs_protocol_msg_array.hpp | 10 ++ 8 files changed, 171 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index 9681f71508..41aae22caf 100755 --- a/README.md +++ b/README.md @@ -485,6 +485,7 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History +* v2.0, 2014-12-05, fix [#251](https://github.com/winlinvip/simple-rtmp-server/issues/251), 9k+ clients, use fast cache for msgs queue. 2.0.57 * v2.0, 2014-12-04, fix [#241](https://github.com/winlinvip/simple-rtmp-server/issues/241), add mw(merged-write) config. 2.0.53 * v2.0, 2014-12-04, for [#241](https://github.com/winlinvip/simple-rtmp-server/issues/241), support mr(merged-read) config and reload. 2.0.52. * v2.0, 2014-12-04, enable [#241](https://github.com/winlinvip/simple-rtmp-server/issues/241) and [#248](https://github.com/winlinvip/simple-rtmp-server/issues/248), +25% performance, 2.5k publisher. 2.0.50 diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index 0de7e9ba6f..b2f5199a65 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -605,7 +605,7 @@ int SrsRtmpConn::do_playing(SrsSource* source, SrsQueueRecvThread* trd) // get messages from consumer. // each msg in msgs.msgs must be free, for the SrsMessageArray never free them. int count = 0; - if ((ret = consumer->dump_packets(msgs.max, msgs.msgs, count)) != ERROR_SUCCESS) { + if ((ret = consumer->dump_packets(&msgs, &count)) != ERROR_SUCCESS) { srs_error("get messages from consumer failed. ret=%d", ret); return ret; } diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index 595dfbe2e7..228cb0d327 100644 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -41,6 +41,7 @@ using namespace std; #include #include #include +#include #define CONST_MAX_JITTER_MS 500 #define DEFAULT_FRAME_TIME_MS 40 @@ -166,22 +167,12 @@ SrsMessageQueue::~SrsMessageQueue() clear(); } -int SrsMessageQueue::count() -{ - return (int)msgs.size(); -} - -int SrsMessageQueue::duration() -{ - return (int)(av_end_time - av_start_time); -} - void SrsMessageQueue::set_queue_size(double queue_size) { queue_size_ms = (int)(queue_size * 1000); } -int SrsMessageQueue::enqueue(SrsSharedPtrMessage* msg) +int SrsMessageQueue::enqueue(SrsSharedPtrMessage* msg, bool* is_overflow) { int ret = ERROR_SUCCESS; @@ -196,6 +187,11 @@ int SrsMessageQueue::enqueue(SrsSharedPtrMessage* msg) msgs.push_back(msg); while (av_end_time - av_start_time > queue_size_ms) { + // notice the caller queue already overflow and shrinked. + if (is_overflow) { + *is_overflow = true; + } + shrink(); } @@ -305,10 +301,20 @@ SrsConsumer::SrsConsumer(SrsSource* _source) mw_min_msgs = 0; mw_duration = 0; mw_waiting = false; + + mw_cache = new SrsMessageArray(SRS_PERF_MW_MSGS); + mw_count = 0; + mw_first_pkt = mw_last_pkt = 0; } SrsConsumer::~SrsConsumer() { + if (mw_cache) { + mw_cache->free(mw_count); + mw_count = 0; + } + srs_freep(mw_cache); + source->on_consumer_destroy(this); srs_freep(jitter); srs_freep(queue); @@ -341,22 +347,53 @@ int SrsConsumer::enqueue(SrsSharedPtrMessage* msg, bool atc, int tba, int tbv, S } } - if ((ret = queue->enqueue(msg)) != ERROR_SUCCESS) { - return ret; + // use fast cache if available + if (mw_count < mw_cache->max) { + // update fast cache timestamps + if (mw_count == 0) { + mw_first_pkt = msg->header.timestamp; + } + mw_last_pkt = msg->header.timestamp; + + mw_cache->msgs[mw_count++] = msg; + } else{ + // fast cache is full, use queue. + bool is_overflow = false; + if ((ret = queue->enqueue(msg, &is_overflow)) != ERROR_SUCCESS) { + return ret; + } + // when overflow, clear cache and refresh the fast cache. + if (is_overflow) { + mw_cache->free(mw_count); + if ((ret = dumps_queue_to_fast_cache()) != ERROR_SUCCESS) { + return ret; + } + } } // fire the mw when msgs is enough. - if (mw_waiting && queue->count() > mw_min_msgs && queue->duration() > mw_duration) { - st_cond_signal(mw_wait); - mw_waiting = false; + if (mw_waiting) { + // when fast cache not overflow, always flush. + // so we donot care about the queue. + bool fast_cache_overflow = mw_count >= mw_cache->max; + int duration_ms = (int)(mw_last_pkt - mw_first_pkt); + bool match_min_msgs = mw_count > mw_min_msgs; + + // when fast cache overflow, or duration ok, signal to flush. + if (fast_cache_overflow || (match_min_msgs && duration_ms > mw_duration)) { + st_cond_signal(mw_wait); + mw_waiting = false; + } } return ret; } -int SrsConsumer::dump_packets(int max_count, SrsMessage** pmsgs, int& count) +int SrsConsumer::dump_packets(SrsMessageArray* msgs, int* count) { - srs_assert(max_count > 0); + int ret =ERROR_SUCCESS; + + srs_assert(msgs->max > 0); if (should_update_source_id) { srs_trace("update source_id=%d[%d]", source->source_id(), source->source_id()); @@ -365,10 +402,24 @@ int SrsConsumer::dump_packets(int max_count, SrsMessage** pmsgs, int& count) // paused, return nothing. if (paused) { - return ERROR_SUCCESS; + return ret; + } + + // only dumps an whole array to msgs. + for (int i = 0; i < mw_count; i++) { + msgs->msgs[i] = mw_cache->msgs[i]; } + *count = mw_count; - return queue->dump_packets(max_count, pmsgs, count); + // when fast cache is not filled, + // we donot check the queue, direclty zero fast cache. + if (mw_count < mw_cache->max) { + mw_count = 0; + mw_first_pkt = mw_last_pkt = 0; + return ret; + } + + return dumps_queue_to_fast_cache(); } void SrsConsumer::wait(int nb_msgs, int duration) @@ -376,14 +427,20 @@ void SrsConsumer::wait(int nb_msgs, int duration) mw_min_msgs = nb_msgs; mw_duration = duration; - // already ok, donot wait. - if (queue->count() > mw_min_msgs && queue->duration() > mw_duration) { + // when fast cache not overflow, always flush. + // so we donot care about the queue. + bool fast_cache_overflow = mw_count >= mw_cache->max; + int duration_ms = (int)(mw_last_pkt - mw_first_pkt); + bool match_min_msgs = mw_count > mw_min_msgs; + + // when fast cache overflow, or duration ok, signal to flush. + if (fast_cache_overflow || (match_min_msgs && duration_ms > mw_duration)) { return; } // the enqueue will notify this cond. mw_waiting = true; - + // wait for msgs to incoming. st_cond_wait(mw_wait); } @@ -397,6 +454,26 @@ int SrsConsumer::on_play_client_pause(bool is_pause) return ret; } +int SrsConsumer::dumps_queue_to_fast_cache() +{ + int ret =ERROR_SUCCESS; + + // fill fast cache with queue. + if ((ret = queue->dump_packets(mw_cache->max, mw_cache->msgs, mw_count)) != ERROR_SUCCESS) { + return ret; + } + // set the timestamp when got message. + if (mw_count > 0) { + SrsMessage* first_msg = mw_cache->msgs[0]; + mw_first_pkt = first_msg->header.timestamp; + + SrsMessage* last_msg = mw_cache->msgs[mw_count - 1]; + mw_last_pkt = last_msg->header.timestamp; + } + + return ret; +} + SrsGopCache::SrsGopCache() { cached_video_count = 0; diff --git a/trunk/src/app/srs_app_source.hpp b/trunk/src/app/srs_app_source.hpp index 63795b66c0..b2f93eeeca 100644 --- a/trunk/src/app/srs_app_source.hpp +++ b/trunk/src/app/srs_app_source.hpp @@ -48,6 +48,7 @@ class SrsRequest; class SrsStSocket; class SrsRtmpServer; class SrsEdgeProxyContext; +class SrsMessageArray; #ifdef SRS_AUTO_HLS class SrsHls; #endif @@ -115,14 +116,6 @@ class SrsMessageQueue SrsMessageQueue(); virtual ~SrsMessageQueue(); public: - /** - * get the count of queue. - */ - virtual int count(); - /** - * get duration of queue. - */ - virtual int duration(); /** * set the queue size * @param queue_size the queue size in seconds. @@ -132,8 +125,9 @@ class SrsMessageQueue /** * 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 int enqueue(SrsSharedPtrMessage* msg); + virtual int enqueue(SrsSharedPtrMessage* msg, bool* is_overflow = NULL); /** * get packets in consumer queue. * @pmsgs SrsMessages*[], used to store the msgs, user must alloc it. @@ -168,6 +162,14 @@ class SrsConsumer bool mw_waiting; int mw_min_msgs; int mw_duration; + // use fast cache for msgs + // @see https://github.com/winlinvip/simple-rtmp-server/issues/251 + SrsMessageArray* mw_cache; + // the count of msg in fast cache. + int mw_count; + // the packet time in fast cache. + int64_t mw_first_pkt; + int64_t mw_last_pkt; public: SrsConsumer(SrsSource* _source); virtual ~SrsConsumer(); @@ -197,11 +199,11 @@ class SrsConsumer virtual int enqueue(SrsSharedPtrMessage* msg, bool atc, int tba, int tbv, SrsRtmpJitterAlgorithm ag); /** * get packets in consumer queue. - * @pmsgs SrsMessages*[], used to store the msgs, user must alloc it. - * @count the count in array, output param. + * @param msgs the msgs array to dump packets to send. + * @param count the count in array, output param. * @max_count the max count to dequeue, must be positive. */ - virtual int dump_packets(int max_count, SrsMessage** pmsgs, int& count); + virtual int dump_packets(SrsMessageArray* msgs, int* count); /** * wait for messages incomming, atleast nb_msgs and in duration. * @param nb_msgs the messages count to wait. @@ -212,6 +214,12 @@ class SrsConsumer * when client send the pause message. */ virtual int on_play_client_pause(bool is_pause); +private: + /** + * dumps the queue to fast cache, + * when fast cache is clear or queue is overflow. + */ + virtual int dumps_queue_to_fast_cache(); }; /** diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 7ac574fc2c..3b039b76cc 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 56 +#define VERSION_REVISION 57 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/core/srs_core_performance.hpp b/trunk/src/core/srs_core_performance.hpp index 97c5191ba6..6b1f49cfdb 100644 --- a/trunk/src/core/srs_core_performance.hpp +++ b/trunk/src/core/srs_core_performance.hpp @@ -75,14 +75,24 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * @see SrsConfig::get_mw_sleep_ms() * @remark the mw sleep and msgs to send, maybe: * mw_sleep msgs iovs -* 350 24/48 48/84 -* 500 24/48 48/84 -* 800 42/64 84/128 -* 1000 64/85 128/170 -* 1200 65/86 130/172 -* 1500 87/110 174/220 -* 1800 106/128 212/256 -* 2000 134/142 268/284 +* 350 43 86 +* 400 44 88 +* 500 46 92 +* 600 46 92 +* 700 82 164 +* 800 81 162 +* 900 80 160 +* 1000 88 176 +* 1100 91 182 +* 1200 89 178 +* 1300 119 238 +* 1400 120 240 +* 1500 119 238 +* 1600 131 262 +* 1700 +* 1800 +* 1900 +* 2000 */ // the default config of mw. #define SRS_PERF_MW_SLEEP 350 diff --git a/trunk/src/rtmp/srs_protocol_msg_array.cpp b/trunk/src/rtmp/srs_protocol_msg_array.cpp index 4c002bcf84..699603be4e 100644 --- a/trunk/src/rtmp/srs_protocol_msg_array.cpp +++ b/trunk/src/rtmp/srs_protocol_msg_array.cpp @@ -32,10 +32,7 @@ SrsMessageArray::SrsMessageArray(int max_msgs) msgs = new SrsMessage*[max_msgs]; max = max_msgs; - // initialize - for (int i = 0; i < max_msgs; i++) { - msgs[i] = NULL; - } + zero(max_msgs); } SrsMessageArray::~SrsMessageArray() @@ -46,4 +43,23 @@ SrsMessageArray::~SrsMessageArray() srs_freep(msgs); } +void SrsMessageArray::free(int count) +{ + // initialize + for (int i = 0; i < count; i++) { + SrsMessage* msg = msgs[i]; + srs_freep(msg); + + msgs[i] = NULL; + } +} + +void SrsMessageArray::zero(int count) +{ + // initialize + for (int i = 0; i < count; i++) { + msgs[i] = NULL; + } +} + diff --git a/trunk/src/rtmp/srs_protocol_msg_array.hpp b/trunk/src/rtmp/srs_protocol_msg_array.hpp index c7a9cce65a..aff10def7b 100644 --- a/trunk/src/rtmp/srs_protocol_msg_array.hpp +++ b/trunk/src/rtmp/srs_protocol_msg_array.hpp @@ -60,6 +60,16 @@ class SrsMessageArray * free the msgs not sent out(not NULL). */ virtual ~SrsMessageArray(); +public: + /** + * free specified count of messages. + */ + virtual void free(int count); +private: + /** + * zero initialize the message array. + */ + virtual void zero(int count); }; #endif From cd317859db877fd3e4afee8adc8aa6db9cec34ae Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 5 Dec 2014 17:07:27 +0800 Subject: [PATCH 327/800] add mw sleep time and msgs/iovs table. --- README.md | 5 +++-- trunk/src/core/srs_core_performance.hpp | 18 +++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 41aae22caf..f8520e2bdf 100755 --- a/README.md +++ b/README.md @@ -727,7 +727,8 @@ The play benchmark by [st-load](https://github.com/winlinvip/st-load): * 2014-11-12, SRS 2.0.14, 3.5k(3500)clients, 95%CPU, 78MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/8acd143a7a152885b815999162660fd4e7a3f247) * 2014-11-13, SRS 2.0.15, 6.0k(6000)clients, 82%CPU, 203MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/cc6aca9ad55342a06440ce7f3b38453776b2b2d1) * 2014-11-22, SRS 2.0.30, 7.5k(7500)clients, 87%CPU, 320MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/58136ec178e3d47db6c90a59875d7e40946936e5) -* 2014-12-05, SRS 2.0.55, 8.0k(8000)clients, 89%CPU, 360MB. (mw_sleep=1800)[commit](https://github.com/winlinvip/simple-rtmp-server/commit/58136ec178e3d47db6c90a59875d7e40946936e5) +* 2014-12-05, SRS 2.0.55, 8.0k(8000)clients, 89%CPU, 360MB. (mw_sleep=350)[commit](https://github.com/winlinvip/simple-rtmp-server/commit/58136ec178e3d47db6c90a59875d7e40946936e5) +* 2014-12-05, SRS 2.0.57, 9.0k(9000)clients, 92%CPU, 410MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/9ee138746f83adc26f0e236ec017f4d68a300004) ### Publish benchmark @@ -741,7 +742,7 @@ The publish benchmark by [st-load](https://github.com/winlinvip/st-load): * 2014-12-04, SRS 2.0.49, 1.4k(1400) publishers, 68%CPU, 144MB. * 2014-12-04, SRS 2.0.49, 2.5k(2500) publishers, 95%CPU, 404MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/29324fab469e0f7cef9ad04ffdbce832ac7dd9ff) * 2014-12-04, SRS 2.0.51, 2.5k(2500) publishers, 91%CPU, 259MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/f57801eb46c16755b173984b915a4166922df6a6) -* 2014-12-04, SRS 2.0.52, 4.0k(4000) publishers, 80%CPU, 331MB. (mr_sleep=2000)[commit](https://github.com/winlinvip/simple-rtmp-server/commit/5589b13d2e216b91f97afb78ee0c011b2fccf7da) +* 2014-12-04, SRS 2.0.52, 4.0k(4000) publishers, 80%CPU, 331MB. (mr_sleep=350)[commit](https://github.com/winlinvip/simple-rtmp-server/commit/5589b13d2e216b91f97afb78ee0c011b2fccf7da) ## Architecture diff --git a/trunk/src/core/srs_core_performance.hpp b/trunk/src/core/srs_core_performance.hpp index 6b1f49cfdb..d7ba665f37 100644 --- a/trunk/src/core/srs_core_performance.hpp +++ b/trunk/src/core/srs_core_performance.hpp @@ -89,25 +89,25 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 1400 120 240 * 1500 119 238 * 1600 131 262 -* 1700 -* 1800 -* 1900 -* 2000 +* 1700 131 262 +* 1800 133 266 +* 1900 141 282 +* 2000 150 300 */ // the default config of mw. #define SRS_PERF_MW_SLEEP 350 /** * how many msgs can be send entirely. * for play clients to get msgs then totally send out. -* for the mw sleep set to 1800, the msgs is about 128. -* @remark, recomment to 156. +* for the mw sleep set to 1800, the msgs is about 133. +* @remark, recomment to 256. */ -#define SRS_PERF_MW_MSGS 156 +#define SRS_PERF_MW_MSGS 256 /** * how many msgs atleast to send. -* @remark, recomment to 8. +* @remark, recomment to 32. */ -#define SRS_PERF_MW_MIN_MSGS 8 +#define SRS_PERF_MW_MIN_MSGS 32 /** * how many chunk stream to cache, [0, N]. From cdb1984ba466a629fb63770338d8d2cef1848e46 Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 5 Dec 2014 17:24:40 +0800 Subject: [PATCH 328/800] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f8520e2bdf..0841381a6d 100755 --- a/README.md +++ b/README.md @@ -728,7 +728,7 @@ The play benchmark by [st-load](https://github.com/winlinvip/st-load): * 2014-11-13, SRS 2.0.15, 6.0k(6000)clients, 82%CPU, 203MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/cc6aca9ad55342a06440ce7f3b38453776b2b2d1) * 2014-11-22, SRS 2.0.30, 7.5k(7500)clients, 87%CPU, 320MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/58136ec178e3d47db6c90a59875d7e40946936e5) * 2014-12-05, SRS 2.0.55, 8.0k(8000)clients, 89%CPU, 360MB. (mw_sleep=350)[commit](https://github.com/winlinvip/simple-rtmp-server/commit/58136ec178e3d47db6c90a59875d7e40946936e5) -* 2014-12-05, SRS 2.0.57, 9.0k(9000)clients, 92%CPU, 410MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/9ee138746f83adc26f0e236ec017f4d68a300004) +* 2014-12-05, SRS 2.0.57, 9.0k(9000)clients, 90%CPU, 468MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/9ee138746f83adc26f0e236ec017f4d68a300004) ### Publish benchmark From 92ecdf088b7cee6ae45ab57702165b0964a16d0e Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 5 Dec 2014 18:47:17 +0800 Subject: [PATCH 329/800] for bug #251, use macro to define the fast cache and cond wait. 2.0.58 --- trunk/src/app/srs_app_rtmp_conn.cpp | 8 +++ trunk/src/app/srs_app_source.cpp | 67 +++++++++++++++++++++++++ trunk/src/app/srs_app_source.hpp | 17 +++++++ trunk/src/core/srs_core.hpp | 2 +- trunk/src/core/srs_core_performance.hpp | 14 ++++++ 5 files changed, 107 insertions(+), 1 deletion(-) diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index b2f5199a65..0122c35a1e 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -598,9 +598,11 @@ int SrsRtmpConn::do_playing(SrsSource* source, SrsQueueRecvThread* trd) // collect elapse for pithy print. pithy_print.elapse(); +#ifdef SRS_PERF_QUEUE_COND_WAIT // wait for message to incoming. // @see https://github.com/winlinvip/simple-rtmp-server/issues/251 consumer->wait(SRS_PERF_MW_MIN_MSGS, mw_sleep); +#endif // get messages from consumer. // each msg in msgs.msgs must be free, for the SrsMessageArray never free them. @@ -610,8 +612,14 @@ int SrsRtmpConn::do_playing(SrsSource* source, SrsQueueRecvThread* trd) return ret; } +#ifdef SRS_PERF_QUEUE_COND_WAIT // we use wait to get messages, so the count must be positive. srs_assert(count > 0); +#else + if (count <= 0) { + st_usleep(mw_sleep * 1000); + } +#endif srs_info("got %d msgs, min=%d, mw=%d", count, SRS_PERF_MW_MIN_MSGS, mw_sleep); // reportable diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index 228cb0d327..77a107fda2 100644 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -167,6 +167,16 @@ SrsMessageQueue::~SrsMessageQueue() clear(); } +int SrsMessageQueue::size() +{ + return (int)msgs.size(); +} + +int SrsMessageQueue::duration() +{ + return (int)(av_end_time - av_start_time); +} + void SrsMessageQueue::set_queue_size(double queue_size) { queue_size_ms = (int)(queue_size * 1000); @@ -297,28 +307,37 @@ SrsConsumer::SrsConsumer(SrsSource* _source) queue = new SrsMessageQueue(); should_update_source_id = false; +#ifdef SRS_PERF_QUEUE_COND_WAIT mw_wait = st_cond_new(); mw_min_msgs = 0; mw_duration = 0; mw_waiting = false; +#endif +#ifdef SRS_PERF_QUEUE_FAST_CACHE mw_cache = new SrsMessageArray(SRS_PERF_MW_MSGS); mw_count = 0; mw_first_pkt = mw_last_pkt = 0; +#endif } SrsConsumer::~SrsConsumer() { +#ifdef SRS_PERF_QUEUE_FAST_CACHE if (mw_cache) { mw_cache->free(mw_count); mw_count = 0; } srs_freep(mw_cache); +#endif source->on_consumer_destroy(this); srs_freep(jitter); srs_freep(queue); + +#ifdef SRS_PERF_QUEUE_COND_WAIT st_cond_destroy(mw_wait); +#endif } void SrsConsumer::set_queue_size(double queue_size) @@ -347,6 +366,7 @@ int SrsConsumer::enqueue(SrsSharedPtrMessage* msg, bool atc, int tba, int tbv, S } } +#ifdef SRS_PERF_QUEUE_FAST_CACHE // use fast cache if available if (mw_count < mw_cache->max) { // update fast cache timestamps @@ -371,6 +391,7 @@ int SrsConsumer::enqueue(SrsSharedPtrMessage* msg, bool atc, int tba, int tbv, S } } + #ifdef SRS_PERF_QUEUE_COND_WAIT // fire the mw when msgs is enough. if (mw_waiting) { // when fast cache not overflow, always flush. @@ -385,6 +406,26 @@ int SrsConsumer::enqueue(SrsSharedPtrMessage* msg, bool atc, int tba, int tbv, S mw_waiting = false; } } + #endif +#else + if ((ret = queue->enqueue(msg, NULL)) != ERROR_SUCCESS) { + return ret; + } + + #ifdef SRS_PERF_QUEUE_COND_WAIT + // fire the mw when msgs is enough. + if (mw_waiting) { + int duration_ms = queue->duration(); + bool match_min_msgs = queue->size() > mw_min_msgs; + + // when duration ok, signal to flush. + if (match_min_msgs && duration_ms > mw_duration) { + st_cond_signal(mw_wait); + mw_waiting = false; + } + } + #endif +#endif return ret; } @@ -405,6 +446,7 @@ int SrsConsumer::dump_packets(SrsMessageArray* msgs, int* count) return ret; } +#ifdef SRS_PERF_QUEUE_FAST_CACHE // only dumps an whole array to msgs. for (int i = 0; i < mw_count; i++) { msgs->msgs[i] = mw_cache->msgs[i]; @@ -420,13 +462,26 @@ int SrsConsumer::dump_packets(SrsMessageArray* msgs, int* count) } return dumps_queue_to_fast_cache(); +#else + + // pump msgs from queue. + int nb_msgs = 0; + if ((ret = queue->dump_packets(msgs->max, msgs->msgs, nb_msgs)) != ERROR_SUCCESS) { + return ret; + } + *count = nb_msgs; + + return ret; +#endif } +#ifdef SRS_PERF_QUEUE_COND_WAIT void SrsConsumer::wait(int nb_msgs, int duration) { mw_min_msgs = nb_msgs; mw_duration = duration; +#ifdef SRS_PERF_QUEUE_FAST_CACHE // when fast cache not overflow, always flush. // so we donot care about the queue. bool fast_cache_overflow = mw_count >= mw_cache->max; @@ -437,12 +492,22 @@ void SrsConsumer::wait(int nb_msgs, int duration) if (fast_cache_overflow || (match_min_msgs && duration_ms > mw_duration)) { return; } +#else + int duration_ms = queue->duration(); + bool match_min_msgs = queue->size() > mw_min_msgs; + + // when duration ok, signal to flush. + if (match_min_msgs && duration_ms > mw_duration) { + return; + } +#endif // the enqueue will notify this cond. mw_waiting = true; // wait for msgs to incoming. st_cond_wait(mw_wait); } +#endif int SrsConsumer::on_play_client_pause(bool is_pause) { @@ -454,6 +519,7 @@ int SrsConsumer::on_play_client_pause(bool is_pause) return ret; } +#ifdef SRS_PERF_QUEUE_FAST_CACHE int SrsConsumer::dumps_queue_to_fast_cache() { int ret =ERROR_SUCCESS; @@ -473,6 +539,7 @@ int SrsConsumer::dumps_queue_to_fast_cache() return ret; } +#endif SrsGopCache::SrsGopCache() { diff --git a/trunk/src/app/srs_app_source.hpp b/trunk/src/app/srs_app_source.hpp index b2f93eeeca..fac678ed7c 100644 --- a/trunk/src/app/srs_app_source.hpp +++ b/trunk/src/app/srs_app_source.hpp @@ -36,6 +36,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +#include class SrsPlayEdge; class SrsPublishEdge; @@ -116,6 +117,14 @@ class SrsMessageQueue SrsMessageQueue(); virtual ~SrsMessageQueue(); public: + /** + * get the size of queue. + */ + virtual int size(); + /** + * get the duration of queue. + */ + virtual int duration(); /** * set the queue size * @param queue_size the queue size in seconds. @@ -156,12 +165,15 @@ class SrsConsumer bool paused; // when source id changed, notice all consumers bool should_update_source_id; +#ifdef SRS_PERF_QUEUE_COND_WAIT // the cond wait for mw. // @see https://github.com/winlinvip/simple-rtmp-server/issues/251 st_cond_t mw_wait; bool mw_waiting; int mw_min_msgs; int mw_duration; +#endif +#ifdef SRS_PERF_QUEUE_FAST_CACHE // use fast cache for msgs // @see https://github.com/winlinvip/simple-rtmp-server/issues/251 SrsMessageArray* mw_cache; @@ -170,6 +182,7 @@ class SrsConsumer // the packet time in fast cache. int64_t mw_first_pkt; int64_t mw_last_pkt; +#endif public: SrsConsumer(SrsSource* _source); virtual ~SrsConsumer(); @@ -204,22 +217,26 @@ class SrsConsumer * @max_count the max count to dequeue, must be positive. */ virtual int dump_packets(SrsMessageArray* msgs, int* count); +#ifdef SRS_PERF_QUEUE_COND_WAIT /** * wait for messages incomming, atleast nb_msgs and in duration. * @param nb_msgs the messages count to wait. * @param duration the messgae duration to wait. */ virtual void wait(int nb_msgs, int duration); +#endif /** * when client send the pause message. */ virtual int on_play_client_pause(bool is_pause); private: +#ifdef SRS_PERF_QUEUE_FAST_CACHE /** * dumps the queue to fast cache, * when fast cache is clear or queue is overflow. */ virtual int dumps_queue_to_fast_cache(); +#endif }; /** diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 3b039b76cc..aaa822fd60 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 57 +#define VERSION_REVISION 58 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/core/srs_core_performance.hpp b/trunk/src/core/srs_core_performance.hpp index d7ba665f37..2cfcab65af 100644 --- a/trunk/src/core/srs_core_performance.hpp +++ b/trunk/src/core/srs_core_performance.hpp @@ -109,6 +109,20 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define SRS_PERF_MW_MIN_MSGS 32 +/** +* whether enable the fast cache. +* @remark this improve performance for large connectios. +* @remark this also introduce complex, default to disable it. +* @see https://github.com/winlinvip/simple-rtmp-server/issues/251 +*/ +#undef SRS_PERF_QUEUE_FAST_CACHE +/** +* whether use cond wait to send messages. +* @remark this improve performance for large connectios. +* @see https://github.com/winlinvip/simple-rtmp-server/issues/251 +*/ +#undef SRS_PERF_QUEUE_COND_WAIT + /** * how many chunk stream to cache, [0, N]. * to imporove about 10% performance when chunk size small, and 5% for large chunk. From e80c8603d4e611c7ca88ceb12aa91e1ac7f7fc20 Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 5 Dec 2014 20:55:19 +0800 Subject: [PATCH 330/800] fix #251, revert changes, for the cond wait and fast cache queue is no use. 2.0.59 --- trunk/src/app/srs_app_rtmp_conn.cpp | 18 +-- trunk/src/app/srs_app_source.cpp | 165 +----------------------- trunk/src/app/srs_app_source.hpp | 36 +----- trunk/src/core/srs_core.hpp | 2 +- trunk/src/core/srs_core_performance.hpp | 23 +--- trunk/src/rtmp/srs_protocol_stack.cpp | 14 +- 6 files changed, 24 insertions(+), 234 deletions(-) diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index 0122c35a1e..263e1a3f2f 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -598,29 +598,19 @@ int SrsRtmpConn::do_playing(SrsSource* source, SrsQueueRecvThread* trd) // collect elapse for pithy print. pithy_print.elapse(); -#ifdef SRS_PERF_QUEUE_COND_WAIT - // wait for message to incoming. - // @see https://github.com/winlinvip/simple-rtmp-server/issues/251 - consumer->wait(SRS_PERF_MW_MIN_MSGS, mw_sleep); -#endif - // get messages from consumer. // each msg in msgs.msgs must be free, for the SrsMessageArray never free them. int count = 0; - if ((ret = consumer->dump_packets(&msgs, &count)) != ERROR_SUCCESS) { + if ((ret = consumer->dump_packets(&msgs, count)) != ERROR_SUCCESS) { srs_error("get messages from consumer failed. ret=%d", ret); return ret; } - -#ifdef SRS_PERF_QUEUE_COND_WAIT - // we use wait to get messages, so the count must be positive. - srs_assert(count > 0); -#else + + // no messages, sleep for a while. if (count <= 0) { st_usleep(mw_sleep * 1000); } -#endif - srs_info("got %d msgs, min=%d, mw=%d", count, SRS_PERF_MW_MIN_MSGS, mw_sleep); + srs_info("got %d msgs, mw=%d", count, mw_sleep); // reportable if (pithy_print.can_print()) { diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index 77a107fda2..d2a3dcce07 100644 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -306,38 +306,13 @@ SrsConsumer::SrsConsumer(SrsSource* _source) jitter = new SrsRtmpJitter(); queue = new SrsMessageQueue(); should_update_source_id = false; - -#ifdef SRS_PERF_QUEUE_COND_WAIT - mw_wait = st_cond_new(); - mw_min_msgs = 0; - mw_duration = 0; - mw_waiting = false; -#endif - -#ifdef SRS_PERF_QUEUE_FAST_CACHE - mw_cache = new SrsMessageArray(SRS_PERF_MW_MSGS); - mw_count = 0; - mw_first_pkt = mw_last_pkt = 0; -#endif } SrsConsumer::~SrsConsumer() { -#ifdef SRS_PERF_QUEUE_FAST_CACHE - if (mw_cache) { - mw_cache->free(mw_count); - mw_count = 0; - } - srs_freep(mw_cache); -#endif - source->on_consumer_destroy(this); srs_freep(jitter); srs_freep(queue); - -#ifdef SRS_PERF_QUEUE_COND_WAIT - st_cond_destroy(mw_wait); -#endif } void SrsConsumer::set_queue_size(double queue_size) @@ -365,72 +340,15 @@ int SrsConsumer::enqueue(SrsSharedPtrMessage* msg, bool atc, int tba, int tbv, S return ret; } } - -#ifdef SRS_PERF_QUEUE_FAST_CACHE - // use fast cache if available - if (mw_count < mw_cache->max) { - // update fast cache timestamps - if (mw_count == 0) { - mw_first_pkt = msg->header.timestamp; - } - mw_last_pkt = msg->header.timestamp; - - mw_cache->msgs[mw_count++] = msg; - } else{ - // fast cache is full, use queue. - bool is_overflow = false; - if ((ret = queue->enqueue(msg, &is_overflow)) != ERROR_SUCCESS) { - return ret; - } - // when overflow, clear cache and refresh the fast cache. - if (is_overflow) { - mw_cache->free(mw_count); - if ((ret = dumps_queue_to_fast_cache()) != ERROR_SUCCESS) { - return ret; - } - } - } - - #ifdef SRS_PERF_QUEUE_COND_WAIT - // fire the mw when msgs is enough. - if (mw_waiting) { - // when fast cache not overflow, always flush. - // so we donot care about the queue. - bool fast_cache_overflow = mw_count >= mw_cache->max; - int duration_ms = (int)(mw_last_pkt - mw_first_pkt); - bool match_min_msgs = mw_count > mw_min_msgs; - - // when fast cache overflow, or duration ok, signal to flush. - if (fast_cache_overflow || (match_min_msgs && duration_ms > mw_duration)) { - st_cond_signal(mw_wait); - mw_waiting = false; - } - } - #endif -#else + if ((ret = queue->enqueue(msg, NULL)) != ERROR_SUCCESS) { return ret; } - #ifdef SRS_PERF_QUEUE_COND_WAIT - // fire the mw when msgs is enough. - if (mw_waiting) { - int duration_ms = queue->duration(); - bool match_min_msgs = queue->size() > mw_min_msgs; - - // when duration ok, signal to flush. - if (match_min_msgs && duration_ms > mw_duration) { - st_cond_signal(mw_wait); - mw_waiting = false; - } - } - #endif -#endif - return ret; } -int SrsConsumer::dump_packets(SrsMessageArray* msgs, int* count) +int SrsConsumer::dump_packets(SrsMessageArray* msgs, int& count) { int ret =ERROR_SUCCESS; @@ -446,68 +364,13 @@ int SrsConsumer::dump_packets(SrsMessageArray* msgs, int* count) return ret; } -#ifdef SRS_PERF_QUEUE_FAST_CACHE - // only dumps an whole array to msgs. - for (int i = 0; i < mw_count; i++) { - msgs->msgs[i] = mw_cache->msgs[i]; - } - *count = mw_count; - - // when fast cache is not filled, - // we donot check the queue, direclty zero fast cache. - if (mw_count < mw_cache->max) { - mw_count = 0; - mw_first_pkt = mw_last_pkt = 0; - return ret; - } - - return dumps_queue_to_fast_cache(); -#else - // pump msgs from queue. - int nb_msgs = 0; - if ((ret = queue->dump_packets(msgs->max, msgs->msgs, nb_msgs)) != ERROR_SUCCESS) { + if ((ret = queue->dump_packets(msgs->max, msgs->msgs, count)) != ERROR_SUCCESS) { return ret; } - *count = nb_msgs; return ret; -#endif -} - -#ifdef SRS_PERF_QUEUE_COND_WAIT -void SrsConsumer::wait(int nb_msgs, int duration) -{ - mw_min_msgs = nb_msgs; - mw_duration = duration; - -#ifdef SRS_PERF_QUEUE_FAST_CACHE - // when fast cache not overflow, always flush. - // so we donot care about the queue. - bool fast_cache_overflow = mw_count >= mw_cache->max; - int duration_ms = (int)(mw_last_pkt - mw_first_pkt); - bool match_min_msgs = mw_count > mw_min_msgs; - - // when fast cache overflow, or duration ok, signal to flush. - if (fast_cache_overflow || (match_min_msgs && duration_ms > mw_duration)) { - return; - } -#else - int duration_ms = queue->duration(); - bool match_min_msgs = queue->size() > mw_min_msgs; - - // when duration ok, signal to flush. - if (match_min_msgs && duration_ms > mw_duration) { - return; - } -#endif - - // the enqueue will notify this cond. - mw_waiting = true; - // wait for msgs to incoming. - st_cond_wait(mw_wait); } -#endif int SrsConsumer::on_play_client_pause(bool is_pause) { @@ -519,28 +382,6 @@ int SrsConsumer::on_play_client_pause(bool is_pause) return ret; } -#ifdef SRS_PERF_QUEUE_FAST_CACHE -int SrsConsumer::dumps_queue_to_fast_cache() -{ - int ret =ERROR_SUCCESS; - - // fill fast cache with queue. - if ((ret = queue->dump_packets(mw_cache->max, mw_cache->msgs, mw_count)) != ERROR_SUCCESS) { - return ret; - } - // set the timestamp when got message. - if (mw_count > 0) { - SrsMessage* first_msg = mw_cache->msgs[0]; - mw_first_pkt = first_msg->header.timestamp; - - SrsMessage* last_msg = mw_cache->msgs[mw_count - 1]; - mw_last_pkt = last_msg->header.timestamp; - } - - return ret; -} -#endif - SrsGopCache::SrsGopCache() { cached_video_count = 0; diff --git a/trunk/src/app/srs_app_source.hpp b/trunk/src/app/srs_app_source.hpp index fac678ed7c..023f6d3883 100644 --- a/trunk/src/app/srs_app_source.hpp +++ b/trunk/src/app/srs_app_source.hpp @@ -165,24 +165,6 @@ class SrsConsumer bool paused; // when source id changed, notice all consumers bool should_update_source_id; -#ifdef SRS_PERF_QUEUE_COND_WAIT - // the cond wait for mw. - // @see https://github.com/winlinvip/simple-rtmp-server/issues/251 - st_cond_t mw_wait; - bool mw_waiting; - int mw_min_msgs; - int mw_duration; -#endif -#ifdef SRS_PERF_QUEUE_FAST_CACHE - // use fast cache for msgs - // @see https://github.com/winlinvip/simple-rtmp-server/issues/251 - SrsMessageArray* mw_cache; - // the count of msg in fast cache. - int mw_count; - // the packet time in fast cache. - int64_t mw_first_pkt; - int64_t mw_last_pkt; -#endif public: SrsConsumer(SrsSource* _source); virtual ~SrsConsumer(); @@ -216,27 +198,11 @@ class SrsConsumer * @param count the count in array, output param. * @max_count the max count to dequeue, must be positive. */ - virtual int dump_packets(SrsMessageArray* msgs, int* count); -#ifdef SRS_PERF_QUEUE_COND_WAIT - /** - * wait for messages incomming, atleast nb_msgs and in duration. - * @param nb_msgs the messages count to wait. - * @param duration the messgae duration to wait. - */ - virtual void wait(int nb_msgs, int duration); -#endif + virtual int dump_packets(SrsMessageArray* msgs, int& count); /** * when client send the pause message. */ virtual int on_play_client_pause(bool is_pause); -private: -#ifdef SRS_PERF_QUEUE_FAST_CACHE - /** - * dumps the queue to fast cache, - * when fast cache is clear or queue is overflow. - */ - virtual int dumps_queue_to_fast_cache(); -#endif }; /** diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index aaa822fd60..14036f94ae 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 58 +#define VERSION_REVISION 59 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/core/srs_core_performance.hpp b/trunk/src/core/srs_core_performance.hpp index 2cfcab65af..cf08a9d721 100644 --- a/trunk/src/core/srs_core_performance.hpp +++ b/trunk/src/core/srs_core_performance.hpp @@ -100,28 +100,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * how many msgs can be send entirely. * for play clients to get msgs then totally send out. * for the mw sleep set to 1800, the msgs is about 133. -* @remark, recomment to 256. +* @remark, recomment to 128. */ -#define SRS_PERF_MW_MSGS 256 -/** -* how many msgs atleast to send. -* @remark, recomment to 32. -*/ -#define SRS_PERF_MW_MIN_MSGS 32 - -/** -* whether enable the fast cache. -* @remark this improve performance for large connectios. -* @remark this also introduce complex, default to disable it. -* @see https://github.com/winlinvip/simple-rtmp-server/issues/251 -*/ -#undef SRS_PERF_QUEUE_FAST_CACHE -/** -* whether use cond wait to send messages. -* @remark this improve performance for large connectios. -* @see https://github.com/winlinvip/simple-rtmp-server/issues/251 -*/ -#undef SRS_PERF_QUEUE_COND_WAIT +#define SRS_PERF_MW_MSGS 128 /** * how many chunk stream to cache, [0, N]. diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp index bc84046fe9..70ed47f43d 100644 --- a/trunk/src/rtmp/srs_protocol_stack.cpp +++ b/trunk/src/rtmp/srs_protocol_stack.cpp @@ -736,8 +736,20 @@ int SrsProtocol::do_send_messages(SrsMessage** msgs, int nb_msgs) if (iov_index <= 0) { return ret; } - srs_info("mw %d msgs in %d iovs, max_msgs=%d, nb_out_iovs=%d", + + // calc the bytes of iovs, for debug. +#if 0 + int nb_bytes = 0; + for (int i = 0; i < iov_index; i++) { + iovec* iov = out_iovs + i; + nb_bytes += iov->iov_len; + } + srs_warn("mw %d msgs %dB in %d iovs, max_msgs=%d, nb_out_iovs=%d", + nb_msgs, nb_bytes, iov_index, SRS_PERF_MW_MSGS, nb_out_iovs); +#else + srs_info("mw %d msgs in %d iovs, max_msgs=%d, nb_out_iovs=%d", nb_msgs, iov_index, SRS_PERF_MW_MSGS, nb_out_iovs); +#endif // send by writev // sendout header and payload by writev. From 528ae1e9b15e861f5ad9f723230361825b320624 Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 5 Dec 2014 22:00:57 +0800 Subject: [PATCH 331/800] refine source and queue dump msgs. --- trunk/src/app/srs_app_source.cpp | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index d2a3dcce07..549a3bde0b 100644 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -211,22 +211,24 @@ int SrsMessageQueue::enqueue(SrsSharedPtrMessage* msg, bool* is_overflow) int SrsMessageQueue::dump_packets(int max_count, SrsMessage** pmsgs, int& count) { int ret = ERROR_SUCCESS; - - if (msgs.empty()) { + + int nb_msgs = (int)msgs.size(); + if (nb_msgs <= 0) { return ret; } srs_assert(max_count > 0); - count = srs_min(max_count, (int)msgs.size()); - + count = srs_min(max_count, nb_msgs); + + SrsSharedPtrMessage** omsgs = msgs.data(); for (int i = 0; i < count; i++) { - pmsgs[i] = msgs[i]; + pmsgs[i] = omsgs[i]; } - SrsMessage* last = msgs[count - 1]; + SrsSharedPtrMessage* last = omsgs[count - 1]; av_start_time = last->header.timestamp; - if (count == (int)msgs.size()) { + if (count >= nb_msgs) { // the pmsgs is big enough and clear msgs at most time. msgs.clear(); } else { @@ -1108,9 +1110,11 @@ int SrsSource::on_audio(SrsMessage* __audio) #endif // copy to all consumer - if (true) { - for (int i = 0; i < (int)consumers.size(); i++) { - SrsConsumer* consumer = consumers.at(i); + int nb_consumers = (int)consumers.size(); + if (nb_consumers > 0) { + SrsConsumer** pconsumer = consumers.data(); + for (int i = 0; i < nb_consumers; i++) { + SrsConsumer* consumer = pconsumer[i]; SrsSharedPtrMessage* copy = msg.copy(); if ((ret = consumer->enqueue(copy, atc, sample_rate, frame_rate, jitter_algorithm)) != ERROR_SUCCESS) { srs_error("dispatch the audio failed. ret=%d", ret); From f9b9a60de701bf80c1caeeb2e542210d12b62500 Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 5 Dec 2014 23:03:52 +0800 Subject: [PATCH 332/800] for bug #251, remove the SrsMessage, use SrsCommonMessage or SrsSharedPtrMessage. 2.0.60 --- trunk/src/app/srs_app_bandwidth.cpp | 8 +- trunk/src/app/srs_app_edge.cpp | 12 +- trunk/src/app/srs_app_edge.hpp | 8 +- trunk/src/app/srs_app_forward.cpp | 2 +- trunk/src/app/srs_app_recv_thread.cpp | 14 +- trunk/src/app/srs_app_recv_thread.hpp | 12 +- trunk/src/app/srs_app_rtmp_conn.cpp | 12 +- trunk/src/app/srs_app_rtmp_conn.hpp | 8 +- trunk/src/app/srs_app_source.cpp | 14 +- trunk/src/app/srs_app_source.hpp | 16 +- trunk/src/core/srs_core.hpp | 2 +- trunk/src/libs/srs_lib_bandwidth.cpp | 8 +- trunk/src/libs/srs_librtmp.cpp | 4 +- trunk/src/qt/srs/srs-qt.pro.user | 2 +- trunk/src/rtmp/srs_protocol_msg_array.cpp | 4 +- trunk/src/rtmp/srs_protocol_msg_array.hpp | 4 +- trunk/src/rtmp/srs_protocol_rtmp.cpp | 52 ++-- trunk/src/rtmp/srs_protocol_rtmp.hpp | 24 +- trunk/src/rtmp/srs_protocol_stack.cpp | 263 ++++++++-------- trunk/src/rtmp/srs_protocol_stack.hpp | 194 ++++++------ trunk/src/utest/srs_utest_protocol.cpp | 348 +++++++++++----------- 21 files changed, 502 insertions(+), 509 deletions(-) diff --git a/trunk/src/app/srs_app_bandwidth.cpp b/trunk/src/app/srs_app_bandwidth.cpp index c065f64eb5..db90787e4a 100644 --- a/trunk/src/app/srs_app_bandwidth.cpp +++ b/trunk/src/app/srs_app_bandwidth.cpp @@ -94,12 +94,12 @@ int _srs_expect_bandwidth_packet(SrsRtmpServer* rtmp, _CheckPacketType pfn) int ret = ERROR_SUCCESS; while (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; SrsBandwidthPacket* pkt = NULL; if ((ret = rtmp->expect_message(&msg, &pkt)) != ERROR_SUCCESS) { return ret; } - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); SrsAutoFree(SrsBandwidthPacket, pkt); srs_info("get bwtc message success."); @@ -380,12 +380,12 @@ int SrsBandwidth::publish_checking(SrsBandwidthSample* sample, SrsKbpsLimit* lim srs_update_system_time_ms(); int64_t starttime = srs_get_system_time_ms(); while ((srs_get_system_time_ms() - starttime) < sample->duration_ms) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; SrsBandwidthPacket* pkt = NULL; if ((ret = _rtmp->expect_message(&msg, &pkt)) != ERROR_SUCCESS) { return ret; } - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); SrsAutoFree(SrsBandwidthPacket, pkt); srs_info("get publish message success."); diff --git a/trunk/src/app/srs_app_edge.cpp b/trunk/src/app/srs_app_edge.cpp index 37d9ef941e..587c8d499e 100644 --- a/trunk/src/app/srs_app_edge.cpp +++ b/trunk/src/app/srs_app_edge.cpp @@ -183,7 +183,7 @@ int SrsEdgeIngester::ingest() } // read from client. - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; if ((ret = client->recv_message(&msg)) != ERROR_SUCCESS) { if (!srs_is_client_gracefully_close(ret)) { srs_error("pull origin server message failed. ret=%d", ret); @@ -193,7 +193,7 @@ int SrsEdgeIngester::ingest() srs_verbose("edge loop recv message. ret=%d", ret); srs_assert(msg); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); if ((ret = process_publish_message(msg)) != ERROR_SUCCESS) { return ret; @@ -256,7 +256,7 @@ int SrsEdgeIngester::connect_app(string ep_server, string ep_port) return ret; } -int SrsEdgeIngester::process_publish_message(SrsMessage* msg) +int SrsEdgeIngester::process_publish_message(SrsCommonMessage* msg) { int ret = ERROR_SUCCESS; @@ -485,7 +485,7 @@ int SrsEdgeForwarder::cycle() // read from client. if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ret = client->recv_message(&msg); srs_verbose("edge loop recv message. ret=%d", ret); @@ -534,7 +534,7 @@ int SrsEdgeForwarder::cycle() return ret; } -int SrsEdgeForwarder::proxy(SrsMessage* msg) +int SrsEdgeForwarder::proxy(SrsCommonMessage* msg) { int ret = ERROR_SUCCESS; @@ -825,7 +825,7 @@ int SrsPublishEdge::on_client_publish() return ret; } -int SrsPublishEdge::on_proxy_publish(SrsMessage* msg) +int SrsPublishEdge::on_proxy_publish(SrsCommonMessage* msg) { return forwarder->proxy(msg); } diff --git a/trunk/src/app/srs_app_edge.hpp b/trunk/src/app/srs_app_edge.hpp index d81e09723a..ee2278f368 100644 --- a/trunk/src/app/srs_app_edge.hpp +++ b/trunk/src/app/srs_app_edge.hpp @@ -42,7 +42,7 @@ class SrsRequest; class SrsPlayEdge; class SrsPublishEdge; class SrsRtmpClient; -class SrsMessage; +class SrsCommonMessage; class SrsMessageQueue; class ISrsProtocolReaderWriter; class SrsKbps; @@ -104,7 +104,7 @@ class SrsEdgeIngester : public ISrsThreadHandler virtual void close_underlayer_socket(); virtual int connect_server(std::string& ep_server, std::string& ep_port); virtual int connect_app(std::string ep_server, std::string ep_port); - virtual int process_publish_message(SrsMessage* msg); + virtual int process_publish_message(SrsCommonMessage* msg); }; /** @@ -148,7 +148,7 @@ class SrsEdgeForwarder : public ISrsThreadHandler public: virtual int cycle(); public: - virtual int proxy(SrsMessage* msg); + virtual int proxy(SrsCommonMessage* msg); private: virtual void close_underlayer_socket(); virtual int connect_server(std::string& ep_server, std::string& ep_port); @@ -214,7 +214,7 @@ class SrsPublishEdge /** * proxy publish stream to edge */ - virtual int on_proxy_publish(SrsMessage* msg); + virtual int on_proxy_publish(SrsCommonMessage* msg); /** * proxy unpublish stream to edge. */ diff --git a/trunk/src/app/srs_app_forward.cpp b/trunk/src/app/srs_app_forward.cpp index 52b62256b2..134fc083be 100644 --- a/trunk/src/app/srs_app_forward.cpp +++ b/trunk/src/app/srs_app_forward.cpp @@ -404,7 +404,7 @@ int SrsForwarder::forward() // read from client. if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ret = client->recv_message(&msg); srs_verbose("play loop recv message. ret=%d", ret); diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index 5ff6de5280..10b482f530 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -81,7 +81,7 @@ int SrsRecvThread::cycle() continue; } - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; // recv and handle message ret = rtmp->recv_message(&msg); @@ -145,9 +145,9 @@ SrsQueueRecvThread::~SrsQueueRecvThread() trd.stop(); // clear all messages. - std::vector::iterator it; + std::vector::iterator it; for (it = queue.begin(); it != queue.end(); ++it) { - SrsMessage* msg = *it; + SrsCommonMessage* msg = *it; srs_freep(msg); } queue.clear(); @@ -173,11 +173,11 @@ int SrsQueueRecvThread::size() return (int)queue.size(); } -SrsMessage* SrsQueueRecvThread::pump() +SrsCommonMessage* SrsQueueRecvThread::pump() { srs_assert(!queue.empty()); - SrsMessage* msg = *queue.begin(); + SrsCommonMessage* msg = *queue.begin(); queue.erase(queue.begin()); @@ -198,7 +198,7 @@ bool SrsQueueRecvThread::can_handle() return empty(); } -int SrsQueueRecvThread::handle(SrsMessage* msg) +int SrsQueueRecvThread::handle(SrsCommonMessage* msg) { // put into queue, the send thread will get and process it, // @see SrsRtmpConn::process_play_control_msg @@ -335,7 +335,7 @@ bool SrsPublishRecvThread::can_handle() return true; } -int SrsPublishRecvThread::handle(SrsMessage* msg) +int SrsPublishRecvThread::handle(SrsCommonMessage* msg) { int ret = ERROR_SUCCESS; diff --git a/trunk/src/app/srs_app_recv_thread.hpp b/trunk/src/app/srs_app_recv_thread.hpp index 125a16e671..957c8d85b4 100644 --- a/trunk/src/app/srs_app_recv_thread.hpp +++ b/trunk/src/app/srs_app_recv_thread.hpp @@ -38,7 +38,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include class SrsRtmpServer; -class SrsMessage; +class SrsCommonMessage; class SrsRtmpConn; class SrsSource; class SrsRequest; @@ -62,7 +62,7 @@ class ISrsMessageHandler /** * process the received message. */ - virtual int handle(SrsMessage* msg) = 0; + virtual int handle(SrsCommonMessage* msg) = 0; /** * when recv message error. */ @@ -107,7 +107,7 @@ class SrsRecvThread : public ISrsThreadHandler class SrsQueueRecvThread : public ISrsMessageHandler { private: - std::vector queue; + std::vector queue; SrsRecvThread trd; SrsRtmpServer* rtmp; // the recv thread error code. @@ -121,11 +121,11 @@ class SrsQueueRecvThread : public ISrsMessageHandler public: virtual bool empty(); virtual int size(); - virtual SrsMessage* pump(); + virtual SrsCommonMessage* pump(); virtual int error_code(); public: virtual bool can_handle(); - virtual int handle(SrsMessage* msg); + virtual int handle(SrsCommonMessage* msg); virtual void on_recv_error(int ret); public: virtual void on_thread_start(); @@ -183,7 +183,7 @@ class SrsPublishRecvThread : virtual public ISrsMessageHandler // interface ISrsMessageHandler public: virtual bool can_handle(); - virtual int handle(SrsMessage* msg); + virtual int handle(SrsCommonMessage* msg); virtual void on_recv_error(int ret); // interface IMergeReadHandler public: diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index 263e1a3f2f..3e5424ba44 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -576,7 +576,7 @@ int SrsRtmpConn::do_playing(SrsSource* source, SrsQueueRecvThread* trd) // @see: https://github.com/winlinvip/simple-rtmp-server/issues/196 // @see: https://github.com/winlinvip/simple-rtmp-server/issues/217 while (!trd->empty()) { - SrsMessage* msg = trd->pump(); + SrsCommonMessage* msg = trd->pump(); srs_verbose("pump client message to process."); if ((ret = process_play_control_msg(consumer, msg)) != ERROR_SUCCESS) { @@ -628,7 +628,7 @@ int SrsRtmpConn::do_playing(SrsSource* source, SrsQueueRecvThread* trd) // we start to collect the durations for each message. if (user_specified_duration_to_stop) { for (int i = 0; i < count; i++) { - SrsMessage* msg = msgs.msgs[i]; + SrsSharedPtrMessage* msg = msgs.msgs[i]; // foreach msg, collect the duration. // @remark: never use msg when sent it, for the protocol sdk will free it. @@ -806,7 +806,7 @@ int SrsRtmpConn::do_publishing(SrsSource* source, SrsPublishRecvThread* trd) return ret; } -int SrsRtmpConn::handle_publish_message(SrsSource* source, SrsMessage* msg, bool is_fmle, bool vhost_is_edge) +int SrsRtmpConn::handle_publish_message(SrsSource* source, SrsCommonMessage* msg, bool is_fmle, bool vhost_is_edge) { int ret = ERROR_SUCCESS; @@ -850,7 +850,7 @@ int SrsRtmpConn::handle_publish_message(SrsSource* source, SrsMessage* msg, bool return ret; } -int SrsRtmpConn::process_publish_message(SrsSource* source, SrsMessage* msg, bool vhost_is_edge) +int SrsRtmpConn::process_publish_message(SrsSource* source, SrsCommonMessage* msg, bool vhost_is_edge) { int ret = ERROR_SUCCESS; @@ -915,7 +915,7 @@ int SrsRtmpConn::process_publish_message(SrsSource* source, SrsMessage* msg, boo return ret; } -int SrsRtmpConn::process_play_control_msg(SrsConsumer* consumer, SrsMessage* msg) +int SrsRtmpConn::process_play_control_msg(SrsConsumer* consumer, SrsCommonMessage* msg) { int ret = ERROR_SUCCESS; @@ -923,7 +923,7 @@ int SrsRtmpConn::process_play_control_msg(SrsConsumer* consumer, SrsMessage* msg srs_verbose("ignore all empty message."); return ret; } - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); if (!msg->header.is_amf0_command() && !msg->header.is_amf3_command()) { srs_info("ignore all message except amf0/amf3 command."); diff --git a/trunk/src/app/srs_app_rtmp_conn.hpp b/trunk/src/app/srs_app_rtmp_conn.hpp index 3ecaf82cab..07c77e14df 100644 --- a/trunk/src/app/srs_app_rtmp_conn.hpp +++ b/trunk/src/app/srs_app_rtmp_conn.hpp @@ -40,7 +40,7 @@ class SrsResponse; class SrsSource; class SrsRefer; class SrsConsumer; -class SrsMessage; +class SrsCommonMessage; class SrsStSocket; #ifdef SRS_AUTO_HTTP_CALLBACK class SrsHttpHooks; @@ -101,9 +101,9 @@ class SrsRtmpConn : public virtual SrsConnection, public virtual ISrsReloadHandl virtual int fmle_publishing(SrsSource* source); virtual int flash_publishing(SrsSource* source); virtual int do_publishing(SrsSource* source, SrsPublishRecvThread* trd); - virtual int handle_publish_message(SrsSource* source, SrsMessage* msg, bool is_fmle, bool vhost_is_edge); - virtual int process_publish_message(SrsSource* source, SrsMessage* msg, bool vhost_is_edge); - virtual int process_play_control_msg(SrsConsumer* consumer, SrsMessage* msg); + virtual int handle_publish_message(SrsSource* source, SrsCommonMessage* msg, bool is_fmle, bool vhost_is_edge); + virtual int process_publish_message(SrsSource* source, SrsCommonMessage* msg, bool vhost_is_edge); + virtual int process_play_control_msg(SrsConsumer* consumer, SrsCommonMessage* msg); virtual void change_mw_sleep(int sleep_ms); private: virtual int check_edge_token_traverse_auth(); diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index 549a3bde0b..cec809e648 100644 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -208,7 +208,7 @@ int SrsMessageQueue::enqueue(SrsSharedPtrMessage* msg, bool* is_overflow) return ret; } -int SrsMessageQueue::dump_packets(int max_count, SrsMessage** pmsgs, int& count) +int SrsMessageQueue::dump_packets(int max_count, SrsSharedPtrMessage** pmsgs, int& count) { int ret = ERROR_SUCCESS; @@ -951,7 +951,7 @@ bool SrsSource::can_publish() return _can_publish; } -int SrsSource::on_meta_data(SrsMessage* msg, SrsOnMetaDataPacket* metadata) +int SrsSource::on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* metadata) { int ret = ERROR_SUCCESS; @@ -1072,7 +1072,7 @@ int SrsSource::on_meta_data(SrsMessage* msg, SrsOnMetaDataPacket* metadata) return ret; } -int SrsSource::on_audio(SrsMessage* __audio) +int SrsSource::on_audio(SrsCommonMessage* __audio) { int ret = ERROR_SUCCESS; @@ -1183,7 +1183,7 @@ int SrsSource::on_audio(SrsMessage* __audio) return ret; } -int SrsSource::on_video(SrsMessage* __video) +int SrsSource::on_video(SrsCommonMessage* __video) { int ret = ERROR_SUCCESS; @@ -1287,7 +1287,7 @@ int SrsSource::on_video(SrsMessage* __video) return ret; } -int SrsSource::on_aggregate(SrsMessage* msg) +int SrsSource::on_aggregate(SrsCommonMessage* msg) { int ret = ERROR_SUCCESS; @@ -1349,7 +1349,7 @@ int SrsSource::on_aggregate(SrsMessage* msg) // to common message. SrsCommonMessage __o; - SrsMessage& o = __o; + SrsCommonMessage& o = __o; o.header.message_type = type; o.header.payload_length = data_size; @@ -1570,7 +1570,7 @@ int SrsSource::on_edge_start_publish() return publish_edge->on_client_publish(); } -int SrsSource::on_edge_proxy_publish(SrsMessage* msg) +int SrsSource::on_edge_proxy_publish(SrsCommonMessage* msg) { return publish_edge->on_proxy_publish(msg); } diff --git a/trunk/src/app/srs_app_source.hpp b/trunk/src/app/srs_app_source.hpp index 023f6d3883..ea135f6833 100644 --- a/trunk/src/app/srs_app_source.hpp +++ b/trunk/src/app/srs_app_source.hpp @@ -41,7 +41,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. class SrsPlayEdge; class SrsPublishEdge; class SrsSource; -class SrsMessage; +class SrsCommonMessage; class SrsOnMetaDataPacket; class SrsSharedPtrMessage; class SrsForwarder; @@ -139,11 +139,11 @@ class SrsMessageQueue virtual int enqueue(SrsSharedPtrMessage* msg, bool* is_overflow = NULL); /** * get packets in consumer queue. - * @pmsgs SrsMessages*[], used to store the msgs, user must alloc it. + * @pmsgs SrsCommonMessages*[], 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 int dump_packets(int max_count, SrsMessage** pmsgs, int& count); + virtual int dump_packets(int max_count, SrsSharedPtrMessage** pmsgs, int& count); private: /** * remove a gop from the front. @@ -395,10 +395,10 @@ class SrsSource : public ISrsReloadHandler // logic data methods public: virtual bool can_publish(); - virtual int on_meta_data(SrsMessage* msg, SrsOnMetaDataPacket* metadata); - virtual int on_audio(SrsMessage* audio); - virtual int on_video(SrsMessage* video); - virtual int on_aggregate(SrsMessage* msg); + virtual int on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* metadata); + virtual int on_audio(SrsCommonMessage* audio); + virtual int on_video(SrsCommonMessage* video); + virtual int on_aggregate(SrsCommonMessage* msg); /** * the pre-publish is we are very sure we are * trying to publish stream, please lock the resource, @@ -425,7 +425,7 @@ class SrsSource : public ISrsReloadHandler // for edge, when publish edge stream, check the state virtual int on_edge_start_publish(); // for edge, proxy the publish - virtual int on_edge_proxy_publish(SrsMessage* msg); + virtual int on_edge_proxy_publish(SrsCommonMessage* msg); // for edge, proxy stop publish virtual void on_edge_proxy_unpublish(); private: diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 14036f94ae..214b49472f 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 59 +#define VERSION_REVISION 60 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/libs/srs_lib_bandwidth.cpp b/trunk/src/libs/srs_lib_bandwidth.cpp index c3816293d0..9bfb570643 100644 --- a/trunk/src/libs/srs_lib_bandwidth.cpp +++ b/trunk/src/libs/srs_lib_bandwidth.cpp @@ -67,12 +67,12 @@ int _srs_expect_bandwidth_packet(SrsRtmpClient* rtmp, _CheckPacketType pfn) int ret = ERROR_SUCCESS; while (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; SrsBandwidthPacket* pkt = NULL; if ((ret = rtmp->expect_message(&msg, &pkt)) != ERROR_SUCCESS) { return ret; } - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); SrsAutoFree(SrsBandwidthPacket, pkt); srs_info("get final message success."); @@ -88,12 +88,12 @@ int _srs_expect_bandwidth_packet2(SrsRtmpClient* rtmp, _CheckPacketType pfn, Srs int ret = ERROR_SUCCESS; while (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; SrsBandwidthPacket* pkt = NULL; if ((ret = rtmp->expect_message(&msg, &pkt)) != ERROR_SUCCESS) { return ret; } - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); srs_info("get final message success."); if (pfn(pkt)) { diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index 17fdb269c8..a5c18e0ca9 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -755,7 +755,7 @@ int srs_rtmp_read_packet(srs_rtmp_t rtmp, char* type, u_int32_t* timestamp, char Context* context = (Context*)rtmp; for (;;) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; if ((ret = context->rtmp->recv_message(&msg)) != ERROR_SUCCESS) { return ret; } @@ -763,7 +763,7 @@ int srs_rtmp_read_packet(srs_rtmp_t rtmp, char* type, u_int32_t* timestamp, char continue; } - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); if (msg->header.is_audio()) { *type = SRS_RTMP_TYPE_AUDIO; diff --git a/trunk/src/qt/srs/srs-qt.pro.user b/trunk/src/qt/srs/srs-qt.pro.user index ffd773f849..101e5889a1 100755 --- a/trunk/src/qt/srs/srs-qt.pro.user +++ b/trunk/src/qt/srs/srs-qt.pro.user @@ -1,6 +1,6 @@ - + ProjectExplorer.Project.ActiveTarget diff --git a/trunk/src/rtmp/srs_protocol_msg_array.cpp b/trunk/src/rtmp/srs_protocol_msg_array.cpp index 699603be4e..adda6dd151 100644 --- a/trunk/src/rtmp/srs_protocol_msg_array.cpp +++ b/trunk/src/rtmp/srs_protocol_msg_array.cpp @@ -29,7 +29,7 @@ SrsMessageArray::SrsMessageArray(int max_msgs) { srs_assert(max_msgs > 0); - msgs = new SrsMessage*[max_msgs]; + msgs = new SrsSharedPtrMessage*[max_msgs]; max = max_msgs; zero(max_msgs); @@ -47,7 +47,7 @@ void SrsMessageArray::free(int count) { // initialize for (int i = 0; i < count; i++) { - SrsMessage* msg = msgs[i]; + SrsSharedPtrMessage* msg = msgs[i]; srs_freep(msg); msgs[i] = NULL; diff --git a/trunk/src/rtmp/srs_protocol_msg_array.hpp b/trunk/src/rtmp/srs_protocol_msg_array.hpp index aff10def7b..6eea3112b2 100644 --- a/trunk/src/rtmp/srs_protocol_msg_array.hpp +++ b/trunk/src/rtmp/srs_protocol_msg_array.hpp @@ -30,7 +30,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include -class SrsMessage; +class SrsSharedPtrMessage; /** * the class to auto free the shared ptr message array. @@ -49,7 +49,7 @@ class SrsMessageArray * for instance, msg= msgs.msgs[i], msgs.msgs[i]=NULL, send(msg), * where send(msg) will always send and free it. */ - SrsMessage** msgs; + SrsSharedPtrMessage** msgs; int max; public: /** diff --git a/trunk/src/rtmp/srs_protocol_rtmp.cpp b/trunk/src/rtmp/srs_protocol_rtmp.cpp index 2b6525dbd1..9cbe655d13 100644 --- a/trunk/src/rtmp/srs_protocol_rtmp.cpp +++ b/trunk/src/rtmp/srs_protocol_rtmp.cpp @@ -360,22 +360,22 @@ int64_t SrsRtmpClient::get_send_bytes() return protocol->get_send_bytes(); } -int SrsRtmpClient::recv_message(SrsMessage** pmsg) +int SrsRtmpClient::recv_message(SrsCommonMessage** pmsg) { return protocol->recv_message(pmsg); } -int SrsRtmpClient::decode_message(SrsMessage* msg, SrsPacket** ppacket) +int SrsRtmpClient::decode_message(SrsCommonMessage* msg, SrsPacket** ppacket) { return protocol->decode_message(msg, ppacket); } -int SrsRtmpClient::send_and_free_message(SrsMessage* msg, int stream_id) +int SrsRtmpClient::send_and_free_message(SrsSharedPtrMessage* msg, int stream_id) { return protocol->send_and_free_message(msg, stream_id); } -int SrsRtmpClient::send_and_free_messages(SrsMessage** msgs, int nb_msgs, int stream_id) +int SrsRtmpClient::send_and_free_messages(SrsSharedPtrMessage** msgs, int nb_msgs, int stream_id) { return protocol->send_and_free_messages(msgs, nb_msgs, stream_id); } @@ -509,13 +509,13 @@ int SrsRtmpClient::connect_app2( } // expect connect _result - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; SrsConnectAppResPacket* pkt = NULL; if ((ret = expect_message(&msg, &pkt)) != ERROR_SUCCESS) { srs_error("expect connect app response message failed. ret=%d", ret); return ret; } - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); SrsAutoFree(SrsConnectAppResPacket, pkt); // server info @@ -566,13 +566,13 @@ int SrsRtmpClient::create_stream(int& stream_id) // CreateStream _result. if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; SrsCreateStreamResPacket* pkt = NULL; if ((ret = expect_message(&msg, &pkt)) != ERROR_SUCCESS) { srs_error("expect create stream response message failed. ret=%d", ret); return ret; } - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); SrsAutoFree(SrsCreateStreamResPacket, pkt); srs_info("get create stream response message"); @@ -700,13 +700,13 @@ int SrsRtmpClient::fmle_publish(string stream, int& stream_id) // expect result of CreateStream if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; SrsCreateStreamResPacket* pkt = NULL; if ((ret = expect_message(&msg, &pkt)) != ERROR_SUCCESS) { srs_error("expect create stream response message failed. ret=%d", ret); return ret; } - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); SrsAutoFree(SrsCreateStreamResPacket, pkt); srs_info("get create stream response message"); @@ -787,22 +787,22 @@ int64_t SrsRtmpServer::get_send_bytes() return protocol->get_send_bytes(); } -int SrsRtmpServer::recv_message(SrsMessage** pmsg) +int SrsRtmpServer::recv_message(SrsCommonMessage** pmsg) { return protocol->recv_message(pmsg); } -int SrsRtmpServer::decode_message(SrsMessage* msg, SrsPacket** ppacket) +int SrsRtmpServer::decode_message(SrsCommonMessage* msg, SrsPacket** ppacket) { return protocol->decode_message(msg, ppacket); } -int SrsRtmpServer::send_and_free_message(SrsMessage* msg, int stream_id) +int SrsRtmpServer::send_and_free_message(SrsSharedPtrMessage* msg, int stream_id) { return protocol->send_and_free_message(msg, stream_id); } -int SrsRtmpServer::send_and_free_messages(SrsMessage** msgs, int nb_msgs, int stream_id) +int SrsRtmpServer::send_and_free_messages(SrsSharedPtrMessage** msgs, int nb_msgs, int stream_id) { return protocol->send_and_free_messages(msgs, nb_msgs, stream_id); } @@ -838,13 +838,13 @@ int SrsRtmpServer::connect_app(SrsRequest* req) { int ret = ERROR_SUCCESS; - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; SrsConnectAppPacket* pkt = NULL; if ((ret = expect_message(&msg, &pkt)) != ERROR_SUCCESS) { srs_error("expect connect app message failed. ret=%d", ret); return ret; } - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); SrsAutoFree(SrsConnectAppPacket, pkt); srs_info("get connect app message"); @@ -1001,7 +1001,7 @@ int SrsRtmpServer::identify_client(int stream_id, SrsRtmpConnType& type, string& int ret = ERROR_SUCCESS; while (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; if ((ret = protocol->recv_message(&msg)) != ERROR_SUCCESS) { if (!srs_is_client_gracefully_close(ret)) { srs_error("recv identify client message failed. ret=%d", ret); @@ -1009,7 +1009,7 @@ int SrsRtmpServer::identify_client(int stream_id, SrsRtmpConnType& type, string& return ret; } - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); SrsMessageHeader& h = msg->header; if (h.is_ackledgement() || h.is_set_chunk_size() || h.is_window_ackledgement_size() || h.is_user_control_message()) { @@ -1233,7 +1233,7 @@ int SrsRtmpServer::start_fmle_publish(int stream_id) // FCPublish double fc_publish_tid = 0; if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; SrsFMLEStartPacket* pkt = NULL; if ((ret = expect_message(&msg, &pkt)) != ERROR_SUCCESS) { srs_error("recv FCPublish message failed. ret=%d", ret); @@ -1241,7 +1241,7 @@ int SrsRtmpServer::start_fmle_publish(int stream_id) } srs_info("recv FCPublish request message success."); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); SrsAutoFree(SrsFMLEStartPacket, pkt); fc_publish_tid = pkt->transaction_id; @@ -1259,7 +1259,7 @@ int SrsRtmpServer::start_fmle_publish(int stream_id) // createStream double create_stream_tid = 0; if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; SrsCreateStreamPacket* pkt = NULL; if ((ret = expect_message(&msg, &pkt)) != ERROR_SUCCESS) { srs_error("recv createStream message failed. ret=%d", ret); @@ -1267,7 +1267,7 @@ int SrsRtmpServer::start_fmle_publish(int stream_id) } srs_info("recv createStream request message success."); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); SrsAutoFree(SrsCreateStreamPacket, pkt); create_stream_tid = pkt->transaction_id; @@ -1284,7 +1284,7 @@ int SrsRtmpServer::start_fmle_publish(int stream_id) // publish if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; SrsPublishPacket* pkt = NULL; if ((ret = expect_message(&msg, &pkt)) != ERROR_SUCCESS) { srs_error("recv publish message failed. ret=%d", ret); @@ -1292,7 +1292,7 @@ int SrsRtmpServer::start_fmle_publish(int stream_id) } srs_info("recv publish request message success."); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); SrsAutoFree(SrsPublishPacket, pkt); } // publish response onFCPublish(NetStream.Publish.Start) @@ -1417,7 +1417,7 @@ int SrsRtmpServer::identify_create_stream_client(SrsCreateStreamPacket* req, int } while (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; if ((ret = protocol->recv_message(&msg)) != ERROR_SUCCESS) { if (!srs_is_client_gracefully_close(ret)) { srs_error("recv identify client message failed. ret=%d", ret); @@ -1425,7 +1425,7 @@ int SrsRtmpServer::identify_create_stream_client(SrsCreateStreamPacket* req, int return ret; } - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); SrsMessageHeader& h = msg->header; if (h.is_ackledgement() || h.is_set_chunk_size() || h.is_window_ackledgement_size() || h.is_user_control_message()) { diff --git a/trunk/src/rtmp/srs_protocol_rtmp.hpp b/trunk/src/rtmp/srs_protocol_rtmp.hpp index f4fb0aea96..09b45e30ef 100644 --- a/trunk/src/rtmp/srs_protocol_rtmp.hpp +++ b/trunk/src/rtmp/srs_protocol_rtmp.hpp @@ -37,14 +37,14 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. class SrsProtocol; class ISrsProtocolReaderWriter; -class ISrsMessage; +class ISrsCommonMessage; class SrsCommonMessage; class SrsCreateStreamPacket; class SrsFMLEStartPacket; class SrsPublishPacket; class SrsOnMetaDataPacket; class SrsPlayPacket; -class SrsMessage; +class SrsCommonMessage; class SrsPacket; class SrsAmf0Object; class IMergeReadHandler; @@ -207,14 +207,14 @@ class SrsRtmpClient * never NULL if decode success. * @remark, drop message when msg is empty or payload length is empty. */ - virtual int recv_message(SrsMessage** pmsg); + virtual int recv_message(SrsCommonMessage** pmsg); /** * decode bytes oriented RTMP message to RTMP packet, * @param ppacket, output decoded packet, * always NULL if error, never NULL if success. * @return error when unknown packet, error when decode failed. */ - virtual int decode_message(SrsMessage* msg, SrsPacket** ppacket); + virtual int decode_message(SrsCommonMessage* msg, SrsPacket** ppacket); /** * send the RTMP message and always free it. * user must never free or use the msg after this method, @@ -222,7 +222,7 @@ class SrsRtmpClient * @param msg, the msg to send out, never be NULL. * @param stream_id, the stream id of packet to send over, 0 for control message. */ - virtual int send_and_free_message(SrsMessage* msg, int stream_id); + virtual int send_and_free_message(SrsSharedPtrMessage* msg, int stream_id); /** * send the RTMP message and always free it. * user must never free or use the msg after this method, @@ -231,7 +231,7 @@ class SrsRtmpClient * @param nb_msgs, the size of msgs to send out. * @param stream_id, the stream id of packet to send over, 0 for control message. */ - virtual int send_and_free_messages(SrsMessage** msgs, int nb_msgs, int stream_id); + virtual int send_and_free_messages(SrsSharedPtrMessage** msgs, int nb_msgs, int stream_id); /** * send the RTMP packet and always free it. * user must never free or use the packet after this method, @@ -316,7 +316,7 @@ class SrsRtmpClient * if need to set timeout, use set timeout of SrsProtocol. */ template - int expect_message(SrsMessage** pmsg, T** ppacket) + int expect_message(SrsCommonMessage** pmsg, T** ppacket) { return protocol->expect_message(pmsg, ppacket); } @@ -389,14 +389,14 @@ class SrsRtmpServer * never NULL if decode success. * @remark, drop message when msg is empty or payload length is empty. */ - virtual int recv_message(SrsMessage** pmsg); + virtual int recv_message(SrsCommonMessage** pmsg); /** * decode bytes oriented RTMP message to RTMP packet, * @param ppacket, output decoded packet, * always NULL if error, never NULL if success. * @return error when unknown packet, error when decode failed. */ - virtual int decode_message(SrsMessage* msg, SrsPacket** ppacket); + virtual int decode_message(SrsCommonMessage* msg, SrsPacket** ppacket); /** * send the RTMP message and always free it. * user must never free or use the msg after this method, @@ -404,7 +404,7 @@ class SrsRtmpServer * @param msg, the msg to send out, never be NULL. * @param stream_id, the stream id of packet to send over, 0 for control message. */ - virtual int send_and_free_message(SrsMessage* msg, int stream_id); + virtual int send_and_free_message(SrsSharedPtrMessage* msg, int stream_id); /** * send the RTMP message and always free it. * user must never free or use the msg after this method, @@ -416,7 +416,7 @@ class SrsRtmpServer * @remark performance issue, to support 6k+ 250kbps client, * @see https://github.com/winlinvip/simple-rtmp-server/issues/194 */ - virtual int send_and_free_messages(SrsMessage** msgs, int nb_msgs, int stream_id); + virtual int send_and_free_messages(SrsSharedPtrMessage** msgs, int nb_msgs, int stream_id); /** * send the RTMP packet and always free it. * user must never free or use the packet after this method, @@ -523,7 +523,7 @@ class SrsRtmpServer * if need to set timeout, use set timeout of SrsProtocol. */ template - int expect_message(SrsMessage** pmsg, T** ppacket) + int expect_message(SrsCommonMessage** pmsg, T** ppacket) { return protocol->expect_message(pmsg, ppacket); } diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp index 70ed47f43d..1282534747 100644 --- a/trunk/src/rtmp/srs_protocol_stack.cpp +++ b/trunk/src/rtmp/srs_protocol_stack.cpp @@ -386,14 +386,112 @@ void SrsMessageHeader::initialize_video(int size, u_int32_t time, int stream) perfer_cid = RTMP_CID_Video; } -SrsMessage::SrsMessage() +SrsCommonMessage::SrsCommonMessage() { payload = NULL; size = 0; } -SrsMessage::~SrsMessage() +SrsCommonMessage::~SrsCommonMessage() +{ + srs_freep(payload); +} + +SrsSharedPtrMessage::__SrsSharedPtr::__SrsSharedPtr() { + payload = NULL; + size = 0; + shared_count = 0; +} + +SrsSharedPtrMessage::__SrsSharedPtr::~__SrsSharedPtr() +{ + srs_freep(payload); +} + +SrsSharedPtrMessage::SrsSharedPtrMessage() +{ + ptr = NULL; +} + +SrsSharedPtrMessage::~SrsSharedPtrMessage() +{ + if (ptr) { + if (ptr->shared_count == 0) { + srs_freep(ptr); + } else { + ptr->shared_count--; + } + } +} + +int SrsSharedPtrMessage::create(SrsCommonMessage* msg) +{ + int ret = ERROR_SUCCESS; + + if ((ret = create(&msg->header, msg->payload, msg->size)) != ERROR_SUCCESS) { + return ret; + } + + // to prevent double free of payload: + // initialize already attach the payload of msg, + // detach the payload to transfer the owner to shared ptr. + msg->payload = NULL; + msg->size = 0; + + return ret; +} + +int SrsSharedPtrMessage::create(SrsMessageHeader* pheader, char* payload, int size) +{ + int ret = ERROR_SUCCESS; + + srs_assert(pheader != NULL); + if (ptr) { + ret = ERROR_SYSTEM_ASSERT_FAILED; + srs_error("should not set the payload twice. ret=%d", ret); + srs_assert(false); + + return ret; + } + + header = *pheader; + header.payload_length = size; + + ptr = new __SrsSharedPtr(); + + // direct attach the data. + ptr->payload = payload; + ptr->size = size; + + // message can access it. + this->payload = ptr->payload; + this->size = ptr->size; + + return ret; +} + +int SrsSharedPtrMessage::count() +{ + srs_assert(ptr); + return ptr->shared_count; +} + +SrsSharedPtrMessage* SrsSharedPtrMessage::copy() +{ + srs_assert(ptr); + + SrsSharedPtrMessage* copy = new SrsSharedPtrMessage(); + + copy->header = header; + + copy->ptr = ptr; + ptr->shared_count++; + + copy->payload = ptr->payload; + copy->size = ptr->size; + + return copy; } SrsProtocol::AckWindowSize::AckWindowSize() @@ -541,14 +639,14 @@ int64_t SrsProtocol::get_send_bytes() return skt->get_send_bytes(); } -int SrsProtocol::recv_message(SrsMessage** pmsg) +int SrsProtocol::recv_message(SrsCommonMessage** pmsg) { *pmsg = NULL; int ret = ERROR_SUCCESS; while (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; if ((ret = recv_interlaced_message(&msg)) != ERROR_SUCCESS) { if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) { @@ -587,7 +685,7 @@ int SrsProtocol::recv_message(SrsMessage** pmsg) return ret; } -int SrsProtocol::decode_message(SrsMessage* msg, SrsPacket** ppacket) +int SrsProtocol::decode_message(SrsCommonMessage* msg, SrsPacket** ppacket) { *ppacket = NULL; @@ -620,7 +718,7 @@ int SrsProtocol::decode_message(SrsMessage* msg, SrsPacket** ppacket) return ret; } -int SrsProtocol::do_send_messages(SrsMessage** msgs, int nb_msgs) +int SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs) { int ret = ERROR_SUCCESS; @@ -634,7 +732,7 @@ int SrsProtocol::do_send_messages(SrsMessage** msgs, int nb_msgs) // try to send use the c0c3 header cache, // if cache is consumed, try another loop. for (int i = 0; i < nb_msgs; i++) { - SrsMessage* msg = msgs[i]; + SrsSharedPtrMessage* msg = msgs[i]; // ignore empty message. if (!msg->payload || msg->size <= 0) { @@ -785,22 +883,23 @@ int SrsProtocol::do_send_and_free_packet(SrsPacket* packet, int stream_id) } // to message - SrsMessage* msg = new SrsCommonMessage(); - - msg->payload = payload; - msg->size = size; - - msg->header.payload_length = size; - msg->header.message_type = packet->get_message_type(); - msg->header.stream_id = stream_id; - msg->header.perfer_cid = packet->get_prefer_cid(); + SrsMessageHeader header; + header.payload_length = size; + header.message_type = packet->get_message_type(); + header.stream_id = stream_id; + header.perfer_cid = packet->get_prefer_cid(); - // donot use the auto free to free the msg, - // for performance issue. - ret = do_send_messages(&msg, 1); + SrsSharedPtrMessage* msg = new SrsSharedPtrMessage(); + ret = msg->create(&header, payload, size); if (ret == ERROR_SUCCESS) { - ret = on_send_packet(msg, packet); + ret = do_send_messages(&msg, 1); + if (ret == ERROR_SUCCESS) { + ret = on_send_packet(msg, packet); + } } + + // donot use the auto free to free the msg, + // for performance issue. srs_freep(msg); return ret; @@ -1054,12 +1153,12 @@ int SrsProtocol::do_decode_message(SrsMessageHeader& header, SrsStream* stream, return ret; } -int SrsProtocol::send_and_free_message(SrsMessage* msg, int stream_id) +int SrsProtocol::send_and_free_message(SrsSharedPtrMessage* msg, int stream_id) { return send_and_free_messages(&msg, 1, stream_id); } -int SrsProtocol::send_and_free_messages(SrsMessage** msgs, int nb_msgs, int stream_id) +int SrsProtocol::send_and_free_messages(SrsSharedPtrMessage** msgs, int nb_msgs, int stream_id) { // always not NULL msg. srs_assert(msgs); @@ -1067,7 +1166,7 @@ int SrsProtocol::send_and_free_messages(SrsMessage** msgs, int nb_msgs, int stre // update the stream id in header. for (int i = 0; i < nb_msgs; i++) { - SrsMessage* msg = msgs[i]; + SrsSharedPtrMessage* msg = msgs[i]; // we assume that the stream_id in a group must be the same. if (msg->header.stream_id == stream_id) { break; @@ -1080,7 +1179,7 @@ int SrsProtocol::send_and_free_messages(SrsMessage** msgs, int nb_msgs, int stre int ret = do_send_messages(msgs, nb_msgs); for (int i = 0; i < nb_msgs; i++) { - SrsMessage* msg = msgs[i]; + SrsSharedPtrMessage* msg = msgs[i]; srs_freep(msg); } @@ -1113,7 +1212,7 @@ int SrsProtocol::send_and_free_packet(SrsPacket* packet, int stream_id) return ret; } -int SrsProtocol::recv_interlaced_message(SrsMessage** pmsg) +int SrsProtocol::recv_interlaced_message(SrsCommonMessage** pmsg) { int ret = ERROR_SUCCESS; @@ -1173,7 +1272,7 @@ int SrsProtocol::recv_interlaced_message(SrsMessage** pmsg) chunk->header.payload_length, chunk->header.timestamp, chunk->header.stream_id); // read msg payload from chunk stream. - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; if ((ret = read_message_payload(chunk, &msg)) != ERROR_SUCCESS) { if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) { srs_error("read message payload failed. ret=%d", ret); @@ -1591,7 +1690,7 @@ int SrsProtocol::read_message_header(SrsChunkStream* chunk, char fmt) return ret; } -int SrsProtocol::read_message_payload(SrsChunkStream* chunk, SrsMessage** pmsg) +int SrsProtocol::read_message_payload(SrsChunkStream* chunk, SrsCommonMessage** pmsg) { int ret = ERROR_SUCCESS; @@ -1650,7 +1749,7 @@ int SrsProtocol::read_message_payload(SrsChunkStream* chunk, SrsMessage** pmsg) return ret; } -int SrsProtocol::on_recv_message(SrsMessage* msg) +int SrsProtocol::on_recv_message(SrsCommonMessage* msg) { int ret = ERROR_SUCCESS; @@ -1743,7 +1842,7 @@ int SrsProtocol::on_recv_message(SrsMessage* msg) return ret; } -int SrsProtocol::on_send_packet(SrsMessage* msg, SrsPacket* packet) +int SrsProtocol::on_send_packet(SrsSharedPtrMessage* msg, SrsPacket* packet) { int ret = ERROR_SUCCESS; @@ -1859,112 +1958,6 @@ SrsChunkStream::~SrsChunkStream() srs_freep(msg); } -SrsCommonMessage::SrsCommonMessage() -{ -} - -SrsCommonMessage::~SrsCommonMessage() -{ - srs_freep(payload); -} - -SrsSharedPtrMessage::__SrsSharedPtr::__SrsSharedPtr() -{ - payload = NULL; - size = 0; - shared_count = 0; -} - -SrsSharedPtrMessage::__SrsSharedPtr::~__SrsSharedPtr() -{ - srs_freep(payload); -} - -SrsSharedPtrMessage::SrsSharedPtrMessage() -{ - ptr = NULL; -} - -SrsSharedPtrMessage::~SrsSharedPtrMessage() -{ - if (ptr) { - if (ptr->shared_count == 0) { - srs_freep(ptr); - } else { - ptr->shared_count--; - } - } -} - -int SrsSharedPtrMessage::create(SrsMessage* msg) -{ - int ret = ERROR_SUCCESS; - - if ((ret = create(&msg->header, msg->payload, msg->size)) != ERROR_SUCCESS) { - return ret; - } - - // to prevent double free of payload: - // initialize already attach the payload of msg, - // detach the payload to transfer the owner to shared ptr. - msg->payload = NULL; - msg->size = 0; - - return ret; -} - -int SrsSharedPtrMessage::create(SrsMessageHeader* pheader, char* payload, int size) -{ - int ret = ERROR_SUCCESS; - - srs_assert(pheader != NULL); - if (ptr) { - ret = ERROR_SYSTEM_ASSERT_FAILED; - srs_error("should not set the payload twice. ret=%d", ret); - srs_assert(false); - - return ret; - } - - header = *pheader; - header.payload_length = size; - - ptr = new __SrsSharedPtr(); - - // direct attach the data. - ptr->payload = payload; - ptr->size = size; - - // message can access it. - SrsMessage::payload = ptr->payload; - SrsMessage::size = ptr->size; - - return ret; -} - -int SrsSharedPtrMessage::count() -{ - srs_assert(ptr); - return ptr->shared_count; -} - -SrsSharedPtrMessage* SrsSharedPtrMessage::copy() -{ - srs_assert(ptr); - - SrsSharedPtrMessage* copy = new SrsSharedPtrMessage(); - - copy->header = header; - - copy->ptr = ptr; - ptr->shared_count++; - - copy->payload = ptr->payload; - copy->size = ptr->size; - - return copy; -} - SrsPacket::SrsPacket() { } diff --git a/trunk/src/rtmp/srs_protocol_stack.hpp b/trunk/src/rtmp/srs_protocol_stack.hpp index 72d30a11db..b99f359c31 100644 --- a/trunk/src/rtmp/srs_protocol_stack.hpp +++ b/trunk/src/rtmp/srs_protocol_stack.hpp @@ -51,7 +51,7 @@ class SrsStream; class SrsAmf0Object; class SrsAmf0Any; class SrsMessageHeader; -class SrsMessage; +class SrsCommonMessage; class SrsChunkStream; class SrsSharedPtrMessage; class IMergeReadHandler; @@ -136,15 +136,10 @@ class SrsMessageHeader /** * message is raw data RTMP message, bytes oriented, * protcol always recv RTMP message, and can send RTMP message or RTMP packet. -* the shared-ptr message is a special RTMP message, use ref-count for performance issue. -* -* @remark, never directly new SrsMessage, the constructor is protected, -* for in the SrsMessage, we never know whether we should free the message, -* for SrsCommonMessage, we should free the payload, -* while for SrsSharedPtrMessage, we should use ref-count to free it. -* so, use these two concrete message, SrsCommonMessage or SrsSharedPtrMessage instread. +* the common message is read from underlay protocol sdk. +* while the shared ptr message used to copy and send. */ -class SrsMessage +class SrsCommonMessage { // 4.1. Message Header public: @@ -158,16 +153,90 @@ class SrsMessage */ int size; /** - * the payload of message, the SrsMessage never know about the detail of payload, + * the payload of message, the SrsCommonMessage never know about the detail of payload, * user must use SrsProtocol.decode_message to get concrete packet. * @remark, not all message payload can be decoded to packet. for example, * video/audio packet use raw bytes, no video/audio packet. */ char* payload; -protected: - SrsMessage(); public: - virtual ~SrsMessage(); + SrsCommonMessage(); +public: + virtual ~SrsCommonMessage(); +}; + +/** +* shared ptr message. +* for audio/video/data message that need less memory copy. +* and only for output. +* +* create first object by constructor and create(), +* use copy if need reference count message. +* +*/ +class SrsSharedPtrMessage +{ +// 4.1. Message Header +public: + SrsMessageHeader header; +// 4.2. Message Payload +public: + /** + * current message parsed size, + * size <= header.payload_length + * for the payload maybe sent in multiple chunks. + */ + int size; + /** + * the payload of message, the SrsCommonMessage never know about the detail of payload, + * user must use SrsProtocol.decode_message to get concrete packet. + * @remark, not all message payload can be decoded to packet. for example, + * video/audio packet use raw bytes, no video/audio packet. + */ + char* payload; +private: + class __SrsSharedPtr + { + public: + char* payload; + int size; + int shared_count; + + __SrsSharedPtr(); + virtual ~__SrsSharedPtr(); + }; + __SrsSharedPtr* ptr; +public: + SrsSharedPtrMessage(); + virtual ~SrsSharedPtrMessage(); +public: + /** + * create shared ptr message, + * copy header, manage the payload of msg, + * set the payload to NULL to prevent double free. + * @remark payload of msg set to NULL if success. + */ + virtual int create(SrsCommonMessage* msg); + /** + * create shared ptr message, + * from the header and payload. + * @remark user should never free the payload. + */ + virtual int create(SrsMessageHeader* pheader, char* payload, int size); + /** + * get current reference count. + * when this object created, count set to 0. + * if copy() this object, count increase 1. + * if this or copy deleted, free payload when count is 0, or count--. + * @remark, assert object is created. + */ + virtual int count(); +public: + /** + * copy current shared ptr message, use ref-count. + * @remark, assert object is created. + */ + virtual SrsSharedPtrMessage* copy(); }; /** @@ -321,14 +390,14 @@ class SrsProtocol * never NULL if decode success. * @remark, drop message when msg is empty or payload length is empty. */ - virtual int recv_message(SrsMessage** pmsg); + virtual int recv_message(SrsCommonMessage** pmsg); /** * decode bytes oriented RTMP message to RTMP packet, * @param ppacket, output decoded packet, * always NULL if error, never NULL if success. * @return error when unknown packet, error when decode failed. */ - virtual int decode_message(SrsMessage* msg, SrsPacket** ppacket); + virtual int decode_message(SrsCommonMessage* msg, SrsPacket** ppacket); /** * send the RTMP message and always free it. * user must never free or use the msg after this method, @@ -336,7 +405,7 @@ class SrsProtocol * @param msg, the msg to send out, never be NULL. * @param stream_id, the stream id of packet to send over, 0 for control message. */ - virtual int send_and_free_message(SrsMessage* msg, int stream_id); + virtual int send_and_free_message(SrsSharedPtrMessage* msg, int stream_id); /** * send the RTMP message and always free it. * user must never free or use the msg after this method, @@ -345,7 +414,7 @@ class SrsProtocol * @param nb_msgs, the size of msgs to send out. * @param stream_id, the stream id of packet to send over, 0 for control message. */ - virtual int send_and_free_messages(SrsMessage** msgs, int nb_msgs, int stream_id); + virtual int send_and_free_messages(SrsSharedPtrMessage** msgs, int nb_msgs, int stream_id); /** * send the RTMP packet and always free it. * user must never free or use the packet after this method, @@ -371,7 +440,7 @@ class SrsProtocol * if need to set timeout, use set timeout of SrsProtocol. */ template - int expect_message(SrsMessage** pmsg, T** ppacket) + int expect_message(SrsCommonMessage** pmsg, T** ppacket) { *pmsg = NULL; *ppacket = NULL; @@ -379,7 +448,7 @@ class SrsProtocol int ret = ERROR_SUCCESS; while (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; if ((ret = recv_message(&msg)) != ERROR_SUCCESS) { if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) { srs_error("recv message failed. ret=%d", ret); @@ -418,7 +487,7 @@ class SrsProtocol * send out the messages, donot free it, * the caller must free the param msgs. */ - virtual int do_send_messages(SrsMessage** msgs, int nb_msgs); + virtual int do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs); /** * underlayer api for send and free packet. */ @@ -442,7 +511,7 @@ class SrsProtocol * return success and pmsg set to NULL if no entire message got, * return success and pmsg set to entire message if got one. */ - virtual int recv_interlaced_message(SrsMessage** pmsg); + virtual int recv_interlaced_message(SrsCommonMessage** pmsg); /** * read the chunk basic header(fmt, cid) from chunk stream. * user can discovery a SrsChunkStream by cid. @@ -457,15 +526,15 @@ class SrsProtocol * read the chunk payload, remove the used bytes in buffer, * if got entire message, set the pmsg. */ - virtual int read_message_payload(SrsChunkStream* chunk, SrsMessage** pmsg); + virtual int read_message_payload(SrsChunkStream* chunk, SrsCommonMessage** pmsg); /** * when recv message, update the context. */ - virtual int on_recv_message(SrsMessage* msg); + virtual int on_recv_message(SrsCommonMessage* msg); /** * when message sentout, update the context. */ - virtual int on_send_packet(SrsMessage* msg, SrsPacket* packet); + virtual int on_send_packet(SrsSharedPtrMessage* msg, SrsPacket* packet); private: /** * auto response the ack message. @@ -505,7 +574,7 @@ class SrsChunkStream /** * partially read message. */ - SrsMessage* msg; + SrsCommonMessage* msg; /** * decoded msg count, to identify whether the chunk stream is fresh. */ @@ -515,75 +584,6 @@ class SrsChunkStream virtual ~SrsChunkStream(); }; -/** -* the common message used free the payload in common way. -*/ -class SrsCommonMessage : public SrsMessage -{ -public: - SrsCommonMessage(); - virtual ~SrsCommonMessage(); -}; - -/** -* shared ptr message. -* for audio/video/data message that need less memory copy. -* and only for output. -* -* create first object by constructor and create(), -* use copy if need reference count message. -* -* Usage: -* SrsSharedPtrMessage msg; -* -*/ -class SrsSharedPtrMessage : public SrsMessage -{ -private: - class __SrsSharedPtr - { - public: - char* payload; - int size; - int shared_count; - - __SrsSharedPtr(); - virtual ~__SrsSharedPtr(); - }; - __SrsSharedPtr* ptr; -public: - SrsSharedPtrMessage(); - virtual ~SrsSharedPtrMessage(); -public: - /** - * create shared ptr message, - * copy header, manage the payload of msg, - * set the payload to NULL to prevent double free. - * @remark payload of msg set to NULL if success. - */ - virtual int create(SrsMessage* msg); - /** - * create shared ptr message, - * from the header and payload. - * @remark user should never free the payload. - */ - virtual int create(SrsMessageHeader* pheader, char* payload, int size); - /** - * get current reference count. - * when this object created, count set to 0. - * if copy() this object, count increase 1. - * if this or copy deleted, free payload when count is 0, or count--. - * @remark, assert object is created. - */ - virtual int count(); -public: - /** - * copy current shared ptr message, use ref-count. - * @remark, assert object is created. - */ - virtual SrsSharedPtrMessage* copy(); -}; - /** * the decoded message payload. * @remark we seperate the packet from message, @@ -1227,7 +1227,7 @@ class SrsOnBWDonePacket : public SrsPacket /** * onStatus command, AMF0 Call -* @remark, user must set the stream_id by SrsMessage.set_packet(). +* @remark, user must set the stream_id by SrsCommonMessage.set_packet(). */ class SrsOnStatusCallPacket : public SrsPacket { @@ -1336,7 +1336,7 @@ class SrsBandwidthPacket : public SrsPacket /** * onStatus data, AMF0 Data -* @remark, user must set the stream_id by SrsMessage.set_packet(). +* @remark, user must set the stream_id by SrsCommonMessage.set_packet(). */ class SrsOnStatusDataPacket : public SrsPacket { @@ -1365,7 +1365,7 @@ class SrsOnStatusDataPacket : public SrsPacket /** * AMF0Data RtmpSampleAccess -* @remark, user must set the stream_id by SrsMessage.set_packet(). +* @remark, user must set the stream_id by SrsCommonMessage.set_packet(). */ class SrsSampleAccessPacket : public SrsPacket { diff --git a/trunk/src/utest/srs_utest_protocol.cpp b/trunk/src/utest/srs_utest_protocol.cpp index 98907684b2..10afb87f4d 100644 --- a/trunk/src/utest/srs_utest_protocol.cpp +++ b/trunk/src/utest/srs_utest_protocol.cpp @@ -635,9 +635,9 @@ VOID TEST(ProtocolStackTest, ProtocolRecvMessage) }; bio.in_buffer.append(data, sizeof(data)); - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); SrsPacket* pkt = NULL; EXPECT_TRUE(ERROR_SUCCESS == proto.decode_message(msg, &pkt)); @@ -671,9 +671,9 @@ VOID TEST(ProtocolStackTest, ProtocolRecvMessageBug98) }; bio.in_buffer.append(data, sizeof(data)); - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); SrsPacket* pkt = NULL; EXPECT_TRUE(ERROR_SUCCESS == proto.decode_message(msg, &pkt)); @@ -706,9 +706,9 @@ VOID TEST(ProtocolStackTest, ProtocolRecvAckSizeMessage) }; bio.in_buffer.append(data, sizeof(data)); - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); SrsPacket* pkt = NULL; EXPECT_TRUE(ERROR_SUCCESS == proto.decode_message(msg, &pkt)); @@ -740,9 +740,9 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVMessage) }; bio.in_buffer.append(data, sizeof(data)); - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); } @@ -767,9 +767,9 @@ VOID TEST(ProtocolStackTest, ProtocolRecvAMessage) }; bio.in_buffer.append(data, sizeof(data)); - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_audio()); } @@ -813,9 +813,9 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVMessage2Trunk) }; bio.in_buffer.append(data, sizeof(data)); - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); } @@ -904,15 +904,15 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVAMessage) } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_audio()); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); } } @@ -1022,15 +1022,15 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVAFmt1) } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_audio()); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); } } @@ -1138,15 +1138,15 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVAFmt2) } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_audio()); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); } } @@ -1253,15 +1253,15 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVAFmt3) } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_audio()); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); } } @@ -1396,25 +1396,25 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVAVMessage) } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x10, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_audio()); EXPECT_EQ(0x15, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x20, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); @@ -1564,25 +1564,25 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVAVFmt1) } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x10, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_audio()); EXPECT_EQ(0x15, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x22, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); @@ -1730,25 +1730,25 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVAVFmt2) } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x10, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_audio()); EXPECT_EQ(0x15, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x22, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); @@ -1895,25 +1895,25 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVAVFmt3) } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x10, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_audio()); EXPECT_EQ(0x15, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x20, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); @@ -2095,33 +2095,33 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVAVVMessage) } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x10, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_audio()); EXPECT_EQ(0x15, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x20, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x30, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); @@ -2315,33 +2315,33 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVAVVFmt1) } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x10, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_audio()); EXPECT_EQ(0x15, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x20, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x30, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); @@ -2531,33 +2531,33 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVAVVFmt2) } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x10, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_audio()); EXPECT_EQ(0x15, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x20, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x30, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); @@ -2745,33 +2745,33 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVAVVFmt3) } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x10, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_audio()); EXPECT_EQ(0x15, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x20, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x30, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); @@ -2965,33 +2965,33 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVAVVFmt11) } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x10, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_audio()); EXPECT_EQ(0x15, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x20, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x40, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); @@ -3187,33 +3187,33 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVAVVFmt11Length) } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x10, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_audio()); EXPECT_EQ(0x15, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x20, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x40, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); @@ -3405,33 +3405,33 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVAVVFmt12) } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x10, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_audio()); EXPECT_EQ(0x15, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x20, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x40, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); @@ -3626,35 +3626,35 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVAVVFmt12Length) } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x10, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); EXPECT_EQ(0x110, msg->header.payload_length); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_audio()); EXPECT_EQ(0x15, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x20, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); EXPECT_EQ(0x120, msg->header.payload_length); } if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x40, msg->header.timestamp); EXPECT_EQ(0x01, msg->header.stream_id); @@ -3697,9 +3697,9 @@ VOID TEST(ProtocolStackTest, ProtocolRecvExtTimeMessage) }; bio.in_buffer.append(data, sizeof(data)); - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x10, msg->header.timestamp); } @@ -3739,9 +3739,9 @@ VOID TEST(ProtocolStackTest, ProtocolRecvExtTimeMessage2) }; bio.in_buffer.append(data, sizeof(data)); - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); EXPECT_EQ(0x7f010203, msg->header.timestamp); } @@ -3783,9 +3783,9 @@ VOID TEST(ProtocolStackTest, ProtocolRecvExtTimeMessage3) }; bio.in_buffer.append(data, sizeof(data)); - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); // always use 31bits timestamp EXPECT_EQ(0x7f010203, msg->header.timestamp); @@ -3856,9 +3856,9 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVExtTime2Trunk) }; bio.in_buffer.append(data, sizeof(data)); - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); // 0xCX with extended timestamp. EXPECT_EQ(0x00010203, msg->header.timestamp); @@ -3911,9 +3911,9 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVExtTime2Trunk2) }; bio.in_buffer.append(data, sizeof(data)); - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); // 0xCX without extended timestamp. EXPECT_EQ(0x00010203, msg->header.timestamp); @@ -3962,9 +3962,9 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVCid1BMin) }; bio.in_buffer.append(data, sizeof(data)); - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); // 1B cid(6bits), min is 2 EXPECT_EQ(0x02, msg->header.perfer_cid); @@ -4013,9 +4013,9 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVCid1BNormal) }; bio.in_buffer.append(data, sizeof(data)); - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); // 1B cid(6bits), cid in 2-63 EXPECT_EQ(0x09, msg->header.perfer_cid); @@ -4064,9 +4064,9 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVCid1BMax) }; bio.in_buffer.append(data, sizeof(data)); - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); // 1B cid(6bits), max is 63 EXPECT_EQ(0x3F, msg->header.perfer_cid); @@ -4115,9 +4115,9 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVCid2BMin) }; bio.in_buffer.append(data, sizeof(data)); - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); // 2B cid(8bits), min is 64 EXPECT_EQ(64, msg->header.perfer_cid); @@ -4166,9 +4166,9 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVCid2BNormal) }; bio.in_buffer.append(data, sizeof(data)); - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); // 2B cid(8bits), cid in 64-319 EXPECT_EQ(0x10+64, msg->header.perfer_cid); @@ -4217,9 +4217,9 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVCid2BNormal2) }; bio.in_buffer.append(data, sizeof(data)); - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); // 2B cid(8bits), cid in 64-319 EXPECT_EQ(0x11+64, msg->header.perfer_cid); @@ -4268,9 +4268,9 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVCid2BMax) }; bio.in_buffer.append(data, sizeof(data)); - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); // 2B cid(68bits), max is 319 EXPECT_EQ(319, msg->header.perfer_cid); @@ -4319,9 +4319,9 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVCid3BMin) }; bio.in_buffer.append(data, sizeof(data)); - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); // 3B cid(16bits), min is 64 EXPECT_EQ(64, msg->header.perfer_cid); @@ -4370,9 +4370,9 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVCid3BNormal) }; bio.in_buffer.append(data, sizeof(data)); - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); // 3B cid(16bits), cid in 64-65599 EXPECT_EQ(0x10*256+64, msg->header.perfer_cid); @@ -4421,9 +4421,9 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVCid3BNormal2) }; bio.in_buffer.append(data, sizeof(data)); - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); // 3B cid(16bits), cid in 64-65599 EXPECT_EQ(0x01 + (0x10*256) + 64, msg->header.perfer_cid); @@ -4472,9 +4472,9 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVCid3BNormal3) }; bio.in_buffer.append(data, sizeof(data)); - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); // 3B cid(16bits), cid in 64-65599 EXPECT_EQ(0xFF + (0x10*256) + 64, msg->header.perfer_cid); @@ -4523,9 +4523,9 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVCid3BNormal4) }; bio.in_buffer.append(data, sizeof(data)); - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); // 3B cid(16bits), cid in 64-65599 EXPECT_EQ(0x02 + (0x10*256) + 64, msg->header.perfer_cid); @@ -4574,9 +4574,9 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVCid3BMax) }; bio.in_buffer.append(data, sizeof(data)); - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); // 2B cid(16bits), max is 65599 EXPECT_EQ(65599, msg->header.perfer_cid); @@ -4612,9 +4612,9 @@ VOID TEST(ProtocolStackTest, ProtocolRecvV0LenMessage) }; bio.in_buffer.append(data, sizeof(data)); - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_video()); // protocol stack will ignore the empty video message. EXPECT_EQ(4, msg->header.payload_length); @@ -4630,7 +4630,7 @@ VOID TEST(ProtocolStackTest, ProtocolSendVMessage) char data[] = {0x01, 0x02, 0x03, 0x04}; - SrsMessage* msg = new SrsCommonMessage(); + SrsCommonMessage* msg = new SrsCommonMessage(); msg->size = sizeof(data); msg->payload = new char[msg->size]; memcpy(msg->payload, data, msg->size); @@ -5320,7 +5320,7 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVMessageFmtInvalid) }; bio.in_buffer.append(data, sizeof(data)); - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; EXPECT_FALSE(ERROR_SUCCESS == proto.recv_message(&msg)); } @@ -5340,7 +5340,7 @@ VOID TEST(ProtocolStackTest, ProtocolAckSizeFlow) } if (true) { - SrsMessage* msg = new SrsCommonMessage(); + SrsCommonMessage* msg = new SrsCommonMessage(); msg->header.payload_length = msg->size = 4096; msg->payload = new char[msg->size]; @@ -5358,16 +5358,16 @@ VOID TEST(ProtocolStackTest, ProtocolAckSizeFlow) // recv SrsSetWindowAckSizePacket if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); ASSERT_TRUE(msg->header.is_window_ackledgement_size()); } // recv video if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); ASSERT_TRUE(msg->header.is_video()); } @@ -5378,15 +5378,15 @@ VOID TEST(ProtocolStackTest, ProtocolAckSizeFlow) } // recv auto send acked size. #1 if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); ASSERT_TRUE(msg->header.is_ackledgement()); } // send again if (true) { - SrsMessage* msg = new SrsCommonMessage(); + SrsCommonMessage* msg = new SrsCommonMessage(); msg->header.payload_length = msg->size = 4096; msg->payload = new char[msg->size]; @@ -5402,9 +5402,9 @@ VOID TEST(ProtocolStackTest, ProtocolAckSizeFlow) } // recv video if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); ASSERT_TRUE(msg->header.is_video()); } @@ -5415,9 +5415,9 @@ VOID TEST(ProtocolStackTest, ProtocolAckSizeFlow) } // recv auto send acked size. #2 if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); ASSERT_TRUE(msg->header.is_ackledgement()); } } @@ -5444,9 +5444,9 @@ VOID TEST(ProtocolStackTest, ProtocolPingFlow) } // recv ping if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); EXPECT_TRUE(msg->header.is_user_control_message()); } @@ -5458,9 +5458,9 @@ VOID TEST(ProtocolStackTest, ProtocolPingFlow) } // recv ping if (true) { - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); ASSERT_TRUE(msg->header.is_user_control_message()); SrsPacket* pkt = NULL; @@ -5519,10 +5519,10 @@ VOID TEST(ProtocolStackTest, ProtocolExcpectMessage) }; bio.in_buffer.append(data, sizeof(data)); - SrsMessage* msg = NULL; + SrsCommonMessage* msg = NULL; SrsConnectAppPacket* pkt = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.expect_message(&msg, &pkt)); - SrsAutoFree(SrsMessage, msg); + SrsAutoFree(SrsCommonMessage, msg); SrsAutoFree(SrsConnectAppPacket, pkt); ASSERT_TRUE(NULL != pkt); } From d3a103184aefb20771a499f7bad72a77a6c7e2b6 Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 5 Dec 2014 23:23:11 +0800 Subject: [PATCH 333/800] refine protocol sdk send msg, the chunk header generate. --- trunk/src/rtmp/srs_protocol_stack.cpp | 27 ++++++++++----------------- trunk/src/rtmp/srs_protocol_stack.hpp | 6 ++---- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp index 1282534747..5589ecd049 100644 --- a/trunk/src/rtmp/srs_protocol_stack.cpp +++ b/trunk/src/rtmp/srs_protocol_stack.cpp @@ -755,17 +755,10 @@ int SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs) // always write the header event payload is empty. while (p < pend) { - // always has header - int nbh = 0; - char* header = NULL; - generate_chunk_header(c0c3_cache, &msg->header, p == msg->payload, &nbh, &header); - srs_assert(nbh > 0); + // header use iov[0]. + generate_chunk_header(c0c3_cache, &msg->header, p == msg->payload, iov); - // header iov - iov[0].iov_base = header; - iov[0].iov_len = nbh; - - // payload iov + // payload use iov[1]. int payload_size = pend - p; if (payload_size > out_chunk_size) { payload_size = out_chunk_size; @@ -788,14 +781,14 @@ int SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs) int realloc_size = sizeof(iovec) * nb_out_iovs; out_iovs = (iovec*)realloc(out_iovs, realloc_size); } + + // to next c0c3 header cache + c0c3_cache_index += iov[0].iov_len; + c0c3_cache = out_c0c3_caches + c0c3_cache_index; // to next pair of iovs iov_index += 2; iov = out_iovs + iov_index; - - // to next c0c3 header cache - c0c3_cache_index += nbh; - c0c3_cache = out_c0c3_caches + c0c3_cache_index; // the cache header should never be realloc again, // for the ptr is set to iovs, so we just warn user to set larger @@ -905,7 +898,7 @@ int SrsProtocol::do_send_and_free_packet(SrsPacket* packet, int stream_id) return ret; } -void SrsProtocol::generate_chunk_header(char* cache, SrsMessageHeader* mh, bool c0, int* pnbh, char** ph) +void SrsProtocol::generate_chunk_header(char* cache, SrsMessageHeader* mh, bool c0, iovec* iov) { // to directly set the field. char* pp = NULL; @@ -982,8 +975,8 @@ void SrsProtocol::generate_chunk_header(char* cache, SrsMessageHeader* mh, bool } // always has header - *pnbh = p - cache; - *ph = cache; + iov->iov_base = cache; + iov->iov_len = p - cache; } int SrsProtocol::do_decode_message(SrsMessageHeader& header, SrsStream* stream, SrsPacket** ppacket) diff --git a/trunk/src/rtmp/srs_protocol_stack.hpp b/trunk/src/rtmp/srs_protocol_stack.hpp index b99f359c31..e19a18c96d 100644 --- a/trunk/src/rtmp/srs_protocol_stack.hpp +++ b/trunk/src/rtmp/srs_protocol_stack.hpp @@ -496,11 +496,9 @@ class SrsProtocol * generate the chunk header for msg. * @param mh, the header of msg to send. * @param c0, whether the first chunk, the c0 chunk. - * @param pnbh, output the size of header. - * @param ph, output the header cache. - * user should never free it, it's cached header. + * @param iov, output the header and size to iovec. */ - virtual void generate_chunk_header(char* cache, SrsMessageHeader* mh, bool c0, int* pnbh, char** ph); + virtual void generate_chunk_header(char* cache, SrsMessageHeader* mh, bool c0, iovec* iov); /** * imp for decode_message */ From de993b646511ed8a825914a743a6a9e055c462fa Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 5 Dec 2014 23:49:53 +0800 Subject: [PATCH 334/800] for bug #251, refine the directly access ptrs for hls,dvr,forwarder,consumer. --- trunk/src/app/srs_app_dvr.cpp | 34 +++++++------------- trunk/src/app/srs_app_dvr.hpp | 22 ++++++++++--- trunk/src/app/srs_app_forward.cpp | 12 +++++-- trunk/src/app/srs_app_forward.hpp | 18 +++++++++-- trunk/src/app/srs_app_hls.cpp | 14 +++++---- trunk/src/app/srs_app_hls.hpp | 6 ++-- trunk/src/app/srs_app_source.cpp | 52 ++++++++++++++++--------------- trunk/src/app/srs_app_source.hpp | 6 ++-- 8 files changed, 97 insertions(+), 67 deletions(-) diff --git a/trunk/src/app/srs_app_dvr.cpp b/trunk/src/app/srs_app_dvr.cpp index d58d642a0c..0fe5ea1821 100644 --- a/trunk/src/app/srs_app_dvr.cpp +++ b/trunk/src/app/srs_app_dvr.cpp @@ -196,13 +196,16 @@ int SrsDvrPlan::on_meta_data(SrsOnMetaDataPacket* metadata) return ret; } -int SrsDvrPlan::on_audio(SrsSharedPtrMessage* audio) +int SrsDvrPlan::on_audio(SrsSharedPtrMessage* __audio) { int ret = ERROR_SUCCESS; if (!dvr_enabled) { return ret; } + + SrsSharedPtrMessage* audio = __audio->copy(); + SrsAutoFree(SrsSharedPtrMessage, audio); if ((jitter->correct(audio, 0, 0, jitter_algorithm)) != ERROR_SUCCESS) { return ret; @@ -222,13 +225,16 @@ int SrsDvrPlan::on_audio(SrsSharedPtrMessage* audio) return ret; } -int SrsDvrPlan::on_video(SrsSharedPtrMessage* video) +int SrsDvrPlan::on_video(SrsSharedPtrMessage* __video) { int ret = ERROR_SUCCESS; if (!dvr_enabled) { return ret; } + + SrsSharedPtrMessage* video = __video->copy(); + SrsAutoFree(SrsSharedPtrMessage, video); char* payload = video->payload; int size = video->size; @@ -571,30 +577,14 @@ int SrsDvr::on_meta_data(SrsOnMetaDataPacket* metadata) return ret; } -int SrsDvr::on_audio(SrsSharedPtrMessage* audio) +int SrsDvr::on_audio(SrsSharedPtrMessage* __audio) { - int ret = ERROR_SUCCESS; - - SrsAutoFree(SrsSharedPtrMessage, audio); - - if ((ret = plan->on_audio(audio)) != ERROR_SUCCESS) { - return ret; - } - - return ret; + return plan->on_audio(__audio); } -int SrsDvr::on_video(SrsSharedPtrMessage* video) +int SrsDvr::on_video(SrsSharedPtrMessage* __video) { - int ret = ERROR_SUCCESS; - - SrsAutoFree(SrsSharedPtrMessage, video); - - if ((ret = plan->on_video(video)) != ERROR_SUCCESS) { - return ret; - } - - return ret; + return plan->on_video(__video); } #endif diff --git a/trunk/src/app/srs_app_dvr.hpp b/trunk/src/app/srs_app_dvr.hpp index edea5cc75d..9f90923f48 100644 --- a/trunk/src/app/srs_app_dvr.hpp +++ b/trunk/src/app/srs_app_dvr.hpp @@ -119,8 +119,14 @@ class SrsDvrPlan : public ISrsReloadHandler virtual int on_publish(); virtual void on_unpublish() = 0; virtual int on_meta_data(SrsOnMetaDataPacket* metadata); - virtual int on_audio(SrsSharedPtrMessage* audio); - virtual int on_video(SrsSharedPtrMessage* video); + /** + * @param __audio, directly ptr, copy it if need to save it. + */ + virtual int on_audio(SrsSharedPtrMessage* __audio); + /** + * @param __video, directly ptr, copy it if need to save it. + */ + virtual int on_video(SrsSharedPtrMessage* __video); // interface ISrsReloadHandler public: virtual int on_reload_vhost_dvr(std::string vhost); @@ -166,7 +172,13 @@ class SrsDvrSegmentPlan : public SrsDvrPlan virtual int initialize(SrsSource* source, SrsRequest* req); virtual int on_publish(); virtual void on_unpublish(); + /** + * @param audio, directly ptr, copy it if need to save it. + */ virtual int on_audio(SrsSharedPtrMessage* audio); + /** + * @param video, directly ptr, copy it if need to save it. + */ virtual int on_video(SrsSharedPtrMessage* video); private: virtual int update_duration(SrsSharedPtrMessage* msg); @@ -208,12 +220,14 @@ class SrsDvr virtual int on_meta_data(SrsOnMetaDataPacket* metadata); /** * mux the audio packets to dvr. + * @param __audio, directly ptr, copy it if need to save it. */ - virtual int on_audio(SrsSharedPtrMessage* audio); + virtual int on_audio(SrsSharedPtrMessage* __audio); /** * mux the video packets to dvr. + * @param __video, directly ptr, copy it if need to save it. */ - virtual int on_video(SrsSharedPtrMessage* video); + virtual int on_video(SrsSharedPtrMessage* __video); }; #endif diff --git a/trunk/src/app/srs_app_forward.cpp b/trunk/src/app/srs_app_forward.cpp index 134fc083be..d3e20988ed 100644 --- a/trunk/src/app/srs_app_forward.cpp +++ b/trunk/src/app/srs_app_forward.cpp @@ -156,9 +156,11 @@ void SrsForwarder::on_unpublish() kbps->set_io(NULL, NULL); } -int SrsForwarder::on_meta_data(SrsSharedPtrMessage* metadata) +int SrsForwarder::on_meta_data(SrsSharedPtrMessage* __metadata) { int ret = ERROR_SUCCESS; + + SrsSharedPtrMessage* metadata = __metadata->copy(); if ((ret = jitter->correct(metadata, 0, 0, SrsRtmpJitterAlgorithmFULL)) != ERROR_SUCCESS) { srs_freep(metadata); @@ -172,10 +174,12 @@ int SrsForwarder::on_meta_data(SrsSharedPtrMessage* metadata) return ret; } -int SrsForwarder::on_audio(SrsSharedPtrMessage* msg) +int SrsForwarder::on_audio(SrsSharedPtrMessage* __audio) { int ret = ERROR_SUCCESS; + SrsSharedPtrMessage* msg = __audio->copy(); + if ((ret = jitter->correct(msg, 0, 0, SrsRtmpJitterAlgorithmFULL)) != ERROR_SUCCESS) { srs_freep(msg); return ret; @@ -193,9 +197,11 @@ int SrsForwarder::on_audio(SrsSharedPtrMessage* msg) return ret; } -int SrsForwarder::on_video(SrsSharedPtrMessage* msg) +int SrsForwarder::on_video(SrsSharedPtrMessage* __video) { int ret = ERROR_SUCCESS; + + SrsSharedPtrMessage* msg = __video->copy(); if ((ret = jitter->correct(msg, 0, 0, SrsRtmpJitterAlgorithmFULL)) != ERROR_SUCCESS) { srs_freep(msg); diff --git a/trunk/src/app/srs_app_forward.hpp b/trunk/src/app/srs_app_forward.hpp index c2fdb6f570..0c8ca24fdd 100644 --- a/trunk/src/app/srs_app_forward.hpp +++ b/trunk/src/app/srs_app_forward.hpp @@ -80,9 +80,21 @@ class SrsForwarder : public ISrsThreadHandler public: virtual int on_publish(); virtual void on_unpublish(); - virtual int on_meta_data(SrsSharedPtrMessage* metadata); - virtual int on_audio(SrsSharedPtrMessage* msg); - virtual int on_video(SrsSharedPtrMessage* msg); + /** + * forward the audio packet. + * @param __metadata, directly ptr, copy it if need to save it. + */ + virtual int on_meta_data(SrsSharedPtrMessage* __metadata); + /** + * forward the audio packet. + * @param __audio, directly ptr, copy it if need to save it. + */ + virtual int on_audio(SrsSharedPtrMessage* __audio); + /** + * forward the video packet. + * @param __video, directly ptr, copy it if need to save it. + */ + virtual int on_video(SrsSharedPtrMessage* __video); // interface ISrsThreadHandler. public: virtual int cycle(); diff --git a/trunk/src/app/srs_app_hls.cpp b/trunk/src/app/srs_app_hls.cpp index 1f0c6bba0d..d36e708c65 100644 --- a/trunk/src/app/srs_app_hls.cpp +++ b/trunk/src/app/srs_app_hls.cpp @@ -1426,15 +1426,16 @@ int SrsHls::on_meta_data(SrsAmf0Object* metadata) return ret; } -int SrsHls::on_audio(SrsSharedPtrMessage* audio) +int SrsHls::on_audio(SrsSharedPtrMessage* __audio) { int ret = ERROR_SUCCESS; - SrsAutoFree(SrsSharedPtrMessage, audio); - if (!hls_enabled) { return ret; } + + SrsSharedPtrMessage* audio = __audio->copy(); + SrsAutoFree(SrsSharedPtrMessage, audio); sample->clear(); if ((ret = codec->audio_aac_demux(audio->payload, audio->size, sample)) != ERROR_SUCCESS) { @@ -1470,15 +1471,16 @@ int SrsHls::on_audio(SrsSharedPtrMessage* audio) return ret; } -int SrsHls::on_video(SrsSharedPtrMessage* video) +int SrsHls::on_video(SrsSharedPtrMessage* __video) { int ret = ERROR_SUCCESS; - SrsAutoFree(SrsSharedPtrMessage, video); - if (!hls_enabled) { return ret; } + + SrsSharedPtrMessage* video = __video->copy(); + SrsAutoFree(SrsSharedPtrMessage, video); sample->clear(); if ((ret = codec->video_avc_demux(video->payload, video->size, sample)) != ERROR_SUCCESS) { diff --git a/trunk/src/app/srs_app_hls.hpp b/trunk/src/app/srs_app_hls.hpp index 8b77945b61..067a16822a 100644 --- a/trunk/src/app/srs_app_hls.hpp +++ b/trunk/src/app/srs_app_hls.hpp @@ -326,12 +326,14 @@ class SrsHls virtual int on_meta_data(SrsAmf0Object* metadata); /** * mux the audio packets to ts. + * @param __audio, directly ptr, copy it if need to save it. */ - virtual int on_audio(SrsSharedPtrMessage* audio); + virtual int on_audio(SrsSharedPtrMessage* __audio); /** * mux the video packets to ts. + * @param __video, directly ptr, copy it if need to save it. */ - virtual int on_video(SrsSharedPtrMessage* video); + virtual int on_video(SrsSharedPtrMessage* __video); private: virtual void hls_mux(); }; diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index cec809e648..21ea77f317 100644 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -332,10 +332,12 @@ int SrsConsumer::get_time() return jitter->get_time(); } -int SrsConsumer::enqueue(SrsSharedPtrMessage* msg, bool atc, int tba, int tbv, SrsRtmpJitterAlgorithm ag) +int SrsConsumer::enqueue(SrsSharedPtrMessage* __msg, bool atc, int tba, int tbv, SrsRtmpJitterAlgorithm ag) { int ret = ERROR_SUCCESS; + SrsSharedPtrMessage* msg = __msg->copy(); + if (!atc) { if ((ret = jitter->correct(msg, tba, tbv, ag)) != ERROR_SUCCESS) { srs_freep(msg); @@ -409,7 +411,7 @@ void SrsGopCache::set(bool enabled) srs_info("enable gop cache"); } -int SrsGopCache::cache(SrsSharedPtrMessage* msg) +int SrsGopCache::cache(SrsSharedPtrMessage* __msg) { int ret = ERROR_SUCCESS; @@ -418,6 +420,9 @@ int SrsGopCache::cache(SrsSharedPtrMessage* msg) return ret; } + // the gop cache know when to gop it. + SrsSharedPtrMessage* msg = __msg; + // disable gop cache when not h.264 if (!SrsFlvCodec::video_is_h264(msg->payload, msg->size)) { srs_info("gop donot cache video for none h.264"); @@ -840,15 +845,15 @@ int SrsSource::on_forwarder_start(SrsForwarder* forwarder) // feed the forwarder the metadata/sequence header, // when reload to enable the forwarder. - if (cache_metadata && (ret = forwarder->on_meta_data(cache_metadata->copy())) != ERROR_SUCCESS) { + if (cache_metadata && (ret = forwarder->on_meta_data(cache_metadata)) != ERROR_SUCCESS) { srs_error("forwarder process onMetaData message failed. ret=%d", ret); return ret; } - if (cache_sh_video && (ret = forwarder->on_video(cache_sh_video->copy())) != ERROR_SUCCESS) { + if (cache_sh_video && (ret = forwarder->on_video(cache_sh_video)) != ERROR_SUCCESS) { srs_error("forwarder process video sequence header message failed. ret=%d", ret); return ret; } - if (cache_sh_audio && (ret = forwarder->on_audio(cache_sh_audio->copy())) != ERROR_SUCCESS) { + if (cache_sh_audio && (ret = forwarder->on_audio(cache_sh_audio)) != ERROR_SUCCESS) { srs_error("forwarder process audio sequence header message failed. ret=%d", ret); return ret; } @@ -865,11 +870,11 @@ int SrsSource::on_hls_start() // when reload to start hls, hls will never get the sequence header in stream, // use the SrsSource.on_hls_start to push the sequence header to HLS. // TODO: maybe need to decode the metadata? - if (cache_sh_video && (ret = hls->on_video(cache_sh_video->copy())) != ERROR_SUCCESS) { + if (cache_sh_video && (ret = hls->on_video(cache_sh_video)) != ERROR_SUCCESS) { srs_error("hls process video sequence header message failed. ret=%d", ret); return ret; } - if (cache_sh_audio && (ret = hls->on_audio(cache_sh_audio->copy())) != ERROR_SUCCESS) { + if (cache_sh_audio && (ret = hls->on_audio(cache_sh_audio)) != ERROR_SUCCESS) { srs_error("hls process audio sequence header message failed. ret=%d", ret); return ret; } @@ -908,11 +913,11 @@ int SrsSource::on_dvr_request_sh() } } - if (cache_sh_video && (ret = dvr->on_video(cache_sh_video->copy())) != ERROR_SUCCESS) { + if (cache_sh_video && (ret = dvr->on_video(cache_sh_video)) != ERROR_SUCCESS) { srs_error("dvr process video sequence header message failed. ret=%d", ret); return ret; } - if (cache_sh_audio && (ret = dvr->on_audio(cache_sh_audio->copy())) != ERROR_SUCCESS) { + if (cache_sh_audio && (ret = dvr->on_audio(cache_sh_audio)) != ERROR_SUCCESS) { srs_error("dvr process audio sequence header message failed. ret=%d", ret); return ret; } @@ -1048,8 +1053,7 @@ int SrsSource::on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* metadata std::vector::iterator it; for (it = consumers.begin(); it != consumers.end(); ++it) { SrsConsumer* consumer = *it; - SrsSharedPtrMessage* copy = cache_metadata->copy(); - if ((ret = consumer->enqueue(copy, atc, sample_rate, frame_rate, jitter_algorithm)) != ERROR_SUCCESS) { + if ((ret = consumer->enqueue(cache_metadata, atc, sample_rate, frame_rate, jitter_algorithm)) != ERROR_SUCCESS) { srs_error("dispatch the metadata failed. ret=%d", ret); return ret; } @@ -1062,7 +1066,7 @@ int SrsSource::on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* metadata std::vector::iterator it; for (it = forwarders.begin(); it != forwarders.end(); ++it) { SrsForwarder* forwarder = *it; - if ((ret = forwarder->on_meta_data(cache_metadata->copy())) != ERROR_SUCCESS) { + if ((ret = forwarder->on_meta_data(cache_metadata)) != ERROR_SUCCESS) { srs_error("forwarder process onMetaData message failed. ret=%d", ret); return ret; } @@ -1086,7 +1090,7 @@ int SrsSource::on_audio(SrsCommonMessage* __audio) srs_verbose("initialize shared ptr audio success."); #ifdef SRS_AUTO_HLS - if ((ret = hls->on_audio(msg.copy())) != ERROR_SUCCESS) { + if ((ret = hls->on_audio(&msg)) != ERROR_SUCCESS) { srs_warn("hls process audio message failed, ignore and disable hls. ret=%d", ret); // unpublish, ignore ret. @@ -1098,7 +1102,7 @@ int SrsSource::on_audio(SrsCommonMessage* __audio) #endif #ifdef SRS_AUTO_DVR - if ((ret = dvr->on_audio(msg.copy())) != ERROR_SUCCESS) { + if ((ret = dvr->on_audio(&msg)) != ERROR_SUCCESS) { srs_warn("dvr process audio message failed, ignore and disable dvr. ret=%d", ret); // unpublish, ignore ret. @@ -1115,8 +1119,7 @@ int SrsSource::on_audio(SrsCommonMessage* __audio) SrsConsumer** pconsumer = consumers.data(); for (int i = 0; i < nb_consumers; i++) { SrsConsumer* consumer = pconsumer[i]; - SrsSharedPtrMessage* copy = msg.copy(); - if ((ret = consumer->enqueue(copy, atc, sample_rate, frame_rate, jitter_algorithm)) != ERROR_SUCCESS) { + if ((ret = consumer->enqueue(&msg, atc, sample_rate, frame_rate, jitter_algorithm)) != ERROR_SUCCESS) { srs_error("dispatch the audio failed. ret=%d", ret); return ret; } @@ -1197,7 +1200,7 @@ int SrsSource::on_video(SrsCommonMessage* __video) srs_verbose("initialize shared ptr video success."); #ifdef SRS_AUTO_HLS - if ((ret = hls->on_video(msg.copy())) != ERROR_SUCCESS) { + if ((ret = hls->on_video(&msg)) != ERROR_SUCCESS) { srs_warn("hls process video message failed, ignore and disable hls. ret=%d", ret); // unpublish, ignore ret. @@ -1209,7 +1212,7 @@ int SrsSource::on_video(SrsCommonMessage* __video) #endif #ifdef SRS_AUTO_DVR - if ((ret = dvr->on_video(msg.copy())) != ERROR_SUCCESS) { + if ((ret = dvr->on_video(&msg)) != ERROR_SUCCESS) { srs_warn("dvr process video message failed, ignore and disable dvr. ret=%d", ret); // unpublish, ignore ret. @@ -1224,8 +1227,7 @@ int SrsSource::on_video(SrsCommonMessage* __video) if (true) { for (int i = 0; i < (int)consumers.size(); i++) { SrsConsumer* consumer = consumers.at(i); - SrsSharedPtrMessage* copy = msg.copy(); - if ((ret = consumer->enqueue(copy, atc, sample_rate, frame_rate, jitter_algorithm)) != ERROR_SUCCESS) { + if ((ret = consumer->enqueue(&msg, atc, sample_rate, frame_rate, jitter_algorithm)) != ERROR_SUCCESS) { srs_error("dispatch the video failed. ret=%d", ret); return ret; } @@ -1234,11 +1236,11 @@ int SrsSource::on_video(SrsCommonMessage* __video) } // copy to all forwarders. - if (true) { + if (!forwarders.empty()) { std::vector::iterator it; for (it = forwarders.begin(); it != forwarders.end(); ++it) { SrsForwarder* forwarder = *it; - if ((ret = forwarder->on_video(msg.copy())) != ERROR_SUCCESS) { + if ((ret = forwarder->on_video(&msg)) != ERROR_SUCCESS) { srs_error("forwarder process video message failed. ret=%d", ret); return ret; } @@ -1512,20 +1514,20 @@ void SrsSource::on_unpublish() SrsRtmpJitterAlgorithm ag = jitter_algorithm; // copy metadata. - if (cache_metadata && (ret = consumer->enqueue(cache_metadata->copy(), atc, tba, tbv, ag)) != ERROR_SUCCESS) { + if (cache_metadata && (ret = consumer->enqueue(cache_metadata, atc, tba, tbv, ag)) != ERROR_SUCCESS) { srs_error("dispatch metadata failed. ret=%d", ret); return ret; } srs_info("dispatch metadata success"); // copy sequence header - if (cache_sh_video && (ret = consumer->enqueue(cache_sh_video->copy(), atc, tba, tbv, ag)) != ERROR_SUCCESS) { + if (cache_sh_video && (ret = consumer->enqueue(cache_sh_video, atc, tba, tbv, ag)) != ERROR_SUCCESS) { srs_error("dispatch video sequence header failed. ret=%d", ret); return ret; } srs_info("dispatch video sequence header success"); - if (cache_sh_audio && (ret = consumer->enqueue(cache_sh_audio->copy(), atc, tba, tbv, ag)) != ERROR_SUCCESS) { + if (cache_sh_audio && (ret = consumer->enqueue(cache_sh_audio, atc, tba, tbv, ag)) != ERROR_SUCCESS) { srs_error("dispatch audio sequence header failed. ret=%d", ret); return ret; } diff --git a/trunk/src/app/srs_app_source.hpp b/trunk/src/app/srs_app_source.hpp index ea135f6833..69da17a7d8 100644 --- a/trunk/src/app/srs_app_source.hpp +++ b/trunk/src/app/srs_app_source.hpp @@ -184,6 +184,7 @@ class SrsConsumer virtual int get_time(); /** * enqueue an shared ptr message. + * @param __msg, directly ptr, copy it if need to save it. * @param whether atc, donot use jitter correct if true. * @param tba timebase of audio. * used to calc the audio time delta if time-jitter detected. @@ -191,7 +192,7 @@ class SrsConsumer * used to calc the video time delta if time-jitter detected. * @param ag the algorithm of time jitter. */ - virtual int enqueue(SrsSharedPtrMessage* msg, bool atc, int tba, int tbv, SrsRtmpJitterAlgorithm ag); + virtual int enqueue(SrsSharedPtrMessage* __msg, bool atc, int tba, int tbv, SrsRtmpJitterAlgorithm ag); /** * get packets in consumer queue. * @param msgs the msgs array to dump packets to send. @@ -252,8 +253,9 @@ class SrsGopCache * only for h264 codec * 1. cache the gop when got h264 video packet. * 2. clear gop when got keyframe. + * @param __msg, directly ptr, copy it if need to save it. */ - virtual int cache(SrsSharedPtrMessage* msg); + virtual int cache(SrsSharedPtrMessage* __msg); /** * clear the gop cache. */ From d073adde58735b49c91390c0d27a60003d539393 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 6 Dec 2014 00:59:10 +0800 Subject: [PATCH 335/800] for bug #251, somhc(session-oriented message-header cache). 2.0.61 --- trunk/src/core/srs_core.hpp | 2 +- trunk/src/rtmp/srs_protocol_stack.cpp | 95 +++++++++++++++++++-------- trunk/src/rtmp/srs_protocol_stack.hpp | 9 ++- 3 files changed, 77 insertions(+), 29 deletions(-) diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 214b49472f..ebd5c04731 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 60 +#define VERSION_REVISION 61 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp index 5589ecd049..5f7bb5c07e 100644 --- a/trunk/src/rtmp/srs_protocol_stack.cpp +++ b/trunk/src/rtmp/srs_protocol_stack.cpp @@ -729,6 +729,13 @@ int SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs) int c0c3_cache_index = 0; char* c0c3_cache = out_c0c3_caches + c0c3_cache_index; + // the somc(session-oriented message-header cache), + // many message header are same, use cache. + // @see https://github.com/winlinvip/simple-rtmp-server/issues/251 + SrsMessageHeader* somhc = NULL; + char* somhc_bytes = NULL; + int nb_somhc_bytes = 0; + // try to send use the c0c3 header cache, // if cache is consumed, try another loop. for (int i = 0; i < nb_msgs; i++) { @@ -755,8 +762,20 @@ int SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs) // always write the header event payload is empty. while (p < pend) { + // the first chunk is c0, others is c3. + bool is_c0 = p == msg->payload; + // header use iov[0]. - generate_chunk_header(c0c3_cache, &msg->header, p == msg->payload, iov); + generate_chunk_header(somhc, somhc_bytes, nb_somhc_bytes, + c0c3_cache, SRS_CONSTS_C0C3_HEADERS_MAX - c0c3_cache_index, + &msg->header, is_c0, iov); + + // set somhc to the first header. + if (!somhc) { + somhc = &msg->header; + somhc_bytes = (char*)iov[0].iov_base; + nb_somhc_bytes = iov[0].iov_len; + } // payload use iov[1]. int payload_size = pend - p; @@ -898,8 +917,10 @@ int SrsProtocol::do_send_and_free_packet(SrsPacket* packet, int stream_id) return ret; } -void SrsProtocol::generate_chunk_header(char* cache, SrsMessageHeader* mh, bool c0, iovec* iov) -{ +void SrsProtocol::generate_chunk_header( + SrsMessageHeader* somhc, char* somhc_bytes, int nb_somhc_bytes, + char* cache, int nb_cache, SrsMessageHeader* mh, bool c0, iovec* iov +) { // to directly set the field. char* pp = NULL; @@ -910,37 +931,55 @@ void SrsProtocol::generate_chunk_header(char* cache, SrsMessageHeader* mh, bool u_int32_t timestamp = (u_int32_t)mh->timestamp; if (c0) { + // if cached header, copy it. + if (somhc) { + srs_assert(nb_cache >= nb_somhc_bytes); + memcpy(cache, somhc_bytes, nb_somhc_bytes); + } + // write new chunk stream header, fmt is 0 *p++ = 0x00 | (mh->perfer_cid & 0x3F); // chunk message header, 11 bytes // timestamp, 3bytes, big-endian - if (timestamp < RTMP_EXTENDED_TIMESTAMP) { - pp = (char*)×tamp; - *p++ = pp[2]; - *p++ = pp[1]; - *p++ = pp[0]; + if (somhc && somhc->timestamp == mh->timestamp) { + p += 3; } else { - *p++ = 0xFF; - *p++ = 0xFF; - *p++ = 0xFF; + if (timestamp < RTMP_EXTENDED_TIMESTAMP) { + pp = (char*)×tamp; + *p++ = pp[2]; + *p++ = pp[1]; + *p++ = pp[0]; + } else { + *p++ = 0xFF; + *p++ = 0xFF; + *p++ = 0xFF; + } } // message_length, 3bytes, big-endian - pp = (char*)&mh->payload_length; - *p++ = pp[2]; - *p++ = pp[1]; - *p++ = pp[0]; + if (somhc && somhc->payload_length == mh->payload_length) { + p += 3; + } else { + pp = (char*)&mh->payload_length; + *p++ = pp[2]; + *p++ = pp[1]; + *p++ = pp[0]; + } // message_type, 1bytes *p++ = mh->message_type; - // message_length, 3bytes, little-endian - pp = (char*)&mh->stream_id; - *p++ = pp[0]; - *p++ = pp[1]; - *p++ = pp[2]; - *p++ = pp[3]; + // stream_id, 4bytes, little-endian. + if (somhc && somhc->stream_id == mh->stream_id) { + p += 4; + } else { + pp = (char*)&mh->stream_id; + *p++ = pp[0]; + *p++ = pp[1]; + *p++ = pp[2]; + *p++ = pp[3]; + } } else { // write no message header chunk stream, fmt is 3 // @remark, if perfer_cid > 0x3F, that is, use 2B/3B chunk header, @@ -967,11 +1006,15 @@ void SrsProtocol::generate_chunk_header(char* cache, SrsMessageHeader* mh, bool // @see: http://blog.csdn.net/win_lin/article/details/13363699 // TODO: FIXME: extract to outer. if (timestamp >= RTMP_EXTENDED_TIMESTAMP) { - pp = (char*)×tamp; - *p++ = pp[3]; - *p++ = pp[2]; - *p++ = pp[1]; - *p++ = pp[0]; + if (somhc && somhc->payload_length == mh->payload_length) { + p += 4; + } else { + pp = (char*)×tamp; + *p++ = pp[3]; + *p++ = pp[2]; + *p++ = pp[1]; + *p++ = pp[0]; + } } // always has header diff --git a/trunk/src/rtmp/srs_protocol_stack.hpp b/trunk/src/rtmp/srs_protocol_stack.hpp index e19a18c96d..40c75fcae2 100644 --- a/trunk/src/rtmp/srs_protocol_stack.hpp +++ b/trunk/src/rtmp/srs_protocol_stack.hpp @@ -84,7 +84,7 @@ class SrsMessageHeader /** * 4bytes. * Four-byte field that identifies the stream of the message. These - * bytes are set in big-endian format. + * bytes are set in little-endian format. */ int32_t stream_id; @@ -494,11 +494,16 @@ class SrsProtocol virtual int do_send_and_free_packet(SrsPacket* packet, int stream_id); /** * generate the chunk header for msg. + * @param somhc, session-oriented message-header cache. + * @param somhc_bytes, the serialized bytes. + * @param nb_somhc_bytes, the size of somhc_bytes. * @param mh, the header of msg to send. * @param c0, whether the first chunk, the c0 chunk. * @param iov, output the header and size to iovec. */ - virtual void generate_chunk_header(char* cache, SrsMessageHeader* mh, bool c0, iovec* iov); + virtual void generate_chunk_header( + SrsMessageHeader* somhc, char* somhc_bytes, int nb_somhc_bytes, + char* cache, int nb_cache, SrsMessageHeader* mh, bool c0, iovec* iov); /** * imp for decode_message */ From 9892b92258b3342703bc67557c49a74c9a26d5ed Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 6 Dec 2014 01:19:49 +0800 Subject: [PATCH 336/800] Revert "for bug #251, somhc(session-oriented message-header cache). 2.0.61" It hurt performance, should never use it. This reverts commit d073adde58735b49c91390c0d27a60003d539393. --- trunk/src/core/srs_core.hpp | 2 +- trunk/src/rtmp/srs_protocol_stack.cpp | 95 ++++++++------------------- trunk/src/rtmp/srs_protocol_stack.hpp | 9 +-- 3 files changed, 29 insertions(+), 77 deletions(-) diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index ebd5c04731..214b49472f 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 61 +#define VERSION_REVISION 60 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp index 5f7bb5c07e..5589ecd049 100644 --- a/trunk/src/rtmp/srs_protocol_stack.cpp +++ b/trunk/src/rtmp/srs_protocol_stack.cpp @@ -729,13 +729,6 @@ int SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs) int c0c3_cache_index = 0; char* c0c3_cache = out_c0c3_caches + c0c3_cache_index; - // the somc(session-oriented message-header cache), - // many message header are same, use cache. - // @see https://github.com/winlinvip/simple-rtmp-server/issues/251 - SrsMessageHeader* somhc = NULL; - char* somhc_bytes = NULL; - int nb_somhc_bytes = 0; - // try to send use the c0c3 header cache, // if cache is consumed, try another loop. for (int i = 0; i < nb_msgs; i++) { @@ -762,20 +755,8 @@ int SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs) // always write the header event payload is empty. while (p < pend) { - // the first chunk is c0, others is c3. - bool is_c0 = p == msg->payload; - // header use iov[0]. - generate_chunk_header(somhc, somhc_bytes, nb_somhc_bytes, - c0c3_cache, SRS_CONSTS_C0C3_HEADERS_MAX - c0c3_cache_index, - &msg->header, is_c0, iov); - - // set somhc to the first header. - if (!somhc) { - somhc = &msg->header; - somhc_bytes = (char*)iov[0].iov_base; - nb_somhc_bytes = iov[0].iov_len; - } + generate_chunk_header(c0c3_cache, &msg->header, p == msg->payload, iov); // payload use iov[1]. int payload_size = pend - p; @@ -917,10 +898,8 @@ int SrsProtocol::do_send_and_free_packet(SrsPacket* packet, int stream_id) return ret; } -void SrsProtocol::generate_chunk_header( - SrsMessageHeader* somhc, char* somhc_bytes, int nb_somhc_bytes, - char* cache, int nb_cache, SrsMessageHeader* mh, bool c0, iovec* iov -) { +void SrsProtocol::generate_chunk_header(char* cache, SrsMessageHeader* mh, bool c0, iovec* iov) +{ // to directly set the field. char* pp = NULL; @@ -931,55 +910,37 @@ void SrsProtocol::generate_chunk_header( u_int32_t timestamp = (u_int32_t)mh->timestamp; if (c0) { - // if cached header, copy it. - if (somhc) { - srs_assert(nb_cache >= nb_somhc_bytes); - memcpy(cache, somhc_bytes, nb_somhc_bytes); - } - // write new chunk stream header, fmt is 0 *p++ = 0x00 | (mh->perfer_cid & 0x3F); // chunk message header, 11 bytes // timestamp, 3bytes, big-endian - if (somhc && somhc->timestamp == mh->timestamp) { - p += 3; - } else { - if (timestamp < RTMP_EXTENDED_TIMESTAMP) { - pp = (char*)×tamp; - *p++ = pp[2]; - *p++ = pp[1]; - *p++ = pp[0]; - } else { - *p++ = 0xFF; - *p++ = 0xFF; - *p++ = 0xFF; - } - } - - // message_length, 3bytes, big-endian - if (somhc && somhc->payload_length == mh->payload_length) { - p += 3; - } else { - pp = (char*)&mh->payload_length; + if (timestamp < RTMP_EXTENDED_TIMESTAMP) { + pp = (char*)×tamp; *p++ = pp[2]; *p++ = pp[1]; *p++ = pp[0]; + } else { + *p++ = 0xFF; + *p++ = 0xFF; + *p++ = 0xFF; } + // message_length, 3bytes, big-endian + pp = (char*)&mh->payload_length; + *p++ = pp[2]; + *p++ = pp[1]; + *p++ = pp[0]; + // message_type, 1bytes *p++ = mh->message_type; - // stream_id, 4bytes, little-endian. - if (somhc && somhc->stream_id == mh->stream_id) { - p += 4; - } else { - pp = (char*)&mh->stream_id; - *p++ = pp[0]; - *p++ = pp[1]; - *p++ = pp[2]; - *p++ = pp[3]; - } + // message_length, 3bytes, little-endian + pp = (char*)&mh->stream_id; + *p++ = pp[0]; + *p++ = pp[1]; + *p++ = pp[2]; + *p++ = pp[3]; } else { // write no message header chunk stream, fmt is 3 // @remark, if perfer_cid > 0x3F, that is, use 2B/3B chunk header, @@ -1006,15 +967,11 @@ void SrsProtocol::generate_chunk_header( // @see: http://blog.csdn.net/win_lin/article/details/13363699 // TODO: FIXME: extract to outer. if (timestamp >= RTMP_EXTENDED_TIMESTAMP) { - if (somhc && somhc->payload_length == mh->payload_length) { - p += 4; - } else { - pp = (char*)×tamp; - *p++ = pp[3]; - *p++ = pp[2]; - *p++ = pp[1]; - *p++ = pp[0]; - } + pp = (char*)×tamp; + *p++ = pp[3]; + *p++ = pp[2]; + *p++ = pp[1]; + *p++ = pp[0]; } // always has header diff --git a/trunk/src/rtmp/srs_protocol_stack.hpp b/trunk/src/rtmp/srs_protocol_stack.hpp index 40c75fcae2..e19a18c96d 100644 --- a/trunk/src/rtmp/srs_protocol_stack.hpp +++ b/trunk/src/rtmp/srs_protocol_stack.hpp @@ -84,7 +84,7 @@ class SrsMessageHeader /** * 4bytes. * Four-byte field that identifies the stream of the message. These - * bytes are set in little-endian format. + * bytes are set in big-endian format. */ int32_t stream_id; @@ -494,16 +494,11 @@ class SrsProtocol virtual int do_send_and_free_packet(SrsPacket* packet, int stream_id); /** * generate the chunk header for msg. - * @param somhc, session-oriented message-header cache. - * @param somhc_bytes, the serialized bytes. - * @param nb_somhc_bytes, the size of somhc_bytes. * @param mh, the header of msg to send. * @param c0, whether the first chunk, the c0 chunk. * @param iov, output the header and size to iovec. */ - virtual void generate_chunk_header( - SrsMessageHeader* somhc, char* somhc_bytes, int nb_somhc_bytes, - char* cache, int nb_cache, SrsMessageHeader* mh, bool c0, iovec* iov); + virtual void generate_chunk_header(char* cache, SrsMessageHeader* mh, bool c0, iovec* iov); /** * imp for decode_message */ From a6599f2250e5bc7397631fdeb60132e2ad88e7d9 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 6 Dec 2014 09:52:26 +0800 Subject: [PATCH 337/800] remove the qt modify file. --- trunk/src/qt/srs/srs-qt.pro.user | 1 - 1 file changed, 1 deletion(-) diff --git a/trunk/src/qt/srs/srs-qt.pro.user b/trunk/src/qt/srs/srs-qt.pro.user index 101e5889a1..8672380b5b 100755 --- a/trunk/src/qt/srs/srs-qt.pro.user +++ b/trunk/src/qt/srs/srs-qt.pro.user @@ -1,6 +1,5 @@ - ProjectExplorer.Project.ActiveTarget From d827928eebcf019993eaf943df8bd3480cf2c77e Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 6 Dec 2014 09:55:51 +0800 Subject: [PATCH 338/800] for bug #251, merge the performance refines. --- trunk/src/app/srs_app_rtmp_conn.cpp | 34 ++++++++++---- trunk/src/app/srs_app_source.cpp | 59 +++++++++++++++++++++---- trunk/src/app/srs_app_source.hpp | 21 +++++++-- trunk/src/core/srs_core_performance.hpp | 16 +++++++ trunk/src/rtmp/srs_protocol_rtmp.hpp | 1 - trunk/src/rtmp/srs_protocol_stack.cpp | 27 ++++++----- trunk/src/rtmp/srs_protocol_stack.hpp | 6 ++- 7 files changed, 130 insertions(+), 34 deletions(-) diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index 3e5424ba44..b27b3213c3 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -598,6 +598,12 @@ int SrsRtmpConn::do_playing(SrsSource* source, SrsQueueRecvThread* trd) // collect elapse for pithy print. pithy_print.elapse(); +#ifdef SRS_PERF_QUEUE_COND_WAIT + // wait for message to incoming. + // @see https://github.com/winlinvip/simple-rtmp-server/issues/251 + consumer->wait(SRS_PERF_MW_MIN_MSGS, mw_sleep); +#endif + // get messages from consumer. // each msg in msgs.msgs must be free, for the SrsMessageArray never free them. int count = 0; @@ -605,12 +611,16 @@ int SrsRtmpConn::do_playing(SrsSource* source, SrsQueueRecvThread* trd) srs_error("get messages from consumer failed. ret=%d", ret); return ret; } - - // no messages, sleep for a while. + +#ifdef SRS_PERF_QUEUE_COND_WAIT + // we use wait to get messages, so the count must be positive. + srs_assert(count > 0); +#else if (count <= 0) { st_usleep(mw_sleep * 1000); } - srs_info("got %d msgs, mw=%d", count, mw_sleep); +#endif + srs_info("got %d msgs, min=%d, mw=%d", count, SRS_PERF_MW_MIN_MSGS, mw_sleep); // reportable if (pithy_print.can_print()) { @@ -995,6 +1005,13 @@ void SrsRtmpConn::change_mw_sleep(int sleep_ms) return; } + // get the sock buffer size. + int fd = st_netfd_fileno(stfd); + int onb_sbuf = 0; + socklen_t sock_buf_size = sizeof(int); + getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &onb_sbuf, &sock_buf_size); + +#ifdef SRS_PERF_MW_SO_SNDBUF // the bytes: // 4KB=4096, 8KB=8192, 16KB=16384, 32KB=32768, 64KB=65536, // 128KB=131072, 256KB=262144, 512KB=524288 @@ -1007,11 +1024,6 @@ void SrsRtmpConn::change_mw_sleep(int sleep_ms) // 2000*5000/8=1250000B(about 1220KB). int kbps = 5000; int socket_buffer_size = sleep_ms * kbps / 8; - - int fd = st_netfd_fileno(stfd); - int onb_sbuf = 0; - socklen_t sock_buf_size = sizeof(int); - getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &onb_sbuf, &sock_buf_size); // socket send buffer, system will double it. int nb_sbuf = socket_buffer_size / 2; @@ -1022,9 +1034,13 @@ void SrsRtmpConn::change_mw_sleep(int sleep_ms) } getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &nb_sbuf, &sock_buf_size); - srs_trace("mw change sleep %d=>%d, max_msgs=%d, esbuf=%d, sbuf %d=>%d", + srs_trace("mw changed sleep %d=>%d, max_msgs=%d, esbuf=%d, sbuf %d=>%d", mw_sleep, sleep_ms, SRS_PERF_MW_MSGS, socket_buffer_size, onb_sbuf, nb_sbuf); +#else + srs_trace("mw changed sleep %d=>%d, max_msgs=%d, sbuf %d", + mw_sleep, sleep_ms, SRS_PERF_MW_MSGS, onb_sbuf); +#endif mw_sleep = sleep_ms; } diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index 21ea77f317..523dafc5ad 100644 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -182,7 +182,7 @@ void SrsMessageQueue::set_queue_size(double queue_size) queue_size_ms = (int)(queue_size * 1000); } -int SrsMessageQueue::enqueue(SrsSharedPtrMessage* msg, bool* is_overflow) +int SrsMessageQueue::enqueue(SrsSharedPtrMessage* msg) { int ret = ERROR_SUCCESS; @@ -197,11 +197,6 @@ int SrsMessageQueue::enqueue(SrsSharedPtrMessage* msg, bool* is_overflow) msgs.push_back(msg); while (av_end_time - av_start_time > queue_size_ms) { - // notice the caller queue already overflow and shrinked. - if (is_overflow) { - *is_overflow = true; - } - shrink(); } @@ -211,7 +206,7 @@ int SrsMessageQueue::enqueue(SrsSharedPtrMessage* msg, bool* is_overflow) int SrsMessageQueue::dump_packets(int max_count, SrsSharedPtrMessage** pmsgs, int& count) { int ret = ERROR_SUCCESS; - + int nb_msgs = (int)msgs.size(); if (nb_msgs <= 0) { return ret; @@ -308,6 +303,13 @@ SrsConsumer::SrsConsumer(SrsSource* _source) jitter = new SrsRtmpJitter(); queue = new SrsMessageQueue(); should_update_source_id = false; + +#ifdef SRS_PERF_QUEUE_COND_WAIT + mw_wait = st_cond_new(); + mw_min_msgs = 0; + mw_duration = 0; + mw_waiting = false; +#endif } SrsConsumer::~SrsConsumer() @@ -315,6 +317,10 @@ SrsConsumer::~SrsConsumer() source->on_consumer_destroy(this); srs_freep(jitter); srs_freep(queue); + +#ifdef SRS_PERF_QUEUE_COND_WAIT + st_cond_destroy(mw_wait); +#endif } void SrsConsumer::set_queue_size(double queue_size) @@ -344,11 +350,25 @@ int SrsConsumer::enqueue(SrsSharedPtrMessage* __msg, bool atc, int tba, int tbv, return ret; } } - - if ((ret = queue->enqueue(msg, NULL)) != ERROR_SUCCESS) { + + if ((ret = queue->enqueue(msg)) != ERROR_SUCCESS) { return ret; } + #ifdef SRS_PERF_QUEUE_COND_WAIT + // fire the mw when msgs is enough. + if (mw_waiting) { + int duration_ms = queue->duration(); + bool match_min_msgs = queue->size() > mw_min_msgs; + + // when duration ok, signal to flush. + if (match_min_msgs && duration_ms > mw_duration) { + st_cond_signal(mw_wait); + mw_waiting = false; + } + } + #endif + return ret; } @@ -376,6 +396,27 @@ int SrsConsumer::dump_packets(SrsMessageArray* msgs, int& count) return ret; } +#ifdef SRS_PERF_QUEUE_COND_WAIT +void SrsConsumer::wait(int nb_msgs, int duration) +{ + mw_min_msgs = nb_msgs; + mw_duration = duration; + + int duration_ms = queue->duration(); + bool match_min_msgs = queue->size() > mw_min_msgs; + + // when duration ok, signal to flush. + if (match_min_msgs && duration_ms > mw_duration) { + return; + } + + // the enqueue will notify this cond. + mw_waiting = true; + // wait for msgs to incoming. + st_cond_wait(mw_wait); +} +#endif + int SrsConsumer::on_play_client_pause(bool is_pause) { int ret = ERROR_SUCCESS; diff --git a/trunk/src/app/srs_app_source.hpp b/trunk/src/app/srs_app_source.hpp index 69da17a7d8..d1ec5bae4b 100644 --- a/trunk/src/app/srs_app_source.hpp +++ b/trunk/src/app/srs_app_source.hpp @@ -134,12 +134,11 @@ class SrsMessageQueue /** * 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 int enqueue(SrsSharedPtrMessage* msg, bool* is_overflow = NULL); + virtual int enqueue(SrsSharedPtrMessage* msg); /** * get packets in consumer queue. - * @pmsgs SrsCommonMessages*[], used to store the msgs, user must alloc it. + * @pmsgs SrsSharedPtrMessage*[], used to store the msgs, user must alloc it. * @count the count in array, output param. * @max_count the max count to dequeue, must be positive. */ @@ -165,6 +164,14 @@ class SrsConsumer bool paused; // when source id changed, notice all consumers bool should_update_source_id; +#ifdef SRS_PERF_QUEUE_COND_WAIT + // the cond wait for mw. + // @see https://github.com/winlinvip/simple-rtmp-server/issues/251 + st_cond_t mw_wait; + bool mw_waiting; + int mw_min_msgs; + int mw_duration; +#endif public: SrsConsumer(SrsSource* _source); virtual ~SrsConsumer(); @@ -200,6 +207,14 @@ class SrsConsumer * @max_count the max count to dequeue, must be positive. */ virtual int dump_packets(SrsMessageArray* msgs, int& count); +#ifdef SRS_PERF_QUEUE_COND_WAIT + /** + * wait for messages incomming, atleast nb_msgs and in duration. + * @param nb_msgs the messages count to wait. + * @param duration the messgae duration to wait. + */ + virtual void wait(int nb_msgs, int duration); +#endif /** * when client send the pause message. */ diff --git a/trunk/src/core/srs_core_performance.hpp b/trunk/src/core/srs_core_performance.hpp index cf08a9d721..a3dc15ea28 100644 --- a/trunk/src/core/srs_core_performance.hpp +++ b/trunk/src/core/srs_core_performance.hpp @@ -103,6 +103,22 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * @remark, recomment to 128. */ #define SRS_PERF_MW_MSGS 128 +/** +* whether set the socket send buffer size. +* @see https://github.com/winlinvip/simple-rtmp-server/issues/251 +*/ +#undef SRS_PERF_MW_SO_SNDBUF +/** +* whether set the socket recv buffer size. +* @see https://github.com/winlinvip/simple-rtmp-server/issues/251 +*/ +#undef SRS_PERF_MW_SO_RCVBUF +/** +* whether use cond wait to send messages. +* @remark this improve performance for large connectios. +* @see https://github.com/winlinvip/simple-rtmp-server/issues/251 +*/ +#undef SRS_PERF_QUEUE_COND_WAIT /** * how many chunk stream to cache, [0, N]. diff --git a/trunk/src/rtmp/srs_protocol_rtmp.hpp b/trunk/src/rtmp/srs_protocol_rtmp.hpp index 09b45e30ef..6aa32eba5f 100644 --- a/trunk/src/rtmp/srs_protocol_rtmp.hpp +++ b/trunk/src/rtmp/srs_protocol_rtmp.hpp @@ -37,7 +37,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. class SrsProtocol; class ISrsProtocolReaderWriter; -class ISrsCommonMessage; class SrsCommonMessage; class SrsCreateStreamPacket; class SrsFMLEStartPacket; diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp index 5589ecd049..1282534747 100644 --- a/trunk/src/rtmp/srs_protocol_stack.cpp +++ b/trunk/src/rtmp/srs_protocol_stack.cpp @@ -755,10 +755,17 @@ int SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs) // always write the header event payload is empty. while (p < pend) { - // header use iov[0]. - generate_chunk_header(c0c3_cache, &msg->header, p == msg->payload, iov); + // always has header + int nbh = 0; + char* header = NULL; + generate_chunk_header(c0c3_cache, &msg->header, p == msg->payload, &nbh, &header); + srs_assert(nbh > 0); - // payload use iov[1]. + // header iov + iov[0].iov_base = header; + iov[0].iov_len = nbh; + + // payload iov int payload_size = pend - p; if (payload_size > out_chunk_size) { payload_size = out_chunk_size; @@ -781,14 +788,14 @@ int SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs) int realloc_size = sizeof(iovec) * nb_out_iovs; out_iovs = (iovec*)realloc(out_iovs, realloc_size); } - - // to next c0c3 header cache - c0c3_cache_index += iov[0].iov_len; - c0c3_cache = out_c0c3_caches + c0c3_cache_index; // to next pair of iovs iov_index += 2; iov = out_iovs + iov_index; + + // to next c0c3 header cache + c0c3_cache_index += nbh; + c0c3_cache = out_c0c3_caches + c0c3_cache_index; // the cache header should never be realloc again, // for the ptr is set to iovs, so we just warn user to set larger @@ -898,7 +905,7 @@ int SrsProtocol::do_send_and_free_packet(SrsPacket* packet, int stream_id) return ret; } -void SrsProtocol::generate_chunk_header(char* cache, SrsMessageHeader* mh, bool c0, iovec* iov) +void SrsProtocol::generate_chunk_header(char* cache, SrsMessageHeader* mh, bool c0, int* pnbh, char** ph) { // to directly set the field. char* pp = NULL; @@ -975,8 +982,8 @@ void SrsProtocol::generate_chunk_header(char* cache, SrsMessageHeader* mh, bool } // always has header - iov->iov_base = cache; - iov->iov_len = p - cache; + *pnbh = p - cache; + *ph = cache; } int SrsProtocol::do_decode_message(SrsMessageHeader& header, SrsStream* stream, SrsPacket** ppacket) diff --git a/trunk/src/rtmp/srs_protocol_stack.hpp b/trunk/src/rtmp/srs_protocol_stack.hpp index e19a18c96d..b99f359c31 100644 --- a/trunk/src/rtmp/srs_protocol_stack.hpp +++ b/trunk/src/rtmp/srs_protocol_stack.hpp @@ -496,9 +496,11 @@ class SrsProtocol * generate the chunk header for msg. * @param mh, the header of msg to send. * @param c0, whether the first chunk, the c0 chunk. - * @param iov, output the header and size to iovec. + * @param pnbh, output the size of header. + * @param ph, output the header cache. + * user should never free it, it's cached header. */ - virtual void generate_chunk_header(char* cache, SrsMessageHeader* mh, bool c0, iovec* iov); + virtual void generate_chunk_header(char* cache, SrsMessageHeader* mh, bool c0, int* pnbh, char** ph); /** * imp for decode_message */ From 32d537b96bdca220421fdd5ea76dde8a06a0424c Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 6 Dec 2014 19:56:06 +0800 Subject: [PATCH 339/800] for bug #251, refine code before mic. --- trunk/src/app/srs_app_config.cpp | 11 ++ trunk/src/app/srs_app_reload.cpp | 5 + trunk/src/app/srs_app_reload.hpp | 1 + trunk/src/app/srs_app_source.cpp | 2 +- trunk/src/main/srs_main_server.cpp | 2 + trunk/src/rtmp/srs_protocol_stack.cpp | 173 ++++++++---------------- trunk/src/rtmp/srs_protocol_stack.hpp | 28 ++-- trunk/src/rtmp/srs_protocol_utility.cpp | 81 +++++++++++ trunk/src/rtmp/srs_protocol_utility.hpp | 9 ++ 9 files changed, 188 insertions(+), 124 deletions(-) diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index cd4603b052..b68b3864e5 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -830,6 +830,17 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root) } srs_trace("vhost %s reload mr success.", vhost.c_str()); } + // chunk_size, only one per vhost. + if (!srs_directive_equals(new_vhost->get("chunk_size"), old_vhost->get("chunk_size"))) { + for (it = subscribes.begin(); it != subscribes.end(); ++it) { + ISrsReloadHandler* subscribe = *it; + if ((ret = subscribe->on_reload_vhost_chunk_size(vhost)) != ERROR_SUCCESS) { + srs_error("vhost %s notify subscribes chunk_size failed. ret=%d", vhost.c_str(), ret); + return ret; + } + } + srs_trace("vhost %s reload chunk_size success.", vhost.c_str()); + } // mw, only one per vhost if (!srs_directive_equals(new_vhost->get("mw_latency"), old_vhost->get("mw_latency"))) { for (it = subscribes.begin(); it != subscribes.end(); ++it) { diff --git a/trunk/src/app/srs_app_reload.cpp b/trunk/src/app/srs_app_reload.cpp index cc69a9c448..b1f81f5ecd 100644 --- a/trunk/src/app/srs_app_reload.cpp +++ b/trunk/src/app/srs_app_reload.cpp @@ -150,6 +150,11 @@ int ISrsReloadHandler::on_reload_vhost_mw(string /*vhost*/) return ERROR_SUCCESS; } +int ISrsReloadHandler::on_reload_vhost_chunk_size(string /*vhost*/) +{ + return ERROR_SUCCESS; +} + int ISrsReloadHandler::on_reload_vhost_transcode(string /*vhost*/) { return ERROR_SUCCESS; diff --git a/trunk/src/app/srs_app_reload.hpp b/trunk/src/app/srs_app_reload.hpp index 878518b092..b9377bff57 100644 --- a/trunk/src/app/srs_app_reload.hpp +++ b/trunk/src/app/srs_app_reload.hpp @@ -67,6 +67,7 @@ class ISrsReloadHandler virtual int on_reload_vhost_dvr(std::string vhost); virtual int on_reload_vhost_mr(std::string vhost); virtual int on_reload_vhost_mw(std::string vhost); + virtual int on_reload_vhost_chunk_size(std::string vhost); virtual int on_reload_vhost_transcode(std::string vhost); virtual int on_reload_ingest_removed(std::string vhost, std::string ingest_id); virtual int on_reload_ingest_added(std::string vhost, std::string ingest_id); diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index 523dafc5ad..7daf928521 100644 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -1173,7 +1173,7 @@ int SrsSource::on_audio(SrsCommonMessage* __audio) std::vector::iterator it; for (it = forwarders.begin(); it != forwarders.end(); ++it) { SrsForwarder* forwarder = *it; - if ((ret = forwarder->on_audio(msg.copy())) != ERROR_SUCCESS) { + if ((ret = forwarder->on_audio(&msg)) != ERROR_SUCCESS) { srs_error("forwarder process audio message failed. ret=%d", ret); return ret; } diff --git a/trunk/src/main/srs_main_server.cpp b/trunk/src/main/srs_main_server.cpp index 26a2d585d8..2ea3bdaf9d 100644 --- a/trunk/src/main/srs_main_server.cpp +++ b/trunk/src/main/srs_main_server.cpp @@ -162,6 +162,8 @@ void check_macro_features() srs_warn("MR(merged-read) is disabled, hurts read performance. @see %s", RTMP_SIG_SRS_ISSUES(241)); #endif + srs_trace("writev limits write %d iovs a time", sysconf(_SC_IOV_MAX)); + #if VERSION_MAJOR > 1 #warning "using develop SRS, please use release instead." srs_warn("SRS %s is develop branch, please use %s instead", RTMP_SIG_SRS_VERSION, RTMP_SIG_SRS_RELEASE); diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp index 1282534747..dbf5df2296 100644 --- a/trunk/src/rtmp/srs_protocol_stack.cpp +++ b/trunk/src/rtmp/srs_protocol_stack.cpp @@ -29,6 +29,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #include +#include #include using namespace std; @@ -163,22 +164,6 @@ messages. // the same as the timestamp of Type 0 chunk. #define RTMP_FMT_TYPE3 3 -/**************************************************************************** -***************************************************************************** -****************************************************************************/ -/** -* 6.1. Chunk Format -* Extended timestamp: 0 or 4 bytes -* This field MUST be sent when the normal timsestamp is set to -* 0xffffff, it MUST NOT be sent if the normal timestamp is set to -* anything else. So for values less than 0xffffff the normal -* timestamp field SHOULD be used in which case the extended timestamp -* MUST NOT be present. For values greater than or equal to 0xffffff -* the normal timestamp field MUST NOT be used and MUST be set to -* 0xffffff and the extended timestamp MUST be sent. -*/ -#define RTMP_EXTENDED_TIMESTAMP 0xFFFFFF - /**************************************************************************** ***************************************************************************** ****************************************************************************/ @@ -756,13 +741,11 @@ int SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs) // always write the header event payload is empty. while (p < pend) { // always has header - int nbh = 0; - char* header = NULL; - generate_chunk_header(c0c3_cache, &msg->header, p == msg->payload, &nbh, &header); + int nbh = srs_chunk_header(c0c3_cache, &msg->header, p == msg->payload); srs_assert(nbh > 0); // header iov - iov[0].iov_base = header; + iov[0].iov_base = c0c3_cache; iov[0].iov_len = nbh; // payload iov @@ -813,7 +796,7 @@ int SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs) // sendout all messages and reset the cache, then send again. if ((ret = skt->writev(out_iovs, iov_index, NULL)) != ERROR_SUCCESS) { if (!srs_is_client_gracefully_close(ret)) { - srs_error("send with writev failed. ret=%d", ret); + srs_error("send msgs with writev failed. ret=%d", ret); } return ret; } @@ -834,31 +817,47 @@ int SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs) if (iov_index <= 0) { return ret; } - - // calc the bytes of iovs, for debug. #if 0 + // calc the bytes of iovs, for debug. int nb_bytes = 0; for (int i = 0; i < iov_index; i++) { iovec* iov = out_iovs + i; nb_bytes += iov->iov_len; } - srs_warn("mw %d msgs %dB in %d iovs, max_msgs=%d, nb_out_iovs=%d", + srs_info("mw %d msgs %dB in %d iovs, max_msgs=%d, nb_out_iovs=%d", nb_msgs, nb_bytes, iov_index, SRS_PERF_MW_MSGS, nb_out_iovs); #else srs_info("mw %d msgs in %d iovs, max_msgs=%d, nb_out_iovs=%d", nb_msgs, iov_index, SRS_PERF_MW_MSGS, nb_out_iovs); #endif - // send by writev - // sendout header and payload by writev. - // decrease the sys invoke count to get higher performance. - if ((ret = skt->writev(out_iovs, iov_index, NULL)) != ERROR_SUCCESS) { - if (!srs_is_client_gracefully_close(ret)) { - srs_error("send with writev failed. ret=%d", ret); + // the limits of writev iovs. + static int limits = sysconf(_SC_IOV_MAX); + + // send in a time. + if (iov_index < limits) { + if ((ret = skt->writev(out_iovs, iov_index, NULL)) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("send with writev failed. ret=%d", ret); + } + return ret; } return ret; } + // send in multiple times. + int cur_iov = 0; + while (cur_iov < iov_index) { + int cur_count = srs_min(limits, iov_index - cur_iov); + if ((ret = skt->writev(out_iovs + cur_iov, cur_count, NULL)) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("send with writev failed. ret=%d", ret); + } + return ret; + } + cur_iov += cur_count; + } + return ret; } @@ -888,102 +887,46 @@ int SrsProtocol::do_send_and_free_packet(SrsPacket* packet, int stream_id) header.message_type = packet->get_message_type(); header.stream_id = stream_id; header.perfer_cid = packet->get_prefer_cid(); - - SrsSharedPtrMessage* msg = new SrsSharedPtrMessage(); - ret = msg->create(&header, payload, size); + + ret = do_simple_send(&header, payload, size); if (ret == ERROR_SUCCESS) { - ret = do_send_messages(&msg, 1); - if (ret == ERROR_SUCCESS) { - ret = on_send_packet(msg, packet); - } + ret = on_send_packet(&header, packet); } - - // donot use the auto free to free the msg, - // for performance issue. - srs_freep(msg); return ret; } -void SrsProtocol::generate_chunk_header(char* cache, SrsMessageHeader* mh, bool c0, int* pnbh, char** ph) +int SrsProtocol::do_simple_send(SrsMessageHeader* mh, char* payload, int size) { - // to directly set the field. - char* pp = NULL; - - // generate the header. - char* p = cache; - - // timestamp for c0/c3 - u_int32_t timestamp = (u_int32_t)mh->timestamp; + int ret = ERROR_SUCCESS; - if (c0) { - // write new chunk stream header, fmt is 0 - *p++ = 0x00 | (mh->perfer_cid & 0x3F); - - // chunk message header, 11 bytes - // timestamp, 3bytes, big-endian - if (timestamp < RTMP_EXTENDED_TIMESTAMP) { - pp = (char*)×tamp; - *p++ = pp[2]; - *p++ = pp[1]; - *p++ = pp[0]; - } else { - *p++ = 0xFF; - *p++ = 0xFF; - *p++ = 0xFF; - } + // we directly send out the packet, + // use very simple algorithm, not very fast, + // but it's ok. + char* p = payload; + char* end = p + size; + char c0c3[SRS_CONSTS_RTMP_MAX_FMT0_HEADER_SIZE]; + while (p < end) { + int nbh = srs_chunk_header(c0c3, mh, p == payload); - // message_length, 3bytes, big-endian - pp = (char*)&mh->payload_length; - *p++ = pp[2]; - *p++ = pp[1]; - *p++ = pp[0]; + iovec iovs[2]; + iovs[0].iov_base = c0c3; + iovs[0].iov_len = nbh; - // message_type, 1bytes - *p++ = mh->message_type; + int payload_size = srs_min(end - p, out_chunk_size); + iovs[1].iov_base = p; + iovs[1].iov_len = payload_size; + p += payload_size; - // message_length, 3bytes, little-endian - pp = (char*)&mh->stream_id; - *p++ = pp[0]; - *p++ = pp[1]; - *p++ = pp[2]; - *p++ = pp[3]; - } else { - // write no message header chunk stream, fmt is 3 - // @remark, if perfer_cid > 0x3F, that is, use 2B/3B chunk header, - // SRS will rollback to 1B chunk header. - *p++ = 0xC0 | (mh->perfer_cid & 0x3F); + if ((ret = skt->writev(iovs, 2, NULL)) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("send packet with writev failed. ret=%d", ret); + } + return ret; + } } - // for c0 - // chunk extended timestamp header, 0 or 4 bytes, big-endian - // - // for c3: - // chunk extended timestamp header, 0 or 4 bytes, big-endian - // 6.1.3. Extended Timestamp - // This field is transmitted only when the normal time stamp in the - // chunk message header is set to 0x00ffffff. If normal time stamp is - // set to any value less than 0x00ffffff, this field MUST NOT be - // present. This field MUST NOT be present if the timestamp field is not - // present. Type 3 chunks MUST NOT have this field. - // adobe changed for Type3 chunk: - // FMLE always sendout the extended-timestamp, - // must send the extended-timestamp to FMS, - // must send the extended-timestamp to flash-player. - // @see: ngx_rtmp_prepare_message - // @see: http://blog.csdn.net/win_lin/article/details/13363699 - // TODO: FIXME: extract to outer. - if (timestamp >= RTMP_EXTENDED_TIMESTAMP) { - pp = (char*)×tamp; - *p++ = pp[3]; - *p++ = pp[2]; - *p++ = pp[1]; - *p++ = pp[0]; - } - - // always has header - *pnbh = p - cache; - *ph = cache; + return ret; } int SrsProtocol::do_decode_message(SrsMessageHeader& header, SrsStream* stream, SrsPacket** ppacket) @@ -1842,7 +1785,7 @@ int SrsProtocol::on_recv_message(SrsCommonMessage* msg) return ret; } -int SrsProtocol::on_send_packet(SrsSharedPtrMessage* msg, SrsPacket* packet) +int SrsProtocol::on_send_packet(SrsMessageHeader* mh, SrsPacket* packet) { int ret = ERROR_SUCCESS; @@ -1851,7 +1794,7 @@ int SrsProtocol::on_send_packet(SrsSharedPtrMessage* msg, SrsPacket* packet) return ret; } - switch (msg->header.message_type) { + switch (mh->message_type) { case RTMP_MSG_SetChunkSize: { SrsSetChunkSizePacket* pkt = dynamic_cast(packet); srs_assert(pkt != NULL); diff --git a/trunk/src/rtmp/srs_protocol_stack.hpp b/trunk/src/rtmp/srs_protocol_stack.hpp index b99f359c31..753787c112 100644 --- a/trunk/src/rtmp/srs_protocol_stack.hpp +++ b/trunk/src/rtmp/srs_protocol_stack.hpp @@ -56,6 +56,22 @@ class SrsChunkStream; class SrsSharedPtrMessage; class IMergeReadHandler; +/**************************************************************************** +***************************************************************************** +****************************************************************************/ +/** +* 6.1. Chunk Format +* Extended timestamp: 0 or 4 bytes +* This field MUST be sent when the normal timsestamp is set to +* 0xffffff, it MUST NOT be sent if the normal timestamp is set to +* anything else. So for values less than 0xffffff the normal +* timestamp field SHOULD be used in which case the extended timestamp +* MUST NOT be present. For values greater than or equal to 0xffffff +* the normal timestamp field MUST NOT be used and MUST be set to +* 0xffffff and the extended timestamp MUST be sent. +*/ +#define RTMP_EXTENDED_TIMESTAMP 0xFFFFFF + /** * 4.1. Message Header */ @@ -493,14 +509,10 @@ class SrsProtocol */ virtual int do_send_and_free_packet(SrsPacket* packet, int stream_id); /** - * generate the chunk header for msg. - * @param mh, the header of msg to send. - * @param c0, whether the first chunk, the c0 chunk. - * @param pnbh, output the size of header. - * @param ph, output the header cache. - * user should never free it, it's cached header. + * use simple algorithm to send the header and bytes. + * @remark, for do_send_and_free_packet to send. */ - virtual void generate_chunk_header(char* cache, SrsMessageHeader* mh, bool c0, int* pnbh, char** ph); + virtual int do_simple_send(SrsMessageHeader* mh, char* payload, int size); /** * imp for decode_message */ @@ -534,7 +546,7 @@ class SrsProtocol /** * when message sentout, update the context. */ - virtual int on_send_packet(SrsSharedPtrMessage* msg, SrsPacket* packet); + virtual int on_send_packet(SrsMessageHeader* mh, SrsPacket* packet); private: /** * auto response the ack message. diff --git a/trunk/src/rtmp/srs_protocol_utility.cpp b/trunk/src/rtmp/srs_protocol_utility.cpp index b79e458291..ac4ea8ad69 100644 --- a/trunk/src/rtmp/srs_protocol_utility.cpp +++ b/trunk/src/rtmp/srs_protocol_utility.cpp @@ -29,6 +29,7 @@ using namespace std; #include #include #include +#include void srs_discovery_tc_url( string tcUrl, @@ -203,3 +204,83 @@ bool srs_aac_startswith_adts(SrsStream* stream) return true; } +int srs_chunk_header(char* cache, SrsMessageHeader* mh, bool c0) +{ + // to directly set the field. + char* pp = NULL; + + // generate the header. + char* p = cache; + + // timestamp for c0/c3 + u_int32_t timestamp = (u_int32_t)mh->timestamp; + + if (c0) { + // write new chunk stream header, fmt is 0 + *p++ = 0x00 | (mh->perfer_cid & 0x3F); + + // chunk message header, 11 bytes + // timestamp, 3bytes, big-endian + if (timestamp < RTMP_EXTENDED_TIMESTAMP) { + pp = (char*)×tamp; + *p++ = pp[2]; + *p++ = pp[1]; + *p++ = pp[0]; + } else { + *p++ = 0xFF; + *p++ = 0xFF; + *p++ = 0xFF; + } + + // message_length, 3bytes, big-endian + pp = (char*)&mh->payload_length; + *p++ = pp[2]; + *p++ = pp[1]; + *p++ = pp[0]; + + // message_type, 1bytes + *p++ = mh->message_type; + + // stream_id, 4bytes, little-endian + pp = (char*)&mh->stream_id; + *p++ = pp[0]; + *p++ = pp[1]; + *p++ = pp[2]; + *p++ = pp[3]; + } else { + // write no message header chunk stream, fmt is 3 + // @remark, if perfer_cid > 0x3F, that is, use 2B/3B chunk header, + // SRS will rollback to 1B chunk header. + *p++ = 0xC0 | (mh->perfer_cid & 0x3F); + } + + // for c0 + // chunk extended timestamp header, 0 or 4 bytes, big-endian + // + // for c3: + // chunk extended timestamp header, 0 or 4 bytes, big-endian + // 6.1.3. Extended Timestamp + // This field is transmitted only when the normal time stamp in the + // chunk message header is set to 0x00ffffff. If normal time stamp is + // set to any value less than 0x00ffffff, this field MUST NOT be + // present. This field MUST NOT be present if the timestamp field is not + // present. Type 3 chunks MUST NOT have this field. + // adobe changed for Type3 chunk: + // FMLE always sendout the extended-timestamp, + // must send the extended-timestamp to FMS, + // must send the extended-timestamp to flash-player. + // @see: ngx_rtmp_prepare_message + // @see: http://blog.csdn.net/win_lin/article/details/13363699 + // TODO: FIXME: extract to outer. + if (timestamp >= RTMP_EXTENDED_TIMESTAMP) { + pp = (char*)×tamp; + *p++ = pp[3]; + *p++ = pp[2]; + *p++ = pp[1]; + *p++ = pp[0]; + } + + // always has header + return p - cache; +} + diff --git a/trunk/src/rtmp/srs_protocol_utility.hpp b/trunk/src/rtmp/srs_protocol_utility.hpp index 2ec909133f..e1deedd933 100644 --- a/trunk/src/rtmp/srs_protocol_utility.hpp +++ b/trunk/src/rtmp/srs_protocol_utility.hpp @@ -34,6 +34,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include class SrsStream; +class SrsMessageHeader; /** * parse the tcUrl, output the schema, host, vhost, app and port. @@ -103,5 +104,13 @@ extern bool srs_avc_startswith_annexb(SrsStream* stream, int* pnb_start_code = N */ extern bool srs_aac_startswith_adts(SrsStream* stream); +/** +* generate the chunk header for msg. +* @param mh, the header of msg to send. +* @param c0, whether the first chunk, the c0 chunk. +* @return the size of header. +*/ +extern int srs_chunk_header(char* cache, SrsMessageHeader* mh, bool c0); + #endif From d53fd7f5703f8116659350bcb2cf3017a25ce309 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 6 Dec 2014 20:10:05 +0800 Subject: [PATCH 340/800] for bug #251, support mic(message iovs cache). 2.0.61 --- trunk/src/app/srs_app_source.cpp | 53 +++++ trunk/src/app/srs_app_source.hpp | 6 + trunk/src/core/srs_core.hpp | 2 +- trunk/src/core/srs_core_performance.hpp | 9 + trunk/src/kernel/srs_kernel_error.hpp | 1 + trunk/src/main/srs_main_server.cpp | 5 + trunk/src/rtmp/srs_protocol_stack.cpp | 292 ++++++++++++++++++++++++ trunk/src/rtmp/srs_protocol_stack.hpp | 91 +++++++- 8 files changed, 457 insertions(+), 2 deletions(-) diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index 7daf928521..e0b7cc64a3 100644 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -634,6 +634,10 @@ SrsSource::SrsSource(SrsRequest* req) _srs_config->subscribe(this); atc = _srs_config->get_atc(_req->vhost); + +#ifdef SRS_PERF_MW_MSG_IOVS_CACHE + chunk_size = 0; +#endif } SrsSource::~SrsSource() @@ -860,6 +864,26 @@ int SrsSource::on_reload_vhost_dvr(string vhost) return ret; } +int SrsSource::on_reload_vhost_chunk_size(string vhost) +{ + int ret = ERROR_SUCCESS; + + if (_req->vhost != vhost) { + return ret; + } + +#ifdef SRS_PERF_MW_MSG_IOVS_CACHE + int size = _srs_config->get_chunk_size(_req->vhost); + if (chunk_size != size) { + srs_warn("connected clients will error for mic chunk_size changed %d=>%d", + chunk_size, size); + } + chunk_size = size; +#endif + + return ret; +} + int SrsSource::on_reload_vhost_transcode(string vhost) { int ret = ERROR_SUCCESS; @@ -1089,6 +1113,14 @@ int SrsSource::on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* metadata } srs_verbose("initialize shared ptr metadata success."); +#ifdef SRS_PERF_MW_MSG_IOVS_CACHE + if ((ret = cache_metadata->mic_evaluate(chunk_size)) != ERROR_SUCCESS) { + srs_error("mic metadata iovs failed, chunk_size=%d. ret=%d", chunk_size, ret); + return ret; + } + srs_info("mic metadata iovs ok, chunk_size=%d", chunk_size); +#endif + // copy to all consumer if (true) { std::vector::iterator it; @@ -1130,6 +1162,14 @@ int SrsSource::on_audio(SrsCommonMessage* __audio) } srs_verbose("initialize shared ptr audio success."); +#ifdef SRS_PERF_MW_MSG_IOVS_CACHE + if ((ret = msg.mic_evaluate(chunk_size)) != ERROR_SUCCESS) { + srs_error("mic audio iovs failed, chunk_size=%d. ret=%d", chunk_size, ret); + return ret; + } + srs_info("mic audio iovs ok, chunk_size=%d", chunk_size); +#endif + #ifdef SRS_AUTO_HLS if ((ret = hls->on_audio(&msg)) != ERROR_SUCCESS) { srs_warn("hls process audio message failed, ignore and disable hls. ret=%d", ret); @@ -1240,6 +1280,14 @@ int SrsSource::on_video(SrsCommonMessage* __video) } srs_verbose("initialize shared ptr video success."); +#ifdef SRS_PERF_MW_MSG_IOVS_CACHE + if ((ret = msg.mic_evaluate(chunk_size)) != ERROR_SUCCESS) { + srs_error("mic video iovs failed, chunk_size=%d. ret=%d", chunk_size, ret); + return ret; + } + srs_info("mic video iovs ok, chunk_size=%d", chunk_size); +#endif + #ifdef SRS_AUTO_HLS if ((ret = hls->on_video(&msg)) != ERROR_SUCCESS) { srs_warn("hls process video message failed, ignore and disable hls. ret=%d", ret); @@ -1491,6 +1539,11 @@ int SrsSource::on_publish() return ret; } #endif + +#ifdef SRS_PERF_MW_MSG_IOVS_CACHE + chunk_size = _srs_config->get_chunk_size(_req->vhost); + srs_trace("mic use chunk_size=%d to send msgs", chunk_size); +#endif return ret; } diff --git a/trunk/src/app/srs_app_source.hpp b/trunk/src/app/srs_app_source.hpp index d1ec5bae4b..68893de62a 100644 --- a/trunk/src/app/srs_app_source.hpp +++ b/trunk/src/app/srs_app_source.hpp @@ -352,6 +352,11 @@ class SrsSource : public ISrsReloadHandler std::vector forwarders; // for aggregate message SrsStream* aggregate_stream; +#ifdef SRS_PERF_MW_MSG_IOVS_CACHE + // the chunk size for mic, + // update when publish stream. + int chunk_size; +#endif private: /** * the sample rate of audio in metadata. @@ -396,6 +401,7 @@ class SrsSource : public ISrsReloadHandler virtual int on_reload_vhost_forward(std::string vhost); virtual int on_reload_vhost_hls(std::string vhost); virtual int on_reload_vhost_dvr(std::string vhost); + virtual int on_reload_vhost_chunk_size(std::string vhost); virtual int on_reload_vhost_transcode(std::string vhost); // for the tools callback public: diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 214b49472f..ebd5c04731 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 60 +#define VERSION_REVISION 61 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/core/srs_core_performance.hpp b/trunk/src/core/srs_core_performance.hpp index a3dc15ea28..9615a858d1 100644 --- a/trunk/src/core/srs_core_performance.hpp +++ b/trunk/src/core/srs_core_performance.hpp @@ -103,6 +103,15 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * @remark, recomment to 128. */ #define SRS_PERF_MW_MSGS 128 +/** +* use iovs cache in each msg, +* for the shared ptr message, we calc once and used for every copy. +* @see https://github.com/winlinvip/simple-rtmp-server/issues/251 +* @remark if enable this, donot use protocol iovs cache. +* @remark when reload change the chunk size, previous clients error. +*/ +#undef SRS_PERF_MW_MSG_IOVS_CACHE + /** * whether set the socket send buffer size. * @see https://github.com/winlinvip/simple-rtmp-server/issues/251 diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index 51141b107b..006851fcdd 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -134,6 +134,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define ERROR_OpenSslSha256DigestSize 2037 #define ERROR_OpenSslGetPeerPublicKey 2038 #define ERROR_OpenSslComputeSharedKey 2039 +#define ERROR_RTMP_MIC_CHUNKSIZE_CHANGED 2040 // // system control message, // not an error, but special control logic. diff --git a/trunk/src/main/srs_main_server.cpp b/trunk/src/main/srs_main_server.cpp index 2ea3bdaf9d..c568c1421c 100644 --- a/trunk/src/main/srs_main_server.cpp +++ b/trunk/src/main/srs_main_server.cpp @@ -149,6 +149,11 @@ void show_macro_features() #endif srs_trace("system default latency in ms: mw(0-%d) + mr(0-%d) + play-queue(0-%d)", SRS_PERF_MW_SLEEP, possible_mr_latency, SRS_PERF_PLAY_QUEUE*1000); + +#ifndef SRS_PERF_MW_MSG_IOVS_CACHE + srs_warn("MIC(message iovs cache) enabled, the connected clients will be" + "disconneted when reload changed the chunk_size."); +#endif } void check_macro_features() diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp index dbf5df2296..5cdb48c984 100644 --- a/trunk/src/rtmp/srs_protocol_stack.cpp +++ b/trunk/src/rtmp/srs_protocol_stack.cpp @@ -387,16 +387,110 @@ SrsSharedPtrMessage::__SrsSharedPtr::__SrsSharedPtr() payload = NULL; size = 0; shared_count = 0; + +#ifdef SRS_PERF_MW_MSG_IOVS_CACHE + nb_iovs = 0; + iovs = NULL; + chunk_size = 0; +#endif } SrsSharedPtrMessage::__SrsSharedPtr::~__SrsSharedPtr() { srs_freep(payload); + +#ifdef SRS_PERF_MW_MSG_IOVS_CACHE + srs_freep(iovs); +#endif } +#ifdef SRS_PERF_MW_MSG_IOVS_CACHE +int SrsSharedPtrMessage::__SrsSharedPtr::mic_evaluate( + SrsMessageHeader* mh, int chunk_size +) { + int ret = ERROR_SUCCESS; + + // use the chunk size, shuold not be changed. + this->chunk_size = chunk_size; + + // ignore size + srs_chunk_header(mic_c0, mh, true); + mic_c3 = 0xC0 | (mh->perfer_cid & 0x3F); + + // calc number of iovs + nb_chunks = mh->payload_length / chunk_size; + if (mh->payload_length % chunk_size) { + nb_chunks++; + } + nb_iovs = 1/*cid*/ + 1/*size*/ + 1 /*type*/+ 1/*chunk*/; + // left chunks, always cid+chunk. + if (nb_chunks > 0) { + nb_iovs += (nb_chunks - 1) * 2; + } + + // create iovs + srs_freep(iovs); + iovs = new iovec[nb_iovs]; + + // for payload chunks. + char* p = payload; + char* end = p + size; + iovec* iov = iovs + 0; + while (p < end) { + // size of payload. + int payload_size = srs_min(chunk_size, end - p); + + // header, c0 or c3 + if (p == payload) { + // c0, cid+size+type + // cid, 1B + iov[0].iov_base = mic_c0; + iov[0].iov_len = 1; + + // size(payload length), 3B + iov[1].iov_base = mic_c0 + 4; + iov[1].iov_len = 3; + + // type(message type) + iov[2].iov_base = mic_c0 + 7; + iov[2].iov_len = 1; + + // chunk + iov[3].iov_base = p; + iov[3].iov_len = payload_size; + + // move to next iovs. + iov += 4; + } else { + // c3 + iov[0].iov_base = &mic_c3; + iov[0].iov_len = 1; + + // chunk + iov[1].iov_base = p; + iov[1].iov_len = payload_size; + + // move to next iovs. + iov += 2; + } + + // to next chunk + p += payload_size; + } + + return ret; +} +#endif + SrsSharedPtrMessage::SrsSharedPtrMessage() { ptr = NULL; + +#ifdef SRS_PERF_MW_MSG_IOVS_CACHE + mic_etime_present = false; + iovs = NULL; + nb_iovs = 0; +#endif } SrsSharedPtrMessage::~SrsSharedPtrMessage() @@ -408,6 +502,10 @@ SrsSharedPtrMessage::~SrsSharedPtrMessage() ptr->shared_count--; } } + +#ifdef SRS_PERF_MW_MSG_IOVS_CACHE + srs_freep(iovs); +#endif } int SrsSharedPtrMessage::create(SrsCommonMessage* msg) @@ -479,6 +577,153 @@ SrsSharedPtrMessage* SrsSharedPtrMessage::copy() return copy; } +#ifdef SRS_PERF_MW_MSG_IOVS_CACHE +int SrsSharedPtrMessage::mic_evaluate(int chunk_size) +{ + int ret = ERROR_SUCCESS; + + // when chunk size changed, error to disconnect the client.. + if (ptr->chunk_size > 0 && chunk_size != ptr->chunk_size) { + ret = ERROR_RTMP_MIC_CHUNKSIZE_CHANGED; + srs_warn("mic chunk size changed %d=>%d, ret=%d", + ptr->chunk_size, chunk_size, ret); + return ret; + } + + // calc the shared ptr iovs at the first time. + if (ptr->chunk_size <= 0) { + if ((ret = ptr->mic_evaluate(&header, chunk_size)) != ERROR_SUCCESS) { + srs_warn("mic evaluate source iovs failed. ret=%d", ret); + return ret; + } + } + + // calc the private iovs + char* pp = NULL; + + // timestamp for c0/c3 + u_int32_t timestamp = (u_int32_t)header.timestamp; + mic_etime_present = timestamp >= RTMP_EXTENDED_TIMESTAMP; + + // chunk message header, 11 bytes + // timestamp, 3bytes, big-endian + char* p = mic_c0_time; + if (!mic_etime_present) { + pp = (char*)×tamp; + *p++ = pp[2]; + *p++ = pp[1]; + *p++ = pp[0]; + } else { + *p++ = 0xFF; + *p++ = 0xFF; + *p++ = 0xFF; + } + + // stream_id, 4bytes, little-endian + p = mic_c0_sid; + pp = (char*)&header.stream_id; + *p++ = pp[0]; + *p++ = pp[1]; + *p++ = pp[2]; + *p++ = pp[3]; + + // for c0 + // chunk extended timestamp header, 0 or 4 bytes, big-endian + // + // for c3: + // chunk extended timestamp header, 0 or 4 bytes, big-endian + // 6.1.3. Extended Timestamp + // This field is transmitted only when the normal time stamp in the + // chunk message header is set to 0x00ffffff. If normal time stamp is + // set to any value less than 0x00ffffff, this field MUST NOT be + // present. This field MUST NOT be present if the timestamp field is not + // present. Type 3 chunks MUST NOT have this field. + // adobe changed for Type3 chunk: + // FMLE always sendout the extended-timestamp, + // must send the extended-timestamp to FMS, + // must send the extended-timestamp to flash-player. + // @see: ngx_rtmp_prepare_message + // @see: http://blog.csdn.net/win_lin/article/details/13363699 + // TODO: FIXME: extract to outer. + p = mic_etime; + if (mic_etime_present) { + pp = (char*)×tamp; + *p++ = pp[3]; + *p++ = pp[2]; + *p++ = pp[1]; + *p++ = pp[0]; + } + + // calc number of iovs. + nb_iovs = 1/*time*/ + 1/*sid*/; + // insert etime before all chunks. + if (mic_etime_present) { + nb_iovs += ptr->nb_chunks; + } + + // create iovs + srs_freep(iovs); + iovs = new iovec[nb_iovs]; + + // time, 3B + iovs[0].iov_base = mic_c0_time; + iovs[0].iov_len = 3; + + // sid, 4B + iovs[1].iov_base = mic_c0_sid; + iovs[1].iov_len = 4; + + // etime, 4B for each chunks. + for (int i = 2; i < nb_iovs; i++) { + iovs[i].iov_base = mic_etime; + iovs[i].iov_len = 4; + } + + return ret; +} + +int SrsSharedPtrMessage::mic_iovs_count() +{ + return nb_iovs + ptr->nb_iovs; +} + +int SrsSharedPtrMessage::mic_iovs_dump(iovec* _iovs, int _nb_iovs) +{ + int shared_index = 0; + int private_index = 0; + int index = 0; + + // dumps all. + srs_assert(nb_iovs + ptr->nb_iovs <= _nb_iovs); + + // dump the c0 chunk + _iovs[index++] = ptr->iovs[shared_index++]; // cid + _iovs[index++] = iovs[private_index++]; // time + _iovs[index++] = ptr->iovs[shared_index++]; // size + _iovs[index++] = ptr->iovs[shared_index++]; // type + _iovs[index++] = iovs[private_index++]; // sid + if (mic_etime_present) { + _iovs[index++] = iovs[private_index++]; // etime + } + _iovs[index++] = ptr->iovs[shared_index++]; // chunk + + // dump left c3 chunks + for (int i = 1; i < ptr->nb_chunks; i++) { + _iovs[index++] = ptr->iovs[shared_index++]; // cid + if (mic_etime_present) { + _iovs[index++] = iovs[private_index++]; // etime + } + _iovs[index++] = ptr->iovs[shared_index++]; // chunk + } + + srs_assert(index == private_index + shared_index); + srs_assert(index == nb_iovs + ptr->nb_iovs); + srs_assert(index <= _nb_iovs); + + return nb_iovs + ptr->nb_iovs; +} +#endif + SrsProtocol::AckWindowSize::AckWindowSize() { ack_window_size = 0; @@ -498,8 +743,10 @@ SrsProtocol::SrsProtocol(ISrsProtocolReaderWriter* io) // each chunk consumers atleast 2 iovs srs_assert(nb_out_iovs >= 2); +#ifndef SRS_PERF_MW_MSG_IOVS_CACHE warned_c0c3_cache_dry = false; auto_response_when_recv = true; +#endif cs_cache = NULL; if (SRS_PERF_CHUNK_STREAM_CACHE > 0) { @@ -707,6 +954,7 @@ int SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs) { int ret = ERROR_SUCCESS; +#ifndef SRS_PERF_MW_MSG_IOVS_CACHE // TODO: FIXME: use cache system instead. int iov_index = 0; iovec* iov = out_iovs + iov_index; @@ -811,6 +1059,40 @@ int SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs) } } } +#else + // send all iovs for all msgs. + int total_iovs = 0; + for (int i = 0; i < nb_msgs; i++) { + SrsSharedPtrMessage* msg = msgs[i]; + if ((ret = msg->mic_evaluate(out_chunk_size)) != ERROR_SUCCESS) { + srs_error("mic evaluate failed, chunk=%d. ret=%d", out_chunk_size, ret); + return ret; + } + total_iovs += msg->mic_iovs_count(); + } + srs_verbose("mic nb_iovs=%d, max=%d", total_iovs, nb_out_iovs); + + // realloc the iovs if exceed, + // for we donot know how many messges maybe to send entirely, + // we just alloc the iovs, it's ok. + if (total_iovs > nb_out_iovs) { + srs_warn("resize iovs %d => %d, msgs=%d, max_msgs=%d", + nb_out_iovs, total_iovs, nb_msgs, SRS_PERF_MW_MSGS); + + nb_out_iovs = total_iovs; + int realloc_size = sizeof(iovec) * nb_out_iovs; + out_iovs = (iovec*)realloc(out_iovs, realloc_size); + } + + // dumps iovs + int iov_index = 0; + for (int i = 0; i < nb_msgs; i++) { + SrsSharedPtrMessage* msg = msgs[i]; + iov_index += msg->mic_iovs_dump( + out_iovs + iov_index, nb_out_iovs - iov_index + ); + } +#endif // maybe the iovs already sendout when c0c3 cache dry, // so just ignore when no iovs to send. @@ -824,11 +1106,21 @@ int SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs) iovec* iov = out_iovs + i; nb_bytes += iov->iov_len; } + #ifndef SRS_PERF_MW_MSG_IOVS_CACHE srs_info("mw %d msgs %dB in %d iovs, max_msgs=%d, nb_out_iovs=%d", nb_msgs, nb_bytes, iov_index, SRS_PERF_MW_MSGS, nb_out_iovs); + #else + srs_info("mic nb_iovs=%d, max=%d, msgs=%d %dB", + total_iovs, nb_out_iovs, nb_msgs, nb_bytes); + #endif #else + #ifndef SRS_PERF_MW_MSG_IOVS_CACHE srs_info("mw %d msgs in %d iovs, max_msgs=%d, nb_out_iovs=%d", nb_msgs, iov_index, SRS_PERF_MW_MSGS, nb_out_iovs); + #else + srs_info("mic nb_iovs=%d, max=%d, msgs=%d", + total_iovs, nb_out_iovs, nb_msgs); + #endif #endif // the limits of writev iovs. diff --git a/trunk/src/rtmp/srs_protocol_stack.hpp b/trunk/src/rtmp/srs_protocol_stack.hpp index 753787c112..1704588b43 100644 --- a/trunk/src/rtmp/srs_protocol_stack.hpp +++ b/trunk/src/rtmp/srs_protocol_stack.hpp @@ -214,14 +214,82 @@ class SrsSharedPtrMessage class __SrsSharedPtr { public: + // actual shared payload. char* payload; + // size of payload. int size; + // the reference count int shared_count; - + public: + // the iovs cache in shared ptr message. + // @see https://github.com/winlinvip/simple-rtmp-server/issues/251 + #ifdef SRS_PERF_MW_MSG_IOVS_CACHE + /** + * the mic(msg iovs cache). + * why share the cache in msg? + * all msgs of a source are same for: + * 1. cid, all use the same cid, copy from src msg. + * 2. size, all msg size never changed. + * 3. type, type never changed. + * 4. chunk size, all connections in a vhost use the same chunk size. + * the different: + * 1. time and etime, almost different. + * 2. stream id, maybe different, but almost the same. + * @remark, when reload change the chunk size, clients will be disconnected. + */ + // the c0 shared section for all msgs + // 1. cid, 1B, same. + // 2. [*]time, 3B, not same. + // 3. size, 3B, same. + // 4. type, 1B, same. + // 5. [*]stream id, 4B, not same, little-endian. + // 6. [*]etime, 4B, not same. + // the stared field must be calced in each msg. + char mic_c0[16]; + // the c3 headers. + char mic_c3; + // the calced iovs for all msg, + // we assumpt that the chunk size is not changed for a vhost, + // if do changed, the client will got an error msg and disconnect. + iovec* iovs; + int nb_iovs; + // the msgs source chunk size, + // which is evaluated the iovs first time, + // this cannot be changed. + int chunk_size; + // the number of chunks. + int nb_chunks; + #endif + public: __SrsSharedPtr(); virtual ~__SrsSharedPtr(); + public: + #ifdef SRS_PERF_MW_MSG_IOVS_CACHE + /** + * for iovs msg cache, calc the iovs. + * @param chunk_size use the specified chunk size to evaluate the iovs. + */ + virtual int mic_evaluate(SrsMessageHeader* mh, int chunk_size); + #endif }; __SrsSharedPtr* ptr; +private: + // msgs level cache. +#ifdef SRS_PERF_MW_MSG_IOVS_CACHE + // the c0 private section for this + // 1. time, 3B, not same, not used. + // 2. stream id, 4B, almost the same, little-endian. + // 3. etime, 4B, optional, always same for all chunk when present. + // the stared field must be calced in each msg. + char mic_c0_time[3]; + char mic_c0_sid[4]; + char mic_etime[4]; + // whether etime present. + bool mic_etime_present; + // the calced private iovs for this msg + iovec* iovs; + int nb_iovs; +#endif public: SrsSharedPtrMessage(); virtual ~SrsSharedPtrMessage(); @@ -253,6 +321,23 @@ class SrsSharedPtrMessage * @remark, assert object is created. */ virtual SrsSharedPtrMessage* copy(); +public: +#ifdef SRS_PERF_MW_MSG_IOVS_CACHE + /** + * for iovs msg cache, calc the iovs. + * @param chunk_size use the specified chunk size to evaluate the iovs. + */ + virtual int mic_evaluate(int chunk_size); + /** + * count the total iovs needed. + */ + virtual int mic_iovs_count(); + /** + * dump all iovs, the _nb_iovs must equals to mic_iovs_count(). + * @return the dumped count. + */ + virtual int mic_iovs_dump(iovec* _iovs, int _nb_iovs); +#endif }; /** @@ -326,6 +411,9 @@ class SrsProtocol */ iovec* out_iovs; int nb_out_iovs; + // if use iovs cache in each msg, + // donot use protocol level c0c3 cache. +#ifndef SRS_PERF_MW_MSG_IOVS_CACHE /** * output header cache. * used for type0, 11bytes(or 15bytes with extended timestamp) header. @@ -337,6 +425,7 @@ class SrsProtocol char out_c0c3_caches[SRS_CONSTS_C0C3_HEADERS_MAX]; // whether warned user to increase the c0c3 header cache. bool warned_c0c3_cache_dry; +#endif /** * output chunk size, default to 128, set by config. */ From 330819fb74169d345f270857a63083c614cc7309 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 6 Dec 2014 22:50:51 +0800 Subject: [PATCH 341/800] for bug #251, refine the mic algorithm. 2.0.63 --- trunk/src/core/srs_core.hpp | 2 +- trunk/src/core/srs_core_performance.hpp | 19 +- trunk/src/kernel/srs_kernel_error.hpp | 1 + trunk/src/rtmp/srs_protocol_stack.cpp | 252 +++++++++++------------- trunk/src/rtmp/srs_protocol_stack.hpp | 15 +- 5 files changed, 139 insertions(+), 150 deletions(-) diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index ebd5c04731..b666732677 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 61 +#define VERSION_REVISION 63 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/core/srs_core_performance.hpp b/trunk/src/core/srs_core_performance.hpp index 9615a858d1..9bb640965e 100644 --- a/trunk/src/core/srs_core_performance.hpp +++ b/trunk/src/core/srs_core_performance.hpp @@ -97,13 +97,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // the default config of mw. #define SRS_PERF_MW_SLEEP 350 /** -* how many msgs can be send entirely. -* for play clients to get msgs then totally send out. -* for the mw sleep set to 1800, the msgs is about 133. -* @remark, recomment to 128. -*/ -#define SRS_PERF_MW_MSGS 128 -/** * use iovs cache in each msg, * for the shared ptr message, we calc once and used for every copy. * @see https://github.com/winlinvip/simple-rtmp-server/issues/251 @@ -111,6 +104,18 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * @remark when reload change the chunk size, previous clients error. */ #undef SRS_PERF_MW_MSG_IOVS_CACHE +/** +* how many msgs can be send entirely. +* for play clients to get msgs then totally send out. +* for the mw sleep set to 1800, the msgs is about 133. +* @remark, recomment to 128. +* @remark, when mic enabled, use larger iovs cache, to 512. +*/ +#ifndef SRS_PERF_MW_MSG_IOVS_CACHE + #define SRS_PERF_MW_MSGS 128 +#else + #define SRS_PERF_MW_MSGS 512 +#endif /** * whether set the socket send buffer size. diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index 006851fcdd..179a77cb57 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -135,6 +135,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define ERROR_OpenSslGetPeerPublicKey 2038 #define ERROR_OpenSslComputeSharedKey 2039 #define ERROR_RTMP_MIC_CHUNKSIZE_CHANGED 2040 +#define ERROR_RTMP_MIC_CACHE_OVERFLOW 2041 // // system control message, // not an error, but special control logic. diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp index 5cdb48c984..e7a327f643 100644 --- a/trunk/src/rtmp/srs_protocol_stack.cpp +++ b/trunk/src/rtmp/srs_protocol_stack.cpp @@ -422,7 +422,7 @@ int SrsSharedPtrMessage::__SrsSharedPtr::mic_evaluate( if (mh->payload_length % chunk_size) { nb_chunks++; } - nb_iovs = 1/*cid*/ + 1/*size*/ + 1 /*type*/+ 1/*chunk*/; + nb_iovs = 1/*cid*/ + 1/*size*//*type*/+ 1/*chunk*/; // left chunks, always cid+chunk. if (nb_chunks > 0) { nb_iovs += (nb_chunks - 1) * 2; @@ -448,19 +448,16 @@ int SrsSharedPtrMessage::__SrsSharedPtr::mic_evaluate( iov[0].iov_len = 1; // size(payload length), 3B + // type(message type), 1B iov[1].iov_base = mic_c0 + 4; - iov[1].iov_len = 3; - - // type(message type) - iov[2].iov_base = mic_c0 + 7; - iov[2].iov_len = 1; + iov[1].iov_len = 4; // chunk - iov[3].iov_base = p; - iov[3].iov_len = payload_size; + iov[2].iov_base = p; + iov[2].iov_len = payload_size; // move to next iovs. - iov += 4; + iov += 3; } else { // c3 iov[0].iov_base = &mic_c3; @@ -488,8 +485,6 @@ SrsSharedPtrMessage::SrsSharedPtrMessage() #ifdef SRS_PERF_MW_MSG_IOVS_CACHE mic_etime_present = false; - iovs = NULL; - nb_iovs = 0; #endif } @@ -502,10 +497,6 @@ SrsSharedPtrMessage::~SrsSharedPtrMessage() ptr->shared_count--; } } - -#ifdef SRS_PERF_MW_MSG_IOVS_CACHE - srs_freep(iovs); -#endif } int SrsSharedPtrMessage::create(SrsCommonMessage* msg) @@ -598,9 +589,26 @@ int SrsSharedPtrMessage::mic_evaluate(int chunk_size) } } + return ret; +} + +int SrsSharedPtrMessage::mic_iovs_dump(iovec* iovs, int max_nb_iovs) +{ // calc the private iovs char* pp = NULL; + // calc number of iovs. + int nb_iovs = 1/*time*/ + 1/*sid*/; + // insert etime before all chunks. + if (mic_etime_present) { + nb_iovs += ptr->nb_chunks; + } + + // not enough, return nagetive to try another loop. + if (max_nb_iovs < nb_iovs + ptr->nb_iovs) { + return -1; + } + // timestamp for c0/c3 u_int32_t timestamp = (u_int32_t)header.timestamp; mic_etime_present = timestamp >= RTMP_EXTENDED_TIMESTAMP; @@ -654,72 +662,57 @@ int SrsSharedPtrMessage::mic_evaluate(int chunk_size) *p++ = pp[0]; } - // calc number of iovs. - nb_iovs = 1/*time*/ + 1/*sid*/; - // insert etime before all chunks. - if (mic_etime_present) { - nb_iovs += ptr->nb_chunks; - } - - // create iovs - srs_freep(iovs); - iovs = new iovec[nb_iovs]; + // dumps all ovs + iovec* shared = ptr->iovs; + iovec* iov = iovs; + // dump the c0 chunk + // cid + iov->iov_len = shared->iov_len; + iov->iov_base = shared->iov_base; + iov++; shared++; // time, 3B - iovs[0].iov_base = mic_c0_time; - iovs[0].iov_len = 3; - + iov->iov_base = mic_c0_time; + iov->iov_len = 3; + iov++; + // size, type + iov->iov_len = shared->iov_len; + iov->iov_base = shared->iov_base; + iov++; shared++; // sid, 4B - iovs[1].iov_base = mic_c0_sid; - iovs[1].iov_len = 4; - - // etime, 4B for each chunks. - for (int i = 2; i < nb_iovs; i++) { - iovs[i].iov_base = mic_etime; - iovs[i].iov_len = 4; - } - - return ret; -} - -int SrsSharedPtrMessage::mic_iovs_count() -{ - return nb_iovs + ptr->nb_iovs; -} - -int SrsSharedPtrMessage::mic_iovs_dump(iovec* _iovs, int _nb_iovs) -{ - int shared_index = 0; - int private_index = 0; - int index = 0; - - // dumps all. - srs_assert(nb_iovs + ptr->nb_iovs <= _nb_iovs); - - // dump the c0 chunk - _iovs[index++] = ptr->iovs[shared_index++]; // cid - _iovs[index++] = iovs[private_index++]; // time - _iovs[index++] = ptr->iovs[shared_index++]; // size - _iovs[index++] = ptr->iovs[shared_index++]; // type - _iovs[index++] = iovs[private_index++]; // sid + iov->iov_base = mic_c0_sid; + iov->iov_len = 4; + iov++; + // etime, 4B if (mic_etime_present) { - _iovs[index++] = iovs[private_index++]; // etime + // etime + iov->iov_base = mic_etime; + iov->iov_len = 4; + iov++; } - _iovs[index++] = ptr->iovs[shared_index++]; // chunk + // chunk + iov->iov_len = shared->iov_len; + iov->iov_base = shared->iov_base; + iov++; shared++; // dump left c3 chunks for (int i = 1; i < ptr->nb_chunks; i++) { - _iovs[index++] = ptr->iovs[shared_index++]; // cid + // cid + iov->iov_len = shared->iov_len; + iov->iov_base = shared->iov_base; + iov++; shared++; if (mic_etime_present) { - _iovs[index++] = iovs[private_index++]; // etime + // etime + iov->iov_base = mic_etime; + iov->iov_len = 4; + iov++; } - _iovs[index++] = ptr->iovs[shared_index++]; // chunk + // chunk + iov->iov_len = shared->iov_len; + iov->iov_base = shared->iov_base; + iov++; shared++; } - srs_assert(index == private_index + shared_index); - srs_assert(index == nb_iovs + ptr->nb_iovs); - srs_assert(index <= _nb_iovs); - return nb_iovs + ptr->nb_iovs; } #endif @@ -955,7 +948,6 @@ int SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs) int ret = ERROR_SUCCESS; #ifndef SRS_PERF_MW_MSG_IOVS_CACHE - // TODO: FIXME: use cache system instead. int iov_index = 0; iovec* iov = out_iovs + iov_index; @@ -1042,10 +1034,7 @@ int SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs) // when c0c3 cache dry, // sendout all messages and reset the cache, then send again. - if ((ret = skt->writev(out_iovs, iov_index, NULL)) != ERROR_SUCCESS) { - if (!srs_is_client_gracefully_close(ret)) { - srs_error("send msgs with writev failed. ret=%d", ret); - } + if ((ret = do_iovs_send(out_iovs, iov_index)) != ERROR_SUCCESS) { return ret; } @@ -1059,76 +1048,73 @@ int SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs) } } } -#else - // send all iovs for all msgs. - int total_iovs = 0; - for (int i = 0; i < nb_msgs; i++) { - SrsSharedPtrMessage* msg = msgs[i]; - if ((ret = msg->mic_evaluate(out_chunk_size)) != ERROR_SUCCESS) { - srs_error("mic evaluate failed, chunk=%d. ret=%d", out_chunk_size, ret); - return ret; - } - total_iovs += msg->mic_iovs_count(); - } - srs_verbose("mic nb_iovs=%d, max=%d", total_iovs, nb_out_iovs); - - // realloc the iovs if exceed, - // for we donot know how many messges maybe to send entirely, - // we just alloc the iovs, it's ok. - if (total_iovs > nb_out_iovs) { - srs_warn("resize iovs %d => %d, msgs=%d, max_msgs=%d", - nb_out_iovs, total_iovs, nb_msgs, SRS_PERF_MW_MSGS); - - nb_out_iovs = total_iovs; - int realloc_size = sizeof(iovec) * nb_out_iovs; - out_iovs = (iovec*)realloc(out_iovs, realloc_size); - } - - // dumps iovs - int iov_index = 0; - for (int i = 0; i < nb_msgs; i++) { - SrsSharedPtrMessage* msg = msgs[i]; - iov_index += msg->mic_iovs_dump( - out_iovs + iov_index, nb_out_iovs - iov_index - ); - } -#endif // maybe the iovs already sendout when c0c3 cache dry, // so just ignore when no iovs to send. if (iov_index <= 0) { return ret; } -#if 0 - // calc the bytes of iovs, for debug. - int nb_bytes = 0; - for (int i = 0; i < iov_index; i++) { - iovec* iov = out_iovs + i; - nb_bytes += iov->iov_len; - } - #ifndef SRS_PERF_MW_MSG_IOVS_CACHE - srs_info("mw %d msgs %dB in %d iovs, max_msgs=%d, nb_out_iovs=%d", - nb_msgs, nb_bytes, iov_index, SRS_PERF_MW_MSGS, nb_out_iovs); - #else - srs_info("mic nb_iovs=%d, max=%d, msgs=%d %dB", - total_iovs, nb_out_iovs, nb_msgs, nb_bytes); - #endif -#else - #ifndef SRS_PERF_MW_MSG_IOVS_CACHE srs_info("mw %d msgs in %d iovs, max_msgs=%d, nb_out_iovs=%d", nb_msgs, iov_index, SRS_PERF_MW_MSGS, nb_out_iovs); - #else - srs_info("mic nb_iovs=%d, max=%d, msgs=%d", - total_iovs, nb_out_iovs, nb_msgs); - #endif + + return do_iovs_send(out_iovs, iov_index); +#else + // send all iovs for all msgs. + int msg_sent = 0; + while (msg_sent < nb_msgs) { + int iov_index = 0; + for (int i = msg_sent; i < nb_msgs; i++) { + SrsSharedPtrMessage* msg = msgs[i]; + + // evaluate + if ((ret = msg->mic_evaluate(out_chunk_size)) != ERROR_SUCCESS) { + return ret; + } + + // dump msg to iovec. + int ok_iovs = msg->mic_iovs_dump( + out_iovs + iov_index, nb_out_iovs - iov_index + ); + // protocol iovs cache exceed. + if (ok_iovs <= 0) { + break; + } + + // ok, dump next. + msg_sent++; + iov_index += ok_iovs; + } + srs_info("mic nb_iovs=%d, msgs=%d, msg_sent=%d, iovs_sent=%d", + nb_out_iovs, nb_msgs, msg_sent, iov_index); + + // cache not enough. + if (iov_index <= 0) { + ret = ERROR_RTMP_MIC_CACHE_OVERFLOW; + srs_warn("mic iovs overflow, nb_iovs=%d, msgs=%d, msg_sent=%d, iovs_sent=%d, ret=%d", + nb_out_iovs, nb_msgs, msg_sent, iov_index, ret); + return ret; + } + + // send out these iovs. + if ((ret = do_iovs_send(out_iovs, iov_index)) != ERROR_SUCCESS) { + return ret; + } + } + + return ret; #endif +} + +int SrsProtocol::do_iovs_send(iovec* iovs, int size) +{ + int ret = ERROR_SUCCESS; // the limits of writev iovs. static int limits = sysconf(_SC_IOV_MAX); // send in a time. - if (iov_index < limits) { - if ((ret = skt->writev(out_iovs, iov_index, NULL)) != ERROR_SUCCESS) { + if (size < limits) { + if ((ret = skt->writev(iovs, size, NULL)) != ERROR_SUCCESS) { if (!srs_is_client_gracefully_close(ret)) { srs_error("send with writev failed. ret=%d", ret); } @@ -1139,9 +1125,9 @@ int SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs) // send in multiple times. int cur_iov = 0; - while (cur_iov < iov_index) { - int cur_count = srs_min(limits, iov_index - cur_iov); - if ((ret = skt->writev(out_iovs + cur_iov, cur_count, NULL)) != ERROR_SUCCESS) { + while (cur_iov < size) { + int cur_count = srs_min(limits, size - cur_iov); + if ((ret = skt->writev(iovs + cur_iov, cur_count, NULL)) != ERROR_SUCCESS) { if (!srs_is_client_gracefully_close(ret)) { srs_error("send with writev failed. ret=%d", ret); } diff --git a/trunk/src/rtmp/srs_protocol_stack.hpp b/trunk/src/rtmp/srs_protocol_stack.hpp index 1704588b43..56a61a257c 100644 --- a/trunk/src/rtmp/srs_protocol_stack.hpp +++ b/trunk/src/rtmp/srs_protocol_stack.hpp @@ -286,9 +286,6 @@ class SrsSharedPtrMessage char mic_etime[4]; // whether etime present. bool mic_etime_present; - // the calced private iovs for this msg - iovec* iovs; - int nb_iovs; #endif public: SrsSharedPtrMessage(); @@ -329,14 +326,10 @@ class SrsSharedPtrMessage */ virtual int mic_evaluate(int chunk_size); /** - * count the total iovs needed. - */ - virtual int mic_iovs_count(); - /** * dump all iovs, the _nb_iovs must equals to mic_iovs_count(). - * @return the dumped count. + * @return the dumped count. -1 if not enough iovs. */ - virtual int mic_iovs_dump(iovec* _iovs, int _nb_iovs); + virtual int mic_iovs_dump(iovec* iovs, int max_nb_iovs); #endif }; @@ -594,6 +587,10 @@ class SrsProtocol */ virtual int do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs); /** + * send iovs. send multiple times if exceed limits. + */ + virtual int do_iovs_send(iovec* iovs, int size); + /** * underlayer api for send and free packet. */ virtual int do_send_and_free_packet(SrsPacket* packet, int stream_id); From bba6063492454af4ce35d0c2c156176ba9f7bc21 Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 7 Dec 2014 11:25:05 +0800 Subject: [PATCH 342/800] for bug #251, the shared ptr message share the header. 2.0.64 --- trunk/src/app/srs_app_dvr.cpp | 18 ++-- trunk/src/app/srs_app_edge.cpp | 2 +- trunk/src/app/srs_app_hls.cpp | 4 +- trunk/src/app/srs_app_rtmp_conn.cpp | 8 +- trunk/src/app/srs_app_source.cpp | 56 +++++------ trunk/src/core/srs_core.hpp | 2 +- trunk/src/kernel/srs_kernel_consts.hpp | 2 +- trunk/src/rtmp/srs_protocol_stack.cpp | 124 ++++++++++++++++++------ trunk/src/rtmp/srs_protocol_stack.hpp | 65 ++++++++++++- trunk/src/rtmp/srs_protocol_utility.cpp | 122 ++++++++++++++++------- trunk/src/rtmp/srs_protocol_utility.hpp | 25 ++++- 11 files changed, 308 insertions(+), 120 deletions(-) diff --git a/trunk/src/app/srs_app_dvr.cpp b/trunk/src/app/srs_app_dvr.cpp index 0fe5ea1821..0298e0486a 100644 --- a/trunk/src/app/srs_app_dvr.cpp +++ b/trunk/src/app/srs_app_dvr.cpp @@ -213,7 +213,7 @@ int SrsDvrPlan::on_audio(SrsSharedPtrMessage* __audio) char* payload = audio->payload; int size = audio->size; - int64_t timestamp = filter_timestamp(audio->header.timestamp); + int64_t timestamp = filter_timestamp(audio->timestamp); if ((ret = enc->write_audio(timestamp, payload, size)) != ERROR_SUCCESS) { return ret; } @@ -262,7 +262,7 @@ int SrsDvrPlan::on_video(SrsSharedPtrMessage* __video) return ret; } - int32_t timestamp = filter_timestamp(video->header.timestamp); + int32_t timestamp = filter_timestamp(video->timestamp); if ((ret = enc->write_video(timestamp, payload, size)) != ERROR_SUCCESS) { return ret; } @@ -332,20 +332,20 @@ int SrsDvrPlan::update_duration(SrsSharedPtrMessage* msg) // set the segment starttime at first time if (segment->starttime < 0) { - segment->starttime = msg->header.timestamp; + segment->starttime = msg->timestamp; } // no previous packet or timestamp overflow. - if (segment->stream_previous_pkt_time < 0 || segment->stream_previous_pkt_time > msg->header.timestamp) { - segment->stream_previous_pkt_time = msg->header.timestamp; + if (segment->stream_previous_pkt_time < 0 || segment->stream_previous_pkt_time > msg->timestamp) { + segment->stream_previous_pkt_time = msg->timestamp; } // collect segment and stream duration, timestamp overflow is ok. - segment->duration += msg->header.timestamp - segment->stream_previous_pkt_time; - segment->stream_duration += msg->header.timestamp - segment->stream_previous_pkt_time; + segment->duration += msg->timestamp - segment->stream_previous_pkt_time; + segment->stream_duration += msg->timestamp - segment->stream_previous_pkt_time; // update previous packet time - segment->stream_previous_pkt_time = msg->header.timestamp; + segment->stream_previous_pkt_time = msg->timestamp; return ret; } @@ -488,7 +488,7 @@ int SrsDvrSegmentPlan::update_duration(SrsSharedPtrMessage* msg) // when wait keyframe, ignore if no frame arrived. // @see https://github.com/winlinvip/simple-rtmp-server/issues/177 if (_srs_config->get_dvr_wait_keyframe(_req->vhost)) { - if (!msg->header.is_video()) { + if (!msg->is_video()) { return ret; } diff --git a/trunk/src/app/srs_app_edge.cpp b/trunk/src/app/srs_app_edge.cpp index 587c8d499e..a08f7e26e7 100644 --- a/trunk/src/app/srs_app_edge.cpp +++ b/trunk/src/app/srs_app_edge.cpp @@ -560,7 +560,7 @@ int SrsEdgeForwarder::proxy(SrsCommonMessage* msg) } srs_verbose("initialize shared ptr msg success."); - copy.header.stream_id = stream_id; + copy.stream_id = stream_id; if ((ret = queue->enqueue(copy.copy())) != ERROR_SUCCESS) { srs_error("enqueue edge publish msg failed. ret=%d", ret); } diff --git a/trunk/src/app/srs_app_hls.cpp b/trunk/src/app/srs_app_hls.cpp index d36e708c65..889f751a71 100644 --- a/trunk/src/app/srs_app_hls.cpp +++ b/trunk/src/app/srs_app_hls.cpp @@ -1458,7 +1458,7 @@ int SrsHls::on_audio(SrsSharedPtrMessage* __audio) } // the pts calc from rtmp/flv header. - int64_t pts = audio->header.timestamp * 90; + int64_t pts = audio->timestamp * 90; // for pure audio, we need to update the stream dts also. stream_dts = pts; @@ -1503,7 +1503,7 @@ int SrsHls::on_video(SrsSharedPtrMessage* __video) return ret; } - int64_t dts = video->header.timestamp * 90; + int64_t dts = video->timestamp * 90; stream_dts = dts; if ((ret = hls_cache->write_video(codec, muxer, dts, sample)) != ERROR_SUCCESS) { srs_error("hls cache write video failed. ret=%d", ret); diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index b27b3213c3..de25901a93 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -642,11 +642,11 @@ int SrsRtmpConn::do_playing(SrsSource* source, SrsQueueRecvThread* trd) // foreach msg, collect the duration. // @remark: never use msg when sent it, for the protocol sdk will free it. - if (starttime < 0 || starttime > msg->header.timestamp) { - starttime = msg->header.timestamp; + if (starttime < 0 || starttime > msg->timestamp) { + starttime = msg->timestamp; } - duration += msg->header.timestamp - starttime; - starttime = msg->header.timestamp; + duration += msg->timestamp - starttime; + starttime = msg->timestamp; } } diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index e0b7cc64a3..0e30be98c9 100644 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -85,10 +85,10 @@ int SrsRtmpJitter::correct(SrsSharedPtrMessage* msg, int tba, int tbv, SrsRtmpJi if (ag == SrsRtmpJitterAlgorithmZERO) { // for the first time, last_pkt_correct_time is zero. // while when timestamp overflow, the timestamp become smaller, reset the last_pkt_correct_time. - if (last_pkt_correct_time <= 0 || last_pkt_correct_time > msg->header.timestamp) { - last_pkt_correct_time = msg->header.timestamp; + if (last_pkt_correct_time <= 0 || last_pkt_correct_time > msg->timestamp) { + last_pkt_correct_time = msg->timestamp; } - msg->header.timestamp -= last_pkt_correct_time; + msg->timestamp -= last_pkt_correct_time; return ret; } @@ -99,8 +99,8 @@ int SrsRtmpJitter::correct(SrsSharedPtrMessage* msg, int tba, int tbv, SrsRtmpJi // full jitter algorithm, do jitter correct. // set to 0 for metadata. - if (!msg->header.is_audio() && !msg->header.is_video()) { - msg->header.timestamp = 0; + if (!msg->is_av()) { + msg->timestamp = 0; return ret; } @@ -117,15 +117,15 @@ int SrsRtmpJitter::correct(SrsSharedPtrMessage* msg, int tba, int tbv, SrsRtmpJi * 3. last_pkt_correct_time: simply add the positive delta, * and enforce the time monotonically. */ - int64_t time = msg->header.timestamp; + int64_t time = msg->timestamp; int64_t delta = time - last_pkt_time; // if jitter detected, reset the delta. if (delta < 0 || delta > CONST_MAX_JITTER_MS) { // calc the right diff by audio sample rate - if (msg->header.is_audio() && sample_rate > 0) { + if (msg->is_audio() && sample_rate > 0) { delta = (int64_t)(delta * 1000.0 / sample_rate); - } else if (msg->header.is_video() && frame_rate > 0) { + } else if (msg->is_video() && frame_rate > 0) { delta = (int64_t)(delta * 1.0 / frame_rate); } else { delta = DEFAULT_FRAME_TIME_MS; @@ -145,7 +145,7 @@ int SrsRtmpJitter::correct(SrsSharedPtrMessage* msg, int tba, int tbv, SrsRtmpJi last_pkt_correct_time = srs_max(0, last_pkt_correct_time + delta); - msg->header.timestamp = last_pkt_correct_time; + msg->timestamp = last_pkt_correct_time; last_pkt_time = time; return ret; @@ -186,12 +186,12 @@ int SrsMessageQueue::enqueue(SrsSharedPtrMessage* msg) { int ret = ERROR_SUCCESS; - if (msg->header.is_audio() || msg->header.is_video()) { + if (msg->is_av()) { if (av_start_time == -1) { - av_start_time = msg->header.timestamp; + av_start_time = msg->timestamp; } - av_end_time = msg->header.timestamp; + av_end_time = msg->timestamp; } msgs.push_back(msg); @@ -221,7 +221,7 @@ int SrsMessageQueue::dump_packets(int max_count, SrsSharedPtrMessage** pmsgs, in } SrsSharedPtrMessage* last = omsgs[count - 1]; - av_start_time = last->header.timestamp; + av_start_time = last->timestamp; if (count >= nb_msgs) { // the pmsgs is big enough and clear msgs at most time. @@ -248,13 +248,13 @@ void SrsMessageQueue::shrink() for (int i = 1; i < (int)msgs.size(); i++) { SrsSharedPtrMessage* msg = msgs[i]; - if (msg->header.is_video()) { + if (msg->is_video()) { if (SrsFlvCodec::video_is_keyframe(msg->payload, msg->size)) { // the max frame index to remove. iframe_index = i; // set the start time, we will remove until this frame. - av_start_time = msg->header.timestamp; + av_start_time = msg->timestamp; break; } @@ -471,7 +471,7 @@ int SrsGopCache::cache(SrsSharedPtrMessage* __msg) } // got video, update the video count if acceptable - if (msg->header.is_video()) { + if (msg->is_video()) { cached_video_count++; audio_after_last_video_count = 0; } @@ -483,7 +483,7 @@ int SrsGopCache::cache(SrsSharedPtrMessage* __msg) } // ok, gop cache enabled, and got an audio. - if (msg->header.is_audio()) { + if (msg->is_audio()) { audio_after_last_video_count++; } @@ -495,7 +495,7 @@ int SrsGopCache::cache(SrsSharedPtrMessage* __msg) } // clear gop cache when got key frame - if (msg->header.is_video() && SrsFlvCodec::video_is_keyframe(msg->payload, msg->size)) { + if (msg->is_video() && SrsFlvCodec::video_is_keyframe(msg->payload, msg->size)) { srs_info("clear gop cache when got keyframe. vcount=%d, count=%d", cached_video_count, (int)gop_cache.size()); @@ -556,7 +556,7 @@ int64_t SrsGopCache::start_time() SrsSharedPtrMessage* msg = gop_cache[0]; srs_assert(msg); - return msg->header.timestamp; + return msg->timestamp; } bool SrsGopCache::pure_audio() @@ -1239,7 +1239,7 @@ int SrsSource::on_audio(SrsCommonMessage* __audio) srs_trace("%dB audio sh, " "codec(%d, profile=%d, %dchannels, %dkbps, %dHZ), " "flv(%dbits, %dchannels, %dHZ)", - msg.header.payload_length, codec.audio_codec_id, + msg.size, codec.audio_codec_id, codec.aac_profile, codec.aac_channels, codec.audio_data_rate / 1000, aac_sample_rates[codec.aac_sample_rate], flv_sample_sizes[sample.sound_size], flv_sound_types[sample.sound_type], @@ -1257,10 +1257,10 @@ int SrsSource::on_audio(SrsCommonMessage* __audio) // if atc, update the sequence header to abs time. if (atc) { if (cache_sh_audio) { - cache_sh_audio->header.timestamp = msg.header.timestamp; + cache_sh_audio->timestamp = msg.timestamp; } if (cache_metadata) { - cache_metadata->header.timestamp = msg.header.timestamp; + cache_metadata->timestamp = msg.timestamp; } } @@ -1352,7 +1352,7 @@ int SrsSource::on_video(SrsCommonMessage* __video) srs_trace("%dB video sh, " "codec(%d, profile=%d, level=%d, %dx%d, %dkbps, %dfps, %ds)", - msg.header.payload_length, codec.video_codec_id, + msg.size, codec.video_codec_id, codec.avc_profile, codec.avc_level, codec.width, codec.height, codec.video_data_rate / 1000, codec.frame_rate, codec.duration); return ret; @@ -1368,10 +1368,10 @@ int SrsSource::on_video(SrsCommonMessage* __video) // if atc, update the sequence header to abs time. if (atc) { if (cache_sh_video) { - cache_sh_video->header.timestamp = msg.header.timestamp; + cache_sh_video->timestamp = msg.timestamp; } if (cache_metadata) { - cache_metadata->header.timestamp = msg.header.timestamp; + cache_metadata->timestamp = msg.timestamp; } } @@ -1593,13 +1593,13 @@ void SrsSource::on_unpublish() // if atc, update the sequence header to gop cache time. if (atc && !gop_cache->empty()) { if (cache_metadata) { - cache_metadata->header.timestamp = gop_cache->start_time(); + cache_metadata->timestamp = gop_cache->start_time(); } if (cache_sh_video) { - cache_sh_video->header.timestamp = gop_cache->start_time(); + cache_sh_video->timestamp = gop_cache->start_time(); } if (cache_sh_audio) { - cache_sh_audio->header.timestamp = gop_cache->start_time(); + cache_sh_audio->timestamp = gop_cache->start_time(); } } diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index b666732677..ddf9b0d4f2 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 63 +#define VERSION_REVISION 64 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/kernel/srs_kernel_consts.hpp b/trunk/src/kernel/srs_kernel_consts.hpp index 5b5860c0c9..e761223290 100644 --- a/trunk/src/kernel/srs_kernel_consts.hpp +++ b/trunk/src/kernel/srs_kernel_consts.hpp @@ -96,7 +96,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * that is, 1+4=5bytes. */ // always use fmt0 as cache. -//#define SRS_CONSTS_RTMP_MAX_FMT3_HEADER_SIZE 5 +#define SRS_CONSTS_RTMP_MAX_FMT3_HEADER_SIZE 5 /** * for performance issue, diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp index e7a327f643..b5b8c10aed 100644 --- a/trunk/src/rtmp/srs_protocol_stack.cpp +++ b/trunk/src/rtmp/srs_protocol_stack.cpp @@ -405,21 +405,25 @@ SrsSharedPtrMessage::__SrsSharedPtr::~__SrsSharedPtr() } #ifdef SRS_PERF_MW_MSG_IOVS_CACHE -int SrsSharedPtrMessage::__SrsSharedPtr::mic_evaluate( - SrsMessageHeader* mh, int chunk_size -) { +int SrsSharedPtrMessage::__SrsSharedPtr::mic_evaluate(int chunk_size) +{ int ret = ERROR_SUCCESS; // use the chunk size, shuold not be changed. this->chunk_size = chunk_size; - // ignore size - srs_chunk_header(mic_c0, mh, true); - mic_c3 = 0xC0 | (mh->perfer_cid & 0x3F); + // c0 header + int nbh = srs_chunk_header_c0( + header.perfer_cid, 0, header.payload_length, + header.message_type, 0, + mic_c0, sizeof(mic_c0)); + srs_assert(nbh > 0);; + // c3 header + mic_c3 = 0xC0 | (header.perfer_cid & 0x3F); // calc number of iovs - nb_chunks = mh->payload_length / chunk_size; - if (mh->payload_length % chunk_size) { + nb_chunks = header.payload_length / chunk_size; + if (header.payload_length % chunk_size) { nb_chunks++; } nb_iovs = 1/*cid*/ + 1/*size*//*type*/+ 1/*chunk*/; @@ -529,12 +533,14 @@ int SrsSharedPtrMessage::create(SrsMessageHeader* pheader, char* payload, int si return ret; } - header = *pheader; - header.payload_length = size; - ptr = new __SrsSharedPtr(); // direct attach the data. + ptr->header.message_type = pheader->message_type; + ptr->header.payload_length = size; + ptr->header.perfer_cid = pheader->perfer_cid; + this->timestamp = pheader->timestamp; + this->stream_id = pheader->stream_id; ptr->payload = payload; ptr->size = size; @@ -551,17 +557,68 @@ int SrsSharedPtrMessage::count() return ptr->shared_count; } +bool SrsSharedPtrMessage::check(int stream_id) +{ + // we donot use the complex basic header, + // ensure the basic header is 1bytes. + if (ptr->header.perfer_cid < 2) { + srs_info("change the chunk_id=%d to default=%d", + ptr->header.perfer_cid, RTMP_CID_ProtocolControl); + ptr->header.perfer_cid = RTMP_CID_ProtocolControl; + } + + // we assume that the stream_id in a group must be the same. + if (this->stream_id == stream_id) { + return true; + } + this->stream_id = stream_id; + + return false; +} + +bool SrsSharedPtrMessage::is_av() +{ + return ptr->header.message_type == RTMP_MSG_AudioMessage + || ptr->header.message_type == RTMP_MSG_VideoMessage; +} + +bool SrsSharedPtrMessage::is_audio() +{ + return ptr->header.message_type == RTMP_MSG_AudioMessage; +} + +bool SrsSharedPtrMessage::is_video() +{ + return ptr->header.message_type == RTMP_MSG_VideoMessage; +} + +#ifndef SRS_PERF_MW_MSG_IOVS_CACHE +int SrsSharedPtrMessage::chunk_header(char* cache, int nb_cache, bool c0) +{ + if (c0) { + return srs_chunk_header_c0( + ptr->header.perfer_cid, timestamp, ptr->header.payload_length, + ptr->header.message_type, stream_id, + cache, nb_cache); + } else { + return srs_chunk_header_c3( + ptr->header.perfer_cid, timestamp, + cache, nb_cache); + } +} +#endif + SrsSharedPtrMessage* SrsSharedPtrMessage::copy() { srs_assert(ptr); SrsSharedPtrMessage* copy = new SrsSharedPtrMessage(); - copy->header = header; - copy->ptr = ptr; ptr->shared_count++; + copy->timestamp = timestamp; + copy->stream_id = stream_id; copy->payload = ptr->payload; copy->size = ptr->size; @@ -583,7 +640,7 @@ int SrsSharedPtrMessage::mic_evaluate(int chunk_size) // calc the shared ptr iovs at the first time. if (ptr->chunk_size <= 0) { - if ((ret = ptr->mic_evaluate(&header, chunk_size)) != ERROR_SUCCESS) { + if ((ret = ptr->mic_evaluate(chunk_size)) != ERROR_SUCCESS) { srs_warn("mic evaluate source iovs failed. ret=%d", ret); return ret; } @@ -610,7 +667,7 @@ int SrsSharedPtrMessage::mic_iovs_dump(iovec* iovs, int max_nb_iovs) } // timestamp for c0/c3 - u_int32_t timestamp = (u_int32_t)header.timestamp; + u_int32_t timestamp = (u_int32_t)this->timestamp; mic_etime_present = timestamp >= RTMP_EXTENDED_TIMESTAMP; // chunk message header, 11 bytes @@ -629,7 +686,7 @@ int SrsSharedPtrMessage::mic_iovs_dump(iovec* iovs, int max_nb_iovs) // stream_id, 4bytes, little-endian p = mic_c0_sid; - pp = (char*)&header.stream_id; + pp = (char*)&stream_id; *p++ = pp[0]; *p++ = pp[1]; *p++ = pp[2]; @@ -964,14 +1021,6 @@ int SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs) srs_info("ignore empty message."); continue; } - - // we donot use the complex basic header, - // ensure the basic header is 1bytes. - if (msg->header.perfer_cid < 2) { - srs_info("change the chunk_id=%d to default=%d", - msg->header.perfer_cid, RTMP_CID_ProtocolControl); - msg->header.perfer_cid = RTMP_CID_ProtocolControl; - } // p set to current write position, // it's ok when payload is NULL and size is 0. @@ -981,7 +1030,8 @@ int SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs) // always write the header event payload is empty. while (p < pend) { // always has header - int nbh = srs_chunk_header(c0c3_cache, &msg->header, p == msg->payload); + int nb_cache = SRS_CONSTS_C0C3_HEADERS_MAX - c0c3_cache_index; + int nbh = msg->chunk_header(c0c3_cache, nb_cache, p == msg->payload); srs_assert(nbh > 0); // header iov @@ -1066,8 +1116,8 @@ int SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs) for (int i = msg_sent; i < nb_msgs; i++) { SrsSharedPtrMessage* msg = msgs[i]; - // evaluate - if ((ret = msg->mic_evaluate(out_chunk_size)) != ERROR_SUCCESS) { + // evaluate the first + if (i == 0 && (ret = msg->mic_evaluate(out_chunk_size)) != ERROR_SUCCESS) { return ret; } @@ -1185,7 +1235,18 @@ int SrsProtocol::do_simple_send(SrsMessageHeader* mh, char* payload, int size) char* end = p + size; char c0c3[SRS_CONSTS_RTMP_MAX_FMT0_HEADER_SIZE]; while (p < end) { - int nbh = srs_chunk_header(c0c3, mh, p == payload); + int nbh = 0; + if (p == payload) { + nbh = srs_chunk_header_c0( + mh->perfer_cid, mh->timestamp, mh->payload_length, + mh->message_type, mh->stream_id, + c0c3, sizeof(c0c3)); + } else { + nbh = srs_chunk_header_c3( + mh->perfer_cid, mh->timestamp, + c0c3, sizeof(c0c3)); + } + srs_assert(nbh > 0);; iovec iovs[2]; iovs[0].iov_base = c0c3; @@ -1388,11 +1449,12 @@ int SrsProtocol::send_and_free_messages(SrsSharedPtrMessage** msgs, int nb_msgs, // update the stream id in header. for (int i = 0; i < nb_msgs; i++) { SrsSharedPtrMessage* msg = msgs[i]; - // we assume that the stream_id in a group must be the same. - if (msg->header.stream_id == stream_id) { + + // check perfer cid and stream, + // when one msg stream id is ok, ignore left. + if (msg->check(stream_id)) { break; } - msg->header.stream_id = stream_id; } // donot use the auto free to free the msg, diff --git a/trunk/src/rtmp/srs_protocol_stack.hpp b/trunk/src/rtmp/srs_protocol_stack.hpp index 56a61a257c..eec6cdc103 100644 --- a/trunk/src/rtmp/srs_protocol_stack.hpp +++ b/trunk/src/rtmp/srs_protocol_stack.hpp @@ -181,6 +181,32 @@ class SrsCommonMessage virtual ~SrsCommonMessage(); }; +/** +* the message header for shared ptr message. +* only the message for all msgs are same. +*/ +struct SrsSharedMessageHeader +{ + /** + * 3bytes. + * Three-byte field that represents the size of the payload in bytes. + * It is set in big-endian format. + */ + int32_t payload_length; + /** + * 1byte. + * One byte field to represent the message type. A range of type IDs + * (1-7) are reserved for protocol control messages. + */ + int8_t message_type; + /** + * get the perfered cid(chunk stream id) which sendout over. + * set at decoding, and canbe used for directly send message, + * for example, dispatch to all connections. + */ + int perfer_cid; +}; + /** * shared ptr message. * for audio/video/data message that need less memory copy. @@ -194,7 +220,22 @@ class SrsSharedPtrMessage { // 4.1. Message Header public: - SrsMessageHeader header; + // the header can shared, only set the timestamp and stream id. + // @see https://github.com/winlinvip/simple-rtmp-server/issues/251 + //SrsSharedMessageHeader header; + /** + * Four-byte field that contains a timestamp of the message. + * The 4 bytes are packed in the big-endian order. + * @remark, used as calc timestamp when decode and encode time. + * @remark, we use 64bits for large time for jitter detect and hls. + */ + int64_t timestamp; + /** + * 4bytes. + * Four-byte field that identifies the stream of the message. These + * bytes are set in big-endian format. + */ + int32_t stream_id; // 4.2. Message Payload public: /** @@ -214,6 +255,9 @@ class SrsSharedPtrMessage class __SrsSharedPtr { public: + // shared message header. + // @see https://github.com/winlinvip/simple-rtmp-server/issues/251 + SrsSharedMessageHeader header; // actual shared payload. char* payload; // size of payload. @@ -269,7 +313,7 @@ class SrsSharedPtrMessage * for iovs msg cache, calc the iovs. * @param chunk_size use the specified chunk size to evaluate the iovs. */ - virtual int mic_evaluate(SrsMessageHeader* mh, int chunk_size); + virtual int mic_evaluate(int chunk_size); #endif }; __SrsSharedPtr* ptr; @@ -312,6 +356,23 @@ class SrsSharedPtrMessage * @remark, assert object is created. */ virtual int count(); + /** + * check perfer cid and stream id. + * @return whether stream id already set. + */ + virtual bool check(int stream_id); +public: + virtual bool is_av(); + virtual bool is_audio(); + virtual bool is_video(); +public: +#ifndef SRS_PERF_MW_MSG_IOVS_CACHE + /** + * generate the chunk header to cache. + * @return the size of header. + */ + virtual int chunk_header(char* cache, int nb_cache, bool c0); +#endif public: /** * copy current shared ptr message, use ref-count. diff --git a/trunk/src/rtmp/srs_protocol_utility.cpp b/trunk/src/rtmp/srs_protocol_utility.cpp index ac4ea8ad69..defb2b26ff 100644 --- a/trunk/src/rtmp/srs_protocol_utility.cpp +++ b/trunk/src/rtmp/srs_protocol_utility.cpp @@ -204,7 +204,11 @@ bool srs_aac_startswith_adts(SrsStream* stream) return true; } -int srs_chunk_header(char* cache, SrsMessageHeader* mh, bool c0) +int srs_chunk_header_c0( + int perfer_cid, u_int32_t timestamp, int32_t payload_length, + int8_t message_type, int32_t stream_id, + char* cache, int nb_cache +) { // to directly set the field. char* pp = NULL; @@ -212,48 +216,94 @@ int srs_chunk_header(char* cache, SrsMessageHeader* mh, bool c0) // generate the header. char* p = cache; - // timestamp for c0/c3 - u_int32_t timestamp = (u_int32_t)mh->timestamp; + // no header. + if (nb_cache < SRS_CONSTS_RTMP_MAX_FMT0_HEADER_SIZE) { + return 0; + } - if (c0) { - // write new chunk stream header, fmt is 0 - *p++ = 0x00 | (mh->perfer_cid & 0x3F); - - // chunk message header, 11 bytes - // timestamp, 3bytes, big-endian - if (timestamp < RTMP_EXTENDED_TIMESTAMP) { - pp = (char*)×tamp; - *p++ = pp[2]; - *p++ = pp[1]; - *p++ = pp[0]; - } else { - *p++ = 0xFF; - *p++ = 0xFF; - *p++ = 0xFF; - } - - // message_length, 3bytes, big-endian - pp = (char*)&mh->payload_length; + // write new chunk stream header, fmt is 0 + *p++ = 0x00 | (perfer_cid & 0x3F); + + // chunk message header, 11 bytes + // timestamp, 3bytes, big-endian + if (timestamp < RTMP_EXTENDED_TIMESTAMP) { + pp = (char*)×tamp; *p++ = pp[2]; *p++ = pp[1]; *p++ = pp[0]; - - // message_type, 1bytes - *p++ = mh->message_type; - - // stream_id, 4bytes, little-endian - pp = (char*)&mh->stream_id; - *p++ = pp[0]; - *p++ = pp[1]; - *p++ = pp[2]; - *p++ = pp[3]; } else { - // write no message header chunk stream, fmt is 3 - // @remark, if perfer_cid > 0x3F, that is, use 2B/3B chunk header, - // SRS will rollback to 1B chunk header. - *p++ = 0xC0 | (mh->perfer_cid & 0x3F); + *p++ = 0xFF; + *p++ = 0xFF; + *p++ = 0xFF; } + // message_length, 3bytes, big-endian + pp = (char*)&payload_length; + *p++ = pp[2]; + *p++ = pp[1]; + *p++ = pp[0]; + + // message_type, 1bytes + *p++ = message_type; + + // stream_id, 4bytes, little-endian + pp = (char*)&stream_id; + *p++ = pp[0]; + *p++ = pp[1]; + *p++ = pp[2]; + *p++ = pp[3]; + + // for c0 + // chunk extended timestamp header, 0 or 4 bytes, big-endian + // + // for c3: + // chunk extended timestamp header, 0 or 4 bytes, big-endian + // 6.1.3. Extended Timestamp + // This field is transmitted only when the normal time stamp in the + // chunk message header is set to 0x00ffffff. If normal time stamp is + // set to any value less than 0x00ffffff, this field MUST NOT be + // present. This field MUST NOT be present if the timestamp field is not + // present. Type 3 chunks MUST NOT have this field. + // adobe changed for Type3 chunk: + // FMLE always sendout the extended-timestamp, + // must send the extended-timestamp to FMS, + // must send the extended-timestamp to flash-player. + // @see: ngx_rtmp_prepare_message + // @see: http://blog.csdn.net/win_lin/article/details/13363699 + // TODO: FIXME: extract to outer. + if (timestamp >= RTMP_EXTENDED_TIMESTAMP) { + pp = (char*)×tamp; + *p++ = pp[3]; + *p++ = pp[2]; + *p++ = pp[1]; + *p++ = pp[0]; + } + + // always has header + return p - cache; +} + +int srs_chunk_header_c3( + int perfer_cid, u_int32_t timestamp, + char* cache, int nb_cache +) +{ + // to directly set the field. + char* pp = NULL; + + // generate the header. + char* p = cache; + + // no header. + if (nb_cache < SRS_CONSTS_RTMP_MAX_FMT3_HEADER_SIZE) { + return 0; + } + + // write no message header chunk stream, fmt is 3 + // @remark, if perfer_cid > 0x3F, that is, use 2B/3B chunk header, + // SRS will rollback to 1B chunk header. + *p++ = 0xC0 | (perfer_cid & 0x3F); + // for c0 // chunk extended timestamp header, 0 or 4 bytes, big-endian // diff --git a/trunk/src/rtmp/srs_protocol_utility.hpp b/trunk/src/rtmp/srs_protocol_utility.hpp index e1deedd933..1c5983a935 100644 --- a/trunk/src/rtmp/srs_protocol_utility.hpp +++ b/trunk/src/rtmp/srs_protocol_utility.hpp @@ -105,12 +105,27 @@ extern bool srs_avc_startswith_annexb(SrsStream* stream, int* pnb_start_code = N extern bool srs_aac_startswith_adts(SrsStream* stream); /** -* generate the chunk header for msg. -* @param mh, the header of msg to send. -* @param c0, whether the first chunk, the c0 chunk. -* @return the size of header. +* generate the c0 chunk header for msg. +* @param cache, the cache to write header. +* @param nb_cache, the size of cache. +* @return the size of header. 0 if cache not enough. */ -extern int srs_chunk_header(char* cache, SrsMessageHeader* mh, bool c0); +extern int srs_chunk_header_c0( + int perfer_cid, u_int32_t timestamp, int32_t payload_length, + int8_t message_type, int32_t stream_id, + char* cache, int nb_cache +); + +/** +* generate the c3 chunk header for msg. +* @param cache, the cache to write header. +* @param nb_cache, the size of cache. +* @return the size of header. 0 if cache not enough. +*/ +extern int srs_chunk_header_c3( + int perfer_cid, u_int32_t timestamp, + char* cache, int nb_cache +); #endif From 1bbdae6632b927230658caae643f8d57a3bf052d Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 7 Dec 2014 11:37:51 +0800 Subject: [PATCH 343/800] refine code. --- trunk/src/rtmp/srs_protocol_stack.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp index b5b8c10aed..3b041bcb54 100644 --- a/trunk/src/rtmp/srs_protocol_stack.cpp +++ b/trunk/src/rtmp/srs_protocol_stack.cpp @@ -1039,10 +1039,7 @@ int SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs) iov[0].iov_len = nbh; // payload iov - int payload_size = pend - p; - if (payload_size > out_chunk_size) { - payload_size = out_chunk_size; - } + int payload_size = srs_min(out_chunk_size, pend - p); iov[1].iov_base = p; iov[1].iov_len = payload_size; From 8f72f79504cfc0491dafbd12c00db73e5696a6d3 Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 7 Dec 2014 12:08:38 +0800 Subject: [PATCH 344/800] for bug #251, add queue fast cache. 2.0.65 --- trunk/src/app/srs_app_source.cpp | 105 +++++++++++++++++++++++- trunk/src/app/srs_app_source.hpp | 21 ++++- trunk/src/core/srs_core.hpp | 2 +- trunk/src/core/srs_core_performance.hpp | 7 ++ 4 files changed, 129 insertions(+), 6 deletions(-) diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index 0e30be98c9..287181244d 100644 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -182,7 +182,7 @@ void SrsMessageQueue::set_queue_size(double queue_size) queue_size_ms = (int)(queue_size * 1000); } -int SrsMessageQueue::enqueue(SrsSharedPtrMessage* msg) +int SrsMessageQueue::enqueue(SrsSharedPtrMessage* msg, bool* is_overflow) { int ret = ERROR_SUCCESS; @@ -197,6 +197,11 @@ int SrsMessageQueue::enqueue(SrsSharedPtrMessage* msg) msgs.push_back(msg); while (av_end_time - av_start_time > queue_size_ms) { + // notice the caller queue already overflow and shrinked. + if (is_overflow) { + *is_overflow = true; + } + shrink(); } @@ -310,10 +315,23 @@ SrsConsumer::SrsConsumer(SrsSource* _source) mw_duration = 0; mw_waiting = false; #endif + +#ifdef SRS_PERF_QUEUE_FAST_CACHE + mw_cache = new SrsMessageArray(SRS_PERF_MW_MSGS); + mw_count = 0; + mw_first_pkt = mw_last_pkt = 0; +#endif } SrsConsumer::~SrsConsumer() { +#ifdef SRS_PERF_QUEUE_FAST_CACHE + if (mw_cache) { + mw_cache->free(mw_count); + mw_count = 0; + } + srs_freep(mw_cache); +#endif source->on_consumer_destroy(this); srs_freep(jitter); srs_freep(queue); @@ -351,11 +369,37 @@ int SrsConsumer::enqueue(SrsSharedPtrMessage* __msg, bool atc, int tba, int tbv, } } - if ((ret = queue->enqueue(msg)) != ERROR_SUCCESS) { +#ifdef SRS_PERF_QUEUE_FAST_CACHE + // use fast cache if available + if (mw_count < mw_cache->max) { + // update fast cache timestamps + if (mw_count == 0) { + mw_first_pkt = msg->timestamp; + } + mw_last_pkt = msg->timestamp; + + mw_cache->msgs[mw_count++] = msg; + } else{ + // fast cache is full, use queue. + bool is_overflow = false; + if ((ret = queue->enqueue(msg, &is_overflow)) != ERROR_SUCCESS) { + return ret; + } + // when overflow, clear cache and refresh the fast cache. + if (is_overflow) { + mw_cache->free(mw_count); + if ((ret = dumps_queue_to_fast_cache()) != ERROR_SUCCESS) { + return ret; + } + } + } +#else + if ((ret = queue->enqueue(msg, NULL)) != ERROR_SUCCESS) { return ret; } +#endif - #ifdef SRS_PERF_QUEUE_COND_WAIT +#ifdef SRS_PERF_QUEUE_COND_WAIT // fire the mw when msgs is enough. if (mw_waiting) { int duration_ms = queue->duration(); @@ -367,7 +411,7 @@ int SrsConsumer::enqueue(SrsSharedPtrMessage* __msg, bool atc, int tba, int tbv, mw_waiting = false; } } - #endif +#endif return ret; } @@ -388,12 +432,30 @@ int SrsConsumer::dump_packets(SrsMessageArray* msgs, int& count) return ret; } +#ifdef SRS_PERF_QUEUE_FAST_CACHE + // only dumps an whole array to msgs. + for (int i = 0; i < mw_count; i++) { + msgs->msgs[i] = mw_cache->msgs[i]; + } + count = mw_count; + + // when fast cache is not filled, + // we donot check the queue, direclty zero fast cache. + if (mw_count < mw_cache->max) { + mw_count = 0; + mw_first_pkt = mw_last_pkt = 0; + return ret; + } + + return dumps_queue_to_fast_cache(); +#else // pump msgs from queue. if ((ret = queue->dump_packets(msgs->max, msgs->msgs, count)) != ERROR_SUCCESS) { return ret; } return ret; +#endif } #ifdef SRS_PERF_QUEUE_COND_WAIT @@ -402,6 +464,18 @@ void SrsConsumer::wait(int nb_msgs, int duration) mw_min_msgs = nb_msgs; mw_duration = duration; +#ifdef SRS_PERF_QUEUE_FAST_CACHE + // when fast cache not overflow, always flush. + // so we donot care about the queue. + bool fast_cache_overflow = mw_count >= mw_cache->max; + int duration_ms = (int)(mw_last_pkt - mw_first_pkt); + bool match_min_msgs = mw_count > mw_min_msgs; + + // when fast cache overflow, or duration ok, signal to flush. + if (fast_cache_overflow || (match_min_msgs && duration_ms > mw_duration)) { + return; + } +#else int duration_ms = queue->duration(); bool match_min_msgs = queue->size() > mw_min_msgs; @@ -409,6 +483,7 @@ void SrsConsumer::wait(int nb_msgs, int duration) if (match_min_msgs && duration_ms > mw_duration) { return; } +#endif // the enqueue will notify this cond. mw_waiting = true; @@ -427,6 +502,28 @@ int SrsConsumer::on_play_client_pause(bool is_pause) return ret; } +#ifdef SRS_PERF_QUEUE_FAST_CACHE +int SrsConsumer::dumps_queue_to_fast_cache() +{ + int ret =ERROR_SUCCESS; + + // fill fast cache with queue. + if ((ret = queue->dump_packets(mw_cache->max, mw_cache->msgs, mw_count)) != ERROR_SUCCESS) { + return ret; + } + // set the timestamp when got message. + if (mw_count > 0) { + SrsSharedPtrMessage* first_msg = mw_cache->msgs[0]; + mw_first_pkt = first_msg->timestamp; + + SrsSharedPtrMessage* last_msg = mw_cache->msgs[mw_count - 1]; + mw_last_pkt = last_msg->timestamp; + } + + return ret; +} +#endif + SrsGopCache::SrsGopCache() { cached_video_count = 0; diff --git a/trunk/src/app/srs_app_source.hpp b/trunk/src/app/srs_app_source.hpp index 68893de62a..d1746c0fff 100644 --- a/trunk/src/app/srs_app_source.hpp +++ b/trunk/src/app/srs_app_source.hpp @@ -134,8 +134,9 @@ class SrsMessageQueue /** * 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 int enqueue(SrsSharedPtrMessage* msg); + virtual int enqueue(SrsSharedPtrMessage* msg, bool* is_overflow = NULL); /** * get packets in consumer queue. * @pmsgs SrsSharedPtrMessage*[], used to store the msgs, user must alloc it. @@ -172,6 +173,16 @@ class SrsConsumer int mw_min_msgs; int mw_duration; #endif +#ifdef SRS_PERF_QUEUE_FAST_CACHE + // use fast cache for msgs + // @see https://github.com/winlinvip/simple-rtmp-server/issues/251 + SrsMessageArray* mw_cache; + // the count of msg in fast cache. + int mw_count; + // the packet time in fast cache. + int64_t mw_first_pkt; + int64_t mw_last_pkt; +#endif public: SrsConsumer(SrsSource* _source); virtual ~SrsConsumer(); @@ -219,6 +230,14 @@ class SrsConsumer * when client send the pause message. */ virtual int on_play_client_pause(bool is_pause); +private: +#ifdef SRS_PERF_QUEUE_FAST_CACHE + /** + * dumps the queue to fast cache, + * when fast cache is clear or queue is overflow. + */ + virtual int dumps_queue_to_fast_cache(); +#endif }; /** diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index ddf9b0d4f2..9c6a2092d4 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 64 +#define VERSION_REVISION 65 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/core/srs_core_performance.hpp b/trunk/src/core/srs_core_performance.hpp index 9bb640965e..ffb4788d5e 100644 --- a/trunk/src/core/srs_core_performance.hpp +++ b/trunk/src/core/srs_core_performance.hpp @@ -128,6 +128,13 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #undef SRS_PERF_MW_SO_RCVBUF /** +* whether enable the fast cache. +* @remark this improve performance for large connectios. +* @remark this also introduce complex, default to disable it. +* @see https://github.com/winlinvip/simple-rtmp-server/issues/251 +*/ +#undef SRS_PERF_QUEUE_FAST_CACHE +/** * whether use cond wait to send messages. * @remark this improve performance for large connectios. * @see https://github.com/winlinvip/simple-rtmp-server/issues/251 From 5a2b8afeadcd1bf7c63b7228b416d4c4ba87614e Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 7 Dec 2014 13:26:45 +0800 Subject: [PATCH 345/800] for bug #251, add queue fast vector. 2.0.66 --- trunk/src/app/srs_app_source.cpp | 95 ++++++++++++++++++++++++- trunk/src/app/srs_app_source.hpp | 32 +++++++++ trunk/src/core/srs_core.hpp | 2 +- trunk/src/core/srs_core_performance.hpp | 5 ++ 4 files changed, 131 insertions(+), 3 deletions(-) diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index 287181244d..341c8cf801 100644 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -156,6 +156,93 @@ int SrsRtmpJitter::get_time() return (int)last_pkt_correct_time; } +#ifdef SRS_PERF_QUEUE_FAST_VECTOR +SrsFastVector::SrsFastVector() +{ + count = 0; + nb_msgs = SRS_PERF_MW_MSGS * 2; + msgs = new SrsSharedPtrMessage*[nb_msgs]; +} + +SrsFastVector::~SrsFastVector() +{ + free(); +} + +int SrsFastVector::size() +{ + return count; +} + +int SrsFastVector::begin() +{ + return 0; +} + +int SrsFastVector::end() +{ + return count; +} + +SrsSharedPtrMessage** SrsFastVector::data() +{ + return msgs; +} + +SrsSharedPtrMessage* SrsFastVector::at(int index) +{ + srs_assert(index < count); + return msgs[index]; +} + +void SrsFastVector::clear() +{ + count = 0; +} + +void SrsFastVector::erase(int _begin, int _end) +{ + srs_assert(_begin < _end); + + // move all erased to previous. + for (int i = 0; i < count - _end; i++) { + msgs[_begin + i] = msgs[_end + i]; + } + + // update the count. + count -= _end - _begin; +} + +void SrsFastVector::push_back(SrsSharedPtrMessage* msg) +{ + // increase vector. + if (count >= nb_msgs) { + int size = nb_msgs * 2; + SrsSharedPtrMessage** buf = new SrsSharedPtrMessage*[size]; + for (int i = 0; i < nb_msgs; i++) { + buf[i] = msgs[i]; + } + srs_warn("fast vector incrase %d=>%d", nb_msgs, size); + + // use new array. + srs_freep(msgs); + msgs = buf; + nb_msgs = size; + } + + msgs[count++] = msg; +} + +void SrsFastVector::free() +{ + for (int i = 0; i < count; i++) { + SrsSharedPtrMessage* msg = msgs[i]; + srs_freep(msg); + } + count = 0; +} +#endif + SrsMessageQueue::SrsMessageQueue() { queue_size_ms = 0; @@ -251,7 +338,7 @@ void SrsMessageQueue::shrink() // for when we shrinked, the first is the iframe, // we will directly remove the gop next time. for (int i = 1; i < (int)msgs.size(); i++) { - SrsSharedPtrMessage* msg = msgs[i]; + SrsSharedPtrMessage* msg = msgs.at(i); if (msg->is_video()) { if (SrsFlvCodec::video_is_keyframe(msg->payload, msg->size)) { @@ -281,7 +368,7 @@ void SrsMessageQueue::shrink() // remove the first gop from the front for (int i = 0; i < iframe_index; i++) { - SrsSharedPtrMessage* msg = msgs[i]; + SrsSharedPtrMessage* msg = msgs.at(i); srs_freep(msg); } msgs.erase(msgs.begin(), msgs.begin() + iframe_index); @@ -289,12 +376,16 @@ void SrsMessageQueue::shrink() void SrsMessageQueue::clear() { +#ifndef SRS_PERF_QUEUE_FAST_VECTOR std::vector::iterator it; for (it = msgs.begin(); it != msgs.end(); ++it) { SrsSharedPtrMessage* msg = *it; srs_freep(msg); } +#else + msgs.free(); +#endif msgs.clear(); diff --git a/trunk/src/app/srs_app_source.hpp b/trunk/src/app/srs_app_source.hpp index d1746c0fff..533e9b5a1b 100644 --- a/trunk/src/app/srs_app_source.hpp +++ b/trunk/src/app/srs_app_source.hpp @@ -102,6 +102,34 @@ class SrsRtmpJitter virtual int get_time(); }; +#ifdef SRS_PERF_QUEUE_FAST_VECTOR +/** +* to alloc and increase fixed space, +* fast remove and insert for msgs sender. +* @see https://github.com/winlinvip/simple-rtmp-server/issues/251 +*/ +class SrsFastVector +{ +private: + SrsSharedPtrMessage** msgs; + int nb_msgs; + int count; +public: + SrsFastVector(); + virtual ~SrsFastVector(); +public: + virtual int size(); + virtual int begin(); + virtual int end(); + virtual SrsSharedPtrMessage** data(); + virtual SrsSharedPtrMessage* at(int index); + virtual void clear(); + virtual void erase(int _begin, int _end); + virtual void push_back(SrsSharedPtrMessage* msg); + virtual void free(); +}; +#endif + /** * the message queue for the consumer(client), forwarder. * we limit the size in seconds, drop old messages(the whole gop) if full. @@ -112,7 +140,11 @@ class SrsMessageQueue int64_t av_start_time; int64_t av_end_time; int queue_size_ms; +#ifdef SRS_PERF_QUEUE_FAST_VECTOR + SrsFastVector msgs; +#else std::vector msgs; +#endif public: SrsMessageQueue(); virtual ~SrsMessageQueue(); diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 9c6a2092d4..26c3c1f7cd 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 65 +#define VERSION_REVISION 66 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/core/srs_core_performance.hpp b/trunk/src/core/srs_core_performance.hpp index ffb4788d5e..e37e0cee0a 100644 --- a/trunk/src/core/srs_core_performance.hpp +++ b/trunk/src/core/srs_core_performance.hpp @@ -135,6 +135,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #undef SRS_PERF_QUEUE_FAST_CACHE /** +* whether enable the fast vector for qeueue. +* @see https://github.com/winlinvip/simple-rtmp-server/issues/251 +*/ +#undef SRS_PERF_QUEUE_FAST_VECTOR +/** * whether use cond wait to send messages. * @remark this improve performance for large connectios. * @see https://github.com/winlinvip/simple-rtmp-server/issues/251 From 438ee30ac18a231be29c417a4d471f4b1be7fafe Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 7 Dec 2014 13:37:56 +0800 Subject: [PATCH 346/800] for bug #251, refine the queue fast vector. 2.0.66 --- trunk/src/app/srs_app_source.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index 341c8cf801..5f6f491f33 100644 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -160,7 +160,7 @@ int SrsRtmpJitter::get_time() SrsFastVector::SrsFastVector() { count = 0; - nb_msgs = SRS_PERF_MW_MSGS * 2; + nb_msgs = SRS_PERF_MW_MSGS * 8; msgs = new SrsSharedPtrMessage*[nb_msgs]; } From 55d98fceeda635aaa3af99347822fc65efaa66c9 Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 7 Dec 2014 16:15:19 +0800 Subject: [PATCH 347/800] for bug #251, add min msgs for queue cond wait. --- trunk/src/core/srs_core_performance.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/trunk/src/core/srs_core_performance.hpp b/trunk/src/core/srs_core_performance.hpp index e37e0cee0a..4757c66e8e 100644 --- a/trunk/src/core/srs_core_performance.hpp +++ b/trunk/src/core/srs_core_performance.hpp @@ -139,12 +139,18 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * @see https://github.com/winlinvip/simple-rtmp-server/issues/251 */ #undef SRS_PERF_QUEUE_FAST_VECTOR +#if defined(SRS_PERF_QUEUE_FAST_CACHE) && defined(SRS_PERF_QUEUE_FAST_VECTOR) + #error "fast cache conflict with fast vector" +#endif /** * whether use cond wait to send messages. * @remark this improve performance for large connectios. * @see https://github.com/winlinvip/simple-rtmp-server/issues/251 */ #undef SRS_PERF_QUEUE_COND_WAIT +#ifdef SRS_PERF_QUEUE_COND_WAIT + #define SRS_PERF_MW_MIN_MSGS 8 +#endif /** * how many chunk stream to cache, [0, N]. From 1311b6fe6576fd7b9c6d299b0f8f2e8d202f4bf8 Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 7 Dec 2014 16:31:47 +0800 Subject: [PATCH 348/800] fix #251, support 10k clients. 2.0.67 --- trunk/conf/full.conf | 4 ++-- trunk/src/core/srs_core.hpp | 2 +- trunk/src/core/srs_core_performance.hpp | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index de0ca04987..2e47826d51 100755 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -165,8 +165,8 @@ vhost mrw.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 - mw_latency 350; + # default: 450 + mw_latency 450; } # vhost for edge, edge and origin is the same vhost diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 26c3c1f7cd..b7ea4e366a 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 66 +#define VERSION_REVISION 67 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/core/srs_core_performance.hpp b/trunk/src/core/srs_core_performance.hpp index 4757c66e8e..2ac545e09e 100644 --- a/trunk/src/core/srs_core_performance.hpp +++ b/trunk/src/core/srs_core_performance.hpp @@ -95,7 +95,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 2000 150 300 */ // the default config of mw. -#define SRS_PERF_MW_SLEEP 350 +#define SRS_PERF_MW_SLEEP 450 /** * use iovs cache in each msg, * for the shared ptr message, we calc once and used for every copy. @@ -138,7 +138,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * whether enable the fast vector for qeueue. * @see https://github.com/winlinvip/simple-rtmp-server/issues/251 */ -#undef SRS_PERF_QUEUE_FAST_VECTOR +#define SRS_PERF_QUEUE_FAST_VECTOR #if defined(SRS_PERF_QUEUE_FAST_CACHE) && defined(SRS_PERF_QUEUE_FAST_VECTOR) #error "fast cache conflict with fast vector" #endif @@ -147,7 +147,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * @remark this improve performance for large connectios. * @see https://github.com/winlinvip/simple-rtmp-server/issues/251 */ -#undef SRS_PERF_QUEUE_COND_WAIT +#define SRS_PERF_QUEUE_COND_WAIT #ifdef SRS_PERF_QUEUE_COND_WAIT #define SRS_PERF_MW_MIN_MSGS 8 #endif From ed93c5021893e5f6a8ebf250e955a1a43a075102 Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 7 Dec 2014 16:36:00 +0800 Subject: [PATCH 349/800] update readme. 10k clients. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0841381a6d..92008e891f 100755 --- a/README.md +++ b/README.md @@ -729,6 +729,7 @@ The play benchmark by [st-load](https://github.com/winlinvip/st-load): * 2014-11-22, SRS 2.0.30, 7.5k(7500)clients, 87%CPU, 320MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/58136ec178e3d47db6c90a59875d7e40946936e5) * 2014-12-05, SRS 2.0.55, 8.0k(8000)clients, 89%CPU, 360MB. (mw_sleep=350)[commit](https://github.com/winlinvip/simple-rtmp-server/commit/58136ec178e3d47db6c90a59875d7e40946936e5) * 2014-12-05, SRS 2.0.57, 9.0k(9000)clients, 90%CPU, 468MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/9ee138746f83adc26f0e236ec017f4d68a300004) +* 2014-12-07, SRS 2.0.67, 10k(10000)clients, 95%CPU, 656MB. [commit](https://github.com/winlinvip/simple-rtmp-server/commit/1311b6fe6576fd7b9c6d299b0f8f2e8d202f4bf8) ### Publish benchmark From ffacf1b774a44acf18e0d6dfd700db720ce0bf7b Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 7 Dec 2014 16:45:20 +0800 Subject: [PATCH 350/800] update 2.0 history for 1.0 released. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 49930c805d..0db64a366d 100755 --- a/README.md +++ b/README.md @@ -522,6 +522,7 @@ Supported operating systems and hardware: * v2.0, 2014-10-19, fix [#184](https://github.com/winlinvip/simple-rtmp-server/issues/184), support AnnexB in RTMP body for HLS. 2.0.2 * v2.0, 2014-10-18, remove supports for OSX(darwin). 2.0.1. * v2.0, 2014-10-16, revert github srs README to English. 2.0.0. +* v1.0, 2014-12-05, [1.0 release(1.0.10)](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0) released. 59391 lines. * v1.0, 2014-10-09, [1.0 beta(1.0.0)](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.beta) released. 59316 lines. * v1.0, 2014-10-08, fix [#151](https://github.com/winlinvip/simple-rtmp-server/issues/151), always reap ts whatever audio or video packet. 0.9.223. * v1.0, 2014-10-08, fix [#162](https://github.com/winlinvip/simple-rtmp-server/issues/162), failed if no epoll. 0.9.222. From a3de1c71fc621523724182e229795727a8bff403 Mon Sep 17 00:00:00 2001 From: winlin Date: Mon, 8 Dec 2014 09:49:45 +0800 Subject: [PATCH 351/800] reset the mw_latency to 350. --- trunk/conf/full.conf | 4 ++-- trunk/src/core/srs_core_performance.hpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index 2e47826d51..de0ca04987 100755 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -165,8 +165,8 @@ vhost mrw.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: 450 - mw_latency 450; + # default: 350 + mw_latency 350; } # vhost for edge, edge and origin is the same vhost diff --git a/trunk/src/core/srs_core_performance.hpp b/trunk/src/core/srs_core_performance.hpp index 2ac545e09e..391dd09136 100644 --- a/trunk/src/core/srs_core_performance.hpp +++ b/trunk/src/core/srs_core_performance.hpp @@ -95,7 +95,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 2000 150 300 */ // the default config of mw. -#define SRS_PERF_MW_SLEEP 450 +#define SRS_PERF_MW_SLEEP 350 /** * use iovs cache in each msg, * for the shared ptr message, we calc once and used for every copy. From 6a49ae4ffb1034de6034b0c96412b29754aae382 Mon Sep 17 00:00:00 2001 From: winlin Date: Mon, 8 Dec 2014 09:51:51 +0800 Subject: [PATCH 352/800] update readme, for 10k performance --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0db64a366d..a8a5cb70ff 100755 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ HLS( [CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_DeliveryHLS), [EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_DeliveryHLS) ), -high-performance(7.5k+ clients)( +high-performance(10k+ clients)( [CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Performance), [EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_EN_Performance) ), @@ -448,7 +448,7 @@ Supported operating systems and hardware: 1. Research and simplify st, [bug #182](https://github.com/winlinvip/simple-rtmp-server/issues/182). 1. Support compile [srs-librtmp on windows](https://github.com/winlinvip/srs.librtmp), [bug #213](https://github.com/winlinvip/simple-rtmp-server/issues/213). -1. Support [7.5k+ clients](https://github.com/winlinvip/simple-rtmp-server/issues/217), 4Gbps per process. +1. Support [10k+ clients](https://github.com/winlinvip/simple-rtmp-server/issues/251), 4Gbps per process. 1. Support publish aac adts raw stream( [CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_SrsLibrtmp#publish-audio-raw-stream), [EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_SrsLibrtmp#publish-audio-raw-stream) From 149d5863e94a7be781248353ff2e7dd468386849 Mon Sep 17 00:00:00 2001 From: winlin Date: Mon, 8 Dec 2014 11:06:33 +0800 Subject: [PATCH 353/800] update reame --- README.md | 36 +----------------------------------- 1 file changed, 1 insertion(+), 35 deletions(-) diff --git a/README.md b/README.md index a8a5cb70ff..c21401c974 100755 --- a/README.md +++ b/README.md @@ -486,6 +486,7 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History +* v2.0, 2014-12-08, update wiki for mr([EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_LowLatency#merged-read), [CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_LowLatency#merged-read)) and mw([EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_LowLatency#merged-write), [CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_LowLatency#merged-write)). * v2.0, 2014-12-07, fix [#251](https://github.com/winlinvip/simple-rtmp-server/issues/251), 10k+ clients, use queue cond wait and fast vector. 2.0.67 * v2.0, 2014-12-05, fix [#251](https://github.com/winlinvip/simple-rtmp-server/issues/251), 9k+ clients, use fast cache for msgs queue. 2.0.57 * v2.0, 2014-12-04, fix [#241](https://github.com/winlinvip/simple-rtmp-server/issues/241), add mw(merged-write) config. 2.0.53 @@ -860,41 +861,6 @@ Schema#2: SRS RTMP Edge server pull stream from origin (or upstream SRS RTMP Edge server), then delivery to Client.
-### (plan) SRS Multiple processes Architecture(design by wenjie) - -
-                 +---------------+              +--------+
-                 | upnode server |              + client +
-                 +-------+-------+              +---+----+
-            -------------+------------network-------+---------
-                         |                          |
- +--------+         +----+-----------+         +----+----------+
- | master +--fork->-+ back source(1) +-->-pull-+ stream 1-N(2) +
- +---+----+         +----------------+         +-------+-------+
-     |                                                 |
-     +-------------------------------------fork--->-----+
-     |                           +-------------+
-     +-------------------fork-->-+ http/vod(3) |
-                                 +-------------+
-Remark:
-(1) back source process: create by master process, get stream from 
-    upnode server if edge, create stream if origin, serve the stream 
-    process.
-(2) stream process: create by master process, get stream from back
-    source process, serve the client.
-(3) the embeded mininum http server, also provides vod service. for
-    http server, it provides http api, hls(live/vod) delivery. for
-    vod server, it slice the file to hls(m3u8/ts).
-Remark:
-(a) This multiple processes architecture is design by wenjie, it's a
-    very simple and powerful multiple process architecture, for the
-    master no need to pass between stream process.
-(b) The CLI architecture is similar to this, instead, cli process
-    will collect informations from all stream process, master process
-    only send signals to child processes.
-(c) Maybe multiple thread is ok? By winlin.
-
- ### Bandwidth Test Workflow

From 7150a99f4126ee2ac8b75b19490a6971c7072e81 Mon Sep 17 00:00:00 2001
From: winlin 
Date: Wed, 10 Dec 2014 09:15:38 +0800
Subject: [PATCH 354/800] for bug #251, remove the mic(message iovs cache), no
 use.

---
 trunk/src/app/srs_app_source.cpp        |  53 -----
 trunk/src/app/srs_app_source.hpp        |   6 -
 trunk/src/core/srs_core.hpp             |   2 +-
 trunk/src/core/srs_core_performance.hpp |  15 +-
 trunk/src/main/srs_main_server.cpp      |   5 -
 trunk/src/rtmp/srs_protocol_stack.cpp   | 292 ------------------------
 trunk/src/rtmp/srs_protocol_stack.hpp   |  81 -------
 7 files changed, 2 insertions(+), 452 deletions(-)

diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp
index 5f6f491f33..0f6fc1b01d 100644
--- a/trunk/src/app/srs_app_source.cpp
+++ b/trunk/src/app/srs_app_source.cpp
@@ -822,10 +822,6 @@ SrsSource::SrsSource(SrsRequest* req)
     
     _srs_config->subscribe(this);
     atc = _srs_config->get_atc(_req->vhost);
-    
-#ifdef SRS_PERF_MW_MSG_IOVS_CACHE
-    chunk_size = 0;
-#endif
 }
 
 SrsSource::~SrsSource()
@@ -1052,26 +1048,6 @@ int SrsSource::on_reload_vhost_dvr(string vhost)
     return ret;
 }
 
-int SrsSource::on_reload_vhost_chunk_size(string vhost)
-{
-    int ret = ERROR_SUCCESS;
-    
-    if (_req->vhost != vhost) {
-        return ret;
-    }
-
-#ifdef SRS_PERF_MW_MSG_IOVS_CACHE
-    int size = _srs_config->get_chunk_size(_req->vhost);
-    if (chunk_size != size) {
-        srs_warn("connected clients will error for mic chunk_size changed %d=>%d", 
-            chunk_size, size);
-    }
-    chunk_size = size;
-#endif
-    
-    return ret;
-}
-
 int SrsSource::on_reload_vhost_transcode(string vhost)
 {
     int ret = ERROR_SUCCESS;
@@ -1301,14 +1277,6 @@ int SrsSource::on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* metadata
     }
     srs_verbose("initialize shared ptr metadata success.");
     
-#ifdef SRS_PERF_MW_MSG_IOVS_CACHE
-    if ((ret = cache_metadata->mic_evaluate(chunk_size)) != ERROR_SUCCESS) {
-        srs_error("mic metadata iovs failed, chunk_size=%d. ret=%d", chunk_size, ret);
-        return ret;
-    }
-    srs_info("mic metadata iovs ok, chunk_size=%d", chunk_size);
-#endif
-    
     // copy to all consumer
     if (true) {
         std::vector::iterator it;
@@ -1350,14 +1318,6 @@ int SrsSource::on_audio(SrsCommonMessage* __audio)
     }
     srs_verbose("initialize shared ptr audio success.");
     
-#ifdef SRS_PERF_MW_MSG_IOVS_CACHE
-    if ((ret = msg.mic_evaluate(chunk_size)) != ERROR_SUCCESS) {
-        srs_error("mic audio iovs failed, chunk_size=%d. ret=%d", chunk_size, ret);
-        return ret;
-    }
-    srs_info("mic audio iovs ok, chunk_size=%d", chunk_size);
-#endif
-    
 #ifdef SRS_AUTO_HLS
     if ((ret = hls->on_audio(&msg)) != ERROR_SUCCESS) {
         srs_warn("hls process audio message failed, ignore and disable hls. ret=%d", ret);
@@ -1468,14 +1428,6 @@ int SrsSource::on_video(SrsCommonMessage* __video)
     }
     srs_verbose("initialize shared ptr video success.");
     
-#ifdef SRS_PERF_MW_MSG_IOVS_CACHE
-    if ((ret = msg.mic_evaluate(chunk_size)) != ERROR_SUCCESS) {
-        srs_error("mic video iovs failed, chunk_size=%d. ret=%d", chunk_size, ret);
-        return ret;
-    }
-    srs_info("mic video iovs ok, chunk_size=%d", chunk_size);
-#endif
-    
 #ifdef SRS_AUTO_HLS
     if ((ret = hls->on_video(&msg)) != ERROR_SUCCESS) {
         srs_warn("hls process video message failed, ignore and disable hls. ret=%d", ret);
@@ -1727,11 +1679,6 @@ int SrsSource::on_publish()
         return ret;
     }
 #endif
-    
-#ifdef SRS_PERF_MW_MSG_IOVS_CACHE
-    chunk_size = _srs_config->get_chunk_size(_req->vhost);
-    srs_trace("mic use chunk_size=%d to send msgs", chunk_size);
-#endif
 
     return ret;
 }
diff --git a/trunk/src/app/srs_app_source.hpp b/trunk/src/app/srs_app_source.hpp
index 533e9b5a1b..a0177493c6 100644
--- a/trunk/src/app/srs_app_source.hpp
+++ b/trunk/src/app/srs_app_source.hpp
@@ -403,11 +403,6 @@ class SrsSource : public ISrsReloadHandler
     std::vector forwarders;
     // for aggregate message
     SrsStream* aggregate_stream;
-#ifdef SRS_PERF_MW_MSG_IOVS_CACHE
-    // the chunk size for mic,
-    // update when publish stream.
-    int chunk_size;
-#endif
 private:
     /**
     * the sample rate of audio in metadata.
@@ -452,7 +447,6 @@ class SrsSource : public ISrsReloadHandler
     virtual int on_reload_vhost_forward(std::string vhost);
     virtual int on_reload_vhost_hls(std::string vhost);
     virtual int on_reload_vhost_dvr(std::string vhost);
-    virtual int on_reload_vhost_chunk_size(std::string vhost);
     virtual int on_reload_vhost_transcode(std::string vhost);
 // for the tools callback
 public:
diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp
index b7ea4e366a..b61fa752df 100644
--- a/trunk/src/core/srs_core.hpp
+++ b/trunk/src/core/srs_core.hpp
@@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 // current release version
 #define VERSION_MAJOR       2
 #define VERSION_MINOR       0
-#define VERSION_REVISION    67
+#define VERSION_REVISION    68
 // server info.
 #define RTMP_SIG_SRS_KEY "SRS"
 #define RTMP_SIG_SRS_ROLE "origin/edge server"
diff --git a/trunk/src/core/srs_core_performance.hpp b/trunk/src/core/srs_core_performance.hpp
index 391dd09136..68de5ab34c 100644
--- a/trunk/src/core/srs_core_performance.hpp
+++ b/trunk/src/core/srs_core_performance.hpp
@@ -97,25 +97,12 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 // the default config of mw.
 #define SRS_PERF_MW_SLEEP 350
 /**
-* use iovs cache in each msg,
-* for the shared ptr message, we calc once and used for every copy.
-* @see https://github.com/winlinvip/simple-rtmp-server/issues/251
-* @remark if enable this, donot use protocol iovs cache.
-* @remark when reload change the chunk size, previous clients error.
-*/
-#undef SRS_PERF_MW_MSG_IOVS_CACHE
-/**
 * how many msgs can be send entirely.
 * for play clients to get msgs then totally send out.
 * for the mw sleep set to 1800, the msgs is about 133.
 * @remark, recomment to 128.
-* @remark, when mic enabled, use larger iovs cache, to 512.
 */
-#ifndef SRS_PERF_MW_MSG_IOVS_CACHE
-    #define SRS_PERF_MW_MSGS 128
-#else
-    #define SRS_PERF_MW_MSGS 512
-#endif
+#define SRS_PERF_MW_MSGS 128
 
 /**
 * whether set the socket send buffer size.
diff --git a/trunk/src/main/srs_main_server.cpp b/trunk/src/main/srs_main_server.cpp
index c568c1421c..2ea3bdaf9d 100644
--- a/trunk/src/main/srs_main_server.cpp
+++ b/trunk/src/main/srs_main_server.cpp
@@ -149,11 +149,6 @@ void show_macro_features()
 #endif
     srs_trace("system default latency in ms: mw(0-%d) + mr(0-%d) + play-queue(0-%d)",
         SRS_PERF_MW_SLEEP, possible_mr_latency, SRS_PERF_PLAY_QUEUE*1000);
-        
-#ifndef SRS_PERF_MW_MSG_IOVS_CACHE
-    srs_warn("MIC(message iovs cache) enabled, the connected clients will be"
-        "disconneted when reload changed the chunk_size.");
-#endif
 }
 
 void check_macro_features()
diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp
index 3b041bcb54..3722a7eabe 100644
--- a/trunk/src/rtmp/srs_protocol_stack.cpp
+++ b/trunk/src/rtmp/srs_protocol_stack.cpp
@@ -387,109 +387,16 @@ SrsSharedPtrMessage::__SrsSharedPtr::__SrsSharedPtr()
     payload = NULL;
     size = 0;
     shared_count = 0;
-
-#ifdef SRS_PERF_MW_MSG_IOVS_CACHE
-    nb_iovs = 0;
-    iovs = NULL;
-    chunk_size = 0;
-#endif
 }
 
 SrsSharedPtrMessage::__SrsSharedPtr::~__SrsSharedPtr()
 {
     srs_freep(payload);
-
-#ifdef SRS_PERF_MW_MSG_IOVS_CACHE
-    srs_freep(iovs);
-#endif
 }
 
-#ifdef SRS_PERF_MW_MSG_IOVS_CACHE
-int SrsSharedPtrMessage::__SrsSharedPtr::mic_evaluate(int chunk_size) 
-{
-    int ret = ERROR_SUCCESS;
-    
-    // use the chunk size, shuold not be changed.
-    this->chunk_size = chunk_size;
-    
-    // c0 header
-    int nbh = srs_chunk_header_c0(
-            header.perfer_cid, 0, header.payload_length,
-            header.message_type, 0,
-            mic_c0, sizeof(mic_c0));
-    srs_assert(nbh > 0);;
-    // c3 header
-    mic_c3 = 0xC0 | (header.perfer_cid & 0x3F);
-    
-    // calc number of iovs
-    nb_chunks = header.payload_length / chunk_size;
-    if (header.payload_length % chunk_size) {
-        nb_chunks++;
-    }
-    nb_iovs = 1/*cid*/ + 1/*size*//*type*/+ 1/*chunk*/;
-    // left chunks, always cid+chunk.
-    if (nb_chunks > 0) {
-        nb_iovs += (nb_chunks - 1) * 2;
-    }
-    
-    // create iovs
-    srs_freep(iovs);
-    iovs = new iovec[nb_iovs];
-    
-    // for payload chunks.
-    char* p = payload;
-    char* end = p + size;
-    iovec* iov = iovs + 0;
-    while (p < end) {
-        // size of payload.
-        int payload_size = srs_min(chunk_size, end - p);
-        
-        // header, c0 or c3
-        if (p == payload) {
-            // c0, cid+size+type
-            // cid, 1B
-            iov[0].iov_base = mic_c0;
-            iov[0].iov_len = 1;
-            
-            // size(payload length), 3B
-            // type(message type), 1B
-            iov[1].iov_base = mic_c0 + 4;
-            iov[1].iov_len = 4;
-        
-            // chunk
-            iov[2].iov_base = p;
-            iov[2].iov_len = payload_size;
-            
-            // move to next iovs.
-            iov += 3;
-        } else {
-            // c3
-            iov[0].iov_base = &mic_c3;
-            iov[0].iov_len = 1;
-        
-            // chunk
-            iov[1].iov_base = p;
-            iov[1].iov_len = payload_size;
-            
-            // move to next iovs.
-            iov += 2;
-        }
-        
-        // to next chunk
-        p += payload_size;
-    }
-    
-    return ret;
-}
-#endif
-
 SrsSharedPtrMessage::SrsSharedPtrMessage()
 {
     ptr = NULL;
-    
-#ifdef SRS_PERF_MW_MSG_IOVS_CACHE
-    mic_etime_present = false;
-#endif
 }
 
 SrsSharedPtrMessage::~SrsSharedPtrMessage()
@@ -592,7 +499,6 @@ bool SrsSharedPtrMessage::is_video()
     return ptr->header.message_type == RTMP_MSG_VideoMessage;
 }
 
-#ifndef SRS_PERF_MW_MSG_IOVS_CACHE
 int SrsSharedPtrMessage::chunk_header(char* cache, int nb_cache, bool c0)
 {
     if (c0) {
@@ -606,7 +512,6 @@ int SrsSharedPtrMessage::chunk_header(char* cache, int nb_cache, bool c0)
             cache, nb_cache);
     }
 }
-#endif
 
 SrsSharedPtrMessage* SrsSharedPtrMessage::copy()
 {
@@ -625,155 +530,6 @@ SrsSharedPtrMessage* SrsSharedPtrMessage::copy()
     return copy;
 }
 
-#ifdef SRS_PERF_MW_MSG_IOVS_CACHE
-int SrsSharedPtrMessage::mic_evaluate(int chunk_size)
-{
-    int ret = ERROR_SUCCESS;
-    
-    // when chunk size changed, error to disconnect the client..
-    if (ptr->chunk_size > 0 && chunk_size != ptr->chunk_size) {
-        ret = ERROR_RTMP_MIC_CHUNKSIZE_CHANGED;
-        srs_warn("mic chunk size changed %d=>%d, ret=%d", 
-            ptr->chunk_size, chunk_size, ret);
-        return ret;
-    }
-    
-    // calc the shared ptr iovs at the first time.
-    if (ptr->chunk_size <= 0) {
-        if ((ret = ptr->mic_evaluate(chunk_size)) != ERROR_SUCCESS) {
-            srs_warn("mic evaluate source iovs failed. ret=%d", ret);
-            return ret;
-        }
-    }
-    
-    return ret;
-}
-
-int SrsSharedPtrMessage::mic_iovs_dump(iovec* iovs, int max_nb_iovs) 
-{
-    // calc the private iovs
-    char* pp = NULL;
-    
-    // calc number of iovs.
-    int nb_iovs = 1/*time*/ + 1/*sid*/;
-    // insert etime before all chunks.
-    if (mic_etime_present) {
-        nb_iovs += ptr->nb_chunks;
-    }
-    
-    // not enough, return nagetive to try another loop.
-    if (max_nb_iovs < nb_iovs + ptr->nb_iovs) {
-        return -1;
-    }
-    
-    // timestamp for c0/c3
-    u_int32_t timestamp = (u_int32_t)this->timestamp;
-    mic_etime_present = timestamp >= RTMP_EXTENDED_TIMESTAMP;
-    
-    // chunk message header, 11 bytes
-    // timestamp, 3bytes, big-endian
-    char* p = mic_c0_time;
-    if (!mic_etime_present) {
-        pp = (char*)×tamp;
-        *p++ = pp[2];
-        *p++ = pp[1];
-        *p++ = pp[0];
-    } else {
-        *p++ = 0xFF;
-        *p++ = 0xFF;
-        *p++ = 0xFF;
-    }
-        
-    // stream_id, 4bytes, little-endian
-    p = mic_c0_sid;
-    pp = (char*)&stream_id;
-    *p++ = pp[0];
-    *p++ = pp[1];
-    *p++ = pp[2];
-    *p++ = pp[3];
-    
-    // for c0
-    // chunk extended timestamp header, 0 or 4 bytes, big-endian
-    // 
-    // for c3:
-    // chunk extended timestamp header, 0 or 4 bytes, big-endian
-    // 6.1.3. Extended Timestamp
-    // This field is transmitted only when the normal time stamp in the
-    // chunk message header is set to 0x00ffffff. If normal time stamp is
-    // set to any value less than 0x00ffffff, this field MUST NOT be
-    // present. This field MUST NOT be present if the timestamp field is not
-    // present. Type 3 chunks MUST NOT have this field.
-    // adobe changed for Type3 chunk:
-    //        FMLE always sendout the extended-timestamp,
-    //        must send the extended-timestamp to FMS,
-    //        must send the extended-timestamp to flash-player.
-    // @see: ngx_rtmp_prepare_message
-    // @see: http://blog.csdn.net/win_lin/article/details/13363699
-    // TODO: FIXME: extract to outer.
-    p = mic_etime;
-    if (mic_etime_present) {
-        pp = (char*)×tamp;
-        *p++ = pp[3];
-        *p++ = pp[2];
-        *p++ = pp[1];
-        *p++ = pp[0];
-    }
-    
-    // dumps all ovs
-    iovec* shared = ptr->iovs;
-    iovec* iov = iovs;
-    
-    // dump the c0 chunk
-    // cid
-    iov->iov_len = shared->iov_len;
-    iov->iov_base = shared->iov_base;
-    iov++; shared++;
-    // time, 3B
-    iov->iov_base = mic_c0_time;
-    iov->iov_len = 3;
-    iov++;
-    // size, type
-    iov->iov_len = shared->iov_len;
-    iov->iov_base = shared->iov_base;
-    iov++; shared++;
-    // sid, 4B
-    iov->iov_base = mic_c0_sid;
-    iov->iov_len = 4;
-    iov++;
-    // etime, 4B
-    if (mic_etime_present) {
-        // etime
-        iov->iov_base = mic_etime;
-        iov->iov_len = 4;
-        iov++;
-    }
-    // chunk
-    iov->iov_len = shared->iov_len;
-    iov->iov_base = shared->iov_base;
-    iov++; shared++;
-    
-    // dump left c3 chunks
-    for (int i = 1; i < ptr->nb_chunks; i++) {
-        // cid
-        iov->iov_len = shared->iov_len;
-        iov->iov_base = shared->iov_base;
-        iov++; shared++;
-        if (mic_etime_present) {
-            // etime
-            iov->iov_base = mic_etime;
-            iov->iov_len = 4;
-            iov++;
-        }
-        // chunk
-        iov->iov_len = shared->iov_len;
-        iov->iov_base = shared->iov_base;
-        iov++; shared++;
-    }
-    
-    return nb_iovs + ptr->nb_iovs;
-}
-#endif
-
 SrsProtocol::AckWindowSize::AckWindowSize()
 {
     ack_window_size = 0;
@@ -793,10 +549,8 @@ SrsProtocol::SrsProtocol(ISrsProtocolReaderWriter* io)
     // each chunk consumers atleast 2 iovs
     srs_assert(nb_out_iovs >= 2);
     
-#ifndef SRS_PERF_MW_MSG_IOVS_CACHE
     warned_c0c3_cache_dry = false;
     auto_response_when_recv = true;
-#endif
     
     cs_cache = NULL;
     if (SRS_PERF_CHUNK_STREAM_CACHE > 0) {
@@ -1004,7 +758,6 @@ int SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs)
 {
     int ret = ERROR_SUCCESS;
     
-#ifndef SRS_PERF_MW_MSG_IOVS_CACHE
     int iov_index = 0;
     iovec* iov = out_iovs + iov_index;
     
@@ -1105,51 +858,6 @@ int SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs)
         nb_msgs, iov_index, SRS_PERF_MW_MSGS, nb_out_iovs);
 
     return do_iovs_send(out_iovs, iov_index);
-#else
-    // send all iovs for all msgs.
-    int msg_sent = 0;
-    while (msg_sent < nb_msgs) {
-        int iov_index = 0;
-        for (int i = msg_sent; i < nb_msgs; i++) {
-            SrsSharedPtrMessage* msg = msgs[i];
-            
-            // evaluate the first
-            if (i == 0 && (ret = msg->mic_evaluate(out_chunk_size)) != ERROR_SUCCESS) {
-                return ret;
-            }
-            
-            // dump msg to iovec.
-            int ok_iovs = msg->mic_iovs_dump(
-                out_iovs + iov_index, nb_out_iovs - iov_index
-            );
-            // protocol iovs cache exceed.
-            if (ok_iovs <= 0) {
-                break;
-            }
-            
-            // ok, dump next.
-            msg_sent++;
-            iov_index += ok_iovs;
-        }
-        srs_info("mic nb_iovs=%d, msgs=%d, msg_sent=%d, iovs_sent=%d", 
-            nb_out_iovs, nb_msgs, msg_sent, iov_index);
-        
-        // cache not enough.
-        if (iov_index <= 0) {
-            ret = ERROR_RTMP_MIC_CACHE_OVERFLOW;
-            srs_warn("mic iovs overflow, nb_iovs=%d, msgs=%d, msg_sent=%d, iovs_sent=%d, ret=%d",
-                nb_out_iovs, nb_msgs, msg_sent, iov_index, ret);
-            return ret;
-        }
-        
-        // send out these iovs.
-        if ((ret = do_iovs_send(out_iovs, iov_index)) != ERROR_SUCCESS) {
-            return ret;
-        }
-    }
-    
-    return ret;
-#endif
 }
 
 int SrsProtocol::do_iovs_send(iovec* iovs, int size)
diff --git a/trunk/src/rtmp/srs_protocol_stack.hpp b/trunk/src/rtmp/srs_protocol_stack.hpp
index eec6cdc103..bd8d7bce7c 100644
--- a/trunk/src/rtmp/srs_protocol_stack.hpp
+++ b/trunk/src/rtmp/srs_protocol_stack.hpp
@@ -264,73 +264,11 @@ class SrsSharedPtrMessage
         int size;
         // the reference count
         int shared_count;
-    public:
-        // the iovs cache in shared ptr message.
-        // @see https://github.com/winlinvip/simple-rtmp-server/issues/251
-    #ifdef SRS_PERF_MW_MSG_IOVS_CACHE
-        /**
-        * the mic(msg iovs cache).
-        * why share the cache in msg?
-        * all msgs of a source are same for:
-        *      1. cid, all use the same cid, copy from src msg.
-        *      2. size, all msg size never changed.
-        *      3. type, type never changed.
-        *      4. chunk size, all connections in a vhost use the same chunk size.
-        * the different:
-        *      1. time and etime, almost different.
-        *      2. stream id, maybe different, but almost the same.
-        * @remark, when reload change the chunk size, clients will be disconnected.
-        */
-        // the c0 shared section for all msgs
-        //      1. cid, 1B, same.
-        //      2. [*]time, 3B, not same.
-        //      3. size, 3B, same.
-        //      4. type, 1B, same.
-        //      5. [*]stream id, 4B, not same, little-endian.
-        //      6. [*]etime, 4B, not same.
-        // the stared field must be calced in each msg.
-        char mic_c0[16];
-        // the c3 headers.
-        char mic_c3;
-        // the calced iovs for all msg,
-        // we assumpt that the chunk size is not changed for a vhost,
-        // if do changed, the client will got an error msg and disconnect.
-        iovec* iovs;
-        int nb_iovs;
-        // the msgs source chunk size,
-        // which is evaluated the iovs first time,
-        // this cannot be changed.
-        int chunk_size;
-        // the number of chunks.
-        int nb_chunks;
-    #endif
     public:
         __SrsSharedPtr();
         virtual ~__SrsSharedPtr();
-    public:
-    #ifdef SRS_PERF_MW_MSG_IOVS_CACHE
-        /**
-        * for iovs msg cache, calc the iovs.
-        * @param chunk_size use the specified chunk size to evaluate the iovs.
-        */
-        virtual int mic_evaluate(int chunk_size);
-    #endif
     };
     __SrsSharedPtr* ptr;
-private:
-    // msgs level cache.
-#ifdef SRS_PERF_MW_MSG_IOVS_CACHE
-    // the c0 private section for this
-    //      1. time, 3B, not same, not used.
-    //      2. stream id, 4B, almost the same, little-endian.
-    //      3. etime, 4B, optional, always same for all chunk when present.
-    // the stared field must be calced in each msg.
-    char mic_c0_time[3];
-    char mic_c0_sid[4];
-    char mic_etime[4];
-    // whether etime present.
-    bool mic_etime_present;
-#endif
 public:
     SrsSharedPtrMessage();
     virtual ~SrsSharedPtrMessage();
@@ -366,32 +304,17 @@ class SrsSharedPtrMessage
     virtual bool is_audio();
     virtual bool is_video();
 public:
-#ifndef SRS_PERF_MW_MSG_IOVS_CACHE
     /**
     * generate the chunk header to cache.
     * @return the size of header.
     */
     virtual int chunk_header(char* cache, int nb_cache, bool c0);
-#endif
 public:
     /**
     * copy current shared ptr message, use ref-count.
     * @remark, assert object is created.
     */
     virtual SrsSharedPtrMessage* copy();
-public:
-#ifdef SRS_PERF_MW_MSG_IOVS_CACHE
-    /**
-    * for iovs msg cache, calc the iovs.
-    * @param chunk_size use the specified chunk size to evaluate the iovs.
-    */
-    virtual int mic_evaluate(int chunk_size);
-    /**
-    * dump all iovs, the _nb_iovs must equals to mic_iovs_count().
-    * @return the dumped count. -1 if not enough iovs.
-    */
-    virtual int mic_iovs_dump(iovec* iovs, int max_nb_iovs);
-#endif
 };
 
 /**
@@ -465,9 +388,6 @@ class SrsProtocol
     */
     iovec* out_iovs;
     int nb_out_iovs;
-    // if use iovs cache in each msg,
-    // donot use protocol level c0c3 cache.
-#ifndef SRS_PERF_MW_MSG_IOVS_CACHE
     /**
     * output header cache.
     * used for type0, 11bytes(or 15bytes with extended timestamp) header.
@@ -479,7 +399,6 @@ class SrsProtocol
     char out_c0c3_caches[SRS_CONSTS_C0C3_HEADERS_MAX];
     // whether warned user to increase the c0c3 header cache.
     bool warned_c0c3_cache_dry;
-#endif
     /**
     * output chunk size, default to 128, set by config.
     */

From 6bdd0af728e983b8b43ed44b963dac1e4933a1ae Mon Sep 17 00:00:00 2001
From: winlin 
Date: Wed, 10 Dec 2014 09:18:49 +0800
Subject: [PATCH 355/800] for bug #251, remove the queue fast cache for it's
 too complex, use queue fast vector instead. 2.0.69

---
 trunk/src/app/srs_app_source.cpp        | 96 +------------------------
 trunk/src/app/srs_app_source.hpp        | 18 -----
 trunk/src/core/srs_core.hpp             |  2 +-
 trunk/src/core/srs_core_performance.hpp | 10 ---
 4 files changed, 3 insertions(+), 123 deletions(-)

diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp
index 0f6fc1b01d..68f960adde 100644
--- a/trunk/src/app/srs_app_source.cpp
+++ b/trunk/src/app/srs_app_source.cpp
@@ -406,23 +406,10 @@ SrsConsumer::SrsConsumer(SrsSource* _source)
     mw_duration = 0;
     mw_waiting = false;
 #endif
-    
-#ifdef SRS_PERF_QUEUE_FAST_CACHE
-    mw_cache = new SrsMessageArray(SRS_PERF_MW_MSGS);
-    mw_count = 0;
-    mw_first_pkt = mw_last_pkt = 0;
-#endif
 }
 
 SrsConsumer::~SrsConsumer()
 {
-#ifdef SRS_PERF_QUEUE_FAST_CACHE
-    if (mw_cache) {
-        mw_cache->free(mw_count);
-        mw_count = 0;
-    }
-    srs_freep(mw_cache);
-#endif
     source->on_consumer_destroy(this);
     srs_freep(jitter);
     srs_freep(queue);
@@ -460,35 +447,9 @@ int SrsConsumer::enqueue(SrsSharedPtrMessage* __msg, bool atc, int tba, int tbv,
         }
     }
     
-#ifdef SRS_PERF_QUEUE_FAST_CACHE
-    // use fast cache if available
-    if (mw_count < mw_cache->max) {
-        // update fast cache timestamps
-        if (mw_count == 0) {
-            mw_first_pkt = msg->timestamp;
-        }
-        mw_last_pkt = msg->timestamp;
-        
-        mw_cache->msgs[mw_count++] = msg;
-    } else{
-        // fast cache is full, use queue.
-        bool is_overflow = false;
-        if ((ret = queue->enqueue(msg, &is_overflow)) != ERROR_SUCCESS) {
-            return ret;
-        }
-        // when overflow, clear cache and refresh the fast cache.
-        if (is_overflow) {
-            mw_cache->free(mw_count);
-            if ((ret = dumps_queue_to_fast_cache()) != ERROR_SUCCESS) {
-                return ret;
-            }
-        }
-    }
-#else
     if ((ret = queue->enqueue(msg, NULL)) != ERROR_SUCCESS) {
         return ret;
     }
-#endif
     
 #ifdef SRS_PERF_QUEUE_COND_WAIT
     // fire the mw when msgs is enough.
@@ -522,31 +483,13 @@ int SrsConsumer::dump_packets(SrsMessageArray* msgs, int& count)
     if (paused) {
         return ret;
     }
-    
-#ifdef SRS_PERF_QUEUE_FAST_CACHE
-    // only dumps an whole array to msgs.
-    for (int i = 0; i < mw_count; i++) {
-        msgs->msgs[i] = mw_cache->msgs[i];
-    }
-    count = mw_count;
-    
-    // when fast cache is not filled, 
-    // we donot check the queue, direclty zero fast cache.
-    if (mw_count < mw_cache->max) {
-        mw_count = 0;
-        mw_first_pkt = mw_last_pkt = 0;
-        return ret;
-    }
-    
-    return dumps_queue_to_fast_cache();
-#else
+
     // pump msgs from queue.
     if ((ret = queue->dump_packets(msgs->max, msgs->msgs, count)) != ERROR_SUCCESS) {
         return ret;
     }
     
     return ret;
-#endif
 }
 
 #ifdef SRS_PERF_QUEUE_COND_WAIT
@@ -554,19 +497,7 @@ void SrsConsumer::wait(int nb_msgs, int duration)
 {
     mw_min_msgs = nb_msgs;
     mw_duration = duration;
-    
-#ifdef SRS_PERF_QUEUE_FAST_CACHE
-    // when fast cache not overflow, always flush.
-    // so we donot care about the queue.
-    bool fast_cache_overflow = mw_count >= mw_cache->max;
-    int duration_ms = (int)(mw_last_pkt - mw_first_pkt);
-    bool match_min_msgs = mw_count > mw_min_msgs;
-    
-    // when fast cache overflow, or duration ok, signal to flush.
-    if (fast_cache_overflow || (match_min_msgs && duration_ms > mw_duration)) {
-        return;
-    }
-#else
+
     int duration_ms = queue->duration();
     bool match_min_msgs = queue->size() > mw_min_msgs;
     
@@ -574,7 +505,6 @@ void SrsConsumer::wait(int nb_msgs, int duration)
     if (match_min_msgs && duration_ms > mw_duration) {
         return;
     }
-#endif
     
     // the enqueue will notify this cond.
     mw_waiting = true;
@@ -593,28 +523,6 @@ int SrsConsumer::on_play_client_pause(bool is_pause)
     return ret;
 }
 
-#ifdef SRS_PERF_QUEUE_FAST_CACHE
-int SrsConsumer::dumps_queue_to_fast_cache()
-{
-    int ret =ERROR_SUCCESS;
-    
-    // fill fast cache with queue.
-    if ((ret = queue->dump_packets(mw_cache->max, mw_cache->msgs, mw_count)) != ERROR_SUCCESS) {
-        return ret;
-    }
-    // set the timestamp when got message.
-    if (mw_count > 0) {
-        SrsSharedPtrMessage* first_msg = mw_cache->msgs[0];
-        mw_first_pkt = first_msg->timestamp;
-        
-        SrsSharedPtrMessage* last_msg = mw_cache->msgs[mw_count - 1];
-        mw_last_pkt = last_msg->timestamp;
-    }
-    
-    return ret;
-}
-#endif
-
 SrsGopCache::SrsGopCache()
 {
     cached_video_count = 0;
diff --git a/trunk/src/app/srs_app_source.hpp b/trunk/src/app/srs_app_source.hpp
index a0177493c6..0a8a7672c2 100644
--- a/trunk/src/app/srs_app_source.hpp
+++ b/trunk/src/app/srs_app_source.hpp
@@ -205,16 +205,6 @@ class SrsConsumer
     int mw_min_msgs;
     int mw_duration;
 #endif
-#ifdef SRS_PERF_QUEUE_FAST_CACHE
-    // use fast cache for msgs
-    // @see https://github.com/winlinvip/simple-rtmp-server/issues/251
-    SrsMessageArray* mw_cache;
-    // the count of msg in fast cache.
-    int mw_count;
-    // the packet time in fast cache.
-    int64_t mw_first_pkt;
-    int64_t mw_last_pkt;
-#endif
 public:
     SrsConsumer(SrsSource* _source);
     virtual ~SrsConsumer();
@@ -262,14 +252,6 @@ class SrsConsumer
     * when client send the pause message.
     */
     virtual int on_play_client_pause(bool is_pause);
-private:
-#ifdef SRS_PERF_QUEUE_FAST_CACHE
-    /**
-    * dumps the queue to fast cache, 
-    * when fast cache is clear or queue is overflow.
-    */
-    virtual int dumps_queue_to_fast_cache();
-#endif
 };
 
 /**
diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp
index b61fa752df..1fc98e048a 100644
--- a/trunk/src/core/srs_core.hpp
+++ b/trunk/src/core/srs_core.hpp
@@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 // current release version
 #define VERSION_MAJOR       2
 #define VERSION_MINOR       0
-#define VERSION_REVISION    68
+#define VERSION_REVISION    69
 // server info.
 #define RTMP_SIG_SRS_KEY "SRS"
 #define RTMP_SIG_SRS_ROLE "origin/edge server"
diff --git a/trunk/src/core/srs_core_performance.hpp b/trunk/src/core/srs_core_performance.hpp
index 68de5ab34c..4439047d24 100644
--- a/trunk/src/core/srs_core_performance.hpp
+++ b/trunk/src/core/srs_core_performance.hpp
@@ -115,20 +115,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 #undef SRS_PERF_MW_SO_RCVBUF
 /**
-* whether enable the fast cache.
-* @remark this improve performance for large connectios.
-* @remark this also introduce complex, default to disable it.
-* @see https://github.com/winlinvip/simple-rtmp-server/issues/251
-*/
-#undef SRS_PERF_QUEUE_FAST_CACHE
-/**
 * whether enable the fast vector for qeueue.
 * @see https://github.com/winlinvip/simple-rtmp-server/issues/251
 */
 #define SRS_PERF_QUEUE_FAST_VECTOR
-#if defined(SRS_PERF_QUEUE_FAST_CACHE) && defined(SRS_PERF_QUEUE_FAST_VECTOR)
-    #error "fast cache conflict with fast vector"
-#endif
 /**
 * whether use cond wait to send messages.
 * @remark this improve performance for large connectios.

From 68ade0a267307e9899ee5796ef680da89267beb4 Mon Sep 17 00:00:00 2001
From: winlin 
Date: Wed, 10 Dec 2014 18:06:09 +0800
Subject: [PATCH 356/800] add log info for rtmp conn. change the mw_latency to
 100 for realtime.

---
 trunk/conf/realtime.conf            | 4 ++++
 trunk/src/app/srs_app_rtmp_conn.cpp | 3 +++
 2 files changed, 7 insertions(+)
 mode change 100644 => 100755 trunk/conf/realtime.conf

diff --git a/trunk/conf/realtime.conf b/trunk/conf/realtime.conf
old mode 100644
new mode 100755
index 6fd74b60e4..00ccd03c54
--- a/trunk/conf/realtime.conf
+++ b/trunk/conf/realtime.conf
@@ -7,4 +7,8 @@ max_connections     1000;
 vhost __defaultVhost__ {
     gop_cache       off;
     queue_length    10;
+    mr {
+        enabled     off;
+    }
+    mw_latency      100;
 }
diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp
index de25901a93..e866f55e1b 100644
--- a/trunk/src/app/srs_app_rtmp_conn.cpp
+++ b/trunk/src/app/srs_app_rtmp_conn.cpp
@@ -615,8 +615,11 @@ int SrsRtmpConn::do_playing(SrsSource* source, SrsQueueRecvThread* trd)
 #ifdef SRS_PERF_QUEUE_COND_WAIT
         // we use wait to get messages, so the count must be positive.
         srs_assert(count > 0);
+        srs_info("mw wait %dms and got %d msgs %"PRId64"-%"PRId64"ms", 
+            mw_sleep, count, msgs.msgs[0]->timestamp, msgs.msgs[count - 1]->timestamp);
 #else
         if (count <= 0) {
+            srs_info("mw sleep %dms for no msg", mw_sleep);
             st_usleep(mw_sleep * 1000);
         }
 #endif

From 10297fab519811845b549a8af40a6bcbd23411e8 Mon Sep 17 00:00:00 2001
From: winlin 
Date: Fri, 12 Dec 2014 21:51:06 +0800
Subject: [PATCH 357/800] fix #257, support 0.1s+ latency. 2.0.70

---
 README.md                               |  2 +
 trunk/conf/full.conf                    | 13 ++++++
 trunk/conf/realtime.conf                |  3 +-
 trunk/research/players/js/srs.player.js |  2 +-
 trunk/research/players/srs_player.html  | 14 ++++++-
 trunk/src/app/srs_app_config.cpp        | 32 +++++++++++++--
 trunk/src/app/srs_app_config.hpp        |  6 +++
 trunk/src/app/srs_app_recv_thread.cpp   | 32 +++++++++++++--
 trunk/src/app/srs_app_recv_thread.hpp   |  4 ++
 trunk/src/app/srs_app_reload.cpp        |  5 +++
 trunk/src/app/srs_app_reload.hpp        |  1 +
 trunk/src/app/srs_app_rtmp_conn.cpp     | 54 ++++++++++++++++++++-----
 trunk/src/app/srs_app_rtmp_conn.hpp     |  4 ++
 trunk/src/app/srs_app_source.cpp        | 13 ++++--
 trunk/src/app/srs_app_source.hpp        |  3 +-
 trunk/src/core/srs_core.hpp             |  2 +-
 trunk/src/core/srs_core_performance.hpp |  9 +++++
 trunk/src/kernel/srs_kernel_utility.cpp |  8 ++--
 trunk/src/kernel/srs_kernel_utility.hpp |  2 +-
 19 files changed, 179 insertions(+), 30 deletions(-)
 mode change 100644 => 100755 trunk/research/players/srs_player.html

diff --git a/README.md b/README.md
index c21401c974..96890e4d61 100755
--- a/README.md
+++ b/README.md
@@ -453,6 +453,7 @@ Supported operating systems and hardware:
 [CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_SrsLibrtmp#publish-audio-raw-stream),
 [EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_SrsLibrtmp#publish-audio-raw-stream)
 ) by srs-librtmp.
+1. Support 0.1s+ latency, read [#257](https://github.com/winlinvip/simple-rtmp-server/issues/257).
 1. [no-plan] Support <500ms latency, FRSC(Fast RTMP-compatible Stream Channel tech).
 1. [no-plan] Support RTMP 302 redirect [#92](https://github.com/winlinvip/simple-rtmp-server/issues/92).
 1. [no-plan] Support multiple processes, for both origin and edge
@@ -486,6 +487,7 @@ Supported operating systems and hardware:
 * 2013-10-17, Created.
## History +* v2.0, 2014-12-12, fix [#257](https://github.com/winlinvip/simple-rtmp-server/issues/257), support 0.1s+ latency. 2.0.70 * v2.0, 2014-12-08, update wiki for mr([EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_LowLatency#merged-read), [CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_LowLatency#merged-read)) and mw([EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_LowLatency#merged-write), [CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_LowLatency#merged-write)). * v2.0, 2014-12-07, fix [#251](https://github.com/winlinvip/simple-rtmp-server/issues/251), 10k+ clients, use queue cond wait and fast vector. 2.0.67 * v2.0, 2014-12-05, fix [#251](https://github.com/winlinvip/simple-rtmp-server/issues/251), 9k+ clients, use fast cache for msgs queue. 2.0.57 diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index de0ca04987..343e51b8d1 100755 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -145,6 +145,13 @@ vhost __defaultVhost__ { # the MR(merged-read) setting for publisher. # the MW(merged-write) settings for player. vhost mrw.srs.com { + # whether enable min delay mode for vhost. + # for min latence mode: + # 1. disable the mr for vhost. + # 2. use timeout for cond wait for consumer queue. + # @see https://github.com/winlinvip/simple-rtmp-server/issues/257 + # default: on + min_latency off; # about MR, read https://github.com/winlinvip/simple-rtmp-server/issues/241 mr { # whether enable the MR(merged-read) @@ -440,6 +447,12 @@ vhost debug.srs.com { # the vhost for min delay, donot cache any stream. vhost min.delay.com { + # @see vhost mrw.srs.com for detail. + min_latency on; + mr { + enabled off; + } + mw_latency 100; # whether cache the last gop. # if on, cache the last gop and dispatch to client, # to enabled fast startup for client, client play immediately. diff --git a/trunk/conf/realtime.conf b/trunk/conf/realtime.conf index 00ccd03c54..4ac824b6f4 100755 --- a/trunk/conf/realtime.conf +++ b/trunk/conf/realtime.conf @@ -1,5 +1,5 @@ # the config for srs to delivery realtime RTMP stream -# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_SampleRealtime +# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_SampleRealtime # @see full.conf for detail config. listen 1935; @@ -7,6 +7,7 @@ max_connections 1000; vhost __defaultVhost__ { gop_cache off; queue_length 10; + min_latency on; mr { enabled off; } diff --git a/trunk/research/players/js/srs.player.js b/trunk/research/players/js/srs.player.js index dad0ba9278..f04859ca9c 100755 --- a/trunk/research/players/js/srs.player.js +++ b/trunk/research/players/js/srs.player.js @@ -22,7 +22,7 @@ function SrsPlayer(container, width, height, private_object) { this.height = height; this.id = SrsPlayer.__id++; this.stream_url = null; - this.buffer_time = 0.8; // default to 0.8 + this.buffer_time = 0.3; // default to 0.3 this.volume = 1.0; // default to 100% this.callbackObj = null; diff --git a/trunk/research/players/srs_player.html b/trunk/research/players/srs_player.html old mode 100644 new mode 100755 index 4e26ed0468..0345d16f59 --- a/trunk/research/players/srs_player.html +++ b/trunk/research/players/srs_player.html @@ -96,7 +96,7 @@ srs_player = new SrsPlayer("player_id", srs_get_player_width(), srs_get_player_height()); srs_player.on_player_ready = function() { - select_buffer_time("#btn_bt_0_8", 0.8); + select_buffer_time("#btn_bt_0_1", 0.1); this.play(url); }; srs_player.on_player_metadata = function(metadata) { @@ -231,6 +231,15 @@ } if (true) { + $("#btn_bt_0_1").click(function(){ + select_buffer_time("#btn_bt_0_1", 0.1); + }); + $("#btn_bt_0_2").click(function(){ + select_buffer_time("#btn_bt_0_2", 0.2); + }); + $("#btn_bt_0_3").click(function(){ + select_buffer_time("#btn_bt_0_3", 0.3); + }); $("#btn_bt_0_5").click(function(){ select_buffer_time("#btn_bt_0_5", 0.5); }); @@ -504,6 +513,9 @@

SrsPlayer

-### [HDS/HLS origin backup](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_RTMP-ATC) +### [HDS/HLS origin backup](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_RTMP-ATC)
                         +----------+        +----------+
@@ -1240,10 +1240,10 @@ over some protocol and remux to rtmp to SRS. Read 
Date: Wed, 29 Apr 2015 18:03:17 +0800
Subject: [PATCH 788/800] migarate to srs org.

---
 trunk/src/core/srs_core.hpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp
index 9c0f262562..9bdb47631d 100644
--- a/trunk/src/core/srs_core.hpp
+++ b/trunk/src/core/srs_core.hpp
@@ -40,7 +40,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #define RTMP_SIG_SRS_NAME RTMP_SIG_SRS_KEY"(Simple RTMP Server)"
 #define RTMP_SIG_SRS_URL_SHORT "github.com/simple-rtmp-server/srs"
 #define RTMP_SIG_SRS_URL "https://"RTMP_SIG_SRS_URL_SHORT
-#define RTMP_SIG_SRS_WEB "http://blog.csdn.net/win_lin"
+#define RTMP_SIG_SRS_WEB "http://ossrs.net"
 #define RTMP_SIG_SRS_EMAIL "winlin@vip.126.com"
 #define RTMP_SIG_SRS_LICENSE "The MIT License (MIT)"
 #define RTMP_SIG_SRS_COPYRIGHT "Copyright (c) 2013-2015 SRS(simple-rtmp-server)"

From 4b372d8d16d74f890097aee553b2f75328726025 Mon Sep 17 00:00:00 2001
From: winlin 
Date: Wed, 29 Apr 2015 18:46:47 +0800
Subject: [PATCH 789/800] change primary to the primary branch.

---
 trunk/src/core/srs_core.hpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp
index 9bdb47631d..0a419c5aa9 100644
--- a/trunk/src/core/srs_core.hpp
+++ b/trunk/src/core/srs_core.hpp
@@ -44,7 +44,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #define RTMP_SIG_SRS_EMAIL "winlin@vip.126.com"
 #define RTMP_SIG_SRS_LICENSE "The MIT License (MIT)"
 #define RTMP_SIG_SRS_COPYRIGHT "Copyright (c) 2013-2015 SRS(simple-rtmp-server)"
-#define RTMP_SIG_SRS_PRIMARY "SRS Team"
+#define RTMP_SIG_SRS_PRIMARY "SRS/"VERSION_STABLE_BRANCH
 #define RTMP_SIG_SRS_AUTHROS "winlin,wenjie.zhao"
 #define RTMP_SIG_SRS_CONTRIBUTORS_URL RTMP_SIG_SRS_URL"/blob/master/AUTHORS.txt"
 #define RTMP_SIG_SRS_HANDSHAKE RTMP_SIG_SRS_KEY"("RTMP_SIG_SRS_VERSION")"

From 4d18073dabccf68868b367dd303ef4fff8e79e62 Mon Sep 17 00:00:00 2001
From: winlin 
Date: Thu, 30 Apr 2015 13:42:30 +0800
Subject: [PATCH 790/800] update contact add skype.

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index bdbe95af52..8ea7cb4033 100755
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@ Download from ossrs.net:
 [Centos6-x86_64](http://www.ossrs.net/srs.release/releases/files/SRS-CentOS6-x86_64-1.0.27.zip) 
 [more...](http://www.ossrs.net/srs.release/releases/)
 
-Contact by QQ or Weixin, read [Contact](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_Contact)
+Contact by QQ or Skype, read [Contact](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_Contact)
 
 ## Why SRS?
 

From f0c24eeacc5a0a351bd2a660d887ba6729231bca Mon Sep 17 00:00:00 2001
From: winlin 
Date: Sun, 3 May 2015 10:56:20 +0800
Subject: [PATCH 791/800] add stream caster for post flv over http.

---
 trunk/conf/full.conf                          |  7 +++
 trunk/configure                               |  3 +-
 trunk/ide/srs_upp/srs_upp.upp                 |  2 +
 trunk/ide/srs_vs2010/srs.vcxproj              |  4 +-
 .../srs_xcode.xcodeproj/project.pbxproj       |  6 ++
 trunk/src/app/srs_app_caster_flv.cpp          | 48 ++++++++++++++
 trunk/src/app/srs_app_caster_flv.hpp          | 52 +++++++++++++++
 trunk/src/app/srs_app_config.hpp              |  1 +
 trunk/src/app/srs_app_server.cpp              | 63 ++++++++++++++++++-
 trunk/src/app/srs_app_server.hpp              | 20 ++++++
 10 files changed, 203 insertions(+), 3 deletions(-)
 create mode 100644 trunk/src/app/srs_app_caster_flv.cpp
 create mode 100644 trunk/src/app/srs_app_caster_flv.hpp

diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf
index 78911b92f7..a639e26758 100644
--- a/trunk/conf/full.conf
+++ b/trunk/conf/full.conf
@@ -159,6 +159,7 @@ stream_caster {
     # the caster type of stream, the casters:
     #       mpegts_over_udp, MPEG-TS over UDP caster.
     #       rtsp, Real Time Streaming Protocol (RTSP).
+    #       flv, FLV over HTTP POST.
     caster          mpegts_over_udp;
     # the output rtmp url.
     # for mpegts_over_udp caster, the typically output url:
@@ -195,6 +196,12 @@ stream_caster {
     rtp_port_min    57200;
     rtp_port_max    57300;
 }
+stream_caster {
+    enabled         off;
+    caster          flv;
+    output          rtmp://127.0.0.1/[app]/[stream];
+    listen          8936;
+}
 
 #############################################################################################
 # RTMP/HTTP VHOST sections
diff --git a/trunk/configure b/trunk/configure
index 067c91ca49..7f87274423 100755
--- a/trunk/configure
+++ b/trunk/configure
@@ -175,7 +175,8 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
             "srs_app_json" "srs_app_ingest" "srs_app_ffmpeg" "srs_app_utility" "srs_app_dvr" "srs_app_edge"
             "srs_app_kbps" "srs_app_heartbeat" "srs_app_empty" "srs_app_http_client"
             "srs_app_recv_thread" "srs_app_security" "srs_app_statistic" "srs_app_hds"
-            "srs_app_mpegts_udp" "srs_app_rtsp" "srs_app_listener" "srs_app_async_call")
+            "srs_app_mpegts_udp" "srs_app_rtsp" "srs_app_listener" "srs_app_async_call"
+            "srs_app_caster_flv")
     DEFINES=""
     # add each modules for app
     for SRS_MODULE in ${SRS_MODULES[*]}; do
diff --git a/trunk/ide/srs_upp/srs_upp.upp b/trunk/ide/srs_upp/srs_upp.upp
index 424a3bbd37..e302bb98b5 100755
--- a/trunk/ide/srs_upp/srs_upp.upp
+++ b/trunk/ide/srs_upp/srs_upp.upp
@@ -69,6 +69,8 @@ file
         ../../src/app/srs_app_async_call.cpp,
 	../../src/app/srs_app_bandwidth.hpp,
 	../../src/app/srs_app_bandwidth.cpp,
+        ../../src/app/srs_app_caster_flv.hpp,
+        ../../src/app/srs_app_caster_flv.cpp,
 	../../src/app/srs_app_conn.hpp,
 	../../src/app/srs_app_conn.cpp,
 	../../src/app/srs_app_config.hpp,
diff --git a/trunk/ide/srs_vs2010/srs.vcxproj b/trunk/ide/srs_vs2010/srs.vcxproj
index c9050517a7..f7d0a9ff10 100755
--- a/trunk/ide/srs_vs2010/srs.vcxproj
+++ b/trunk/ide/srs_vs2010/srs.vcxproj
@@ -85,6 +85,7 @@
     
     
     
+    
     
     
     
@@ -165,6 +166,7 @@
     
     
     
+    
     
     
     
@@ -213,4 +215,4 @@
   
   
   
-
\ No newline at end of file
+
diff --git a/trunk/ide/srs_xcode/srs_xcode.xcodeproj/project.pbxproj b/trunk/ide/srs_xcode/srs_xcode.xcodeproj/project.pbxproj
index 15522b8376..94b678670b 100644
--- a/trunk/ide/srs_xcode/srs_xcode.xcodeproj/project.pbxproj
+++ b/trunk/ide/srs_xcode/srs_xcode.xcodeproj/project.pbxproj
@@ -75,6 +75,7 @@
 		3C1232ED1AAEA70F00CE8F6C /* libhttp_parser.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3C1232EC1AAEA70F00CE8F6C /* libhttp_parser.a */; };
 		3C1EE6AE1AB1055800576EE9 /* srs_app_hds.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C1EE6AC1AB1055800576EE9 /* srs_app_hds.cpp */; };
 		3C1EE6D71AB1367D00576EE9 /* README.md in Sources */ = {isa = PBXBuildFile; fileRef = 3C1EE6D61AB1367D00576EE9 /* README.md */; };
+		3C28EDDF1AF5C43F00A3AEAC /* srs_app_caster_flv.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C28EDDD1AF5C43F00A3AEAC /* srs_app_caster_flv.cpp */; };
 		3C36DB5B1ABD1CB90066CCAF /* srs_lib_bandwidth.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C36DB551ABD1CB90066CCAF /* srs_lib_bandwidth.cpp */; };
 		3C36DB5C1ABD1CB90066CCAF /* srs_lib_simple_socket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C36DB571ABD1CB90066CCAF /* srs_lib_simple_socket.cpp */; };
 		3C36DB5D1ABD1CB90066CCAF /* srs_librtmp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C36DB591ABD1CB90066CCAF /* srs_librtmp.cpp */; };
@@ -318,6 +319,8 @@
 		3C1EE6D41AB1367D00576EE9 /* DONATIONS.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = DONATIONS.txt; path = ../../../DONATIONS.txt; sourceTree = ""; };
 		3C1EE6D51AB1367D00576EE9 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = LICENSE; path = ../../../LICENSE; sourceTree = ""; };
 		3C1EE6D61AB1367D00576EE9 /* README.md */ = {isa = PBXFileReference; explicitFileType = net.daringfireball.markdown; fileEncoding = 4; name = README.md; path = ../../../README.md; sourceTree = ""; wrapsLines = 0; };
+		3C28EDDD1AF5C43F00A3AEAC /* srs_app_caster_flv.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_app_caster_flv.cpp; path = ../../../src/app/srs_app_caster_flv.cpp; sourceTree = ""; };
+		3C28EDDE1AF5C43F00A3AEAC /* srs_app_caster_flv.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_app_caster_flv.hpp; path = ../../../src/app/srs_app_caster_flv.hpp; sourceTree = ""; };
 		3C36DB551ABD1CB90066CCAF /* srs_lib_bandwidth.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_lib_bandwidth.cpp; path = ../../../src/libs/srs_lib_bandwidth.cpp; sourceTree = ""; };
 		3C36DB561ABD1CB90066CCAF /* srs_lib_bandwidth.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_lib_bandwidth.hpp; path = ../../../src/libs/srs_lib_bandwidth.hpp; sourceTree = ""; };
 		3C36DB571ABD1CB90066CCAF /* srs_lib_simple_socket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_lib_simple_socket.cpp; path = ../../../src/libs/srs_lib_simple_socket.cpp; sourceTree = ""; };
@@ -511,6 +514,8 @@
 		3C12324B1AAE81CE00CE8F6C /* app */ = {
 			isa = PBXGroup;
 			children = (
+				3C28EDDD1AF5C43F00A3AEAC /* srs_app_caster_flv.cpp */,
+				3C28EDDE1AF5C43F00A3AEAC /* srs_app_caster_flv.hpp */,
 				3CD88B3D1ACA9C58000359E0 /* srs_app_async_call.cpp */,
 				3CD88B3E1ACA9C58000359E0 /* srs_app_async_call.hpp */,
 				3C12324C1AAE81D900CE8F6C /* srs_app_bandwidth.cpp */,
@@ -908,6 +913,7 @@
 				3C1232261AAE814D00CE8F6C /* srs_kernel_flv.cpp in Sources */,
 				3C663F1A1AB0155100286D8B /* srs_rtmp_dump.c in Sources */,
 				3CE6CD311AE4AFB800706E07 /* srs_main_ingest_hls.cpp in Sources */,
+				3C28EDDF1AF5C43F00A3AEAC /* srs_app_caster_flv.cpp in Sources */,
 				3C1232241AAE814D00CE8F6C /* srs_kernel_error.cpp in Sources */,
 				3C1232441AAE81A400CE8F6C /* srs_rtmp_handshake.cpp in Sources */,
 				3C1232291AAE814D00CE8F6C /* srs_kernel_stream.cpp in Sources */,
diff --git a/trunk/src/app/srs_app_caster_flv.cpp b/trunk/src/app/srs_app_caster_flv.cpp
new file mode 100644
index 0000000000..11a4f1a03f
--- /dev/null
+++ b/trunk/src/app/srs_app_caster_flv.cpp
@@ -0,0 +1,48 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2013-2015 SRS(simple-rtmp-server)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include 
+
+#ifdef SRS_AUTO_STREAM_CASTER
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+SrsAppCasterFlv::SrsAppCasterFlv(SrsConfDirective* c)
+{
+}
+
+SrsAppCasterFlv::~SrsAppCasterFlv()
+{
+}
+
+int SrsAppCasterFlv::on_tcp_client(st_netfd_t stfd)
+{
+    int ret = ERROR_SUCCESS;
+    return ret;
+}
+
+#endif
diff --git a/trunk/src/app/srs_app_caster_flv.hpp b/trunk/src/app/srs_app_caster_flv.hpp
new file mode 100644
index 0000000000..6d0015e8c3
--- /dev/null
+++ b/trunk/src/app/srs_app_caster_flv.hpp
@@ -0,0 +1,52 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2013-2015 SRS(simple-rtmp-server)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef SRS_APP_CASTER_FLV_HPP
+#define SRS_APP_CASTER_FLV_HPP
+
+/*
+#include 
+*/
+
+#include 
+
+#ifdef SRS_AUTO_STREAM_CASTER
+
+class SrsConfDirective;
+
+#include 
+#include 
+
+class SrsAppCasterFlv : public ISrsTcpHandler
+{
+public:
+    SrsAppCasterFlv(SrsConfDirective* c);
+    virtual ~SrsAppCasterFlv();
+// ISrsTcpHandler
+public:
+    virtual int on_tcp_client(st_netfd_t stfd);
+};
+
+#endif
+
+#endif
diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp
index c32262cb26..6e2a1b3533 100644
--- a/trunk/src/app/srs_app_config.hpp
+++ b/trunk/src/app/srs_app_config.hpp
@@ -100,6 +100,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #define SRS_CONF_DEFAULT_STREAM_CASTER_ENABLED false
 #define SRS_CONF_DEFAULT_STREAM_CASTER_MPEGTS_OVER_UDP "mpegts_over_udp"
 #define SRS_CONF_DEFAULT_STREAM_CASTER_RTSP "rtsp"
+#define SRS_CONF_DEFAULT_STREAM_CASTER_FLV "flv"
 
 #define SRS_CONF_DEFAULT_STATS_NETWORK_DEVICE_INDEX 0
 
diff --git a/trunk/src/app/srs_app_server.cpp b/trunk/src/app/srs_app_server.cpp
index 3c21a542b6..b522c25452 100644
--- a/trunk/src/app/srs_app_server.cpp
+++ b/trunk/src/app/srs_app_server.cpp
@@ -46,6 +46,7 @@ using namespace std;
 #include 
 #include 
 #include 
+#include 
 
 // signal defines.
 #define SIGNAL_RELOAD SIGHUP
@@ -206,7 +207,7 @@ int SrsRtspListener::listen(string ip, int port)
     listener = new SrsTcpListener(this, ip, port);
 
     if ((ret = listener->listen()) != ERROR_SUCCESS) {
-        srs_error("udp caster listen failed. ret=%d", ret);
+        srs_error("rtsp caster listen failed. ret=%d", ret);
         return ret;
     }
     
@@ -231,6 +232,64 @@ int SrsRtspListener::on_tcp_client(st_netfd_t stfd)
     return ret;
 }
 
+SrsHttpFlvListener::SrsHttpFlvListener(SrsServer* server, SrsListenerType type, SrsConfDirective* c) : SrsListener(server, type)
+{
+    listener = NULL;
+    
+    // the caller already ensure the type is ok,
+    // we just assert here for unknown stream caster.
+    srs_assert(_type == SrsListenerRtsp);
+    if (_type == SrsListenerRtsp) {
+        caster = new SrsAppCasterFlv(c);
+    }
+}
+
+SrsHttpFlvListener::~SrsHttpFlvListener()
+{
+    srs_freep(caster);
+    srs_freep(listener);
+}
+
+int SrsHttpFlvListener::listen(string ip, int port)
+{
+    int ret = ERROR_SUCCESS;
+    
+    // the caller already ensure the type is ok,
+    // we just assert here for unknown stream caster.
+    srs_assert(_type == SrsListenerRtsp);
+    
+    _ip = ip;
+    _port = port;
+    
+    srs_freep(listener);
+    listener = new SrsTcpListener(this, ip, port);
+    
+    if ((ret = listener->listen()) != ERROR_SUCCESS) {
+        srs_error("flv caster listen failed. ret=%d", ret);
+        return ret;
+    }
+    
+    srs_info("listen thread cid=%d, current_cid=%d, "
+             "listen at port=%d, type=%d, fd=%d started success, ep=%s:%d",
+             pthread->cid(), _srs_context->get_id(), _port, _type, fd, ip.c_str(), port);
+    
+    srs_trace("%s listen at tcp://%s:%d, fd=%d", srs_listener_type2string(_type).c_str(), ip.c_str(), _port, listener->fd());
+    
+    return ret;
+}
+
+int SrsHttpFlvListener::on_tcp_client(st_netfd_t stfd)
+{
+    int ret = ERROR_SUCCESS;
+    
+    if ((ret = caster->on_tcp_client(stfd)) != ERROR_SUCCESS) {
+        srs_warn("accept client error. ret=%d", ret);
+        return ret;
+    }
+    
+    return ret;
+}
+
 SrsUdpCasterListener::SrsUdpCasterListener(SrsServer* server, SrsListenerType type, SrsConfDirective* c) : SrsListener(server, type)
 {
     _type = type;
@@ -1003,6 +1062,8 @@ int SrsServer::listen_stream_caster()
             listener = new SrsUdpCasterListener(this, SrsListenerMpegTsOverUdp, stream_caster);
         } else if (caster == SRS_CONF_DEFAULT_STREAM_CASTER_RTSP) {
             listener = new SrsRtspListener(this, SrsListenerRtsp, stream_caster);
+        } else if (caster == SRS_CONF_DEFAULT_STREAM_CASTER_FLV) {
+            listener = new SrsHttpFlvListener(this, SrsListenerFlv, stream_caster);
         } else {
             ret = ERROR_STREAM_CASTER_ENGINE;
             srs_error("unsupported stream caster %s. ret=%d", caster.c_str(), ret);
diff --git a/trunk/src/app/srs_app_server.hpp b/trunk/src/app/srs_app_server.hpp
index 1bad2d8b1f..b2ecafbb15 100644
--- a/trunk/src/app/srs_app_server.hpp
+++ b/trunk/src/app/srs_app_server.hpp
@@ -66,6 +66,8 @@ enum SrsListenerType
     SrsListenerMpegTsOverUdp    = 3,
     // TCP stream, RTSP stream.
     SrsListenerRtsp             = 4,
+    // HTTP stream, FLV over HTTP POST.
+    SrsListenerFlv              = 5,
 };
 
 /**
@@ -123,6 +125,24 @@ class SrsRtspListener : virtual public SrsListener, virtual public ISrsTcpHandle
     virtual int on_tcp_client(st_netfd_t stfd);
 };
 
+/**
+ * the tcp listener, for http flv server.
+ */
+class SrsHttpFlvListener : virtual public SrsListener, virtual public ISrsTcpHandler
+{
+private:
+    SrsTcpListener* listener;
+    ISrsTcpHandler* caster;
+public:
+    SrsHttpFlvListener(SrsServer* server, SrsListenerType type, SrsConfDirective* c);
+    virtual ~SrsHttpFlvListener();
+public:
+    virtual int listen(std::string ip, int port);
+// ISrsTcpHandler
+public:
+    virtual int on_tcp_client(st_netfd_t stfd);
+};
+
 /**
 * the udp listener, for udp server.
 */

From 022b6aa56185f15069b460c6cc81ec999d7f29e3 Mon Sep 17 00:00:00 2001
From: winlin 
Date: Sun, 3 May 2015 23:34:59 +0800
Subject: [PATCH 792/800] refine the http remux for http flv stream.

---
 trunk/src/app/srs_app_caster_flv.cpp | 43 ++++++++++++++++++++++++++++
 trunk/src/app/srs_app_caster_flv.hpp | 22 +++++++++++++-
 trunk/src/app/srs_app_conn.cpp       | 17 +++++++----
 trunk/src/app/srs_app_conn.hpp       | 23 ++++++++++++---
 trunk/src/app/srs_app_http_api.cpp   |  4 +--
 trunk/src/app/srs_app_http_api.hpp   |  2 +-
 trunk/src/app/srs_app_http_conn.cpp  |  8 +++---
 trunk/src/app/srs_app_http_conn.hpp  |  5 ++--
 trunk/src/app/srs_app_rtmp_conn.cpp  |  7 +++--
 trunk/src/app/srs_app_rtmp_conn.hpp  |  4 ++-
 trunk/src/app/srs_app_server.cpp     | 14 ++++++---
 trunk/src/app/srs_app_server.hpp     | 15 +++++++---
 12 files changed, 133 insertions(+), 31 deletions(-)

diff --git a/trunk/src/app/srs_app_caster_flv.cpp b/trunk/src/app/srs_app_caster_flv.cpp
index 11a4f1a03f..1f6b7a16c1 100644
--- a/trunk/src/app/srs_app_caster_flv.cpp
+++ b/trunk/src/app/srs_app_caster_flv.cpp
@@ -25,23 +25,66 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 #ifdef SRS_AUTO_STREAM_CASTER
 
+#include 
+using namespace std;
+
 #include 
 #include 
 #include 
 #include 
 #include 
+#include 
+#include 
 
 SrsAppCasterFlv::SrsAppCasterFlv(SrsConfDirective* c)
 {
+    http_mux = new SrsHttpServeMux();
+    output = _srs_config->get_stream_caster_output(c);
 }
 
 SrsAppCasterFlv::~SrsAppCasterFlv()
 {
 }
 
+int SrsAppCasterFlv::initialize()
+{
+    int ret = ERROR_SUCCESS;
+    
+    if ((ret = http_mux->handle("/", this)) != ERROR_SUCCESS) {
+        return ret;
+    }
+    
+    return ret;
+}
+
 int SrsAppCasterFlv::on_tcp_client(st_netfd_t stfd)
 {
     int ret = ERROR_SUCCESS;
+    
+    SrsHttpConn* conn = new SrsHttpConn(this, stfd, http_mux);
+    conns.push_back(conn);
+    
+    if ((ret = conn->start()) != ERROR_SUCCESS) {
+        return ret;
+    }
+    
+    return ret;
+}
+
+void SrsAppCasterFlv::remove(SrsConnection* c)
+{
+    std::vector::iterator it;
+    if ((it = std::find(conns.begin(), conns.end(), c)) != conns.end()) {
+        conns.erase(it);
+    }
+}
+
+int SrsAppCasterFlv::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r)
+{
+    int ret = ERROR_SUCCESS;
+    
+    srs_trace("flv: handle request at %s", r->path().c_str());
+    
     return ret;
 }
 
diff --git a/trunk/src/app/srs_app_caster_flv.hpp b/trunk/src/app/srs_app_caster_flv.hpp
index 6d0015e8c3..fb092f5b2b 100644
--- a/trunk/src/app/srs_app_caster_flv.hpp
+++ b/trunk/src/app/srs_app_caster_flv.hpp
@@ -30,21 +30,41 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 #include 
 
+#include 
+#include 
+
 #ifdef SRS_AUTO_STREAM_CASTER
 
 class SrsConfDirective;
+class SrsHttpServeMux;
+class SrsHttpConn;
 
 #include 
 #include 
+#include 
+#include 
 
-class SrsAppCasterFlv : public ISrsTcpHandler
+class SrsAppCasterFlv : virtual public ISrsTcpHandler
+    , virtual public IConnectionManager, virtual public ISrsHttpHandler
 {
+private:
+    std::string output;
+    SrsHttpServeMux* http_mux;
+    std::vector conns;
 public:
     SrsAppCasterFlv(SrsConfDirective* c);
     virtual ~SrsAppCasterFlv();
+public:
+    virtual int initialize();
 // ISrsTcpHandler
 public:
     virtual int on_tcp_client(st_netfd_t stfd);
+// IConnectionManager
+public:
+    virtual void remove(SrsConnection* c);
+// ISrsHttpHandler
+public:
+    virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r);
 };
 
 #endif
diff --git a/trunk/src/app/srs_app_conn.cpp b/trunk/src/app/srs_app_conn.cpp
index 656cbe0316..4c483ba1c9 100644
--- a/trunk/src/app/srs_app_conn.cpp
+++ b/trunk/src/app/srs_app_conn.cpp
@@ -25,14 +25,21 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 #include 
 #include 
-#include 
 #include 
 
-SrsConnection::SrsConnection(SrsServer* srs_server, st_netfd_t client_stfd)
+IConnectionManager::IConnectionManager()
+{
+}
+
+IConnectionManager::~IConnectionManager()
+{
+}
+
+SrsConnection::SrsConnection(IConnectionManager* cm, st_netfd_t c)
 {
     id = 0;
-    server = srs_server;
-    stfd = client_stfd;
+    manager = cm;
+    stfd = c;
     
     // the client thread should reap itself, 
     // so we never use joinable.
@@ -86,7 +93,7 @@ int SrsConnection::cycle()
 void SrsConnection::on_thread_stop()
 {
     // TODO: FIXME: never remove itself, use isolate thread to do cleanup.
-    server->remove(this);
+    manager->remove(this);
 }
 
 int SrsConnection::srs_id()
diff --git a/trunk/src/app/srs_app_conn.hpp b/trunk/src/app/srs_app_conn.hpp
index 8cff99a18a..29a6eace76 100644
--- a/trunk/src/app/srs_app_conn.hpp
+++ b/trunk/src/app/srs_app_conn.hpp
@@ -36,7 +36,22 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #include 
 #include 
 
-class SrsServer;
+class SrsConnection;
+
+/**
+ * the manager for connection.
+ */
+class IConnectionManager
+{
+public:
+    IConnectionManager();
+    virtual ~IConnectionManager();
+public:
+    /**
+     * remove the specified connection.
+     */
+    virtual void remove(SrsConnection* c) = 0;
+};
 
 /**
 * the basic connection of SRS,
@@ -57,9 +72,9 @@ class SrsConnection : public virtual ISrsThreadHandler, public virtual IKbpsDelt
     int id;
 protected:
     /**
-    * the server object to manage the connection.
+    * the manager object to manage the connection.
     */
-    SrsServer* server;
+    IConnectionManager* manager;
     /**
     * the underlayer st fd handler.
     */
@@ -69,7 +84,7 @@ class SrsConnection : public virtual ISrsThreadHandler, public virtual IKbpsDelt
     */
     std::string ip;
 public:
-    SrsConnection(SrsServer* srs_server, st_netfd_t client_stfd);
+    SrsConnection(IConnectionManager* cm, st_netfd_t c);
     virtual ~SrsConnection();
 public:
     /**
diff --git a/trunk/src/app/srs_app_http_api.cpp b/trunk/src/app/srs_app_http_api.cpp
index 5c95af7b0f..0f3aac193a 100644
--- a/trunk/src/app/srs_app_http_api.cpp
+++ b/trunk/src/app/srs_app_http_api.cpp
@@ -473,8 +473,8 @@ int SrsGoApiStreams::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r)
     return srs_go_http_response_json(w, ss.str());
 }
 
-SrsHttpApi::SrsHttpApi(SrsServer* svr, st_netfd_t fd, SrsHttpServeMux* m) 
-    : SrsConnection(svr, fd)
+SrsHttpApi::SrsHttpApi(IConnectionManager* cm, st_netfd_t fd, SrsHttpServeMux* m)
+    : SrsConnection(cm, fd)
 {
     mux = m;
     parser = new SrsHttpParser();
diff --git a/trunk/src/app/srs_app_http_api.hpp b/trunk/src/app/srs_app_http_api.hpp
index 6251fa30c2..66e971088c 100644
--- a/trunk/src/app/srs_app_http_api.hpp
+++ b/trunk/src/app/srs_app_http_api.hpp
@@ -166,7 +166,7 @@ class SrsHttpApi : public SrsConnection
     SrsHttpServeMux* mux;
     bool crossdomain_required;
 public:
-    SrsHttpApi(SrsServer* svr, st_netfd_t fd, SrsHttpServeMux* m);
+    SrsHttpApi(IConnectionManager* cm, st_netfd_t fd, SrsHttpServeMux* m);
     virtual ~SrsHttpApi();
 // interface IKbpsDelta
 public:
diff --git a/trunk/src/app/srs_app_http_conn.cpp b/trunk/src/app/srs_app_http_conn.cpp
index 43fa69e0be..c6f4b94b5a 100644
--- a/trunk/src/app/srs_app_http_conn.cpp
+++ b/trunk/src/app/srs_app_http_conn.cpp
@@ -1334,11 +1334,11 @@ int SrsHttpServer::initialize_hls_streaming()
     return ret;
 }
 
-SrsHttpConn::SrsHttpConn(SrsServer* svr, st_netfd_t fd, SrsHttpServer* m) 
-    : SrsConnection(svr, fd)
+SrsHttpConn::SrsHttpConn(IConnectionManager* cm, st_netfd_t fd, SrsHttpServeMux* m)
+    : SrsConnection(cm, fd)
 {
     parser = new SrsHttpParser();
-    http_server = m;
+    http_mux = m;
 }
 
 SrsHttpConn::~SrsHttpConn()
@@ -1424,7 +1424,7 @@ int SrsHttpConn::process_request(ISrsHttpResponseWriter* w, SrsHttpMessage* r)
         r->method_str().c_str(), r->url().c_str(), r->content_length());
     
     // use default server mux to serve http request.
-    if ((ret = http_server->mux.serve_http(w, r)) != ERROR_SUCCESS) {
+    if ((ret = http_mux->serve_http(w, r)) != ERROR_SUCCESS) {
         if (!srs_is_client_gracefully_close(ret)) {
             srs_error("serve http msg failed. ret=%d", ret);
         }
diff --git a/trunk/src/app/srs_app_http_conn.hpp b/trunk/src/app/srs_app_http_conn.hpp
index 418364294c..63f87a5ffb 100644
--- a/trunk/src/app/srs_app_http_conn.hpp
+++ b/trunk/src/app/srs_app_http_conn.hpp
@@ -39,6 +39,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #include 
 #include 
 
+class SrsServer;
 class SrsSource;
 class SrsRequest;
 class SrsConsumer;
@@ -375,9 +376,9 @@ class SrsHttpConn : public SrsConnection
 {
 private:
     SrsHttpParser* parser;
-    SrsHttpServer* http_server;
+    SrsHttpServeMux* http_mux;
 public:
-    SrsHttpConn(SrsServer* svr, st_netfd_t fd, SrsHttpServer* m);
+    SrsHttpConn(IConnectionManager* cm, st_netfd_t fd, SrsHttpServeMux* m);
     virtual ~SrsHttpConn();
 // interface IKbpsDelta
 public:
diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp
index 588a6cdf46..6b0583f495 100644
--- a/trunk/src/app/srs_app_rtmp_conn.cpp
+++ b/trunk/src/app/srs_app_rtmp_conn.cpp
@@ -75,12 +75,13 @@ using namespace std;
 // when edge timeout, retry next.
 #define SRS_EDGE_TOKEN_TRAVERSE_TIMEOUT_US (int64_t)(3*1000*1000LL)
 
-SrsRtmpConn::SrsRtmpConn(SrsServer* srs_server, st_netfd_t client_stfd)
-    : SrsConnection(srs_server, client_stfd)
+SrsRtmpConn::SrsRtmpConn(SrsServer* svr, st_netfd_t c)
+    : SrsConnection(svr, c)
 {
+    server = svr;
     req = new SrsRequest();
     res = new SrsResponse();
-    skt = new SrsStSocket(client_stfd);
+    skt = new SrsStSocket(c);
     rtmp = new SrsRtmpServer(skt);
     refer = new SrsRefer();
     bandwidth = new SrsBandwidth();
diff --git a/trunk/src/app/srs_app_rtmp_conn.hpp b/trunk/src/app/srs_app_rtmp_conn.hpp
index 7c1a2205ad..39aad6e0e4 100644
--- a/trunk/src/app/srs_app_rtmp_conn.hpp
+++ b/trunk/src/app/srs_app_rtmp_conn.hpp
@@ -34,6 +34,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #include 
 #include 
 
+class SrsServer;
 class SrsRtmpServer;
 class SrsRequest;
 class SrsResponse;
@@ -61,6 +62,7 @@ class SrsRtmpConn : public virtual SrsConnection, public virtual ISrsReloadHandl
     // for the thread to directly access any field of connection.
     friend class SrsPublishRecvThread;
 private:
+    SrsServer* server;
     SrsRequest* req;
     SrsResponse* res;
     SrsStSocket* skt;
@@ -81,7 +83,7 @@ class SrsRtmpConn : public virtual SrsConnection, public virtual ISrsReloadHandl
     // @see https://github.com/simple-rtmp-server/srs/issues/257
     bool realtime;
 public:
-    SrsRtmpConn(SrsServer* srs_server, st_netfd_t client_stfd);
+    SrsRtmpConn(SrsServer* svr, st_netfd_t c);
     virtual ~SrsRtmpConn();
 protected:
     virtual int do_cycle();
diff --git a/trunk/src/app/srs_app_server.cpp b/trunk/src/app/srs_app_server.cpp
index b522c25452..851c8023cc 100644
--- a/trunk/src/app/srs_app_server.cpp
+++ b/trunk/src/app/srs_app_server.cpp
@@ -106,6 +106,8 @@ std::string srs_listener_type2string(SrsListenerType type)
         return "MPEG-TS over UDP";
     case SrsListenerRtsp:
         return "RTSP";
+    case SrsListenerFlv:
+        return "HTTP-FLV";
     default:
         return "UNKONWN";
     }
@@ -238,8 +240,8 @@ SrsHttpFlvListener::SrsHttpFlvListener(SrsServer* server, SrsListenerType type,
     
     // the caller already ensure the type is ok,
     // we just assert here for unknown stream caster.
-    srs_assert(_type == SrsListenerRtsp);
-    if (_type == SrsListenerRtsp) {
+    srs_assert(_type == SrsListenerFlv);
+    if (_type == SrsListenerFlv) {
         caster = new SrsAppCasterFlv(c);
     }
 }
@@ -256,11 +258,15 @@ int SrsHttpFlvListener::listen(string ip, int port)
     
     // the caller already ensure the type is ok,
     // we just assert here for unknown stream caster.
-    srs_assert(_type == SrsListenerRtsp);
+    srs_assert(_type == SrsListenerFlv);
     
     _ip = ip;
     _port = port;
     
+    if ((ret = caster->initialize()) != ERROR_SUCCESS) {
+        return ret;
+    }
+    
     srs_freep(listener);
     listener = new SrsTcpListener(this, ip, port);
     
@@ -1157,7 +1163,7 @@ int SrsServer::accept_client(SrsListenerType type, st_netfd_t client_stfd)
 #endif
     } else if (type == SrsListenerHttpStream) {
 #ifdef SRS_AUTO_HTTP_SERVER
-        conn = new SrsHttpConn(this, client_stfd, http_stream_mux);
+        conn = new SrsHttpConn(this, client_stfd, &http_stream_mux->mux);
 #else
         srs_warn("close http client for server not support http-server");
         srs_close_stfd(client_stfd);
diff --git a/trunk/src/app/srs_app_server.hpp b/trunk/src/app/srs_app_server.hpp
index b2ecafbb15..1ff7cee90b 100644
--- a/trunk/src/app/srs_app_server.hpp
+++ b/trunk/src/app/srs_app_server.hpp
@@ -38,6 +38,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #include 
 #include 
 #include 
+#include 
 
 class SrsServer;
 class SrsConnection;
@@ -51,6 +52,9 @@ class ISrsTcpHandler;
 class ISrsUdpHandler;
 class SrsUdpListener;
 class SrsTcpListener;
+#ifdef SRS_AUTO_STREAM_CASTER
+class SrsAppCasterFlv;
+#endif
 
 // listener type for server to identify the connection,
 // that is, use different type to process the connection.
@@ -66,7 +70,7 @@ enum SrsListenerType
     SrsListenerMpegTsOverUdp    = 3,
     // TCP stream, RTSP stream.
     SrsListenerRtsp             = 4,
-    // HTTP stream, FLV over HTTP POST.
+    // TCP stream, FLV stream over HTTP.
     SrsListenerFlv              = 5,
 };
 
@@ -126,13 +130,13 @@ class SrsRtspListener : virtual public SrsListener, virtual public ISrsTcpHandle
 };
 
 /**
- * the tcp listener, for http flv server.
+ * the tcp listener, for flv stream server.
  */
 class SrsHttpFlvListener : virtual public SrsListener, virtual public ISrsTcpHandler
 {
 private:
     SrsTcpListener* listener;
-    ISrsTcpHandler* caster;
+    SrsAppCasterFlv* caster;
 public:
     SrsHttpFlvListener(SrsServer* server, SrsListenerType type, SrsConfDirective* c);
     virtual ~SrsHttpFlvListener();
@@ -215,6 +219,7 @@ class ISrsServerCycle
 */
 class SrsServer : virtual public ISrsReloadHandler
     , virtual public ISrsSourceHandler, virtual public ISrsHlsHandler
+    , virtual public IConnectionManager
 {
 private:
 #ifdef SRS_AUTO_HTTP_API
@@ -279,7 +284,7 @@ class SrsServer : virtual public ISrsReloadHandler
     virtual int http_handle();
     virtual int ingest();
     virtual int cycle();
-// server utility
+// IConnectionManager
 public:
     /**
     * callback for connection to remove itself.
@@ -287,6 +292,8 @@ class SrsServer : virtual public ISrsReloadHandler
     * @see SrsConnection.on_thread_stop().
     */
     virtual void remove(SrsConnection* conn);
+// server utilities.
+public:
     /**
     * callback for signal manager got a signal.
     * the signal manager convert signal to io message,

From a95fd6d1400ddf900b8c55e4988e0896b20b09a7 Mon Sep 17 00:00:00 2001
From: winlin 
Date: Sun, 3 May 2015 23:57:22 +0800
Subject: [PATCH 793/800] read the http flv stream.

---
 trunk/src/app/srs_app_caster_flv.cpp | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/trunk/src/app/srs_app_caster_flv.cpp b/trunk/src/app/srs_app_caster_flv.cpp
index 1f6b7a16c1..e57d0a044b 100644
--- a/trunk/src/app/srs_app_caster_flv.cpp
+++ b/trunk/src/app/srs_app_caster_flv.cpp
@@ -35,6 +35,7 @@ using namespace std;
 #include 
 #include 
 #include 
+#include 
 
 SrsAppCasterFlv::SrsAppCasterFlv(SrsConfDirective* c)
 {
@@ -78,12 +79,24 @@ void SrsAppCasterFlv::remove(SrsConnection* c)
         conns.erase(it);
     }
 }
-
+#define SRS_HTTP_FLV_STREAM_BUFFER 4096
 int SrsAppCasterFlv::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r)
 {
     int ret = ERROR_SUCCESS;
     
-    srs_trace("flv: handle request at %s", r->path().c_str());
+    srs_info("flv: handle request at %s", r->path().c_str());
+    
+    char* buffer = new char[SRS_HTTP_FLV_STREAM_BUFFER];
+    SrsAutoFree(char, buffer);
+    
+    ISrsHttpResponseReader* rr = r->body_reader();
+    while (!rr->eof()) {
+        int nb_read = 0;
+        if ((ret = rr->read(buffer, SRS_HTTP_FLV_STREAM_BUFFER, &nb_read)) != ERROR_SUCCESS) {
+            return ret;
+        }
+        srs_trace("flv: read %dB from %s", nb_read, r->path().c_str());
+    }
     
     return ret;
 }

From ea1e015a4e3301e0040436f1802cd9ec33ed617b Mon Sep 17 00:00:00 2001
From: winlin 
Date: Mon, 4 May 2015 18:11:52 +0800
Subject: [PATCH 794/800] fix the http read chunked encoding bug.

---
 trunk/src/app/srs_app_caster_flv.cpp | 23 ++++++++++--
 trunk/src/app/srs_app_caster_flv.hpp | 10 +++++
 trunk/src/app/srs_app_http.cpp       | 56 +++++++++++++++-------------
 trunk/src/app/srs_app_http.hpp       |  2 +
 trunk/src/app/srs_app_http_conn.cpp  | 31 ++++++++++++---
 trunk/src/app/srs_app_http_conn.hpp  | 14 +++++++
 trunk/src/app/srs_app_server.cpp     |  2 +-
 7 files changed, 104 insertions(+), 34 deletions(-)

diff --git a/trunk/src/app/srs_app_caster_flv.cpp b/trunk/src/app/srs_app_caster_flv.cpp
index e57d0a044b..96854a59ac 100644
--- a/trunk/src/app/srs_app_caster_flv.cpp
+++ b/trunk/src/app/srs_app_caster_flv.cpp
@@ -37,6 +37,8 @@ using namespace std;
 #include 
 #include 
 
+#define SRS_HTTP_FLV_STREAM_BUFFER 4096
+
 SrsAppCasterFlv::SrsAppCasterFlv(SrsConfDirective* c)
 {
     http_mux = new SrsHttpServeMux();
@@ -62,7 +64,7 @@ int SrsAppCasterFlv::on_tcp_client(st_netfd_t stfd)
 {
     int ret = ERROR_SUCCESS;
     
-    SrsHttpConn* conn = new SrsHttpConn(this, stfd, http_mux);
+    SrsHttpConn* conn = new SrsDynamicHttpConn(this, stfd, http_mux);
     conns.push_back(conn);
     
     if ((ret = conn->start()) != ERROR_SUCCESS) {
@@ -79,7 +81,7 @@ void SrsAppCasterFlv::remove(SrsConnection* c)
         conns.erase(it);
     }
 }
-#define SRS_HTTP_FLV_STREAM_BUFFER 4096
+
 int SrsAppCasterFlv::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r)
 {
     int ret = ERROR_SUCCESS;
@@ -95,10 +97,25 @@ int SrsAppCasterFlv::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r)
         if ((ret = rr->read(buffer, SRS_HTTP_FLV_STREAM_BUFFER, &nb_read)) != ERROR_SUCCESS) {
             return ret;
         }
-        srs_trace("flv: read %dB from %s", nb_read, r->path().c_str());
+        //srs_trace("flv: read %dB from %s", nb_read, r->path().c_str());
     }
     
     return ret;
 }
 
+SrsDynamicHttpConn::SrsDynamicHttpConn(IConnectionManager* cm, st_netfd_t fd, SrsHttpServeMux* m)
+    : SrsHttpConn(cm, fd, m)
+{
+}
+
+SrsDynamicHttpConn::~SrsDynamicHttpConn()
+{
+}
+
+int SrsDynamicHttpConn::on_got_http_message(SrsHttpMessage* msg)
+{
+    int ret = ERROR_SUCCESS;
+    return ret;
+}
+
 #endif
diff --git a/trunk/src/app/srs_app_caster_flv.hpp b/trunk/src/app/srs_app_caster_flv.hpp
index fb092f5b2b..fc4edd83b4 100644
--- a/trunk/src/app/srs_app_caster_flv.hpp
+++ b/trunk/src/app/srs_app_caster_flv.hpp
@@ -43,6 +43,7 @@ class SrsHttpConn;
 #include 
 #include 
 #include 
+#include 
 
 class SrsAppCasterFlv : virtual public ISrsTcpHandler
     , virtual public IConnectionManager, virtual public ISrsHttpHandler
@@ -67,6 +68,15 @@ class SrsAppCasterFlv : virtual public ISrsTcpHandler
     virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r);
 };
 
+class SrsDynamicHttpConn : public SrsHttpConn
+{
+public:
+    SrsDynamicHttpConn(IConnectionManager* cm, st_netfd_t fd, SrsHttpServeMux* m);
+    virtual ~SrsDynamicHttpConn();
+public:
+    virtual int on_got_http_message(SrsHttpMessage* msg);
+};
+
 #endif
 
 #endif
diff --git a/trunk/src/app/srs_app_http.cpp b/trunk/src/app/srs_app_http.cpp
index f038f5f713..27979de4f5 100644
--- a/trunk/src/app/srs_app_http.cpp
+++ b/trunk/src/app/srs_app_http.cpp
@@ -905,6 +905,7 @@ int SrsHttpResponseReader::initialize(SrsFastBuffer* body)
 {
     int ret = ERROR_SUCCESS;
     
+    nb_chunk = 0;
     nb_left_chunk = 0;
     nb_total_read = 0;
     buffer = body;
@@ -999,33 +1000,35 @@ int SrsHttpResponseReader::read_chunked(char* data, int nb_data, int* nb_read)
         }
         
         // all bytes in chunk is left now.
-        nb_left_chunk = ilength;
+        nb_chunk = nb_left_chunk = ilength;
     }
     
-    // left bytes in chunk, read some.
-    srs_assert(nb_left_chunk);
-    
-    int nb_bytes = srs_min(nb_left_chunk, nb_data);
-    ret = read_specified(data, nb_bytes, &nb_bytes);
-    
-    // the nb_bytes used for output already read size of bytes.
-    if (nb_read) {
-        *nb_read = nb_bytes;
-    }
-    nb_left_chunk -= nb_bytes;
-    
-    // error or still left bytes in chunk, ignore and read in future.
-    if (nb_left_chunk > 0 || (ret != ERROR_SUCCESS)) {
-        return ret;
-    }
-    srs_info("http: read %d bytes of chunk", nb_bytes);
-    
-    // read payload when length specifies some payload.
-    if (nb_left_chunk <= 0) {
+    if (nb_chunk <= 0) {
+        // for the last chunk, eof.
         is_eof = true;
+    } else {
+        // for not the last chunk, there must always exists bytes.
+        // left bytes in chunk, read some.
+        srs_assert(nb_left_chunk);
+        
+        int nb_bytes = srs_min(nb_left_chunk, nb_data);
+        ret = read_specified(data, nb_bytes, &nb_bytes);
+        
+        // the nb_bytes used for output already read size of bytes.
+        if (nb_read) {
+            *nb_read = nb_bytes;
+        }
+        nb_left_chunk -= nb_bytes;
+        srs_info("http: read %d bytes of chunk", nb_bytes);
+        
+        // error or still left bytes in chunk, ignore and read in future.
+        if (nb_left_chunk > 0 || (ret != ERROR_SUCCESS)) {
+            return ret;
+        }
+        srs_info("http: read total chunk %dB", nb_chunk);
     }
     
-    // the CRLF of chunk payload end.
+    // for both the last or not, the CRLF of chunk payload end.
     if ((ret = buffer->grow(skt, 2)) != ERROR_SUCCESS) {
         if (!srs_is_client_gracefully_close(ret)) {
             srs_error("read EOF of chunk from server failed. ret=%d", ret);
@@ -1064,9 +1067,12 @@ int SrsHttpResponseReader::read_specified(char* data, int nb_data, int* nb_read)
     // increase the total read to determine whether EOF.
     nb_total_read += nb_bytes;
     
-    // when read completed, eof.
-    if (nb_total_read >= (int)owner->content_length()) {
-        is_eof = true;
+    // for not chunked
+    if (!owner->is_chunked()) {
+        // when read completed, eof.
+        if (nb_total_read >= (int)owner->content_length()) {
+            is_eof = true;
+        }
     }
     
     return ret;
diff --git a/trunk/src/app/srs_app_http.hpp b/trunk/src/app/srs_app_http.hpp
index 7c2e1c9381..90344751bd 100644
--- a/trunk/src/app/srs_app_http.hpp
+++ b/trunk/src/app/srs_app_http.hpp
@@ -436,6 +436,8 @@ class SrsHttpResponseReader : virtual public ISrsHttpResponseReader
     bool is_eof;
     // the left bytes in chunk.
     int nb_left_chunk;
+    // the number of bytes of current chunk.
+    int nb_chunk;
     // already read total bytes.
     int64_t nb_total_read;
 public:
diff --git a/trunk/src/app/srs_app_http_conn.cpp b/trunk/src/app/srs_app_http_conn.cpp
index c6f4b94b5a..21eb95511f 100644
--- a/trunk/src/app/srs_app_http_conn.cpp
+++ b/trunk/src/app/srs_app_http_conn.cpp
@@ -1398,11 +1398,8 @@ int SrsHttpConn::do_cycle()
         // always free it in this scope.
         SrsAutoFree(SrsHttpMessage, req);
         
-        // TODO: FIXME: use the post body.
-        std::string res;
-        
-        // get response body.
-        if ((ret = req->body_read_all(res)) != ERROR_SUCCESS) {
+        // may should discard the body.
+        if ((ret = on_got_http_message(req)) != ERROR_SUCCESS) {
             return ret;
         }
         
@@ -1434,5 +1431,29 @@ int SrsHttpConn::process_request(ISrsHttpResponseWriter* w, SrsHttpMessage* r)
     return ret;
 }
 
+SrsStaticHttpConn::SrsStaticHttpConn(IConnectionManager* cm, st_netfd_t fd, SrsHttpServeMux* m)
+    : SrsHttpConn(cm, fd, m)
+{
+}
+
+SrsStaticHttpConn::~SrsStaticHttpConn()
+{
+}
+
+int SrsStaticHttpConn::on_got_http_message(SrsHttpMessage* msg)
+{
+    int ret = ERROR_SUCCESS;
+    
+    // TODO: FIXME: use the post body.
+    std::string res;
+    
+    // get response body.
+    if ((ret = msg->body_read_all(res)) != ERROR_SUCCESS) {
+        return ret;
+    }
+    
+    return ret;
+}
+
 #endif
 
diff --git a/trunk/src/app/srs_app_http_conn.hpp b/trunk/src/app/srs_app_http_conn.hpp
index 63f87a5ffb..5b4bf62d58 100644
--- a/trunk/src/app/srs_app_http_conn.hpp
+++ b/trunk/src/app/srs_app_http_conn.hpp
@@ -388,10 +388,24 @@ class SrsHttpConn : public SrsConnection
     virtual void cleanup();
 protected:
     virtual int do_cycle();
+protected:
+    // when got http message,
+    // for the static service or api, discard any body.
+    // for the stream caster, for instance, http flv streaming, may discard the flv header or not.
+    virtual int on_got_http_message(SrsHttpMessage* msg) = 0;
 private:
     virtual int process_request(ISrsHttpResponseWriter* w, SrsHttpMessage* r);
 };
 
+class SrsStaticHttpConn : public SrsHttpConn
+{
+public:
+    SrsStaticHttpConn(IConnectionManager* cm, st_netfd_t fd, SrsHttpServeMux* m);
+    virtual ~SrsStaticHttpConn();
+public:
+    virtual int on_got_http_message(SrsHttpMessage* msg);
+};
+
 #endif
 
 #endif
diff --git a/trunk/src/app/srs_app_server.cpp b/trunk/src/app/srs_app_server.cpp
index 851c8023cc..9b06c8a9dd 100644
--- a/trunk/src/app/srs_app_server.cpp
+++ b/trunk/src/app/srs_app_server.cpp
@@ -1163,7 +1163,7 @@ int SrsServer::accept_client(SrsListenerType type, st_netfd_t client_stfd)
 #endif
     } else if (type == SrsListenerHttpStream) {
 #ifdef SRS_AUTO_HTTP_SERVER
-        conn = new SrsHttpConn(this, client_stfd, &http_stream_mux->mux);
+        conn = new SrsStaticHttpConn(this, client_stfd, &http_stream_mux->mux);
 #else
         srs_warn("close http client for server not support http-server");
         srs_close_stfd(client_stfd);

From ba6c3132e37073ee7c0d08b92b73b676bead0d88 Mon Sep 17 00:00:00 2001
From: winlin 
Date: Mon, 4 May 2015 18:28:41 +0800
Subject: [PATCH 795/800] refine code, use decoder to parse flv stream

---
 trunk/src/app/srs_app_caster_flv.cpp  | 72 +++++++++++++++++++++++++++
 trunk/src/app/srs_app_caster_flv.hpp  | 39 +++++++++++++++
 trunk/src/kernel/srs_kernel_error.hpp |  1 +
 3 files changed, 112 insertions(+)

diff --git a/trunk/src/app/srs_app_caster_flv.cpp b/trunk/src/app/srs_app_caster_flv.cpp
index 96854a59ac..8be72aec17 100644
--- a/trunk/src/app/srs_app_caster_flv.cpp
+++ b/trunk/src/app/srs_app_caster_flv.cpp
@@ -36,6 +36,7 @@ using namespace std;
 #include 
 #include 
 #include 
+#include 
 
 #define SRS_HTTP_FLV_STREAM_BUFFER 4096
 
@@ -92,6 +93,13 @@ int SrsAppCasterFlv::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r)
     SrsAutoFree(char, buffer);
     
     ISrsHttpResponseReader* rr = r->body_reader();
+    SrsHttpFileReader reader(rr);
+    SrsFlvDecoder dec;
+    
+    if ((ret = dec.initialize(&reader)) != ERROR_SUCCESS) {
+        return ret;
+    }
+    
     while (!rr->eof()) {
         int nb_read = 0;
         if ((ret = rr->read(buffer, SRS_HTTP_FLV_STREAM_BUFFER, &nb_read)) != ERROR_SUCCESS) {
@@ -118,4 +126,68 @@ int SrsDynamicHttpConn::on_got_http_message(SrsHttpMessage* msg)
     return ret;
 }
 
+SrsHttpFileReader::SrsHttpFileReader(ISrsHttpResponseReader* h)
+{
+    http = h;
+}
+
+SrsHttpFileReader::~SrsHttpFileReader()
+{
+}
+
+int SrsHttpFileReader::open(std::string /*file*/)
+{
+    return ERROR_SUCCESS;
+}
+
+void SrsHttpFileReader::close()
+{
+}
+
+bool SrsHttpFileReader::is_open()
+{
+    return false;
+}
+
+int64_t SrsHttpFileReader::tellg()
+{
+    return 0;
+}
+
+void SrsHttpFileReader::skip(int64_t /*size*/)
+{
+}
+
+int64_t SrsHttpFileReader::lseek(int64_t offset)
+{
+    return offset;
+}
+
+int64_t SrsHttpFileReader::filesize()
+{
+    return 0;
+}
+
+int SrsHttpFileReader::read(void* buf, size_t count, ssize_t* pnread)
+{
+    int ret = ERROR_SUCCESS;
+    
+    if (http->eof()) {
+        ret = ERROR_HTTP_REQUEST_EOF;
+        srs_error("flv: encoder EOF. ret=%d", ret);
+        return ret;
+    }
+    
+    int nread = 0;
+    if ((ret = http->read((char*)buf, (int)count, &nread)) != ERROR_SUCCESS) {
+        return ret;
+    }
+    
+    if (pnread) {
+        *pnread = nread;
+    }
+    
+    return ret;
+}
+
 #endif
diff --git a/trunk/src/app/srs_app_caster_flv.hpp b/trunk/src/app/srs_app_caster_flv.hpp
index fc4edd83b4..61899f713a 100644
--- a/trunk/src/app/srs_app_caster_flv.hpp
+++ b/trunk/src/app/srs_app_caster_flv.hpp
@@ -44,7 +44,11 @@ class SrsHttpConn;
 #include 
 #include 
 #include 
+#include 
 
+/**
+ * the stream caster for flv stream over HTTP POST.
+ */
 class SrsAppCasterFlv : virtual public ISrsTcpHandler
     , virtual public IConnectionManager, virtual public ISrsHttpHandler
 {
@@ -68,6 +72,9 @@ class SrsAppCasterFlv : virtual public ISrsTcpHandler
     virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r);
 };
 
+/**
+ * the dynamic http connection, never drop the body.
+ */
 class SrsDynamicHttpConn : public SrsHttpConn
 {
 public:
@@ -77,6 +84,38 @@ class SrsDynamicHttpConn : public SrsHttpConn
     virtual int on_got_http_message(SrsHttpMessage* msg);
 };
 
+/**
+ * the http wrapper for file reader,
+ * to read http post stream like a file.
+ */
+class SrsHttpFileReader : public SrsFileReader
+{
+private:
+    ISrsHttpResponseReader* http;
+public:
+    SrsHttpFileReader(ISrsHttpResponseReader* h);
+    virtual ~SrsHttpFileReader();
+public:
+    /**
+     * open file reader, can open then close then open...
+     */
+    virtual int open(std::string file);
+    virtual void close();
+public:
+    // TODO: FIXME: extract interface.
+    virtual bool is_open();
+    virtual int64_t tellg();
+    virtual void skip(int64_t size);
+    virtual int64_t lseek(int64_t offset);
+    virtual int64_t filesize();
+public:
+    /**
+     * read from file.
+     * @param pnread the output nb_read, NULL to ignore.
+     */
+    virtual int read(void* buf, size_t count, ssize_t* pnread);
+};
+
 #endif
 
 #endif
diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp
index bb2cf05d86..bfcdad6b64 100644
--- a/trunk/src/kernel/srs_kernel_error.hpp
+++ b/trunk/src/kernel/srs_kernel_error.hpp
@@ -255,6 +255,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #define ERROR_HTTP_INVALID_CHUNK_HEADER     4026
 #define ERROR_AVC_NALU_UEV                  4027
 #define ERROR_AAC_BYTES_INVALID             4028
+#define ERROR_HTTP_REQUEST_EOF             4029
 
 ///////////////////////////////////////////////////////
 // user-define error.

From cba667556026c0bd3b7eef9e8c5975be413659e8 Mon Sep 17 00:00:00 2001
From: winlin 
Date: Mon, 4 May 2015 19:06:38 +0800
Subject: [PATCH 796/800] refine the http message, set the connection if
 required.

---
 trunk/src/app/srs_app_caster_flv.cpp  | 173 ++++++++++++++++++++++++--
 trunk/src/app/srs_app_caster_flv.hpp  |  20 +++
 trunk/src/app/srs_app_http.cpp        |  15 ++-
 trunk/src/app/srs_app_http.hpp        |   8 +-
 trunk/src/app/srs_app_http_api.cpp    |   2 +-
 trunk/src/app/srs_app_http_client.cpp |   4 +-
 trunk/src/app/srs_app_http_conn.cpp   |   2 +-
 7 files changed, 206 insertions(+), 18 deletions(-)

diff --git a/trunk/src/app/srs_app_caster_flv.cpp b/trunk/src/app/srs_app_caster_flv.cpp
index 8be72aec17..3a7493403d 100644
--- a/trunk/src/app/srs_app_caster_flv.cpp
+++ b/trunk/src/app/srs_app_caster_flv.cpp
@@ -37,6 +37,12 @@ using namespace std;
 #include 
 #include 
 #include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
 
 #define SRS_HTTP_FLV_STREAM_BUFFER 4096
 
@@ -84,10 +90,54 @@ void SrsAppCasterFlv::remove(SrsConnection* c)
 }
 
 int SrsAppCasterFlv::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r)
+{
+    SrsDynamicHttpConn* conn = dynamic_cast(r->connection());
+    srs_assert(conn);
+    
+    std::string app = srs_path_dirname(r->path());
+    std::string stream = srs_path_basename(r->path());
+    
+    std::string o = output;
+    if (!app.empty() && app != "/") {
+        o = srs_string_replace(o, "[app]", app);
+    }
+    o = srs_string_replace(o, "[stream]", stream);
+    
+    // remove the extension.
+    if (srs_string_ends_with(o, ".flv")) {
+        o = o.substr(0, o.length() - 4);
+    }
+    
+    return conn->proxy(w, r, o);
+}
+
+SrsDynamicHttpConn::SrsDynamicHttpConn(IConnectionManager* cm, st_netfd_t fd, SrsHttpServeMux* m)
+    : SrsHttpConn(cm, fd, m)
+{
+    
+    req = NULL;
+    io = NULL;
+    client = NULL;
+    stfd = NULL;
+    stream_id = 0;
+}
+
+SrsDynamicHttpConn::~SrsDynamicHttpConn()
+{
+}
+
+int SrsDynamicHttpConn::on_got_http_message(SrsHttpMessage* msg)
+{
+    int ret = ERROR_SUCCESS;
+    return ret;
+}
+
+int SrsDynamicHttpConn::proxy(ISrsHttpResponseWriter* w, SrsHttpMessage* r, std::string o)
 {
     int ret = ERROR_SUCCESS;
     
-    srs_info("flv: handle request at %s", r->path().c_str());
+    output = o;
+    srs_trace("flv: proxy %s to %s", r->uri().c_str(), output.c_str());
     
     char* buffer = new char[SRS_HTTP_FLV_STREAM_BUFFER];
     SrsAutoFree(char, buffer);
@@ -111,19 +161,126 @@ int SrsAppCasterFlv::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r)
     return ret;
 }
 
-SrsDynamicHttpConn::SrsDynamicHttpConn(IConnectionManager* cm, st_netfd_t fd, SrsHttpServeMux* m)
-    : SrsHttpConn(cm, fd, m)
+int SrsDynamicHttpConn::connect()
 {
+    int ret = ERROR_SUCCESS;
+    
+    // when ok, ignore.
+    // TODO: FIXME: should reconnect when disconnected.
+    if (io || client) {
+        return ret;
+    }
+    
+    // parse uri
+    if (!req) {
+        req = new SrsRequest();
+        
+        size_t pos = string::npos;
+        string uri = req->tcUrl = output;
+        
+        // tcUrl, stream
+        if ((pos = uri.rfind("/")) != string::npos) {
+            req->stream = uri.substr(pos + 1);
+            req->tcUrl = uri = uri.substr(0, pos);
+        }
+        
+        srs_discovery_tc_url(req->tcUrl,
+                             req->schema, req->host, req->vhost, req->app, req->port,
+                             req->param);
+    }
+    
+    // connect host.
+    if ((ret = srs_socket_connect(req->host, ::atoi(req->port.c_str()), ST_UTIME_NO_TIMEOUT, &stfd)) != ERROR_SUCCESS) {
+        srs_error("mpegts: connect server %s:%s failed. ret=%d", req->host.c_str(), req->port.c_str(), ret);
+        return ret;
+    }
+    io = new SrsStSocket(stfd);
+    client = new SrsRtmpClient(io);
+    
+    client->set_recv_timeout(SRS_CONSTS_RTMP_RECV_TIMEOUT_US);
+    client->set_send_timeout(SRS_CONSTS_RTMP_SEND_TIMEOUT_US);
+    
+    // connect to vhost/app
+    if ((ret = client->handshake()) != ERROR_SUCCESS) {
+        srs_error("mpegts: handshake with server failed. ret=%d", ret);
+        return ret;
+    }
+    if ((ret = connect_app(req->host, req->port)) != ERROR_SUCCESS) {
+        srs_error("mpegts: connect with server failed. ret=%d", ret);
+        return ret;
+    }
+    if ((ret = client->create_stream(stream_id)) != ERROR_SUCCESS) {
+        srs_error("mpegts: connect with server failed, stream_id=%d. ret=%d", stream_id, ret);
+        return ret;
+    }
+    
+    // publish.
+    if ((ret = client->publish(req->stream, stream_id)) != ERROR_SUCCESS) {
+        srs_error("mpegts: publish failed, stream=%s, stream_id=%d. ret=%d",
+                  req->stream.c_str(), stream_id, ret);
+        return ret;
+    }
+    
+    return ret;
 }
 
-SrsDynamicHttpConn::~SrsDynamicHttpConn()
+// TODO: FIXME: refine the connect_app.
+int SrsDynamicHttpConn::connect_app(string ep_server, string ep_port)
 {
+    int ret = ERROR_SUCCESS;
+    
+    // args of request takes the srs info.
+    if (req->args == NULL) {
+        req->args = SrsAmf0Any::object();
+    }
+    
+    // notify server the edge identity,
+    // @see https://github.com/simple-rtmp-server/srs/issues/147
+    SrsAmf0Object* data = req->args;
+    data->set("srs_sig", SrsAmf0Any::str(RTMP_SIG_SRS_KEY));
+    data->set("srs_server", SrsAmf0Any::str(RTMP_SIG_SRS_KEY" "RTMP_SIG_SRS_VERSION" ("RTMP_SIG_SRS_URL_SHORT")"));
+    data->set("srs_license", SrsAmf0Any::str(RTMP_SIG_SRS_LICENSE));
+    data->set("srs_role", SrsAmf0Any::str(RTMP_SIG_SRS_ROLE));
+    data->set("srs_url", SrsAmf0Any::str(RTMP_SIG_SRS_URL));
+    data->set("srs_version", SrsAmf0Any::str(RTMP_SIG_SRS_VERSION));
+    data->set("srs_site", SrsAmf0Any::str(RTMP_SIG_SRS_WEB));
+    data->set("srs_email", SrsAmf0Any::str(RTMP_SIG_SRS_EMAIL));
+    data->set("srs_copyright", SrsAmf0Any::str(RTMP_SIG_SRS_COPYRIGHT));
+    data->set("srs_primary", SrsAmf0Any::str(RTMP_SIG_SRS_PRIMARY));
+    data->set("srs_authors", SrsAmf0Any::str(RTMP_SIG_SRS_AUTHROS));
+    // for edge to directly get the id of client.
+    data->set("srs_pid", SrsAmf0Any::number(getpid()));
+    data->set("srs_id", SrsAmf0Any::number(_srs_context->get_id()));
+    
+    // local ip of edge
+    std::vector ips = srs_get_local_ipv4_ips();
+    assert(_srs_config->get_stats_network() < (int)ips.size());
+    std::string local_ip = ips[_srs_config->get_stats_network()];
+    data->set("srs_server_ip", SrsAmf0Any::str(local_ip.c_str()));
+    
+    // generate the tcUrl
+    std::string param = "";
+    std::string tc_url = srs_generate_tc_url(ep_server, req->vhost, req->app, ep_port, param);
+    
+    // upnode server identity will show in the connect_app of client.
+    // @see https://github.com/simple-rtmp-server/srs/issues/160
+    // the debug_srs_upnode is config in vhost and default to true.
+    bool debug_srs_upnode = _srs_config->get_debug_srs_upnode(req->vhost);
+    if ((ret = client->connect_app(req->app, tc_url, req, debug_srs_upnode)) != ERROR_SUCCESS) {
+        srs_error("mpegts: connect with server failed, tcUrl=%s, dsu=%d. ret=%d",
+                  tc_url.c_str(), debug_srs_upnode, ret);
+        return ret;
+    }
+    
+    return ret;
 }
 
-int SrsDynamicHttpConn::on_got_http_message(SrsHttpMessage* msg)
+void SrsDynamicHttpConn::close()
 {
-    int ret = ERROR_SUCCESS;
-    return ret;
+    srs_freep(client);
+    srs_freep(io);
+    srs_freep(req);
+    srs_close_stfd(stfd);
 }
 
 SrsHttpFileReader::SrsHttpFileReader(ISrsHttpResponseReader* h)
@@ -146,7 +303,7 @@ void SrsHttpFileReader::close()
 
 bool SrsHttpFileReader::is_open()
 {
-    return false;
+    return true;
 }
 
 int64_t SrsHttpFileReader::tellg()
diff --git a/trunk/src/app/srs_app_caster_flv.hpp b/trunk/src/app/srs_app_caster_flv.hpp
index 61899f713a..79ee4e370f 100644
--- a/trunk/src/app/srs_app_caster_flv.hpp
+++ b/trunk/src/app/srs_app_caster_flv.hpp
@@ -38,6 +38,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 class SrsConfDirective;
 class SrsHttpServeMux;
 class SrsHttpConn;
+class SrsRtmpClient;
+class SrsStSocket;
+class SrsRequest;
 
 #include 
 #include 
@@ -77,11 +80,28 @@ class SrsAppCasterFlv : virtual public ISrsTcpHandler
  */
 class SrsDynamicHttpConn : public SrsHttpConn
 {
+private:
+    std::string output;
+private:
+    SrsRequest* req;
+    st_netfd_t stfd;
+    SrsStSocket* io;
+    SrsRtmpClient* client;
+    int stream_id;
 public:
     SrsDynamicHttpConn(IConnectionManager* cm, st_netfd_t fd, SrsHttpServeMux* m);
     virtual ~SrsDynamicHttpConn();
 public:
     virtual int on_got_http_message(SrsHttpMessage* msg);
+public:
+    virtual int proxy(ISrsHttpResponseWriter* w, SrsHttpMessage* r, std::string o);
+private:
+    // connect to rtmp output url.
+    // @remark ignore when not connected, reconnect when disconnected.
+    virtual int connect();
+    virtual int connect_app(std::string ep_server, std::string ep_port);
+    // close the connected io and rtmp to ready to be re-connect.
+    virtual void close();
 };
 
 /**
diff --git a/trunk/src/app/srs_app_http.cpp b/trunk/src/app/srs_app_http.cpp
index 27979de4f5..4ed1453f28 100644
--- a/trunk/src/app/srs_app_http.cpp
+++ b/trunk/src/app/srs_app_http.cpp
@@ -1078,8 +1078,9 @@ int SrsHttpResponseReader::read_specified(char* data, int nb_data, int* nb_read)
     return ret;
 }
 
-SrsHttpMessage::SrsHttpMessage(SrsStSocket* io)
+SrsHttpMessage::SrsHttpMessage(SrsStSocket* io, SrsConnection* c)
 {
+    conn = c;
     chunked = false;
     _uri = new SrsHttpUri();
     _body = new SrsHttpResponseReader(this, io);
@@ -1165,6 +1166,11 @@ char* SrsHttpMessage::http_ts_send_buffer()
     return _http_ts_send_buffer;
 }
 
+SrsConnection* SrsHttpMessage::connection()
+{
+    return conn;
+}
+
 u_int8_t SrsHttpMessage::method()
 {
     return (u_int8_t)_header.method;
@@ -1230,8 +1236,9 @@ string SrsHttpMessage::uri()
 {
     std::string uri = _uri->get_schema();
     if (uri.empty()) {
-        uri += "http://";
+        uri += "http";
     }
+    uri += "://";
     
     uri += host();
     uri += path();
@@ -1393,7 +1400,7 @@ int SrsHttpParser::initialize(enum http_parser_type type)
     return ret;
 }
 
-int SrsHttpParser::parse_message(SrsStSocket* skt, SrsHttpMessage** ppmsg)
+int SrsHttpParser::parse_message(SrsStSocket* skt, SrsConnection* conn, SrsHttpMessage** ppmsg)
 {
     *ppmsg = NULL;
     
@@ -1418,7 +1425,7 @@ int SrsHttpParser::parse_message(SrsStSocket* skt, SrsHttpMessage** ppmsg)
     }
 
     // create msg
-    SrsHttpMessage* msg = new SrsHttpMessage(skt);
+    SrsHttpMessage* msg = new SrsHttpMessage(skt, conn);
     
     // initalize http msg, parse url.
     if ((ret = msg->update(url, &header, buffer, headers)) != ERROR_SUCCESS) {
diff --git a/trunk/src/app/srs_app_http.hpp b/trunk/src/app/srs_app_http.hpp
index 90344751bd..190999b506 100644
--- a/trunk/src/app/srs_app_http.hpp
+++ b/trunk/src/app/srs_app_http.hpp
@@ -50,6 +50,7 @@ class SrsSimpleBuffer;
 class SrsHttpMuxEntry;
 class ISrsHttpResponseWriter;
 class SrsFastBuffer;
+class SrsConnection;
 
 // http specification
 // CR             = 
@@ -506,8 +507,10 @@ class SrsHttpMessage
     std::vector _headers;
     // the query map
     std::map _query;
+    // the transport connection, can be NULL.
+    SrsConnection* conn;
 public:
-    SrsHttpMessage(SrsStSocket* io);
+    SrsHttpMessage(SrsStSocket* io, SrsConnection* c);
     virtual ~SrsHttpMessage();
 public:
     /**
@@ -518,6 +521,7 @@ class SrsHttpMessage
     );
 public:
     virtual char* http_ts_send_buffer();
+    virtual SrsConnection* connection();
 public:
     virtual u_int8_t method();
     virtual u_int16_t status_code();
@@ -617,7 +621,7 @@ class SrsHttpParser
     * or error and *ppmsg must be NULL.
     * @remark, if success, *ppmsg always NOT-NULL, *ppmsg always is_complete().
     */
-    virtual int parse_message(SrsStSocket* skt, SrsHttpMessage** ppmsg);
+    virtual int parse_message(SrsStSocket* skt, SrsConnection* conn, SrsHttpMessage** ppmsg);
 private:
     /**
     * parse the HTTP message to member field: msg.
diff --git a/trunk/src/app/srs_app_http_api.cpp b/trunk/src/app/srs_app_http_api.cpp
index 0f3aac193a..d4e3ea5fcb 100644
--- a/trunk/src/app/srs_app_http_api.cpp
+++ b/trunk/src/app/srs_app_http_api.cpp
@@ -528,7 +528,7 @@ int SrsHttpApi::do_cycle()
         SrsHttpMessage* req = NULL;
         
         // get a http message
-        if ((ret = parser->parse_message(&skt, &req)) != ERROR_SUCCESS) {
+        if ((ret = parser->parse_message(&skt, this, &req)) != ERROR_SUCCESS) {
             return ret;
         }
 
diff --git a/trunk/src/app/srs_app_http_client.cpp b/trunk/src/app/srs_app_http_client.cpp
index a36ef69e10..377f9a79b3 100644
--- a/trunk/src/app/srs_app_http_client.cpp
+++ b/trunk/src/app/srs_app_http_client.cpp
@@ -105,7 +105,7 @@ int SrsHttpClient::post(string path, string req, SrsHttpMessage** ppmsg)
     }
     
     SrsHttpMessage* msg = NULL;
-    if ((ret = parser->parse_message(skt, &msg)) != ERROR_SUCCESS) {
+    if ((ret = parser->parse_message(skt, NULL, &msg)) != ERROR_SUCCESS) {
         srs_error("parse http post response failed. ret=%d", ret);
         return ret;
     }
@@ -151,7 +151,7 @@ int SrsHttpClient::get(string path, std::string req, SrsHttpMessage** ppmsg)
     }
 
     SrsHttpMessage* msg = NULL;
-    if ((ret = parser->parse_message(skt, &msg)) != ERROR_SUCCESS) {
+    if ((ret = parser->parse_message(skt, NULL, &msg)) != ERROR_SUCCESS) {
         srs_error("parse http post response failed. ret=%d", ret);
         return ret;
     }
diff --git a/trunk/src/app/srs_app_http_conn.cpp b/trunk/src/app/srs_app_http_conn.cpp
index 21eb95511f..bdf8e2dc0f 100644
--- a/trunk/src/app/srs_app_http_conn.cpp
+++ b/trunk/src/app/srs_app_http_conn.cpp
@@ -1388,7 +1388,7 @@ int SrsHttpConn::do_cycle()
         SrsHttpMessage* req = NULL;
         
         // get a http message
-        if ((ret = parser->parse_message(&skt, &req)) != ERROR_SUCCESS) {
+        if ((ret = parser->parse_message(&skt, this, &req)) != ERROR_SUCCESS) {
             return ret;
         }
 

From ad32048c1c09c4acbf2d92263066687e8cd8f506 Mon Sep 17 00:00:00 2001
From: winlin 
Date: Mon, 4 May 2015 21:55:19 +0800
Subject: [PATCH 797/800] fix the http flv stream caster.

---
 trunk/src/app/srs_app_caster_flv.cpp | 103 +++++++++++++++++++++++++--
 trunk/src/app/srs_app_caster_flv.hpp |   4 ++
 2 files changed, 100 insertions(+), 7 deletions(-)

diff --git a/trunk/src/app/srs_app_caster_flv.cpp b/trunk/src/app/srs_app_caster_flv.cpp
index 3a7493403d..f28d639047 100644
--- a/trunk/src/app/srs_app_caster_flv.cpp
+++ b/trunk/src/app/srs_app_caster_flv.cpp
@@ -95,7 +95,10 @@ int SrsAppCasterFlv::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r)
     srs_assert(conn);
     
     std::string app = srs_path_dirname(r->path());
+    app = srs_string_trim_start(app, "/");
+    
     std::string stream = srs_path_basename(r->path());
+    stream = srs_string_trim_start(stream, "/");
     
     std::string o = output;
     if (!app.empty() && app != "/") {
@@ -120,10 +123,15 @@ SrsDynamicHttpConn::SrsDynamicHttpConn(IConnectionManager* cm, st_netfd_t fd, Sr
     client = NULL;
     stfd = NULL;
     stream_id = 0;
+    
+    pprint = SrsPithyPrint::create_caster();
 }
 
 SrsDynamicHttpConn::~SrsDynamicHttpConn()
 {
+    close();
+    
+    srs_freep(pprint);
 }
 
 int SrsDynamicHttpConn::on_got_http_message(SrsHttpMessage* msg)
@@ -150,12 +158,87 @@ int SrsDynamicHttpConn::proxy(ISrsHttpResponseWriter* w, SrsHttpMessage* r, std:
         return ret;
     }
     
+    char header[9];
+    if ((ret = dec.read_header(header)) != ERROR_SUCCESS) {
+        if (!srs_is_client_gracefully_close(ret)) {
+            srs_error("flv: proxy flv header failed. ret=%d", ret);
+        }
+        return ret;
+    }
+    srs_trace("flv: proxy drop flv header.");
+    
+    char pps[4];
+    if ((ret = dec.read_previous_tag_size(pps)) != ERROR_SUCCESS) {
+        if (!srs_is_client_gracefully_close(ret)) {
+            srs_error("flv: proxy flv header pps failed. ret=%d", ret);
+        }
+        return ret;
+    }
+    
     while (!rr->eof()) {
-        int nb_read = 0;
-        if ((ret = rr->read(buffer, SRS_HTTP_FLV_STREAM_BUFFER, &nb_read)) != ERROR_SUCCESS) {
+        pprint->elapse();
+        
+        if ((ret = connect()) != ERROR_SUCCESS) {
+            return ret;
+        }
+        
+        char type;
+        int32_t size;
+        u_int32_t time;
+        if ((ret = dec.read_tag_header(&type, &size, &time)) != ERROR_SUCCESS) {
+            if (!srs_is_client_gracefully_close(ret)) {
+                srs_error("flv: proxy tag header failed. ret=%d", ret);
+            }
+            return ret;
+        }
+        
+        char* data = new char[size];
+        if ((ret = dec.read_tag_data(data, size)) != ERROR_SUCCESS) {
+            srs_freep(data);
+            if (!srs_is_client_gracefully_close(ret)) {
+                srs_error("flv: proxy tag data failed. ret=%d", ret);
+            }
+            return ret;
+        }
+        
+        if ((ret = rtmp_write_packet(type, time, data, size)) != ERROR_SUCCESS) {
+            if (!srs_is_client_gracefully_close(ret)) {
+                srs_error("flv: proxy rtmp packet failed. ret=%d", ret);
+            }
+            return ret;
+        }
+        
+        if ((ret = dec.read_previous_tag_size(pps)) != ERROR_SUCCESS) {
+            if (!srs_is_client_gracefully_close(ret)) {
+                srs_error("flv: proxy tag header pps failed. ret=%d", ret);
+            }
             return ret;
         }
-        //srs_trace("flv: read %dB from %s", nb_read, r->path().c_str());
+    }
+    
+    return ret;
+}
+
+int SrsDynamicHttpConn::rtmp_write_packet(char type, u_int32_t timestamp, char* data, int size)
+{
+    int ret = ERROR_SUCCESS;
+    
+    SrsSharedPtrMessage* msg = NULL;
+    
+    if ((ret = srs_rtmp_create_msg(type, timestamp, data, size, stream_id, &msg)) != ERROR_SUCCESS) {
+        srs_error("flv: create shared ptr msg failed. ret=%d", ret);
+        return ret;
+    }
+    srs_assert(msg);
+    
+    if (pprint->can_print()) {
+        srs_trace("flv: send msg %s age=%d, dts=%"PRId64", size=%d",
+                  msg->is_audio()? "A":msg->is_video()? "V":"N", pprint->age(), msg->timestamp, msg->size);
+    }
+    
+    // send out encoded msg.
+    if ((ret = client->send_and_free_message(msg, stream_id)) != ERROR_SUCCESS) {
+        return ret;
     }
     
     return ret;
@@ -335,13 +418,19 @@ int SrsHttpFileReader::read(void* buf, size_t count, ssize_t* pnread)
         return ret;
     }
     
-    int nread = 0;
-    if ((ret = http->read((char*)buf, (int)count, &nread)) != ERROR_SUCCESS) {
-        return ret;
+    int total_read = 0;
+    while (total_read < count) {
+        int nread = 0;
+        if ((ret = http->read((char*)buf + total_read, (int)(count - total_read), &nread)) != ERROR_SUCCESS) {
+            return ret;
+        }
+        
+        srs_assert(nread);
+        total_read += nread;
     }
     
     if (pnread) {
-        *pnread = nread;
+        *pnread = total_read;
     }
     
     return ret;
diff --git a/trunk/src/app/srs_app_caster_flv.hpp b/trunk/src/app/srs_app_caster_flv.hpp
index 79ee4e370f..ac8987fd26 100644
--- a/trunk/src/app/srs_app_caster_flv.hpp
+++ b/trunk/src/app/srs_app_caster_flv.hpp
@@ -41,6 +41,7 @@ class SrsHttpConn;
 class SrsRtmpClient;
 class SrsStSocket;
 class SrsRequest;
+class SrsPithyPrint;
 
 #include 
 #include 
@@ -82,6 +83,7 @@ class SrsDynamicHttpConn : public SrsHttpConn
 {
 private:
     std::string output;
+    SrsPithyPrint* pprint;
 private:
     SrsRequest* req;
     st_netfd_t stfd;
@@ -95,6 +97,8 @@ class SrsDynamicHttpConn : public SrsHttpConn
     virtual int on_got_http_message(SrsHttpMessage* msg);
 public:
     virtual int proxy(ISrsHttpResponseWriter* w, SrsHttpMessage* r, std::string o);
+private:
+    virtual int rtmp_write_packet(char type, u_int32_t timestamp, char* data, int size);
 private:
     // connect to rtmp output url.
     // @remark ignore when not connected, reconnect when disconnected.

From e221d002a20ed91889d0cb38fb8d2b3fa4634ee2 Mon Sep 17 00:00:00 2001
From: winlin 
Date: Tue, 5 May 2015 07:37:12 +0800
Subject: [PATCH 798/800] fix the flv caster bug, when nread is 0, disconnect
 it.

---
 trunk/src/app/srs_app_caster_flv.cpp | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/trunk/src/app/srs_app_caster_flv.cpp b/trunk/src/app/srs_app_caster_flv.cpp
index f28d639047..8e88f37fb2 100644
--- a/trunk/src/app/srs_app_caster_flv.cpp
+++ b/trunk/src/app/srs_app_caster_flv.cpp
@@ -425,6 +425,12 @@ int SrsHttpFileReader::read(void* buf, size_t count, ssize_t* pnread)
             return ret;
         }
         
+        if (nread == 0) {
+            ret = ERROR_HTTP_REQUEST_EOF;
+            srs_warn("flv: encoder read EOF. ret=%d", ret);
+            break;
+        }
+        
         srs_assert(nread);
         total_read += nread;
     }

From ffdabf32ed19b1db134b9d384eb43ecf24f22be3 Mon Sep 17 00:00:00 2001
From: winlin 
Date: Tue, 5 May 2015 11:44:20 +0800
Subject: [PATCH 799/800] add push flv conf

---
 trunk/conf/full.conf     |  1 +
 trunk/conf/push.flv.conf | 14 ++++++++++++++
 2 files changed, 15 insertions(+)
 create mode 100644 trunk/conf/push.flv.conf

diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf
index a639e26758..bab1433ded 100644
--- a/trunk/conf/full.conf
+++ b/trunk/conf/full.conf
@@ -174,6 +174,7 @@ stream_caster {
     # the listen port for stream caster.
     #       for mpegts_over_udp caster, listen at udp port. for example, 8935.
     #       for rtsp caster, listen at tcp port. for example, 554.
+    #       for flv caster, listen at tcp port. for example, 8936.
     # TODO: support listen at <[ip:]port>
     listen          8935;
     # for the rtsp caster, the rtp server local port over udp,
diff --git a/trunk/conf/push.flv.conf b/trunk/conf/push.flv.conf
new file mode 100644
index 0000000000..140ff6eab9
--- /dev/null
+++ b/trunk/conf/push.flv.conf
@@ -0,0 +1,14 @@
+# push HTTP FLV to SRS.
+# @see https://github.com/simple-rtmp-server/srs/wiki/v2_CN_Streamer#push-http-flv-to-srs
+# @see full.conf for detail config.
+
+listen              1935;
+max_connections     1000;
+stream_caster {
+    enabled         on;
+    caster          flv;
+    output          rtmp://127.0.0.1/[app]/[stream];
+    listen          8936;
+}
+vhost __defaultVhost__ {
+}

From 99e4bad56f7799e124df38914163a03e19b1e0a2 Mon Sep 17 00:00:00 2001
From: winlin 
Date: Wed, 6 May 2015 22:04:47 +0800
Subject: [PATCH 800/800] update donation.

---
 DONATIONS.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/DONATIONS.txt b/DONATIONS.txt
index d526681fe9..6b670090e0 100644
--- a/DONATIONS.txt
+++ b/DONATIONS.txt
@@ -14,6 +14,7 @@ RMB 500-999
 * [2015-04-11 12:48] 丁一
 
 RMB 100-499
+* [2015-05-06 22:04] 姜庆东
 * [2015-04-10 19:52] 阳成飞
 * [2015-03-30 13:34] 扶凯
 * [2015-03-29 11-07] 姚伟斌